Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用
1、简述
LD2连接PA5,而PA5可以映射TIM2_CH1,配合TIM2,可以输出PWM。
本片文章大量工作是添加了shell命令,可以通过pwm命令开关pwm以及设置pwm的频率,占空比等。
2、TIM2的初始化
prescaler以及period的计算,之前的文章里已经讲了很多了,不管gd32、mm32 ,stm32都是类似了。
__STATIC_INLINE void BOARD_ConfigureTim2PwmOutput(void)
{/*************************//* GPIO AF configuration *//*************************//* Enable the peripheral clock of GPIOs */LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);/* GPIO TIM2_CH1 configuration */LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_5, LL_GPIO_MODE_ALTERNATE);LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_5, LL_GPIO_PULL_DOWN);LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_5, LL_GPIO_SPEED_FREQ_HIGH);LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_5, LL_GPIO_AF_1);/***********************************************//* Configure the NVIC to handle TIM2 interrupt *//***********************************************/NVIC_SetPriority(TIM2_IRQn, 0);NVIC_EnableIRQ(TIM2_IRQn);/******************************//* Peripheral clocks enabling *//******************************//* Enable the timer peripheral clock */LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); /***************************//* Time base configuration *//***************************//* Set counter mode *//* Reset value is LL_TIM_COUNTERMODE_UP *///LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);/* Set the pre-scaler value to have TIM2 counter clock equal to 10 kHz */LL_TIM_SetPrescaler(TIM2, __LL_TIM_CALC_PSC(SystemCoreClock, 1000000));/* Enable TIM2_ARR register preload. Writing to or reading from the *//* auto-reload register accesses the preload register. The content of the *//* preload register are transferred into the shadow register at each update *//* event (UEV). */ LL_TIM_EnableARRPreload(TIM2);/* Set the auto-reload value to have a counter frequency of 1000 Hz *//* TIM2CLK = SystemCoreClock / (APB prescaler & multiplier) */LL_TIM_SetAutoReload(TIM2, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), 1000));/*********************************//* Output waveform configuration *//*********************************//* Set output mode *//* Reset value is LL_TIM_OCMODE_FROZEN */LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1);/* Set output channel polarity *//* Reset value is LL_TIM_OCPOLARITY_HIGH */LL_TIM_OC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCPOLARITY_HIGH);/* Set compare value to half of the counter period (50% duty cycle ) */LL_TIM_OC_SetCompareCH1(TIM2, ( (LL_TIM_GetAutoReload(TIM2) + 1 ) / 2));/* Enable TIM2_CCR1 register preload. Read/Write operations access the *//* preload register. TIM2_CCR1 preload value is loaded in the active *//* at each update event. */LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH1);/**************************//* TIM2 interrupts set-up *//**************************//* Enable the capture/compare interrupt for channel 1*/LL_TIM_EnableIT_CC1(TIM2);/**********************************//* Start output signal generation *//**********************************//* Enable output channel 1 */LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);/* Enable counter */LL_TIM_EnableCounter(TIM2);/* Force update generation */LL_TIM_GenerateEvent_UPDATE(TIM2);
}
这边开了一个捕获中断,这里只是在捕获到高电平时,获取了一下当前的计数值,用来计算占空比。
void TIM2_IRQHandler(void)
{/* Check whether CC1 interrupt is pending */if(LL_TIM_IsActiveFlag_CC1(TIM2) == 1){/* Clear the update interrupt flag*/LL_TIM_ClearFlag_CC1(TIM2);/* TIM2 capture/compare interrupt processing(function defined in main.c) */TimerCaptureCompare_Callback();}
}
uint16_t uwMeasuredDutyCycle;void TimerCaptureCompare_Callback(void)
{uwMeasuredDutyCycle = (LL_TIM_GetCounter(TIM2) * 100) / ( LL_TIM_GetAutoReload(TIM2) + 1 );
}
3、shell 命令添加。
pwm -i 获取系统信息
pwm -h 命令帮助
pwm -d xx 设置占空比
pwm -f xx 设置pwm的频率
pwm -p xx 设置tim的presclaer,设置后频率设置为1000HZ
pwm -b 1开启呼吸灯,pwm -b 0关闭呼吸灯
命令代码:
#include "extend_shell.h"
#include "shell_port.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getopt.h"
#include "main.h"volatile bool isRunBreath = false;const char *ocMode[] = {"LL_TIM_OCMODE_FROZEN","LL_TIM_OCMODE_ACTIVE","LL_TIM_OCMODE_INACTIVE","LL_TIM_OCMODE_TOGGLE","LL_TIM_OCMODE_FORCED_INACTIVE","LL_TIM_OCMODE_FORCED_ACTIVE","LL_TIM_OCMODE_PWM1","LL_TIM_OCMODE_PWM2"
};uint32_t TypeOcMode[8] = {LL_TIM_OCMODE_FROZEN,LL_TIM_OCMODE_ACTIVE,LL_TIM_OCMODE_INACTIVE,LL_TIM_OCMODE_TOGGLE,LL_TIM_OCMODE_FORCED_INACTIVE,LL_TIM_OCMODE_FORCED_ACTIVE,LL_TIM_OCMODE_PWM1,LL_TIM_OCMODE_PWM2};static const char * Find_OCModeName(uint32_t ocModeType)
{uint32_t i=0;for (i=0; i< 8;i++){if (ocModeType == TypeOcMode[i])break;}return ocMode[i];
} void PWM_ShowUsage(void)
{printf("Usage:\r\n");printf(" pwm (-h | --help)\r\n");printf(" pwm (-i | --info)\r\n");printf(" pwm (-r | --reload) set pwm reload (0-65535)\r\n");printf(" pwm (-p | --prescaler) set pwm prescaler (0-65535)\r\n");printf(" pwm (-d | --duty) set pwm duty (1-100)\r\n");printf(" pwm (-e | --enable) enable/disable pwm mode (0/1) \r\n");printf(" pwm (-b | --breath) enable/disable led breath mode (0/1)\r\n");}void PWM_ShowInfomation(void)
{LL_RCC_ClocksTypeDef rccClock;LL_RCC_GetSystemClocksFreq(&rccClock);printf("Nucleo-F411RE Info:\r\n");printf(" Version : %s %s\r\n",__DATE__,__TIME__);printf(" System Clock : %ld\r\n",rccClock.SYSCLK_Frequency);printf(" AHB Clock : %ld\r\n",rccClock.HCLK_Frequency);printf(" APB1 Clock : %ld\r\n",rccClock.PCLK1_Frequency);printf(" APB2 Clock : %ld\r\n",rccClock.PCLK2_Frequency);printf(" TIM2 Status : %s\r\n",LL_TIM_IsEnabledCounter(TIM2)==1?"enable" : "disable");printf(" TIM2 OC Mode : %s\r\n",Find_OCModeName(LL_TIM_OC_GetMode(TIM2,LL_TIM_CHANNEL_CH1)));printf(" TIM2 CHannel Status : %s\r\n",LL_TIM_CC_IsEnabledChannel(TIM2,LL_TIM_CHANNEL_CH1) ==1 ?"enable": "disable");printf(" TIM2 PWM Prescaler : %ld\r\n",LL_TIM_GetPrescaler(TIM2) + 1);printf(" TIM2 PWM AutoReload : %ld\r\n",LL_TIM_GetAutoReload(TIM2) + 1);printf(" TIM2 PWM Duty : %.1f%%\r\n",(float)(LL_TIM_OC_GetCompareCH1(TIM2)* 100)/(LL_TIM_GetAutoReload(TIM2) + 1));printf(" TIM2 PWM Freq : %ldHZ\r\n",(rccClock.SYSCLK_Frequency)/((LL_TIM_GetPrescaler(TIM2) + 1)*(LL_TIM_GetAutoReload(TIM2) + 1)));
}int PWM_Control(int argc,char *argv[])
{ int c;int longindex = 0;int duty = 0;int breath = 0;const char short_options[] = "hid:e:b:p:f:";const struct option long_options[] ={{"help", 0, NULL, 'h'},{"info", 0, NULL, 'i'},{"duty", 1, NULL, 'd'},{"enable", 1, NULL, 'e'},{"breath", 1, NULL, 'b'},{"freq", 1, NULL, 'f'},{"prescaler", 1, NULL, 'p'},{NULL, 0, NULL, 0},};if (argc == 1){/* goto the help */PWM_ShowUsage();return 0;}/* init 0 */optind = 0;opterr = 0;/* parse */do{/* parse the args */c = getopt_long(argc, argv, short_options, long_options, &longindex);switch (c){case 'f':printf("Now set pwm freq %s\r\n",optarg);int freq = strtol(optarg,NULL,10);if (freq > 0){int reload = __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), freq);if (reload + 1 > 65535){printf("auto reload value must 0-65535,please set prscaler first\r\n");return 0;}else{LL_TIM_DisableCounter(TIM2);LL_TIM_SetAutoReload(TIM2, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), freq));LL_TIM_OC_SetCompareCH1(TIM2, ( (LL_TIM_GetAutoReload(TIM2) + 1 ) / 2));LL_TIM_EnableCounter(TIM2);}}else{printf(" set freq error ,must pwm -f xx\r\n");return 0;}break; case 'p':printf("Now set pwm prescaler %s\r\n",optarg);int prescaler = strtol(optarg,NULL,10);if (prescaler > 0 && (prescaler) < 65535){LL_TIM_DisableCounter(TIM2);LL_TIM_SetPrescaler(TIM2,prescaler-1);int reload = __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), 1000); //修改perscaler 默认1000HZif (reload + 1 > 65535){printf("reload value must 0-65525\r\n");return 0;}else{LL_TIM_SetAutoReload(TIM2,reload);LL_TIM_OC_SetCompareCH1(TIM2, ( (LL_TIM_GetAutoReload(TIM2) + 1 ) / 2));LL_TIM_EnableCounter(TIM2);}}else{printf(" set prescaler error ,must pwm -f xx\r\n");return 0;}break;case 'h'/* constant-expression */:/* code */PWM_ShowUsage();return 0;case 'i':PWM_ShowInfomation();break;case 'd': duty = strtol(optarg,NULL,10);printf("Now set pwm duty %s\r\n",optarg);if (duty >= 0 && duty <=100){duty = (duty * (LL_TIM_GetAutoReload(TIM2) + 1)) / 100;LL_TIM_OC_SetCompareCH1(TIM2, duty);}else{printf(" set duty error,must between 0 and 100\r\n");return 0;}break;case 'e': printf("Now set pwm state\r\n");break;case 'b': breath = strtol(optarg,NULL,10);if (breath == 1){isRunBreath = true;}else if (breath == 0){isRunBreath = false;}else{printf(" error ,must be 0/1\r\n");}printf("Now set pwm breath\r\n");break;default:break;}}while (c != -1);return 0;
}SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), pwm, PWM_Control, PWM_Control);
代码
代码下载