Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
3555
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Таймеры

324

Когда таймер отсчитывает в режиме TIM_COUNTERMODE_DOWN, он запускается со значения Period и ведет обратный отсчет до нуля: когда счетчик достигает конца, срабатывает IRQ таймера и устанавливается флаг UIF (то есть генерируется событие обновления и

HAL_TIM_PeriodElapsedCallback() вызывается HAL).

Напротив, когда таймер отсчитывает в режиме TIM_COUNTERMODE_CENTERALIGNED, он начинает отсчет с нуля до значения Period: это приводит к тому, что срабатывает IRQ таймера и устанавливается флаг UIF (то есть генерируется событие обновления и HAL_TIM_PeriodElapsedCallback() вызывается HAL). Затем таймер начинает обратный отсчет до нуля, и генерируется другое событие обновления (а также соответствующий IRQ).

11.3.5. Режим захвата входного сигнала

Таймеры общего назначения не предназначены для использования в качестве генераторов временного отсчета. Несмотря на то, что их вполне можно использовать для данной работы, эту задачу следует выполнять другими таймерами, такими как базовые таймеры и таймер SysTick. Таймеры общего назначения предлагают гораздо более продвинутые возможности, которые можно использовать для управления другими важными действиями, связанными со временем.

На рисунке 16 показана структурная схема входных каналов в таймере общего назначения25. Как видите, каждый вход подключен к детектору фронта (edge detector), который также оснащен фильтром (input filter), используемым для «борьбы с дребезгом» входного сигнала. Выход детектора фронта поступает в мультиплексор источников (IC1, IC2 и т. д.). Он позволяет «переназначить» входные каналы, если выбранный I/O привязан к другому периферийному устройству. Наконец, выделенный предделитель позволяет «замедлить» частоту входного сигнала, чтобы соответствовать рабочей частоте таймера, если ее нельзя снизить, как мы увидим через некоторое время.

Рисунок 16: Структурная схема входного канала в таймере общего назначения

25 Некоторые таймеры общего назначения (например, TIM14) имеют меньше входных каналов и, следовательно, упрощенную структурную схему входного каскада. Обратитесь к справочному руководству по вашему микроконтроллеру, чтобы узнать точную структурную схему таймера, который вы собираетесь использовать.

1
TIMx _ CLK

Таймеры

325

Режим захвата входного сигнала (Input capture mode), предоставляемый таймерами общего назначения и расширенного управления, позволяет вычислять частоту внешних сигналов, подаваемых на каждый из 4 каналов, которые предоставляют данные таймеры. При этом захват выполняется независимо для каждого канала.

Рисунок 17: Процесс захвата внешнего сигнала, поданного на один из каналов таймера

На рисунке 17 показано, как работает процесс захвата. TIMx – это таймер, сконфигурированный на работу с заданной тактовой частотой TIMx_CLK26. Это означает, что он увеличивает регистр TIMx_CNT до значения Period каждые секунды. Предпо-

ложим, что мы подаем сигнал прямоугольной формы к одному из каналов таймера, и предположим, что мы сконфигурировали таймер с запуском на каждом переднем (нарастающем) фронте входного сигнала, тогда получим, что регистр TIMx_CCRx27 будет обновляться содержимым регистра TIMx_CNT на каждом обнаруженном перепаде. Когда это происходит, таймер генерирует соответствующее прерывание или запрос к DMA, позволяя отслеживать значение счетчика.

Чтобы получить период внешнего сигнала, необходимо выполнить два захвата подряд. Период рассчитывается путем вычитания этих двух значений CNT0 (значение 4 на рисунке 17) и CNT1 (значение 20 на рисунке 17) с использованием следующей формулы:

 

TIMx _ CLK

 

−1

 

Период = Захват

 

 

 

 

(Prescaler +1)(Предделитель канала)(Индекс полярности)

 

 

 

 

 

[4]

где:

Захват = CNT CNT

, если CNT

CNT

1

0

0

1

Захват = (TIMx _ Period CNT )+ CNT

, если CNT

CNT

0

1

0

1

Предделитель канала – это дополнительный предделитель, который можно применить к входному каналу, а Индекс полярности равен 1, если канал сконфигурирован на запуск

26Тактовая частота таймера не зависит от того, как работает таймер (в данном случае режим захвата входного сигнала). Как видно из предыдущих параграфов, тактовый сигнал таймера зависит от частоты шины или внешнего источника тактового сигнала и от соответствующих параметров предделителя.

27CCR – это сокращение от Capture Compare Register (регистр захвата/сравнения), а x – номер канала.

HAL_TIM_IC_ConfigChannel()

Таймеры

326

по переднему (нарастающему) или заднему (спадающему) фронту входного сигнала, или равен 2, если отбираются оба фронта.

Другим важным условием является то, что частота возникновения UEV должна быть ниже частоты отбираемого сигнала. Причина, по которой это имеет значение, очевидна: если таймер работает быстрее, чем отбираемый сигнал, то он переполнится (то есть счетчик истечет до Period), прежде чем сможет произвести выборку фронтов сигнала (см. рисунок 18). По этой причине обычно удобно установить значение Period на максимальное и увеличить Prescaler, чтобы снизить частоту дискретизации.

Рисунок 18: Если таймер работает быстрее, чем происходит выборка сигнала, он переполняется до того, как обнаружатся два передних (нарастающих) фронта

Для конфигурации входных каналов используется функция

и экземпляр структуры Си TIM_IC_InitTypeDef, которая определена следующим образом:

typedef struct {

 

 

uint32_t ICPolarity;

/* Задает активный фронт входного сигнала.

*/

uint32_t ICSelection;

/* Задает вход.

*/

uint32_t ICPrescaler;

/* Задает предделитель захвата входного сигнала.

*/

uint32_t ICFilter;

/* Задает фильтр захвата входного сигнала.

*/

} TIM_IC_InitTypeDef;

 

 

ICPolarity: задает полярность входного сигнала и может принимать значение из

таблицы 16.

ICSelection: задает используемый вход таймера. Может принимать значение из

таблицы 17. Можно выборочно переназначить входные каналы на разные входные источники, то есть (IC1, IC2) отобразить на (TI2, TI1) и (IC3, IC4) отобразить на (TI4, TI3). Обычно это используется для различения захвата переднего фронта от захвата заднего фронта для сигналов, где TВКЛ отличается от TВЫКЛ . Также воз-

можно захватывать из такого же внутреннего канала, называемого TRC, подключенного к источникам ITR0..ITR3.

ICPrescaler: конфигурирует каскад предделителя заданного входа. Может прини-

мать значение из таблицы 18.

ICFilter: это 4-битное поле задает частоту, используемую для отбора внешнего

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

Таймеры

327

Таблица 16: Доступная полярность захвата входного сигнала

Режим полярности

захвата

входного сигнала

Описание

TIM_ICPOLARITY_RISING

Захватывается передний фронт внешнего сигнала

TIM_ICPOLARITY_FALLING

Захватывается задний фронт внешнего сигнала

TIM_ICPOLARITY_BOTHEDGE

Передний и задний фронты внешнего сигнала опреде-

 

ляют период захвата (это увеличивает частоту дискрети-

 

зации сигнала)

Таблица 17: Доступный выбор режимов захвата входного сигнала

Выбор режима захвата входного

 

сигнала

Описание

TIM_ICSELECTION_DIRECTTI

Вход 1, 2, 3 или 4 таймера выбран для подключения к

 

IC1, IC2, IC3 или IC4 соответственно.

TIM_ICSELECTION_INDIRECTTI

Вход 1, 2, 3 или 4 таймера выбран для подключения к IC2, IC1, IC4 или IC3 соответственно.

TIM_ICSELECTION_TRC

Вход 1, 2, 3 или 4 таймера выбран для подключения к TRC (линия запуска на рисунке 3 – вход TRC выделен красным на рисунке 16)

Таблица 18: Доступные режимы предделителя захвата входного сигнала

Режим предделителя захвата

 

входного сигнала

Описание

 

 

TIM_ICPSC_DIV1

Предделитель не используется

TIM_ICPSC_DIV2

Захват выполняется один раз каждые 2 события

TIM_ICPSC_DIV4

Захват выполняется один раз каждые 4 события

TIM_ICPSC_DIV8

Захват выполняется один раз каждые 8 события

Теперь самое время увидеть практический пример. Мы будем перестраивать Пример 2 данной главы так, чтобы производить выборку частоты переключения вывода PA5 (тот, что подключен к светодиоду LD2) через канал 1 таймера TIM3 (в микроконтроллере STM32F030 этот вывод совпадает с выводом PA6). Таким образом, мы сконфигурируем канал 1 как вывод захвата входного сигнала и сконфигурируем его в режиме DMA, чтобы он инициировал запрос TIM3_CH1 для автоматического заполнения временного буфера, в котором будет храниться значение регистра TIM3_CNT при обнаружении переднего (нарастающего) фронта входного сигнала.

Прежде чем проанализировать функцию main(), лучше всего взглянуть на процедуры инициализации TIM3.

Имя файла: src/main-ex6.c

59/* Функция инициализации TIM3 */

