06-20-10
Open Source Hardware Ctd.
Last time I showed that the TL7700 can be used as a power supply monitoring tool. I interfaced it with a potentiometer to show that different supply voltages could be measured by the position of the potentiometer wiper. The next step is to automate this process so that any uC (in this case arduino) can determine the voltage under which they are operating. The critical thing to know is the wiper position, so I looked around for a digital potentiometer that would support that. Some digi-pots only support incriment/decriment functions, and others use a serial protocol that allows you to read and wtite the wiper position. There are two popular serial protocols that I ran into:
Serial Peripheral Interface (SPI) is a classic serial protocol developed for sending and receiving data simultaneously. The SPI bus can host numerous devices, which are addressed by pulling the individual’s Slave Select pin low. One drawback of SPI is that it requires 3 pins for communication AND a slave select pin for each device on the bus. Bummer that. Another option for communication is I2C. Aye Squared Sea uses only 2 wires for communication, multiple devices can share the bus, and addressing is done serially. Data transfer does not complete simultaneously, as with SPI, but the benifit of using only two pins makes it more attractive. The only drawaback that I can see is that using I2C sacrifices two analog pins (analog4 is SDA and analog5 is SCL). The ATmega has an hardware blocks for both of these protocols, which makes for less code overhead.
The two candidates for digiPot are:
MCP4151 SPI, 256 steps on wiper, PDIP-8, SOIC-8, DFN, ~$1
MCP4551 I2C, 256 steps on wiper, MSOP, ~$1
Both made by Microchip and both pretty darn cheap, considering. The TL7700 is ~$2.50, so the nominal cost of this Voltage Monitoring circuit runs about $3.50. The low price and ease of use make this design pretty attractive. I choose the MCP4551 for size, cost, and communication protocol (I2C).
I don’t happen to have any of the MCP4551, and it’s got a MSOP package handicap. But, I do happen any to have a couple of DIP DS1803’s laying around. They are dual digital potentiometers mady by Dallas Semiconductor (now MAXIM). They use I2C, and have similar commands to the MCP4551, so transitioning to the target hardware won’t be to difficult. I’ve got a fresh battery, so let’s test the system!
In the ATmega datasheed,I2C is called TWI. I’m not sure why, perhaps something to do with copyright controls over ‘I2C’? Whatever, it’s the same thing in practice. The available library for I2C on arduino is called wire and it’s alittle clunky. I read the datasheet, and came up with a simple function that drives the TWI hardware block with minimal code overhead. You’re welcome.
The setup in the picture here has the Boarduino running on 9V external supply. The Voltage follower is supplying power to the DS1803 and the TL7700. The DS1803 has a 100K rating, and I have attached resistors to the P0H and P0L pins as follows:
+V – 470K – H0, L0 – 47K – GND.
That broadens my resolution a bit on the wiper position. Future versions will use a 10K DigiPot.

