Introduction: DS1803 Dual Digital Potentiometer With Arduino
I like to share the usage of a DS1803 digital potmeter with an Arduino. This IC contains two digital potmeters which can be controlled over a two wire interface, for this I use the wire.h library.
This IC can replace a normal analog potmeter. In this way you are able to control for example an amplifier or power supply.
In this instructable I control the brightness of two LED's to show the working.
The arduino counts the pulses of a rotary encoder and place the value in variable pot[0] and pot[1]. When you push the switch on the encoder, you can switch between pot[0] and pot[1].
The actual value of the pots are read back from the DS1803 and placed in variable potValue[0] and potValue[1] and displayed on a LCD.
Step 1: Connections of the DS1803
Here you can see the connections of the DS1803. H is the high side of the potentiometer, L the low side and W the wiper. SCL and SDA are the bus connections.
With connection A0, A1 and A2 you can give the DS1803 it's own adress, in this way you can controll more devices via one bus. In my example I have give the DS1803 adres 0 by connecting all pins to the ground.
Step 2: Command Byte
The way the DS1803 operates can be used in the command byte. When you select "write potentiometer-0" both potentiometers are selected, when you only want adjust potentiometer-0, you only have to send the first data byte. "Write potentiometer-1" only adjust potmeter-1. "Write to both potentiometers" gives both potentiometers the same value.
Step 3: Control of the DS1803
The control byte (figure 3) has a device identifier, this stays always the same. In my example A0, A1 and A2 are 0 because we select adres by putting all A-pins to ground. The last bit R/W will be set to 0 or 1 by the command "Wire.beginTransmission" and "Wire.requestFrom" in the Arduino. In figure 5 you can see the whole telegram. The read telegram is shown in figure 4.
Step 4: Set Up
This circuit shows how to connect everything. The Nokia LCD is available with different connections, be sure that you connect yours right. Also the rotary encoder his different versions, some has the common on the middle pin others not. I have put a little filter network (470 Ohm resistor with 100nF cap) to filter the A and B output signals of the encoder. I need this filter because the output did have a lot of noise. I also put a debounce timer in my program to cancel some noise. For the rest I think the circuit is clear. The LCD can be ordered via Adafruit https://www.adafruit.com/product/338 .
Step 5: The Program
For the use of the 2-wire bus I include the Wire.h library. To use the LCD I include the Adafruit library which you can download from https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library also the Adafruit_GFX.h library is available here https://github.com/adafruit/Adafruit-GFX-Library.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);
Here you can see all the variables. Control byte and command byte as described before. The deBounceTime can be adjusted depending on the noise on you encoder.
byte pot[2] = {1, 1};
byte controlByte = B0101000; // 7 bits, byte commandByte = B10101001; // last 2 bits is potmeter selection. byte potValue[2]; int i = 0; int deBounceTime = 10; // Adjust this value depending on the noise const int encoder_A = 8; const int encoder_B = 9; const int buttonPin = 2; unsigned long newDebounceTime = 0; unsigned long oldTime; boolean pressed = 0; boolean count = 1;
In the setup I define the right pins and put the static text on the LCD
void setup() {
Wire.begin(); Serial.begin(9600); pinMode(encoder_A, INPUT); pinMode(encoder_B, INPUT); pinMode(buttonPin, INPUT); newDebounceTime = millis();display.begin(); display.setContrast(50); display.clearDisplay(); display.setTextSize(1); display.setTextColor(BLACK); display.setCursor(0,10); display.println("POT 1="); display.setCursor(0,22); display.println("POT 2="); display.display();
}
In the loop I first check if the interval is more then 500ms, if yes the LCD get updated. If not the button on the encoder is checked. If pressed the toggleBuffer get called. After this the encoder is checked. If input 0 is low (rotation detected) I check input B, if input B is 0 I increment pot[], others I decrement. After this the value will be send to the DS1803 via wire.write.
void loop() {
interval(); if(digitalRead(buttonPin)== 1 && (pressed == 0)){toggleBuffer();} if(digitalRead(buttonPin)== 0){pressed = 0;}
if (digitalRead(encoder_A) == 0 && count == 0 && (millis() - newDebounceTime > deBounceTime)){ if (digitalRead(encoder_B) == 0){ pot[i]++; if(pot[i] > 25){pot[i] = 25;} }else{ pot[i]--; if(pot[i] < 1){pot[i] = 1;} } count = 1; newDebounceTime = millis();
Wire.beginTransmission(controlByte); // start transmitting Wire.write(commandByte); // selection of potmeters Wire.write(pot[0] * 10); // send 1st byte of potmeter data Wire.write(pot[1] * 10); // send 2nd byte of potmeter data Wire.endTransmission(); // stop transmitting }else if (digitalRead(encoder_A) == 1 && digitalRead(encoder_B) == 1 && count == 1 && (millis() - newDebounceTime > deBounceTime)){ count = 0; newDebounceTime = millis(); } }
void toggleBuffer(){ pressed = 1;
if (i == 0){i = 1;}else{i = 0;} }
First I clear the area where I have to write the varibles. I do this to draw a rectangle in this area. After that I write the variables to the screen.
void writeToLCD(){
Wire.requestFrom(controlByte, 2); potValue[0] = Wire.read(); // read first potmeter byte potValue[1] = Wire.read(); // read second potmeter byte display.fillRect(40, 0, 40, 45, WHITE); // clear variable screen on LCD display.setCursor(40,10); display.print(potValue[0]); // write 1st potmeter value to LCD display.setCursor(40,22); display.print(potValue[1]); // write 2nd potmeter value to LCD display.setCursor(60,(10 + i * 12)); display.print("<"); display.display(); }
void interval(){ // interval timer to write data to LCD
if ((millis() - oldTime) > 500 ) { writeToLCD(); oldTime = millis(); } }