IC(input Capture):输入捕获
CC(Capture /Compare):输入捕获,输出比较单元
输出比较可以通过比较CNT和CCR寄存器值的关系,对输出电平进行置1,置0的反转操作,用于输出一定频率的占空比PWM波形
高级定时器和通用定时器有四个输出比较通道
什么是PWM:全称是脉冲宽度调制
PWM参数:频率1/Ts 占空比=Ton/Ts 分辨率=占空比变化步距
TON=高电平比上Ts的时间
TOFF:表示低电平比上Ts的时间
TS:表示周期
例如:我们50%占空比就是TON和TOFF时间是相等的
再例如我们20%占空比:就是TON占周期的20%,TOFF占周期的80%
也就是假设5v的波形,百分之50占空比代表高低电平都是时间相等的,电压接近2.5v
假设5v的波形,百分之20占空比那么高电平持持续只占20%也就是时间测量电压接近1V
也就是占空比越大实际等效于高电平,占空比越小等效于低电平,占空比100%高电平,占空比0%就是低电平。
计算公式:
CK_PSC:主时钟频率
下面两个在前面的Timer里面有仔细描述过配置方法
PSC:预分频器的值
ARR:自动重装载器的值
现在我要输出10k赫兹的,占空比50%,PWM分辨率是1%的 PWM
10000=72000000/(PSC+1)/(ARR+1) PSC=719
50%=CCR/(ARR+1) CCR=50
1%=1/(ARR+1) ARR=99
再举例我要输出50k赫兹,占空比10%,PWM分辨率是1%的 PWM
50000=72000000/(PSC+1)/(ARR+1) PSC=14.4
10%=CCR/(ARR+1) CCR=10
1%=1/(ARR+1) ARR=99
所以一般占频率设置能被2整除最好
这样就配置好了三个重要的PWM参数
配置比较输出模块的函数
初始化输出比较单元
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);
上面四个函数分别配置四个输出比较参数1是选择的定时器,参数2是传入结构体
给输出比较结构体赋默认值的
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
传入输出比较的结构体
单独修改输出使能参数
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);
使能TIME
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
代码实现
先查找要操作的TIM2作为PWM输出的GPIO是哪个引脚,由图可以看出是PA0
去数据手册里面可以查找到TIM2作为输出比较通道,端口应该配置成推挽复用输出
初始化GPIO
void pwm_Init(void)
{
//初始化GPIO
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
//传入结构体
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
根据计算公式得出我们需要配置以下几个重要的寄存器 PSC,ARR,CCR
PSC:预分频器的值
ARR:自动重装载器的值
CCR:捕获/比较寄存器
现在我要输出10k赫兹的,占空比50%,PWM分辨率是1%的 PWM
10000=72000000/(PSC+1)/(ARR+1) PSC=719
50%=CCR/(ARR+1) CCR=50
1%=1/(ARR+1) ARR=99
配置PSC,ARR
void pwm_Init(void)
{
//初始化GPIO
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
//传入结构体
GPIO_Init(GPIOA,&GPIO_InitStruct);
//开启APB1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
TIM_InternalClockConfig(TIM2);
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
//选择分频一般是1分频
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
//计数模式
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
//接下来三个参数的配置就是时基关键寄存器参数的配置
//周期就是ARR自动重装载器的值
TIM_TimeBaseInitStruct.TIM_Period=100-1;
//Prescaler是PSC预分频器的值
TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;
//RepetitionCounter是重复计数器的值 高级定时器才有
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
}
配置CCR,并使能
//初始化GPIO
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
//传入结构体
GPIO_Init(GPIOA,&GPIO_InitStruct);
//开启APB1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
TIM_InternalClockConfig(TIM2);
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
//选择分频一般是1分频
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
//计数模式
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
//接下来三个参数的配置就是时基关键寄存器参数的配置
//周期就是ARR自动重装载器的值
TIM_TimeBaseInitStruct.TIM_Period=100-1;
//Prescaler是PSC预分频器的值
TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;
//RepetitionCounter是重复计数器的值 高级定时器才有
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
//Tim输出PWM的配置
TIM_OCInitTypeDef TIM_OCInitStruct;
//给结构体赋初值
TIM_OCStructInit(&TIM_OCInitStruct);
//输出比较模式
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
//输出比较极性
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
//设置输出比较使能
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
//设置CCR 如果CCR的值需要不断变化就需要单独设置CCR的值并屏蔽这段代码
TIM_OCInitStruct.TIM_Pulse=50;
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
//使能TIM2
TIM_Cmd(TIM2,ENABLE);
}
单独设置CCR的值
void PWM_Set(unsigned short Compare)
{
//单独设置CCR的值
TIM_SetCompare1(TIM2,Compare);
}
如果此时PA0已经被其他占用那么可以使用引脚重映射来映射到其他引脚上面(需要使用到AFIO)
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
引脚重映射专用,需要开启AFIO时钟
的第一个参数 可用部分重映射1,部分重映射2,全部重映射
第二个参数填使能或者不使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
这样就把PA0的TIM2换到了PA15
前提是重映射由TIM2才可以哈
因为PA15默认是JTDI所以我们要关闭这个功能才能使PA15变成正常的GPIO
解除调试端口的复用
SWJ就是JATA和SWD两种调试方式
第一个是:解除JTREST引脚的复用也就是PB4
第二个是:解除JTAG调试端口的复用
第三个:就是把SWD和JTAG端口调试全部解除。这样的话你以后下载程序都不能用这两种下载了
所以代码改一下就好了这样就把PA0的TIM2功能重映射到PA15了
void pwm_Init(void)
{
//TIM2从PA0重映射到PA15
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//解除PA15的重映射,让其变成正常GPIO
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
//初始化GPIO
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
//传入结构体
GPIO_Init(GPIOA,&GPIO_InitStruct);
//开启APB1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//选择时基单元的时钟 注意这里是配置时钟 (我们选择内部时钟)
TIM_InternalClockConfig(TIM2);
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
//选择分频一般是1分频
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
//计数模式
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
//接下来三个参数的配置就是时基关键寄存器参数的配置
//周期就是ARR自动重装载器的值
TIM_TimeBaseInitStruct.TIM_Period=100-1;
//Prescaler是PSC预分频器的值
TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;
//RepetitionCounter是重复计数器的值 高级定时器才有
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
//Tim输出PWM的配置
TIM_OCInitTypeDef TIM_OCInitStruct;
//给结构体赋初值
TIM_OCStructInit(&TIM_OCInitStruct);
//输出比较模式
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
//输出比较极性
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
//设置输出比较使能
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
//设置CCR
TIM_OCInitStruct.TIM_Pulse=50;
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
//使能TIM2
TIM_Cmd(TIM2,ENABLE);
}
笔记来自哔哩哔哩江科大自化协
软件仿真可以看出没什么大问题
从下面这个图片也可以看出我们设置的ARR,CCR,PSC的值
下面是来自2024/5/16号我有个项目需要输出PWM和测试PWM
附上示波器勾的波形 我单片机有的问题(正常输出应该就是不会超过10010K)
经过验证我重新换一个MCU就好了