Продолжаем работать с CMSIS.
Также продолжаем работу с таймерами и в данном занятии мы изучим возможность аппаратной реализации широтно-импульсной модуляции (ШИМ или PWM).
Правда, скорее всего мы данную возможность не изучим, так как мы её уже изучили в уроке 149, в котором мы применяли аппаратную реализацию PWM, используя библиотеку LL. Мы изучили все регистры таймеров, участвующие в реализации PWM и их биты. Поэтому, кто не видел данный урок, обязательно посмотрите. Тем более схема для практического тестирования у нас будет та же, что и в уроке 149, а также почти во всех предыдущих уроках по CMSIS кроме предыдущего
Мы также настроим два таймера — TIM2 с его каналами CH3 и CH4, и TIM3 со всеми его каналами. Тем самым в работе у нас будут ножки портов PA2, PA3, PA6, PA7, PB0 и PB1. Данные ножки будут настроены в альтернативный режим.
Проект для данного занятия был сделан из проекта прошлого урока с именем CMSIS_LED_DYN и назван CMSIS_PWM.
Откроем наш проект в Keil, удалим файл led.c из дерева проекта, так как мы сегодня с индикатором не работаем, а также удалим данный файл и файл led.h из папок src и inc.
Также в файле main.c удалим подключение библиотеки
#include «led.h»
Вот это тоже удалим
extern uint8_t R1,R2,R3,R4;
extern uint16_t num_gl;
В функции main() удалим весь код из тела бесконечного цикла.
Прерываниями от таймеров мы в данном уроке пользоваться не будем, поэтому удалим вызов данного макроса
TIM_EnableIT_UPDATE(TIM2);
Функцию обработки прерываний от таймера TIM2_IRQHandler удалим вместе с телом.
Также отдельно мы не будем настраивать GPIO, поэтому удалим вызов функции настройки из функции main()
GPIO_Init();
Также можно удалить и данную функцию вместе с телом.
Из тела функции инициализации таймера TIM2_Init удалим пока весь код и начнём писать новый.
Добавим несколько локальных переменных, которые нам пригодятся в дальнейшем
1 2 3 |
static void TIM2_Init(void) { uint32_t tmpcr1, tmpcr2, tmpccer, tmpccmr2; |
Включим тактирование таймера
1 2 |
uint32_t tmpcr1, tmpcr2, tmpccer, tmpccmr2; SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM2EN); |
Считаем регистр CR1 в переменную, очистим там ненужные биты (почему именно эти, а потому что именно такие настройки, которые мы с вами подробно исследовали в уроке с LL) и запишем значение переменной обратно в регистр
1 2 3 4 |
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM2EN); tmpcr1 = READ_REG(TIM2->CR1); CLEAR_BIT(tmpcr1, TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD); WRITE_REG(TIM2->CR1, tmpcr1); |
Настроим период авто перезагрузки таймера и делитель. Делитель устанавливаем в 0
1 2 3 4 5 |
WRITE_REG(TIM2->CR1, tmpcr1); //Set the auto-reload value WRITE_REG(TIM2->ARR, 65535); //Set the prescaler value WRITE_REG(TIM2->PSC, 0); |
Включим автогенерацию событий для немедленной перезагрузки делителя и значения счётчика повторений
1 2 3 4 |
WRITE_REG(TIM2->PSC, 0); //Generate an update event to reload the Prescaler //and the repetition counter value (if applicable) immediately SET_BIT(TIM2->EGR, TIM_EGR_UG); |
Отключим автоперезагрузку
1 2 3 |
SET_BIT(TIM2->EGR, TIM_EGR_UG); //Disable auto-reload CLEAR_BIT(TIM2->CR1, TIM_CR1_ARPE); |
Включим тактирование таймера от внутреннего источника
1 2 3 |
CLEAR_BIT(TIM2->CR1, TIM_CR1_ARPE); //Set clock source internal CLEAR_BIT(TIM2->SMCR, TIM_SMCR_SMS | TIM_SMCR_ECE); |
Начнём теперь настройку каналов 3 и 4, по возможности применяя их одновременную инициализацию.
Включим предделитель на каналы 3 и 4
1 2 3 |
CLEAR_BIT(TIM2->SMCR, TIM_SMCR_SMS | TIM_SMCR_ECE); //CH3 AND CH4 Enable Preload SET_BIT(TIM2->CCMR2, TIM_CCMR2_OC4PE | TIM_CCMR2_OC3PE); |
Отключим пока эти каналы для дальнейшей безболезненной настройки
1 2 3 |
SET_BIT(TIM2->CCMR2, TIM_CCMR2_OC4PE | TIM_CCMR2_OC3PE); //Disable the Channel 3 and 4: Reset the CC3E and CC4E Bits CLEAR_BIT(TIM2->CCER, TIM_CCER_CC4E | TIM_CCER_CC3E); |
Считаем в переменные значения регистров
1 2 3 4 5 6 7 |
CLEAR_BIT(TIM2->CCER, TIM_CCER_CC4E | TIM_CCER_CC3E); //Get the TIM2 CCER register value tmpccer = READ_REG(TIM2->CCER); //Get the TIM2 CR2 register value tmpcr2 = READ_REG(TIM2->CR2); //Get the TIM2 CCMR2 register value tmpccmr2 = READ_REG(TIM2->CCMR2); |
Очистим в каналах биты, отвечающие за захват/сравнение
1 2 3 |
tmpccmr2 = READ_REG(TIM2->CCMR2); //Reset Capture/Compare selection Bits CLEAR_BIT(tmpccmr2, TIM_CCMR2_CC4S | TIM_CCMR2_CC3S); |
Включим выходы каналов
1 2 3 4 5 |
CLEAR_BIT(tmpccmr2, TIM_CCMR2_CC4S | TIM_CCMR2_CC3S); //Select the Output Compare Mode MODIFY_REG(tmpccmr2, TIM_CCMR2_OC4M | TIM_CCMR2_OC3M, \ TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | \ TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1); |
Выберем полярность
1 2 3 |
TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1); //Set the Output Compare Polarity CLEAR_BIT(tmpccer, TIM_CCER_CC4P | TIM_CCER_CC3P); |
Активируем наши ножки каналов
1 2 3 |
CLEAR_BIT(tmpccer, TIM_CCER_CC4P | TIM_CCER_CC3P); //Set the Output State CLEAR_BIT(tmpccer, TIM_CCER_CC4E | TIM_CCER_CC3E); |
Запишем значения переменных обратно в регистры
1 2 3 4 5 |
CLEAR_BIT(tmpccer, TIM_CCER_CC4E | TIM_CCER_CC3E); //Write to TIM2 CR2 WRITE_REG(TIM2->CR2, tmpcr2); //Write to TIM2 CCMR2 WRITE_REG(TIM2->CCMR2, tmpccmr2); |
Обнулим регистры сравнения, установив тем самым начальный коэффициент заполнения 0
1 2 3 4 |
WRITE_REG(TIM2->CCMR2, tmpccmr2); //Set the Capture Compare Registers value WRITE_REG(TIM2->CCR3, 0); WRITE_REG(TIM2->CCR4, 0); |
Сохраним значение ещё одной переменной обратно в регистр
1 2 3 |
WRITE_REG(TIM2->CCR4, 0); //Write to TIM2 CCER WRITE_REG(TIM2->CCER, tmpccer); |
Отключим режим Fast сравнения
1 2 3 |
WRITE_REG(TIM2->CCER, tmpccer); //TIM2 OC Disable Fast CLEAR_BIT(TIM2->CCMR2, TIM_CCMR2_OC4FE | TIM_CCMR2_OC3FE); |
Отключим мастер-режим таймера
1 2 3 |
CLEAR_BIT(TIM2->CCMR2, TIM_CCMR2_OC4FE | TIM_CCMR2_OC3FE); //Disable Master Mode Selection CLEAR_BIT(TIM2->CR2, TIM_CR2_MMS); |
Также отключим режим Master/Slave
1 2 3 |
CLEAR_BIT(TIM2->CR2, TIM_CR2_MMS); //Disable Master/Slave mode CLEAR_BIT(TIM2->SMCR, TIM_SMCR_MSM); |
Настроим также ножки порта на соответствующий альтернативный режим
1 2 3 4 5 |
CLEAR_BIT(TIM2->SMCR, TIM_SMCR_MSM); //Set GPIO SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN); MODIFY_REG(GPIOA->CRL, GPIO_CRL_CNF3_0 | GPIO_CRL_CNF2_0 | GPIO_CRL_MODE3_0 | GPIO_CRL_MODE2_0,\ GPIO_CRL_CNF3_1 | GPIO_CRL_CNF2_1 | GPIO_CRL_MODE3_1 | GPIO_CRL_MODE2_1); |
Вот и вся инициализация таймера, хоть и не маленькая, но, благодаря нашим знаниям, полученным ранее, она нам не показалась сильно тяжёлой.
Добавим подобную функцию для таймера 3. Там будет чуть посложнее, так как мы включаем все 4 канала, но не намного
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
//---------------------------------------------------------- static void TIM3_Init(void) { uint32_t tmpcr1, tmpcr2, tmpccer, tmpccmr1, tmpccmr2; SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN); tmpcr1 = READ_REG(TIM3->CR1); CLEAR_BIT(tmpcr1, TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD); WRITE_REG(TIM3->CR1, tmpcr1); //Set the auto-reload value WRITE_REG(TIM3->ARR, 65535); //Set the prescaler value WRITE_REG(TIM3->PSC, 0); //Generate an update event to reload the Prescaler //and the repetition counter value (if applicable) immediately SET_BIT(TIM3->EGR, TIM_EGR_UG); //Disable auto-reload CLEAR_BIT(TIM3->CR1, TIM_CR1_ARPE); //Set clock source internal CLEAR_BIT(TIM3->SMCR, TIM_SMCR_SMS | TIM_SMCR_ECE); //CH1, CH2, CH3 AND CH4 Enable Preload SET_BIT(TIM3->CCMR1, TIM_CCMR1_OC2PE | TIM_CCMR1_OC1PE); SET_BIT(TIM3->CCMR2, TIM_CCMR2_OC4PE | TIM_CCMR2_OC3PE); //Disable the Channel 1, 2, 3 and 4: Reset the CC3E and CC4E Bits CLEAR_BIT(TIM3->CCER, TIM_CCER_CC4E | TIM_CCER_CC3E | \ TIM_CCER_CC2E | TIM_CCER_CC1E); //Get the TIM3 CCER register value tmpccer = READ_REG(TIM3->CCER); //Get the TIM3 CR2 register value tmpcr2 = READ_REG(TIM3->CR2); //Get the TIM3 CCMR1 register value tmpccmr1 = READ_REG(TIM3->CCMR1); //Get the TIM3 CCMR2 register value tmpccmr2 = READ_REG(TIM3->CCMR2); //Reset Capture/Compare selection Bits CLEAR_BIT(tmpccmr1, TIM_CCMR1_CC2S | TIM_CCMR1_CC1S); CLEAR_BIT(tmpccmr2, TIM_CCMR2_CC4S | TIM_CCMR2_CC3S); //Select the Output Compare Mode MODIFY_REG(tmpccmr1, TIM_CCMR1_OC2M | TIM_CCMR1_OC1M, \ TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | \ TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1); MODIFY_REG(tmpccmr2, TIM_CCMR2_OC4M | TIM_CCMR2_OC3M, \ TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | \ TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1); //Set the Output Compare Polarity CLEAR_BIT(tmpccer, TIM_CCER_CC4P | TIM_CCER_CC3P | TIM_CCER_CC2P | TIM_CCER_CC1P); //Set the Output State CLEAR_BIT(tmpccer, TIM_CCER_CC4E | TIM_CCER_CC3E | TIM_CCER_CC2E | TIM_CCER_CC1E); //Write to TIM2 CR2 WRITE_REG(TIM3->CR2, tmpcr2); //Write to TIM2 CCMR1 WRITE_REG(TIM3->CCMR1, tmpccmr1); //Write to TIM2 CCMR2 WRITE_REG(TIM3->CCMR2, tmpccmr2); //Set the Capture Compare Registers value WRITE_REG(TIM3->CCR1, 0); WRITE_REG(TIM3->CCR2, 0); WRITE_REG(TIM3->CCR3, 0); WRITE_REG(TIM3->CCR4, 0); //Write to TIM2 CCER WRITE_REG(TIM3->CCER, tmpccer); //TIM2 OC Disable Fast CLEAR_BIT(TIM3->CCMR1, TIM_CCMR1_OC2FE | TIM_CCMR1_OC1FE ); CLEAR_BIT(TIM3->CCMR2, TIM_CCMR2_OC4FE | TIM_CCMR2_OC3FE ); //Disable Master Mode Selection CLEAR_BIT(TIM3->CR2, TIM_CR2_MMS); //Disable Master/Slave mode CLEAR_BIT(TIM3->SMCR, TIM_SMCR_MSM); //Set GPIO SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPAEN); MODIFY_REG(GPIOA->CRL, GPIO_CRL_CNF7_0 | GPIO_CRL_CNF6_0 | GPIO_CRL_MODE7_0 | GPIO_CRL_MODE6_0,\ GPIO_CRL_CNF7_1 | GPIO_CRL_CNF6_1 | GPIO_CRL_MODE7_1 | GPIO_CRL_MODE6_1); MODIFY_REG(GPIOB->CRL, GPIO_CRL_CNF1_0 | GPIO_CRL_CNF0_0 | GPIO_CRL_MODE1_0 | GPIO_CRL_MODE0_0,\ GPIO_CRL_CNF1_1 | GPIO_CRL_CNF0_1 | GPIO_CRL_MODE1_1 | GPIO_CRL_MODE0_1); } //---------------------------------------------------------- |
Вызовем также и эту функцию в main()
1 2 |
TIM2_Init(); TIM3_Init(); |
Добавим макрос включения каналов
1 2 |
#define TIM_DisableCounter(TIMx) CLEAR_BIT(TIMx->CR1, TIM_CR1_CEN) #define TIM_CC_EnableChannel(TIMx, Channels) SET_BIT(TIMx->CCER, Channels) |
В функции main() перед запуском таймера 2 включим его каналы
1 2 |
TIM3_Init(); TIM_CC_EnableChannel(TIM2, TIM_CCER_CC4E | TIM_CCER_CC3E); |
Также включим каналы таймера 3 и запустим его
1 2 3 |
TIM_EnableCounter(TIM2); TIM_CC_EnableChannel(TIM3, TIM_CCER_CC4E | TIM_CCER_CC3E | TIM_CCER_CC2E | TIM_CCER_CC1E); TIM_EnableCounter(TIM3); |
Изменим тип локальной переменной в целях расширения диапазона значений
uint32_t i;
В бесконечном цикле плавно порегулируем значения регистров также как и в уроке с библиотекой LL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
while(1) { for(i=0;i<768432;i++) { if(i<65536) WRITE_REG(TIM2->CCR3, i); else if ((i>65535)&&(i<131072)) WRITE_REG(TIM2->CCR3,131071-i); else if((i>131071)&&(i<196608)) WRITE_REG(TIM2->CCR4,i-131072); else if ((i>196607)&&(i<262144)) WRITE_REG(TIM2->CCR4,262143-i); else if((i>262143)&&(i<327680)) WRITE_REG(TIM3->CCR1,i-262144); else if ((i>327679)&&(i<393216)) WRITE_REG(TIM3->CCR1,393215-i); else if((i>393215)&&(i<458752)) WRITE_REG(TIM3->CCR2,i-393216); else if ((i>458751)&&(i<524288)) WRITE_REG(TIM3->CCR2,524287-i); else if((i>524287)&&(i<589824)) WRITE_REG(TIM3->CCR3,i-524288); else if ((i>589823)&&(i<655360)) WRITE_REG(TIM3->CCR3,655359-i); else if((i>655359)&&(i<720896)) WRITE_REG(TIM3->CCR4,i-655360); else WRITE_REG(TIM3->CCR4,768431-i); delay(75); } |
Соберём код, прошьём контроллер и увидим, как наши 6 светодиодов в планке будут плавно зажигаться и тухнуть по очереди
Итак, на данном занятии мы настроили таймеры на работу в режиме PWM (ШИМ), используя возможности библиотеки CMSIS.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Смотреть ВИДЕОУРОК (нажмите на картинку)
Здравствуйте , я Вам уже писал.Но увы остался без ответа. У меня к Вам огромная просьба. Не могли бы Вы выпустить урок по управлению с помощью МК AVR или STM симистором или тиристором. Управление мощной нагрузкой (лампа накаливания). Например спецэффекты для елочных гирлянд на 220v. Плавное включение ламп, плавное затухание, моргание не затушенных полностью ламп итд. Очень интересная тема. Для нас новичков в этом деле.