STM32F4VE LCD ili9341 DS3231 Keil

Опубликовано stm32 - чт, 04/30/2020 - 11:57

TM32F4VE LCD ili9341 DS3231 Keil 

Всем привет!

В этой статье рассмотрим как подключить микросхему DS3231 (I2C realtime clock (RTC)) к  отладочной плате STM32F4VE.  Микросхема DS3231  очень популярна и расписывать все ее достоинства и не достатки я не буду. Единственное, что покажу - это схема стандартного подключения микросхемы из документации.

Микросхема DS3231

d1_3

Схема включения

 

d1_2  

 

Итак, что подключить микросхему нам необходимо:

1. Микросхема DS3231

2. Два подтягивающих сопротивления.

3. Батарейка 

4. Конденсатор на питание микросхемы

Китайские производители давно продают готовые платы с распаянной микросхемой. В моем распоряжении есть вот такие 

d1_4

Отпаял батарейку т.к. со временем она разрядилась. Можно подключить любую на 3V.  Например CR2032 .  В принципе батарейка для проверок не нужна. Просто когда вы снимите напряжение счетчик времени остановиться и запустится когда подадите напряжение питания.  

Плата минимум. Кварца нет (он внутри). Термокомпенсация у микросхемы внутри ...  Супер ... то, что нужно.

Итак приступим..

Подключение электрическое к плате выполняем следующим образом:

Подключим DA3231  к  STM32F4VE  по шине I2C под номером 2

По схеме STM32F4VE

 PB10 - I2C_SCL

 PB11 - I2C_SDA

d1_5

На плате DS3231 это

Контакт D - он же sDA  -> подключаем соответственно к  PB11

Контакт C - он же sCL  -> подключаем соответственно к PB10

+ подключаем к 3V3 можно к штырям на плате STM32F4VE

подключаем к GND можно к штырям на плате STM32F4VE

d1_6

 

Провода подключили -  переходим в среду программирования  Keil. 

Расписывать все, что можно сделать я не буду. Рассмотрим две основные функции 

1. Чтение даты и времени из DS3231 

2. Установка даты и времени в DS3231  

Откроем проект который мы создавали в статье STM32F4VE LCD ili9341 16bit Keil  и добавим в него следующие файлы

ds3231.c

**************************************************************************************************************************

#ifndef __DS3231_H
#define __DS3231_H

#include "stm32f4xx.h"                  // Device header
 
typedef struct 
    {
    char Seconds;   // Секунды 
    char Minutes;   // Минуты
    char Hour;      // Часы 
    char DayOfWeek; // Дни недели 1-7
    char Date;      // Дни 1-31
    char Month;     // Месяц
    char Year;      // Год 0-99
} DS3231_DateTime;

void ds3231_init(void);   // Инициализируем I2C
void DS3231_SetDateTime(I2C_TypeDef* I2Cx,  DS3231_DateTime *DS3231_Date); // Установка времени и даты в DS3231
void DS3231_GetDateTime(I2C_TypeDef* I2Cx,  DS3231_DateTime *DS3231_Date); // Считывание времени и даты из DS3231

#endif
 

************************************************************************************************************************

ds3231.c

***************************************************************************************************************************

#include "ds3231.h"
#include "delay.h"

#include "stm32f4xx_i2c.h"


void ds3231_init(void) // Инициализируем I2C
{
 
       GPIO_InitTypeDef I2C_user;
       I2C_InitTypeDef i2c;
       
       RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);       // Включим тактирование I2C 
       RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  // Включим тактирование порта B
    
                                                                                                                                    // I2C2  PB10->SCL and PB11->SDA 
       I2C_user.GPIO_Pin =  GPIO_Pin_10 | GPIO_Pin_11;
       I2C_user.GPIO_Mode = GPIO_Mode_AF;
       I2C_user.GPIO_Speed = GPIO_Speed_50MHz;
       I2C_user.GPIO_OType = GPIO_OType_OD;
       I2C_user.GPIO_PuPd = GPIO_PuPd_UP ;
       GPIO_Init(GPIOB, &I2C_user);
        
       GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2);  
       GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2);
       
       I2C_DeInit(I2C2);
        
       i2c.I2C_ClockSpeed = 400000;
       i2c.I2C_Mode= I2C_Mode_I2C;
       i2c.I2C_DutyCycle = I2C_DutyCycle_16_9;
       i2c.I2C_OwnAddress1 = 0x00;    
       
       i2c.I2C_Ack = I2C_Ack_Enable;
       i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
       I2C_Init(I2C2, &i2c);
         
       I2C_Cmd(I2C2, ENABLE);
       I2C_AcknowledgeConfig(I2C2, ENABLE);

}

