// 6E ukázka PWM režimu Compare kanálů // generuje PWM s frekvencí ~732Hz a moduluje jas LED na PC8 a PC9 se "sinusovým" průběhem (skrze lookup tabulku) #include "stm32f0xx.h" #include "stm32f0xx_ll_bus.h" #include "stm32f0xx_ll_rcc.h" #include "stm32f0xx_ll_gpio.h" #include "stm32f0xx_ll_utils.h" #include "stm32f0xx_ll_tim.h" void init_clock(void); void init_gpio(void); void init_tim3(void); #define COUNTS 4 // počet period mezi změnami jasu // sinusový prběh PWM hodnot (v paměti Flash) const uint8_t pwm_table[256]={ 0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95, 0x98,0x9b,0x9e,0xa2,0xa5,0xa7,0xaa,0xad, 0xb0,0xb3,0xb6,0xb9,0xbc,0xbe,0xc1,0xc4, 0xc6,0xc9,0xcb,0xce,0xd0,0xd3,0xd5,0xd7, 0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8, 0xea,0xeb,0xed,0xee,0xf0,0xf1,0xf3,0xf4, 0xf5,0xf6,0xf8,0xf9,0xfa,0xfa,0xfb,0xfc, 0xfd,0xfd,0xfe,0xfe,0xfe,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe,0xfd, 0xfd,0xfc,0xfb,0xfa,0xfa,0xf9,0xf8,0xf6, 0xf5,0xf4,0xf3,0xf1,0xf0,0xee,0xed,0xeb, 0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc, 0xda,0xd7,0xd5,0xd3,0xd0,0xce,0xcb,0xc9, 0xc6,0xc4,0xc1,0xbe,0xbc,0xb9,0xb6,0xb3, 0xb0,0xad,0xaa,0xa7,0xa5,0xa2,0x9e,0x9b, 0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83, 0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a, 0x67,0x64,0x61,0x5d,0x5a,0x58,0x55,0x52, 0x4f,0x4c,0x49,0x46,0x43,0x41,0x3e,0x3b, 0x39,0x36,0x34,0x31,0x2f,0x2c,0x2a,0x28, 0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17, 0x15,0x14,0x12,0x11,0xf,0xe,0xc,0xb, 0xa,0x9,0x7,0x6,0x5,0x5,0x4,0x3, 0x2,0x2,0x1,0x1,0x1,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x2, 0x2,0x3,0x4,0x5,0x5,0x6,0x7,0x9, 0xa,0xb,0xc,0xe,0xf,0x11,0x12,0x14, 0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23, 0x25,0x28,0x2a,0x2c,0x2f,0x31,0x34,0x36, 0x39,0x3b,0x3e,0x41,0x43,0x46,0x49,0x4c, 0x4f,0x52,0x55,0x58,0x5a,0x5d,0x61,0x64, 0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c }; volatile uint16_t i=0, j=128; // indexy v pwm_table[] int main(void){ init_clock(); // 48MHz (HSE bypass) init_gpio(); // LED na PC9 init_tim3(); while (1){ // není co dělat... } } // rutina přerušení timeru (Přetečení) void TIM3_IRQHandler(void){ static uint16_t counter=0; LL_TIM_ClearFlag_UPDATE(TIM3); // nezapomeneme smazat vlajku counter++; // počítadlo period if(counter>=COUNTS){ // každou n-tou periodu nastav nový jas LED counter = 0; if(i<255){i++;}else{i=0;} // index dalšího vzroku z tabulky if(j<255){j++;}else{j=0;} // index dalšího vzroku z tabulky LL_TIM_OC_SetCompareCH3(TIM3,pwm_table[i]); // nastav PWM na CH3 LL_TIM_OC_SetCompareCH4(TIM3,pwm_table[j]); // nastav PWM na CH4 } } // konfigurace timeru void init_tim3(void){ LL_TIM_InitTypeDef tim; LL_TIM_OC_InitTypeDef oc; // inicializujeme struktury (abychom je nemuseli vyplňovat celé) LL_TIM_StructInit(&tim); LL_TIM_OC_StructInit(&oc); // povolíme clock pro TIM3 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3); tim.Prescaler = 256; // 48M/256 = 187.5kHz do timeru tim.Autoreload = 0xFF; // strop timeru 255, f=~732Hz (T=~1.37ms) LL_TIM_Init(TIM3,&tim); // použijeme kanály CH3 a CH4 oc.OCPolarity = LL_TIM_OCPOLARITY_HIGH; // polarita běžná oc.OCState = LL_TIM_OCSTATE_ENABLE; // přidělujeme timeru ovládání výstupu oc.OCMode = LL_TIM_OCMODE_PWM1; // režim PWM1 oc.CompareValue = pwm_table[i]; // startovací hodnota PWM pro CH3 LL_TIM_OC_Init(TIM3,LL_TIM_CHANNEL_CH3,&oc); // aplikujeme nastavení na CH3 (PC8) oc.CompareValue = pwm_table[j]; // startovací hodnota PWM pro CH4 LL_TIM_OC_Init(TIM3,LL_TIM_CHANNEL_CH4,&oc); // aplikujeme nastavení na CH4 (PC9) // nutné zapnout Preload - jinak hrozí vznik glitchů při změně PWM LL_TIM_OC_EnablePreload(TIM3,LL_TIM_CHANNEL_CH3); LL_TIM_OC_EnablePreload(TIM3,LL_TIM_CHANNEL_CH4); LL_TIM_EnableIT_UPDATE(TIM3); // povolím přerušení od přetečení NVIC_SetPriority(TIM3_IRQn,3); // nízká priorita NVIC_EnableIRQ(TIM3_IRQn); // a čítač spustíme LL_TIM_EnableCounter(TIM3); } // výstup TIM3 na PC8,PC9 - LEDky void init_gpio(void){ LL_GPIO_InitTypeDef gp; LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC); gp.Pin = LL_GPIO_PIN_8 | LL_GPIO_PIN_9 ; gp.Mode = LL_GPIO_MODE_ALTERNATE; // Pin bude patřit timeru gp.Alternate = LL_GPIO_AF_0; // na STM32F051 je pro GPIOC všechno na AF0 (víc možností není) gp.OutputType = LL_GPIO_OUTPUT_PUSHPULL; // push-pull výstup (výchozí varianta) gp.Speed = LL_GPIO_SPEED_HIGH; // vysoká rychlost přeběhu (nehraje teď roli) LL_GPIO_Init(GPIOC,&gp); } // 48MHz z externího 8MHz signálu void init_clock(void){ LL_UTILS_PLLInitTypeDef pll; LL_UTILS_ClkInitTypeDef clk; pll.Prediv = LL_RCC_PREDIV_DIV_2; // 8MHz / 2 = 4MHz pll.PLLMul = LL_RCC_PLL_MUL_12; // 4MHz * 12 = 48MHz clk.AHBCLKDivider = LL_RCC_SYSCLK_DIV_1; // APB i AHB bez předděličky clk.APB1CLKDivider = LL_RCC_APB1_DIV_1; // voláme konfiguraci clocku LL_PLL_ConfigSystemClock_HSE(8000000,LL_UTILS_HSEBYPASS_ON,&pll,&clk); // aktualizuj proměnnou SystemCoreClock SystemCoreClockUpdate(); }