Showing posts with label 12 Bit DAC. Show all posts
Showing posts with label 12 Bit DAC. Show all posts

Wednesday, March 30, 2016

ESP8266 - ADC Input frontend



Autorange Analog frontend


For a deeper Hardware description please take a look at the ESP8266 Analog Extension Board





What we will need:
 


ADC Frontend description

 Selection of the input voltage divider is done using an analog switch driven by PCF8574 PORT P0 and P1 bits:

0 - 1:20 Divider
1 - 1:10 Divider
2 - 1:5 Divider
3 - Full Voltage Range


As the ADC Input is programmed to be used in the 0-2V range that will give us the followings available ranges:

0 -> 0 - 40V
1 -> 0 - 20V
2 -> 0 - 10V
3 -> 0 - 2V

In the case of using the MCP3421 ADC at 12 Bit resolution, we will have the following corresponding LSB values:

- 1:20 Divider  -  0.02V
- 1:10 Divider  -  0.01V
- 1:5 Divider    -  0.005V
- Full Range    -  0.001V




Software implementation

1. Init I2C bus/interface

 Standard I2C Bus Initialisation function:

function init_I2C()
    i2c.setup(bus, 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. Set Voltage Divider function

  Select the desired Voltage divider ratio based on the choosen ratio value and calibrated LSB data.

--calibration data
x20 = 0.020056
x10 = 0.01014
x5  = 0.005014
xfl = 0.0010075
xrt = x20 --by default start with highest VDIV!
function SetVDivider(rtio)
    if (rtio==0) then xrt=x20 end
    if (rtio==1) then xrt=x10 end
    if (rtio==2) then xrt=x5 end
    if (rtio==3) then xrt=xfl end
    print("XRT = "..xrt)
    setPortdata(rtio)    -- select desired voltage divider
    return xrt
end



       -- Set/Change Voltage Divider ratio:
SetVDivider(0)  -- 1:20 Divider
SetVDivider(1)  -- 1:10 Divider
SetVDivider(2)  -- 1:5  Divider
SetVDivider(3)  -- 1:1  Full Voltage in!!


5. MAIN Program


-- Main Program
id = 0
sda=2 --GPIO4
scl=1 --GPIO5
dev_addr = 0x20
--calibration data
x20 = 0.020056
x10 = 0.01014
x5  = 0.005014
xfl = 0.0010075
xrt = x20 --by default start with highest VDIV!
--init I2C Bus
init_I2C()

--Init Volatage divider
setPortdata(0) -- --by default start with highest VDIV!
SetVDivider(0)  -- 1:20 Divider

----MCP3421 ADC
require('mcp3421')
sda=2 --GPIO4
scl=1 --GPIO5
mcp3421:init(sda, scl)
mcp3421:write_ADC_config(0x68, 0x10)

tmr.alarm( 0, 1000, 1, function()
    adc_val = mcp3421:read_ADC_data(0x68)
    print("\nADC Value : "..adc_val.." \n Voltage  : " ..adc_val*xrt)
    return adc_val
end)





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)

       


Sunday, January 31, 2016

ESP8266 nEXT EVO - Analog extension Board - P3



This is Part 3 of the ESP8266 nEXT EVO Analog Extension Board (AN1)


In this part we will talk 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






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.  
-------------------------------------------------------------------------------------------------------------------------



PCF8574 General Description

  The PCF8574/74A provides general-purpose remote I/O expansion via the two-wire
bidirectional I2C-bus (serial clock (SCL), serial data (SDA)).
  

   The devices consist of eight quasi-bidirectional ports, 100 kHz I2C-bus interface, three
hardware address inputs and interrupt output operating between 2.5 V and 6 V. The
quasi-bidirectional port can be independently assigned as an input to monitor interrupt
status or keypads, or as an output to activate indicator devices such as LEDs. System
master can read from the input port or write to the output port through a single register.


  The active LOW open-drain interrupt output (INT) can be connected to the interrupt logic
of the microcontroller and is activated when any input state differs from its corresponding
input port register state. It is used to indicate to the microcontroller that an input state has
changed and the device needs to be interrogated without the microcontroller continuously
polling the input register via the I2C-bus.


 
Features : 
  • I2C-bus to parallel port expander 
  • 100 kHz I2C-bus interface (Standard-mode I2C-bus)
  • Operating supply voltage 2.5 V to 6 V with non-overvoltage tolerant I/O held to VDD with 100uA current source
  • 8-bit remote I/O pins that default to inputs at power-up
  • Latched outputs directly drive LEDs
  • Total package sink capability of 80 mA
  • Active LOW open-drain interrupt output
  • Eight programmable slave addresses using three address pins
  • Low standby current (2.5 uA typical)
  • -40C to +85C operation 
  • ESD protection exceeds 2000 V HBM per JESD22-A114 and 1000 V CDM per JESD22-C101
  • Latch-up testing is done to JEDEC standard JESD78 which exceeds 100 mA
  • Packages offered: DIP16, SO16, SSOP20

