Я новичок в STM32 и изучаю книгу Кармин Новиелло - Освоение STM32. Застрял на выполнении примера по работе таймера в режиме сравнения выходного сигнала «main-ex7.c» главы 11 (стр. 333): «В следующем примере показано, как генерировать два выходных сигнала прямоугольной формы, один из которых работает на частоте 50 кГц, а другой – на частоте 100 кГц». Работаю в STM32CubeIDE на отладочной плате STM32F030R8. В этом коде каналы удачно работаю на 50 и 80 кГц: https://pastebin.com/NzFasKps
Вопрос №1: как правильно рассчитать значения для параметров таймера? Я повторяю пример из книги: APB1=48000000; Prescaler=2; Period=65535; Pulse=320. И вижу на осциллограмме (жёлтого цвета в файле OSC1.png) правильный меандр на канале работающем на частоте 50 кГц (48000000 / ((2+1)*320)=50000 Гц). Но как только я меняю значение Period на произвольное, например 60000, то осциллограмма превращается в прерывистые обрывки (оба цвета в файле OSC4.png). Почему изменение Period так влияет, ведь в расчёте значения Pulse он не участвует?
Вопрос №2: в режиме сравнения выходного сигнала, счётчик таймера TIMx_CNT считает до максимального значения (Period). Как только его значение совпадёт с регистром сравнения канала TIMx_CCRx (Pulse) – происходит настроенное изменение уровня канала (высокий, низкий, инвертированный и т.д.). В указанной задаче, чтобы на разных каналах формировать сигнал разной частоты, в HAL_TIM_OC_DelayElapsedCallback каждый раз к текущему значению TIMx_CCRx прибавляется Pulse. Поэтому таймер будет считать ещё Pulse раз, после чего опять вызовет колбэк – это мне понятно. А что происходит, когда таймер уже переполнился и сбросился, а до очередного значения TIMx_CNT он досчитать не успел? Этот импульс получится длиннее, чем нужно? Или как это работает?
Вопрос №3: почему осциллограмма на частотах 100 кГц имеет «Ш» образный, прерывистый неправильный вид. В файлах OSC1.png, OSC2.png и OSC3.png она зелёного цвета. А на частоте 50 кГц имеет нормальную форму – жёлтого цвета. Я так же пробовал 120 кГц – тоже Ш образная, а 80 и 20 – нормальная. Может ли плата не успевать дёргать ногой на частоте 100 и более кГц?
Добрый вечер!
2). Скорее всего макрос __HAL_TIM_SET_COMPARE() просто напрямую в регистр производит запись и все. А регистр 16-битный, соответственно, эффект переполнения по сути аналогичный получается для TIMx->CNT и регистра TIMx->CCR.
Допустим, в данный момент pulse = 65500. В результате в регистр TIMx->CCR записываем 65820 (0x01011C), но поскольку регистр 16-битный, то в регистре окажется значение 284 (0x011C). TIMx->CNT досчитывает до 65535, обнуляется, затем считает с нуля, и при значении 284 срабатывает событие. Таким образом, все работает без сбоев.
1). А это следует как раз из того же самого. Пусть pulse = 59900. Тогда в TIMx->CCR заносится значение 60220, но таймер переполняется на значении 60000, то есть до значения 60220 он уже просто не дойдет.
@aveal Спасибо большое - Ваши советы по вопросам 1 и 2 действительно помогли. Я доработал колбэк с тем, чтобы счётчик канала не становился больше периода таймера. И теперь при Period=60000 каналы корректно работают:
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim) { //увеличу Pulse на 320 для канала 1. Тогда канал будет переключаться на желаемой частоте uint32_t pulse, new_pulse, tim_ARR; int32_t pulse_dif; tim_ARR = htim->Instance->ARR; /* TIMx_CH1 toggling with frequency = 50KHz */ if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { pulse = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); /* Set the Capture Compare Register value */ /*Рассчитаю значение pulse в зависимости от Period - что бы переполнение пульса было синхронно с переполнение таймера*/ new_pulse = pulse + CH1_FREQ; pulse_dif = new_pulse - tim_ARR; if (pulse_dif > 0) { //!!!значение Pulse увеличивать нельзя можно, т.к. оно уже стало больше значения Period new_pulse = pulse_dif;} __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, new_pulse); }
Однако вопрос под номером 3 мне решить не удаётся. Я думал что у контроллера STM32F030R8 возможно имеются какие то ограничения по частоте работы канала в режиме Output compare и поэтому он сбоит на на частотах выше 50 кГц. У меня есть ещё одна плата Nucleo STM32G071RBT6 - я хотел протестировать работу канала на частоте выше 50 кГц на ней. Но как я понял, у этого контроллера отсутствует связь между DMA и GPIO, и моя затея не увенчается успехом.
Не подскажите, какие надо сделать настройки, что бы канал заработал на частоте 100 кГц?
А на первом канале проблема из пункта 3 точно так же проявляется?
@aveal да, на первом тоже. Я меня местами и каналы осциллографа и провода - результат тот же
А можешь проект скинуть базовый полный, я попробую на какой-нибудь плате воспроизвести.
@aveal Два архива с проектами я разместил на фалобменнике dropmefiles на 2 недели. В архиве "1 Original (Period 65535).zip" находится оригинальный код из книги (скопирован с гитхаба книги). А в архиве "2 Modifi callback (Period 60000).zip" изменённая мной функция HAL_TIM_OC_DelayElapsedCallback, что бы она могла работать с произвольным значением Period. Оба проекта выдают непонятную осциллограмму на канале 2 работающем на частоте 100 кГц.
А ещё вопрос: перед записью Prescaler и Period в программу, необходимо из них вычесть единицу. А со значением Pulse необходимо поступать так же? Просто в книге не вычитается единица. А мне кажется что должна, потому что суть Pulse аналогична сути Period
@dutch похоже что просто не хватает быстродействия... То есть:
1. Попадаем сюда __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, (pulse + CH2_FREQ));. Допустим pulse = 5000, CH2_FREQ = 160, записываем в регистр 5160.
2. Когда таймер дошел до этого значения, срабатывает прерывание TIM3_IRQHandler(). Но пока через HAL_TIM_IRQHandler() и т. д. контроллер дойдет до callback'а, до строки из пункта 1, таймер уже успеет досчитать, к примеру, до 5400. А в регистр будет записано 5160 + 160 = 5320. Получается, что просто за счет выполнения кода таймер успел проскочить и теперь событие сработает только после того, как таймер переполнится.
Решение такое для этого примера, как вариант.
1. Поднимаем частоту контроллера:
У меня тут внешний кварц и контроллер STM32F103.
2. В настройках проекта включаем оптимизацию:
После этого на 100КГц работает четко.
@aveal В настройках проекта я включил оптимизацию и второй канал заработал на частоте 100 кГц. Это помогло - спасибо большое!