STM32F4 и RTC. Часть 2. Часы с будильником на STM32.

Всем доброго времени суток! И сегодня мы снова поговорим о модуле RTC микроконтроллеров STM32. А точнее мы разберем его функции более подробно. Напомню, что в предыдущей статье мы начали знакомиться с часами реального времени и написали программу для их инициализации и запуска (статья тут).

Так вот, сегодня мы сделаем часы с будильником. Я для этого по традиции буду использовать плату STM32F4 Discovery. Но для начала давайте разберемся с теоретической частью. И для начала поковыряемся в файлах библиотеки Standard Peripheral Library, относящихся к модулю RTC. Как обычно, в SPL мы находим два нужных нам файла – stm32f4xx_rtc.c и stm32f4xx_rtc.h. В первой реализованы функции на все случаи жизни, но мы поговорим о них немного позже. А сначала рассмотрим определения структур из заголовочного файла библиотеки. И первая из них:

typedef struct
{
	uint32_t RTC_HourFormat;
	uint32_t RTC_AsynchPrediv;
	uint32_t RTC_SynchPrediv;
}RTC_InitTypeDef;

Эта структура, как понятно из ее названия, необходима при инициализации часов реального времени. И более того мы ее уже использовали в предыдущей статье про RTC 🙂 Далее идет структура, которая является по большому счету базовой для всех функций, реализованных в SPL:

typedef struct
{
	uint8_t RTC_Hours;
	uint8_t RTC_Minutes;
	uint8_t RTC_Seconds;
	uint8_t RTC_H12;
}RTC_TimeTypeDef;

Эта структура собственно позволяет задать время – то есть часы, минуты и секунды. Так же тут можно задать формат отображения времени 12 или 24 часовой. В принципе в SPL снабжено соответствующими комментариями, кроме того название любой переменной однозначно позволяет понять для чего она будет использоваться, поэтому тут и пояснять особо нечего. Тем не менее продолжим:

typedef struct
{
	uint8_t RTC_WeekDay;
	uint8_t RTC_Month;
	uint8_t RTC_Date;
	uint8_t RTC_Year;
}RTC_DateTypeDef;

Структура RTC_DateTypeDef аналогична предыдущей структуре RTC_TimeTypeDef, разница лишь в том, что она позволяет задать день недели, месяц, дату и год. И последнее:

typedef struct
{
	RTC_TimeTypeDef RTC_AlarmTime;
	uint32_t RTC_AlarmMask;
	uint32_t RTC_AlarmDateWeekDaySel;
	uint8_t RTC_AlarmDateWeekDay;
}RTC_AlarmTypeDef;

Эту структуру мы будем использовать для настройки будильника. И, пожалуй, стоит обсудить ее поля поподробнее.

Первое поле – RTC_AlarmTime – представляет из себя уже упомянутую структуру RTC_TimeTypeDef, предназначенную для установки времени пробуждения, то есть времени срабатывания нашего будильника. Поле RTC_AlarmDateWeekDaySel может иметь следующие значения:

RTC_AlarmDateWeekDaySel_Date
RTC_AlarmDateWeekDaySel_WeekDay

При выборе первого из этих значений будильник будет срабатывать в определенный день месяца, то есть при наступлении даты, которую мы передадим в другое поле этой структуры, а именно в поле RTC_AlarmDateWeekDay. Если же мы выбрали значение RTC_AlarmDateWeekDaySel_WeekDay, то будильник будет срабатывать в определенный день недели. Нужный день недели мы записываем все в то же поле RTC_AlarmDateWeekDay.

И, наконец, осталось рассмотреть RTC_AlarmMask. Назначение этого поля следующее. Если мы хотим настроить будильник так, чтобы он срабатывал в определенное время, независимо от даты и дня недели, то необходимо задать:

RTC_AlarmMask = RTC_AlarmMask_DateWeekDay

Все оказалось довольно-таки просто 🙂 Можно двигаться дальше. Не будем отдельно обсуждать каждую из функций, реализованных в файле stm32f4xx_rtc.c, лучше напишем программу, в которой будем использовать некоторые из них. Соответственно в процессе написания кода разберемся как и для чего нужно использовать ту или иную функцию. А пример будет такой – установим определенное время и запустим часы, а кроме того установим будильник и создадим обработчик прерывания для него. В общем, пора бы уже перейти к делу!

Для начала, как обычно, подключаем все файлы, которые нам понадобятся:

