Как Вы знаете в STM32 много 16 битных таймеров. При этом их разрядности порой мало для получения тайм штампов. При этом 32-битных таймеров всего два: TIM2 и TIM5.

Таймер

Разрядность

Системная шина

Каналов компараторов

TIM1

16

APB2

4

TIM2

32

APB1

4

TIM3

16

APB1

4

TIM4

16

APB1

4

TIM5

32

APB1

4

TIM6

16

APB1

0

TIM7

16

APB1

0

TIM8

16

APB2

4

TIM9

16

APB2

2

TIM10

16

APB2

2

TIM11

16

APB2

2

TIM12

16

APB1

2

TIM13

16

APB1

2

TIM14

16

APB1

2

Определения

up-time - время с момента включения электропитания на электронную плату
тайм штамп - временная отметка. По сути это и есть up_time. Нужна для того, чтобы подписывать строчку в UART-Log-е загрузки прошивки.

В чем проблема?
Проблема в том, что в STM32 большинство таймеров обладают очень низкой разрядностью. Двенадцать таймеров 16 битные. Есть только два 32 битных таймера и они обычно чем-то заняты: моторы, счетчики импульсов и пр.

При этом большинство настоящих прошивок требуют возможности получать микросекундные тайм штампы. Это нужно для выдерживания пауз менее 1 ms, для импровизированных планировщиков на основе limiter-ов, для подписывания логов в UARTе и прочего. То есть нужен таймер, который увеличивается на +1 каждую одну микросекунду и не переполняется в обозримой перспективе.

Проблема с том что в случае 16 битного таймера такой таймер будет переполняться каждые 65 ms. Это ни о чем. Человек моргает и то продолжительнее. Переполнение за 65 ms - это плохо.

разрядность

Тактирование, MHz

Макс число

Переполнение,s

Переполнение,min

16

1

65536

0.065536

0.00109

32

1

4294967296

4294.96

71

64

1

18446744073709551616

18446744073709

307445734561

Постановка задачи.
Из двух 16 бит таймеров собрать один 32 битный таймер.

Таймер

Биты

Режим работы

PSC

Разрядность

TIM3

0...15

master

47

16

TIM9

16....31

slave

0

16

Реализация

Хорошая новость заключается в том, что микроконтроллеры STM32 позволяют собирать каскадом составные аппаратные таймеры. Схемотехнически это выглядит так.

Надо отметить, что в STM32 не всяким таймером можно тактировать конкретный аппаратный таймер (TIM9). Например Timer 9 может тактировать одним из 4х таймеров на выбор. Выбор осуществляется трёхбитовым битовым полем Trigger selection (TS) в регистре TIMx_SMCR

Ведомый таймер (TIM9)

Trigger selection

Trigger selection

Ведущий таймер \/

dec

bin

TIM2

0

0b00

TIM3

1

0b01

TIM10

2

0b10

TIM11

3

0b11

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

Каждый отдельный таймер обладает целым железнодорожным вокзалом для выбора источника тактирования. План в том, чтобы сконфигурировать тактирование по такому маршруту.

Настройка ведущего таймера TIM3

--Подать тактирование на Timer3. В регистре RCC_APB1ENR установить бит 1.

--Активировать тактирование от внутренней шины
--В регистре TIMx_CR1 поле Clock division установить в ноль CKD=0. Пред делитель для тактовых импульсов.
--Переключить таймер3 в режим мастера. В регистре TIMx_CR2 в поле MMS ( Master mode selection ) прописать значение 2=0b10. Это для генерирования события Update. При переполнении таймера произойдет тактирование ведомого таймера. При переполнении ведущего таймера ведомый таймер увеличится на единицу.
--Прописать пред делитель, чтобы получилось инкрементирование каждый 1us. Значение пред делителя зависит от частоты тактирования таймера.
--Активировать таймер 3. Прописать в поле CEN=1 (Counter enable). После этого таймер начнет считать.
Сырые значения регистров выглядят так

