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 默认1000HZ
if (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);
代码
代码下载