Here’s the response that I got from the circuit above, running the code pasted in line at the end of this post:
Voltage Wiper Position Analog Reading
- 2.9 253 142
- 3.0 235 140
- 3.1 216 138
- 3.2 192 124
- 3.3 184 125
- 3.4 168 122
- 3.5 158 123
- 3.6 144 120
Pulling the ATmega out of the blue board and dropping it into a bread board (naked!) is easy enough, which is great because I want to power it with the Voltage Follower output to simulate a changing battery supply. I ran into trouble in doing this, however, because of the Brown Out Detector. A supply voltage, sourced from my voltage follower, below 3.4V would cause the chip to reset, and when the power came back up again, the BORF would be set (Brown Out Reset Flag is bit 2 of the MCU Status Register – MCUSR). I used the chip from off the boarduino, and also one from my new duemilanove with the same BORF trip point. The brown out detector level is set by the BODLEVEL bits (Bits 2-0 of the Extended Fuse Byte on the ATmega 328. Bits 2-0 of the High Fuse Byte on the ATmega 48,88,168)
BODLEVEL settings:
110 = ~1.8V
101 = ~2.7V
100 = ~4.3V
So, it looks like my chips’ fuses are set for a BODLEVEL of 4.3V? That’s wierd. Most of the forums state that the arduino fuse settings for the (328) Extended Fuse Byte are 0×05, which is 2.7V…. Oh well, not going to sweat it. I have some PIC16F88 chips in my kit, and will clone the code in PICBasicPro to run the self powered voltage test. That will be posted next. Here’s the arduino code that i ran to get the results listed above. Cheers.
/*
Test code for comtrolling the DS1803 digital potentiometer Dual
Using hardware TWI, control function calls:
byte = I2C_Read(SLA);
I2C_Write(SLA,COMMAND,DATA);
TWO ANALOG PINS TAKEN OVER TO USE I2C:
Analog 5 = SCL
Analog 4 = SDA
This will control a DS1803 that is interfaced with a TL7700 Voltage Supervisor.
The TL7700 RESET pin is connected to D2 (Trip), and goes high when the Vsense rises to 500mV.
It works shockingly well even with the 100K DS1803 that I have hooked up. I look forward to
working with a 10K pot to bring down the historesis (as noted on TL7700 DataSheet!)
*/
// these are (some) Status values sent from TWSR on completion of transmission
#define START 0×08 // START sent 08h
#define ACK_S_W 0×18 // ACK after SLA W 18h
#define NACK_S_W 0×20 // nACK after SLA W 20h
#define ACK_D_W 0×28 // ACK after DATA W 28h
#define NACK_D_W 0×30 // nACK after DATA W 30h
#define ACK_S_R 0×40 // ACK after SLA R 40h
#define NACK_S_R 0×48 // nACK after SLA R 48h
#define ACK_D_R 0×50 // ACK after DATA R 50h
#define NACK_D_R 0×58 // nACK after DATA R 58h
#define R_START 0×10 // Repeated Start 10h
#define SLA B01010000 //Slave Address = 0.1.0.1.A3.A2.A1.x
#define Write_P0 B10101001 //write P0, option to write P1 afterwards
#define Write_P1 B10101010 //write P1 only
#define Write_P0_P1 B10101011 //write P0 and P1 to the same value
int potValue;
int Trip = 2;
byte Wiper;
boolean tripped = false;
void setup() {
pinMode(Trip,INPUT);
Serial.begin(9600);
// I2C initialization
TWSR = 0; //set the SCL prescaler value to 1
TWBR = 12; //set the bit rate register to effect a 400KHz SCL frequency
delay(100);
I2C_Write(SLA,Write_P0,0); //set the wiper position to 0
Serial.println(“it’s on”); //acknowledge startup/reset
Serial.print(“MCUSR: “);
Serial.println(MCUSR,HEX); //print the MCUSR for BOR check
MCUSR = 0; //clear the MCUSR for next time
}
void loop(){
for (int i = 0; i<=255; i++){
I2C_Write(SLA,Write_P0,i);
delay(10);
// Serial.print(Wiper,DEC);
// Serial.print(” = “);
// Serial.println(potValue);
if ((digitalRead(Trip) == 1)&&(tripped == false)){
Wiper = I2C_Read(SLA);
potValue = analogRead(3);
Serial.print(“Voltage Trip Point = “);
Serial.print(Wiper,DEC);
Serial.print(“, “);
Serial.println(potValue);
tripped = true;
}
}
tripped = false;
Serial.println(“One More Time!”);
}
void I2C_Write(byte address,byte command,byte data){
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //send START (clear TWINT)
while (!(TWCR & (1<<TWINT))); //wait for TWINT to set
if ((TWSR & 0xF8) != START){ERROR(1);} //barf TWSR if not as expected
TWDR = address; //load address int TWDR
TWCR = (1<<TWINT)|(1<<TWEN); //clear TWINT and start transmission
while (!(TWCR & (1<<TWINT))); //wait for TWINT to set
if ((TWSR & 0xF8) != ACK_S_W){ERROR(2);} //barf TWSR if not as expected
TWDR = command; //load data into TWDR
TWCR = (1<<TWINT)|(1<<TWEN); //clear TWINT and start transmission
while (!(TWCR & (1<<TWINT))); //wait for TWINT to set
if ((TWSR & 0xF8) != ACK_D_W){ERROR(3);} //barf TWSR if not as expected
TWDR = data; //load data into TWDR
TWCR = (1<<TWINT)|(1<<TWEN); //clear TWINT and start transmission
while (!(TWCR & (1<<TWINT))); //wait for TWINT to set
if ((TWSR & 0xF8) != ACK_D_W){ERROR(4);} //barf TWSR if not as expected
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); //transmit STOP
}
byte I2C_Read(byte address){
byte data;
address ++; //set R/W bit for Read operation
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //send START (clear TWINT)
while (!(TWCR & (1<<TWINT))); //wait for TWINT to set
if ((TWSR & 0xF8) != START){ERROR(5);} //barf TWSR if not as expected
TWDR = address; //load address int TWDR
TWCR = (1<<TWINT)|(1<<TWEN); //clear TWINT and start transmission
while (!(TWCR & (1<<TWINT))); //wait for TWINT to set
if ((TWSR & 0xF8) != ACK_S_R){ERROR(6);} //barf TWSR if not as expected
TWCR = (1<<TWINT)|(1<<TWEN); //clear TWINT and start transmission
while (!(TWCR & (1<<TWINT))); //wait for TWINT to set
if ((TWSR & 0xF8) != NACK_D_R){ERROR(7);} //barf TWSR if not as expected
data = TWDR; //read TWDR into data
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); //transmit STOP
return data;
}
void ERROR(int j){
Serial.print(“TWSR = “);
Serial.print(TWSR,HEX);
Serial.print(” – “);
Serial.println(j);
}


Great…
love your blog, http://yourlocalblog.com/jennie/ ,Thanks again….
Great One…
I must say, its worth it! My link, http://house-of.com/kris/,thanks haha…
very helpful…
I preferred to thank you for this good article. http://lychtb.sexusblog.com/2011/09/01/beautiful-a-wedding-dress/ I by all odds liked every little bit of it…