void DS3231_SendData(I2C_TypeDef* I2Cx, uint8_t data)
{
    I2C_SendData(I2Cx, data);
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
};

uint8_t DS3231_ReceiveData(I2C_TypeDef* I2Cx)
{
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED));
    return I2C_ReceiveData(I2Cx);
};

void DS3231_SendAddressWrite(I2C_TypeDef* I2Cx) 
{
    I2C_Send7bitAddress(I2Cx, 0xD0, I2C_Direction_Transmitter);
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
};

void DS3231_SendAddressRead(I2C_TypeDef* I2Cx)
{
    I2C_Send7bitAddress(I2Cx, 0xD0, I2C_Direction_Receiver);
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
};

void DS3231_GenerateStart(I2C_TypeDef* I2Cx) 
{
    I2C_GenerateSTART(I2Cx, ENABLE);
    while(!I2C_GetFlagStatus(I2Cx, I2C_FLAG_SB));
};
void DS3231_GenerateStop(I2C_TypeDef* I2Cx)
{
    I2C_GenerateSTOP(I2Cx, ENABLE);
    while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_STOPF));
};

uint8_t DS3231_DecToBCD(uint8_t value)   // Функция конвертации из DEC в BCD (DS3231 понимает только в BCD)
{
    return ((value / 10) << 4) + (value % 10);// convert decimal to BCD
};
uint8_t DS3231_BCDToDec(uint8_t value)  // Функция конвертации из BCD в DEC (Для вывода на LCD)
{
    return ((value >> 4) * 10) + (value & 0x0F);// convert BCD(Binary Coded Decimal) to Decimal
};

void DS3231_GoToAddress(I2C_TypeDef* I2Cx, uint8_t AddressByte)
{
    while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));  
    I2C_AcknowledgeConfig(I2Cx, ENABLE);

    DS3231_GenerateStart(I2Cx);
    DS3231_SendAddressWrite(I2Cx);
    DS3231_SendData(I2Cx, AddressByte);
    DS3231_GenerateStop(I2Cx);
};

