Tuesday, February 23, 2016

MCP4728 - 12 Bit I2C DAC Driver - AN1



 MCP4726 - 12 Bit I2C DAC Driver for ESP8266 nEXT EVO AN-1


Youtube Video here


    From today we will move on the Analog interface part of the nEXT EVO Board AN-1 and we will start talking about the Digital to Analog conversion, Analog Autoscaling input and Analog to Digital conversion .

 For the Digital to Analog conversion part the choosen one is the Microchip MCP4728 I2C DAC IC.

 The MCP4728 device is a quad, 12-bit voltage output Digital-to-Analog Convertor (DAC) with non-volatile memory (EEPROM).

As it has a on-board precision output amplifier with rail-to-rail analog output swing capabilities that means first of all that we don't need any other Output Buffer as mandatory needed for non-buffered DAC's.

 The MCP4728 device has also a high precision internal voltage reference (VREF = 2.048V). The user can select the internal reference or external reference (VDD) for each channel individually.


Features

• 12-Bit Voltage Output DAC with 4 Buffered Voltage Outputs

        - Each output is driven by its own output buffer with a gain of 1 or 2 depending on the gain and
       VREF selection bit settings.
        - In normal mode, the DC impedance of the output pin is about 1Ω. In Power-Down mode,
       the output pin is internally connected to 1 kΩ, 100 kΩ, or 500 kΩ, depending on the
       Power-Down selection bit settings.
        - The VOUT pin can drive up to 1000 pF of capacitive load. It is recommended to use a load
       with RL greater than 5 kΩ.

• On-Board Non-Volatile Memory (EEPROM) for DAC Codes and I2CTM Address Bits
• Internal or External Voltage Reference Selection
• Output Voltage Range:
     - Using Internal VREF (2.048V):
            0.000V to 2.048V with Gain Setting = 1
           0.000V to 4.096V with Gain Setting = 2
     - Using External VREF (VDD): 0.000V to VDD
• ±0.2 LSB DNL (typical)
• Fast Settling Time: 6 μs (typical)
• Normal or Power-Down Mode
• Low Power Consumption
• Single-Supply Operation: 2.7V to 5.5V
• I2C Interface:
      - Address bits: User Programmable to EEPROM
      - Standard (100 kbps), Fast (400 kbps) and High Speed (3.4 Mbps) Modes
• 10-Lead MSOP Package
• Extended Temperature Range: -40°C to +125°C

If you want you can consider it the 4 channels big brother of the MCP4726 1 Channel 12 Bit DAC  

For more details, please see MCP4728 Datasheet



What we will need:
 
ESP8266 nEXT Evo + AN1 Board



Driver implementation
 
 
As MCP4728 has a I2C compatible compatible interface, building a driver for it it's a pretty straigh forward process:

 
1 . I2C Bus initialisation function
function  init_i2c(sda, scl)
          i2c.setup(id, sda, scl, i2c.SLOW)
     end

2. DAC Register Data load

 Each channel has its own volatile DAC input register and EEPROM. The details of the input registers and EEPROM are shown in the tables below:



  

2.1 Simple version using the Power supply Voltage as External 
      Voltage reference, Vref=Vcc

        -- single Write command version
 function dac(ch_reg,voltage)
          volt=(voltage*4096)/vcal -- calibrate!
          print("Voltage Steps:" .. string.format("%d",volt))

          msb = bit.rshift(volt, 8)
          print("MSB:" .. string.format("%d",msb))      
          lsb = volt-bit.lshift(msb,8)
          print("LSB:" .. string.format("%d",lsb))      

          i2c.start(id)
          i2c.address(id, dac_addr ,i2c.TRANSMITTER)
          i2c.write(id,ch_reg)
    
          i2c.write(id,msb)
          i2c.write(id,lsb)
          i2c.stop(id)
end


2.2 Using the internal Voltage Reference, Vref=2.048V
          - Gain settings also enabled, 1x,  2x
 function dac_vref(ch_reg,vref,g,voltage)
          volt=(voltage*4096)/vcal -- calibrate!
          print("Voltage Steps:" .. string.format("%d",volt))

          msb = bit.rshift(volt, 8)
          print("MSB:" .. string.format("%d",msb))      
          lsb = volt-bit.lshift(msb,8)
          print("LSB:" .. string.format("%d",lsb))      
          if (vref==1) then
                msb = msb + 128
                if (g==2) then msb = msb + 16 end
          end
          i2c.start(id)
          i2c.address(id, dac_addr ,i2c.TRANSMITTER)
          i2c.write(id,ch_reg)
    
          i2c.write(id,msb)
          i2c.write(id,lsb)
          i2c.stop(id)
end


 3. TEST program

3.1 Vref = External, Vref=Vcc


init_i2c(sda,scl)
--SINGLE WRITE COMMAND: WRITE A SINGLE DAC INPUT REGISTER AND EEPROM

ch_reg=0x58         -- CH A - VRef Vcc
dac(ch_reg,0.5)
ch_reg=0x5A        -- CH B - VRef Vcc
dac(ch_reg,1)
ch_reg=0x5C        -- CH C - VRef Vcc
dac(ch_reg,2)
ch_reg=0x5E        -- CH D - VRef Vcc
dac(ch_reg,3)

3.2 Vref = Internal, Vref=2.048V

 -- SET PG to x1 or x2 - valid only when Vref = Vref Internal !!
ch_reg = 0x58
vref= 1    --set internal Vref = 2.048 !!
vcal=2.048

gain=2
voltage = 1
voltage= voltage/2
dac_vref(ch_reg,vref,gain,voltage)
voltage=0.5
dac_vref(ch_reg,vref,gain,voltage)


 3.3 Vref = Vcc

