Top.Mail.Ru

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

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

Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также небольшая подборка по таймерам из нового курса:

Так вот, сегодня мы сделаем часы с будильником. Я для этого по традиции буду использовать плату STM32F4 Discovery. Но для начала давайте разберемся с теоретической частью. И для начала поковыряемся в файлах библиотеки SPL, относящихся к модулю RTC. Как обычно, находим два нужных нам файла - 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 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Михаил
Михаил
10 лет назад

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

Илья
Илья
Ответ на комментарий  Aveal
8 лет назад

Подскажите как это вывести на 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 и ничего не меняется.

Сергей
Сергей
Ответ на комментарий  Илья
7 лет назад

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

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

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

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

в настройках ничего не трогаю, подключаю 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 не знаю.

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

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

Geka
Geka
10 лет назад

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

Geka
Geka
10 лет назад

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

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

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

RusikOk
RusikOk
Ответ на комментарий  Дмитрий
7 лет назад

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

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

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

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

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

beginner
beginner
9 лет назад

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

beginner
beginner
9 лет назад

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

RusikOk
RusikOk
7 лет назад

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

RusikOk
RusikOk
7 лет назад

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

RusikOk
RusikOk
Ответ на комментарий  Aveal
7 лет назад

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

RusikOk
RusikOk
Ответ на комментарий  Aveal
7 лет назад

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

Egor
7 лет назад

Добрый день! Подскажите
Работаю с 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
Ответ на комментарий  Egor
7 лет назад

Если всё таки нужна расшифровка 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
Ответ на комментарий  Egor
7 лет назад

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

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

Egor
Ответ на комментарий  Egor
7 лет назад

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

Ultrasat
6 лет назад

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

Alex
Alex
6 лет назад

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

Anton
Anton
6 лет назад

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

38
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x