STM32F103 PWM配置
目录
在《STM32F103
定时器配置》中我们介绍了PWM
的产生原理,本节介绍介绍如何编码实现PWM
的输出。
一、PWM
相关寄存器
TIMx
如果要产生PWM
,除了我们上一节提到的如下寄存器:
- 控制寄存器(
TIMx_CR1
); DMA
/中断使能寄存器(TIMx_DIER
);- 预分频寄存器(
TIMx_PSC
); - 从模式控制寄存器(
TIMx_SMCR
); - 自动重装载寄存器(
TIMx_ARR
); - 状态寄存器(
TIMx_SR
);
还需要使用到:
- 捕获/比较模式寄存器(
TIMx_CCM1/2
); - 捕获/比较使能寄存器(
TIMx_CCER
); - 捕获/比较寄存器(
TIMx_CCR1~4
); - 刹车和死区寄存器(
TIMx_BDTR
)(只有高级定时器用到)。
注意:这里捕获指的是输入捕获,比较指的时输出比较。
1.1 捕获/比较模式寄存器(TIMx_CCMR1/2
)
捕获/比较模式寄存器一共有两个:
TIMx_CCMR1
:控制通道1和2;TIMx_CCMR2
:控制通道3和4;
这里以 TIMx_CCM1
寄存器为例进行介绍:



1.2 捕获/比较使能寄存器(TIMx_CCER
)
TIMx_CCER
寄存器每4位描述一个通道;


其中:
CCxE
:输入/捕获x输出使能位,我们需要重点关注;CCxP
:输入/捕获x
输出极性。
1.3 捕获/比较寄存器(TIMx_CCR1~4
)
捕获/比较模式寄存器一共有4
个,依次用于描述每一个通道,这里以TIMx_CCR1
为例;

1.4 刹车和死区寄存器(TIMx_BDTR
)
TIMx_BDTR
寄存器需要重点关注位15
主输出使能位;


