вторник, 20 мая 2014 г.

Arduino. Работаем с часами DS3231 по i2C через Wire.

Копнул более основательно часики.

Для начала запустил сканер устройств на шине i2C. Рекомендую сохранить этот скетч в свою библиотеку, чтобы он был под руками - очень полезная штука!

Сканер обнаруживает два устройства на шине i2C по следующим адресам:
1. 0x57 - это адрес микросхемы памяти AT24C32
2. 0x68 - адрес микросхемы DS3231SN

Cтоит учитывать - сканер возвращает адреса в формате шестнадцатеричной системы. Т.е в привычном нам десятичном формате получим адреса 87 и 104. Нас интересует адрес 0x68 (104). Сделал себе первую пометочку.

Следующий шаг - скачать даташит к микросхеме. Найти его можно, например, по этой ссылке
Читаю, радуюсь большому количеству английских букв и разнообразных таблиц. :)

Самое интересное началось на странице 11, где присутствует следующая таблица:

Figure 1. Timekeeing Registers

Addressbit 7
MSB
bit 6bit 5bit 4bit 3bit 2bit 1bit 0
LSB
FunctionRange
00H010 secondssecondsSeconds00-59
01H010 minutesminutesMinutes00-59
02H012/24AM/PM
10 hour
10 hourhourHours1-12+AM/PM
00-23
03H00000dayDay1-7
04H0010 datedateDate00-31
05HCentury0010 monthmonthMonth/Century01-12+Century
06H10 yearyearYear00-99
07HA1M110 secondssecondsAlarm 1
seconds
00-59
08HA1M210 minutesminutesAlarm 1
minutes
00-59
09HA1M312/24AM/PM
10 hour
10 hourhourAlarm 1
hours
1-12+AM/PM
00-23
0AHA1M4DY/DT10 datedayAlarm 1 day1-7
dateAlarm 1 date1-31
0BHA2M210 minutesminutesAlarm 2 minutes00-59
0CHA2M312/24AM/PM
10 hour
10 hourhourAlarm 2 hours1-12+AM/PM
00-23
0DHA2M4DY/DT10 datedayAlarm 2 day1-7
dateAlarm 2 date1-31
0EHEOSCBBSQWCONVRS2RS1INTCNA2IEA1IEControl---
0FHOSF000EN32kHzBSYA2FA1FControl/status---
10HSIGNDATADATADATADATADATADATADATAAgign Offset---
11HSIGNDATADATADATADATADATADATADATAMSB of Temp---
12HDATADATA000000LSB of Temp---
Рекомендую все-таки скачать даташит - там есть такие замечательные отметочки в оригинальной таблице - крышечки. Как я понял, обозначают, какое состояние бита отвечает за состояние AM/PM или DY/DT, например. В моей таблице этого нет.

Первоначально интерес тут представляют строки с 00H до 06H и c 0EH до 12H. Блок с алармами (07H-0DH) пока пропущу - им займусь позже.

Далее приведен скетч, работающий напрямую с данными на DS3231.

