Coder
Published on 2025-04-09 / 53 Visits
0
0

单片机IO(ADC)采集PWM输入和0到10V信号-STC8H/G

一、由来

因单片机(STC8G1K08)选型问题,项目需要4路pwm输入检测并兼容0-10V采集,也就是说单片机相关的引脚,既要支持ADC也要支持PWM捕捉,很不幸,该单片机不支持,大写的尴尬!!!!

同时该单片机的外部中断引脚也是固定引脚,且只有中断0和1支持上升沿+下降沿,因此外部中断不能用!!!

double kill!!!

二、解决

首先引脚支持ADC,所以0-10V检测不存在问题

PWM输入只能采用轮询方案来实现

1. 主要原理

  1. 检测引脚的高低电平

  2. 如果是高电平,则等待低电平到了

  3. 低电平到了后,启动定时器进行计数,再等待高电平到来

  4. 高电平到了后,记录定时器时间(即PWM低电平时间),继续等待低电平到了

  5. 低电平到了后,记录定时器时间(即PWM周期时间),步骤4和步骤5即可计算出占空比和频率

  6. 每步里面可以增加超时时间,一旦超时,则认为不存在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%之间,基本满足要求。


Comment