Калибровка 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.
Часть 3: мигаем светодиодом
Мигание светодиодом это традиционный hello world для микроконтроллеров. Это один из самых простых способов взаимодействия с окружающей средой без помощи отладчика. В этой части именно этим мы и займёмся.
Сразу оговоримся, что эта часть и далее уже очень сильно зависят от конкретного процессора и даже платы. Все адреса приведены со ссылками на reference manual, что должно помочь в переводе кода на другие процессоры.
На плате blue pill синий светодиод подключен анодом (плюсом) к напряжению на 3.3 вольта, а катодом (минусом) к выводу микроконтролера, обозначенному C13 / 2 / PC13 / TAMPERRTC. Большинство выводов микроконтроллера способны выполнять разные задачи, отсюда и несколько названий. Теперь откроем Datasheet на микроконтроллер, и найдём этот вывод там. На плате видно, что у микросхемы имеется 48 выводов, по 12 на каждой стороне. Этому соответствует тип корпуса LQFP48, его можно увидеть в разделе 3, изображении 8. Там же видно, что у этого корпуса действительно есть вывод с номером 2 и функционалом PC13-TAMPER-RTC. Соответствие между платы и datasheet контролера мы провели и теперь точно знаем, с каким выводом будем работать.
Для того, чтобы реализовать этот функционал, микроконтроллер имеет набор GPIO портов. Порт может быть сконфигурирован в режиме входа (input), в этом случае он определяет напряжение между соответствующим выводом и «землёй» в бинарном виде ( 0 это отсутствие напряжения, 1 это номинальное напряжение). Но нас интересует конфигурация порта в режиме выхода (output). В этом случае микроконтроллер для состояния 0 подключает вывод к земле, а для состояния 1 либо соединяет вывод с источником напряжения в режиме двухтактного выхода (push-pull output), либо отключает вывод от схемы в режиме выхода с открытым коллектором (open drain output).
В применении к нашей схеме 0 соединит выход микроконтроллера с землёй, а значит и катод светодиода. Через светодиод потечёт ток и он включится. 1 в любом режиме отключит светодиод.
Интересующий нас вывод микроконтроллера называется PC13, это можно раскрыть как Port C 13. У микроконтроллера имеется несколько портов ввода-вывода и каждый порт способен работать с несколькими выводами. Другое обозначение порта C это GPIOC. Если посмотреть в Reference Manual, разделе 3.1 изображение 1 (System Architecture), можно увидеть, что устройство GPIOC подключено через шину APB2 (Advanced Peripheral Bus). Это важно понимать, чтобы потом найти нужные пункты в документации.
При загрузке у микроконтроллера отключены практически все периферийные устройства. Поэтому первое, что нужно сделать, это включить порт GPIOC. Далее нужно настроить вывод C13 как выход с открытым коллектором. По сути это все подготовительные действия. После этого можно управлять выводом C13, включая и выключая светодиод. Для задержки между включением и выключением будем использовать «пустой» цикл, который займёт процессор работой на нужное число миллисекунд.
Проект возьмём из второй части, переименовав loop в blink .
Итак первое, что нужно сделать, это включить порт GPIOC. Для включения периферийных устройств в микроконтроллере имеется RCC (reset and clock control, управление сбросом и тактированием). Взаимодействие с RCC осуществляется через запись нужных значений в регистры RCC. Подробное описание этих регистров можно почитать в Reference Manual, разделе 8.3. В разделе 8.3.7 описан регистр RCC_APB2ENR, включающий устройства на шине APB2. В частности бит 4 IOPCEN отвечает именно за порт ввода-вывода C. Регистр RCC_APB2ENR имеет смещение 0x18 от начала адресного пространства, выделенного для RCC. Если посмотреть в Reference Manual, разделе 3.3, можно найти, что для RCC выделено адресное пространство 0x4002_1000 — 0x4002_13FF . Резюмируя: нам нужно проставить единицу в 4-й бит по адресу 0x4002_1000 + 0x18 .
На языке ассемблера это действие будет записываться следующим образом:
// enable I/O port C clock ldr r0, =0x40021000 + 0x18 // RCC_APB2ENR ldr r1, [r0] orr r1, 1
Этому коду соответствует псевдокод
// enable I/O port C clock r0 := 0x4002_1000 + 0x18; r1 := memory[r0]; r1[4] := 1; memory[r0] := r1;
После этого нужно настроить вывод С13. В reference manual, разделе 9.2.2 описан регистр GPIOx_CRH со смещением 0x04 , который позволяет настраивать этот вывод: биты 20 и 21 с названием MODE13 и биты 22 и 23 с названием CNF13 . В разделе 9.1, таблице 21 подробно описаны все сочетания значений. Для нашего случая нужно установить CNF1 в 0, CNF0 в 1, MODE1 в 1, MODE0 в 0. Это настроит порт в режим general purpose output, open-drain, maximum output speed 2 MHz. В reference manual, разделе 3.3 можно найти адреса для GPIO Port C: 0x4001 1000 - 0x4001 13FF . Таким образом нам нужно модифицировать машинное слово по адресу 0x4001 1000 + 0x04 .
Для наглядности приведём то, что нам нужно сделать. ? означает, что мы не меняем этот бит.
+------------------------------ CNF13, bit 1 |+----------------------------- CNF13, bit 0 ||+---------------------------- MODE13, bit 1 |||+--------------------------- MODE13, bit 0 |||| 0x4001 1004: . . 0110 . . . . .
На ассемблере это запишется так:
// configure PC13 as open-drain output with 10 MHz speed ldr r0, =0x40011000 + 0x04 // GPIOC_CRH ldr r1, [r0] bic r1, 1
Этому коду соответствует псевдокод
// configure PC13 as open-drain output with 10 MHz speed r0 := 0x4001_1000 + 0x04; r1 := memory[r0]; r1[20] := 0; r1[21] := 1; r1[22] := 1; r1[23] := 0; memory[r0] := r1;
Этого кода хватает, чтобы включить светодиод (т.к. по умолчанию порт имеет состояние 0).
Для того, чтобы светодиод начал мигать, нужно ещё немножко кода. Управление портом выхода осуществляется через регистр GPIOx_ODR , имеющий смещение 0x0c . Порту 13, как несложно догадаться, соответствует бит 13. Для задержки используем пустой цикл из миллиона итераций, он занимает примерно половину секунды.
В итоге у нас получился такой код для мигания:
blink_loop: // wait loop ldr r0, =1000000 delay_loop: subs r0, 1 bne delay_loop // toggle PC13 ldr r0, =0x40011000 + 0x0c // GPIOC_ODR ldr r1, [r0] eor r1, 1
Ему соответствует псевдокод
blink_loop: // wait loop r0 := 1000000; delay_loop: r0 := r0 - 1; if r0 != 0 goto delay_loop; // toggle PC13 r0 := 0x4001_1000 + 0x0c; r1 := memory[r0]; r1[13] := ~r1[13]; memory[r0] := r1; goto blink_loop;
Прошиваем его командой make flash и если всё сделано правильно, то синий светодиод замигает.
Полный код доступен на гитхабе.
STM32 RTC, Calendar.
Начнем с того, что RTC — это аббревиатура которая расшифровывается следующим образом Real-time clock или по-русски, часы реального времени. В былые времена, при использовании МК AVR в качестве RTC, использовал отдельную микросхему, общение с которой происходило по определенному протоколу. У STM32 RTC же представляет собой модуль, реализованный внутри МК.
- Автоматическое пробуждение во всех режимах энергосбережения
- Независимый BCD таймер счетчик. Отсчет времени и календарь реализованы аппаратно, с возможностью настройки сигнализирующих прерываний.
- Программный флаг пробуждения с возможностью вызова прерывания.
- Два 32-х разрядных регистра, в которых хранятся секунды, минуты, часы(в 12 часовом или 24 часовом формате), день недели, день месяца, месяц и год. Секунды хранятся в двоичном формате, все остальное в двоично-десятичном.
- Компенсация длинны месяца(а также високосного года) выполняется автоматически. Также возможна компенсация летнего времени.
- Два 32-х разрядных регистра программируемых сигнализирующих прерываний.
- Наличие калибровки для компенсации отклонения кварцевого резонатора.
- После сброса область RTC защищена от случайной записи.
- До тех пор пока напряжение питания RTC остается в рабочем диапазоне, он будет работать в не зависимости от режима работы(Run mode, low-power mode or under reset).
- Для повышения точности, можно синхронизировать часы с сетью 50 или 60Hz.
- Возможно записывать время возникновения события, так называемый Time-stamp.
- Определение вмешательства(tamper), при этом сбрасываются все backup регистры.
- Alarm A
- Alarm B
- Wakeup interrupt
- Time-stamp
- Tamper detection
В качестве примера предлагаю рассмотреть как запустить часы и настроить календарь, но перед этим рассмотрим внутреннюю структуру RTC.

