目录
项目要求
项目完成情况
项目实现流程
使用STMCubeMx创建项目
工程代码
1.软件定时器;
2.重写fputc;
3.ADC转换按键按下时的值和传感器的值;
4.判断火势等级的函数;
5.按键中断回调函数;
遇到的问题与解决方法
项目要求
- 同时检测按键和可燃气体(火焰)传感器,按键按下的时候 串口打印“楼层 + 火势情况” ? (按键中断实现),(楼层用五向按键判断 上下左右中分别代表1-5层)?? ?
- ADC每500ms开启一次(使用软件定时器)采集火焰值/有害气体值,显示屏实时显示采集到的数值(我自己又显示了楼层和火势等级信息)
- 火焰强度/有害气体浓度(一级、二级、三级)
- ? ? ? ? 一级:亮绿灯 ?(正常状态 没火/无有害气体)
- ????????二级:亮黄灯 ?(有小火/有害气体浓度较低)
- ????????三级:蓝灯闪烁(有大火/有害气体浓度高)600ms间隔闪烁(要求使用软件定时器)
项目完成情况
????????使用的光敏传感器代替火焰传感器,效果类似;
????????实现功能:
- ADC转换:转换按键按下时的值和传感器值,根据按键不同方向不同值的特性,划分范围区分按键按下的方向,
- 按键中断:按键按下时开始在屏幕显示按键方向对应楼层并实时显示传感器值和火势等级,还可以通过串口在电脑打印楼层信息和传感器值;
- 串口打印:通过按键中断实现按键按下时串口输出楼层和传感器值;
- GPIO:实现led灯闪烁,火势三级时蓝灯闪烁,火势小于三级时led灯停止闪烁并点亮对应等级的led灯。
项目实现流程
使用STMCubeMx创建项目
1.配置引脚;
2.由于led灯一端接+3.3V电压,所以led接芯片的引脚初始化为高电平,保证led一开始是灭的;
3.使能EXTI8;
4.外部时钟选择陶瓷谐振器;
5.时基源选择滴答定时器;
6.ADC使能通道IN0和通道IN4;
7.SP1选择全双工主机
每一个像素点为16位,由两个8位拼接起来;
时钟频率为48MHz,所以分频为4分频;
8.串口通信选择异步通信;
9.时钟树设置,时钟频率设置为48MHz;
10.生成工程文件
工程代码
1.软件定时器;
????????自己写一个延时函数(.c文件.h文件),实现延时函数HAL_Delay( )的功能;
#ifndef __DELAYTIME_H__
#define __DELAYTIME_H__
#include "stm32f0xx_hal.h"
typedef struct
{
uint32_t start;
uint32_t delay;
}MY_TIM;
void setTime(MY_TIM *tim,uint32_t delayms);
uint32_t compareTime(MY_TIM *tim);
#endif
#include "delaytime.h"
//软件定时器配置函数
void setTime(MY_TIM *tim,uint32_t delayms)
{
tim->start = HAL_GetTick(); //获取当前时间
tim->delay = delayms; //获取用户设置的延时时间,单位ms
}
//判断是否到达延时条件
uint32_t compareTime(MY_TIM *tim)
{
//用当前时间减去用户设置延时时间时的时间
if(HAL_GetTick() - tim->start < tim->delay)
{
return 0; //未达到延时条件
}else
{
return 1; //已到达延时时间
}
}
2.重写fputc;
????????重写fputc函数后,可以直接在main函数中使用printf串口打印;
int fputc(int ch,FILE* f)
{
while(!(USART1->ISR & (1 << 7)));
USART1->TDR = ch;
return ch;
}
3.ADC转换按键按下时的值和传感器的值;
????????采集多个通道的传感器数值时,通过判断ADC转换结束后的EOC标志来实现,当单个通道转换完成后会产生EOC信号,即ADC_ISR寄存器第2位被置1;
while (1)
{
//软件定时器,判断延时是否结束
if(compareTime(&tim1) == 1)
{
HAL_ADC_Start(&hadc); //启动ADC
//HAL_ADC_PollForConversion(&hadc,1000); //只采集单个通道时需要添加该句
while(!(ADC1->ISR &(1<<2))); //等待转换结束
key_value = HAL_ADC_GetValue(&hadc); //获取按键的转换结果
while(!(ADC1->ISR &(1<<2)));
fire_value = HAL_ADC_GetValue(&hadc); //获取转换后光敏(火焰)传感器的值
HAL_ADC_Stop(&hadc); //停止ADC
//使用标志位,保证按键按下后屏幕上显示按键方向的代表楼层和传感器动态数值
if(flag == 1)
{
getFire(fire_value);
}
//重新设置延时时间
setTime(&tim1,500);
}
}
4.判断火势等级的函数;
????????由于使用的传感器是光敏传感器,所以我们可以使用三种情况下的传感器数值来分别代表三个火势等级,即
- 正常光照下的传感器数值的范围;
- 明亮光源照射下的传感器数值范围;
- 遮挡光照后的传感器数值的范围;
????????经实验测得,正常光照下的范围大约为2300~3200;明亮光源照射下的范围大约为0~2000;遮挡光照下的范围大约为3200~5000;
????????在函数中判断当ADC转换后的传感器的值在某一范围时,向lcd屏幕上显示对应得火势等级和传感器数值,并控制对应的led灯的亮灭;
void getFire(uint16_t fire_value)
{
if(fire_value > 0 && fire_value < 2000)
{
Gui_DrawFont_GBK16(10,30,WHITE,BLACK,"LEVEL 2:"); //向lcd屏幕上打印火势等级
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET); //点亮对应led灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0|GPIO_PIN_1,GPIO_PIN_SET); //熄灭对应led灯
}else if(fire_value > 2300 && fire_value < 3200)
{
Gui_DrawFont_GBK16(10,30,WHITE,BLACK,"LEVEL 1:");
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1|GPIO_PIN_2,GPIO_PIN_SET);
}else if(fire_value > 3200 && fire_value < 5000)
{
Gui_DrawFont_GBK16(10,30,WHITE,BLACK,"LEVEL 3:");
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0|GPIO_PIN_2,GPIO_PIN_SET);
while(1)
{
//软件定时器
if(compareTime(&tim2) == 1)
{
//在循环中使用翻转电平的库函数,实现led灯的闪烁
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
memset(fire_array,0,32);
//将传感器数值转换成字符串,方便使用lcd的字符串显示函数
sprintf(fire_array,"%d",fire_value);
Gui_DrawFont_GBK16(74,30,WHITE,BLACK,fire_array);
setTime(&tim2,600);
}
//为了能够在循环中读取传感器新测得的数值,重新进行ADC的开启和关闭过程
HAL_ADC_Start(&hadc);
//HAL_ADC_PollForConversion(&hadc,1000);
while(!(ADC1->ISR &(1<<2))); //等待转换
key_value = HAL_ADC_GetValue(&hadc);
while(!(ADC1->ISR &(1<<2)));
fire_value = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
//当判断出光敏传感器的值不处于该范围时,跳出循环
if(fire_value < 3200)
break;
}
}
memset(fire_array,0,32);
sprintf(fire_array,"%d",fire_value);
Gui_DrawFont_GBK16(74,30,WHITE,BLACK,fire_array);
}
5.按键中断回调函数;
????????该回调函数在main.c文件中重写定义即可,不需要手动调用和声明,当按键按下时自动调用;
????????同样先进行实验来判断按键向不同方向按下时的ADC转换值,
- 向下按时,转换值范围大约为2500~2600;
- 向前按时,转换值范围大约为2000~2200;
- 向后按时,转换值范围大约为400~500;
- 向左按时,转换值范围大约为1400~1600;
- 向右按时,转换值范围大约为2900~3000;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//判断对应按键是否按下
if(GPIO_Pin == GPIO_PIN_8)
{
//按键按下时使标志位等于1,使光敏传感器的值能够输出到屏幕上;
flag = 1;
HAL_ADC_Start(&hadc);
//HAL_ADC_PollForConversion(&hadc,1000);
while(!(ADC1->ISR &(1<<2))); //等待转换
key_value = HAL_ADC_GetValue(&hadc);
while(!(ADC1->ISR &(1<<2)));
fire_value = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
if(key_value > 2500 && key_value < 2600)
{
Gui_DrawFont_GBK16(10,10,WHITE,BLACK,"1 floor");
printf("1 floor/fire_data:%d",fire_value);
}else if(key_value > 2000 && key_value < 2200)
{
Gui_DrawFont_GBK16(10,10,WHITE,BLACK,"2 floor");
printf("2 floor/fire_data:%d",fire_value);
}else if(key_value > 400 && key_value < 500)
{
Gui_DrawFont_GBK16(10,10,WHITE,BLACK,"3 floor");
printf("3 floor/fire_data:%d",fire_value);
}else if(key_value > 1400 && key_value < 1600)
{
Gui_DrawFont_GBK16(10,10,WHITE,BLACK,"4 floor");
printf("4 floor/fire_data:%d",fire_value);
}else if(key_value > 2900 && key_value < 3000)
{
Gui_DrawFont_GBK16(10,10,WHITE,BLACK,"5 floor");
printf("5 floor/fire_data:%d",fire_value);
}
}
}
遇到的问题与解决方法
问题1:刚开始做这个项目的时候,我只知道传感器的值可以根据不同情况来区分不同等级,但是不清楚如何判断按键按下时的方向。
解决方法:后来经过查阅资料,无意间发现五向键按下不同方向时,产生的电压不同,所以我联想到,按键按下不同方向时ADC转换的值应该也不一样,经过实验后,发现确实如此。
问题2:火势等级为三级时,要求为蓝灯闪烁,但是蓝灯闪烁后,显示屏的传感器数值不再更新,而且蓝灯有时闪烁,有时不闪烁。
解决方法:通过在蓝灯闪烁的while循环代码中,添加ADC转换传感器数值的程序代码,并且判断转换的值是否在该火势等级的范围内,不在则break跳出循环。
问题3:lcd屏幕的字符串显示函数只能传字符串,不能传数值。
解决方法:使用sprintf函数将转换的传感器数值转换成字符串;
|