Bidirectional I/O Expander Example

Something to remember: 
  • PCF8574 can SINK but NOT SOURCE much current - 100uA only (it cannot output high, if you want). Look at the above example how is connected the LED for SINKING current.
  • Each of the 8 GPIOs have a minimum guaranteed sinking current of 10 mA per bit at 5 V.
  • Each pin needs its own limiting resistor to prevent damage to the device!! keep under 25mA/pin.
  • Maximum device limit sink current in about 80mA. If you need more, look after PCA8574 (200mA max sink current!)

For more details please take a look at the PCF8574 Datasheet



For easy testing the PCF8574 8Bit Port Output Pins we will use a very simple "Ghetto-Tester" based on 8 LED's and corresponding current limiting resitors:




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. Depending on how to you choose you socket type, you can install it on TOP or Bottom of the ESP8266 nEXT EVO Board :






    By default, on the AN-1 Board you should have the following available devices:
  • PCF8574 8Bit I/O Extension port  - at address 0x20
  • LM75 Temperature sensor             - at address 0x48
  • MCP4728 - 4x12Bit DAC             - at address 0x60
  • MCP3421 - 18Bit ADC                 - at address 0x68
   When also connected, the SSD1306 OLED Display will be available at 0x3C


   A first step in testing the AN-1 available functions and devices will be to scan the nEXT I²C Bus and see if all the existing ones are alive and responding to the I²C Master requests. Also will list any new added devices, if alive.

Using the SCANBUS program described in the Part 2 of the AN-1 Series, you can find very easy if your PCF8574 8Bit I/O Extension port  is available on the nEXT Bus :








 Software implementation


PCF8574 8Bit I/O port is a quasi-bidirectional I/O Port, same as Port 1,2,3 on the 8051 MCU, if you like.

    A quasi-bidirectional I/O is an input or output port without using a direction control register.
Whenever the master reads the register, the value returned to master depends on the
actual voltage or status of the pin. At power on, all the ports are HIGH with a weak 100 uA
internal pull-up to VDD, but can be driven LOW by an internal transistor, or an external
signal. The I/O ports are entirely independent of each other, but each I/O octal is controlled by the same read or write data byte.

Advantages of the quasi-bidirectional I/O over totem pole I/O include:

  • Better for driving LEDs since the p-channel (transistor to VDD) is small, which saves die size and therefore cost. LED drive only requires an internal transistor to ground, while the LED is connected to VDD through a current-limiting resistor. Totem pole I/O have both n-channel and p-channel transistors, which allow solid HIGH and LOW output levels without a pull-up resistor — good for logic levels.
  • Simpler architecture — only a single register and the I/O can be both input and output at the same time. Totem pole I/O have a direction register that specifies the port pin direction and it is always in that configuration unless the direction is explicitlychanged.

  • Does not require a command byte. The simplicity of one register (no need for the pointer register or, technically, the command byte) is an advantage in some embedded systems where every byte counts because of memory or bandwidth limitations.


Testing I/O Port Output

There is only one register to control four possibilities of the port pin: Input HIGH, input
LOW, output HIGH, or output LOW.

  • Input HIGH: The master needs to write 1 to the register to set the port as an input mode if the device is not in the default power-on condition. The master reads the register to check the input status. If the external source pulls the port pin up to VDD or drives logic 1, then the master will read the value of 1.

  • Input LOW: The master needs to write 1 to the register to set the port to input mode if the device is not in the default power-on condition. The master reads the register to check the input status. If the external source pulls the port pin down to VSS or drives logic 0, which sinks the weak 100uA current source, then the master will read the value of 0.

  • Output HIGH: The master writes 1 to the register. There is an additional ‘accelerator’ or strong pull-up current when the master sets the port HIGH. The additional strong pull-up is only active during the HIGH time of the acknowledge clock cycle. This accelerator current helps the port’s 100uA current source make a faster rising edge into a heavily loaded output, but only at the start of the acknowledge clock cycle to avoid bus contention if an external signal is pulling the port LOW to VSS/driving the port with logic 0 at the same time. After the half clock cycle there is only the 100uA current source to hold the port HIGH.
  • Output LOW: The master writes 0 to the register. There is a strong current sink transistor that holds the port pin LOW. A large current may flow into the port, which could potentially damage the part if the master writes a 0 to the register and an external source is pulling the port HIGH at the same time.