- LSI — низкочастотный внутренний RC-генератор на 40 KHz.
- LSE — внешний, "часовой" кварцевый резонатор на 32 768 Hz.
- HSE/32 — внешний высокочастотный кварцевый резонатор с предделителем 32.

LSEON(LSE oscillator enable) — включение LSE.
LSERDY(LSE oscillator ready) — флаг готовности LSE.
LSEBYP(LSE oscillator bypass) — единица в этом бите позволяет тактировать RTC от внешнего источника. Внешний тактовый сигнал (меандр, синус или треугольник) со скважностью 50% подаётся только на вывод OSC32_IN, при этом вывод OSC32_OUT можно использовать в качестве GPIO.
- 00: нижний
- 01: между нижним и средним
- 10: между средним и высоким
- 11: высокий, значение по умолчанию
- 01: LSE выступает в качестве источника.
- 10: LSI выступает в качестве источника.
- 11: HSE/32 выступает в качестве источника.
BDRST(Backup domain software reset) — единица в этом бите полностью сбрасывает значение Backup domain.
После того как выбран источник тактирования необходимо позаботится о том, чтобы счетные регистры RTC тактировались с частотой один 1Hz. Для понижения частоты тактирования до 1Hz предназначены два предделителя, синхронный и асинхронный.

Расчет их значений производится по формуле.

