Всем доброго времени суток! И сегодня мы снова поговорим о модуле 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 секунд после начала выполнения. Таким образом, мы с вами добились того, чего и хотели. Собственно, на этом все на сегодня, с установкой времени и будильника мы полностью разобрались ) Если возникнут какие-нибудь вопросы - обязательно пишите в комментариях к статье.
Огромное Вам спасибо за статьи по stm! Подскажите пожалуйста куда посмотреть для поднятия полного RS-232? На stm32f3discovery
Скоро напишу статью с примером ) Плата на днях придет
Подскажите как это вывести на 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 и ничего не меняется.
Для тех, кто прочитает это когда-нибудь: time.RTC_Hours etc.. используется для установки времени в RTC! Она не показывает текущее время!
и снова здравствуйте )
столкнулся со следующей проблемой:
работал в кейле с STM32 Discovery все было хорошо, прошивалось, работала отладка.
купил STM32F4 Discovery, поключил, кейл пишет: "No ST-LINK detected"
При чем "STM32 ST-LINK Utility" с платкой прекрасно работает, а кейл не видит.
я так понимаю дело в драйверах ?
А в Кейле все правильно настроено?
в настройках ничего не трогаю, подключаю 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 не знаю.
Надо в Кейле в меню Flash-Configure flash tools выбрать st-link (как и для STM32Discovery), затем кнопочка Settings, откроется новое окно, там жмем Add и надо выбрать STM32F4xx Flash. Ну и в этом же окне вкладка Debug и там SW.
Должно заработать
об этом моменте я знаю... к сожалению это не то (
Можно попробовать драйвер st-link'а переустановить
Спасибо за Ваши статьи. По ним изучаю STM32. Вопрос по этой статье - как получить данные по времени и дате? Я так понимаю RTC_GetTime() ? Можно какой-нибудь пример - не врубаюсь в структуру процедуру.
Ну вот, например:
объявляем переменную:
RTC_TimeTypeDef currrentTime;
В коде вызываем функцию:
RTC_GetTime(RTC_Format_BIN, ¤tTime);
В итоге получаем:
currentTime.RTC_Hours - часы
currentTime.RTC_Minutes - минуты
currentTime.RTC_Seconds - секунды
Спасибо. Все работает!
Здравствуйте, уважаемые. В общем собрал я проект с RTC под stm32f103, и ничего, как водится не работает. При инициализации виснет, тактирование не начинается. Подскажите, а может сначала нужно альтернативную функцию ножек OSC32, ну вроде вот этого что-то прописать: GPIO_Mode_AF_PP. В общем, подскажите, люди добрые
у f103 rtc реализованы по другому и поэтому этот код работать не будет.
Разве кейл поддерживает эмуляцию переферии stm32? Я так понимаю вы пользуете кейл 5... как вы прсматриваете регистры ?
У меня Кейл 4. В принципе, я почти всегда в железе отлаживаю, только когда начинал с STM32 (давным-давно) пользовался Кейловским симулятором. А так спектр возможностей симулятора довольно широкий )
Да это я знаю... когда я пользовал LPC2478 то тоже отлаживал в симуляторе многие вещи... кейл прекрасно симулирует этот камень.. и всю почти периферию. а вот stm32f4 не хочет симулировать. Нет у него таких возможностей и в планах тоже.
Подскажите,как запустить RTC на f103
SPL в принципе не особо сильно отличается для разных семейств, так что примерно также как и для F4.
Попробовал, определений типов таких же не нашел, буду штудировать reference manual
как-то глупо каждый раз выставлять часы после выключения. там же я надеюсь есть своя батарейка для rtc
тоесть получается если использовать прерывание от будильника то нужно пожертвовать одной ножкой контроллера и не использовать ее?
Почему?
ну если при срабатывании будильник выставляет высокий/низкий уровень на ногу контроллера которая настроена на вход с прерыванием. то нога стает недоступна для нормального использования
Где именно и на какую ногу будильник что-то выставляет?
цытата: "17 линию внешних прерываний"
хотя и правда. я не подумал, что в каждом порту по 16 линий. с толку сбило "внешнее прерывание"
тогда получается что все круто и без костылей) спасибо
Не за что )
Там этих EXTI больше 30 прерываний (для STM32F3 - 36), а к физическим ножкам подключены первые 16, остальные - на определенные события.
Добрый день! Подскажите
Работаю с 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-го нет
Если всё таки нужна расшифровка 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);
Так нашёл ошибку там нужно что бы прерывание то же что бы называлось EXTI9_5_IRQHandler(void)
Но от этого не легче.
так как в регистрах при сработке того или другого прерывания взводятся оба EXTI->PR->PR6 = 1 и EXTI->PR->PR7 = 1
Следовательно я ни как не смогу их разделить?
Сможете подсказать как получить 10 самостоятельных внешних прерываний?
Нужно еще посмотреть по всем регистрам/функциям - по идее должен быть способ, чтобы определить какое именно событие его вызвало.
Странное дело. Используя HAL_RTC_GetTime() НЕ работает при обычном запуске (RESET от кнопки на плате), но работает если включить отладку Debug в Keil. Причем просто - Start Debug Session and Run.
А без отладки - не считает, хотя прошивку не меняешь вообще.
Не знаю что делает EXTI17, но у меня на двух платах при таких настройках выгорела нога PC13. На ней висела кнопка с подтяжкой 10к на 3.3. После инита будильника она стала звониться на землю. Проверил на другом рабочем проце - тоже самое. Тупо выгорает пин.
Добрый день! В статье код на разбит на несколько частей. А есть ли исходный файл со всем блоком подключения RTC?
Добрый день!
К сожалению, не сохранился полный проект...