Калибровка RTC на STM32F1, корректировка хода
Этой строчкой мы выводим на вывод Tamper (PC13), сигнал, который равен частота LSE (кварцевого резонатора) поделенной на 64, с идеальным кварцем частота была бы равна 512 Гц (32768/64=512), но так как кварцы не идеальны, то частота будет уходить от 512 Гц. Теперь нам надо как можно точнее определить частоту на выводе Tamper. Так как калибровка в STM32F1 умеет только пропускать такты (убавлять значения счетчика) через каждые 2^20 (1048576) тактов, то для начала рассмотрим пример, когда частота Tamper получилась больше 512 Гц. у меня получилась частота 512,00137625969938607194976140096 Гц, умножаем на 64 и получаем частоту кварца 32768,088080620760708604784729661 Гц, затем это делим на наш делитель 32768*3600 сек в часе*24 часа*30 дней получаем 2592006,9673147281419892456670924, и видим что наши часы уйдут на 6,967 сек за 30 дней. теперь можем воспользоваться калибровочной таблицей:
Значение калибровки | Значение отклонения в ppm | Значение отклонения в секундах за месяц (30 дней) | Значение калибровки | Значение отклонения в ppm | Значение отклонения в секундах за месяц (30 дней) | Значение калибровки | Значение отклонения в ppm | Значение отклонения в секундах за месяц (30 дней) |
0 | 0 | 0 | 43 | 41 | 106 | 86 | 82 | 213 |
1 | 1 | 2 | 44 | 42 | 109 | 87 | 83 | 215 |
2 | 2 | 5 | 45 | 43 | 111 | 88 | 84 | 218 |
3 | 3 | 7 | 46 | 44 | 114 | 89 | 85 | 220 |
4 | 4 | 10 | 47 | 45 | 116 | 90 | 86 | 222 |
5 | 5 | 12 | 48 | 46 | 119 | 91 | 87 | 225 |
6 | 6 | 15 | 49 | 47 | 121 | 92 | 88 | 227 |
7 | 7 | 17 | 50 | 48 | 124 | 93 | 89 | 230 |
8 | 8 | 20 | 51 | 49 | 126 | 94 | 90 | 232 |
9 | 9 | 22 | 52 | 50 | 129 | 95 | 91 | 235 |
10 | 10 | 25 | 53 | 51 | 131 | 96 | 92 | 237 |
11 | 10 | 27 | 54 | 51 | 133 | 97 | 93 | 240 |
12 | 11 | 30 | 55 | 52 | 136 | 98 | 93 | 242 |
13 | 12 | 32 | 56 | 53 | 138 | 99 | 94 | 245 |
14 | 13 | 35 | 57 | 54 | 141 | 100 | 95 | 247 |
15 | 14 | 37 | 58 | 55 | 143 | 101 | 96 | 250 |
16 | 15 | 40 | 59 | 56 | 146 | 102 | 97 | 252 |
17 | 16 | 42 | 60 | 57 | 148 | 103 | 98 | 255 |
18 | 17 | 44 | 61 | 58 | 151 | 104 | 99 | 257 |
19 | 18 | 47 | 62 | 59 | 153 | 105 | 100 | 260 |
20 | 19 | 49 | 63 | 60 | 156 | 106 | 101 | 262 |
21 | 20 | 52 | 64 | 61 | 158 | 107 | 102 | 264 |
22 | 21 | 54 | 65 | 62 | 161 | 108 | 103 | 267 |
23 | 22 | 57 | 66 | 63 | 163 | 109 | 104 | 269 |
24 | 23 | 59 | 67 | 64 | 166 | 110 | 105 | 272 |
25 | 24 | 62 | 68 | 65 | 168 | 111 | 106 | 274 |
26 | 25 | 64 | 69 | 66 | 171 | 112 | 107 | 277 |
27 | 26 | 67 | 70 | 67 | 173 | 113 | 108 | 279 |
28 | 27 | 69 | 71 | 68 | 176 | 114 | 109 | 282 |
29 | 28 | 72 | 72 | 69 | 178 | 115 | 110 | 284 |
30 | 29 | 74 | 73 | 70 | 180 | 116 | 111 | 287 |
31 | 30 | 77 | 74 | 71 | 183 | 117 | 112 | 289 |
32 | 31 | 79 | 75 | 72 | 185 | 118 | 113 | 292 |
33 | 31 | 82 | 76 | 72 | 188 | 119 | 113 | 294 |
34 | 32 | 84 | 77 | 73 | 190 | 120 | 114 | 297 |
35 | 33 | 87 | 78 | 74 | 193 | 121 | 115 | 299 |
36 | 34 | 89 | 79 | 75 | 195 | 122 | 116 | 302 |
37 | 35 | 91 | 80 | 76 | 198 | 123 | 117 | 304 |
38 | 36 | 94 | 81 | 77 | 200 | 124 | 118 | 307 |
39 | 37 | 96 | 82 | 78 | 203 | 125 | 119 | 309 |
40 | 38 | 99 | 83 | 79 | 205 | 126 | 120 | 311 |
41 | 39 | 101 | 84 | 80 | 208 | 127 | 121 | 314 |
42 | 40 | 104 | 85 | 81 | 210 |
Так как у нас получило отклонение почти 7 секунд за 30 дней то устанавливаем калибровочное значение 3.
А теперь рассмотрим если кварц работает медленнее. К примеру частота Tamper получилась 511,99567 Гц. умножаем на 64 и получаем частоту кварца 32767,72288 Гц. Значит нам нужно делитель RTC выставит 32767, а не 32768. и производим те же действия что в предыдущем примере. 32767,72288/32767*3600*24*30=2592057,1826825769829401532029176. В этом примере без калибровки наши часы будут убегать на 57,182 секунд за 30 дней, теперь же выберем подходящее значение из таблицы. Калибровочная поправка равна 23.
RTC HAL stm32
Речь пойдёт о программировании RTC (встроенных часов) в микроконтроллере stm32.
Описание сделано для F103 и F303, но так как у них много общих функций, то читать нужно всё.
Первым делом нужно указать источник тактирования для часов. Указываем внешний кварцевый резонатор, он есть на многих платах…
В CubeMX выбираем RCC и указываем — Low Speed External (LSE) ⇨ Crystal/Ceramic Resonator
В мультиплексоре RTC Clock Mux нужно указать источник LSE…
Если внешнего кварца нет, тогда в мультиплексоре укажите LSI, а Low Speed External (LSE) ⇨ Disable.
Переходим в раздел RTC и делаем так…
Для F103
Data Format ⇨ Binary data format .
Со временем и календарём всё понятно. Формат 24-х часовой.
Auto Predivider Calcalation ⇨ Enabled — активирован автоматический расчёт предделителя.
Asynchronous Predivider value — предделитель часового кварца. С его помощью достигается тактирование в 1Гц (один тик в одну секунду). Если отключить автоматический режим, то нужно будет ввести число от 0 до 127.
Для F303
Здесь два предделителя для настройки частоты — Asynchronous Predivider value и Synchronous Predivider value . Для внешнего кварца укажите 127 и 255.
Если у Вас LSI 40kHz, тогда во втором предделителе укажите 311. Если частота другая, тогда смотрите ниже.
Настройка предделителей для разных источников тактирования…
У нас тактируется от LSE = 32.768кГц, тогда исходя из формулы получается следующее: 128 * 256 = 32768 / 32.768кГц = 1Гц (один тик в секунду). Если уменьшить или увеличить какое-нибудь значение, то часы пойдут быстрее или медленнее.
Hour Format — можно изменить формат времени 12/24.
Day Light Saving: value of hour adjustment — зимнее/летнее время (добавляет/вычитает один час из текущего времени).
Пишем код. В функции static void MX_RTC_Init(void) есть две структуры, их надо объявить как глобальные…
/* USER CODE BEGIN PV */ RTC_TimeTypeDef sTime = ; RTC_DateTypeDef DateToUpdate = ; char trans_str[64] = ; /* USER CODE END PV */
И заодно объявим массив для вывода данных в UART.
У F303 структура даты называется sDate.
В бесконечном цикле будем читать дату и время:
/* USER CODE BEGIN WHILE */ while (1) < HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); // RTC_FORMAT_BIN , RTC_FORMAT_BCD snprintf(trans_str, 63, "Time %d:%d:%d\n", sTime.Hours, sTime.Minutes, sTime.Seconds); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTC_GetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN); snprintf(trans_str, 63, "Date %d-%d-20%d\n", DateToUpdate.Date, DateToUpdate.Month, DateToUpdate.Year); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_Delay(1000); .
Если на пин Vbat подключить батерейку или просто подать 3 вольта, тогда после ресета данные будут сохраняться. Предварительно нужно в функции static void MX_RTC_Init(void) закомментировать установку времени и даты…
/**Initialize RTC and set the Time and Date */ sTime.Hours = 10; sTime.Minutes = 34; sTime.Seconds = 0; sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; sTime.StoreOperation = RTC_STOREOPERATION_RESET; //if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) // < // Error_Handler(); //>DateToUpdate.WeekDay = RTC_WEEKDAY_WEDNESDAY; DateToUpdate.Month = RTC_MONTH_FEBRUARY; DateToUpdate.Date = 20; DateToUpdate.Year = 19; //if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK) //< // Error_Handler(); //>
Эти же функции можно использовать где-нибудь в программе для изменения времени/даты на лету.
Теперь можно прошить ещё раз и понажимать ресет. Данные должны сохраняться.
Помните о том, что при повторной генерации проекта в CubeMX комментарии удалятся!
У микроконтроллера F103, дата не сохраняется. Это связано с тем, что F103 всего один 32-х битный регистр, см. спойлер…
спойлер
Вот так выглядит схема RTC в F103…
От батарейки работает только регистр со временем и будильником, ну и ещё предделитель. То есть дату сохранить нельзя.
Но, дату сохранить нельзя только если пользоваться HAL, если же написать свой костыль, тогда в RTC_CNT можно сохранять дату/время в UNIX-формате, то есть количество секунд прошедшее с 1970 года. А потом средствами СИ вытаскивать из этого числа дату и время.
У более «жирных» камней сохраняется и дата и время.
Если используется LSI, то данные сохранятся, но время идти не будет.
Если у вас микроконтроллер F4xx или F7xx, и вы хотите считывать только время (без даты), то в любом случае нужно после функции HAL_RTC_GetTime(. ) вызывать HAL_RTC_GetDate(. ) , в противном случае время не будет обновляться.
Будильник (Alarm) для F103
Будильник сработает через пять секунд после старта.
RTC OUT ⇨ RTC Output on the Tamper pin — во время срабатывания будильника на tamper-пин (РС13) будет подан кратковременный импульс. Можно подключить светодиод и посмотреть. На плате BluePill увидеть что-либо трудно так как импульс очень уж слабенький (я подключал через транзистор).
Output ⇨ Alarm pulse signal on the TAMPER pin — во время срабатывания будильника подаётся импульс. При выборе этого режима появляется пункт с настройкой будильника — Alarm A .
Можно сгенерировать проект, прошить и посмотреть как мигнёт светодиод.
Output ⇨ RTC clock with a frequency divided by 64 on the TAMPER pin — на tamper-пин будет подана частота с часового кварца поделённая на 64. Можно осциллографом проверять точность кварца.
Output ⇨ Second pulse signal on the TAMPER pin — на tamper-пин будет подаваться импульс раз в секунду.
После того как попробуете эти режимы верните всё как на картинке.
Включите прерывание от будильника…
Будильник соединён с линией EXTI17 – RTC Alert event.
Добавьте колбек будильника:
/* USER CODE BEGIN 0 */ void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
Прошивайте и смотрите результат.
Чтобы посмотреть настройки будильника из программы, нужно вызвать функцию HAL_RTC_GetAlarm() …
пример
Структуру будильника объявим как глобальную:
/* USER CODE BEGIN PV */ RTC_TimeTypeDef sTime = ; RTC_DateTypeDef DateToUpdate = ; RTC_AlarmTypeDef sAlarm = ; // структура будильника char trans_str[64] = ; /* USER CODE END PV */
/* USER CODE BEGIN WHILE */ while (1) < HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); // RTC_FORMAT_BIN , RTC_FORMAT_BCD snprintf(trans_str, 63, "Time %d:%d:%d\n", sTime.Hours, sTime.Minutes, sTime.Seconds); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTC_GetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN); snprintf(trans_str, 63, "Date %d-%d-20%d\n", DateToUpdate.Date, DateToUpdate.Month, DateToUpdate.Year); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTC_GetAlarm(&hrtc, &sAlarm, RTC_ALARM_A, RTC_FORMAT_BIN); snprintf(trans_str, 63, "Alarm %d:%d:%d\n", sAlarm.AlarmTime.Hours, sAlarm.AlarmTime.Minutes, sAlarm.AlarmTime.Seconds); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_Delay(1000); .
Для программной установки будильника нужно воспользоваться функцией HAL_RTC_SetAlarm_IT() , она есть в функции инициализации — static void MX_RTC_Init(void) .
Помимо прерывания от будильника, есть ещё прерывание, которое может вызываться раз в секунду.
Включите глобальное прерывание…
Добавьте ещё один колбек:
/* USER CODE BEGIN 0 */ void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
Перед бесконечным циклом добавьте функцию:
/* USER CODE BEGIN 2 */ HAL_RTCEx_SetSecond_IT(&hrtc); /* USER CODE END 2 */
Прошейте и смотрите.
Как использовать этот функционал — например можно сделать так: в колбеке будильника запускаем это прерывание, после чего оно вызовется 10 раз и выключится. В прерывании можно делать что угодно, например подавать импульс на какой-нибудь пин с подключённой пищалкой.
пример
/* USER CODE BEGIN PV */ RTC_TimeTypeDef sTime = ; RTC_DateTypeDef DateToUpdate = ; RTC_AlarmTypeDef sAlarm = ; char trans_str[64] = ; volatile uint8_t count = 0; // добавить счётчик /* USER CODE END PV */
/* USER CODE BEGIN 0 */ void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc) < // тут подаём импульс на какой-нибудь пин count++; if(count >9) HAL_RTCEx_DeactivateSecond(hrtc); // отключаем ежесекундное прерывание snprintf(trans_str, 63, "One sec\n"); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); > void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) < snprintf(trans_str, 63, "ALARM\n"); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTCEx_SetSecond_IT(hrtc); // запускаем ежесекундное прерывание >/* USER CODE END 0 */
/* USER CODE BEGIN 2 */ //HAL_RTCEx_SetSecond_IT(&hrtc); /* USER CODE END 2 */
Если установить RTC OUT ⇨ Disable …
… то режимы Output ⇨ RTC clock with a frequency divided by 64 on the TAMPER pin и Output ⇨ Second pulse signal on the TAMPER pin будут работать, а если RTC OUT ⇨ No RTC Output , то на tamper-пин ничего нельзя подать.
Tamper для F103 и F303
В RTC OUT указывайте что хотите. Calendar можно включить, а можно и не включать.
До этого мы использовали tamper-пин для вывода сигнала, а сейчас он будет выполнять обратную функцию.
У F103 есть десять 16-ти битных регистров для хранения пользовательских данных (backup registers). Если подключена батарейка, то данные в этих регистрах не обнуляются ни при нажатии Reset, ни при выходе из спящего режима, ни при отключении основного питания.
Если в эти регистры записать какие-то данные, то их можно будет стереть подав на tamper-пин кратковременный импульс.
В Reference manual предлагается использовать этот функционал в качестве контроля несанкционированного доступа к устройству. Видимо поэтому это назвали Tamper (вмешательство).
Какой именно сигнал послужит триггером, настраивается в пункте Tamper Trigger .
Rising Edge — с LOW на HIGH.
Falling Edge — с HIGH на LOW.
Внутренняя подтяжка такая слабенькая, что срабатывает от прикосновения пальца, поэтому желательно подтянуть пин к «плюсу» резистором (10КОм).
Запишем в первые два регистра данные:
/* USER CODE BEGIN 2 */ HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 1234); // в первый регистр запишем число 1234 HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, 5678); // во второй регистр запишем число 5678 /* USER CODE END 2 */
В бесконечном цикле читаем эти данные:
/* USER CODE BEGIN WHILE */ while (1) < uint16_t reg_1 = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1); uint16_t reg_2 = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2); snprintf(trans_str, 63, "Reg_1 %d Reg_2 %d\n", reg_1, reg_2); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_Delay(1000); .
Прошейте эту программу и коротните пин на «землю» — данные обнулятся.
Теперь если нажать Reset, то по идее данные должны будут записаться заново, но этого не случится. Дело в том, что после подачи сигнала запись в эти регистры будет запрещена. Чтобы восстановить возможность записи нужно полностью обесточить плату (батарейку тоже нужно отключить). Так происходит только в режиме Tamper, если его отключить, то эти регистры можно перезаписывать как угодно.
Помимо обнуления регистров этот сигнал может вызывать прерывание…
В программе нужно добавить только колбек:
/* USER CODE BEGIN 0 */ void HAL_RTCEx_Tamper1EventCallback(RTC_HandleTypeDef *hrtc)
Если включено прерывание, то при нажатии Reset регистры с данными будут перезаписываться!
Для работы без прерываний можно воспользоваться функцией ожидания сигнала.
пример
Структуру тампера объявляем глобально:
/* USER CODE BEGIN PV */ RTC_TimeTypeDef sTime = ; RTC_DateTypeDef DateToUpdate = ; RTC_AlarmTypeDef sAlarm = ; RTC_TamperTypeDef sTamper = ; // структура тампера char trans_str[64] = ; /* USER CODE END PV */
В бесконечном цикле делаем так:
/* USER CODE BEGIN WHILE */ while (1) < static uint16_t count = 0; // просто счётчик для наглядности uint16_t reg_1 = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1); uint16_t reg_2 = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2); snprintf(trans_str, 63, "Before Reg_1 %d Reg_2 %d\n", reg_1, reg_2); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); snprintf(trans_str, 63, "Start %d Wait 10 sec. \n", count++); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTCEx_SetTamper(&hrtc, &sTamper); HAL_RTCEx_PollForTamper1Event(&hrtc, 10000); // ждём сигнала 10 сек. reg_1 = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1); reg_2 = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2); snprintf(trans_str, 63, "After Reg_1 %d Reg_2 %d\n\n", reg_1, reg_2); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_Delay(1000); .
Опять же, при нажатии Reset регистры с данными будут перезаписываться.
У F303 15 пользовательских регистров и три tamper-пина.
Прерывания включаются так…
У каждого пина свой колбек:
void HAL_RTCEx_Tamper1EventCallback(RTC_HandleTypeDef *hrtc) void HAL_RTCEx_Tamper2EventCallback(RTC_HandleTypeDef *hrtc) void HAL_RTCEx_Tamper3EventCallback(RTC_HandleTypeDef *hrtc)
Всё остальное как у F103.
Я когда ковырялся с этим функционалом, время от времени возникало ощущение что что-то «глючит» (регистры то записываются, то не записываются, то обнуляются, то не обнуляются), но потом стало понятно что это всего лишь следствие неправильных действий. Это к тому, что нужно проявить терпение и разобраться.
Будильник (Alarm) для F303
У этого микроконтроллера два будильника А и В…
У них есть два варианта настроек:
Internal Alarm — просто будильник, может вызывать прерывание.
Routed to OUT — во время срабатывания будильника можно вызвать прерывание и/или подать кратковременный импульс на пин РС13 .
Сейчас укажите Internal Alarm А .
Настройте всё как на картинке…
Время у нас установлено 10:34:00, а будильник сработает в 10:34:10.
Sub Seconds — это миллисекунды.
Следующие пункты, это различные комбинации настроек времени/даты срабатывания будильника. Например пункт Alarm Mask Date Week day ⇨ Enable говорит о том, что будильник должен срабатывать каждый день.
Если активировать пункт Alarm Mask Minutes ⇨ Enable , то будильник будет срабатывать каждую минуту в течении часа. Срабатывания будут происходить не ровно в минуту, а минута + 10сек.
Если активировать пункт Alarm Mask Seconds ⇨ Enable , то срабатывать будет каждую секунду в течении минуты.
Этих комбинаций достаточно много, поэтому надеюсь что вы разберётесь самостоятельно.
Alarm combinations
Активируйте прерывание от будильника…
В код добавьте колбек:
/* USER CODE BEGIN 0 */ void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
Alarm B
У Alarm B свой колбек:
HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *hrtc)
В бесконечном цикле будем выводить инфу:
/* USER CODE BEGIN WHILE */ while (1) < HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); // RTC_FORMAT_BIN , RTC_FORMAT_BCD snprintf(trans_str, 63, "Time %d:%d:%d\n", sTime.Hours, sTime.Minutes, sTime.Seconds); //snprintf(trans_str, 63, "Time %d:%d:%d:%lu\n", sTime.Hours, sTime.Minutes, sTime.Seconds, sTime.SubSeconds); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTC_GetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN); snprintf(trans_str, 63, "Date %d-%d-20%d\n", DateToUpdate.Date, DateToUpdate.Month, DateToUpdate.Year); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTC_GetAlarm(&hrtc, &sAlarm, RTC_ALARM_A, RTC_FORMAT_BIN); snprintf(trans_str, 63, "Alarm %d:%d:%d\n", sAlarm.AlarmTime.Hours, sAlarm.AlarmTime.Minutes, sAlarm.AlarmTime.Seconds); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_Delay(1000); .
Чтобы будильник подавал импульс на пин РС13, надо указать Routed to OUT …
В настройках появятся два дополнительных пункта…
Output Polarity — на ножку будет подаваться «плюс».
Output Type — почитайте здесь.
Делайте как на картинке и не забудьте будильник настроить, он сбивается при изменении режима.
Подключите светодиод к пину РС13 и смотрите как он мигнёт во время срабатывания будильника.
WakeUp
WakeUp может выводить МК из спящего режима, вызывать прерывание и подавать сигнал на пин РС13.
WakeUp это простой 16-ти битный (от 0 до 65535) счётчик. Тактирование можно настроить с помощью предделителей часового генератора.
Например если сделать так…
Тогда счётчик будет увеличиваться со скоростью 2048 единиц в секунду и достигнет 10000 примерно через 5 секунд (32.768кГц / 16 = 2048, 10000 / 2048 = 4.88 сек). То есть WakeUp будет срабатывать каждые ~5 секунд.
Можно не заморачиваться с предделителями, а просто указать 1Hz…
Счётчик будет увеличиваться со скоростью 1 единица в секунду и срабатывать через каждые 5 сек.
Таким образом можно настроить пробуждение МК на достаточно большой интервал. Например если указать 65000, то WakeUp будет срабатывать каждые 18 часов.
Если включено прерывание, то на пин РС13 подаётся кратковременный импульс, а если отключено, то подаётся постоянный сигнал.
И добавьте соответствующий колбек:
/* USER CODE BEGIN 0 */ void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
Прошивайте и смотрите что получилось.
Посмотреть значение счётчика можно так:
uint32_t wu = HAL_RTCEx_GetWakeUpTimer(&hrtc); snprintf(trans_str, 63, "WakeUp %lu\n\n", wu); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000);
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
TimeStamp
Прикольная функция. Если на пин РС13 подать внешний импульс, тогда в специальные регистры будут записаны текущие время и дата. Настройка только одна и она ничем не отличается от того, что написано в главе «Tamper для F103 и F303». То есть нужно указать фронт сигнала и подтянуть пин.
В бесконечном цикле сделайте так:
/* USER CODE BEGIN WHILE */ while (1) < HAL_RTCEx_GetTimeStamp(&hrtc, &sTime, &DateToUpdate, RTC_FORMAT_BIN); // читаем Time/Date Stamp snprintf(trans_str, 63, "TimeStamp %d:%d:%d\n", sTime.Hours, sTime.Minutes, sTime.Seconds); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); snprintf(trans_str, 63, "DateStamp %d-%d-%d\n", DateToUpdate.Date, DateToUpdate.Month, DateToUpdate.Year); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); snprintf(trans_str, 63, "Time %d:%d:%d\n", sTime.Hours, sTime.Minutes, sTime.Seconds); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_RTC_GetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN); snprintf(trans_str, 63, "Date %d-%d-%d\n\n", DateToUpdate.Date, DateToUpdate.Month, DateToUpdate.Year); HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 1000); HAL_Delay(1000); .
Прошейте МК и коротните РС13 на «землю».
Данные из регистров записываются в структуры времени и даты (чтоб не создавать дополнительные структуры).
Обратите внимание на то, что регистры обнуляются после чтения.
Прерывание то же что и у Tamper…
/* USER CODE BEGIN 0 */ void HAL_RTCEx_TimeStampEventCallback(RTC_HandleTypeDef *hrtc)
Calibration
В мануале про этот выход сказано так:
«Выход RTC_CALIB используется для генерации сигнала переменной частоты. В зависимости от пожелания пользователя этот сигнал может играть роль опорной частоты для внешнего устройства или его можно подключить к зуммеру для генерации звука.»
Есть два варианты частоты — 1Гц и 512Гц…
Запускается и останавливается этот сигнал функциями…
HAL_RTCEx_SetCalibrationOutPut(&hrtc, RTC_CALIBOUTPUT_1HZ); // или RTC_CALIBOUTPUT_512HZ HAL_RTCEx_DeactivateCalibrationOutPut(&hrtc);
Можно помигать или попищать при срабатывании будильника.
Для калибровки часов этот выход нужно подключить к осциллографу и добиваться необходимой частоты двумя способами:
1. Манипулировать предделителями — грубая калибровка.
2. С помощью пропусков (маскировки) или добавления тактов — мягкая калибровка.
Осциллографа у меня нет поэтому я особо в этом не разбирался. За мягкую калибровку отвечает функция HAL_RTCEx_SetSmoothCalib() . Все подробности смотрите в AN3371, глава 1.4, стр. 17.
Reference clock detection — на это пин можно подать опорную частоту (50 Гц) из розетки. Проводить эксперименты я не решился
В мануале есть такая картика…
В статье не описаны некоторые функции — посмотреть их можно в файлах stm32f3xx_hal_rtc.c и stm32f3xx_hal_rtc_ex.c.
На этом всё.
Всем спасибо
-->
Поддержать автора
Задать вопрос по статье
Известит Вас о новых публикациях
18.2.5. Калибровка RTC
RTC может быть откалиброван для компенсации неточностей источника RTCCLK. Это в особенности полезно для приложений, которым требуется повышенная точность RTC, и для тех приложений, которые требуют стабильности RTC при изменениях температуры.
Периферийные устройства RTC предоставляют два типа калибровки: грубая и тонкая калибровки. Давайте проанализируем их.
18.2.5.1. Грубая калибровка RTC
Цифровая грубая калибровка (coarse calibration) может использоваться для компенсации неточности кварца путем добавления (положительная калибровка) или маскирования (отрицательная калибровка) тактовых циклов на выходе асинхронного предделителя. Отрицательная калибровка может быть выполнена с разрешением около 2·10 −6 Гц (или 2 ppm), а положительная калибровка может быть выполнена с разрешением около 4·10 −6 Гц. Максимальный диапазон калибровки от –63·10 −6 Гц до 126·10 −6 Гц.
Мы можем измерить выходную частоту перед асинхронным предделителем, направив ее на специальный вывод (который обычно совпадает с выводом AF1). Когда этот I/O используется для такой операции, он также называется выводом AFO_CALIB. Измеряя выходную частоту с помощью осциллографа, мы можем оценить качество RTCCLK. Ожидается, что AFO_CALIB будет подавать прямоугольный сигнал с фиксированной частотой 512 Гц.
Для выполнения грубой калибровки HAL предоставляет функцию:
HAL_RTCEx_SetCoarseCalib(RTC_HandleTypeDef * hrtc, uint32_t CalibSign, uint32_t Value);
Параметр CalibSign может принимать значения RTC_CALIBSIGN_POSITIVE и RTC_CAL-
IBSIGN_NEGATIVE , в то время как параметр Value может находиться в диапазоне от 0 до 63 с шагом 2·10 −6 Гц при использовании отрицательной калиброкви или от 0 до 126 с шагом 4·10 −6 Гц при использовании положительной калибровки.
Рисунок 3: Структурная схема распределения тактового сигнала RTC
При калибровке RTC с использованием грубой калибровки важно подчеркнуть следующие моменты.
Часы реального времени
• Невозможно проверить результат калибровки, так как выходной сигнал 512 Гц находится перед блоком калибровки (см. рисунок 3 4 ). Вы можете проверить калибровку в некоторых микроконтроллерах STM32, поскольку 1 Гц выход CK_Spre находится после блока грубой калибровки. Обратитесь к справочному руководству по вашему микроконтроллеру.
• Параметры калибровки могут быть изменены только во время инициализации. Поэтому используйте грубую калибровку только для статической коррекции.
18.2.5.2. Тонкая калибровка RTC
Частота RTC может быть откалибрована с разрешением около 0,954·10 −6 Гц (или 0,954 ppm) в диапазоне от –487,1·10 −6 Гц до +488,5·10 −6 Гц. Коррекция частоты выполняется с помощью ряда небольших настроек (сложение и/или одновременное вычитание отдельных импульсов RTCCLK). Эти настройки четко распределены в диапазоне нескольких секунд (8, 16 или 32 секунды), так что RTC четко откалиброван даже при наблюдении коротких промежутков времени.
Для сложения и/или вычитания заданного количества импульсов RTCCLK в выбранном диапазоне (8, 16 или 32 секунды) используются два регистра RTC, называемые CALP и CALM . В то время как CALM позволяет снизить частоту RTC до 487,1·10 −6 Гц при хорошем разрешении, бит CALP можно использовать для увеличения частоты на 488,5·10 −6 Гц. Установка регистра CALP в «1» эффективно вставляет дополнительный импульс RTCCLK каждые 2 11 циклов RTCCLK, что означает, что 512 тактовых импульсов добавляются в течение каждого 32-секундного цикла. Задавая количество импульсов RTCCLK, которые будут маскироваться в течение 32-секундного цикла, воспользовавшись регистром CALM , количество добавляемых тактовых импульсов можно уменьшить до 0.
Используя CALM вместе с CALP , может быть добавлено смещение в диапазоне от –511 до +512 циклов RTCCLK в течение 32-секундного цикла, что соответствует диапазону калибровки от –487,1·10 −6 Гц до +488,5·10 −6 Гц с разрешением около 0,954·10 −6 Гц. Формула для расчета действующей калиброванной частоты ( F CAL ) с учетом входной частоты
) имеет следующий вид:
( 220 + CALM − CALP 512 )
Для выполнения тонкой калибровки (smooth calibration), HAL предоставляет функцию:
HAL_RTCEx_SetSmoothCalib(RTC_HandleTypeDef * hrtc, uint32_t SmoothCalibPeriod, uint32_t SmoothCalibPlusPulses,
Параметр SmoothCalibPeriod может принимать значения, перечисленные в таблице 4, и он определяет интервал распределения. Параметр SmoothCalibPlusPulses может прини-
мать значения RTC_SMOOTHCALIB_PLUSPULSES_SET и RTC_SMOOTHCALIB_PLUSPULSES_RESET и ис-
пользуется для установки/сброса одного бита в регистре CALP .
4 Рисунок взят из AN3371 от ST ( http://www.st.com/content/ccc/resource/technical/document/application_note/7a/9c/de/da/84/e7/47/8a/DM00025 071.pdf/files/DM00025071.pdf/jcr:content/translations/en.DM00025071.pdf ).
Часы реального времени
Параметр SmouthCalibMinusPulsesValue устанавливает количество тактовых импульсов для вычитания, и может принимать любое значение от 0 до 511.
Таблица 4: Доступные значения параметра SmoothCalibPeriod
Период тонкой калибровки составляет 8 с
Период тонкой калибровки составляет 16 с
Период тонкой калибровки составляет 32 с
В отличие от грубой калибровки RTC, влияние тонкой калибровки на тактовый сигнал календаря (тактовый сигнал RTC) можно легко проверить, считав выходной сигнал с вывода AFO_CALIB. Тонкую калибровку можно также выполнять «на лету» для изменения тактового сигнала при изменении температуры или при обнаружении других факторов.
18.2.5.3. Обнаружение опорного тактового сигнала
В некоторых приложениях RTC может быть активно откалиброван с использованием внешнего опорного тактового сигнала. Опорная тактовая частота (на частотах 50 Гц или 60 Гц – типовая частота сети) должна иметь более высокую точность, чем тактовая частота LSE-генератора 32,768 кГц. По этой причине RTC в микроконтроллерах STM32 с большим количеством выводов предоставляет вход опорного тактового сигнала (вывод называется RTC_50Hz), который может быть использован для компенсации неточности тактового сигнала календаря (1Гц).
Вывод RTC_50Hz должен быть сконфигурирован в режиме плавающего входа. Данный механизм позволяет календарю быть таким же точным, как и опорный тактовый сигнал. Обнаружение опорного тактового сигнала включается с помощью функции:
HAL_StatusTypeDef HAL_RTCEx_SetRefClock (RTC_HandleTypeDef * hrtc);
Когда обнаружение опорного тактового сигнала включено, как асинхронный, так и синхронный предделители должны быть установлены в их значения по умолчанию: 0x7F и 0xFF . Когда обнаружение опорного тактового сигнала включено, каждый фронт тактового сигнала 1 Гц сравнивается с точностью до фронта опорного тактового сигнала (если он найден в заданном временном окне). В большинстве случаев два фронта тактовых сигналов выровнены как надо. Когда тактовая частота 1 Гц смещается из-за неточности тактовой частоты LSE-генератора, RTC немного сдвигает тактовую частоту 1 Гц, так что будущие фронты тактовой частоты 1 Гц выравниваются. Окно обновления состоит из трех периодов ck_calib ( ck_calib – выходной сигнал блока грубой калибровки – см. ри-
Если опорный тактовый сигнал приостанавливается, календарь непрерывно обновляется только на основе тактового сигнала LSE-генератора. Затем RTC ожидает опорный тактовый сигнал, используя окно обнаружения, центрированное по фронту выходного тактового сигнала от синхронного предделителя ( ck_spre ). Окно обнаружения составляет семь периодов ck_calib .
Опорный тактовый сигнал может иметь большое локальное отклонение (например, в диапазоне 500·10 −6 Гц), но в долгосрочной перспективе он должен быть намного более точным, чем 32 кГц кварц. Система обнаружения используется только тогда, когда опорный тактовый сигнал необходимо снова обнаружить после потери. Поскольку окно
Argrento's blog
Резервные регистры – это 10 16-битных регистров в устройствах с низкой и средней емкостью или 42 регистра в устройствах с высокой емкостью. Соответственно, резервные регистры могут хранить в себе 20 или 84 байта данных соответственно.
Эти регистры расположены в резервном домене (backup domain), который запитывается от выхода VBAT, в случае отключения питания на VDD. Эти регистры не сбрасываются ни после выхода контроллера из режима ожидания, ни после сброса.
В дополнение ко своей основной функции, резервные регистры служат для обнаружения сигнала сброса и для калибровки часов реального времени.
После сброса доступ к регистрам заблокирован и резервный домен закрыт от возможной паразитной записи. Для разблокировки доступа к регистрам необходимо произвести следующие действия:
- Включить тактирование интерфейсов питания и резервного копирования за счет устанвки битов PWREN и BKPEN в регистре RCC_APB1ENR.
- Установить бит DBP в регистре управления питанием PWR_CR для разблокировки доступа к резервным регистрам и часам реального времени.
4.2 Основные особенности BKP
- Емкость регистров – 20 байт для устройств со средней и низкой емкостью, 84 байта для устройств с высокой емкостью.
- Регистр статуса/управления для управления детектированием сигнала сброса с поддержкой прерываний.
- Специальный калибровочный регистр для хранения калибровочного значения RTC
4.3 Функциональное описание BKP
4.3.1 Детектирование сброса
Пин сброса (TAMPER pin) генерирует событие сброса каждый раз при изменении состояния пина с 0 на 1 или с 1 на 0 (в зависимости от состояния бита TPAL в регистре управления резервным копированием BKP_CR).
Чтобы предотвратить потерю событий сброса, линия, используемая для определения типа фронта, соединяется логическим “И” с линией включения сброса.
- Когда TPAL=0, если на пине TAMPER был высокий уровень до включения детектирования сброса (до установки бита TPE), то дополнительное событие сброса будет сгенерировано как только будет включено обнаружение сброса.
- Когда TPAL=1, если на пине TAMPER был низкий уровень до включения детектирования сброса (до установки бита TPE), то дополнительное событие сброса будет сгенерировано как только будет включено обнаружение сброса.
Если установить бит TPIE в регистре BKP_CSR, то прерывание будет сгенерировано как только будет обнаружен сигнал сброса.
После того, как будет обнаружен и проделан сброс, пин сброса (TAMPER pin) должен быть отключен, а потом вновь включен битом TPE перед повторной записью в резервные регистры (BKP_DRx). Это ограждает прошивку от записи данных в регистры BKP_DRx пока пин сброса показывает наличие сброса. Это эквивалент определения уровня на пине сброса.
4.3.2 Калибровка RTC
Контроллер имеет возможность вывести тактирование RTC с предделителем 64 на пин сброса (TAMPER pin). Это можно включить установкой бита CCO в регистре калибровки RCT BKP_RTCCR.
Тактирование может быть замедлено до 121 ppm (part per millon) с помощью конфигурирования битов CAL[6:0].
Для более подробного ознакомления с темой калибровки RTC просьба обратьтся к AN2604.
4.4 Программирование BKP
void BKP_ClearFlag (void)
Сбрасывает флаг ожидания события от пина Tamper.
Вход:
- Параметров не принимает.
- Значения не возвращает.