Изначально значения PREDIV_A = 127, PREDIV_S = 255, что позволяет из 32768 Hz получить 1 Hz .
Для установки времени выделен один 32-x битный регистр, разбитый на битовые поля.

Данные в нем хранятся в формате BCD, при чем единицы и десятки в отдельных битовых полях. Думаю пояснения требует только бит PM, отвечающий за формат хранения часов. Если в нем установлен ноль, часы будут тикать от 0 до 24 часов, если единица – от 1 до 12.
С датой все аналогично, она хранится в 32-х битном регистре, разбитом на битовые поля.

Ниже приведен код настройки и запуска часов для STM32F303.
#include "stm32f3xx.h" //определяем новый тип, который содержит в себе структуру с битовыми полями typedef struct sRTC_struct{ unsigned year : 6; unsigned month : 4; unsigned week : 3; unsigned date : 5; unsigned hour : 5; unsigned minute :6; unsigned sec : 6; >RTC_struct; //создаем экземпляр этого типа volatile RTC_struct rtc; void RTC_Init(RTC_struct volatile *value) { // Включаем LSI RCC->CSR |= RCC_CSR_LSION; // Ждем его готовности while ( !(RCC->CSR & RCC_CSR_LSIRDY) ){> // Включаем тактирование модуля PWR RCC->APB1ENR |= RCC_APB1ENR_PWREN; // После сброса регистры RTC находятся в защищенном домене // Для разрешения записи необходимо поставить флаг в PWR->CR PWR->CR |= PWR_CR_DBP; // Выбераем источник тактирования RTC( от низкоскоростного внутреннего источника LSI(40 kHz) ) RCC->BDCR |= RCC_BDCR_RTCSEL_LSI; // Включаем тактиование RTC RCC->BDCR |= RCC_BDCR_RTCEN; RTC_Update(value); > void RTC_Update(RTC_struct volatile *value) { uint32_t TR, DR; // Выключаем защиту от записи RTC->WPR = 0xCA; RTC->WPR = 0x53; // Входим в режим редактирования RTC->ISR |= RTC_ISR_INIT; // Ждем подтверждения входа в режим редактирования while( !(RTC->ISR & RTC_ISR_INITF) ){>; // Устанавливаем асинхронный предделитель на 100(99+1). RTC->PRER = (uint32_t)(99 PRER |= (uint32_t)399; // Устанавливаем время TR = ( ((uint32_t)(value->hour/10*16 + value->hour%10) minute/10*16 + value->minute%10) sec/10*16 + value->sec%10) ); RTC->TR = TR; // Устанавливаем дату DR = (uint32_t)(value->year/10*16 + value->year%10) week/10*16 + value->week%10) month/10*16 + value->month%10) date/10*16 + value->date%10); RTC->DR = DR; // Выходим из режима редактирования RTC->ISR &= ~(RTC_ISR_INIT); // Включаем защиту от записи RTC->WPR = 0xFF; >
В случае если RTC тактируется от внешнего кварца, необходимо инициализацию LSI заменить следующим кодом.
//Проверяем тикают ли часики if(RTC->ISR & RTC_ISR_INITS) return; //Включаем LSE RCC->BDCR |= RCC_BDCR_LSEON; // Ждем его готовности while (!(RCC->BDCR & RCC_BDCR_LSEON)){> //Выбираем LSE в качестве источника (кварц 32768) RCC->BDCR |= RCC_BDCR_RTCSEL_LSE; //Включаем тактирование RTC RCC->BDCR |= RCC_BDCR_RTCEN;
Ошибки POS-терминалов