-- EXTERNAL Vref = Vcc
voltage = 2
gain=1
ch_reg = 0x58
vref= 0 -- set EXTERNAL Vref = Vcc = 3.265 - MEASURE & Calibrate!!
vcal=3.265
dac_vref(ch_reg,vref,gain,voltage)

--autoupdate DAC every sec
tmr.alarm( 0, 1000, 1, function()
  print("\nUpdate DAC data")
  dac_vref(ch_reg,vref,gain,voltage)
end)


Tuesday, February 9, 2016

Mailbag - 4x4 Matrix Keyboard for ESP8266 nEXT EVO Board



This Mailbag can also be seen as Part 4 of the ESP8266 nEXT EVO Analog Extension Board (AN1)


  In this part we will continue talking about the ESP8266 nEXT Evo 8 Bit I/O Expansion  Port based on the PCF8574 chip from NXP, testing procedures and software programming for the AN1 Board.

  After testing the Output of the 8Bit I/O Port it's now time to test also the Input function.
  So, what can be easier to use for such a processs than some pushbuttons for interaction?  I was thinking about some sort of buttons interface and because I just have arround a small 4x4 Matrix Keyboard why not directly a keyboard interface, especially that I'm waiting to receive some very thin membrane ones to be used for some projects.





Previous related Articles:



--------------------------------------------------------------------------------------------------------------------------
For any new CBDB orders/requests please feel free to use as usual:
     tech at esp8266-projects.com.


ESP8266 nEXT Evo bare PCB has also been made available directly at Dirty PCBs, our preferred PCB House for experimenting (**):
 http://dirtypcbs.com/view.php?share=9699&accesskey=91d782fd4a10943fd36ecc371c7ff2cd


(**) - Actually you have there 2 Boards for the price of one, a ESP8266 nEXT Evo together with a AN1 nEXT Analog Extension Board that brings you a 18Bit ADC (autoscale 0-40V input!), 4x12Bit DAC, Precison Temperature measurement, 8bit I/O port, etc.  
-------------------------------------------------------------------------------------------------------------------------






What we will need:





    Connection with the ESP8266 nEXT EVO Board is very easy, as Analog Extension Board - AN1 connector is fully compatible with the nEXT connector. Matrix Keyboard can be connected directly to the Analog Extension Board - AN1 8Bit I/O Port :

4x4 Matrix Keyboard connected to the ESP8266 nEXT Evo Board 8Bit I/O Expansion Port

 For more details and explanations please take a look at the Youtube Video from above: https://youtu.be/cVv7GCHmZ_o





Software implementation

For a better understanding of the way how the PCF8574 8Bit quasi-bidirectional I/O Port is working it might be a good idea to look at the prevoius related article and the PCF8574 Datasheet



1. Init I2C bus/interface

         -- init I2C nEXT BUS
       function i2c_init()
            i2c.setup(id, sda, scl, i2c.SLOW)
      end



2. Set PCF8574 PORT Register Function
function setPort( port, stat) 
    i2c.start(id)
    i2c.address(id, dev_addr ,i2c.TRANSMITTER)
    i2c.write(id,stat)
    i2c.stop(id)
end

3. Set Port function 

 Just a nicer way to write data to PCF8574 Register. Remember that we need to write a "ZERO" to the corresponding bit. We are sinking not sourcing !!
function setPortdata(p)
    pp = 255-p
    setPort(0x20,pp)
end

4. I/O Port READ Input
function read_input(dev_addr)
      i2c.start(id)
      i2c.address(id, dev_addr,i2c.RECEIVER)
      c = i2c.read(id,1)
      d = 255-string.byte(c)
      i2c.stop(id)
      --print("Read Value : "..d)
      return c,d
end



 5. OLED DISPLAY Related Functions

    5.1 Init OLED Display
 function init_OLED(sda,scl) --Set up the u8glib lib
     sla = 0x3C
     i2c.setup(0, sda, scl, i2c.SLOW)
     disp = u8g.ssd1306_128x64_i2c(sla)
     disp:setFont(u8g.font_6x10)
     disp:setFontRefHeightExtendedText()
     disp:setDefaultForegroundColor()
     disp:setFontPosTop()
     disp:setRot180()
end

 

     5.2 Print TEXT on Display
function PrintText()
  disp:drawStr(10, 25, str1)
  disp:drawStr(105, 25, str2)
end


 

     5.3 Print_LCD Function
function print_LCD()
   disp:firstPage()
   repeat
     PrintText()
     disp:drawFrame(2,2,126,62)
   until disp:nextPage() == false
end

6. Main Program
str1="Keyboard Input:"
str2="KEY"
id = 0
sda=2                           -- GPIO4
scl=1                            -- GPIO5
dev_addr = 0x20          -- PCF8574 Address

port={1,2,4,8}              -- COLS definition
row={128,64,32,16}     -- ROWS definition
key={'1','4','7','*','2','5','8','0','3','6','9','#','A','B','C','D'}   -- keys translation table

i=1
j=1
k=1
init_OLED(2,1)
print_LCD()
tmr.alarm( 0, 100, 1, function()
  setPortdata(port[i])                        -- activate COL[i]
  read_input(dev_addr)                     -- read active ROW

  while j<5 do
      if d==port[i]+row[j] then
            print("Col[i] = "..port[i])         -- for debug only
            print("Row[j] = "..row[j])        -- for debug only
            print("Read Value : "..d)           --for debug only
            print("Read Key   : "..k)            -- for debug only
            print("Pressed KEY Value : "..key[k].."\n")    -- print pressed KEY value
            str2=key[k]
            print_LCD()                              -- print also on Display
      end
      j=j+1
      k=k+1
      tmr.wdclr()
   end
  j=1

  i=i+1
  if i>4 then i=1
  end
  if k>16 then k=1
  end
end)