-->tdrr 3
Base:0x40000400,Cnt:20
+-----+--------------------+-------+------+------------+------------+
|  N  |        Name        |offset | size |    Addr    |   ValHex   |
+-----+--------------------+-------+------+------------+------------+
|   1 |           TIMx_CR1 | 0x000 |    0 | 0x40000400 | 0x00000001 |
|   2 |           TIMx_CR2 | 0x004 |    4 | 0x40000404 | 0x00000020 |
|   3 |          TIMx_SMCR | 0x008 |    4 | 0x40000408 | 0x00000000 |
|   4 |          TIMx_DIER | 0x00c |    4 | 0x4000040c | 0x00000000 |
|   5 |            TIMx_SR | 0x010 |    4 | 0x40000410 | 0x0000001f |
|   6 |           TIMx_EGR | 0x014 |    4 | 0x40000414 | 0x00000000 |
|   7 |         TIMx_CCMR1 | 0x018 |    4 | 0x40000418 | 0x00000000 |
|   8 |         TIMx_CCMR2 | 0x01c |    4 | 0x4000041c | 0x00000000 |
|   9 |          TIMx_CCER | 0x020 |    4 | 0x40000420 | 0x00000000 |
|  10 |           TIMx_CNT | 0x024 |    4 | 0x40000424 | 0x00009731 |
|  11 |           TIMx_PSC | 0x028 |    4 | 0x40000428 | 0x0000002f |
|  12 |           TIMx_ARR | 0x02c |    4 | 0x4000042c | 0x0000ffff |
|  13 |           TIMx_RCR | 0x030 |    4 | 0x40000430 | 0x00000000 |
|  14 |          TIMx_CCR1 | 0x034 |    4 | 0x40000434 | 0x00000000 |
|  15 |          TIMx_CCR2 | 0x038 |    4 | 0x40000438 | 0x00000000 |
|  16 |          TIMx_CCR3 | 0x03c |    4 | 0x4000043c | 0x00000000 |
|  17 |          TIMx_CCR4 | 0x040 |    4 | 0x40000440 | 0x00000000 |
|  18 |          TIMx_BDTR | 0x044 |    4 | 0x40000444 | 0x00000000 |
|  19 |           TIMx_DCR | 0x048 |    4 | 0x40000448 | 0x00000000 |
|  20 |          TIMx_DMAR | 0x04c |    4 | 0x4000044c | 0x00000001 |
+-----+--------------------+-------+------+------------+------------+

Настройка ведомого таймера TIM9

--Подать тактирование на Timer9. В регистре RCC_APB2ENR установить в 1 бит 16 (RCC_APB2ENR_TIM9EN). Это нужно чтобы появилась возможность прописывать регистры аппаратного таймера. Без активации тактирования настройки не применятся.

-- В регистре TIMx_CR1 поле Clock division установить в ноль CKD=0. Пред делитель для тактовых импульсов.

TIMx_CR1
TIMx_CR1

--В регистре TIMx_SMCR (slave mode control register) поле Slave mode selection установить в SMS=7 ( External Clock mode 1 - Rising edges of the selected trigger (TRGI) clock the counter. )
--В регистре TIMx_SMCR поле Trigger selection установить в значение TS=1-(Internal Trigger 1 (ITR1)) . После инициализации в регистре TIMx_SMCR должно оказаться значение 0x17 = 0b1_0111.

TIMx_SMCR
TIMx_SMCR

--Активировать таймер 9. Прописать в поле CEN=1 (Counter enable). После этого таймер начнет считать.