void DS3231_SetDateTime(I2C_TypeDef* I2Cx,  DS3231_DateTime *DS3231_Date)
{
    while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));    // Проверяем шину на занятость если 1 - то общения не будет!! Это можно увидеть в                                                                                                                              // отладчике.
    I2C_AcknowledgeConfig(I2Cx, ENABLE);
    DS3231_GenerateStart(I2Cx);
    DS3231_SendAddressWrite(I2Cx);
    DS3231_SendData(I2Cx, 0x00);
    DS3231_SendData(I2Cx, DS3231_DecToBCD(DS3231_Date->Seconds));// конвертируем и отправляем значение секунд
    DS3231_SendData(I2Cx, DS3231_DecToBCD(DS3231_Date->Minutes));// конвертируем и отправляем значение минут
    DS3231_SendData(I2Cx, DS3231_DecToBCD(DS3231_Date->Hour));// конвертируем и отправляем значение часов
    DS3231_SendData(I2Cx, DS3231_DecToBCD(DS3231_Date->DayOfWeek));// конвертируем и отправляем значение дня недели
    DS3231_SendData(I2Cx, DS3231_DecToBCD(DS3231_Date->Date));// конвертируем и отправляем значение дня 
    DS3231_SendData(I2Cx, DS3231_DecToBCD(DS3231_Date->Month));// конвертируем и отправляем значение месяца
    DS3231_SendData(I2Cx, DS3231_DecToBCD(DS3231_Date->Year));// конвертируем и отправляем значение года

    DS3231_GenerateStop(I2Cx);
    delay_us(20);                     // Без этой паузы будет зависание при считывании и записи
};
void DS3231_GetDateTime(I2C_TypeDef* I2Cx,  DS3231_DateTime *DS3231_Date)
{
    while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
    I2C_AcknowledgeConfig(I2Cx, ENABLE);
    DS3231_GoToAddress(I2Cx, 0x00);

    DS3231_GenerateStart(I2Cx);

    DS3231_SendAddressRead(I2Cx);

    DS3231_Date->Seconds = DS3231_BCDToDec(DS3231_ReceiveData(I2Cx));// Принимаем и конвертируем значение секунд

    DS3231_Date->Minutes = DS3231_BCDToDec(DS3231_ReceiveData(I2Cx));// Принимаем и конвертируем значение минут

    DS3231_Date->Hour = DS3231_BCDToDec(DS3231_ReceiveData(I2Cx));// Принимаем и конвертируем значение часов

    DS3231_Date->DayOfWeek = DS3231_BCDToDec(DS3231_ReceiveData(I2Cx)); // Принимаем и конвертируем значение дня недели

    DS3231_Date->Date = DS3231_BCDToDec(DS3231_ReceiveData(I2Cx)); // Принимаем и конвертируем значение дня

    DS3231_Date->Month = DS3231_BCDToDec(DS3231_ReceiveData(I2Cx)); // Принимаем и конвертируем значение месяца

    DS3231_Date->Year = DS3231_BCDToDec(DS3231_ReceiveData(I2Cx)); // Принимаем и конвертируем значение года

    I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);

    I2C_AcknowledgeConfig(I2Cx, DISABLE);
    
    DS3231_GenerateStop(I2Cx);
   delay_us(20);                                                                           // Без этой паузы будет зависание при считывании и записи
    I2C_AcknowledgeConfig(I2Cx, ENABLE);

};

*******************************************************************************************************************************************

Выделил все функции которые необходимы для работы. Самые главные из них 

void DS3231_SetDateTime(I2C_TypeDef* I2Cx,  DS3231_DateTime *DS3231_Date)

void DS3231_GetDateTime(I2C_TypeDef* I2Cx,  DS3231_DateTime *DS3231_Date)

Все остальные это функции которые повторяются и в связи с этим, чтобы их не писать постоянно - просто выведены в отдельные.

Перейдем и скорректируем файл main.c. Я выделил то что добавил.

main.c

**************************************************************************************************************************************

#include "main.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ili9341.h"
#include "delay.h"
#include "ds3231.h"    //Подключим  созданные файлы

char str1[20];   // Переменные для вывода на LCD 
char str2[20]; // Переменные для вывода на LCD 

DS3231_DateTime ds3231dt; 

