综合需求---模拟毒气检测通风系统
自动检测部分功能:
①光敏电阻每100ms检测光照强度调节LED的亮度(光照强度越强,灯的亮度越亮)
②毒气检测每100ms 检测毒气浓度,超过20%浓度,通风(电机)启动同时关闭阀门(舵机转180度),浓度越大电机越快
③每100ms通过串口显示芯片内部温度值
④在毒气泄漏情况,30cm内不能有人靠近,如果低于30cm,蜂鸣器报警
操作部分功能:
KEY1 ---RGB彩灯 红色
KEY2 ---RGB彩灯 绿色
KEY3 ---RGB彩灯 蓝色
KEY4 ---RGB彩灯 随机变换颜色
PC端口发送字符串open,阀门打开
PC端口发送字符串close,阀门关闭
1、在定时中断中检测并且控制方式
自动检测部分
/*
函数名:TIM7_IRQHandler
函数功能:基本定时器中断服务函数
返回值:void
形参:void
函数说明:
*/
u16 Tim7_cnt[10];
u8 led3_flag = 1;
u8 RGB_flag = 1;
ADC_t data;
void TIM7_IRQHandler(void)
{
//清除中断标志位
TIM7->SR &= ~(1<<0);
//紧急事件
//Tim7_cnt[1]++;
Tim7_cnt[2]++;
Tim7_cnt[3]++;
//Tim7_cnt[4]++;
//Tim7_cnt[5]++;
Tim7_cnt[6]++;
//第二件事:RGB彩灯
if(Tim7_cnt[2] >= 300)
{
if(RGB_flag == 1)
{
RGB_R += 25;
if(RGB_R >= 1000)
{
RGB_R = 0;
}
RGB_G += 99;
if(RGB_G >= 1000)
{
RGB_G = 0;
}
RGB_B += 155;
if(RGB_B >= 1000)
{
RGB_B = 0;
}
}
Tim7_cnt[2] = 0;
}
//第三件事:超声波测距
if(Tim7_cnt[3] >= 100)
{
HC_RS04_Start();//开始测距信号
Tim7_cnt[3] = 0;
}
//第六件事:
if(Tim7_cnt[6] >= 100)
{
data = get_3sensor_data();
//光敏检测--调节LED的亮度
data.adc_light = 100 - data.adc_light / 4096.0 * 100;
TIM3->CCR1 = data.adc_light * 10;
//毒气检测
data.adc_gas = data.adc_gas / 4096.0 * 100;
if(data.adc_gas >= 20 )
{
STEER = STEER_close;//舵机关闭
MOTOR = 10 * data.adc_gas;//电机开启
}
else
{
MOTOR = MOTOR_close;//电机关闭
}
//芯片内部温度
printf("芯片内部温度:%.1f℃\r\n",data.adc_temp);
Tim7_cnt[6] = 0;
}
}
超声波测距中断服务函数:
/*
函数名:TIM4_IRQHandler
函数功能:通用定时器中断服务函数
返回值:void
形参:void
函数说明:
*/
void TIM4_IRQHandler(void)
{
static u8 TIM4_cnt = 0;
static u16 temp1;
u16 temp2;
u32 temp;
float HC_RS04;
//判断是否为更新中断
if(TIM4->SR & (1 << 0))
{
//清除更新中断标志位
TIM4->SR &= ~(1 << 0);
//记录溢出次数
TIM4_cnt ++;
}
//判断是否为捕获中断
if(TIM4->SR & (1 << 1))
{
//清除捕获中断标志位
TIM4->SR &= ~(1 << 1);
//判断是否为上升沿
if(!(TIM4->CCER & (1 << 1)))
{
//有效次数
TIM4_cnt = 0;
//记录捕获寄存器的值到变量
temp1 = TIM4->CCR1 ;
//切换为下降沿
TIM4->CCER |= (1 << 1);
}
//判断是否为下降沿
else if((TIM4->CCER & (1 << 1)))
{
//记录捕获寄存器的值到变量
temp2 = TIM4->CCR1 ;
//计算脉宽
temp = TIM4_cnt * 65535 - temp1 + temp2;
HC_RS04 = (temp / 1000.0) * 34 / 2;
printf("检测的距离:%.2fcm\r\n",HC_RS04);
if(HC_RS04 < 30 && data.adc_gas >= 20)
{
Beep_ON;
}
else
{
Beep_OFF;
}
//切换为上升沿
TIM4->CCER &= ~(1 << 1);
}
}
}
上位机发送字符控制舵机:
/*
函数名: USART1_IRQHandler
函数功能:中断服务函数
返回值:void
形参:void
函数说明:
*/
USART_REC usart1_val;//接收数据结构体变量
void USART1_IRQHandler(void)
{
//是否产生接收中断
if(USART1->SR & (1 << 5))
{
//清除接收中断的标志位
USART1->SR &= ~(1 << 5);
//紧急事件 以一字节的方式接受,接收完后就到下一次中断
usart1_val.usart_buff[usart1_val.len] = USART1->DR;
usart1_val.len++;
}
//是否产生空闲中断
if(USART1->SR & (1 << 4))
{
//清除空闲标志位
USART1->SR;
USART1->DR;
//紧急事件
usart1_val.usart_buff[usart1_val.len] = '\0';
usart1_val.len = 0;
if(strcmp((const char *)usart1_val.usart_buff,"open") == 0)
{
STEER = STEER_open;
}
else if(strcmp((const char *)usart1_val.usart_buff,"close") == 0)
{
STEER = STEER_close;
}
}
}
操作部分
int main(void)
{
u8 keynum;
NVIC_SetPriorityGrouping(5); //设置优先级分组
Usart1_init(115200);//串口初始化
Key_init();
/***开机提醒***/
//Beep_init();
timer3_ch1_pwm_LED3();//呼吸灯初始化
timer5_ch234_pwm_RGB();//彩灯初始化
timer13_ch1_pwm_motor();//电机初始化
timer14_ch1_pwm_steering_engine();//舵机初始化
HC_RS04();//超声波初始化
gas_light_temp_sensor_init();//毒气&光敏&温度传感器初始化
timer7_Interrupt_ms_init(1);//定时中断初始化
while(1)
{
keynum = Key_scan();
switch(keynum)
{
case 1: RGB_flag = 0;RGB_R = 255;RGB_G = 0; RGB_B = 0; break;
case 2: RGB_flag = 0;RGB_R = 0; RGB_G = 255;RGB_B = 0; break;
case 3: RGB_flag = 0;RGB_R = 0; RGB_G = 0; RGB_B = 255;break;
case 4: RGB_flag = 1;break;
}
}
}
2、状态机扫描的方式
状态机扫描的时间还是基于定时中断的,通过时间+标志位控制程序的运行
int main(void)
{
u8 keynum;
ADC_t data;
NVIC_SetPriorityGrouping(5); //设置优先级分组
Usart1_init(115200);//串口初始化
LED_init();
Key_init();
/***开机提醒***/
Beep_init();
timer3_ch1_pwm_LED3();//呼吸灯初始化
timer5_ch234_pwm_RGB();//彩灯初始化
timer13_ch1_pwm_motor();//电机初始化
timer14_ch1_pwm_steering_engine();//舵机初始化
HC_RS04();//超声波初始化
gas_light_temp_sensor_init();//毒气&光敏&温度传感器初始化
timer7_Interrupt_ms_init(1);
while(1)
{
keynum = Key_scan();
switch(keynum)
{
case 1: RGB_flag = 0;RGB_R = 255;RGB_G = 0; RGB_B = 0; break;
case 2: RGB_flag = 0;RGB_R = 0; RGB_G = 255;RGB_B = 0; break;
case 3: RGB_flag = 0;RGB_R = 0; RGB_G = 0; RGB_B = 255;break;
case 4: RGB_flag = 1;break;
}
/*RGB彩灯*/
if(RGB_flag == 1 && Tim7_cnt[2] >= 100)
{
Tim7_cnt[2] = 0;
if(RGB_flag == 1)
{
RGB_R += 25;
if(RGB_R >= 1000)
{
RGB_R = 0;
}
RGB_G += 99;
if(RGB_G >= 1000)
{
RGB_G = 0;
}
RGB_B += 155;
if(RGB_B >= 1000)
{
RGB_B = 0;
}
}
}
/*ADC检测*/
if(Tim7_cnt[6] >= 100)
{
Tim7_cnt[6] = 0;
data = get_3sensor_data();
//光敏检测--调节LED的亮度
data.adc_light = 100 - data.adc_light / 4096.0 * 100;
TIM3->CCR1 = data.adc_light * 10;
printf("光照强度:%d%%\r\n",data.adc_light);
//毒气检测
data.adc_gas = data.adc_gas / 4096.0 * 100;
if(data.adc_gas >= 20 )
{
STEER = STEER_close;//舵机关闭
MOTOR = 10 * data.adc_gas;//电机开启
}
else
{
MOTOR = MOTOR_close;//电机关闭
}
printf("毒气浓度:%d%%\r\n",data.adc_gas);
//芯片内部温度
printf("芯片内部温度:%.1f℃\r\n",data.adc_temp);
HC_RS04_Start();//发送超声波开始测距信号
}
/*超声波测距*/
if(rs04_flag == 1)
{
rs04_flag = 0;
hc_RS04 = (temp / 1000.0) * 34 / 2;
printf("检测的距离:%.2fcm\r\n",hc_RS04);
if(hc_RS04 < 30 && data.adc_gas >= 20)//****
{
Beep_ON;
}
else
{
Beep_OFF;
}
}
/*上位机发送字符控制舵机*/
if(usart1_val.usart_idle_flag == 1)
{
usart1_val.usart_idle_flag = 0;
if(strcmp((const char *)usart1_val.usart_buff,"open") == 0)
{
STEER = STEER_open;
}
else if(strcmp((const char *)usart1_val.usart_buff,"close") == 0)
{
STEER = STEER_close;
}
}
}
}
/*
函数名:TIM7_IRQHandler
函数功能:基本定时器中断服务函数
返回值:void
形参:void
函数说明:
*/
u16 Tim7_cnt[10];
u8 RGB_flag = 0;
void TIM7_IRQHandler(void)
{
//清除中断标志位
TIM7->SR &= ~(1<<0);
//紧急事件
//Tim7_cnt[1]++;
Tim7_cnt[2]++;
//Tim7_cnt[3]++;
//Tim7_cnt[4]++;
//Tim7_cnt[5]++;
Tim7_cnt[6]++;
}
注意:将所用到的标志位声明为extern全局变量
定时中断扫描与状态机扫描的区别
- 状态机扫描的代码整体上逻辑好理解,易于修改;而中断扫描逻辑性较强,不易于修改
- 状态机扫描的实时性不够中断扫描好,但是整体上差别不明显
- 状态机扫描的时间还是基于定时中断的
总结:
毒气检测通风系统的架构是什么样的?--->程序结构
毒气检测通风系统功能分为自动检测部分和操作部分,其中,自动检测部分包括每100ms获取光照强度、芯片内部温度,每100ms获取毒气浓度并且控制舵机、电机和蜂鸣器;操作部分则包括RGB灯的控制和上位机发送字符串控制舵机。
一开始我是利用在定时中断中检测并且控制的方式实现毒气检测通风系统,我的程序架构是这样的,首先对所用到的设备模块底层驱动初始化(如ADC转换初始化、超声波测距初始化、电机、舵机、RGB的初始化等);在中断中获取数据控制部分,先利用定时器的定时中断完成多件事(如ADC转换获取数据、发送超声波开始测距信号、RGB彩灯随机颜色的变换)、然后捕获中断计算靠近毒气泄露的距离和串口中断接收指令控制舵机;在主程序循环部分,通过按键高速扫描来控制RGB彩灯的切换。但是,这种结构如果中断中要做的事很多,那么中断之间会有影响。
所以,我又利用状态机扫描的方式实现毒气检测通风系统,我的程序架构是这样的,首先还是首先对所用到的设备模块底层驱动初始化(如ADC转换初始化、超声波测距初始化、电机、舵机、RGB的初始化等);在中断控制部分,先利用定时中断获取时间片、然后在捕获中断中获取脉宽、再在串口中断中获取接收到的字符串;在主程序循环部分,通过状态机扫描的方式,高速扫描各功能状态机标志位是否满足,满足就计算数据并控制,包括时间片标志位,脉宽计算完成标志位,串口字符串接收完成标志位等。