/***************************************************************************************/
#include "stm32f4xx.h"
#include "stm32f4xx_exti.h"
#include "stm32f4xx_rtc.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_pwr.h"
#include "stm32f4xx_conf.h"
#include "system_stm32f4xx.h"
#include "misc.h"


/***************************************************************************************/

И сразу же объявим переменные:

/***************************************************************************************/
RTC_InitTypeDef rtc;
RTC_TimeTypeDef time;
RTC_TimeTypeDef alarmTime;
RTC_AlarmTypeDef alarm;
EXTI_InitTypeDef exti;
NVIC_InitTypeDef NVIC_InitStructure;
int i;


/***************************************************************************************/

Для начала работы с часами реального времени необходимо произвести их инициализацию:

/***************************************************************************************/
void initRTC()
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);

	PWR_BackupAccessCmd(ENABLE);

	RCC_BackupResetCmd(ENABLE);
	RCC_BackupResetCmd(DISABLE);

	RCC_LSICmd(ENABLE);
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
	RCC_RTCCLKCmd(ENABLE);

	RTC_StructInit(&rtc);
	rtc.RTC_HourFormat = RTC_HourFormat_24;
	rtc.RTC_SynchPrediv = 0x7FFF;
	RTC_Init(&rtc);
}


/***************************************************************************************/

В принципе тут все то же самое, что и в предыдущей статье про модуль RTC, поэтому останавливаться на этом не будем. Итак, функция main()… Сразу же вызываем функцию глобального разрешения прерываний и нашу функцию инициализации:

__enable_irq();
initRTC();

Готово! Идем дальше. Установим время – пусть это будет например 20:00:00:

time.RTC_H12 = RTC_HourFormat_24;
time.RTC_Hours = 20;
time.RTC_Minutes = 00;
time.RTC_Seconds = 00;

RTC_SetTime(RTC_Format_BIN, &time);

Для установки времени используем функцию RTC_SetTime(), куда в качестве аргументов передаем уже упомянутую структуру с нашим значением времени и используемый формат (возможно два варианта – RTC_Format_BIN и RTC_Format_BCD).

Далее необходимо настроить 17 линию внешних прерываний, поскольку именно она подключена к событию срабатывания будильника. Кроме того сразу же настроим и соответствующее прерывание RTC_Alarm_IRQn:

EXTI_ClearITPendingBit(EXTI_Line17);
exti.EXTI_Line = EXTI_Line17;
exti.EXTI_Mode = EXTI_Mode_Interrupt;
exti.EXTI_Trigger = EXTI_Trigger_Rising;
exti.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti);

NVIC_InitStructure.NVIC_IRQChannel = RTC_Alarm_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

Теперь пришло время установить время включения будильника, пусть будет 20:00:15. То есть через 15 секунд после включения контроллера должен сработать будильник, и программа улетит в обработчик прерывания:

RTC_AlarmStructInit(&alarm);
alarmTime.RTC_H12 = RTC_HourFormat_24;
alarmTime.RTC_Hours = 20;
alarmTime.RTC_Minutes = 00;
alarmTime.RTC_Seconds = 15;

alarm.RTC_AlarmTime = alarmTime;
alarm.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay;

RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &alarm);

У STM32F4 есть Alarm_A и Alarm_B, мы остановим свой выбор на первом из будильников. В общем то теперь нам осталось немного:

RTC_OutputConfig(RTC_Output_AlarmA, RTC_OutputPolarity_High);
RTC_ITConfig(RTC_IT_ALRA, ENABLE);
RTC_AlarmCmd(RTC_Alarm_A, ENABLE);
RTC_ClearFlag(RTC_FLAG_ALRAF);

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

while(1)
{
}

Казалось бы все, но нет! Мы совсем забыли про обработчик прерывания:

void RTC_Alarm_IRQHandler()
{
	if(RTC_GetITStatus(RTC_IT_ALRA) != RESET)
	{
		RTC_ClearITPendingBit(RTC_IT_ALRA);
		EXTI_ClearITPendingBit(EXTI_Line17);
	}
}

Здесь мы проверим, вызвано ли прерывание срабатыванием будильника Alarm_A и очистим соответствующие флаги, вот и все!

Зашиваем программу в микроконтроллер и теперь в режиме отладки можем проверить, что же вышло. Запускаем, ждем заветные 15 секунд, и вот он результат:

RTC

Как видите программа ушла на обработку прерывания как раз через 15 секунд после начала выполнения. Таким образом, мы с вами добились того, чего и хотели! Собственно, на этом все на сегодня, с установкой времени и будильника мы полностью разобрались 🙂 Если возникнут какие-нибудь вопросы – обязательно пишите в комментариях к статье.

Поделиться!

Подписаться
Уведомление о
guest
38 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
Михаил
Михаил
6 лет назад

Огромное Вам спасибо за статьи по stm! Подскажите пожалуйста куда посмотреть для поднятия полного RS-232? На stm32f3discovery

Илья
Илья
Reply to  Aveal
5 лет назад

Подскажите как это вывести на lcd 1602 В келе у меня все работает часы идут. в while прописываю
hd44780_clear();
hd44780_printf(“%0.2d:%0.2d:%0.2d”,time.RTC_Hours, time.RTC_Minutes, time.RTC_Seconds );
delay_ms(5000);

По идее каждые 20 сек информация на дисплее должна обновляться но там постоянно 20:00:00 и ничего не меняется.

Сергей
Сергей
Reply to  Илья
3 лет назад

Для тех, кто прочитает это когда-нибудь: time.RTC_Hours etc.. используется для установки времени в RTC! Она не показывает текущее время!

Богдан
Богдан
6 лет назад

и снова здравствуйте )
столкнулся со следующей проблемой:
работал в кейле с STM32 Discovery все было хорошо, прошивалось, работала отладка.
купил STM32F4 Discovery, поключил, кейл пишет: “No ST-LINK detected”
При чем “STM32 ST-LINK Utility” с платкой прекрасно работает, а кейл не видит.
я так понимаю дело в драйверах ?

Богдан
Богдан
6 лет назад

в настройках ничего не трогаю, подключаю STM32 Discovery, кейл его видит:
http://s019.radikal.ru/i602/1401/5f/e2a5d201129d.png
потом, опять же, ничего не меняя в настройках подключаю STM32F4 Discovery, кейл его уже не видит:
http://i018.radikal.ru/1401/c3/400089da3dfb.png
если потом снова подключить STM32 Discovery, то все работает.
сначала думал что плата отладочная неисправна, но «STM32 ST-LINK Utility» с ней работает, уже прошивал.
но вот еще какая особенность, вот так выглядит диспетчер устройств если подключить F1:
http://i047.radikal.ru/1401/05/4c0d12323228.jpg
а вот так если подключить F4:
http://s003.radikal.ru/i202/1401/7b/527e65e07a65.jpg

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

Богдан
Богдан
6 лет назад

об этом моменте я знаю… к сожалению это не то (

Geka
Geka
6 лет назад

Спасибо за Ваши статьи. По ним изучаю STM32. Вопрос по этой статье – как получить данные по времени и дате? Я так понимаю RTC_GetTime() ? Можно какой-нибудь пример – не врубаюсь в структуру процедуру.

Geka
Geka
6 лет назад

Спасибо. Все работает!

Дмитрий
Дмитрий
6 лет назад

Здравствуйте, уважаемые. В общем собрал я проект с RTC под stm32f103, и ничего, как водится не работает. При инициализации виснет, тактирование не начинается. Подскажите, а может сначала нужно альтернативную функцию ножек OSC32, ну вроде вот этого что-то прописать: GPIO_Mode_AF_PP. В общем, подскажите, люди добрые

RusikOk
RusikOk
Reply to  Дмитрий
3 лет назад

у f103 rtc реализованы по другому и поэтому этот код работать не будет.

Михаил
Михаил
6 лет назад

Разве кейл поддерживает эмуляцию переферии stm32? Я так понимаю вы пользуете кейл 5… как вы прсматриваете регистры ?

Михаил
Михаил
6 лет назад

Да это я знаю… когда я пользовал LPC2478 то тоже отлаживал в симуляторе многие вещи… кейл прекрасно симулирует этот камень.. и всю почти периферию. а вот stm32f4 не хочет симулировать. Нет у него таких возможностей и в планах тоже.

beginner
beginner
5 лет назад

Подскажите,как запустить RTC на f103

beginner
beginner
5 лет назад

Попробовал, определений типов таких же не нашел, буду штудировать reference manual

RusikOk
RusikOk
3 лет назад

как-то глупо каждый раз выставлять часы после выключения. там же я надеюсь есть своя батарейка для rtc

RusikOk
RusikOk
3 лет назад

тоесть получается если использовать прерывание от будильника то нужно пожертвовать одной ножкой контроллера и не использовать ее?

RusikOk
RusikOk
Reply to  Aveal
3 лет назад

ну если при срабатывании будильник выставляет высокий/низкий уровень на ногу контроллера которая настроена на вход с прерыванием. то нога стает недоступна для нормального использования

RusikOk
RusikOk
Reply to  Aveal
3 лет назад

цытата: “17 линию внешних прерываний”
хотя и правда. я не подумал, что в каждом порту по 16 линий. с толку сбило “внешнее прерывание”
тогда получается что все круто и без костылей) спасибо

