4.2 pwm相关
脉宽调制相关知识
在stm32中,pwm代表脉冲宽度调制,通过调整信号的脉冲宽度和周期来模拟不同的电压或功率级别。
pwm的相关参数: 周期、占空比、极性、脉冲宽度、对齐方式
pwm通过改变周期控制频率,通过占空比(脉冲宽度)控制输出电压
周期:
两个上升沿之间的时间间隔。
极性:
设置高电平有效还是低电平有效
占空比:
一个周期之间的有效电平时间 / 一个周期时间
#include "includes.h"
#include "pwm.h"
//GPIO口基地址放入常数数据组GPIO_ARR[0]~GPIO_ARR[5]中
//static const GPIO_TypeDef * GPIO_ARR[] =
// {(GPIO_TypeDef *)GPIOA_BASE,(GPIO_TypeDef *)GPIOB_BASE,
// (GPIO_TypeDef *)GPIOC_BASE,(GPIO_TypeDef *)GPIOD_BASE,
// (GPIO_TypeDef *)GPIOE_BASE,(GPIO_TypeDef *)GPIOH_BASE};
//定时器模块2地址映射
TIM_TypeDef *PWM_ARR[]={TIM2};
//*****************************内部函数******************************
void tim_mux_val(uint16_t pwmNo,uint8_t* TIM_i,uint8_t* chl);
void tim_timer_init1(uint16_t TIM_i,uint32_t f,uint16_t MOD_Value);
//*****************************对外接口函数******************************
//=====================================================================
//函数名称: pwm_init
//功能概要: pwm初始化函数
//参数说明: pwmNo:pwm模块号,在gec.h的宏定义给出,如PWM_PIN0表示PWM0通道
// clockFre:时钟频率,单位:hz,取值:375、750、1500、3000、6000、
// 12000、24000、48000
// period:周期,单位为个数,即计数器跳动次数,范围为1~65536
// duty:占空比:0.0~100.0对应0%~100%
// align:对齐方式 ,在头文件宏定义给出,如PWM_EDGE为边沿对其。
// pol:极性,在头文件宏定义给出,如PWM_PLUS为正极性
//函数返回: 无
//注意: 因为GEC中给出的PWM和输入捕捉都是同一模块的,只是通道不同,所以为防止在使用多组
// PWM和输入捕捉时,频率篡改,需要使得使用到的clockFre和period参数保持一致。
// 占空比:是指高电平在一个周期中所占的时间比例。
//=====================================================================
void pwm_init(uint16_t pwmNo,uint32_t clockFre,uint16_t period,double duty,
uint8_t align,uint8_t pol)
{
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
uint32_t temp;
//(防止越界
if(duty>100.0) duty=100.0;
//(1)获得解析的tim模块号和通道号并对相应的引脚进行引脚复用
tim_mux_val(pwmNo,&TIM_i,&chl);
//(2)PWM对齐方式的设定
if(align == PWM_CENTER)//中心对齐
{
tim_timer_init1(TIM_i,clockFre,period/2);
PWM_ARR[TIM_i]->CR1 |= TIM_CR1_CMS;//设置中心对齐
if(pol == PWM_PLUS) //正极性
{
temp = (uint32_t)(duty*period/200);
if(temp >= 65536) temp=65535;
switch(chl)
{
case 1: //通道1
{
PWM_ARR[TIM_i]->CCMR1 &= ~TIM_CCMR1_OC1M_Msk;
PWM_ARR[TIM_i]->CCMR1 |= 6 << TIM_CCMR1_OC1M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= ~TIM_CCER_CC1P_Msk; //设置为正极性
PWM_ARR[TIM_i]->CCR1=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR1 &=~TIM_CCMR1_OC1PE_Msk;
PWM_ARR[TIM_i]->CCMR1 |=TIM_CCMR1_OC1PE_Msk; //使能ch1预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC1E_Msk; //使能PWM输出
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC1NP_Msk;
break;
}
case 2: //通道2
{
PWM_ARR[TIM_i]->CCMR1 &= ~TIM_CCMR1_OC2M_Msk;
PWM_ARR[TIM_i]->CCMR1 |= 6 << TIM_CCMR1_OC2M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= ~TIM_CCER_CC2P_Msk; //设置为正极性
PWM_ARR[TIM_i]->CCR2=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR1 &=~TIM_CCMR1_OC2PE_Msk;
PWM_ARR[TIM_i]->CCMR1 |=TIM_CCMR1_OC2PE_Msk; //使能ch2预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC2E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC2NP_Msk; //使能PWM输出
break;
}
case 3: //通道3
{
PWM_ARR[TIM_i]->CCMR2 &= ~TIM_CCMR2_OC3M_Msk;
PWM_ARR[TIM_i]->CCMR2 |= 6 << TIM_CCMR2_OC3M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= ~TIM_CCER_CC3P_Msk; //设置为正极性
PWM_ARR[TIM_i]->CCR3=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC3PE_Msk;
PWM_ARR[TIM_i]->CCMR2 |=TIM_CCMR2_OC3PE_Msk; //使能ch3预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC3E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC3NP_Msk; //使能PWM输出
break;
}
case 4: //通道4
{
PWM_ARR[TIM_i]->CCMR2 &= ~TIM_CCMR2_OC4M_Msk;
PWM_ARR[TIM_i]->CCMR2 |= 6 << TIM_CCMR2_OC4M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= ~TIM_CCER_CC4P_Msk; //设置为正极性
PWM_ARR[TIM_i]->CCR4=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC4PE_Msk;
PWM_ARR[TIM_i]->CCMR2 |=TIM_CCMR2_OC4PE_Msk; //使能ch4预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC4E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC4NP_Msk; //使能PWM输出
break;
}
}
}
else //负极性
{
temp = (uint32_t)(period*(100-duty)/200);
if(temp >= 65536) temp=65535;
switch(chl)
{
case 1: //通道1
{
PWM_ARR[TIM_i]->CCMR1 &= ~TIM_CCMR1_OC1M_Msk;
PWM_ARR[TIM_i]->CCMR1 |= 6 << TIM_CCMR1_OC1M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= TIM_CCER_CC1P_Msk; //设置为负极性
PWM_ARR[TIM_i]->CCR1=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR1 &=~TIM_CCMR1_OC1PE_Msk;
PWM_ARR[TIM_i]->CCMR1 |=TIM_CCMR1_OC1PE_Msk; //使能ch1预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC1E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC1NP_Msk; //使能PWM输出
break;
}
case 2: //通道2
{
PWM_ARR[TIM_i]->CCMR1 &= ~TIM_CCMR1_OC2M_Msk;
PWM_ARR[TIM_i]->CCMR1 |= 6 << TIM_CCMR1_OC2M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= TIM_CCER_CC2P_Msk; //设置为负极性
PWM_ARR[TIM_i]->CCR2=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR1 &=~TIM_CCMR1_OC2PE_Msk;
PWM_ARR[TIM_i]->CCMR1 |=TIM_CCMR1_OC2PE_Msk; //使能ch2预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC2E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC2NP_Msk; //使能PWM输出
break;
}
case 3: //通道3
{
PWM_ARR[TIM_i]->CCMR2 &= ~TIM_CCMR2_OC3M_Msk;
PWM_ARR[TIM_i]->CCMR2 |= 6 << TIM_CCMR2_OC3M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= TIM_CCER_CC3P_Msk; //设置为负极性
PWM_ARR[TIM_i]->CCR3=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC3PE_Msk;
PWM_ARR[TIM_i]->CCMR2 |=TIM_CCMR2_OC3PE_Msk; //使能ch3预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC3E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC3NP_Msk; //使能PWM输出
break;
}
case 4: //通道4
{
PWM_ARR[TIM_i]->CCMR2 &= ~TIM_CCMR2_OC4M_Msk;
PWM_ARR[TIM_i]->CCMR2 |= 6 << TIM_CCMR2_OC4M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= TIM_CCER_CC4P_Msk; //设置为负极性
PWM_ARR[TIM_i]->CCR4=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC4PE_Msk;
PWM_ARR[TIM_i]->CCMR2 |=TIM_CCMR2_OC4PE_Msk; //使能ch4预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC4E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC4NP_Msk; //使能PWM输出
break;
}
}
}
}
else //边沿对齐
{
tim_timer_init1(TIM_i,clockFre,period);
PWM_ARR[TIM_i]->CR1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS);//设置向上计数模式、边沿对齐
//(3)PWM极性的设定和PWM占空比的设定
temp = PWM_ARR[TIM_i]->ARR + 1; //计算周期(period)
if(pol == PWM_PLUS) //正极性
{
temp = (uint32_t)(temp*duty/100);
if(temp >= 65536) temp=65535;
switch(chl)
{
case 1: //通道1
{
PWM_ARR[TIM_i]->CCMR1 &= ~TIM_CCMR1_OC1M_Msk;
PWM_ARR[TIM_i]->CCMR1 |= 6 << TIM_CCMR1_OC1M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= ~TIM_CCER_CC1P_Msk; //设置为正极性
PWM_ARR[TIM_i]->CCR1=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR1 &=~TIM_CCMR1_OC1PE_Msk;
PWM_ARR[TIM_i]->CCMR1 |=TIM_CCMR1_OC1PE_Msk; //使能ch1预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC1E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC1NP_Msk; //使能PWM输出
break;
}
case 2: //通道2
{
PWM_ARR[TIM_i]->CCMR1 &= ~TIM_CCMR1_OC2M_Msk;
PWM_ARR[TIM_i]->CCMR1 |= 6 << TIM_CCMR1_OC2M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= ~TIM_CCER_CC2P_Msk; //设置为正极性
PWM_ARR[TIM_i]->CCR2=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR1 &=~TIM_CCMR1_OC2PE_Msk;
PWM_ARR[TIM_i]->CCMR1 |=TIM_CCMR1_OC2PE_Msk; //使能ch2预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC2E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC2NP_Msk; //使能PWM输出
break;
}
case 3: //通道3
{
PWM_ARR[TIM_i]->CCMR2 &= ~TIM_CCMR2_OC3M_Msk;
PWM_ARR[TIM_i]->CCMR2 |= 6 << TIM_CCMR2_OC3M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= ~TIM_CCER_CC3P_Msk; //设置为正极性
PWM_ARR[TIM_i]->CCR3=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC3PE_Msk;
PWM_ARR[TIM_i]->CCMR2 |=TIM_CCMR2_OC3PE_Msk; //使能ch3预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC3E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC3NP_Msk; //使能PWM输出
break;
}
case 4: //通道4
{
PWM_ARR[TIM_i]->CCMR2 &= ~TIM_CCMR2_OC4M_Msk;
PWM_ARR[TIM_i]->CCMR2 |= 6 << TIM_CCMR2_OC4M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= ~TIM_CCER_CC4P_Msk; //设置为正极性
PWM_ARR[TIM_i]->CCR4=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC4PE_Msk;
PWM_ARR[TIM_i]->CCMR2 |=TIM_CCMR2_OC4PE_Msk; //使能ch4预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC4E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC4NP_Msk; //使能PWM输出
break;
}
}
}
else //负极性
{
temp = (uint32_t)(temp*(100-duty)/100);
if(temp >= 65536) temp=65535;
switch(chl)
{
case 1: //通道1
{
PWM_ARR[TIM_i]->CCMR1 &= ~TIM_CCMR1_OC1M_Msk;
PWM_ARR[TIM_i]->CCMR1 |= 6 << TIM_CCMR1_OC1M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= TIM_CCER_CC1P_Msk; //设置为负极性
PWM_ARR[TIM_i]->CCR1=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR1 &=~TIM_CCMR1_OC1PE_Msk;
PWM_ARR[TIM_i]->CCMR1 |=TIM_CCMR1_OC1PE_Msk; //使能ch1预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC1E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC1NP_Msk; //使能PWM输出
break;
}
case 2: //通道2
{
PWM_ARR[TIM_i]->CCMR1 &= ~TIM_CCMR1_OC2M_Msk;
PWM_ARR[TIM_i]->CCMR1 |= 6 << TIM_CCMR1_OC2M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= TIM_CCER_CC2P_Msk; //设置为负极性
PWM_ARR[TIM_i]->CCR2=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR1 &=~TIM_CCMR1_OC2PE_Msk;
PWM_ARR[TIM_i]->CCMR1 |=TIM_CCMR1_OC2PE_Msk; //使能ch2预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC2E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC2NP_Msk; //使能PWM输出
break;
}
case 3: //通道3
{
PWM_ARR[TIM_i]->CCMR2 &= ~TIM_CCMR2_OC3M_Msk;
PWM_ARR[TIM_i]->CCMR2 |= 6 << TIM_CCMR2_OC3M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= TIM_CCER_CC3P_Msk; //设置为负极性
PWM_ARR[TIM_i]->CCR3=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC3PE_Msk;
PWM_ARR[TIM_i]->CCMR2 |=TIM_CCMR2_OC3PE_Msk; //使能ch3预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC3E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC3NP_Msk; //使能PWM输出
break;
}
case 4: //通道4
{
PWM_ARR[TIM_i]->CCMR2 &= ~TIM_CCMR2_OC4M_Msk;
PWM_ARR[TIM_i]->CCMR2 |= 6 << TIM_CCMR2_OC4M_Pos; //选择PWM模式1
PWM_ARR[TIM_i]->CCER &= TIM_CCER_CC4P_Msk; //设置为负极性
PWM_ARR[TIM_i]->CCR4=temp; //设置占空比
PWM_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC4PE_Msk;
PWM_ARR[TIM_i]->CCMR2 |=TIM_CCMR2_OC4PE_Msk; //使能ch4预装载寄存器
PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC4E_Msk;
//PWM_ARR[TIM_i]->CCER |= TIM_CCER_CC4NP_Msk; //使能PWM输出
break;
}
}
}
}
PWM_ARR[TIM_i]->EGR |= TIM_EGR_UG_Msk; //初始化计数器;
PWM_ARR[TIM_i]->CR1 |= TIM_CR1_CEN_Msk | TIM_CR1_ARPE_Msk;//使能定时器
}
//=====================================================================
//函数名称: pwm_update
//功能概要: timx模块Chy通道的PWM更新
//参数说明: pwmNo:pwm模块号,在gec.h的宏定义给出,如PWM_PIN0表示PWM0通道
// duty:占空比:0.0~100.0对应0%~100%
//函数返回: 无
//=====================================================================
void pwm_update(uint16_t pwmNo,double duty)
{
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
uint32_t period;
// 防止越界
if(duty>100.0) duty=100.0;
//1. 获得解析的tpm模块号和通道号
tim_mux_val(pwmNo,&TIM_i,&chl);
period=PWM_ARR[TIM_i]->ARR;
//2. 更新PWM通道寄存器值
switch(chl)
{
case 1:PWM_ARR[TIM_i]->CCR1=(uint32_t)(period*duty/100);break;
case 2:PWM_ARR[TIM_i]->CCR2=(uint32_t)(period*duty/100);break;
case 3:PWM_ARR[TIM_i]->CCR3=(uint32_t)(period*duty/100);break;
case 4:PWM_ARR[TIM_i]->CCR4=(uint32_t)(period*duty/100);break;
}
}
//------以下为内部函数------
//=====================================================================
//函数名称:tpm_mux_val
//功能概要:将传进参数pwmNo进行解析,得出具体模块号与通道号(例:PWM_PIN0
// 解析出PORT引脚,并设置指定引脚为PWM功能)。
//参数说明:pwmNo:pwm模块号,在头文件的宏定义给出,如PWM_PIN0表示PWM0通道
//
//函数返回:无
//=====================================================================
void tim_mux_val(uint16_t pwmNo,uint8_t* TIM_i,uint8_t* chl)
{
//uint8_t port,pin;
//1.解析模块号和通道号
switch(pwmNo)
{
case ((0<<8)|5):
*TIM_i =0;
*chl=1;
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOAEN_Msk;
GPIOA->MODER &= ~GPIO_MODER_MODE5_Msk;
GPIOA->MODER |= 2<<GPIO_MODER_MODE5_Pos;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT5_Msk;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED5_Msk;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5_Msk;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFSEL5_Msk;
GPIOA->AFR[0] |= 1<<GPIO_AFRL_AFSEL5_Pos;
break;
case ((0<<8)|15):
*TIM_i =0;
*chl=1;
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOAEN_Msk;
GPIOA->MODER &= ~GPIO_MODER_MODE15_Msk;
GPIOA->MODER |= 2<<GPIO_MODER_MODE5_Pos;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT15_Msk;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED15_Msk;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD15_Msk;
GPIOA->AFR[1] &= ~GPIO_AFRH_AFSEL15_Msk;
GPIOA->AFR[1] |= 1<<GPIO_AFRH_AFSEL15_Pos;
break;
case ((1<<8)|3):
*TIM_i =0;
*chl=2;
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOBEN_Msk;
GPIOB->MODER &= ~GPIO_MODER_MODE3_Msk;
GPIOB->MODER |= 2<<GPIO_MODER_MODE3_Pos;
GPIOB->OTYPER &= ~GPIO_OTYPER_OT3_Msk;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED3_Msk;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD3_Msk;
GPIOB->AFR[0] &= ~GPIO_AFRL_AFSEL3_Msk;
GPIOB->AFR[0] |= 1<<GPIO_AFRL_AFSEL3_Pos;
break;
case ((1<<8)|10):
*TIM_i =0;
*chl=3;
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOBEN_Msk;
GPIOB->MODER &= ~GPIO_MODER_MODE10_Msk;
GPIOB->MODER |= 2<<GPIO_MODER_MODE10_Pos;
GPIOB->OTYPER &= ~GPIO_OTYPER_OT10_Msk;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED10_Msk;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD10_Msk;
GPIOB->AFR[1] &= ~GPIO_AFRH_AFSEL10_Msk;
GPIOB->AFR[1] |= 1<<GPIO_AFRH_AFSEL10_Pos;
break;
case ((1<<8)|11):
*TIM_i =0;
*chl=4;
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOBEN_Msk;
GPIOB->MODER &= ~GPIO_MODER_MODE11_Msk;
GPIOB->MODER |= 2<<GPIO_MODER_MODE11_Pos;
GPIOB->OTYPER &= ~GPIO_OTYPER_OT11_Msk;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED11_Msk;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD11_Msk;
GPIOB->AFR[1] &= ~GPIO_AFRH_AFSEL11_Msk;
GPIOB->AFR[1] |= 1<<GPIO_AFRH_AFSEL11_Pos;
break;
}
}
//===================================================================
//函数名称: tim_timer_init1
//功能概要: tim模块初始化,设置计数器频率f及计数器溢出时间MOD_Value。
//参数说明: pwmNo:pwm模块号,在gec.h的宏定义给出,如PWM_PIN0表示PWM0通道
// f:单位:Khz,取值:375、750、1500、3000、6000、12000、24000、48000
// MOD_Value:单位个数:范围取决于计数器频率与计数器位数(16位)
//函数返回: 无
//===================================================================
void tim_timer_init1(uint16_t TIM_i,uint32_t f,uint16_t MOD_Value)
{
//局部变量声明
uint32_t clk_f;
if(TIM_i==0)
{
//(1)使能定时器时钟
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN;
//(2)设置时钟频率
clk_f=MCU_SYSTEM_CLK /f;
TIM2->PSC = clk_f-1;
// (3)计数器清零
TIM2->CNT = 0;
//(4)设置溢出值
if(MOD_Value == 0)
{
TIM2->ARR = 0;
}
else
{
TIM2->ARR = (uint32_t)(MOD_Value-1);
}
//(5)更新中断使能
//TIM2->DIER |= TIM_DIER_UIE_Msk;
}
}
输入捕捉与输出比较
输入捕获,当通道输入引脚出现指定的电平跳变时,将当前寄存器的值锁到一个寄存器中,可以用于测量pwm波形的频率,占空比,电平持续时间等问题。
输出比较,将计数器与比较寄存器相比较,大于输出一种波形,小于输出一种波形。可以做一些相应的动作,如:电平翻转等,常用于产生pwm波形。
//===================================================================
//函数名称:incap_init
//功能概要:incap模块初始化。
//参数说明:capNo:输入捕捉通道号(使用宏定义INCAP_PIN0、INCAP_PIN1、…)
// clockFre:时钟频率,单位:Khz,取值:375、750、1500、3000、6000、
// 12000、24000、48000
// period:自动重装载器值,单位为个数,即计数器跳动次数,范围为1~65536
// capmode:输入捕捉模式(上升沿、下降沿、双边沿),有宏定义常数使用
//函数返回:无
//注意: 因为GEC中给出的PWM和输入捕捉都是同一模块的,只是通道不同,所以为防止在使用多组
// PWM和输入捕捉时,频率篡改,需要使得使用到的clockFre和period参数保持一致。
//capNo #define INCAP_PIN0 GEC_10 //GEC_10(PTC_NUM|2)//UART0_TX
// #define INCAP_PIN1 GEC_8 //GEC_8(PTC_NUM|3)//UART0_RX
//===================================================================
void incapture_init(uint16_t capNo,uint32_t clockFre,uint16_t period,uint8_t capmode)
{
GPIO_TypeDef *gpio_ptr; //声明gpio_ptr为GPIO结构体类型指针
uint8_t port,pin; //声明端口port、引脚pin变量
uint8_t TIM_i,chl; //由tpmx_Chy解析出的tim模块号、通道号临时变量
uint32_t temp; //临时存放寄存器里的值
//gpio引脚解析
port = (capNo>>8); //解析出的端口
pin = capNo; //解析出的引脚号
tim_mux_val(capNo,&TIM_i,&chl);//由对应模块号(PTx_num|y))得出时钟模块号和通道号
timer_init2(TIM_i,clockFre,period);//时钟模块初始化
//GPIO寄存器引脚复用
gpio_ptr=GPIO_ARR[port];
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN_Msk<< (port * 1u);//使能GPIO时钟
//进行模式选择,引脚选择备用功能
temp = gpio_ptr->MODER;
temp &= ~(GPIO_MODER_MODE0 << (pin * 2u));
temp|=2<<(pin * 2u);
gpio_ptr->MODER = temp;
gpio_ptr->OTYPER &= ~(GPIO_OTYPER_OT0_Msk<<(pin * 1u));//推挽输出
gpio_ptr->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED0<<(pin * 2u));//设置速度
gpio_ptr->PUPDR &= ~(GPIO_PUPDR_PUPD0<<(pin * 2u));//下拉
//引脚复用选择对应通道功能
gpio_ptr->AFR[0]&=~(GPIO_AFRL_AFSEL0_Msk<< (pin * 4u));
gpio_ptr->AFR[0]|=14<<(pin * 4u);
if(TIM_i==15)
{
if(chl==1)
{
TIM15->CCMR1&=~TIM_CCMR1_IC1F;//无滤波器
TIM15->CCMR1&=~TIM_CCMR1_IC1PSC;//无预分频器,捕获输入上每检测到一个边沿便执行捕获
TIM15->CCMR1|=TIM_CCMR1_CC1S_0;//CC1 通道配置为输入,IC1 映射到 TI1 上
//输入捕捉参数设定CC1P与CC1NP配合使用
if(capmode == CAP_UP)//上升沿捕捉
{
TIM15->CCER&=~TIM_CCER_CC1P;
TIM15->CCER&=~TIM_CCER_CC1NP;
}
else if(capmode == CAP_DOWN)//下降沿捕捉
{
TIM15->CCER|=TIM_CCER_CC1P;
TIM15->CCER&=~TIM_CCER_CC1NP;
}
else if(capmode == CAP_DOUBLE)//双边沿捕捉
{
TIM15->CCER|=TIM_CCER_CC1P;
TIM15->CCER|=TIM_CCER_CC1NP;
}
TIM15->CCER|=TIM_CCER_CC1E;//使能捕获,决定了是否可以实际将计数器值捕获到输入捕获/比较寄存器 1(TIMx_CCR1)
}
if(chl==2)
{
TIM15->CCMR1&=~TIM_CCMR1_IC2F;//无滤波器
TIM15->CCMR1&=~TIM_CCMR1_IC2PSC;//无预分频器,捕获输入上每检测到一个边沿便执行捕获
TIM15->CCMR1|=TIM_CCMR1_CC2S_0;//CC2 通道配置为输入,IC2 映射到 TI2 上
//输入捕捉参数设定CC2P与CC2NP配合使用
if(capmode == CAP_UP)//上升沿捕捉
{
TIM15->CCER&=~TIM_CCER_CC2P;
TIM15->CCER&=~TIM_CCER_CC2NP;
}
else if(capmode == CAP_DOWN)//下降沿捕捉
{
TIM15->CCER|=TIM_CCER_CC2P;
TIM15->CCER&=~TIM_CCER_CC2NP;
}
else if(capmode == CAP_DOUBLE)//双边沿捕捉
{
TIM15->CCER|=TIM_CCER_CC2P;
TIM15->CCER|=TIM_CCER_CC2NP;
}
TIM15->CCER|=TIM_CCER_CC2E;//使能捕获,决定了是否可以实际将计数器值捕获到输入捕获/比较寄存器2(TIMx_CCR2)
}
}
}
//=====================================================================
//函数名称:incapture_value
//功能概要:获取该通道的计数器当前值
//参数说明:capNo:输入捕捉通道号(使用宏定义INCAP_PIN0、INCAP_PIN1、…)
//函数返回:通道的计数器当前值
//=====================================================================
uint16_t get_incapture_value(uint16_t capNo)
{
uint8_t TIM_i,chl; //由tapNo解析出的tim模块号、通道号临时变量
uint16_t cnt=0;
tim_mux_val(capNo,&TIM_i,&chl);//解析tim模块号和通道号
if(TIM_i==15)
{
if(chl==1)
cnt=TIM15->CCR1;
else if(chl==2)
cnt=TIM15->CCR2;
}
return cnt;
}
//===================================================================
//函数名称:cap_clear_flag
//功能概要:清输入捕捉中断标志位。
//参数说明:capNo:输入捕捉通道号(使用宏定义INCAP_PIN0、INCAP_PIN1、…)
//函数返回:无
//===================================================================
void cap_clear_flag(uint16_t capNo)
{
uint8_t TIM_i,chl; //由capNo解析出的tim模块号、通道号临时变量
tim_mux_val(capNo,&TIM_i,&chl);
if(TIM_i==15)
{
if(chl==1)
TIM15->SR&=~TIM_SR_CC1IF;
else if(chl==2)
TIM15->SR&=~TIM_SR_CC2IF;
}
}
//===================================================================
//函数名称:cap_get_flag
//功能概要:获取输入捕捉中断标志位。
//参数说明:capNo:输入捕捉通道号(使用宏定义INCAP_PIN0、INCAP_PIN1、…)
//函数返回:返回当前中断标志位
//===================================================================
uint8_t cap_get_flag(uint16_t capNo)
{
uint8_t TIM_i,chl,flag; //由capNo解析出的tim模块号、通道号临时变量
flag=0;
tim_mux_val(capNo,&TIM_i,&chl); //解析tim模块号和通道号
if(TIM_i==15)
{
if(chl==1)
{
if((TIM15->SR&=TIM_SR_CC1IF)==TIM_SR_CC1IF)
flag=1;
else flag=0;
}
else if(chl==2)
{
if((TIM15->SR&=TIM_SR_CC2IF)==TIM_SR_CC2IF)
flag=1;
else flag=0;
}
}
return flag;
}
//===================================================================
//函数名称:cap_enable_int
//功能概要:使能输入捕捉中断。
//参数说明:capNo:输入捕捉通道号(使用宏定义INCAP_PIN0、INCAP_PIN1、…)
//函数返回:无
//===================================================================
void cap_enable_int(uint16_t capNo)
{
uint8_t TIM_i,chl; //由capNo解析出的tim模块号、通道号临时变量
tim_mux_val(capNo,&TIM_i,&chl); //解析tim模块号和通道号
if(TIM_i==15)
{
if(chl==1)
{
TIM15->DIER|=TIM_DIER_CC1IE;//使能捕获中断
NVIC_EnableIRQ(TIM1_BRK_TIM15_IRQn); //开中断控制器IRQ中断
}
else if(chl==2)
{
TIM15->DIER|=TIM_DIER_CC2IE;//使能捕获中断
NVIC_EnableIRQ(TIM1_BRK_TIM15_IRQn); //开中断控制器IRQ中断
}
}
}
//===================================================================
//函数名称:outcmp_init
//功能概要:outcmp模块初始化。
//参数说明:outcmpNo:模块号,使用gec.h中宏定义,例如OUTCMP_PIN0
// freq:单位:Khz,取值:375、750、1500、3000、6000、12000、24000、48000(频率决定精度)
// cmpPeriod:单位:ms,范围取决于计数器频率与计数器位数(16位)
// comduty:输出比较电平翻转位置占自动重装载寄存器值的比例:0.0~100.0对应0%~100%
// cmpmode:输出比较模式(翻转电平、强制低电平、强制高电平),有宏定义常数使用
//函数返回:无
//===================================================================
void outcmp_init(uint16_t outcmpNo,uint32_t freq,uint32_t cmpPeriod,float cmpduty,uint8_t cmpmode)
{
// GPIO_TypeDef * port_ptr; //声明port_ptr为GPIO结构体类型指针
// uint8_t port,pin; //解析出的端口、引脚号临时变量
// uint8_t mux_val;
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
uint32_t mod;
uint32_t clk_f;
//防止越界
if(cmpduty>100.0) cmpduty=100.0;
//1. gpio引脚解析
//port = (outcmpNo>>8); //解析出的端口
//pin = outcmpNo; //解析出的引脚号
//2. 根据port,给局部变量port_ptr赋值,获得基地址
//port_ptr = GPIO_ARR[port]; //获得GPIO模块相应口基地址
//3. 获得解析的tim模块号和通道号
outcmp_mux_val(outcmpNo,&TIM_i,&chl);
//4.初始化TIM模块功能
switch(TIM_i)
{
case 0:
{
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN; //1)使能定时器时钟
clk_f=MCU_SYSTEM_CLK /(freq*1000);
TIM2->PSC = clk_f-1; //2)设置时钟频率
TIM2->ARR = (uint32_t)(MCU_SYSTEM_CLK_KHZ/(clk_f)*cmpPeriod-1); //3)设置ARR寄存器
TIM2->CNT = 0; //4)计数器清零
OUTCMP_ARR[TIM_i]->EGR |= TIM_EGR_UG_Msk; //初始化定时器
break;
}
}
//5. 根据pin,指定该引脚功能为TIM的通道功能
switch(outcmpNo)
{
case ((0<<8)|5):
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOAEN_Msk;
GPIOA->MODER &= ~GPIO_MODER_MODE5_Msk;
GPIOA->MODER |= 2<<GPIO_MODER_MODE5_Pos;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT5_Msk;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED5_Msk;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5_Msk;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFSEL5_Msk;
GPIOA->AFR[0] |= 1<<GPIO_AFRL_AFSEL5_Pos;
break;
case ((0<<8)|15):
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOAEN_Msk;
GPIOA->MODER &= ~GPIO_MODER_MODE15_Msk;
GPIOA->MODER |= 2<<GPIO_MODER_MODE5_Pos;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT15_Msk;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED15_Msk;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD15_Msk;
GPIOA->AFR[1] &= ~GPIO_AFRH_AFSEL15_Msk;
GPIOA->AFR[1] |= 1<<GPIO_AFRH_AFSEL15_Pos;
break;
case ((1<<8)|3):
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOBEN_Msk;
GPIOB->MODER &= ~GPIO_MODER_MODE3_Msk;
GPIOB->MODER |= 2<<GPIO_MODER_MODE3_Pos;
GPIOB->OTYPER &= ~GPIO_OTYPER_OT3_Msk;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED3_Msk;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD3_Msk;
GPIOB->AFR[0] &= ~GPIO_AFRL_AFSEL3_Msk;
GPIOB->AFR[0] |= 1<<GPIO_AFRL_AFSEL3_Pos;
break;
case ((1<<8)|10):
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOBEN_Msk;
GPIOB->MODER &= ~GPIO_MODER_MODE10_Msk;
GPIOB->MODER |= 2<<GPIO_MODER_MODE10_Pos;
GPIOB->OTYPER &= ~GPIO_OTYPER_OT10_Msk;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED10_Msk;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD10_Msk;
GPIOB->AFR[1] &= ~GPIO_AFRH_AFSEL10_Msk;
GPIOB->AFR[1] |= 1<<GPIO_AFRH_AFSEL10_Pos;
break;
case ((1<<8)|11):
RCC->AHB2ENR |=RCC_AHB2ENR_GPIOBEN_Msk;
GPIOB->MODER &= ~GPIO_MODER_MODE11_Msk;
GPIOB->MODER |= 2<<GPIO_MODER_MODE11_Pos;
GPIOB->OTYPER &= ~GPIO_OTYPER_OT11_Msk;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED11_Msk;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD11_Msk;
GPIOB->AFR[1] &= ~GPIO_AFRH_AFSEL11_Msk;
GPIOB->AFR[1] |= 1<<GPIO_AFRH_AFSEL11_Pos;
break;
}
//6. 输出比较模式的设定
OUTCMP_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_CC3S; //设置通道3为输出
OUTCMP_ARR[TIM_i]->CCMR2 &=~TIM_CCMR2_OC3PE; //输出预载除能
OUTCMP_ARR[TIM_i]->CCER &=~TIM_CCER_CC3P; //设置有效电平为高
if(cmpmode == CMP_REV) //翻转模式
OUTCMP_ARR[TIM_i]->CCMR2 |= 0x3UL<<TIM_CCMR2_OC3M_Pos;
else if(cmpmode == CMP_LOW) //强制低电平模式
OUTCMP_ARR[TIM_i]->CCMR2 |= 0x2UL<<TIM_CCMR2_OC3M_Pos;
else if(cmpmode == CMP_HIGH) //强制高电平模式
{
OUTCMP_ARR[TIM_i]->CCMR2 |= 0x1UL<<TIM_CCMR2_OC3M_Pos;
}
//7. 输出比较相位的设定
mod=OUTCMP_ARR[TIM_i]->ARR+1; //读ARR寄存器的值
OUTCMP_ARR[TIM_i]->CCR3=(uint32_t)(mod*(cmpduty/100.0));
OUTCMP_ARR[TIM_i]->CR1 |= TIM_CR1_CEN_Msk;//使能计数器
OUTCMP_ARR[TIM_i]->CCER |=TIM_CCER_CC3E; //通道3输出比较使能
}
//===================================================================
//函数名称:outcmp_enable_int
//功能概要:使能tim模块中断。
//参数说明:outcmpNo:模块号,使用gec.h中宏定义,例如OUTCMP_PIN0
//函数返回:无
//===================================================================
void outcmp_enable_int(uint16_t outcmpNo)
{
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
outcmp_mux_val(outcmpNo,&TIM_i,&chl);
OUTCMP_ARR[TIM_i]->DIER |= (0x1UL<<chl);
//开TIM中断
NVIC_EnableIRQ(OUTCMP_IRQ[TIM_i]);
}
//===================================================================
//函数名称:outcmp_disable_int
//功能概要:禁用tim模块中断。
//参数说明:outcmpNo:模块号,使用gec.h中宏定义,例如OUTCMP_PIN0
//函数返回:无
//===================================================================
void outcmp_disable_int(uint16_t outcmpNo)
{
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
outcmp_mux_val(outcmpNo,&TIM_i,&chl);
OUTCMP_ARR[TIM_i]->DIER &= ~(0x1UL<<chl);
//关TIM中断
NVIC_DisableIRQ(OUTCMP_IRQ[TIM_i]);
}
//===================================================================
//函数名称:outcmp_get_int
//功能概要:获取TIM模块中断标志
//参数说明:outcmpNo:模块号,使用gec.h中宏定义,例如OUTCMP_PIN0
//函数返回:中断标志 1=有中断产生;0=无中断产生
//===================================================================
uint8_t outcmp_get_int(uint16_t outcmpNo)
{
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
outcmp_mux_val(outcmpNo,&TIM_i,&chl);
//获取TIM_i模块中断标志位
if((OUTCMP_ARR[TIM_i]->SR & TIM_SR_UIF)==TIM_SR_UIF)
return 1;
else
return 0;
}
//===================================================================
//函数名称:outcmp_chl_get_int
//功能概要:获取TIM通道中断标志
//参数说明:outcmpNo:模块号,使用gec.h中宏定义,例如OUTCMP_PIN0
//函数返回:中断标志 1=有中断产生;0=无中断产生
//===================================================================
uint8_t outcmp_chl_get_int(uint16_t outcmpNo)
{
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
outcmp_mux_val(outcmpNo,&TIM_i,&chl);
//获取TIM_i模块chl通道中断标志位
if((OUTCMP_ARR[TIM_i]->SR&(0x1UL<<chl))==(0x1UL<<chl))
return 1;
else
return 0;
}
//===================================================================
//函数名称:outcmp_clear_int
//功能概要:清除TIM中断标志
//参数说明:outcmpNo:模块号,使用gec.h中宏定义,例如OUTCMP_PIN0
//函数返回:清除中断标志位
//===================================================================
void outcmp_clear_int(uint16_t outcmpNo)
{
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
outcmp_mux_val(outcmpNo,&TIM_i,&chl);
//清除TIM_i模块中断标志位
BCLR(0,OUTCMP_ARR[TIM_i]->SR);
}
//===================================================================
//函数名称:outcmp_clear_chl_int
//功能概要:清除TIM通道中断标志
//参数说明:outcmpNo:模块号,使用gec.h中宏定义,例如OUTCMP_PIN0
//函数返回:清除TIM通道中断标志位
//===================================================================
void outcmp_clear_chl_int(uint16_t outcmpNo)
{
uint8_t TIM_i,chl; //由timx_Chy解析出的tim模块号、通道号临时变量
outcmp_mux_val(outcmpNo,&TIM_i,&chl);
//清除TIM_i模块chl通道中断标志位
BCLR(chl,OUTCMP_ARR[TIM_i]->SR);
}
//=====================================================================
//函数名称:outcmp_mux_val
//功能概要:将传进参数timx_Chy进行解析,得出具体模块号与通道号
//参数说明:timx_Chy:模块通道号
//
//函数返回:无
//=====================================================================
void outcmp_mux_val(uint16_t timx_Chy,uint8_t* TIM_i,uint8_t* chl)
{
//1.解析模块号和通道号
switch(timx_Chy)
{
case OUTCMP_PIN0:*TIM_i =0;*chl=3;break;
case OUTCMP_PIN1:*TIM_i =0;*chl=4;break;
case OUTCMP_PIN2:*TIM_i =0;*chl=2;break;
case OUTCMP_PIN3:*TIM_i =0;*chl=1;break;
case OUTCMP_PIN4:*TIM_i =0;*chl=1;break;
}
}
分类:
嵌入式
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了