Всем доброго времени суток! И сегодня мы снова поговорим о модуле RTC микроконтроллеров STM32. А точнее мы разберем его функции более подробно. Напомню, что в предыдущей статье мы начали знакомиться с часами реального времени и написали программу для их инициализации и запуска (статья тут).
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также небольшая подборка по таймерам из нового курса:
- STM32 и таймеры. STM32CubeMx. Настройка и использование.
- STM32 и watchdog. STM32CubeMx. Настройка модуля WWDG.
- STM32 и Timer Input Capture. Режим захвата сигнала.
- STM32CubeMx и watchdog. Настройка и использование IWDG.
Так вот, сегодня мы сделаем часы с будильником. Я для этого по традиции буду использовать плату 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 секунд, и вот он результат:
Как видите программа ушла на обработку прерывания как раз через 15 секунд после начала выполнения. Таким образом, мы с вами добились того, чего и хотели. Собственно, на этом все на сегодня, с установкой времени и будильника мы полностью разобрались ) Если возникнут какие-нибудь вопросы - обязательно пишите в комментариях к статье.