wtorek, 20 czerwca 2017

[26] STM32F4 - RTC DS3231

Ten post chciałbym poświęcić na opisanie programowania zegara RTC DS3231. Jest to zegar czasu rzeczywistego wyprodukowany przez firmę MAXIM. Komunikacja odbywa się poprzez interfejs I2C.

[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]

Dokumentacja do układu jest do pobrania pod tym linkiem.

Cube Mx:


Program w CubeMx należy wyposażyć właściwie tylko w obsługę I2C w celu poprawnego odczytywania danych z układu RTC. Do prezentacji wyników może posłużyć USART bądź LCD.

Dla RTC wykorzystałem I2C1, natomiast dla prezentacji wyników USART2.


Konfiguracja zegarów:


Konfiguracja I2C:


Konfiguracja USART:


Przygotowanie programu:


Wpisywanie oraz odbieranie danych z układu:

  1. typedef enum{
  2.     I2C_Buf_Error = 0x00,
  3.     I2C_Buf_OK
  4. }Error_Stat;
  5. void I2C_WriteBuffer(I2C_HandleTypeDef handler, uint8_t address, uint8_t *buf, uint8_t buffer_sizee)
  6. {
  7.     while(HAL_I2C_Master_Transmit(&handler, (uint16_t) address, (uint8_t*)buf, (uint16_t) buffer_size, (uint32_t)500)!=HAL_OK)
  8.     {
  9.             if(HAL_I2C_GetError(&hi)!=HAL_I2C_ERROR_AF) {
  10.                 return I2C_Buf_Error;
  11.             }
  12.     }
  13.     return I2C_Buf_OK;
  14. }
  15. uint8_t I2C_ReadBuffer(I2C_HandleTypeDef handler, uint8_t address, uint8_t *buffer, uint8_t buffer_size)
  16. {
  17.     while(HAL_I2C_Master_Receive(&handler, (uint16_t) address, (uint8_t*)buffer, (uint16_t)buffer_size, (uint32_t)500)!=HAL_OK)
  18.     {
  19.             if(HAL_I2C_GetError(&hi)!=HAL_I2C_ERROR_AF){
  20.                 return I2C_Buf_Error;
  21.             }
  22.     }
  23.     return I2C_Buf_OK;
  24. }

Funkcje w zależności od statusu operacji zwracają odpowiednią wartość result albo Ok albo Error.

RTC rozpocznie odliczanie po podaniu do niego wartości 0. Wobec tego aby go zainicjalizować należy wysłać adres RTC czyli 0x0D po czym przesłać rejestr do którego chce się wprowadzać dane. Na końcu przesyłane są główne dane jakie należy wprowadzić do układu:

  1. /*
  2.     * Clear control register
  3. */
  4. uint8_t RTC_Init(void)
  5. {
  6.     uint8_t data[2] = {DS3231_Control_REG, 0x00};
  7.     I2C_WriteBuffer(hi2c1, DS3231_RTC_ADDR, data, sizeof(data));
  8. }

Konwersja danych następuje przy użyciu następujących funkcji. Dane z czujnika podawane są w formacie BCD:

  1. inline static uint8_t RTC_ConvertFromDec(uint8_t c){
  2.     return ((c>>4)*10+(0x0F&c));
  3. }
  4. //-----------------------------------------------
  5. inline static uint8_t RTC_ConvertFromBinDec(uint8_t c){
  6.     return ((c/10)<<4)|(c%10);
  7. }

Wybranie danych z bufora prezentuje się następująco:

  1. void getTimeAndDate(void)
  2. {
  3.     clearDS3231Buffer();    //Czyszczenie bufora
  4.    
  5.     I2C_WriteBuffer(hi2c1,(uint16_t)0xD0, rtcData, 1);
  6.     while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { }
  7.     I2C_ReadBuffer(hi2c1,(uint16_t)0xD0, rtcData, 7);
  8.    
  9.     DS3231_Time.year = rtcData[6];
  10.     DS3231_Time.year = RTC_ConvertFromDec(DS3231_Time.year);   
  11.    
  12.     DS3231_Time.month = rtcData[5];
  13.     DS3231_Time.month = RTC_ConvertFromDec(DS3231_Time.month);
  14.    
  15.     DS3231_Time.date = rtcData[4];
  16.     DS3231_Time.date = RTC_ConvertFromDec(DS3231_Time.date);
  17.    
  18.     DS3231_Time.day = rtcData[3];
  19.     DS3231_Time.day = RTC_ConvertFromDec(DS3231_Time.day);
  20.    
  21.     DS3231_Time.hours = rtcData[2];
  22.     DS3231_Time.hours = RTC_ConvertFromDec(DS3231_Time.hours);
  23.    
  24.     DS3231_Time.minutes = rtcData[1];
  25.     DS3231_Time.minutes = RTC_ConvertFromDec(DS3231_Time.minutes);
  26.    
  27.     DS3231_Time.seconds = rtcData[0];
  28.     DS3231_Time.seconds = RTC_ConvertFromDec(DS3231_Time.seconds);
  29.    
  30.     DS3231_Time.yearTab[0] = (char)((DS3231_Time.year/10)%10)+0x30;
  31.     DS3231_Time.yearTab[1] = (char)(DS3231_Time.year%10)+0x30;
  32.    
  33.     DS3231_Time.monthTab[0] = (char)(((DS3231_Time.month/10)%10)+0x30);
  34.     DS3231_Time.monthTab[1] = (char)((DS3231_Time.month%10)+0x30);
  35.    
  36.     DS3231_Time.dateTab[0] = (char)((DS3231_Time.date/10)%10)+0x30;
  37.     DS3231_Time.dateTab[1] = ((char)(DS3231_Time.date%10)+0x30);
  38.        
  39.     DS3231_Time.dayTab = ((char)(DS3231_Time.day%10)+0x30);
  40.    
  41.     DS3231_Time.hoursTab[0] = ((char)((DS3231_Time.hours/10)%10)+0x30);
  42.     DS3231_Time.hoursTab[1] = ((char)(DS3231_Time.hours%10)+0x30);
  43.    
  44.     DS3231_Time.minutesTab[0] = ((char)((DS3231_Time.minutes/10)%10)+0x30);
  45.     DS3231_Time.minutesTab[1] = ((char)(DS3231_Time.minutes%10)+0x30);
  46.    
  47.     DS3231_Time.secondsTab[0] = (char)((DS3231_Time.seconds/10)%10)+0x30;
  48.     DS3231_Time.secondsTab[1] = (char)(DS3231_Time.seconds%10)+0x30;
  49.    
  50.     PrintData(&DS3231_Time);
  51. }

Wypisanie danych na ekran wygląda następująco:

  1. static void PrintData(DS3231_Time_t *DS3231_Values)
  2. {
  3.     char toSend[30];
  4.     uint8_t calculateSize;
  5.     //------------------------------------------------------------------
  6.     sprintf(toSend, "Date: %c%c\r\n", DS3231_Time.dateTab[0],DS3231_Time.dateTab[1]);
  7.     for(uint8_t i = 0; i<30;i++){
  8.         if(toSend[i] == '\n'){
  9.             calculateSize = i;
  10.             break;
  11.         }
  12.     }
  13.     HAL_UART_Transmit(&huart2, (uint8_t*)toSend, calculateSize, 0xFFFF);
  14.     //------------------------------------------------------------------
  15.     sprintf(toSend, "Month: %c%c\r\n", DS3231_Time.monthTab[0],DS3231_Time.monthTab[1]);
  16.     for(uint8_t i = 0; i<30;i++){
  17.         if(toSend[i] == '\n'){
  18.             calculateSize = i;
  19.             break;
  20.         }
  21.     }
  22.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  23.     //------------------------------------------------------------------
  24.     sprintf(toSend, "Year: %c%c\r\n", DS3231_Time.yearTab[0],DS3231_Time.yearTab[1]);  
  25.     for(uint8_t i = 0; i<30;i++){
  26.         if(toSend[i] == '\n'){
  27.             calculateSize = i;
  28.             break;
  29.         }
  30.     }
  31.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  32.     //------------------------------------------------------------------
  33.     sprintf(toSend, "Day: %c\r\n",  DS3231_Time.dayTab);
  34.     for(uint8_t i = 0; i<30;i++){
  35.         if(toSend[i] == '\n'){
  36.             calculateSize = i;
  37.             break;
  38.         }
  39.     }
  40.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  41.     //------------------------------------------------------------------
  42.     sprintf(toSend, "Hour: %c%c\r\n", DS3231_Time.hoursTab[0],  DS3231_Time.hoursTab[1]);
  43.     for(uint8_t i = 0; i<30;i++){
  44.         if(toSend[i] == '\n'){
  45.             calculateSize = i;
  46.             break;
  47.         }
  48.     }
  49.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  50.     //------------------------------------------------------------------
  51.     sprintf(toSend, "Min: %c%c\r\n", DS3231_Time.minutesTab[0],DS3231_Time.minutesTab[1]);
  52.        
  53.     for(uint8_t i = 0; i<30;i++){
  54.         if(toSend[i] == '\n'){
  55.             calculateSize = i;
  56.             break;
  57.         }
  58.     }
  59.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  60.     //------------------------------------------------------------------
  61.     sprintf(toSend, "Sec: %c%c\r\n", DS3231_Time.secondsTab[0],DS3231_Time.secondsTab[1]); 
  62.     for(uint8_t i = 0; i<30;i++){
  63.         if(toSend[i] == '\n'){
  64.             calculateSize = i;
  65.             break;
  66.         }
  67.     }
  68.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  69. }

Data oraz czas są pobierane z układu, po czym są wprowadzane do zmiennej strukturalnej, po czym są wypisywane na ekran.

Inicjalizacja i czyszczenie bufora domyślnymi wartościami wygląda następująco:

  1. static uint8_t clearDS3231Buffer(void){
  2.     DS3231_Time.seconds = 0;
  3.     DS3231_Time.minutes = 0;
  4.     DS3231_Time.hours = 0;
  5.     DS3231_Time.day = 0;   
  6.     DS3231_Time.date = 0;
  7.     DS3231_Time.month = 0;
  8.     DS3231_Time.year = 0;
  9. }

Główna pętla programu wygląda następująco:

  1. int main(void)
  2. {
  3.   /* USER CODE BEGIN 1 */
  4.   uint8_t str1[]="USART Transmit\r\n";
  5.   /* USER CODE END 1 */
  6.   /* MCU Configuration----------------------------------------------------------*/
  7.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  8.   HAL_Init();
  9.   /* USER CODE BEGIN Init */
  10.   /* USER CODE END Init */
  11.   /* Configure the system clock */
  12.   SystemClock_Config();
  13.   /* USER CODE BEGIN SysInit */
  14.   /* USER CODE END SysInit */
  15.   /* Initialize all configured peripherals */
  16.   MX_GPIO_Init();
  17.   MX_USART2_UART_Init();
  18.   MX_I2C1_Init();
  19.   /* USER CODE BEGIN 2 */
  20.   HAL_UART_Transmit(&huart2,str1,16,0xFFFF);
  21.   /* USER CODE END 2 */
  22.   /* Infinite loop */
  23.   /* USER CODE BEGIN WHILE */
  24.   while (1)
  25.   {
  26.         /* USER CODE END WHILE */
  27.         /* USER CODE BEGIN 3 */
  28.         getTimeAndDate();
  29.         HAL_Delay(1000);
  30.   }
  31.   /* USER CODE END 3 */
  32. }

