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

[Решено] STM32 PWM без HAL.

APPE
 APPE
(@appe)
Level 0

Подскажите, как запустить PWM без библиотеки HAL.

Цитата
Topic starter Размещено : 21.04.2022 17:53
Aveal
(@aveal)
Top level Admin

Посмотри тут - https://community.st.com/s/question/0D53W00001TdvdgSAB/how-to-start-pwm-without-using-hal

Вот вырезка:

  • enable GPIOx and TIMx clock in RCC
  • set the appropriate pin's GPIOx_MODER to AF, and GPIOx_AFR to appropriate value; set also GPIOx_OSPEEDR if there's a need for higher slew rates
  • set TIMx_CCMRx to set given channel to Output Compare, and one of the PWM modes
  • set TIMx_CCRx to set duty cycle
  • enable given channel in TIMx_CCER
  • in Advanced timers, set TIMx_BDTR.MOE
  • set TIMx_PSC/TIMx_ARR to set period
  • set TIMx_CR1.CEN to start timer's counter
ОтветитьЦитата
Размещено : 21.04.2022 18:34
Эдуард
(@eduard)
Level 2 Moderator
Методика и последовательность инициализации таймеров TIM для вывода ШИМ на какой либо вывод.
Не все таймеры имеют возможность выводить ШИМ на пин. Для того, что бы узнать какой таймер и на какой вывод может выводить, нужно посмотреть эту информацию или в Datasheet или в CubeMX.
Последовательность программирования канала ШИМ после определения какой таймер и на какую ногу будет выводить необходимую последовательность:
  1. Запрограммировать альтернативную функцию на необходимый вывод порта для данного таймера согласно таблице «Альтернативных функций» из Datasheet;

  2. В RCC включить тактирование на соответсвующий таймер.

  3. PSC – запрограммировать на деление частоты от тактового генератора.

  4. ARR – запрограммировать общую длительность одного периода. (От пары PSC и ARR зависит частота ШИМ);

  5. CCRx – записывается ширина импульса для нужного канала. Она не должна равняться значению ARR или быть больше. Иначе на выходе получим не ШИМ, а просто высокий или низкий уровень.

  6. CCMRx – программируется какой тип ШИМ будет на соответсвующем выходе;

  7. CCER – разрешаем выход для соответсвующего канала на пин;

  8. CR1 – бит CEN разрешает работать таймеру.

При таком подходе тактирование таймера осуществляется от внутреннего тактового генератора. Если нужно внешнее тактирование, там чуть посложнее.
ОтветитьЦитата
Размещено : 21.04.2022 21:16
Эдуард
(@eduard)
Level 2 Moderator
Методика и последовательность инициализации GPIO
Для программирования портов ввода/вывода необходимо пошагово запрограммировать необходимые регистры GPIO. Эти шаги справедливы как для отдельного пина порта, так и для группы пинов.
Шаги следующие:
  1. В тактовом генераторе RCC необходимо подать тактирование на соответсвующий порт. Без этого дальнейшие действия бессмысленны.

  2. Далее идут шаги относящиеся к самому порту GPIOx. OSPEEDR – регистр скорости порта. Выставляется необходимая скорость работы порта;

  3. OTYPER – программируем тип порта PUSH/PULL или Open Drain;

  4. PUPDR – Программируем куда идёт подтяжка;

  5. MODER – прграммируем выполняемую функцию. Вход/выход и т. д.

На этом программирование пина закончено. Для проверки можно через регистр BSSR проверить, всё ли у нас получилось выставляя пин в высокое или низкое состояние и контролируя его или светодиодом или осцилографом.
Все режимы, которые поддерживаются данным портом нужно смотреть в Reference manual на соответствующий МК.
Кроме этого пин можно подключить к внутреннему устройству. Например UART или таймер. Какое устройство можно подключить к данному пину нужно смотреть или в CubeMX, или Datasheet на соответствующий МК.
Такое подключение называется «Альтернативная функция». Она производится с помощью двух регистров ARFL и AFRH. Так как на каждый пин можно назначить до 15 альтернативных функций, под них отводится 4 бита. Значит с помощью одного регистра можно назначить только 8 пинов. Поэтому регистров два — младший и старший. В CMSIS обращение идёт к ним как AFR[0] – младший, AFR[1] – старший.
 
Это программирование GPIO
ОтветитьЦитата
Размещено : 21.04.2022 21:23
APPE
 APPE
(@appe)
Level 0

@aveal @eduard большое спасибо!

ОтветитьЦитата
Topic starter Размещено : 22.04.2022 07:37
Эдуард
(@eduard)
Level 2 Moderator
/*
 * Пример включения ШИМ на таймере TIM2
 * К пину PA0 должет быть подключен светодиод через резистор на землю.
 * За 4 секунды диод должен плавно загореться и резко погаснуть, цикл повторяется.
 */
#include "STM32.h"


int main(void)
{
  SystemClock_Config();

  /*
   * Инициализация порта PA0 для работы с ШИМ на таймере TIM2
   */
  RCC->APB2ENR  |= RCC_APB2ENR_IOPAEN;                                // Включаем тактирование порта A
  GPIOA->CRL    |= GPIO_CRL_CNF0_1;                                   // Включаем альтернативную функцию на выход с Push/Pull
  GPIOA->CRL    |= (GPIO_CRL_MODE0_0 | GPIO_CRL_MODE0_1);             // Включаем PA0 на выход на масксимальной частоте
  /*
   * Инициализация TIM2->CH1 на генерацию PWM
   */
  RCC->APB1ENR  |= RCC_APB1ENR_TIM2EN;                                // Включаем тактирование таймера
  TIM2->PSC     |= 7199;                                              // При тактовой частоте 72 МГц, тактирование таймера 10КГц
  TIM2->ARR     |= 100;                                               // Период ШИМ. Тактовая частота ШИМ будет равняться 10 000/100=100Гц
  TIM2->CCR1    |= 20;                                                // Коэффициент заполнения ШИМ 20% (При таких значениях делителей).
  TIM2->CCMR1   |= (0x110 << TIM_CCMR1_OC1M_Pos);                     // Включаем PWM первого типа
  TIM2->CCER    |= TIM_CCER_CC1E;                                     // Разрешаем первому каналу плеваться на выход
  TIM2->CR1     |= TIM_CR1_CEN;                                       // Включаем таймер
  /*
   * С этого момента на выводе PA0 должен появиться ШИМ
   */
  uint8_t PWM_Fill = 20;                                              // Здесь будем хранить коэффициент заполнения

  while (1)
  {
    TIM2->CCR1  = PWM_Fill;                                           // Закидываем коэффициент заполнения в регистр захвата сравнения первого канала таймера
    delay(500);                                                       // Задержка 0.2 сек
    if(PWM_Fill == 90)                                                // Значение коэффициента заполнения максимально?
      PWM_Fill = 10;                                                  // Да. Сбрасываем до 10
    else
      PWM_Fill += 10;                                                 // Нет. Прибавляем 10
  }
}

 

Это для TIM2. Для TIM1 добавится ещё одна команда.

Инициализация GPIO для STM32F103. Для других ядер по другому.

ОтветитьЦитата
Размещено : 22.04.2022 18:45
Поделиться: