目录

1、输出比较简介:

2、输出比较模式:

3、PWM简介

4、死区发生器:

5、定时器的结构:

通用:

流程:

高级:

流程:

6、代码:

总代码:

详细解答:

7、补充:


1、输出比较简介:

  • OC(Output Compare)输出比较
    • 输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形(CC(Capture/Compare)是捕获/比较的意思,R是Register,寄存器的意思。)所以,CCR是捕获/比较寄存器的意思,这个寄存器是输入捕获和输出比较共用的,当使用输入捕获时,他就是捕获寄存器,当使用输出比较时,他就是比较寄存器
    • 每个高级定时器和通用定时器都拥有4个输出比较通道 高级定时器的前3个通道额外拥有死区生成和互补输出的功能

2、输出比较模式:

3、PWM简介

  • PWM(Pluse Width Modulation) 脉冲宽度调制
  • 在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
  • PWM参数:频率 = 1 / Ts            占空比 = Ton / Ts           分辨率 = 占空比变化步距

4、死区发生器:

规则:

  1. OC1REF 从高→低跳变时:
    1. OC1_DT(主通道):立刻跟着变低
    2. OC1N_DT(互补通道):延迟一段时间再变高(这就是死区)
    3. 效果:两个信号在切换时,有一段「都为低」的空白期,不会同时为高。
  2. OC1REF 从低→高跳变时:
    1. OC1_DT(主通道):延迟一段时间再变高(死区)
    2. OC1N_DT(互补通道):立刻跟着变低
    3. 效果:同样是「都为低」的空白期,避免同时导通

例图:

5、定时器的结构:

通用:

拥有四个通道,每个通道的结构如下图所示:

流程:

  • 最左边的输入部分
    • 当CNT < CCR1时是原本波形,不用做改变。
    • 当CNT = CCR1时是波形改变的一个临界点。
    • 当CNT > CCR1时,波形发生改变。
    • ETRF : 外部触发信号,可用于同步或触发输出模式控制器,让外部事件影响输出行为。
  • 输出模式控制器就是配置TIMx_CCMR1来配置输出模式的。
  • 他会输出oc1ref信号
    • 去向1:至主模式控制器。
      • 这部分功能叫 定时器的主模式触发(TRGO)。OC1REF 是定时器内部的比较参考信号,它的跳变(比如 CNT 和 CCR1 匹配时的电平翻转)不只是用来驱动输出引脚,还可以作为内部触发事件,用来:
        1. 触发其他定时器(从模式定时器)开始计数
        2. 触发 ADC/DAC 外设的转换
        3. 实现多定时器之间的同步、级联
    • 去向2:进入输出极性控制电路。
      • 它的功能主要是控制CC1P寄存器来控制信号要不要反转。
        • CC1P = 0:选择输入0的信号,oc1ref会直接输出(原信号)。
        • CC1P = 1:选择输入1的信号,也就是经过反相器的oc1ref,输出反相后的信号
  • 最后的信号会经过输出使能电路,它的作用是通过配置CC1E寄存器来决定信号最终能不能达到OC1引脚
    • CC1E=0:通道输出被禁用,引脚不受定时器控制(由 GPIO 配置决定)
    • CC1E=1:通道输出使能,前面处理好的信号会送到OC1引脚输出

高级:

也是拥有四个通道,每个通道的结构图如下:

流程:

  • 最左边的输入部分(跟通用定时器一样)
    • 当CNT < CCR1时是原本波形,不用做改变。
    • 当CNT = CCR1时是波形改变的一个临界点。
    • 当CNT > CCR1时,波形发生改变。
    • ETRF : 外部触发信号,可用于同步或触发输出模式控制器,让外部事件影响输出行为。
  • 输出模式控制器就是配置TIMx_CCMR1来配置输出模式的。(跟通用定时器一样)
  • 输出oc1ref信号
    • 去向1:直接进入3输入多路选择器。
    • 去向2:进入死区发生器。输出两路互补信号,并且对两路互补信号的跳变沿做掩饰错开(避免半桥上下路同时导通),随后两路信号分别进入主通道多路选择器和互补通道多路选择器
  • 3输入多路选择器选择完信号后进入极性控制电路。
  • 随后输出使能电路控制是主信号输出还是互补信号输出

6、代码:

总代码:

