一、由来
因单片机(STC8G1K08)选型问题,项目需要4路pwm输入检测并兼容0-10V采集,也就是说单片机相关的引脚,既要支持ADC也要支持PWM捕捉,很不幸,该单片机不支持,大写的尴尬!!!!
同时该单片机的外部中断引脚也是固定引脚,且只有中断0和1支持上升沿+下降沿,因此外部中断不能用!!!
double kill!!!
二、解决
首先引脚支持ADC,所以0-10V检测不存在问题
PWM输入只能采用轮询方案来实现
1. 主要原理
检测引脚的高低电平
如果是高电平,则等待低电平到了
低电平到了后,启动定时器进行计数,再等待高电平到来
高电平到了后,记录定时器时间(即PWM低电平时间),继续等待低电平到了
低电平到了后,记录定时器时间(即PWM周期时间),步骤4和步骤5即可计算出占空比和频率
每步里面可以增加超时时间,一旦超时,则认为不存在pwm信号,进入0-10v检测
2. 代码实现
2.1 定义相关变量
typedef struct{
u16 u16CCR; // 中断使用的中间变量, 用户层不可见
u16 u16Period; // 周期
u16 u16PulseHigh; // 高电平时间
u16 u16PulseLow; // 高电平时间
u16 u16Duty; // 占空比 = PulseHigh /period * 100%
u8 u8Success; // 波形已捕捉完成
}ST_PWM_CAP_ITEM;
2.2 定时器初始化
void Timer_config2(void)
{
TIM_InitTypeDef TIM_InitStructure; //结构定义
TIM_InitStructure.TIM_Mode = TIM_16Bit; //指定工作模式, TIM_16BitAutoReload,TIM_16Bit,TIM_8BitAutoReload,TIM_16BitAutoReloadNoMask
TIM_InitStructure.TIM_ClkSource = TIM_CLOCK_1T; //指定时钟源, TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_Ext
TIM_InitStructure.TIM_ClkOut = DISABLE; //是否输出高速脉冲, ENABLE或DISABLE
TIM_InitStructure.TIM_Value = 0; //初值,
TIM_InitStructure.TIM_Run = DISABLE; //是否初始化后启动定时器, ENABLE或DISABLE
Timer_Inilize(Timer2,&TIM_InitStructure); //初始化Timer0 Timer0,Timer1,Timer2,Timer3,Timer4
NVIC_Timer2_Init(ENABLE,Priority_0); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
}
3.3 轮询
// PWM1检测
g_u16TimeDelay = TIMEOUT_PWM_IN;
if(SYS_PWM1_IN == 1){
// 当前高电平,等待低电平的到来
while(SYS_PWM1_IN == 1){
if(g_u16TimeDelay == 0){
// 超时,非PWM信号
return 0xFFFF;
}
}
// 低电平到了
g_u16TimeDelay = TIMEOUT_PWM_IN;
T2_Load(0);
Timer2_Run(ENABLE);
stPwmCap.astItem[index].u16Period = 0;
stPwmCap.astItem[index].u16PulseLow = 0;
// 当前低电平,等待高电平的到来
while(SYS_PWM1_IN == 0){
if(g_u16TimeDelay == 0){
// 超时,非PWM信号
return 0xFFFF;
}
}
// 高电平到了
g_u16TimeDelay = TIMEOUT_PWM_IN;
stPwmCap.astItem[index].u16PulseLow = (T2H << 8) | T2L;
// 当前高电平,等待低电平的到来
while(SYS_PWM1_IN == 1){
if(g_u16TimeDelay == 0){
// 超时,非PWM信号
return 0xFFFF;
}
}
// 低电平到了,完整一个周期
stPwmCap.astItem[index].u16Period |= (T2H << 8) | T2L;
Timer2_Stop();
stPwmCap.astItem[index].u8Success = 1;
s_u32Temp = stPwmCap.astItem[index].u16PulseLow;
s_u32Temp = s_u32Temp * 1000;
s_u32Temp = s_u32Temp / stPwmCap.astItem[index].u16Period;
stPwmCap.astItem[index].u16Duty = (u16)s_u32Temp;
stPwmCap.astItem[index].u16Duty = 1000 - stPwmCap.astItem[index].u16Duty;
return stPwmCap.astItem[index].u16Duty;
} else {
// 当前低电平,等待高电平的到来
while(SYS_PWM1_IN == 0){
if(g_u16TimeDelay == 0){
// 超时,非PWM信号
return 0xFFFF;
}
}
// 高电平到了
g_u16TimeDelay = TIMEOUT_PWM_IN;
T2_Load(0);
Timer2_Run(ENABLE);
stPwmCap.astItem[index].u16Period = 0;
stPwmCap.astItem[index].u16PulseHigh = 0;
// 当前高电平,等待低电平的到来
while(SYS_PWM1_IN == 1){
if(g_u16TimeDelay == 0){
// 超时,非PWM信号
return 0xFFFF;
}
}
// 低电平到了
g_u16TimeDelay = TIMEOUT_PWM_IN;
stPwmCap.astItem[index].u16PulseHigh = (T2H << 8) | T2L;
// 当前低电平,等待高电平的到来
while(SYS_PWM1_IN == 0){
if(g_u16TimeDelay == 0){
// 超时,非PWM信号
return 0xFFFF;
}
}
// 高电平到了,完整一个周期
stPwmCap.astItem[index].u16Period |= (T2H << 8) | T2L;
Timer2_Stop();
stPwmCap.astItem[index].u8Success = 1;
s_u32Temp = stPwmCap.astItem[index].u16PulseHigh;
s_u32Temp = s_u32Temp * 1000;
s_u32Temp = s_u32Temp / stPwmCap.astItem[index].u16Period;
stPwmCap.astItem[index].u16Duty = (u16)s_u32Temp;
return stPwmCap.astItem[index].u16Duty;
}
三、效果
1khz频率的pwm可以区分,精度1-2%之间,基本满足要求。