Проблема с ограничением частоты таймеров на STM32F7

У меня проблемы с установкой таймеров на плате обнаружения STM32F7 на 500 кГц. По какой-то причине у меня, кажется, максимальная частота составляет около 370 кГц. Я переключаю контакт GPIO с осциллографом на вход и просто меняю период на таймере, чтобы отслеживать, что происходит.

Я использую CubeMX для создания файлов проекта и инициализирую свой таймер:

static void MX_TIM1_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 108;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1;
  sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_OC1REF;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

Затем я запускаю таймер в режиме прерывания:

if(HAL_TIM_Base_Start_IT(&htim1) != HAL_OK)
        {
            Error_Handler();
        }

а затем переключить вывод GPIO по истечении периода:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

    if(htim->Instance == TIM1)
    {

        HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_6);

    }
}

Вывод GPIO установлен как:

  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

Однако я поигрался с Period на таймере и получил следующие результаты:

539 = 100KHz
239 = 300KHz
215 = 330KHz
108 = 369Khz

Я бы ожидал получить 500 кГц с периодом 215, но это не тот случай. Что-то не так с моими настройками?


person MaskedAfrican    schedule 11.07.2019    source источник


Ответы (1)


Настройки таймера правильные. Код прерывания слишком медленный.

Библиотека HAL не подходит для приложений, критичных по времени. HAL пытается (и терпит неудачу) обрабатывать все возможные варианты использования универсальными функциями, что означает много ненужной обработки со связанным задержки. Используйте простой обработчик прерывания вместо TIM1_IRQHandler(), предоставляемого HAL, который просто очищает состояние прерывания и инвертирует бит непосредственно в GPIOG-> ODR. Это должно делать:

void TIM1_IRQHandler(void) {
    TIM1->SR = ~TIM_SR_UIF;
    GPIOG->ODR ^= (1 << 6);
}

всего 2 строки кода вместо более 100 строк HAL_TIM_IRQHandler(). Должен работать до 1 МГц, а может и больше.

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

Таймер может выводить прямоугольную волну (сигнал ШИМ) на свои выходные каналы с частотами до половины тактовой частоты источника. Найдите режим PWM с выравниванием по краям в Справочном руководстве.

person followed Monica to Codidact    schedule 11.07.2019
comment
Я просто использую переключение контактов GPIO, чтобы выяснить, какая частота. Моя цель - использовать таймер в качестве внешнего триггера для АЦП. Тогда мой вопрос: если настройки таймера правильные и я не переключаю GPIO, значит ли это, что АЦП все равно сработает в нужное время? Или есть способ использовать библиотеки LL для этого? - person MaskedAfrican; 12.07.2019
comment
@MaskedAfrican После запуска таймер работает с заданной скоростью, несмотря ни на что. Он будет послушно запускать прерывания, преобразования DMA, что бы вы ему ни сказали, через равные промежутки времени. Таймеру не важно, переключает ли код какие-либо контакты, завершается ли он до следующего запроса прерывания, или есть ли какой-либо код вообще. Если код будет слишком медленным, он пропустит некоторые прерывания, но не заставит таймер останавливаться и ждать, пока код догонит. - person followed Monica to Codidact; 12.07.2019
comment
@MaskedAfrican То же самое и с АЦП, таймер с радостью запустит его на любой частоте до 108 МГц, но вы должны убедиться, что интервал запуска больше суммы времени стабилизации АЦП, времени преобразования и третьего раза, когда я могу сейчас припоминаю. Если АЦП не успевает, он перестанет пропускать триггеры, и вы получите меньше выборок, чем ожидалось. Теперь подумайте внимательно: в коде возникают проблемы с инвертированием одного бита 600 000 раз в секунду. Как бы он справился с образцами, поступающими почти с такой скоростью? Едва ли хватает времени на один-единственный вызов библиотеки. - person followed Monica to Codidact; 12.07.2019
comment
ты прав. так что, по сути, библиотеки LL - это то, что вам нужно. Я посмотрю на реализацию и отчитаюсь. Я использовал ваш TIM1_IRQHandler, и он переключился на 500 кГц, поэтому я думаю, что на этот вопрос есть ответ. - person MaskedAfrican; 12.07.2019