void PWM_Init(void)
{
	//开启TIM2时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

	//开启GPIO外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	//初始化AFIO
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//	//引脚重映射
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);   //把A0重映射到A15
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);   //因为重映射的A15口是调试端口,需要把它变成GPIO口
	
	GPIO_InitTypeDef GPIOA_InitStructure;
	GPIOA_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOA_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIOA_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &GPIOA_InitStructure);
	
	//初始化TIM2
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimInitStructure;
	TIM_TimInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimInitStructure.TIM_Period = 100 - 1;
	TIM_TimInitStructure.TIM_Prescaler = 720 - 1;
	TIM_TimInitStructure.TIM_RepetitionCounter = 0;
	
	TIM_TimeBaseInit(TIM2, &TIM_TimInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_IT_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  //开启更新中断的使能位,只有配置了这一位,定时器才可以把中断请求发出来
	
	//初始化输出比较单元
	TIM_OCInitTypeDef TIM_OCInitStructure;
//	TIM_OCInitStructure.TIM_OCIdleState = ;  //高级定时器才使用
//	TIM_OCInitStructure.TIM_OCNIdleState = ;
//	TIM_OCInitStructure.TIM_OCNPolarity = ;
//	TIM_OCInitStructure.TIM_OutputNState = ;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   //设置输出比较的模式
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;   //设置输出比较的极性
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //设置输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;   //设置CCR
	
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);
}

详细解答:

  • 首先是要打开TIM2和GPIO外设时钟并且初始化GPIO口(不过多赘述了)
	//开启TIM2时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

	//开启GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	GPIO_InitTypeDef GPIOA_InitStructure;
	GPIOA_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOA_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIOA_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &GPIOA_InitStructure);
  • 随后就是初始化TIM2了(上一篇讲了)
	//初始化TIM2
	TIM_InternalClockConfig(TIM2);  //给定时器2配置内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimInitStructure;
	TIM_TimInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //定时器时钟分频系数
	TIM_TimInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式
	TIM_TimInitStructure.TIM_Period = 100 - 1;  //自动重装值ARR
	TIM_TimInitStructure.TIM_Prescaler = 720 - 1;  //预分频器PSC
	TIM_TimInitStructure.TIM_RepetitionCounter = 0;  //重复计数
	
	TIM_TimeBaseInit(TIM2, &TIM_TimInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_IT_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  //开启更新中断的使能位,只有配置了这一位,定时器才可以把中断请求发出来
  • 时基单元的配置:
	//初始化输出比较单元
	TIM_OCInitTypeDef TIM_OCInitStructure;
//	TIM_OCInitStructure.TIM_OCIdleState = ;  //高级定时器才使用
//	TIM_OCInitStructure.TIM_OCNIdleState = ;
//	TIM_OCInitStructure.TIM_OCNPolarity = ;
//	TIM_OCInitStructure.TIM_OutputNState = ;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   //设置输出比较的模式
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;   //设置输出比较的极性
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //设置输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;   //设置CCR

	TIM_OC1Init(TIM2, &TIM_OCInitStructure);  //OC1通道

TIM_OCInitTypeDef的成员:(只列举通用定时器所使用的)

  • TIM_OCMode:配置输出比较的模式(配置红框圈出来的)

  • TIM_OCPolarity:设置输出比较的极性
    • 先是他的可用的值
      • #define TIM_OCPolarity_High  //高电平有效
      • #define TIM_OCPolarity_Low  //低电平有效
    • 对应图中的位置(红框圈出来的)

  • TIM_OutputState:设置使能输出,配置图中的使能输出电路。

  • TIM_Pulse:设置CCR

7、补充:

        有的时候我们所选择的GPIO口会被占用,但是一个OC通道对应一个GPIO口。所以官方给了我们一种重映射的方法,就是将原来的GPIO口重映射到别的GPIO口上。

重映射方法:

  1. 打开AFIO的外设时钟(在APB2总线上)
  2. 使用重映射函数

代码如下:

	//打开AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	//引脚重映射
	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);   //把A0重映射到A15

注意:我们重映射时,是不能随便重映射的。官方给了我们一张芯片引脚定义图(我把它放在最后了),上面记录了引脚编号与位置,主功能与复用功能,特殊功能引脚等。我们需要注意的是,我们重映射的引脚是否为GPIO引脚

映射规则:

GPIO_PinRemapConfig()参数1:

比如我们的代码中是TIM2的OC1通道,OC1通道对应的是A0口。A0口重映射到A15口。如下图所示

但是PA15上电后默认是调试端口JTDI,所以要再关闭其调试端口的复用

图中的PA13,PA14,PA15,PB3,PB4都是调试端口,PA13和PA14是JTAG和SWD调试,PA15,PB3和PB4就只有JTAG调试。

我们解除调试要再一次调用GPIO_PinRemapConfig()函数。

其中的参数1:

注意:第三个参数GPIO_Remap_SWJ_Disable要慎用后果:SWD 下载 / 调试永久失效,芯片只能通过串口 ISP 烧录程序。

以下是解除调试的代码:

	//打开AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	//引脚重映射
	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);   //把A0重映射到A15
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);   //因为重映射的A15口是调试端口,需要把它变成GPIO口

芯片引脚图:

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