Top.Mail.Ru
Уведомления
Очистить все

[Решено] Как работает режим сравнения выходного сигнала (Output compare) в STM32?

Dutch
(@dutch)
Active Member

Я новичок в 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 и более кГц?

OSC1
OSC2
OSC3
OSC4
 Кармин Новиелло   Освоение STM32. Пример Chapter 11 example 7

 

Цитата
Topic starter Размещено : 17.03.2022 21:30
Тэги темы
Aveal
(@aveal)
Illustrious Member Admin

Добрый вечер!

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 он уже просто не дойдет.

 

ОтветитьЦитата
Размещено : 17.03.2022 23:24
Dutch
(@dutch)
Active Member

@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 кГц?

ОтветитьЦитата
Topic starter Размещено : 21.03.2022 11:55
Aveal
(@aveal)
Illustrious Member Admin

А на первом канале проблема из пункта 3 точно так же проявляется?

ОтветитьЦитата
Размещено : 21.03.2022 12:08
Dutch
(@dutch)
Active Member

@aveal да, на первом тоже. Я меня местами и каналы осциллографа и провода - результат тот же

ОтветитьЦитата
Topic starter Размещено : 21.03.2022 12:25
Aveal
(@aveal)
Illustrious Member Admin

А можешь проект скинуть базовый полный, я попробую на какой-нибудь плате воспроизвести.

ОтветитьЦитата
Размещено : 21.03.2022 12:29
Dutch
(@dutch)
Active Member

@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

ОтветитьЦитата
Topic starter Размещено : 21.03.2022 12:59
Aveal
(@aveal)
Illustrious Member Admin

@dutch не, это только для предделителя актуально.

ОтветитьЦитата
Размещено : 21.03.2022 13:06
Aveal
(@aveal)
Illustrious Member Admin

@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КГц работает четко.

ОтветитьЦитата
Размещено : 21.03.2022 16:39
Dutch
(@dutch)
Active Member

@aveal  В настройках проекта я включил оптимизацию и второй канал заработал на частоте 100 кГц. Это помогло - спасибо большое!

1 Original (Period 65535) Результат.zip

 

ОтветитьЦитата
Topic starter Размещено : 21.03.2022 19:33
Aveal
(@aveal)
Illustrious Member Admin

@dutch отлично! 👍 

ОтветитьЦитата
Размещено : 21.03.2022 20:25
Поделиться: