- Войдите или зарегистрируйтесь, чтобы оставлять комментарии
TM32F4VE LCD ili9341 DS3231 Keil
Всем привет!
В этой статье рассмотрим как подключить микросхему DS3231 (I2C realtime clock (RTC)) к отладочной плате STM32F4VE. Микросхема DS3231 очень популярна и расписывать все ее достоинства и не достатки я не буду. Единственное, что покажу - это схема стандартного подключения микросхемы из документации.
Микросхема DS3231
Схема включения
Итак, что подключить микросхему нам необходимо:
1. Микросхема DS3231
2. Два подтягивающих сопротивления.
3. Батарейка
4. Конденсатор на питание микросхемы
Китайские производители давно продают готовые платы с распаянной микросхемой. В моем распоряжении есть вот такие
Отпаял батарейку т.к. со временем она разрядилась. Можно подключить любую на 3V. Например CR2032 . В принципе батарейка для проверок не нужна. Просто когда вы снимите напряжение счетчик времени остановиться и запустится когда подадите напряжение питания.
Плата минимум. Кварца нет (он внутри). Термокомпенсация у микросхемы внутри ... Супер ... то, что нужно.
Итак приступим..
Подключение электрическое к плате выполняем следующим образом:
Подключим DA3231 к STM32F4VE по шине I2C под номером 2
По схеме STM32F4VE
PB10 - I2C_SCL
PB11 - I2C_SDA
На плате DS3231 это
Контакт D - он же sDA -> подключаем соответственно к PB11
Контакт C - он же sCL -> подключаем соответственно к PB10
+ подключаем к 3V3 можно к штырям на плате STM32F4VE
- подключаем к GND можно к штырям на плате STM32F4VE
Провода подключили - переходим в среду программирования 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
Ура!! Ура! Ура!
У нас все получилось. Конечно может возникнуть вопрос - зачем использовать дополнительные микросхемы такие как DS3231 (ведь это RTC) когда можно использовать встроенный RTC в микроконтроллер STM302 F407VE? ......
Во вложении файлы system_stm32f4xx.c для правильной настройки частоты шины и скрин экрана - скачать
Если есть вопросы или необходим исходник проекта из статьи - пишите по адресу stm32@stm32res.ru или Website feedback