#include <Wire.h>
 #define DS3231_I2C_ADDRESS 0x68
  
 byte seconds, minutes, hours, day, date, month, year;
 char weekDay[4];  
 byte tMSB, tLSB;
 float temp3231;
  
 void setup()
 {
   Wire.begin();
   Serial.begin(9600);
   //set control register to output square wave on pin 3 at 1Hz
   Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
   Wire.write(0x0E); //выставляемся в 14й байт 
   Wire.write(B00000000); //сбрасываем контрольные регистры 
   Wire.write(B10001000); //выставляем 1 на статус OSF и En32kHz
   Wire.endTransmission();
 }
  
 void loop() {    
   watchConsole(); //читаем консоль. Если нажата "T" читаем из консоли следующий 7 байт и устанавливаем время.    
   get3231Date(); //получаем данные 

          //и потом их выводим через Serial.print()    
   Serial.print(weekDay); Serial.print(", "); Serial.print(month, DEC); Serial.print("/"); Serial.print(date, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print(" - ");
   Serial.print(hours, DEC); Serial.print(":"); Serial.print(minutes, DEC); Serial.print(":"); Serial.print(seconds, DEC);    
   Serial.print("   Temperature: "); Serial.print(get3231Temp());Serial.print("; ");
          Serial.println(get3231Register(0x0F)); //получаем и выводим статусовый регистр
   delay(1000);
 }
  
 // Convert normal decimal numbers to binary coded decimal
 byte decToBcd(byte val)
 {
   return ( (val/10*16) + (val%10) );
 }
  
 void watchConsole()
 {
   if (Serial.available()) {      // Look for char in serial queue and process if found
     if (Serial.read() == 84) {      //If command = "T" Set Date
       set3231Date();
       get3231Date();
       Serial.println(" ");
     }
   }
 }
    
 void set3231Date()
 {
 //T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year)
 //T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99)
 //Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209
  
   seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. 
   minutes = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   hours   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   day     = (byte) (Serial.read() - 48);
   date    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   month   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   year    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));

   Wire.beginTransmission(DS3231_I2C_ADDRESS); //начали сессию
   Wire.write(0x00); //поставляемся в нулевой байт для чтения данных
   Wire.write(decToBcd(seconds));
   Wire.write(decToBcd(minutes));
   Wire.write(decToBcd(hours));
   Wire.write(decToBcd(day));
   Wire.write(decToBcd(date));
   Wire.write(decToBcd(month));
   Wire.write(decToBcd(year));
   Wire.endTransmission();//закрыли сессию
 }
  
 byte get3231Register(byte regNo) {
   // send request to receive data starting at register regNo
   Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
   Wire.write(regNo); // start at register regNo
   Wire.endTransmission();
   Wire.requestFrom(DS3231_I2C_ADDRESS, 1); // request one byte
   if(Wire.available()) return  Wire.read();
 }
  
 void set3231Register(byte regNo, byte value) {
   Wire.beginTransmission(DS3231_I2C_ADDRESS);
   Wire.write(regNo);
   Wire.write(value);
   Wire.endTransmission();
 }
  
 void get3231Date()
 {
   // send request to receive data starting at register 0
   Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
   Wire.write(0x00); // start at register 0
   Wire.endTransmission();
   Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes
  
   if(Wire.available()) {
     seconds = Wire.read(); // get seconds
     minutes = Wire.read(); // get minutes
     hours   = Wire.read();   // get hours
     day     = Wire.read();
     date    = Wire.read();
     month   = Wire.read(); //temp month
     year    = Wire.read();

//Конвертация происходит очень просто - битовым оператором & мы очищаем МЛАДШИЕ (0,1,2,3) четыре бита , сдвигаем все вправо на 4 позиции (очищенные биты
//вылезут при этом с левой стороны и результат умножаем на 10, т.к. в старших битах хранится информация о десятках секунд.
//После этого битовым оператором & очищаем СТАРШИЕ (4,5,6,7)четыре бита и получаем секунды. Затем суммируем десятки секунд и единиц.
     seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111));
     minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111));
     hours   = (((hours & B00110000)>>4)*10 + (hours & B00001111)); 
     day     = (day & B00000111); // 1-7
     date    = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31
     month   = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow
     year    = (((year & B11110000)>>4)*10 + (year & B00001111));
   }
   else {
     //oh noes, no data!
   }
    
   switch (day) {     
     case 1:
       strcpy(weekDay, "Mon");
       break;
     case 2:
       strcpy(weekDay, "Tue");
       break;
     case 3:
       strcpy(weekDay, "Wed");
       break;
     case 4:
       strcpy(weekDay, "Thu");
       break;
     case 5:
       strcpy(weekDay, "Fri");
       break;
     case 6:
       strcpy(weekDay, "Sat");
       break;
            case 7:
       strcpy(weekDay, "Sun");
       break;
   }
 }
  
 float get3231Temp()
 {
   //temp registers (11h-12h) get updated automatically every 64s
   Wire.beginTransmission(DS3231_I2C_ADDRESS);
   Wire.write(0x11);
   Wire.endTransmission();
   Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
    
   if(Wire.available()) {
     tMSB = Wire.read(); //2's complement int portion
     tLSB = Wire.read(); //fraction portion      
     temp3231 = (tMSB & B01111111); //do 2's math on Tmsb
     temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8
   }
   else {
     //oh noes, no data!
   }
      
   return temp3231;
 }

Вот как-то так. Непонятно пока, что произойдет при совпадении текущего времени с установленным в байтах 07h-0Ah и в 0Bh-0Dh, но этим займусь завтра.


1 комментарий: