Showing posts with label Ds3231. Show all posts
Showing posts with label Ds3231. Show all posts

Tuesday, April 14, 2015

AT24C32 - I2C External EEPROM Data Looger




      As you remember from our previous article about DS3231 RTC Module, we have identified onboard an EEPROM chip, a 32k AT24C32 one. It is independent from the RTC circuit and conected on the I2C bus, perfect companion for a Data Logger System :)


AT24C32 EEPROM


     The AT24C32 provides 32,768 bits of serial electrically erasable and programmable read only memory (EEPROM) organized as 4096 words of 8 bits each. Might not sound too much but believe it or not you can log 6 months of data or even more on it depending on your application requests and how you organize your data logging. 

    For example if you save your data in 1byte, you will have enough for around 170 days or 24 weeks! With an added 16 extra location available for bulding data header/date/time/CRC/whatever your needs ask for. And if you still feel it to small, you can use anytime AT24C64, 64k size (8192 x 8), direct drop-in replacement!

     Also the device’s cascadable feature allows up to 8 devices to share a common I2C bus. The device is optimized for use in many industrial and commercial applications where low power and low voltage operation are essential. In addition, the entire family is available in 2.7V (2.7V to 5.5V) and 1.8V (1.8V to 5.5V)versions.


 
     FEATURES:

     • Low-Voltage and Standard-Voltage Operation
          –  2.7 (VCC = 2.7V to 5.5V)
          –  1.8 (VCC = 1.8V to 5.5V)
     • Low-Power Devices (ISB = 2μA at 5.5V) Available
      • Internally Organized 4096 x 8
      • 2-Wire Serial Interface
      • Schmitt Trigger, Filtered Inputs for Noise Suppression
      • Bidirectional Data Transfer Protocol
      • 100 kHz (1.8V, 2.5V, 2.7V) and 400 kHz (5V) Clock Rate
      • Write Protect Pin for Hardware Data Protection
      • 32-Byte Page Write Mode (Partial Page Writes Allowed)
     • Self-Timed Write Cycle (10 ms max)
     • High Reliability
          –  Endurance: 1 Million Write Cycles
          –  Data Retention: 100 Years
      • Automotive Grade and Extended Temperature Devices Available
      • 8-Pin JEDEC PDIP, 8-Pin JEDEC SOIC, 8-Pin EIAJ SOIC, and 8-pin TSSOP Packages


   For more details please see AT24C32 Datasheet

  

 What we will need:
  • CBDB Board
  • USB adapter (take a look on Part 1 for details how to connect them together)
  • DS3231 Module from previous article

    For programming and uploading the driver and the software we will continue to use the LuaUploader as before.




Driver implementation

   To be able to access and properly operate with any kind of memory devices we need at least 3 basic functions implemented: addressing, read and write. Plus the proper I2C/SPI/Whaterver bus communication initialisation, ofcourse.

1. Init I2C bus/interface:

        address = 0x50,                         -- A2, A1, A0 = 0
        id = 0


        init = function (self, sda, scl)
               self.id = 0
              i2c.setup(self.id, sda, scl, i2c.SLOW)
       end

 

   ADDRESSING:

   The 32K EEPROM requires an 8-bit device address word following a start condition
to enable the chip for a read or write operation.
It uses the three device address bits A2, A1, A0 to allow as many as eight
devices on the same bus. These bits must compare to their corresponding hardwired
input pins. The A2, A1, and A0 pins use an internal proprietary circuit that biases them
to a logic low condition if the pins are allowed to float.


   The eighth bit of the device address is the read/write operation select bit. A read operation
is initiated if this bit is high and a write operation is initiated if this bit is low.



2. WRITE Function

     A write operation requires two 8-bit data word addresses following the device address word and acknowledgment. Upon receipt of this address, the EEPROM  will again respond with a zero and then clock in the first 8-bit data word. Following receipt of the 8-bit data word, the EEPROM will output a zero and the addressing device, such as a microcontroller, must terminate the write sequence with a stop condition.

   At this time the EEPROM enters an internally-timed write cycle, tWR, to the
nonvolatile memory. All inputs are disabled during this write cycle and the EEPROM will
not respond until the write is complete


   write_EEPROM = function (self, devadr, memadr, edata)
       i = 1
       length = string.len(edata)
       adrh=bit.rshift(memadr, 8)
       adrl=bit.band(memadr,0xff)
       i2c.start(self.id)
       i2c.address(self.id, self.address, i2c.TRANSMITTER)
       i2c.write(self.id, adrh)
       i2c.write(self.id, adrl)
       --print(edata)                               --debug only
       --print(string.byte(edata,1))       
--debug only
       while i<=length do
          tmr.wdclr()
          i2c.write(self.id,string.byte(edata,i))
          i = i+1
       end
       i2c.stop(self.id)
   end



3. READ Function

    A random read requires a “dummy” byte write sequence to load in the data word address. Once the device address word and data word address are clocked in and acknowledged by the EEPROM, the microcontroller must generate another start condition.

  The microcontroller now initiates a current address read by sending a device address with the
