输出比较简介
OC(Output Compare)
这里的比较指的是CNT和CCR两者进行比较,CCR是我们可以设定的比较值(介于0和ARR之间),CNT可以向上或者向下计数,那么会出现CNT的值大于或小于比较值的情况,这两种情况下分别输出高低电平就实现了PWM波的生成。
对于通用定时器和高级定时器,它们都有四个输出比较通道(当然也可以是输入捕获通道,因为捕获/比较寄存器是共用的)。
内部运作机制
3个重点:比较值
极性决定了有效电平是高/低
模式选择:模式1,小于比较值为有效电平
模式2,大于比较值为有效电平
(经过比较以后得到的oc1ref参考信号实际上已经是pwm波了,可以送去主模式的TRGO输出去触发其他外设,当然用的最多的还是送去极性选择部分判断是否需要电平翻转)
PWM的工作原理
以向上计数为例,计数值小于比较值时为低电平,否则为高电平。
这种计数方式下可以通过设置模式1,有效电平为低或者模式2,有效电平为高。
引脚复用和重映射
部分外设和引脚的连接关系如下:
在stm32中GPIO口的数量是有限的,而片上外设的数量那么多,就需要引脚复用。复用这个词其实在GPIO口的输入输出模式里也有提到。引脚复用时,代表引脚不是简单的输入输出,而是由片上外设来控制,因此我们要引脚复用的时候,引脚的输入输出模式就要设置为复用模式。简单来说就是,复用输入输出时,意味着控制权交给了片上外设。
回到引脚功能图,可以看到大部分的IO口都默认复用了2~3个片上外设的功能,那如果想同时使用同一个IO口默认复用的两个功能,这肯定会冲突,这就需要重映射(也叫重定义),需要注意的是,重映射不仅需要调用重映射函数,还需要开启AFIO外设时钟。
如图,PA2引脚默认复用了USART2_TX和TIM2_CH3,两个功能都想用的时候可以考虑把TIM2_CH3重映射,查手册或引脚功能图就可以知道它重映射到了哪个引脚上。
对于重映射,还分为部分重映射和完全重映射。部分重映射指对于某个外设的多个通道一部分重映射到了其他引脚,另一部分不变;完全重映射则是所有通道都重映射到了其他引脚。
手册里也给出了各个外设重映射的方案。其中有的外设部分重映射还分了部分重映射1和部分重映射2。这个在GPIO.C文件里也能找到。
重映射时还需要注意一点,有一些IO口是用来端口调试的,默认是不能输入输出的
如果实在需要,就必须先解除它们的功能,再重映射。解除它们的功能需要用到这几个参数。
- 首先SWJ就是SWD(ST-Link)和JTAG两种调试方式
- NoJTRST是解除JTRST引脚的占用,也就是PB4变为正常GPIO口
- JTAGDisable是用来解除所有JTAG调试相关端口的占用,具体就是PB4、PB3、PA15
- Disable则是全解除占用,而SWD是与提供ST-Link下载烧录程序的,如果解除了就只能使用普通串口来烧录程序!再想用ST-Link就只能下载一个没有调用这个函数的程序,把SWD恢复。
输出比较结构体、相关函数
typedef struct
{
uint16_t TIM_OCMode; //PWM模式1/2
uint16_t TIM_OCPolarity; //极性选择 有效电平为高/低
uint16_t TIM_Pulse; //设置CCR的值 也可以先不设置 后面用SetCompare函数随时修改
uint16_t TIM_OutputState; //输出使能
} TIM_OCInitTypeDef;
//这个函数是给输出比较结构体赋一个默认值的
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
//这四个函数是用来配置强制输出模式
//如果你在运行中暂停输出波形并且强制输出高或低电平可以用这四个函数
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
//这四个函数是用来配置CCR寄存器的预装功能
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
//用来配置快速使能
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
//这个功能在手册里,外部事件时清除REF信号那一节有介绍
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
//下面四个函数是用来配置输出比较模块的,OC就是Output Compare
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
//仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数使能主输出,否则PWM将不能正常输出
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
//这些就是单独设置输出比较的极性的
//这里带个N就是高级定时器里的互补通道的配置,OC4没有互补通道,所以就没有OC4N的函数
//这里的函数可以设置极性,在结构体初始化的那个函数里也可以设置极性,这两个地方设置极性的作用是一样的只不过用结构体是一起初始化的,在这里是一个函数的单独修改。
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
//该函数是用来单独修改使能参数
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);
//输出比较模式,是用来单独更改输出比较模式的函数
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);
//这四个函数是用来单独更改CCR寄存器值的函数,这四个函数比较重要,更改占空比就用这四个函数
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
库函数配置步骤
1.外设时钟使能(GPIO、TIMx)
2.配置GPIO(因为需要IO口来输出pwm波 同时注意推挽复用)
3.配置定时器时基单元
4.配置输出比较结构体 (比较值可以先不设置,后面调用比较值函数来修改 方便调节占空比) 然后初始化、能定时器
#include "stm32f10x.h"
#include "pwm.h"
void PWM_20ms()
{
GPIO_InitTypeDef gpio_structinit;
TIM_TimeBaseInitTypeDef TIM3_structinit;
TIM_OCInitTypeDef TIM3_OC1_structinit;
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//部分重映射 重映射时还要开启AFIO 同时PB4与JTAG调试相关,需要先解除JTRST功能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
//配置GPIO TIM3_CH1 原A6 部分重映射B4
gpio_structinit.GPIO_Mode = GPIO_Mode_AF_PP;
gpio_structinit.GPIO_Pin = GPIO_Pin_4;
gpio_structinit.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&gpio_structinit);
//配置时基单元
TIM3_structinit.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM3_structinit.TIM_Period = 200-1; //20ms
TIM3_structinit.TIM_Prescaler = 7200-1;
TIM_TimeBaseInit(TIM3,&TIM3_structinit); //还需使能 但是等配置完输出比较再使能
//配置输出比较
TIM3_OC1_structinit.TIM_OCMode = TIM_OCMode_PWM1; //模式1 小于比较值输出有效电平
TIM3_OC1_structinit.TIM_OCPolarity = TIM_OCPolarity_High; //有效电平为高电平
TIM3_OC1_structinit.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OC1Init(TIM3,&TIM3_OC1_structinit);
TIM_Cmd(TIM3,ENABLE); //使能TIM3
}