输出比较(OC) PWM 的三大重要参数计算以及端口重映射,末尾附上软件仿真

文章详细介绍了如何在STM32微控制器中配置PWM信号,包括计算预分频器(PSC)、自动重装载寄存器(ARR)和捕获/比较寄存器(CCR)的值以生成特定频率和占空比的PWM波形。通过示例展示了如何设置10kHz和50kHz的PWM输出,并强调了占空比与实际电平的关系。此外,还涉及了GPIO初始化、引脚重映射以及定时器配置的相关函数和步骤。
摘要由CSDN通过智能技术生成

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就好了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值