今天写智能温室大棚(花房)的第2部分,实现使用四个按键设置温湿度报警参数。
一、方案介绍
经过系统方案设计分析,使用4个按键分别做如下控制:
key1:参数确认,确认当前参数值,切换到下一个设置参数;
key2:模式选择,系统的显示模式和参数设置模式的切换;
key3:参数值减小;
key4:参数值增加。
在开始进行程序编写的时候,选择了最简单的顺序轮训方式,最终在测试过程中发现实时性很差,最终选择了外部中断的方式。
我们现简要介绍一些STM32外部中断的知识:
STM32
的每个
IO
都可以作为外部 中断的中断输入口
STM32F103
的中断控制器支持
19
个外部中
断
/
事件请求。每个中断设有状态位,每个中断
/事件都有独立的触发和屏蔽设置。
STM32F103 的 19
个外部中断为:
线
0~15
:对应外部
IO
口的输入中断。
线
16
:连接到
PVD
输出。
线
17
:连接到
RTC
闹钟事件。
线
18
:连接到
USB
唤醒事件。
STM32
供
IO
口使用的中断线只有
16
个,但是
STM32
的
IO
口却远远不止 16
个,
于是
STM32 就将GPIO
的管教
GPIOx.0~GPIOx.15(x=A,B,C,D,E
,
F,G)
分别对应中断线
15~0
。这样每个中
断线对应了最多
7
个
IO
口,以线
0
为例:它对应了
GPIOA.0
、
GPIOB.0
、
GPIOC.0、
GPIOD.0
、 GPIOE.0、
GPIOF.0
、
GPIOG.0
。而中断线每次只能连接到
1
个
IO
口上,这样就需要通过配置 来决定对应的中断线配置到哪个 GPIO
上了。
GPIO和中断线的映射关系如下图所示:
二、代码编写
本次使用的4个按键对应的GPIO如下:
// KEY1 PA3
// KEY2 PA4
// KEY3 PA5
// KEY4 PA6
1. key.c
初始化key对应的DPIO模式:
//按键初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能PORTA时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //关闭jtag,使能SWD,可以用SWD模式调试
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6; //PA3、PA4、PA5、PA6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA3、PA4、PA5、PA6
}
对应的key.h
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3) //读取按键0
#define KEY2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4) //读取按键1
#define KEY3 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5) //读取按键1
#define KEY4 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6) //读取按键1
#define KEY1_PRES 1 //KEY1
#define KEY2_PRES 2 //KEY2
#define KEY3_PRES 3 //KEY3
#define KEY4_PRES 4 //KEY4
void KEY_Init(void);//IO初始化
#endif
2. exti.c
按键中断初始化:
//外部中断初始化函数
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
KEY_Init();//初始化按键对应io模式
//GPIOA.7 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource7);
EXTI_InitStructure.EXTI_Line=EXTI_Line7;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOA.4 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource4);
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOA.5 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
EXTI_InitStructure.EXTI_Line=EXTI_Line5;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOA.6 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
EXTI_InitStructure.EXTI_Line=EXTI_Line6;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
}
中断处理函数:
void EXTI4_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY2==0)
{
key2_Hander();
OLED_Clear();
display();
}
EXTI_ClearITPendingBit(EXTI_Line4); //清除EXTI4线路挂起位
}
void EXTI9_5_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY3==0) {
key3_Hander();
OLED_Clear();
display();
}
else if(KEY4==0){
key4_Hander();
OLED_Clear();
display();
}
else if(KEY1==0){
key1_Hander();
OLED_Clear();
display();
}
EXTI_ClearITPendingBit(EXTI_Line5|EXTI_Line6|EXTI_Line7); //清除LINE5/6/7上的中断标志位
}
按键处理函数:
void key1_Hander(void)
{
switch(mode)
{
case 1:break; //显示模式
case 2:
{
currentParameter += 1;
if(currentParameter == 7)
{
currentParameter = 1;
}
break; //设置模式
default:break;
}
}
}
void key2_Hander(void)
{
switch(mode)
{
case 1:
{
mode=2;
currentParameter = 1;
}
break;
case 2:
{
mode=1;
}
break;
default:break;
}
}
void key3_Hander(void)
{
switch(mode)
{
case 1:break; //显示模式
case 2:
{
if(currentParameter == 1)
{
temperatureThreshold_max += 1;
}
else if(currentParameter == 2)
{
temperatureThreshold_min += 1;
}
else if(currentParameter == 3)
{
humidityThreshold_max += 5;
}
else if(currentParameter == 4)
{
humidityThreshold_min += 5;
}
else if(currentParameter == 5)
{
lightThreshold_max += 10;
}
else if(currentParameter == 6)
{
lightThreshold_min += 10;
}
}
break; //设置模式
default:break;
}
}
void key4_Hander(void)
{
switch(mode)
{
case 1:break; //显示模式
case 2:
{
if(currentParameter == 1)
{
temperatureThreshold_max -= 1;
}
else if(currentParameter == 2)
{
temperatureThreshold_min -= 1;
}
else if(currentParameter == 3)
{
humidityThreshold_max -= 5;
}
else if(currentParameter == 4)
{
humidityThreshold_min -= 5;
}
else if(currentParameter == 5)
{
lightThreshold_max -= 10;
}
else if(currentParameter == 6)
{
lightThreshold_min -= 10;
}
}
break; //设置模式
default:break;
}
}
显示函数:
void display()
{
switch(mode)
{
case 0: display0(); break; //开机界面(可不加)
case 1: display1(); break; //显示界面
case 2: //设置界面
{
if(currentParameter == 1)
{
display2(); //设置温度高
}
else if(currentParameter == 2)
{
display3(); //设置温度低
}
else if(currentParameter == 3)
{
display4(); //设置湿度高
}
else if(currentParameter == 4)
{
display5(); //设置湿度低
}
else if(currentParameter == 5)
{
display6(); //设置光照高
}
else if(currentParameter == 6)
{
display7(); //设置光照低
}
}
break;
}
}
//显示界面
void display0() // 开机界面
{
OLED_ShowCHinese(28,0,0); //智
OLED_ShowCHinese(46,0,1); //能
OLED_ShowCHinese(64,0,2); //大
OLED_ShowCHinese(82,0,3); //棚
}
void display1() // 显示界面
{
OLED_ShowCHinese(0,0,4); //温
OLED_ShowCHinese(18,0,5); //度
OLED_ShowString(36,0,":",16);
OLED_ShowString(70,0,"C",16); //℃
OLED_ShowCHinese(0,3,6); //湿
OLED_ShowCHinese(18,3,7); //度
OLED_ShowString(36,3,":",16);
OLED_ShowString(70,3,"%",16); //%
OLED_ShowCHinese(0,6,8); //光
OLED_ShowCHinese(18,6,9); //照
OLED_ShowString(36,6,":",16);
OLED_ShowString(70,6,"%",16); //%
//显示2个数字//x,y :起点坐标 //len :数字的位数//size:字体大小//mode:模式 0,填充模式;1,叠加模式//num:数值(0~4294967295);
//void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
OLED_ShowNum(40,0,temp,3,16);
OLED_ShowNum(40,3,humid,3,16);
OLED_ShowNum(40,6,light,3,16);
}
void display2() // 显示温度高设置界面
{
OLED_ShowCHinese(0,0,4); //温
OLED_ShowCHinese(18,0,5); //度
OLED_ShowCHinese(36,0,10); //高
OLED_ShowString(54,0,":",16);
OLED_ShowString(88,0,"C",16); //℃
OLED_ShowNum(58,0,temperatureThreshold_max,3,16);
}
void display3() // 显示温度低设置界面
{
OLED_ShowCHinese(0,0,4); //温
OLED_ShowCHinese(18,0,5); //度
OLED_ShowCHinese(36,0,11); //低
OLED_ShowString(54,0,":",16);
OLED_ShowString(88,0,"C",16); //℃
OLED_ShowNum(58,0,temperatureThreshold_min,3,16);
}
void display4() // 显示湿度高设置界面
{
OLED_ShowCHinese(0,3,6); //湿
OLED_ShowCHinese(18,3,7); //度
OLED_ShowCHinese(36,3,10); //高
OLED_ShowString(54,3,":",16);
OLED_ShowString(88,3,"%",16); //%
OLED_ShowNum(58,3,humidityThreshold_max,3,16);
}
void display5() // 显示湿度低设置界面
{
OLED_ShowCHinese(0,3,6); //湿
OLED_ShowCHinese(18,3,7); //度
OLED_ShowCHinese(36,3,11); //低
OLED_ShowString(54,3,":",16);
OLED_ShowString(88,3,"%",16); //%
OLED_ShowNum(58,3,humidityThreshold_min,3,16);
}
void display6() // 显示光照高设置界面
{
OLED_ShowCHinese(0,6,8); //光
OLED_ShowCHinese(18,6,9); //照
OLED_ShowCHinese(36,6,10); //高
OLED_ShowString(54,6,":",16);
OLED_ShowString(88,6,"%",16); //%
OLED_ShowNum(58,6,lightThreshold_max,3,16);
}
void display7() // 显示光照低设置界面
{
OLED_ShowCHinese(0,6,8); //光
OLED_ShowCHinese(18,6,9); //照
OLED_ShowCHinese(36,6,11); //低
OLED_ShowString(54,6,":",16);
OLED_ShowString(88,6,"%",16); //%
OLED_ShowNum(58,6,lightThreshold_min,3,16);
}