Подскажите, как запустить PWM без библиотеки HAL.
Посмотри тут - 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
-
Запрограммировать альтернативную функцию на необходимый вывод порта для данного таймера согласно таблице «Альтернативных функций» из Datasheet;
-
В RCC включить тактирование на соответсвующий таймер.
-
PSC – запрограммировать на деление частоты от тактового генератора.
-
ARR – запрограммировать общую длительность одного периода. (От пары PSC и ARR зависит частота ШИМ);
-
CCRx – записывается ширина импульса для нужного канала. Она не должна равняться значению ARR или быть больше. Иначе на выходе получим не ШИМ, а просто высокий или низкий уровень.
-
CCMRx – программируется какой тип ШИМ будет на соответсвующем выходе;
-
CCER – разрешаем выход для соответсвующего канала на пин;
-
CR1 – бит CEN разрешает работать таймеру.
-
В тактовом генераторе RCC необходимо подать тактирование на соответсвующий порт. Без этого дальнейшие действия бессмысленны.
-
Далее идут шаги относящиеся к самому порту GPIOx. OSPEEDR – регистр скорости порта. Выставляется необходимая скорость работы порта;
-
OTYPER – программируем тип порта PUSH/PULL или Open Drain;
-
PUPDR – Программируем куда идёт подтяжка;
-
MODER – прграммируем выполняемую функцию. Вход/выход и т. д.
/* * Пример включения ШИМ на таймере 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. Для других ядер по другому.