Plik konfiguracyje dla DS3231 oraz I2C. Na początku sd3231_i2c.c:

  1. #include "ds3231_i2c.h"
  2. //--------------------------------------
  3. extern I2C_HandleTypeDef hi2c1;
  4. extern UART_HandleTypeDef huart2;
  5. //--------------------------------------
  6. extern uint8_t rtcData[8];
  7. char str[100];
  8. //--------------------------------------
  9. typedef enum ErrorStat{
  10.     I2C_Buf_Error = 0x00,
  11.     I2C_Buf_OK = 0x01
  12. }Error_Stat_Typedef;
  13. typedef enum weekDay_Enum_Struct{
  14.     wdSunday = 0x01,
  15.     wdMonday = 0x02,
  16.     wdTuesday = 0x03,
  17.     wdWednesday = 0x04,
  18.     wdThursday = 0x05,
  19.     wdFriday = 0x06,
  20.     weSaturday = 0x07
  21. }weekDay_t;
  22. typedef struct{
  23.     uint8_t seconds;    /*Seconds 00-59 */
  24.         char secondsTab[2]; /* Contains coverted seconds data*/
  25.     uint8_t minutes;    /*Minutes 00-59 */
  26.         char minutesTab[2]; /* Contains coverted minutes data*/
  27.         uint8_t hours;      /* Hours 00-23 */
  28.     char hoursTab[2];       /* Contains coverted hours data*/
  29.         uint8_t day;        /* Day in a week 1-7 */
  30.     weekDay_t dayTab;       /* Contains coverted day data*/
  31.         uint8_t date;       /* Day in a month    1-31 */
  32.     char dateTab[2];        /* Contains coverted date data*/
  33.         uint8_t month;      /* Month 1-12 */
  34.         char monthTab[2];       /* Contains coverted month data*/
  35.         uint8_t year;       /* Year                00-99 */
  36.         char yearTab[2];        /* Contains coverted year data*/
  37. }DS3231_Time_t;
  38. //--------------------------------------
  39. DS3231_Time_t DS3231_Time;
  40. //--------------------------------------
  41. static uint8_t clearDS3231Buffer(void)
  42. {
  43.     DS3231_Time.seconds = 0;
  44.     DS3231_Time.minutes = 0;
  45.     DS3231_Time.hours = 0;
  46.     DS3231_Time.day = 0;   
  47.     DS3231_Time.date = 0;
  48.     DS3231_Time.month = 0;
  49.     DS3231_Time.year = 0;
  50. }
  51. //--------------------------------------
  52. uint8_t I2C_WriteBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t *buf, uint8_t sizebuf)
  53. {
  54.     while(HAL_I2C_Master_Transmit(&hi, (uint16_t)DEV_ADDR,(uint8_t*)buf,
  55.                 (uint16_t)sizebuf, (uint32_t)500)!= HAL_OK)
  56.     {
  57.         if (HAL_I2C_GetError(&hi) != HAL_I2C_ERROR_AF)
  58.         {
  59.             return I2C_Buf_Error;
  60.         }
  61.     }
  62.     return I2C_Buf_OK;
  63. }
  64. //--------------------------------------
  65. uint8_t I2C_ReadBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t *buf, uint8_t sizebuf)
  66. {
  67.     while(HAL_I2C_Master_Receive(&hi, (uint16_t)DEV_ADDR, (uint8_t*)buf,
  68.                 (uint16_t)sizebuf, (uint32_t)500)!= HAL_OK)
  69.     {
  70.         if (HAL_I2C_GetError(&hi) != HAL_I2C_ERROR_AF)
  71.         {  
  72.             return I2C_Buf_Error;
  73.         }
  74.     }
  75.     return I2C_Buf_OK;
  76. }
  77. //-----------------------------------------------
  78. inline static uint8_t RTC_ConvertFromDec(uint8_t c)
  79. {
  80.     return ((c>>4)*10+(0x0F&c));
  81. }
  82. //-----------------------------------------------
  83. inline static uint8_t RTC_ConvertFromBinDec(uint8_t c)
  84. {
  85.     return ((c/10)<<4)|(c%10);
  86. }
  87. //-----------------------------------------------
  88. static void PrintData(DS3231_Time_t *DS3231_Values)
  89. {
  90.     char toSend[30];
  91.     uint8_t calculateSize;
  92.     //------------------------------------------------------------------
  93.     sprintf(toSend, "Date: %c%c\r\n", DS3231_Time.dateTab[0],DS3231_Time.dateTab[1]);
  94.     for(uint8_t i = 0; i<30;i++){
  95.         if(toSend[i] == '\n'){
  96.             calculateSize = i;
  97.             break;
  98.         }
  99.     }
  100.     HAL_UART_Transmit(&huart2, (uint8_t*)toSend, calculateSize, 0xFFFF);
  101.     //------------------------------------------------------------------
  102.     sprintf(toSend, "Month: %c%c\r\n", DS3231_Time.monthTab[0],DS3231_Time.monthTab[1]);
  103.     for(uint8_t i = 0; i<30;i++){
  104.         if(toSend[i] == '\n'){
  105.             calculateSize = i;
  106.             break;
  107.         }
  108.     }
  109.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  110.     //------------------------------------------------------------------
  111.     sprintf(toSend, "Year: %c%c\r\n", DS3231_Time.yearTab[0],DS3231_Time.yearTab[1]);  
  112.     for(uint8_t i = 0; i<30;i++){
  113.         if(toSend[i] == '\n'){
  114.             calculateSize = i;
  115.             break;
  116.         }
  117.     }
  118.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  119.     //------------------------------------------------------------------
  120.     sprintf(toSend, "Day: %c\r\n",  DS3231_Time.dayTab);
  121.     for(uint8_t i = 0; i<30;i++){
  122.         if(toSend[i] == '\n'){
  123.             calculateSize = i;
  124.             break;
  125.         }
  126.     }
  127.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  128.     //------------------------------------------------------------------
  129.     sprintf(toSend, "Hour: %c%c\r\n", DS3231_Time.hoursTab[0],  DS3231_Time.hoursTab[1]);
  130.     for(uint8_t i = 0; i<30;i++){
  131.         if(toSend[i] == '\n'){
  132.             calculateSize = i;
  133.             break;
  134.         }
  135.     }
  136.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  137.     //------------------------------------------------------------------
  138.     sprintf(toSend, "Min: %c%c\r\n", DS3231_Time.minutesTab[0],DS3231_Time.minutesTab[1]);
  139.        
  140.     for(uint8_t i = 0; i<30;i++){
  141.         if(toSend[i] == '\n'){
  142.             calculateSize = i;
  143.             break;
  144.         }
  145.     }
  146.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  147.     //------------------------------------------------------------------
  148.     sprintf(toSend, "Sec: %c%c\r\n", DS3231_Time.secondsTab[0],DS3231_Time.secondsTab[1]); 
  149.     for(uint8_t i = 0; i<30;i++){
  150.         if(toSend[i] == '\n'){
  151.             calculateSize = i;
  152.             break;
  153.         }
  154.     }
  155.     HAL_UART_Transmit(&huart2,(uint8_t*)toSend,calculateSize,0xFFFF);
  156. }
  157. //-----------------------------------------------
  158. void getTimeAndDate(void)
  159. {
  160.     //clearDS3231Buffer();
  161.    
  162.     I2C_WriteBuffer(hi2c1, (uint16_t)0xD0, rtcData, 1);
  163.     while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { }
  164.     I2C_ReadBuffer(hi2c1, (uint16_t)0xD0, rtcData, 7);
  165.    
  166.     DS3231_Time.year = rtcData[6];
  167.     DS3231_Time.year = RTC_ConvertFromDec(DS3231_Time.year);   
  168.    
  169.     DS3231_Time.month = rtcData[5];
  170.     DS3231_Time.month = RTC_ConvertFromDec(DS3231_Time.month);
  171.    
  172.     DS3231_Time.date = rtcData[4];
  173.     DS3231_Time.date = RTC_ConvertFromDec(DS3231_Time.date);
  174.    
  175.     DS3231_Time.day = rtcData[3];
  176.     DS3231_Time.day = RTC_ConvertFromDec(DS3231_Time.day);
  177.    
  178.     DS3231_Time.hours = rtcData[2];
  179.     DS3231_Time.hours = RTC_ConvertFromDec(DS3231_Time.hours);
  180.    
  181.     DS3231_Time.minutes = rtcData[1];
  182.     DS3231_Time.minutes = RTC_ConvertFromDec(DS3231_Time.minutes);
  183.    
  184.     DS3231_Time.seconds = rtcData[0];
  185.     DS3231_Time.seconds = RTC_ConvertFromDec(DS3231_Time.seconds);
  186.    
  187.     DS3231_Time.yearTab[0] = (char)((DS3231_Time.year/10)%10)+0x30;
  188.     DS3231_Time.yearTab[1] = (char)(DS3231_Time.year%10)+0x30;
  189.    
  190.     DS3231_Time.monthTab[0] = (char)(((DS3231_Time.month/10)%10)+0x30);
  191.     DS3231_Time.monthTab[1] = (char)((DS3231_Time.month%10)+0x30);
  192.    
  193.     DS3231_Time.dateTab[0] = (char)((DS3231_Time.date/10)%10)+0x30;
  194.     DS3231_Time.dateTab[1] = ((char)(DS3231_Time.date%10)+0x30);
  195.        
  196.     DS3231_Time.dayTab = ((char)(DS3231_Time.day%10)+0x30);
  197.    
  198.     DS3231_Time.hoursTab[0] = ((char)((DS3231_Time.hours/10)%10)+0x30);
  199.     DS3231_Time.hoursTab[1] = ((char)(DS3231_Time.hours%10)+0x30);
  200.    
  201.     DS3231_Time.minutesTab[0] = ((char)((DS3231_Time.minutes/10)%10)+0x30);
  202.     DS3231_Time.minutesTab[1] = ((char)(DS3231_Time.minutes%10)+0x30);
  203.    
  204.     DS3231_Time.secondsTab[0] = (char)((DS3231_Time.seconds/10)%10)+0x30;
  205.     DS3231_Time.secondsTab[1] = (char)(DS3231_Time.seconds%10)+0x30;
  206.    
  207.     PrintData(&DS3231_Time);
  208. }

Plik .h wygląda następująco:

  1. #include "stm32f4xx_hal.h"
  2. #include "stm32f4xx_hal_i2c.h"
  3. #include <stdint.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. #include "main.h"
  7. //-----------------------------------------------
  8. #define DS3231_ADD  (uint16_t)0xD0
  9. #define DS3231_SECONDS_REGISTER 0x00
  10. #define DS3231_ALARM1_SECONDS_REGISTER 0x07
  11. #define DS3231_ALARM2_SECONDS_REGISTER 0x0B
  12. #define DS3231_TEMPERATURE_MSB_REGISTER 0x11
  13. #define DS3231_CONTROL_REGISTER 0x0F
  14. //-----------------------------------------------
  15. extern uint8_t rtcData[8];
  16. //-----------------------------------------------
  17. uint8_t I2C_WriteBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t *buf, uint8_t sizebuf);
  18. uint8_t I2C_ReadBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t *buf, uint8_t sizebuf);
  19. /* ======================= */
  20. void getTimeAndDate(void);
  21. //-----------------------------------------------