PWM
脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号,也就是说对脉冲宽度的控制。
STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出!
在TIMx_CCMRx寄存器中的OCxM位写入’110’(PWM模式1)或’111’(PWM模式2),能够独立地设置每个OCx输出通道产生一路PWM。必须设置TIMx_CCMRx寄存OCxPE位以使能相应的预装载寄存器,最后还要设置TIMx_CR1寄存器的ARPE位,(在向上计数或中心对称模式中)使能自动重装载的预装载寄存器。
仅当发生一个更新事件的时候,预装载寄存器才能被传送到影子寄存器,因此在计数器开始计数之前,必须通过设置TIMx_EGR寄存器中的UG位来初始化所有的寄存器。
OCx的极性可以通过软件在TIMx_CCER寄存器中的CCxP位设置,它可以设置为高电平有效或低电平有效。TIMx_CCER寄存器中的CCxE位控制OCx输出使能。详见TIMx_CCERx寄存器的描述。
在PWM模式(模式1或模式2)下,TIMx_CNT和TIMx_CCRx始终在进行比较,(依据计数器的计数方向)以确定是否符合TIMx_CCRx≤TIMx_CNT或者TIMx_CNT≤TIMx_CCRx。然而为了与OCREF_CLR的功能(在下一个PWM周期之前,ETR信号上的一个外部事件能够清除OCxREF)一致,OCxREF信号只能在下述条件下产生:
● 当比较的结果改变,或通用定时器(TIMx)
● 当输出比较模式(TIMx_CCMRx寄存器中的OCxM位)从“冻结”(无比较,OCxM=’000’)切换到某个PWM模式(OCxM=’110’或’111’)。
这样在运行中可以通过软件强置PWM输出。
根据TIMx_CR1寄存器中CMS位的状态,定时器能够产生边沿对齐的PWM信号或中央对齐的PWM信号。
PWM 边沿对齐模式
注:本次学习的使用TIM3的向上计数模式(TIM3_CH2 通道将重映射到 PB5 上)产生PWM。
1. 开启TIM3时钟以及复用功能时钟,配置PB5为复用输出。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //复用时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能GPIOB时钟
GPIO_InitTypeDef GPIO_ITDef_PB5;
GPIO_ITDef_PB5.GPIO_Pin = GPIO_Pin_5;
GPIO_ITDef_PB5.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_ITDef_PB5.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB,&GPIO_ITDef_PB5);
2. 设置TIM3_CH2重映射到 PB5上。
默认条件下,TIM3_REMAP[1:0]为 00,是没有重映射的,所以 TIM3_CH1~TIM3_CH4 分别是接在 PA6、PA7、PB0 和 PB1 上的,而我们想让 TIM3_CH2 映射到 PB5 上,则需要设置TIM3_REMAP[1:0]=10,即部分重映射,这里需要注意,此时 TIM3_CH1 也被映射到 PB4 上了。
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //重映射 TIM3_CH2->PB5
3. 初始化TIM3, 设置TIM3的ARR和PSC 。
注:向上计数模式
TIM_TBIStruct_TIM3.TIM_Period = 100; //设置自动重装载值 ARR
TIM_TBIStruct_TIM3.TIM_Prescaler = 7199; //设置预分频值 PSC
TIM_TBIStruct_TIM3.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TBIStruct_TIM3.TIM_ClockDivision = TIM_CounterMode_Up; //TIM 向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TBIStruct_TIM3);
4. 设置TIM3_CH2的PWM 模式 使能TIM3的CH2输出。
注:在固件库"stm32f10x.h"和"stm32f10x.c"里能查到。
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
//主要还是TIM_OCInitTypeDef结构体的定义内容。
typedef struct
{
uint16_t TIM_OCMode;//选择定时器模式。
/*
TIM_OCMode_Timing TIM 输出比较时间模式
TIM_OCMode_Active TIM 输出比较主动模式
TIM_OCMode_Inactive TIM 输出比较非主动模式
TIM_OCMode_Toggle TIM 输出比较触发模式
TIM_OCMode_PWM1 TIM 脉冲宽度调制模式 1
TIM_OCMode_PWM2 TIM 脉冲宽度调制模式 2
*/
uint16_t TIM_OutputState; //设置比较输出使能
uint16_t TIM_OutputNState; //指定TIM的互补输出比较状态。
//设置了待装入捕获比较寄存器的脉冲值。它的取值必须在 0x0000 和 0xFFFF 之间。
uint16_t TIM_Pulse;
uint16_t TIM_OCPolarity;//输出极性
/*
TIM_OCPolarity_High TIM 输出比较极性高
TIM_OCPolarity_Low TIM 输出比较极性低
*/
uint16_t TIM_OCNPolarity;//指定互补的输出极性。
uint16_t TIM_OCIdleState; //指定空闲状态下的TIM输出比较引脚状态。
uint16_t TIM_OCNIdleState; //指定空闲状态下的TIM输出比较引脚状态。
} TIM_OCInitTypeDef;
//初始化 TIM3 Channel2 PWM 模式
TIM_OCInitTypeDef TIM_OCITDef_TIM3;
TIM_OCITDef_TIM3.TIM_OCMode = TIM_OCMode_PWM2; //选择 PWM 模式 2
TIM_OCITDef_TIM3.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCITDef_TIM3.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高
TIM_OC2Init(TIM3, &TIM_OCITDef_TIM3); //初始化外设 TIM3 OC2
注:TIM_OutputNState、TIM_OCNPolarity、TIM_OCIdleState 和 TIM_OCNIdleState 是高级定时器 TIM1 和 TIM8 才用到的
5. 使能TIM3。
TIM_Cmd(TIM3, ENABLE); //使能 TIM3
6. 修改TIM3_CCR2来控制占空比。
注:修改Compare2便可以修改占空比。
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
#include "stm32f10x.h"
/*
TIM3输出比较模式
LED PB5
KEY0
*/
u16 data = 0;//0-1000
u8 temp = 2;//1-250 2-500 3-1000
u8 dir = 1;
void delay_ms(u16 time)
{
u16 i = 0;
while(time--)
{
i = 12000;
while(i--);
}
}
void LED_Init_PE5(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitTypeDef GPIO_ITDef_PE5;
GPIO_ITDef_PE5.GPIO_Pin = GPIO_Pin_5;
GPIO_ITDef_PE5.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_ITDef_PE5.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOE,&GPIO_ITDef_PE5);
GPIO_SetBits(GPIOE,GPIO_Pin_5);//1
}
void KEY0_Init_PE4(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitTypeDef GPIO_ITDef_PE4;
GPIO_ITDef_PE4.GPIO_Pin = GPIO_Pin_4;
GPIO_ITDef_PE4.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_Init(GPIOE,&GPIO_ITDef_PE4);
//外部中断
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
EXTI_InitTypeDef EXTI_ITDef_PE4;
EXTI_ITDef_PE4.EXTI_Line = EXTI_Line4;
EXTI_ITDef_PE4.EXTI_Mode = EXTI_Mode_Interrupt;//为中断请求
EXTI_ITDef_PE4.EXTI_Trigger = EXTI_Trigger_Falling;//输入线路下升沿中断
EXTI_ITDef_PE4.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_ITDef_PE4);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置 NVIC 中断分组 2:2 位抢占优先级,2 位响应优先级
NVIC_InitTypeDef NVIC_ITDef;
NVIC_ITDef.NVIC_IRQChannel = EXTI4_IRQn; //使能按键外部中断通道
NVIC_ITDef.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
NVIC_ITDef.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2
NVIC_ITDef.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_ITDef);
}
void TIM3_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //复用时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_ITDef_PB5;
GPIO_ITDef_PB5.GPIO_Pin = GPIO_Pin_5;
GPIO_ITDef_PB5.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_ITDef_PB5.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB,&GPIO_ITDef_PB5);
TIM_DeInit(TIM3);//复位TIM3(可要可不要)
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //重映射 TIM3_CH2->PB5
TIM_TimeBaseInitTypeDef TIM_TBIStruct_TIM3;
TIM_TBIStruct_TIM3.TIM_Period = 100; //设置自动重装载值
TIM_TBIStruct_TIM3.TIM_Prescaler = 7199; //设置预分频值
TIM_TBIStruct_TIM3.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TBIStruct_TIM3.TIM_ClockDivision = TIM_CounterMode_Up; //TIM 向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TBIStruct_TIM3);
//初始化 TIM3 Channel2 PWM 模式
TIM_OCInitTypeDef TIM_OCITDef_TIM3;
TIM_OCITDef_TIM3.TIM_OCMode = TIM_OCMode_PWM2; //选择 PWM 模式 2
TIM_OCITDef_TIM3.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCITDef_TIM3.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高
TIM_OC2Init(TIM3, &TIM_OCITDef_TIM3); //初始化外设 TIM3 OC2
TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能预装载寄存器
TIM_Cmd(TIM3,ENABLE); //使能 TIM3
}
int main(void)
{
LED_Init_PE5();
KEY0_Init_PE4();
TIM3_Init();
while(1)
{
//按键处理(通过按键改变占空比)
switch(temp)
{
case 0:data=0;break;
case 1:data=3;break;
case 2:data=5;break;
case 3:data=10;break;
case 4:data=50;break;
case 5:data=90;break;
case 6:data=100;break;
}
/* data从0-200-0 来修改占空比
delay_ms(10);
if(dir)data++;
else data--;
if(data>200)dir=0;
if(data==0)dir=1;
*/
TIM_SetCompare2(TIM3,data);
}
}
//按键中断处理函数,按键每按一下,PE5的状态翻转一次(LED亮灭翻转)
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4)!=RESET)//判断某个线上的中断是否发生
{
delay_ms(10); //按键延时消斗
if(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)))//再次判断是否按键按下
{
temp++;//按下一次,temp+1,
if(temp>6)temp=0;//这里可以根据实际需求修改
if(GPIO_ReadOutputDataBit(GPIOE,GPIO_Pin_5))//是灭的
{
GPIO_ResetBits(GPIOE,GPIO_Pin_5); //BRR 0 亮
}else
{
GPIO_SetBits(GPIOE,GPIO_Pin_5); //BSRR 1 灭
}
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
}
常用资料:
STM32F10x_StdPeriph_Lib_V3.5.0(官方固件库)
链接:《STM32固件库使用手册的中文翻译版》 提取码:4lkx
链接:(keil5 IAR 等常用助手) 提取码:xzgj
链接:《STM32中文参考手册V10》 提取码:j748