概述:stm32的每个引脚都有其自带的特殊功能,有定时器输出功能的引脚可以输出pwm。如果没有的也可以io口模拟输出,如果非要较真的话,理论上可以算是每个引脚都能输出PWM,但一般我们都使用带定时器输出功能的io。此文章来详述使用stm32F051R8T6型号单片机,带有定时器输出功能的引脚来输出pwm波形,同理其他型号的单片机也可实现。并同时来控制直流电机转速,本文以adda公司生产的AD0212DB-G50直流风扇为例讲述。
1.硬件条件
保证单片机输出IO可以支持定时器功能。我板子上连接风扇的引脚为PC9,根据单片机手册PC9引脚带有定时器功能
同理如下图的几个引脚都可以
再次就不一一列举了,在是用不同型号的单片机时,参考单片机手册。
2.stm32固件库
上图分别是stm32定时器相关的库函数,和相关调用的定时器驱动子函数,用户自己定义。
软硬件条件都准备好了,接下来就是pwm所需的相关配置。
1.首先是输出管脚的IO口设置,PWM输出,自然会采用到IO口作为输出端口,在STM32F051系列中,IO端口可以复用为TIM定时器输出通道。
采用PC9的复用功能AF1作为TIM3定时器的第4通道输出。
2.设置定时器的参数,配置出频率为17.57 KHz的PWM波
考虑time定时器的时钟频率。如果我们设置分频数为0,也就是说time定时器等于系统时钟,system_stm32f0xx.c中已经把系统频率设置在48MHZ,在startup_stm32f0xx.s中,首先运行了systemInit函数,因此可以确定time定时器运行在48MHZ。
定时器产生的PWM的频率可以按照下面的公式进行计算:
预定标的值TIM1_Period = (time定时器频率 / pwm的频率) - 1
预定标的值实际上就是定时器运行多少次算一个PWM周期,这个在设置pwm频率中重要的参数。
配置完整代码如下:
/*==============================================================================函数名 : Fan_PWMConfigInit功能 : 风扇控制引脚输出pwm控制风扇转速输入参数说明: 无返回值说明 : 无
------------------------------------------------------------------------------*/
void Fan_PWMConfigInit(void)
{GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;s32 TimerPeriod, Channel1Pulse;/* 使能GPIO时钟 */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);/* 配置GPIO管脚参数设置*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOC, &GPIO_InitStructure);/* GPIO管脚复用设置*/ GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_1); /*计算预定标 的值,也就是多少个时钟计数为一个周期*/TimerPeriod = (SystemCoreClock / 17570 ) - 1;/*计算CCR1 跳转值 在占空比为50%时*/Channel1Pulse = (u16)(((u32)5 * (TimerPeriod - 1)) / 10);/* TIM3 时钟使能 */RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);/* Time 定时基础设置*/TIM_TimeBaseStructure.TIM_Prescaler = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; /* Time 定时设置为上升沿计算模式*/TIM_TimeBaseStructure.TIM_Period = TimerPeriod;TIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);/* 频道1的PWM 模式设置 */TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;TIM_OCInitStructure.TIM_Pulse = Channel1Pulse; TIM_OC4Init(TIM3, &TIM_OCInitStructure);/* TIM3 计算器使能*/TIM_Cmd(TIM3, ENABLE);/* TIM3 主输出使能 */TIM_CtrlPWMOutputs(TIM3, ENABLE);}
在需要改变风扇转速时,只需要改变占空比即可。
/* 风扇控制 */
static s32 CmdFanCtrl (TCmdTbl *cmdtp, s32 argc, s8 *argv[])
{u8 byCmd;s32 Channel1Pulse;if (argc < 2)return -1;byCmd = strtoul(argv[1], NULL, 0);switch(byCmd){case 1:Channel1Pulse = (u16)(((u32)875 * 2730) / 1000);break;case 2:Channel1Pulse = (u16)(((u32)75 * 2730) / 100);break;case 3:Channel1Pulse = (u16)(((u32)625 * 2730) / 1000);break;case 4:Channel1Pulse = (u16)(((u32)5 * 2730) / 10);break;case 5:Channel1Pulse = (u16)(((u32)375 * 2730) / 1000);break;case 6:Channel1Pulse = (u16)(((u32)25 * 2730) / 100);break;case 7:Channel1Pulse = (u16)(((u32)125 * 2730) / 1000);break;case 8:Channel1Pulse = (u16)(((u32)1 * 2730) / 100);break;}TIM_SetCompare4(TIM3, Channel1Pulse);return 0;
}
TIM_SetCompare4(TIM3, Channel1Pulse);该函数是改变对应TIM定时器对应通道占空比的函数,其中的2370是根据系统时间计算出来。使用不同的定时器和通道,可在stm32f0xx_tim.c中查找不同的库函数。
下面是输出的pwm波形
崩溃问题CmBacktrace开源工具
CmBacktrace/README_ZH.md at master · armink/CmBacktrace · GitHub