本次试验使用STM32的输入捕获功能,测量PWM的频率。需要初始化的模块有PWM输出和输入捕获。
PWM模块初始化设置定时器3通道1,对应引脚为PC6,设置计数频率为(72M/72),自动重装载值为500。
即产生2Khz的PWM波
PWM波初始化
TIM3_CH1_PWM_Init(500-1,72-1); //2k,PC6;
TIM_SetCompare1(TIM3,250); //在main中初始化比较器值;
void TIM3_CH1_PWM_Init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOC,&GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);//改变指定管脚的映射
TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC1Init(TIM3,&TIM_OCInitStructure); //输出比较通道1初始化
TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能TIMx在 CCR2 上的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);//使能预装载寄存器
TIM_Cmd(TIM3,ENABLE); //使能定时器
}
初始化输入捕获功能
输入捕获使用定时器5通道1(PA0),设置计数频率为1M,溢出值为最大(0XFFFF)。
输入捕获的原理是,当定时器捕获到一个上跳沿时,产生捕获中断,标志位TIM_IT_CC1置位,此时记录下计数器中的值(cap_value1),当再次进入中断时,再次记录计数器中的值(cap_value2)。即得到相邻的两个上跳沿之间的计数值,为cap_value2-cap_value1;
根据计数频率1M可以计算当前捕获的PWM波时间。
TIM5_CH1_Input_Init(0xffff-1,71); //输入捕获初始化
void TIM5_CH1_Input_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//使能TIM5时钟
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//管脚设置
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD; //设置下拉输入模式
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
TIM_TimeBaseInitStructure.TIM_Period=arr; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_1; //通道1
TIM_ICInitStructure.TIM_ICFilter=0x00; //滤波
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//捕获极性
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1; //分频系数
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//直接映射到TI1
TIM_ICInit(TIM5,&TIM_ICInitStructure);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;//中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM5,ENABLE); //使能定时器
}
输入捕获中断处理程序分析:
此处设置一个结构体,保存捕获到的信息;Cap_Value1和Cap_Value2较容易理解,就是两次捕获的计数器值;
Get_high的意思是一个标志位,=1表示捕获到一次高电平,=0表示没有捕获到一次高电平
Get_once=1表示成功捕获一次(即捕获到两次上跳沿)
typedef struct
{
u8 Get_high; //捕获到高电平; (标志位)
u8 Get_once; //成功捕获一次;即捕获两次高电平;(标志位)
u16 Cap_Value1; //捕获到第一次高电平时的CNT计数值;
u16 Cap_Value2; //捕获到第二次高电平时的CNT计数值;
}Capture;
当接收到一次上跳,进入中断,此时还没有捕获成功。记录此时的CNT计数器的值为cap_value2,并刷新状态(cap.Get_high=1;)。第二次碰到上升沿时(此时已经捕获到一次上升沿,即cap.Get_high=1),进入中断,刷新cap.Get_once状态为1,即成功捕获一个周期,记录下此时的CNT计数值作为cap_value2。
void TIM5_IRQHandler(void)
{
if(cap.Get_once==0) //如果还未捕捉成功;
{
if(TIM_GetITStatus(TIM5,TIM_IT_CC1)) //捕获中断;
{
if(cap.Get_high==1) //已经捕获了一次高电平,再次进入中断,说明捕获成功了;
{
cap.Get_once=1; //捕获成功;
cap.Cap_Value2=TIM_GetCapture1(TIM5); //保存此时计数器的值;
}
else //如果还没捕获一次高电平,说明是第一次捕获;
{
cap.Get_high=1;
cap.Cap_Value1=TIM_GetCapture1(TIM5);//记录此时的CNT值;
}
}
}
TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_Update);
}
主程序处理
主程序中判断cap.Get_once状态,是否成功捕获到一个PWM周期,
比较Cap_value1和Cap_value2的值,如果Cap_value2比Cap_value1大,表示在一个溢出周期内,
如果Cap_value2比Cap_value1小,表示在捕获计数时,产生了一个溢出,
if(cap.Get_once==1) //成功捕获一次;
{
if(cap.Cap_Value2>cap.Cap_Value1) //如果没有发生溢出;
{
indata=cap.Cap_Value2-cap.Cap_Value1; //记录捕获的计数值;
}
else
{
indata=(0xffff-cap.Cap_Value1)+cap.Cap_Value2;
}
//清除状态;
cap.Get_high=0;
cap.Get_once=0;
cap.Cap_Value1=0;
cap.Cap_Value2=0;
printf("高电平持续时间:%d us\r\n",indata); //总的高电平时间
}