int main(void)
{    
  SysTick_Config(SystemCoreClock / 1000);
    //  Настраиваем порты
    GPIO_init();
    //  Настраиваем кнопки установленные на плате    
    button_ini();
    //
  ds3231_init();

  // Установка времени в DS3231 при первом запуске  
  // Если необходимо выставить время - раскоментируем на один запуск 
        
  //    ds3231dt.Hour =    17;
  //    ds3231dt.Minutes = 53;
 //    ds3231dt.Seconds = 40;
    
 //    ds3231dt.Date = 28;
 //    ds3231dt.Month = 4; 
 //    ds3231dt.Year = 20;
  //   ds3231dt.DayOfWeek = 7;

  
  //DS3231_SetDateTime(I2C2, &dsds3231dt);// set time and date
    
//**************************************************    
    
    LCD_Configuration();
        //
    lcdInit();
    lcdSetOrientation(LCD_ORIENTATION_LANDSCAPE);
    lcdFillRGB(COLOR_WHITE);
    LCD_SetBackLight(90);  //Включаем подсветку на нужную яркость. 0 -max 100- min
    // Пишем текст сверху экрана
    lcdSetTextFont(&Font24);
    lcdSetTextColor(COLOR_BLACK, COLOR_WHITE);
    lcdSetCursor(40, 5);   // xy
    lcdPrintf("www.stm32res.ru");
    lcdSetTextFont(&Font20);
    lcdSetTextColor(COLOR_BLACK, COLOR_WHITE);    
    //

    ds3231_init();   // Инициализируем 
  
    lcdSetTextFont(&Font24);
    lcdSetTextColor(COLOR_BLUE, COLOR_WHITE);    
    lcdSetCursor(120, 50);   // xy
   lcdPrintf("DS3231");
     
    lcdSetTextFont(&Font20);
    lcdSetTextColor(COLOR_BLACK, COLOR_WHITE);    
    
    lcdSetCursor(30, 100);   // xy
    lcdPrintf("TIME ");

    lcdSetCursor(30, 120);   // xy
    lcdPrintf("DAY  ");

    lcdSetCursor(30, 140);   // xy
    lcdPrintf("MONTH  ");

    lcdSetCursor(30, 160);   // xy
    lcdPrintf("YEAR  ");

    lcdSetCursor(30, 180);   // xy
    lcdPrintf("DAYofWEEK  ");

    lcdSetTextColor(COLOR_BLUE, COLOR_WHITE);    

while (1) {
                      // Читаем данные из DS3231
                      DS3231_GetDateTime(I2C2, &ds3231dt);  // Получаем время
                      delay_ms(400);
                       lcdSetCursor(180, 100);   // xy
                       sprintf(str1,"%i:%02d:%02d",ds3231dt.Hour,ds3231dt.Minutes,ds3231dt.Seconds);    //выводим время        
                       lcdPrintf(str1);
    

                        lcdSetCursor(180, 120);   // xy                
                        sprintf(str2, "%i",ds3231dt.Date);//Выводим день
                        lcdPrintf(str2);
         
         
                        lcdSetCursor(180, 140);   // xy                
                        sprintf(str2, "%i",ds3231dt.Month);//Выводим месяц
                        lcdPrintf(str2);
    
    
                        lcdSetCursor(180, 160);   // xy                
                        sprintf(str2, "%i",ds3231dt.Year);//Выводим год
                        lcdPrintf(str2);
    
    
                        lcdSetCursor(180, 180);   // xy                
                        sprintf(str2, "%i",ds3231dt.DayOfWeek);//Выводим день недели
                        lcdPrintf(str2);
         
         // Для визуализации будем включать светодиод когда значение секунды будет "1" и гаснуть когда "0"

       // Выход PA7 должен быть инициализирован соответствующим образом как ВЫХОД

           
                          if (ds3231dt.Seconds & 0x01)
                         {
                         GPIO_ResetBits(GPIOA, GPIO_Pin_7); //Подаем «0» на PA - эта команда включит светодиод (особенность платы)
                         }
                         else
                         {
                         GPIO_SetBits(GPIOA, GPIO_Pin_7); //Подаем «1» на PA  - эта команда выключит (особенность платы)
                          }
          
        }

    }

********************************************************************************************************************************

Собираем проект, исправляем ошибки и загружаем программу в микроконтроллер  STM32F4VE.  После перезапуска на LCD должно появится то, что мы считываем из DS3231

d1_8

 

Ура!! Ура! Ура! 

У нас все получилось.  Конечно может возникнуть вопрос - зачем использовать дополнительные микросхемы такие как DS3231 (ведь это RTC) когда можно использовать встроенный RTC в микроконтроллер STM302 F407VE?  ...... 

Во вложении файлы  system_stm32f4xx.c для правильной настройки частоты шины и скрин экрана - скачать 

Если есть вопросы или необходим исходник проекта  из статьи - пишите по адресу stm32@stm32res.ru или Website feedback

Яндекс.Метрика