Tuesday, May 3, 2016

Design a bipolar voltage source using the Microchip DAC MCP4922

Design a bipolar voltage source using the Microchip DAC MCP4922

The microchip device (MCP4922) is a very easy to use DAC that is accessed via SPI interface. I found it easier to interface with a +5V bus and VCC as opposed to +3.3V despite of what the datasheet says. (http://ww1.microchip.com/downloads/en/DeviceDoc/22250A.pdf)

In addition, I created a simple spreadsheet to enter different values in order to design before layout.

Once you read the datasheet, you'll understand where I got the values from.

DA and DB are the values that control the gain as well as the offset (Maximum 4095 in decimal).

VREFA and VREFB are your input reference sources. I typicaly use 2.5Vdc.

R1-R4 are the gain and offset resistors. I set all to 10k Ohms 1% 1/4Watts.

GA and GB are gain x1 or x2 as desire.

Vo is the output.

Enjoy.


Download worksheet here!!

Monday, March 7, 2016

Control the MCP4922 DAC using an Arduino MEGA2560

Control the Microchip MCP4922-E/P Digital to Analog Converter using an Arduino board.


I've been using the microchip DAC to output a +/-5V output. I used the Arduino MEGA2560 to write to the SPI bus and it worked great. The code is as follows:

//pin#  Pin Name    Mapped Pin Name
//19  PB0 ( SS/PCINT0 ) Digital pin 53 (SS)
//20  PB1 ( SCK/PCINT1 )  Digital pin 52 (SCK)
//21  PB2 ( MOSI/PCINT2 ) Digital pin 51 (MOSI)
//22  PB3 ( MISO/PCINT3 ) Digital pin 50 (MISO)
////////////////////////////////////////////////////////////////////////////////////////////////////

//Microchip MCP4922-E/P 
//Pin No.   Symbol  Function
//1         VDD     Supply Voltage Input (2.7V to 5.5V)
//3         CS      Chip Select Input
//4         SCK     Serial Clock Input
//5         SDI     Serial Data Input
//8         LDAC    Synchronization Input. This pin is used to transfer DAC settings (Input Registers)
//                  to the output registers (VOUT)
//9         SHDN    Hardware Shutdown Input
//10        VOUTB   DACB Output
//11        VREFB   DACB Reference Voltage Input (VSS to VDD)
//12        VSS     Ground reference point for all circuitry on the device
//13        VREFA   DACA Reference Voltage Input (VSS to VDD)
//14        VOUTA   DACA Output
////////////////////////////////////////////////////////////////////////////////////////////////////


// inslude the SPI library:
#include <SPI.h>

// set pin 10 and 11 as the chip select
const int nCS0 = 8;
const int nCS1 = 11;
const int nLDAC = 12;
const int nSHDN = 9;

const int GAINx1 = 1;   
const int GAINx2 = 0;    

const int Write_to_DACA = 0;
const int Write_to_DACB = 1;

const int BUF = 1;
const int nSHDown = 1;

// analog potentiometer joystick
int joystickBlue = A1;
int AnalogValue = 0;    //to store the value here





void setup() {
    
  pinMode(nCS0, OUTPUT);
  pinMode(nCS1, OUTPUT);
  pinMode(nLDAC, OUTPUT);
  pinMode(nSHDN, OUTPUT);
  
//  Set shutdown pin to high
  digitalWrite(nSHDN, HIGH);

// initialize the nLDAC to high
  pinMode(nLDAC, HIGH);
      
  // initialize SPI:
  SPI.begin();
  
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  
  Serial.begin(115200);
}



void loop() {
  // analog values  
      AnalogValue = analogRead(joystickBlue);
  
  // Write to DA
  String myBinString = String(Write_to_DACA) + String(BUF) + String(GAINx2)+ String(nSHDown) + ConvertInt2Bin12(3000);
  
  // Write to DB
  String myBinStringB =String(Write_to_DACB) + String(BUF) + String(GAINx2)+ String(nSHDown) + ConvertInt2Bin12(2048);
  
  
  
  Serial.println(myBinString);  
  int tempA = ConvertBin2Dec(myBinString);
  Serial.println(tempA, HEX);
  Serial.println(" ");

  Serial.println(myBinStringB);  
  int tempB = ConvertBin2Dec(myBinStringB);
  Serial.println(tempB, HEX);
  Serial.println(" ");
  delay(500);
    

  digitalWrite(nCS0, LOW);    //Enable the chip U4 for X output
  SPI.transfer16(tempA);
  digitalWrite(nCS0, HIGH);
  
  digitalWrite(nCS0, LOW);    
  SPI.transfer16(tempB);
  digitalWrite(nCS0, HIGH);
  
  
  Latch_DAC_Input();

  
}


void DAC_Write(int address, int value, int CSNum){
  // Do the chip select value
  if (CSNum == 0) {
    digitalWrite(nCS0, LOW);
  }else if (CSNum == 1) {
    digitalWrite(nCS1, LOW);
  }

  //  send in the address and value via SPI:
  SPI.transfer(address);
  
  SPI.transfer(value);

  if (CSNum == 0) {
    digitalWrite(nCS0, HIGH);
  }else if (CSNum == 1) {
    digitalWrite(nCS1, HIGH);
  }
  
}

void Latch_DAC_Input(){
  //toggle the nLDAC to output voltage
  
  digitalWrite(nLDAC, LOW);
  
  digitalWrite(nLDAC, HIGH);
}


String ConvertInt2Bin12(int Value){
  //this sub will convert any integer value to its corresponding 12 bit
  String myString = String(Value, BIN);
  int lenString = myString.length();
  
  for ( int i=lenString; i<12; ++i){
    myString = "0" + myString;
  }
  
  return myString;
}

int ConvertBin2Dec(String BinData){
  //this sub will convert a 16 bit binary string to its equivalent HEX
  int LenString = BinData.length();
  double ValueDec = 1;
  int DataOut = 0;
  
  

  for ( int i = 0; i < LenString; i++){
  String MyStr =BinData.substring(LenString-i-1, LenString-i);

  if (MyStr == "0"){
    // add a zero
  }else{
    //add a 2^i
    ValueDec = pow(2,(float)i) + ValueDec;
  }

    
  }

  DataOut = ValueDec;

  return DataOut;
}


Parallax Serial LCD Arduino Subroutines

Parallax  Serial LCD 
2 rows x 16 characters Non-backlit (#27976) 
2 rows x 16 characters Backlit (#27977) 
4 rows x 20 characters Backlit (#27979) 

http://elmicro.com/files/parallax/seriallcd-v20.pdf

The best way to interface to this Serial LCD is to use the software serial library offered by Arduino environment. Be sure that the following is on your code.

///////////////////////// Libraries   ///////////////////////////////
  #include <SoftwareSerial.h>

////////////////////////     LCD Variables    ///////////////////////////
SoftwareSerial mySerial(10, 11); // RX, TX

Then, write subroutines to initialize the LCD.

void initializeLCD(){
  mySerial.begin(19200);   //change per LCD
  delay(500);
  // put your setup code here, to run once:
  mySerial.write(12);
  delay(5);
}

Subroutines to control the screen


void LCD_clearScreen(){
  mySerial.write(12);
  delay(10);
}

void Turn_backlight_on_LCD(){
  mySerial.write(17);
  delay(5);
}

void Turn_backlight_off_LCD(){
  mySerial.write(18);
  delay(5);
}

void Line_Feed_LCD(){
  mySerial.write(12);
  delay(5);
}

void Carriage_Return_LCD(){
  mySerial.write(13);
  delay(5);
}

void Move_Cursor(int line, int pos){
  switch (line){
    case 0:
        mySerial.write(128 + pos); 
        delay(5);
    break;
    case 1:
        mySerial.write(148 + pos); 
        delay(5);
    break;
    case 2:
        mySerial.write(168 + pos); 
        delay(5);
    break;
    case 3:
        mySerial.write(188 + pos); 
        delay(5);
    break;
    default:
    // do nothing
    break;
  }
 delay(5);
}


To write the a float value onto the LCD, it would work best if it was converted to a string as described below using dtostrf function

void LCD_floatValue(){
  int StringLen  = 7;
  int numVarAfterDec = 3;
  static char outstr[15];   // the array to store the results
  static float f_val = Lever_Position;

  dtostrf(Lever_Position,7, 1, outstr);
  

  Move_Cursor(0,0);
  delay(5);
  mySerial.print("Value ");
  mySerial.println (outstr);  
}

L298 Motor Driver with Arduino MEGA2560

Arduino MEGA 2560

This is my first blog about the Mega2560. It has become a very useful tool for rapid prototyping and making simple project. When I say simple I mean simple but useful. 
I recently connected a DC Motor that required a potentiometer as a sensor and a motor driver (L298). 

Where to buy:
Arduino, you can buy anywhere nowadays. I usually buy mine from sparkfun $46
(https://www.sparkfun.com/products/11061)

L298 Driver: I also buy this from Sparkfun $35
(https://www.sparkfun.com/products/9670)
Connect the motor signals according to the Arduino declarations below:

//////////////////////////   Motor Driver #1 Signals  /////////////////////////////
const int MOT1_EN = 26;   
const int MOT1_IN1 = 22;  
const int MOT1_IN2 = 24;  
const int MOT1_CSA = A4;  
const int MOT1_CSB = A5;  

const int POT = A0;                          

Initialize the motor signals before using.

      pinMode(MOT1_IN1, OUTPUT);
      pinMode(MOT1_IN2, OUTPUT);
      pinMode(MOT1_EN, OUTPUT);
      pinMode(MOT1_CSA, INPUT);
      pinMode(MOT1_CSB, INPUT);

Create subroutine to move the motors Clockwise and counter clock wise.

void Motor1_CC(){
    digitalWrite(MOT1_EN, HIGH);
    delay(1);  
    digitalWrite(MOT1_IN1, HIGH);
    digitalWrite(MOT1_IN2, LOW);
}

void Motor1_CCW(){
    digitalWrite(MOT1_EN, HIGH);
    delay(1);  
    digitalWrite(MOT1_IN1, LOW);
    digitalWrite(MOT1_IN2, HIGH);
}

Write routines to break and coast motors

void Motor1_FastBrake(){
  //this routine will brake the motor A quickly
    digitalWrite(MOT1_EN, HIGH);
    delay(1);  
    digitalWrite(MOT1_IN1, LOW);
    digitalWrite(MOT1_IN2, LOW);
}

void Motor1_Coast(){
  //this routine will coast Motor A
    digitalWrite(MOT1_EN, LOW);
    delay(1);  
    digitalWrite(MOT1_IN1, LOW);   //irrelevent
    digitalWrite(MOT1_IN2, LOW);   //irrelevent
}

Now we are ready to write our code according to the project requirements. I would read the analog value for the potentiometer and move the motors using the routines as needed.
Enjoy!