Нам часто задают вопрос: насколько надежны POS-терминалы и что может сломаться? Как и с любой техникой, в терминалах может выйти из строя абсолютно любая делать, все зависит от того насколько бережного вы обращаетесь с оборудованием и соблюдаете ли правила эксплуатации. Вообще, терминалы рассчитаны на длительный срок эксплуатации, но неаккуратные пользователи этот срок значительно сокращают.
Механические повреждения — самые частые поломки
Например, очень часто работники просто заливают терминалы различными жидкостями — чаем, кофе или соком, что и приводит к выходу из строя оборудования. Сюда же можно добавить различные падения и бросания (да, да, бывает и такое) терминалов, вследствие чего возникают не только механические повреждения, но и проблемы связанные с программным обеспечением. Все вышеперечисленное — не гарантийный случай.
Теперь рассмотрим несколько наиболее частых ошибок, который можно увидеть на экране POS-терминалов Ingenico:
- LLT — терминал в режиме настройки и ожидает загрузки ПО;
- Ошибка сессии SSL — обычно возникает на POS-терминалах с GPRS модулем. Эта проблема с сотовой связью — для устранения ошибки нужно подобрать сотового оператора с наиболее стабильным уровнем сигнала в том месте, где установлен терминал.
Следующие ошибки возникают в результате срабатывания защиты от взлома и "лечатся" только в СЦ Ingenico:
- грустный смайл — :-(;
- сообщения "UNAUTHORIZED", "ALERT IRRUPTION", "ERROR ххххх" — вероятнее всего сработала защита от вскрытия;
- "BAD Signature" при загрузке LLT - терминал не подписан, либо активирован для другого вендора ПО.
Среди POS-терминалов VeriFone можно встретить следующие ошибки и неисправности:
- Не горит подсветка экрана — в сервис;
- DOWNLOAD NEEDED — терминал в режиме загрузки ПО — инженеры по оборудованию в банке производят загрузку ПО и конфигурацию под клиента;
- TAMPER, BAD RTC CHIP, SECURITY ALARM — если перезагрузка/перепрошивка/замена батарейки не помогли — в сервис;
- При печати чека терминал перезагружается — есть вероятность, что поможет замена блока питания;
- Перестал работать магнитный/чиповый ридер — если перезагрузка/перепрошивка не помогли — в сервис.
На этом пока все. Надеюсь информация была для вас полезной. До новых встреч. Успехов в вашем бизнесе!