概述: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波形