Egor
3 лет назад

Добрый день! Подскажите
Работаю с f103ret6
Мне необходимо подключить 10 внешних EXTI прерывания
у меня есть
EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn, EXTI9_5_IRQn, EXTI15_10_IRQn
Могу ли я использовать к примеру EXTI9_5_IRQn так
EXTIInit(EXTI_Line6, ENABLE, EXTI_Mode_Interrupt, EXTI_Trigger_Falling, EXTI9_5_IRQn, GPIO_PortSourceGPIOB, GPIO_PinSource6);
EXTIInit(EXTI_Line7, ENABLE, EXTI_Mode_Interrupt, EXTI_Trigger_Falling, EXTI9_5_IRQn, GPIO_PortSourceGPIOB, GPIO_PinSource7);

EXTIInit // полная настройка внешнего прерывания

При использовании к примеру EXTI3_IRQn, EXTI4_IRQn всё работает
EXTIInit(EXTI_Line3, ENABLE, EXTI_Mode_Interrupt, EXTI_Trigger_Falling, EXTI3_IRQn, GPIO_PortSourceGPIOA, GPIO_PinSource3);
EXTIInit(EXTI_Line4, ENABLE, EXTI_Mode_Interrupt, EXTI_Trigger_Falling, EXTI4_IRQn, GPIO_PortSourceGPIOC, GPIO_PinSource4);

А в случае с EXTI начиная с 5-го нет

Egor
Reply to  Egor
3 лет назад

Если всё таки нужна расшифровка EXTIInit

/* Инициализация внешних прерываний
*/
#define EXTIInit(line, linecmd, mode, trigger, IRQn, port, pin) \
GPIO_EXTILineConfig(port, pin); \
EXTI_InitStructure.EXTI_Line = line; \
EXTI_InitStructure.EXTI_Mode = mode; \
EXTI_InitStructure.EXTI_Trigger = trigger; \
EXTI_InitStructure.EXTI_LineCmd = linecmd; \
EXTI_Init( & EXTI_InitStructure); \
NVIC_InitStructure.NVIC_IRQChannel = IRQn; \
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F; \
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F; \
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; \
NVIC_Init( & NVIC_InitStructure);

Egor
Reply to  Egor
3 лет назад

Так нашёл ошибку там нужно что бы прерывание то же что бы называлось EXTI9_5_IRQHandler(void)

Но от этого не легче.
так как в регистрах при сработке того или другого прерывания взводятся оба EXTI->PR->PR6 = 1 и EXTI->PR->PR7 = 1
Следовательно я ни как не смогу их разделить?

Egor
Reply to  Egor
3 лет назад

Сможете подсказать как получить 10 самостоятельных внешних прерываний?

Ultrasat
3 лет назад

Странное дело. Используя HAL_RTC_GetTime() НЕ работает при обычном запуске (RESET от кнопки на плате), но работает если включить отладку Debug в Keil. Причем просто – Start Debug Session and Run.
А без отладки – не считает, хотя прошивку не меняешь вообще.

Alex
Alex
2 лет назад

Не знаю что делает EXTI17, но у меня на двух платах при таких настройках выгорела нога PC13. На ней висела кнопка с подтяжкой 10к на 3.3. После инита будильника она стала звониться на землю. Проверил на другом рабочем проце – тоже самое. Тупо выгорает пин.

Anton
Anton
2 лет назад

Добрый день! В статье код на разбит на несколько частей. А есть ли исходный файл со всем блоком подключения RTC?

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Август 2020
Пн Вт Ср Чт Пт Сб Вс
 12
3456789
10111213141516
17181920212223
24252627282930
31  

© 2013-2020 MicroTechnics.ru