Hi all,
remember those software selectable weak (20k) pullups on the input pins ?
It occurred to me that those can be used as a programmable voltage divider:
configuring a few ports with pullup in parallel lowers the overall value of that resistance.
if you want less pins (and thus 20k resistors) parallel, put the surplus of input pins into high-impedance mode.
So, you take a few unused ports, and set them to INPUT.
connect all those INPUT pins together.
then locate the GND pin and insert a 20k resistor between that GND pin and the tied-together pins.

now you can create voltages between 0 and "a bit under your board's
supply voltage". These voltages are measured over the external resistor.
here is the general table of values, assuming 20k pullups and a 20k
resistor to ground
| 0 pins pull up | 0/1volts |
|---|---|
| 1 pin pullup | 1/2 of Vcc |
| 2 pin pullup | 2/3 of Vcc |
| 3 pin pullup | 3/4 of Vcc |
| 4 pin pullup | 4/5 of Vcc |
| I'm sure you see | the pattern |
when increasing and then decreasing the number of pull ups you
get a nice positive half-sinus with these values.
You can play with the value of the external resistor to get the voltages you might want.
Naturally you can also use these pullups as part of an ordinary R-2R resistor ladder, but check if all your pullup resistors have equal value.
The data sheet says minimum = 20kOhm and maximum = 50kOhm, so there is quite a bit of spread...
you can use your ADC in order to measure the actual voltages.
since there are several ways to (e.g.) pick 2 pullups from 4 pins you can select the pins that give the most accurate/desirable value.
using a scope I saw a bit of capacitive behaviour at high frequencies.
meaning that it took time to reach a lower voltage from a higher voltage, with the typical capacitor discharge curve.
I have cobbled together a concept sketch and a sketch to try out maximum attainable frequency. I reached 500 kHz
with the fast version. However, the amplitude of that signal was only half a volt.
the shape of the sinus was very smooth and symmetrical.
The slower concept sketch did about 110 kHz, having an amplitude of 2 volts on my 16 MHz Duamilanuove. it was a bit more jagged.
this is the view at 5 microseconds per division
When lowering the frequency the signal slowly becomes
the sequence of stepped half-sines again.
this is with a delay(5) inserted
concept sketch :
// Ronald van Aalst
// Dec 22 2010
// 2ct DAC (concept)
// creates a crude sinus of about 110 kHz
// utilizing the input pullups (20k to 50k) of the arduino ports,
// using them as a programmable voltage divider
// this program uses pin 4,5,6, and 7
// tie these pins together and connect them to GND
// via a separate 1/4 watt 22k resistor
//
// further ideas :
// -- tie the signal pin to the ADC in order to determine actual voltages generated.
// -- use ADC measurement to select the sets of pins to pullup
// -- put the SetVal functionality in a timed interrupt for background operation
// -- create a R/2R ladder using the pullups and some separate resistors (measure actual voltages with the adc)
void setup() {
}
void loop() {
asm volatile ("cli \n" : : : ); // disable interrupts
for (int i = 0; i<1;) { // use a for because the loop() is too much overhead and creates timing issues on the scope
SetVal(B00000000); // no pullups, signal = 0 volts
SetVal(B00000001); // one pullup active (20k) : signal about 1/2 of 5 volts = 2,5 volts
SetVal(B00000011); // two parallel pullups = 10k : signal = 2/3 of 5 volts = 3,33 volts
SetVal(B00000111); // three par. pullups : signal = 3/4 of 5 volts = 3,75
SetVal(B00001111); // four pullups active : signal = 4/5 of 5 volts = 4 volts
SetVal(B00000111); // and down again
SetVal(B00000011);
SetVal(B00000001); // and ready for next cyclus
}
}
void SetVal (byte pat) {
pat = pat << 4;
DDRD |= B00001111; // pin 7654 are input; rest unchanged
PORTD = ((pat & B11110000) | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
// insert delay here
}
and the fast version
// Ronald van Aalst
// Dec 22 2010
// 2ct DAC (concept; fast version)
// on a 5 volt duamilanove it generates a 0,5 volt top-top 500 khz signal
// utilizing the input pullups (20k to 50k) as a DAC,
// using them as a programmable voltage divider
// this program uses pin 4,5,6, and 7
// tie these pins together and connect them to GND via a separate 1/4 watt 22k resistor
//
void setup() {
}
void loop() {
asm volatile ("cli \n" : : : ); // disable interrupts
DDRD |= B00001111; // pin 7654 are input; rest unchanged
for (int i = 0; i<1;) { // use a for because the loop() is too much overhead and creates timing issues on the scope
// SetVal(B00000000); // no pullups, signal = 0 volts
PORTD = (B00000000 | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
// SetVal(B00000001); // one pullup active (20k) : signal about 1/2 of 5 volts = 2,5 volts
PORTD = (B00010000 | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
// SetVal(B00000011); // two parallel pullups = 10k : signal = 2/3 of 5 volts = 3,33 volts
PORTD = (B00110000 | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
// SetVal(B00000111); // three par. pullups : signal = 3/4 of 5 volts = 3,75
PORTD = (B01110000 | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
// SetVal(B00001111); // four pullups active : signal = 4/5 of 5 volts = 4 volts
PORTD = (B11110000 | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
// SetVal(B00000111); // and down again
PORTD = (B01110000 | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
// SetVal(B00000011);
PORTD = (B00110000 | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
// SetVal(B00000001); // and ready for next cyclus
PORTD = (B00010000 | (PORTD & B00001111)) ; // set pins in "pat" pins on pullup
}
}
so, this is a nice way to implement high frequency sine generator.
at lower frequency you will have to smooth (filter)
the sequence of stepped half-sines into something more resembling a true and smooth sine, I guess.
if only ATMEL had provided a choice between pull-up and pull-down;
then a full sine could be generated.
If anybody knows a trick to flip the signal upside down in order to complete the negative part of the sine,
I'm most interested. I tried hooking the ground of the scope onto an OUTPUT pin, hoping to create a negative voltage that way, but that did not do the trick.
This signal generator depends on the current the INPUT pins will PROVIDE. that is not a lot, since input pins expect to sink current.
So, the signal should not be loaded by whatever you send the signal into. You WILL need some amplifier or at least something to
deliver the signal at less output impedance. a FET or OpAmp should do the trick.
Still, not bad for a single resistor...
Ps, this might be old hat to you, but I have never seen the input pullups used this way.
On the other hand, most things have already been tried, so let
me know if you saw a comparable trick somewhere.