Simple quasi-bidirectional I/O example


    In our case, to light-up our LED's we will use the last option, Output LOW!


  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. Main Program

i2c_init()

-- Direct port bit by bit set
setPortdata(0)   -- All OFF  
setPortdata(1)   -- P0 - ON     
setPortdata(2)   -- P1 - ON     
setPortdata(4)   -- P2 - ON
setPortdata(8)   -- P3 - ON
setPortdata(16)  -- P4 - ON  
setPortdata(32)  -- P5 - ON
setPortdata(64)  -- P6 - ON
setPortdata(128) -- P7 - ON 

-- mixed bit set - set 2 or more bits at the same time. Do not exceed max sink current!
setPortdata(3) -- P0 & P1 - ON 


-- test autoincrement bit
i=1
tmr.alarm( 0, 200, 1, function()
  print(i)
  setPortdata(i)
  i=i+i
  if i>128 then i=1
  end
end)

tmr.stop(0)


-- cycle visual step-by-step, from left-to-right
port={1,2,4,8,128,64,32,16}
i=1
tmr.alarm( 0, 100, 1, function()
  print(port[i])
  setPortdata(port[i])
  i=i+1
  if i>8 then i=1
  end
end)


-- Knight Rider style test - anybody remember about Knight Rider  KITT car?:)
port={1,2,4,8,128,64,32,16}
i=1
c=1
tmr.alarm( 0, 75, 1, function()
  --print(port[i])
  setPortdata(port[i])
  if c<8 then i=i+1
  else
      if c>14 then
                   c=1
                   i=2
       else
            i=i-1
       end
  end
  --print(port[i].."  i="..i.."  c="..c)
  c=c+1
end)


That's all for today, next time, PCF8574 Input, and a nice 4x4 Matrix Keyboard example :)





Thursday, December 10, 2015

ESP8266 nEXT EVO - Analog extension Board - P2



     This is Part 2 of the ESP8266 nEXT EVO Analog Extension Board (AN1)


   In this part we will talk a bit about the nEXT Bus I²C protocol  and we will start also a longer, multi-part discussion about testing procedures and software programming for the AN1 Board devices and functions (I/O Expansion port, Temperature, ADC, DAC, Voltage measurements,etc.






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.  
-------------------------------------------------------------------------------------------------------------------------






 Today topic

  First let's have a very quick look at the nEXT bus protocol: I²C
 
 What is I²C?

     I²C (Inter-Integrated Circuit), pronounced I-squared-C, is a multi-master, multi-slave, single-ended, two-wired serial bus - SDA (data line) and SCL (clock line) -  invented by Philips Semiconductor (now NXP Semiconductors). It is typically used for attaching lower-speed peripheral ICs to processors and microcontrollers.


Features of the I2C-bus:

  • Only two bus lines are required; a serial data line (SDA) and a serial clock line (SCL).
  • Each device connected to the bus is software addressable by a unique address and  simple master/slave relationships exist at all times; masters can operate as master-transmitters or as master-receivers.
  • It is a true multi-master bus including collision detection and arbitration to prevent data corruption if two or more masters simultaneously initiate data transfer.
  • Serial, 8-bit oriented, bidirectional data transfers can be made at up to 100 kbit/s in the Standard-mode, up to 400 kbit/s in the Fast-mode, up to 1 Mbit/s in Fast-mode Plus, or up to 3.4 Mbit/s in the High-speed mode.
  • Serial, 8-bit oriented, unidirectional data transfers up to 5 Mbit/s in Ultra Fast-mode
  • On-chip filtering rejects spikes on the bus data line to preserve data integrity.
  • The number of ICs that can be connected to the same bus is limited only by a maximum bus capacitance. More capacitance may be allowed under some conditions.

   SDA and SCL signals

    Both SDA and SCL are bidirectional lines, connected to a positive supply voltage via a current-source or pull-up resistor:






   When the bus is free, both lines are HIGH. The output stages of devices connected to the bus must have an open-drain or open-collector to perform the wired-AND function. Data on the I²C-bus can be transferred at rates of up to 100 kbit/s in the Standard-mode, up to 400 kbit/s in the Fast-mode, up to 1 Mbit/s in Fast-mode Plus, or up to 3.4 Mbit/s in the High-speed mode. The bus capacitance limits the number of interfaces connected to the bus.

For a single master application, the master’s SCL output can be a push-pull driver design if there are no devices on the bus which would stretch the clock.


   SDA and SCL logic levels

    Due to the variety of different technology devices (CMOS, NMOS, bipolar) that can be connected to the I²C-bus, the levels of the logical ‘0’ (LOW) and ‘1’ (HIGH) are not fixed and depend on the associated level of VDD.

Input reference levels are set as 30 % and 70 % of VDD; VIL is 0.3VDD and VIH is 0.7VDD.

Timing Diagram for F/S-mode devices on the I²C-bus


   Some legacy device input levels were fixed at VIL= 1.5 V and VIH= 3.0 V, but all new devices require this 30 %/70 % specification.


   For a more deep and extensive I²C protocol undestanding ( Data Validity, START and STOP conditions, Byte format, Acknowledge (ACK) and Not Acknowledge (NACK), Clock synchronisation, etc,etc please take a look at the official NXP I²C  Protocol datasheet.  I²C protocol it is quite a serious separate topic to discuss :).

   Before going further with our main topic I will insist only one one more thing, as it looks it creates a lot of confusion sometime:

     The slave address and R/W Bit (7 bit mode)


    Data transfers follow the format shown in the picture below:

A full data transfer


     After the START condition (S), a slave address is sent. This address is seven bits long followed by an eighth bit which is a data direction bit (R/W) — a ‘zero’ indicates a transmission (WRITE), a ‘one’ indicates a request for data (READ):

The first byte after the START procedure



      A data transfer is always terminated by a STOP condition (P) generated by the master.
However, if a master still wishes to communicate on the bus, it can generate a repeated START condition (Sr) and address another slave without first generating a STOP condition. Various combinations of read/write formats are then possible within such a transfer:

  • Master-transmitter transmits to slave-receiver. The transfer direction is not changed and the slave receiver acknowledges each byte:


  • Master reads slave immediately after first byte. At the moment of the first acknowledge, the master-transmitter becomes a master-receiver and the slave-receiver becomes a slave-transmitter. This first acknowledge is still generated by the slave. The master generates subsequent acknowledges. The STOP condition is generated by the master, which sends a not-acknowledge (A) just before the STOP condition:
 

  • Combined format. During a change of direction within a transfer, the START condition and the slave address are both repeated, but with the R/W bit reversed. If a master-receiver sends a repeated START condition, it sends a not-acknowledge (A) just before the repeated START condition:



  

         Now, after a very brief (very!) I²C protocol presentation, let's go back to our Analog Extension Board:

   
ESP8266 nEXT EVO + AN-1 Boards

 
  First thing that we want to do, after the AN-1 Board is properly soldered, cleaned, visual inspection OK, etc, will be to tests it and validate it as a proper working Board.


 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. Depending on how to you choose you socket type, you can install it on TOP or Bottom of the ESP8266 nEXT EVO Board :

ESP8266 nEXT EVO Board + Analog Extension Board AN1


   And, if you want, you can continue stacking them on the nEXT Bus, with a SSD1306 OLED Display, for example:

ESP8266 nEXT EVO + AN1 + SSD1306 OLED Display




   Software implementation

    By default, on the AN-1 Board you should have the following available devices:
  • PCF8574 8Bit I/O Extension port  - at address 0x20
  • LM75 Temperature sensor             - at address 0x48
  • MCP4728 - 4x12Bit DAC             - at address 0x60
  • MCP3421 - 18Bit ADC                 - at address 0x68
   When also connected, the SSD1306 OLED Display will be available at 0x3C


   A fist step in testing the AN-1 available functions and devices will be to scan the nEXT I²C Bus and see if all the existing ones are alive and responding to the I²C Master requests. Also will list any new added devices, if alive.


1. Find device function

function find_dev(i2c_id, dev_addr)
     i2c.start(i2c_id)
     c=i2c.address(i2c_id, dev_addr ,i2c.TRANSMITTER)
     i2c.stop(i2c_id)
     return c
end

2. Scan Bus for devices function

 function scanbus()
    i2c.setup(id,sda,scl,i2c.SLOW)
    for i=1,127 do
       if find_dev(id, i)==true then
            if i==32 then
            print("- PCF8574 8Bit I/O Extension port - found at address 0x"..string.format("%02X",i).." -> "..i)
            else
            if i==72 then
            print("- LM75 Temperature sensor         - found at address 0x"..string.format("%02X",i).." -> "..i)
            else
            if i==96 then
            print("- MCP4728 - 4x12Bit DAC           - found at address 0x"..string.format("%02X",i).." -> "..i)
            else
            if i==104 then
            print("- MCP3421 - 18Bit ADC             - found at address 0x"..string.format("%02X",i).." -> "..i)
            else
            if i==60 then
            print("- SSD1306 OLED Display            - found at address 0x"..string.format("%02X",i).." -> "..i)
            else
            print("- NEW UNREGISTERED DEVICE         - found at address 0x"..string.format("%02X",i).." -> "..i)
            end
          end
        end
       end
       end
       end
       tmr.wdclr()
   end
end


 3. MAIN Scanbus Program

id=0
sda=2
scl=1
scanbus()


Next time we will continue with deeper testing and programming for each AN-1 available device/function.



Tuesday, December 8, 2015

Mailbag !! SSD1306 OLED Display for ESP8266 nEXT EVO Board





 


   I think everybody knows already the popular  SSD1306 0.96" OLED Display available all over the place for a very good price:

SSD1306 0.96" I2C OLED Display


  My first option for a Alphanumeric Display still remain the ST7032i LCD one, but as looking also for a Graphical Display option found this SSD1306 OLED as a easy to use one with the CBDB nEXT EVO Board.

It is a very good choice because:

 -  From the Hardware point of view, as been a Display with a native I2C interface, it is very easy to connect thru the CBDB EVO nEXT Bus (no extra wires, PSU, etc)

 -  From the Software point of view, even more good news:
  • if you want to use NodeMCU in your projects, a driver for SSD1306 is already there, based on the u8glib library.
  • if you want your Arduino IDE also the u8glib driver is there!
  • Full Datasheet available, so you can also implement your own driver very easy.



  The SSD1306 is a 128x64 Dot Matrix Driver/Controller manufactured by Solomon Systech.
Controller Power Supply for IC Logic is between 1.65V to 3.3V so we have here a 3.3V compatible device, no need for any kind of logical levels converter.

   I will not insist too much about the SSD1306 controller, just one thing that might worth to explain a bit, as it looks that it is creating a lot of confusion for many people:

If you look on the back of the module you can see a jumper option for the Display I2C address: 0x7A or 0x78, with default selected on 0x78:




   If you will try to use it like that, with the addresses from above, it will not work, as many people has already found it.

   Why?

  The I2C communication interface consists of slave address with set bit SA0, I2C-bus data signal SDA (SDAOUT/D2 for output and SDAIN/D1 for input) and I2C-bus clock signal SCL (D0). Both the data and clock signals must be connected to pull-up resistors. 

  SSD1306 has to recognize the slave address before transmitting or receiving any information by the
I2C-bus. The device will respond to the slave address following by the slave address bit (“SA0” bit)
and the read/write select bit (“R/W#” bit) with the following byte format:


b7 b6 b5 b4 b3 b2 b1     b0
 0   1  1   1   1   0   SA0  R/W#


 “SA0” bit provides an extension bit for the slave address.
Either “0111100” or “0111101”, can be selected as the slave address of SSD1306.
D/C# pin acts as SA0 for slave address selection, and actually this is a Address Select pin from the above.

So, if we take the 7 Bit I2C address representation from above we will have:

0 1 1 1 1 0 0  - > 0x3C (default)
OR
0 1 1 1 1 0 1   - > 0x3D (alternate)


The 8th bit is the “R/W#” bit and is used to determine the operation mode of the I2C-bus interface:

 - R/W#=1 - readmode.
 - R/W#=0 - write mode.

  For more details please take a look at the SSD1306 Datasheet




What we will need:


Connection with the nEXT EVO Board is very easy, as SSD1306 connector is fully compatible with the nEXT connector:

Connecting SSD1306 Display to the CBDB nEXT EVO Board


 No wires, no hasle, a nice and compact unit:






Software implementation

 As the driver is already available for this type of Display, for simple projects no driver implemetantion is needed, just few functions to make it running:

  1. Display initialisation
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()           -- use it for rotate display
end

2. Print Text routine
str1="ESP8266-Projects.com"
str2="Hello World!!"

function PrintText()
  disp:drawStr(5, 10, str1)
  disp:drawStr(20, 20, str2)
end


3. Print on Display function
function print_OLED()
   disp:firstPage()
   repeat
     PrintText()
     disp:drawCircle(64, 47, 14)
     disp:drawFrame(2,2,126,62)
   until disp:nextPage() == false
end

4. Main program
str1="ESP8266-Projects.com"
str2="Hello World!!"

init_OLED(2,1)
print_OLED()

5. Drawing animation 
function draw_tst()
 for r=1, 31 do
   disp:firstPage()
   repeat
     disp:drawCircle(64, 32, r)
   until disp:nextPage() == false
   tmr.wdclr()
 end

 for r=30, 1, -1 do
   disp:firstPage()
   repeat
     disp:drawCircle(64, 32, r)
   until disp:nextPage() == false
   tmr.wdclr()
 end

print_OLED() end