Главная

Главная

Сообщество

Сообщество

Форум

Форум

STM32

STM32

Электроника

Электроника

Raspberry Pi

Raspberry Pi

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
34 комментариев
Старые
Новые
Михаил
Михаил
13 лет назад

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

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

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

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

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

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

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

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

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

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

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

Geka
Geka
12 лет назад

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

Geka
Geka
12 лет назад

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

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

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

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

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

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

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

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

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

beginner
beginner
11 лет назад

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

beginner
beginner
11 лет назад

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

RusikOk
RusikOk
10 лет назад

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

RusikOk
RusikOk
9 лет назад

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

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

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

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

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

Ultrasat
9 лет назад

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

Alex
Alex
8 лет назад

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

Anton
Anton
8 лет назад

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

Обзор конфиденциальности

На этом сайте используются файлы cookie, что позволяет нам обеспечить наилучшее качество обслуживания пользователей. Информация о файлах cookie хранится в вашем браузере и выполняет такие функции, как распознавание вас при возвращении на наш сайт и помощь нашей команде в понимании того, какие разделы сайта вы считаете наиболее интересными и полезными.