前言:
做项目时需要测量六路PWM的占空比,在网上看到有用定时器输入捕获的做法,在这里我用多路外部中断EXTI和1个定时器的方式来完成,可同时检测多个pwm信号的周期、占空比、频率,实现原理比较简单
本人使用的单片机芯片型号是AT32F403
有纰漏请指出,转载请说明。
学习交流请发邮件 1280253714@qq.com
代码:
pwmIc.h
#ifndef __PWM_IC_H
#define __PWM_IC_H
#include "includes.h"
#define PWM_EXTI_INT_BITS 0x0000000C //把当前的中断使能位先暂存在pwmExtiIntBits里
#define PWM1_GPIO_PIN GPIO_Pins_2
#define PWM1_GPIO_PORT GPIOA
#define PWM1_GPIO_CLK RCC_APB2PERIPH_GPIOA
#define PWM2_GPIO_PIN GPIO_Pins_3
#define PWM2_GPIO_PORT GPIOA
#define PWM2_GPIO_CLK RCC_APB2PERIPH_GPIOA
#define PWM_HIGH 1
#define PWM_LOW 0
typedef struct pwm_s{
bool state;
u8 pwmNum;
u16 ovHighTimes; // pwm高电平的定时器溢出次数
u16 ovLowTimes;
u16 riseCnt; // 上升沿时读取到的定时器的CNT寄存器值
u16 fallCnt;
u32 highTime; // 高电平的总时长,单位:us
u32 lowTime;
u32 period; // 周期,单位:us
float dutyCycle; // 25%占空比时对应0.25
float freq; // 频率,周期为1000us,对应频率为1000Hz
} PWM_S;
extern PWM_S pwm1;
void PWM_IC_Init(void);
#endif
pwmIc.c
#include "includes.h"
PWM_S pwm1;
PWM_S pwm2;
static void pwmIcInit(void)
{
pwm1.state = PWM_LOW;
pwm2.state = PWM_LOW;
}
void PWM_IC_Init(void)
{
pwmIcInit();
GPIO_InitType GPIO_InitStruct;
EXTI_InitType EXTI_InitStruct;
NVIC_InitType NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStruct.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
RCC_APB2PeriphClockCmd(PWM1_GPIO_CLK, ENABLE);
GPIO_InitStruct.GPIO_Pins = PWM1_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(PWM1_GPIO_PORT, &GPIO_InitStruct);
RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_AFIO, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinsSource2);
EXTI_InitStruct.EXTI_Line = EXTI_Line2;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineEnable = ENABLE;
EXTI_Init(&EXTI_InitStruct);
//
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
RCC_APB2PeriphClockCmd(PWM2_GPIO_CLK, ENABLE);
GPIO_InitStruct.GPIO_Pins = PWM2_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(PWM2_GPIO_PORT, &GPIO_InitStruct);
RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_AFIO, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinsSource3);
EXTI_InitStruct.EXTI_Line = EXTI_Line3;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineEnable = ENABLE;
EXTI_Init(&EXTI_InitStruct);
}
static void pwmExtiProc(PWM_S *pwmx, u8 Line)
{
if ( PWM_HIGH == pwmx->state ){ //刚进入高电平
if ( pwmx->fallCnt > pwmx->riseCnt ){
pwmx->highTime = pwmx->ovHighTimes * 1000 + (pwmx->fallCnt - pwmx->riseCnt);
} else {
pwmx->highTime = pwmx->ovHighTimes * 1000 - (pwmx->riseCnt - pwmx->fallCnt);
}
pwmx->riseCnt = TMR5->CNT;
if ( pwmx->riseCnt > pwmx->fallCnt ){
pwmx->lowTime = pwmx->ovLowTimes * 1000 + (pwmx->riseCnt - pwmx->fallCnt);
} else {
pwmx->lowTime = pwmx->ovLowTimes * 1000 - (pwmx->fallCnt - pwmx->riseCnt);
}
pwmx->period = pwmx->highTime + pwmx->lowTime;
pwmx->dutyCycle = ((float)pwmx->highTime)/((float)pwmx->period);
pwmx->freq = (1 / (float)pwmx->period) * 1000000;
EXTI->RTRSEL &= ~bit(Line); //下降沿触发
EXTI->FTRSEL |= bit(Line);
pwmx->ovHighTimes = 0;
pwmx->ovLowTimes = 0;
pwmx->state = PWM_LOW;
} else {
EXTI->FTRSEL &= ~bit(Line); //上升沿触发
EXTI->RTRSEL |= bit(Line);
pwmx->state = PWM_HIGH;
pwmx->fallCnt = TMR5->CNT;
}
}
void EXTI2_IRQHandler (void)
{
EXTI->INTEN &= ~PWM_EXTI_INT_BITS; //失能所有pwm信号对应的中断
pwmExtiProc(&pwm1,2);
EXTI->PND = EXTI_Line2; //清除EXTI_Line2中断标志位
EXTI->INTEN |= PWM_EXTI_INT_BITS; //使能所有pwm信号对应的中断
}
void EXTI3_IRQHandler (void)
{
EXTI->INTEN &= ~PWM_EXTI_INT_BITS; //失能所有pwm信号对应的中断
pwmExtiProc(&pwm2,3);
EXTI->PND = EXTI_Line3;
EXTI->INTEN |= PWM_EXTI_INT_BITS; //使能所有pwm信号对应的中断
}
static void pwmTmrProc(PWM_S *pwmx)
{
if ( PWM_HIGH == pwmx->state ){
pwmx->ovLowTimes++; // 实际上此时是高电平状态
} else {
pwmx->ovHighTimes++;
}
}
//定时器设置为1ms进一次中断
void TMR5_GLOBAL_IRQHandler()
{
if ( TMR_GetINTStatus( TMR5, TMR_INT_Overflow) != RESET )
{
pwmTmrProc(&pwm1);
pwmTmrProc(&pwm2);
TMR_ClearITPendingBit(TMR5 , TMR_INT_Overflow);
}
}
main函数只需添加初始化PWM_IC_Init();
实验结果:
37.5%和50%的占空比