read/write select bit high. The EEPROM acknowledges the device address and serially clocks
out the data word. The microcontroller does not respond with a zero but does generate a following
stop condition

       read_EEPROM = function (self, devadr, memadr, length)
            adrh=bit.rshift(memadr, 8)
            adrl=bit.band(memadr,0xff)
            i2c.start(self.id)
            i2c.address(self.id, self.address, i2c.TRANSMITTER)
            i2c.write(self.id, adrh)
            i2c.write(self.id, adrl)
            i2c.stop(self.id)
            i2c.start(self.id)
            i2c.address(self.id, self.address, i2c.RECEIVER)
            c=i2c.read(self.id, length)
            i2c.stop(self.id)
           print(c)
           return  c
      end




   For testing,  pack it together and save the code on ESP as 'eeprom.lua', restart ESP and run:

  require('eeprom')                                               -- call for new created AT24C32 Module Driver
  memadr=0x00                                                   -- let's read from begining
  sda, scl = 2, 1                                                    -- I2C pins setup
  edata="4.321 - Data from the EEPROM"        -- Data to write to EEPROM

  eeprom:init(sda,scl)                                   -- Init I2C
  eeprom:write_EEPROM(0x50,0,edata)     -- Write Data edata to EEPROM starting with address=0
  eeprom:read_EEPROM(0x50,0,28)           -- Read Data from EEPROM, address=0, length=28









 

Thursday, April 9, 2015

Mailbag Arrival !! DS3231 - I2C Real Time Clock Module





     If  you remember my article about PCF8563 Real Time Clock  this one was a looong avaited MailBox hit. Actually I didn't expect it to show anymore after so much time but miracles happening sometime :)

     The DS3231 is a extremely accurate I2C real-time clock (RTC) with an integrated temperature-compensated crystal oscillator (TCXO) and crystal.

     The device incorporates a battery input, and maintains accurate timekeeping when main power to the device is interrupted. The integration of the crystal resonator enhances the long-term accuracy of the device as well as reduces the piece-part count in a manufacturing line.

     The RTC maintains seconds, minutes, hours, day, date,month, and year information. The date at the end of the month is automatically adjusted for months with fewer than 31 days, including corrections for leap year. The clock operates in either the 24-hour or 12-hour format with an AM/PM indicator. 


      Two programmable time-of-day alarms and a programmable square-wave output are provided. Address and data are transferred serially through an I2C bidirectional bus.
 

      A precision temperature-compensated voltage reference and comparator circuit monitors the status of VCC to detect power failures, to provide a reset output, and to automatically switch to the backup supply when necessary. Additionally, the RST pin is monitored as a pushbutton input for generating a μP reset.

 
DS3231 Typical Operating Circuit


FEATURES :
  •  Highly Accurate RTC Completely Manages All Timekeeping Functions
            •   Real-Time Clock Counts Seconds, Minutes, Hours, Date of the Month, Month, Day of 
                   the   Week, and Year, with Leap-Year Compensation Valid Up to 2100
            •  Accuracy ±2ppm from 0°C to +40°C
            •  Accuracy ±3.5ppm from -40°C to +85°C
            •  Digital Temp Sensor Output: ±3°C Accuracy
            •  Register for Aging Trim
            •  Active-Low RST Output/Pushbutton Reset Debounce Input
            •  Two Time-of-Day Alarms
            •  Programmable Square-Wave Output Signal

  • Simple Serial Interface Connects to Most Microcontrollers
            •  Fast (400kHz) I2C Interface
  • Battery-Backup Input for Continuous Timekeeping
            •  Low Power Operation Extends Battery-Backup Run Time
            •  3.3V Operation

  • Operating Temperature Ranges: Commercial (0°C to +70°C) and Industrial (-40°C to +85°C)
  • Underwriters Laboratories® (UL) Recognized
     
 For more details, please see  DS3231 Datasheet



DS3231 Module:

 
Top


Bottom

Close-up

    As you can see from the pictures above, a nice compact module, with backup battery holder on the back (CR2032). Also you can find on the same module sharing the I2C bus a 24C32N EEPROM ( 32k - 4096 x 8) , totally independent from the RTC circuit. A nice addon for a possible WIFI Datalogger system, what do you think about? :)

  32k might sound a small amount of data storage but depending on your application requests might be more than enough for collecting 6 moths or a year data, even more. As I know already from your requests that this subject is of big interest, we will elaborate more about this one in the next article, for now let's go back to our fancy RTC :)


Clock and Calendar - Theory of operation

   The time and calendar information is obtained by reading the appropriate register bytes. The time and calendar data are set or initialized by writing the appropriate register bytes. The contents of the time and calendar registers are in the binary-coded decimal (BCD) format.
 

    The DS3231 can be run in either 12-hour or 24-hour mode. Bit 6 of the hours register is defined as the 12- or 24-hour mode select bit. When high, the 12-hour mode is selected. In the 12-hour mode, bit 5 is the AM/PM bit with logic-high being PM. In the 24-hour mode, bit 5 is the 20-hour bit (20–23 hours).
 