二、PWM
生成源码
2.1 PWM
初始化步骤
PWM
生成配置流程如下:
(1) TIMx
时钟使能:通过配置RCC_APB1ENR/RCC_APB2ENR
寄存器使能TIMx
时钟;
(2) GPIO
功能复用:配置GPIO
(比如TIM1
通道1
为PA8
)为复用功能推挽输出模式;
(3) 配置TIMx
时基单元;
- 配置
TIMx_ARR
寄存器自动重装载的值; - 配置
TIMx_PSC
寄存器预分频系数;
(4) 配置PWM
相关寄存器;
-
配置
TIMx_CCMRx
寄存器:- 输入捕获/比较输出选择(
CCxS
):配置为比较输出(用于实现PWM
输出功能); - 输出比较模式(
CCxM
):配置为PWM
模式1或者PWM
模式2;
- 输入捕获/比较输出选择(
-
配置
TIMx_CCER
寄存器:- 输入捕获/比较输出使能(
CCxE
):即开启比较输出功能,这样才能输出PWM
;
- 输入捕获/比较输出使能(
-
针对高级定时器配置
TIMx_BDTR
寄存器位15
:使能PWM
输出;
(5) 允许TIMx
工作:配置TIMx_CR1
位0
;
(6) 修改TIMx_CCRx
寄存器可以改变占空比。
2.2 源码实现
2.2.1 PWM
初始化
PWM
初始化函数TIM_PWM_Init
定义如下:
/**************************************************************************************************************
*
* Description: 高级定时器1和8 APB2预分频系数=1 则计数器的时钟频率为 APB2 否则APB2*2
* 通用定时器2~7 APB1预分频系数=1 则计数器的时钟频率为 APB1 否则APB1*2
* Parameter : timx TIMER1~TIMER5 8
DEFAULT_PSC 默认预分频系数
计数器的时钟频率 = Fclk/(PSC[15:0]+1) Fclk单位MHZ
time 中断时间 = (ARR+1)/计数器的时钟频率 单位us
中断频率 = Fclk*1000/((PSC+1)*(ARR+1)) 单位kHZ
* frequent 频率: 1~360 单位khz
Channel 通道 PWM_CH1~4
*
**************************************************************************************************************/
void TIM_PWM_Init(TIMn timn,u8 frequent,PWM_CHANNEL Channel) //PWM初始化
{
u16 arr; //存放自动重装载的值
u8 fclk; //存放定时器时钟频率 MHZ
u16 DEFAULT_PSC = 1; //默认预分频系数为1
u8 REMAP=0x00; //映射情况 0x00默认 0x01 0x02: 0x03 自己设置
if(timn==0||timn==7) //定时器1或8
{
fclk = 72; //默认APB2 1倍频
}
else
{
fclk =72; //默认APB1 2倍频
}
arr = fclk*1000/(DEFAULT_PSC+1)/frequent-1;
//****************************************************************************************
if(timn==0) //定时器1
{
RCC->APB2ENR |=1<<11; //高级定时器1时钟使能
TIM1->BDTR|=1<<15; //主输出(pwm)使能(必须)
if(REMAP==0x00) //默认情况下
{
switch(Channel)
{
case PWM_CH1:
gpio_init(PA8,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ 主输出
gpio_init(PB13,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ 互补输出
break;
case PWM_CH2:
gpio_init(PA9,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PB14,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PA10,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PB15,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PA11,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else if(REMAP==0x01) //部分映射
{
RCC->APB2ENR |= 1<<0; //使能I/O复用时钟
AFIO->MAPR &=~(0x3<<6); //位7:6清零
AFIO->MAPR |= REMAP<<6; //TIM1部分映射
switch(Channel)
{
case PWM_CH1:
gpio_init(PA8,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PA7,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PA9,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PB0,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PA10,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PB1,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PA11,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else if(REMAP==0x03) //完全映射
{
RCC->APB2ENR |= 1<<0; //使能I/O复用时钟
AFIO->MAPR &=~(0x3<<6); //位7:6清零
AFIO->MAPR |= REMAP<<6; //TIM1完全映射
switch(Channel)
{
case PWM_CH1:
gpio_init(PE9,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PE8,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PE11,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PE10,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PE13,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PE12,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PE14,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else
{
ASSERT(0);
}
}
//********************************************************************************************
else if(timn==1) //定时器2
{
RCC->APB1ENR |=1<<0; //定时器2时钟使能
if(REMAP==0x00) //默认情况下
{
switch(Channel)
{
case PWM_CH1:
gpio_init(PA0,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PA1,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PA2,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PA3,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else if(REMAP==0x01) //部分映射
{
RCC->APB2ENR |= 1<<0; //使能I/O复用时钟
AFIO->MAPR &=~(0x3<<8); //位9:8清零
AFIO->MAPR |= REMAP<<8; //TIM2部分映射
switch(Channel)
{
case PWM_CH1:
gpio_init(PA15,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PB3,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PA2,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PA3,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else if(REMAP==0x02) //部分映射
{
RCC->APB2ENR |= 1<<0; //使能I/O复用时钟
AFIO->MAPR &=~(0x3<<8); //位9:8清零
AFIO->MAPR |= REMAP<<8; //TIM2部分映射
switch(Channel)
{
case PWM_CH1:
gpio_init(PA0,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PA1,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PB10,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PB11,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else if(REMAP==0x03) //完全映射
{
RCC->APB2ENR |= 1<<0; //使能I/O复用时钟
AFIO->MAPR &=~(0x3<<8); //位9:8清零
AFIO->MAPR |= REMAP<<8; //TIM2部分映射
switch(Channel)
{
case PWM_CH1:
gpio_init(PA15,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PB3,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PB10,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PB11,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else
{
ASSERT(0);
}
}
//*****************************************************************************************************************
else if(timn==2) //定时器3
{
REMAP=0x03;
RCC->APB1ENR |=1<<1; //定时器3时钟使能
if(REMAP==0x00) //默认情况下
{
switch(Channel)
{
case PWM_CH1:
gpio_init(PA6,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PA7,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PB0,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PB1,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else if(REMAP==0x02) //部分映射
{
RCC->APB2ENR |= 1<<0; //使能I/O复用时钟
AFIO->MAPR &=~(0x3<<10); //位11:10清零
AFIO->MAPR |= REMAP<<10; //TIM3部分映射
switch(Channel)
{
case PWM_CH1:
gpio_init(PB4,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PB5,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PB0,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PB1,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else if(REMAP==0x03) //完全映射
{
RCC->APB2ENR |= 1<<0; //使能I/O复用时钟
AFIO->MAPR &=~(0x3<<10); //位11:10清零
AFIO->MAPR |= REMAP<<10; //TIM3部分映射
switch(Channel)
{
case PWM_CH1:
gpio_init(PC6,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PC7,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PC8,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PC9,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else
{
ASSERT(0);
}
}
//*********************************************************************************************************
else if(timn==3) //定时器4
{
RCC->APB1ENR |=1<<2; //定时器4时钟使能
if(REMAP==0x00) //默认情况下
{
switch(Channel)
{
case PWM_CH1:
gpio_init(PB6,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PB7,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PB8,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PB9,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else if(REMAP==0x01) //完全映射
{
RCC->APB2ENR |= 1<<0; //使能I/O复用时钟
AFIO->MAPR |= REMAP<<12; //TIM4部分映射
switch(Channel)
{
case PWM_CH1:
gpio_init(PD12,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PD13,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PD14,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PD15,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else
{
ASSERT(0);
}
}
//***********************************************************************************************************
else if(timn==4) //定时器5
{
RCC->APB1ENR |=1<<3; //定时器5时钟使能
switch(Channel)
{
case PWM_CH1:
gpio_init(PA0,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PA1,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PA2,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PA3,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
//********************************************************************************************************
else if(timn==7)
{
RCC->APB2ENR |=1<<13; //定时器8时钟使能
TIM8->BDTR|=1<<15; //主输出(pwm)使能(必须)
switch(Channel)
{
case PWM_CH1:
gpio_init(PC6,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PA7,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH2:
gpio_init(PC7,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PB0,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH3:
gpio_init(PC8,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
gpio_init(PB1,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
case PWM_CH4:
gpio_init(PC9,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出,最大速度50MHZ
break;
}
}
else
{
ASSERT(0);
}
TIMx[timn]->PSC = DEFAULT_PSC; //预分频值
TIMx[timn]->ARR = arr; //重新装载的值
switch(Channel)
{
case PWM_CH1:
TIMx[timn]->CCMR1 |=0x07<<4; //PWM2输出模式设置
//TIMx[timn]->CCMR1 |=0X01<<3; //输出比较预装载使能
//TIMx[timn]->CR1 = 1<<7; //自动重装在预装载允许位
TIMx[timn]->CCER |= 0x0F; //低电平有效 CH1使能 如是TIM1则互补输出使能
TIMx[timn]->CCR1 =0; //初始化占空比为0
break;
case PWM_CH2:
TIMx[timn]->CCMR1 |=0x07<<12; //PWM2输出模式设置
//TIMx[timn]->CCMR1 |=0X01<<3; //输出比较预装载使能
//TIMx[timn]->CR1 = 1<<7; //自动重装在预装载允许位
TIMx[timn]->CCER |= 0x0F<<4; //低电平有效 CH2使能 如是TIM1则互补输出使能
TIMx[timn]->CCR2 =0; //初始化占空比为0
break;
case PWM_CH3:
TIMx[timn]->CCMR2 |=0x07<<4; //PWM3输出模式设置
//TIMx[timn]->CCMR1 |=0X01<<3; //输出比较预装载使能
//TIMx[timn]->CR1 = 1<<7; //自动重装在预装载允许位
TIMx[timn]->CCER |= 0x0F<<8; //低电平有效 CH3使能 如是TIM1则互补输出使能
TIMx[timn]->CCR3 =0; //初始化占空比为0
break;
case PWM_CH4:
TIMx[timn]->CCMR2 |=0x07<<12; //PWM4输出模式设置
//TIMx[timn]->CCMR1 |=0X01<<3; //输出比较预装载使能 1:更新事件时装载
//TIMx[timn]->CR1 = 1<<7; //自动重装在预装载允许位
TIMx[timn]->CCER = 0x03<<12; //低电平有效 CH2使能
TIMx[timn]->CCR4 =0; //初始化占空比为0
break;
}
//TIMx[timn]->DIER = 1<<0; //允许更新(溢出)中断 UIE=1;
TIMx[timn]->CR1 = 1<<0; //使能计数器 开始计数
}
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2023-12-05 Rockchip RK3588 - 移植uboot 2017.09 & linux 6.1(友善之家脚本方式)