-->tdrr 9
Base:0x40014000,Cnt:20
+-----+--------------------+-------+------+------------+------------+
|  N  |        Name        |offset | size |    Addr    |   ValHex   | 
+-----+--------------------+-------+------+------------+------------+
|   1 |           TIMx_CR1 | 0x000 |    0 | 0x40014000 | 0x00000001 |
|   2 |           TIMx_CR2 | 0x004 |    4 | 0x40014004 | 0x00000000 |
|   3 |          TIMx_SMCR | 0x008 |    4 | 0x40014008 | 0x00000017 |
|   4 |          TIMx_DIER | 0x00c |    4 | 0x4001400c | 0x00000000 |
|   5 |            TIMx_SR | 0x010 |    4 | 0x40014010 | 0x00000040 |
|   6 |           TIMx_EGR | 0x014 |    4 | 0x40014014 | 0x00000000 |
|   7 |         TIMx_CCMR1 | 0x018 |    4 | 0x40014018 | 0x00000000 |
|   8 |         TIMx_CCMR2 | 0x01c |    4 | 0x4001401c | 0x00000000 |
|   9 |          TIMx_CCER | 0x020 |    4 | 0x40014020 | 0x00000000 |
|  10 |           TIMx_CNT | 0x024 |    4 | 0x40014024 | 0x00003eab |
|  11 |           TIMx_PSC | 0x028 |    4 | 0x40014028 | 0x00000000 |
|  12 |           TIMx_ARR | 0x02c |    4 | 0x4001402c | 0x0000ffff |
|  13 |           TIMx_RCR | 0x030 |    4 | 0x40014030 | 0x00000000 |
|  14 |          TIMx_CCR1 | 0x034 |    4 | 0x40014034 | 0x00000000 |
|  15 |          TIMx_CCR2 | 0x038 |    4 | 0x40014038 | 0x00000000 |
|  16 |          TIMx_CCR3 | 0x03c |    4 | 0x4001403c | 0x00000000 |
|  17 |          TIMx_CCR4 | 0x040 |    4 | 0x40014040 | 0x00000000 |
|  18 |          TIMx_BDTR | 0x044 |    4 | 0x40014044 | 0x00000000 |
|  19 |           TIMx_DCR | 0x048 |    4 | 0x40014048 | 0x00000000 |
|  20 |          TIMx_DMAR | 0x04c |    4 | 0x4001404c | 0x00000000 |
+-----+--------------------+-------+------+------------+------------+

Теперь каждый раз, когда надо получить тайм штамп можно просто прочитать два 16 бит регистра и скомпоновать одно 32битное значение. Однако один момент. TIM9->CNT это volatile переменная. Мы можем попасть на момент обновления регистра CNT. Поэтому надо выполнять как минимум два чтения. Убедиться, что ведомый таймер TIM9->CNT за стабилизировался и только после этого уже компоновать финальный тайм штамп.


uint32_t time_stamp_get_us( void ) {
    uint32_t tim_word_lo = 0x0000; 
    uint32_t tim_word_hi = 0x0000;
    do {
        tim_word_lo = TIM3->CNT;
        tim_word_hi = TIM9->CNT;
    } while( tim_word_hi != TIM9->CNT );
    uint32_t timestamp_dword = (tim_word_hi<<16) | tim_word_lo;
    return timestamp_dword;
}

Достоинства

1) Не происходят частые прерывания по таймеру и, как следствие, не тормозится основная прошивка.
2) Бесхозные 16ти битные таймеры внезапно начинают приносить реальную пользу.
3) Появляется еще один 32 битный таймер буквально из неоткуда.

Недостатки

1) После 72ой минуты 32 таймер переполнится и начнет считать заново. Можно генерировать прерывания и по slave таймеру, умножать счетчик прерываний на 0xFFFFFFFF и прибавлять cnt32. В результате получится уже тайм штамп типа uint64_t.

Итог
Удалось собрать 32 битный таймер буквально из подручных материалов. Каскадирование таймеров может быть хорошим подспорьем в решении прикладных задач прошивки. Это открывает дорогу для генерирования полноценных тайм штампов в прошивках на STM32.

Вопросы

--Можно ли на STM32 собрать из четырех 16 битных таймеров собрать один 64 битный таймер? Может ли таймер работать одновременно в режиме master и slave?

Only registered users can participate in poll. Log in, please.
Вы добавляете в прошивки составные каскадные таймеры?
34.78%да8
65.22%нет15
23 users voted. 6 users abstained.