The century bit (bit 7 of the month register) is toggled when the years register overflows from 99 to 00.
 

   The day-of-week register increments at midnight. Values that correspond to the day of week are user-defined but must be sequential (i.e., if 1 equals Sunday, then 2 equals Monday, and so on). Illogical time and date entries result in undefined operation.
 

   When reading or writing the time and date registers, secondary (user) buffers are used to prevent errors when the internal registers update. When reading the time and date registers, the user buffers are synchronized to the internal registers on any START and when the register pointer rolls over to zero. The time information is read from these secondary registers, while the clock continues to run. This eliminates the need to reread the registers in case the main registers update during a read.
 

   The countdown chain is reset whenever the seconds register is written. Write transfers occur on the acknowledge from the DS3231. Once the countdown chain is reset, to avoid rollover issues the remaining time and date registers must be written within 1 second.

   The 1Hz square-wave output, if enabled, transitions high 500ms after the seconds data transfer, provided the oscillator is already running. 



   What we will need:
  • CBDB Board
  • USB adapter (take a look on Part 1 for details how to connect them together)
  • DS3231 Module from above

    For programming and uploading the driver and the software we will continue to use the LuaUploader as before.



Driver implementation

    As DS3231 has a I2C compatible compatible interface, driver building it following more or less the same  process  as before for I2C devices.



1. Data conversion functions:

  1.1 Decimal to BCD:

        function decToBcd(val)
             local d = string.format("%d",tonumber(val / 10))
             local d1 = tonumber(d*10)
             local d2 = val - d1
            return tonumber(d*16+d2)
         end

  

1.2  BCD to Decimal:

      function bcdToDec(val)
           local hl=bit.rshift(val, 4)
           local hh=bit.band(val,0xf)
          local hr = string.format("%d%d", hl, hh)
          return string.format("%d%d", hl, hh)
     end


 
2. Init I2C bus/interface:

        address = 0x51, -- A2, A1, A0 = 0
        id = 0


        init = function (self, sda, scl)
               self.id = 0
              i2c.setup(self.id, sda, scl, i2c.SLOW)
       end

 

3. ReadTime function:

      readTime = function (self)
       wkd = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }
       i2c.start(self.id)
       i2c.address(self.id, self.address, i2c.TRANSMITTER)
       i2c.write(self.id, 0x00)
       i2c.stop(self.id)
       i2c.start(self.id)
       i2c.address(self.id, self.address, i2c.RECEIVER)
       c=i2c.read(self.id, 7)
       i2c.stop(self.id)
       return  bcdToDec(string.byte(c,1)),
               bcdToDec(string.byte(c,2)),
               bcdToDec(string.byte(c,3)),
               wkd[tonumber(bcdToDec(string.byte(c,4)))],
               bcdToDec(string.byte(c,5)),
               bcdToDec(string.byte(c,6)),
               bcdToDec(string.byte(c,7))
   end



4. SetTime function:

   setTime = function (self, second, minute, hour, day, date, month, year)
       i2c.start(self.id)
       i2c.address(self.id, self.address, i2c.TRANSMITTER)
       i2c.write(self.id, 0x00)
       i2c.write(self.id, decToBcd(second))
       i2c.write(self.id, decToBcd(minute))
       i2c.write(self.id, decToBcd(hour))
       i2c.write(self.id, decToBcd(day))
       i2c.write(self.id, decToBcd(date))
       i2c.write(self.id, decToBcd(month))
       i2c.write(self.id, decToBcd(year))
       i2c.stop(self.id)
   end



For testing,  pack it together and save the code on ESP as 'ds3231.lua', restart ESP and run:

-- Set Initial Time and Date
require('ds3231')                                -- call for new created DS3231 Module Driver
sda, scl = 2, 1                                      --  declare your I2C interface PIN's
ds3231:init(sda, scl)                           -- initialize I2C Bus
 ds3231:setTime(5,08,12,3,6,04,15)   -- setTime(s,min,hour,weekday,day,month, year)
-- get Time and Date
require('ds3231')
sda, scl = 2, 1
ds3231:init(sda, scl)

s, m, h, d, dt, mn, y = ds3231:readTime()
=string.format("%s - %s/%s/20%s",d, dt, mn, y)
=string.format(" %s:%s:%s", h, m, s)


 
First run test



 5.  Read Time & Date - Print on LCD


   require('st7032i')
   sda, scl = 2, 1
   st7032i:init_i2c(sda, scl)
   st7032i:init_LCD()

   Time_LCD = function()
       s, m, h, d, dt, mn, y = ds3231:readTime()
       date = string.format("%s",dt).."/"..string.format("%s",mn).."/"..string.format("20%s",y)
       st7032i:lcd_print(3,1,date)
       time = string.format("%s",h)..":"..string.format("%s",m)..":"..string.format("%s",s)
       st7032i:lcd_print(4,2,time)
   end

   tmr.alarm(0, 1000, 1, function() Time_LCD() end)  -- set call Time_LCD function Timer