60void MX_TIM3_Init(void) {

61TIM_IC_InitTypeDef sConfigIC;

63htim3.Instance = TIM3;

64htim3.Init.Prescaler = 0;

65htim3.Init.CounterMode = TIM_COUNTERMODE_UP;

66htim3.Init.Period = 65535;

67htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

 

Таймеры

328

68

HAL_TIM_IC_Init(&htim3);

 

69

 

 

70sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;

71sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;

72sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;

73sConfigIC.ICFilter = 0;

74HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1);

75}

76

77void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* htim_ic) {

78GPIO_InitTypeDef GPIO_InitStruct;

79if (htim_ic->Instance == TIM3) {

80/* Разрешение тактирования переферии */

81__HAL_RCC_TIM3_CLK_ENABLE();

82

83/**Конфигурация GPIO таймера TIM3

84PA6 ------> TIM3_CH1

85*/

86GPIO_InitStruct.Pin = GPIO_PIN_6;

87GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

88GPIO_InitStruct.Pull = GPIO_NOPULL;

89GPIO_InitStruct.Speed = GPIO_SPEED_LOW;

90GPIO_InitStruct.Alternate = GPIO_AF1_TIM3;

91HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

92

93/* Инициализация DMA периферии */

94hdma_tim3_ch1_trig.Instance = DMA1_Channel4;

95hdma_tim3_ch1_trig.Init.Direction = DMA_PERIPH_TO_MEMORY;

96hdma_tim3_ch1_trig.Init.PeriphInc = DMA_PINC_DISABLE;

97hdma_tim3_ch1_trig.Init.MemInc = DMA_MINC_ENABLE;

98hdma_tim3_ch1_trig.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

99hdma_tim3_ch1_trig.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

100hdma_tim3_ch1_trig.Init.Mode = DMA_NORMAL;

101hdma_tim3_ch1_trig.Init.Priority = DMA_PRIORITY_LOW;

102HAL_DMA_Init(&hdma_tim3_ch1_trig);

103

104/* Несколько указателей дескриптора DMA периферии указывают на один

105и тот же дескриптор DMA.

106Имейте в виду, что существует только один канал для выполнения всех запросов к DMA. */

107__HAL_LINKDMA(htim_ic, hdma[TIM_DMA_ID_CC1], hdma_tim3_ch1_trig);

108}

109}

MX_TIM3_Init() конфигурирует таймер TIM3 так, чтобы он работал на частоте, равной ~732 Гц. Первый канал затем конфигурируется для запуска события захвата (TIM3_CH1) на каждом переднем фронте входного сигнала. Затем HAL_TIM_IC_MspInit() конфигурирует аппаратную часть (вывод PA6, подключенный к каналу 1 таймера TIM3) и дескриптор DMA, используемый для конфигурации запроса TIM3_CH1.

Таймеры

329

Здесь мы должны отметить два момента. Прежде всего, DMA сконфигурирован так, что выравнивание данных периферии и памяти установлено для выполнения 16-битной передачи, поскольку регистр счетчика таймера размером 16 бит. В тех микроконтроллерах, где таймеры TIM2 и TIM5 имеют регистр счетчика размером 32 бита, необходимо сконфигурировать DMA для выполнения передачи с выравниванием по словам. Далее, поскольку мы используем HAL_TIM_IC_Init() в строке 68, HAL предназначен для вызова функции HAL_TIM_IC_MspInit() для выполнения низкоуровневых инициализаций вместо

HAL_TIM_Base_MspInit().

Имя файла: src/main-ex6.c

20uint8_t odrVals[] = { 0x0, 0xFF };

21uint16_t captures[2];

22volatile uint8_t captureDone = 0;

24int main(void) {

25uint16_t diffCapture = 0;

26char msg[30];

28

HAL_Init();

29

 

30Nucleo_BSP_Init();

31MX_DMA_Init();

32

33MX_TIM3_Init();

34MX_TIM6_Init();

36HAL_DMA_Start(&hdma_tim6_up, (uint32_t) odrVals, (uint32_t) &GPIOA->ODR, 2);

37__HAL_TIM_ENABLE_DMA(&htim6, TIM_DMA_UPDATE);

38 HAL_TIM_Base_Start(&htim6); 39

40 HAL_TIM_IC_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t*) captures, 2); 41

42while (1) {

43if (captureDone != 0) {

44if (captures[1] >= captures[0])

45diffCapture = captures[1] - captures[0];

46else

47diffCapture = (htim3.Instance->ARR - captures[0]) + captures[1];

49frequency = HAL_RCC_GetPCLK1Freq() / (htim3.Instance->PSC + 1);

50frequency = (float) frequency / diffCapture;

52sprintf(msg, "Input frequency: %.3f\r\n", frequency);

53HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);

54while (1);

55}

56}

57 }