// DMA 7B - proměnné PWM pomocí DMA // TIM3 generuje PWM na PC8 (LED) // TIM6 dává requesty DMA // DMA z pole ve FLASH paměti nastavuje střídu PWM (jas LED) // Průběh jasu LED emuluje tep člověka... #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" #include "stm32f0xx_ll_dma.h" void init_clock(void); void init_gpio(void); void init_DMA(void); void init_tim6(void); void init_tim3(void); // tabulka intenzit LED (vypadá jako tep člověka) const uint8_t intenzita[143]={ 56,62,69,75,82,88,94,99, 105,110,115,119,124,128,132,137, 142,145,149,152,155,158,161,162, 164,164,164,165,165,164,163,162, 160,158,157,153,150,148,145,143, 141,140,139,138,137,138,139,139, 140,139,140,140,141,140,140,139, 139,138,137,137,135,135,134,135, 134,135,135,135,137,136,136,136, 136,136,136,136,135,136,137,135, 136,135,135,134,133,133,132,132, 132,133,133,135,138,142,146,155, 163,174,184,196,208,219,231,237, 245,248,253,253,255,251,248,242, 235,227,218,207,196,184,173,161, 150,140,129,119,109,100,91,83, 76,69,62,58,54,50,48,47, 45,46,46,48,48,52,53 }; int main(void){ init_clock(); // 48MHz (HSE bypass) init_gpio(); // PC8 LED init_tim3(); // generuje PWM na PC8 init_DMA(); // přenáší PWM hodnoty do TIM3 init_tim6(); // generuje DMA requesty (časuje celý proces) while (1){ // všechno se děje na pozadí ! ;) } } void init_DMA(void){ LL_DMA_InitTypeDef dma; // clock pro DMA LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1); // přenos mezi pamětí a periferií (TIM3 PWM) dma.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; dma.MemoryOrM2MDstAddress = (uint32_t)intenzita; // zdrojová adresa (hodnoty PWM) dma.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; // zdrojový datový typ (uint8) dma.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; // zdrojovou adresu inkrementovat dma.Mode = LL_DMA_MODE_CIRCULAR; // kruhový mód (odesílat pole stále dokola) dma.NbData = sizeof(intenzita); // počet přenášených dat dma.PeriphOrM2MSrcAddress = (uint32_t)&(TIM3->CCR3); // cílová adresa (Compare registr TIM3) dma.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; // cílový datový typ (uint16) dma.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; // cílová adres je fixní dma.Priority = LL_DMA_PRIORITY_MEDIUM; // priorita přenosu střední // Channel3 - requesty od TIM6 LL_DMA_Init(DMA1,LL_DMA_CHANNEL_3,&dma); // aplikovat konfiguraci na Channel3 LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_3); // povolíme Channel } // TIM6 časování jasu (DMA requestů) void init_tim6(void){ LL_TIM_InitTypeDef tim; LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM6); LL_TIM_StructInit(&tim); tim.Prescaler = 47; // 1us do timeru tim.Autoreload = 5116; // 5.117ms perioda (82 úderů za minutu) LL_TIM_Init(TIM6,&tim); // povolíme generovat DMA requesty s přetečením timeru (Update) LL_TIM_EnableDMAReq_UPDATE(TIM6); LL_TIM_EnableCounter(TIM6); // spustíme Timer } // konfigurace PWM timeru (TIM3) 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 = 93; // 48M/93 ~= 510.638kHz do timeru tim.Autoreload = 0xFF; // strop timeru 255, f~1.995kaHz LL_TIM_Init(TIM3,&tim); // použijeme jen kanál CH3 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 = intenzita[0]; // startovací hodnota PWM pro CH3 LL_TIM_OC_Init(TIM3,LL_TIM_CHANNEL_CH3,&oc); // aplikujeme nastavení na CH3 (PC8) // nutné zapnout Preload - jinak hrozí vznik glitchů při změně PWM LL_TIM_OC_EnablePreload(TIM3,LL_TIM_CHANNEL_CH3); // a čítač spustíme LL_TIM_EnableCounter(TIM3); } // výstup TIM3 na PC8 - LED void init_gpio(void){ LL_GPIO_InitTypeDef gp; LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC); gp.Pin = LL_GPIO_PIN_8; 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(); }