STM32-蓝桥杯嵌入式之PWM输出
PWM的配置可以参考官方固件库种STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\TIM\PWM_Output例程来进行改写。
下面来分析一下PWM输出的配置步骤:
- 首先配置时钟,由于开发板只将PA1~PA7这几个GPIO口引出,所以我们在配置PWM的时候,也只能使用这几个脚,那么查看芯片的数据手册可知,可以输出PWM的引脚与对应的通道如下表:
PA1 | TIM2_CH2 |
---|---|
PA2 | TIM2_CH3 |
PA3 | TIM2_CH4 |
PA6 | TIM3_CH1 |
PA7 | TIM3_CH2 |
- 配置GPIO,根据使用的PWM通道,来配置相应的GPIO引脚,模式配置为模拟输入
- 初始化基本定时器,这里需要初始化基本定时器初始化结构体,里面的成员的含义在下图中有详细的解释
下面举一个例子,代码如下,来计算一下定时器的周期应该是多少???
首先我们需要知道定时器的时钟频率对吧,STM32时钟树中有提到如果APB1预分频系数等于1则频率不变,否则频率要乘以2,由于默认把APB1的频率二分频即PCLK1=36MHz,所以到定时器的频率要乘以2,即为72MHz。如果这里不清楚的话,我总结了一个关于STM32时钟的博客,可以参考一下。
现在我们知道定时器时钟的频率是72MHz,SystemCoreClock是72MHz,则PrescalerValue计算出来是71,我们将预分频器设置为71,查看数据手册,如下图,可以知道会对时钟的频率进行72分频,得到的频率是1MHz。然后把TIM_Period 设置为1000-1,即设置ARR(自动重装载寄存器)为999,即定时器计数器从0开始,计数到999,则会自动重新装载,前面我们得到了频率为1MHz,那么定时器每进行一次重转载的周期是1MHz/1000=1kHz,也就是1ms。
/* Compute the prescaler value */
PrescalerValue = (uint16_t) (SystemCoreClock / 1000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
- 其实上面也都是基本定时器的内容,但是产生PWM的基础,这一步就是真正配置PWM了,主要就是对定时器的输出比较模式进行初始化,配置定时器输出比较结构体TIM_OCInitTypeDef,下面是主要的代码,里面对定时器的极性以及PWM的模式进行了详细的说明,参考的是这位博主的说明,解释的特别清楚,一看就能懂。应该是一看就能懂得。
// OC:Output Compare 输出比较
// 输出比较模式时的TIM_OCMode_PWM1和TIM_OCMode_PWM2区别
// 现在假定TIM_OCInitTypeDef.TIM_OCPolarity = TIM_OCPolarity_High,则起始波形为高电位。
// 若TIM_OCInitTypeDef.TIM_OCMode = TIM_OCMode_PWM1时:
// 当计时器值小于比较器设定值时则TIMX输出脚此时输出有效高电位。
// 当计时器值大于或等于比较器设定值时则TIMX输出脚此时输出低电位。
// 若TIM_OCInitTypeDef.TIM_OCMode = TIM_OCMode_PWM2时:
// 当计时器值小于比较器设定值时则TIMX输出脚此时输出有效低电位。
// 当计时器值大于或等于比较器设定值时则TIMX输出脚此时输出高电位。
/* PWM1 Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // Polarity:极性
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
// 输出比较预装载使能
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
- 上面的步骤就已经配置完了,下面我们再使能一下自动重转载,将定时器打开即可。
TIM_ARRPreloadConfig(TIM2, ENABLE);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
两个很重要的函数
// 用于设置ARR的值,通过设置ARR的值来改变定时器的频率
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload)
// 设置输出比较寄存器的值,用于改变PWM的占空比。
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
最后附上详细的代码:
void PWM_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOA Configuration:TIM2 Channel2, 3 as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void PWM_RCC_Configuration(void)
{
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* GPIOA clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO, ENABLE);
}
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint16_t CCR2_Val = 100;
uint16_t CCR3_Val = 900;
uint16_t PrescalerValue = 0;
PWM_RCC_Configuration();
PWM_GPIO_Configuration();
/* Compute the prescaler value */
PrescalerValue = (uint16_t) (SystemCoreClock / 1000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// OC:Output Compare 输出比较
// 输出比较模式时的TIM_OCMode_PWM1和TIM_OCMode_PWM2区别
// 现在假定TIM_OCInitTypeDef.TIM_OCPolarity = TIM_OCPolarity_High,则起始波形为高电位。
// 若TIM_OCInitTypeDef.TIM_OCMode = TIM_OCMode_PWM1时:
// 当计时器值小于比较器设定值时则TIMX输出脚此时输出有效高电位。
// 当计时器值大于或等于比较器设定值时则TIMX输出脚此时输出低电位。
// 若TIM_OCInitTypeDef.TIM_OCMode = TIM_OCMode_PWM2时:
// 当计时器值小于比较器设定值时则TIMX输出脚此时输出有效低电位。
// 当计时器值大于或等于比较器设定值时则TIMX输出脚此时输出高电位。
/* PWM1 Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // Polarity:极性
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel3 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}