今日尝试开发一款简单好学的 C8T6+C#_Winform上位机 的微型家庭中控设备的 小试验品:
这个设备将成为我毕设系统的一个小部分......
主要开发环境与工具介绍:
单片机 STM32F103C8T6 使用标准库函数编程
Visual Studio 2022软件 C# Winform 开发 上位机控制软件
本文主要描述一下STM32部分的实现......STM32部分主要实现:
监控温度湿度,查看时间日期,开灯,开门,报警,串口回传信息给上位机等功能
文章提供完整代码解释、设计点解释、测试效果图、完整工程下载
目录
STM32F103C8T6单片机部分:
这部分就用简单的while(1)主循环加定时器中断的前后台逻辑裸机程序就能很好胜任了,
不需要什么都上操作系统(耗电)......
使用模块与接线:
使用以下传感器与显示器(包含接线):
AHT10 温湿度传感器 :
SCL: PB3
SDA: PB4
OLED显示器:
SCL: PB6
SDA: PB7
Beep蜂鸣器 : PA0
LED 灯 : PA1
舵机 : PA6
按键KEY1 : PA2
按键KEY2 : PB15
**按键KEY3 :**PB14
按键KEY4 : PB13
最后还有个稳压降压模块
硬件实物连接图:
直接飞线连连得了......
代码编程主要逻辑:
灯、蜂鸣器、按键的引脚初始化都只需要初始化为输入输出引脚即可
cpp#include "headfire.h" //蜂鸣器、灯、按键 初始化 void Beep_LED_KEY_init(void) { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启LED相关的GPIO外设时钟*/ RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA,ENABLE); /*开启LED相关的GPIO外设时钟*/ RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE); /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/ GPIO_Init(GPIOA , &GPIO_InitStructure); /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/ GPIO_Init(GPIOA , &GPIO_InitStructure); // 配置PA2为输入模式,无上拉/下拉 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入,你也可以选择GPIO_Mode_IPU(内部上拉)或GPIO_Mode_IPD(内部下拉) GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置GPIOB13为输入模式,这里以内部上拉为例 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 内部上拉输入 GPIO_Init(GPIOB, &GPIO_InitStructure); // 类似地配置GPIOB14和GPIOB15 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_Init(GPIOB, &GPIO_InitStructure); // 保持之前的模式设置(内部上拉) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_Init(GPIOB, &GPIO_InitStructure); // 保持之前的模式设置(内部上拉) } //按键处理函数 //返回按键值 //mode:0,不支持连续按;1,支持连续按; //0,没有任何按键按下 //1,KEY0按下 //2,KEY1按下 //4,KEY3按下 WK_UP //注意此函数有响应优先级,KEY0>KEY1>KEY3!! u8 KEY_Scan(u8 mode) { static u8 key_up=1;//按键按松开标志 if(mode)key_up=1; //支持连按 if(key_up&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)) { delay_ms(10);//去抖动 key_up=0; if(KEY1==0)return KEY1_PRES; else if(KEY2==0)return KEY2_PRES; else if(KEY3==0)return KEY3_PRES; else if(KEY4==0)return KEY4_PRES; }else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)key_up=1; return 0;// 无按键按下 }
cppvoid Beep_LED_KEY_init(void); /* 使用标准的固件库控制IO*/ #define LED(a) if (a) \ GPIO_SetBits(GPIOA, GPIO_Pin_1);\ else \ GPIO_ResetBits(GPIOA, GPIO_Pin_1) /* 使用标准的固件库控制IO*/ #define Beep(a) if (a) \ GPIO_SetBits(GPIOA, GPIO_Pin_0);\ else \ GPIO_ResetBits(GPIOA, GPIO_Pin_0) //#define KEY1 PAin(2) //PA2 //#define KEY2 PBin(15) //PB15 //#define KEY3 PBin(14) //PB14 //#define KEY4 PBin(13) //PB13 #define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2) //读取按键1 #define KEY2 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15) //读取按键2 #define KEY3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14) //读取按键3 #define KEY4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13) //读取按键4 #define KEY1_PRES 1 //KEY1按下 #define KEY2_PRES 2 //KEY2按下 #define KEY3_PRES 3 //KEY3按下 #define KEY4_PRES 4 //KEY4按下 u8 KEY_Scan(u8 mode); #endif
主函数用各种标志位等刷新OLED、按键操作等,定时器实时刷新一些数据或OLED图片...
cpp#include "main.h" float temp; u8 hum; int humm,temper; uint16_t AHT10_cnt,AHT10_flag; char buf[30]; uint16_t AHT10_cnt,AHT10_flag; char buf[30]; //刷新时间标志 uint16_t TimeDisplay_cnt,TimeDisplay,BMP_Display; u8 display; //切换OLED显示内容用(0:温湿度 1:时间) u8 KEY_value; int age=21; u8 door_state=0; //门状态 u8 oled_state=0; //显示状态 u8 led_state=0; //灯状态 u8 Beep_state=0; //报警状态 u8 Beep_s=0; //报警状态 u8 Beep_cnt=0; int main(void) { init_ALL(); //初始化所有函数 while(1) { KEY_value=KEY_Scan(0); switch(KEY_value) { case 0: break; case KEY1_PRES: led_state++; if(led_state%2==1) { LED(1);} if(led_state%2==0) { LED(0);} if(led_state == 16) {led_state=2;} //防止数据异常 break; case KEY2_PRES: Beep_state++; if(Beep_state%2==0) { Beep_s=1;} if(Beep_state%2==1) { Beep_s=0;} if(Beep_state == 16) {Beep_state=2;} //防止数据异常 break; case KEY3_PRES: oled_state++; if(oled_state%2==0) { OLED_Clear();Beep(1);display=1;} if(oled_state%2==1) { OLED_Clear();Beep(1);display=0;} if(oled_state == 16) {oled_state=2;} //防止数据异常 break; case KEY4_PRES: door_state++; if(door_state%2==0) {Beep(1);TIM_SetCompare1(TIM3,60); } if(door_state%2==1) {Beep(1);TIM_SetCompare1(TIM3,260);} if(door_state == 16) {door_state=2;} //防止数据异常 break; } if(Beep_s==1) { Beep(1); } else if(Beep_s==0) { Beep(0); } if(AHT10_flag==1 && display==0) { AHT10_flag=0; AHT10ReadData(&temp,&hum); temper=temp*10; humm=hum; OLED_ShowCHinese(0,0,0); //打印中文"温" OLED_ShowCHinese(16,0,2); //打印中文"度" sprintf(buf,": %d.%d",temper/10,temper%10); OLED_ShowString(32,0,(u8 *)buf,16); OLED_ShowCHinese(0,2,1); //打印中文"湿" OLED_ShowCHinese(16,2,2); //打印中文"度" sprintf(buf,": %d ",humm); OLED_ShowString(32,2,(u8 *)buf,16); //打印班级姓名 sprintf(buf,"%d ", age); OLED_ShowString(0,5,(u8 *)buf,16); OLED_ShowCHinese(16,5,6); //打印中文"通" OLED_ShowCHinese(32,5,7); //打印中文"信" OLED_ShowCHinese(48,5,8); //打印中文"杨" OLED_ShowCHinese(64,5,9); //打印中文"志" OLED_ShowCHinese(80,5,10); //打印中文"豪" } else if(TimeDisplay==1 && display==1) { Time_Display(RTC_GetCounter(),&time1); TimeDisplay=0; } if(BMP_Display==1 && display==1) { switch(BMP_FLAG) { case 1:OLED_DrawBMP(0,0,64,8,BMP1); break; case 2:OLED_DrawBMP(0,0,64,8,BMP2); break; case 3:OLED_DrawBMP(0,0,64,8,BMP3); break; case 4:OLED_DrawBMP(0,0,64,8,BMP4); break; case 5:OLED_DrawBMP(0,0,64,8,BMP5); break; case 6:OLED_DrawBMP(0,0,64,8,BMP6); break; case 7:OLED_DrawBMP(0,0,64,8,BMP7); break; case 8:OLED_DrawBMP(0,0,64,8,BMP8); break; case 9:OLED_DrawBMP(0,0,64,8,BMP9); break; case 10:OLED_DrawBMP(0,0,64,8,BMP10); break; case 11:OLED_DrawBMP(0,0,64,8,BMP11); break; case 12:OLED_DrawBMP(0,0,64,8,BMP12); break; case 13:OLED_DrawBMP(0,0,64,8,BMP13); break; case 14:OLED_DrawBMP(0,0,64,8,BMP14); break; case 15:OLED_DrawBMP(0,0,64,8,BMP15); break; //flash不够了,只能注释掉这些内存 // case 16:OLED_DrawBMP(0,0,64,8,BMP16); break; // case 17:OLED_DrawBMP(0,0,64,8,BMP17); break; // case 18:OLED_DrawBMP(0,0,64,8,BMP18); break; // case 19:OLED_DrawBMP(0,0,64,8,BMP19); break; // case 20:OLED_DrawBMP(0,0,64,8,BMP20); break; // case 21:OLED_DrawBMP(0,0,64,8,BMP21); break; // case 22:OLED_DrawBMP(0,0,64,8,BMP22); break; // case 23:OLED_DrawBMP(0,0,64,8,BMP23); break; // case 24:OLED_DrawBMP(0,0,64,8,BMP24); break; // case 25:OLED_DrawBMP(0,0,64,8,BMP25); break; // case 26:OLED_DrawBMP(0,0,64,8,BMP26); break; // case 27:OLED_DrawBMP(0,0,64,8,BMP27); break; // case 28:OLED_DrawBMP(0,0,64,8,BMP28); break; } BMP_Display=0; } } } //初始化所有函数: void init_ALL(void) { SysTick_Init(72); //初始化滴答计时器 Timer2_Init(); //初始化定时器2 i2c_GPIO_Config(); //IIC初始化 OLED_Init(); //初始化OLED屏幕 OLED_Clear(); //清空屏幕数据 AHT10Init(); //初始化温湿度传感器 Timer3_PWM_init(2000,719);//初始化定时器3 Beep_LED_KEY_init(); //蜂鸣器、灯、按键 初始化 RTC_init(); LED(1); Beep(1); delay_ms(50); LED(0); Beep(0); TIM_SetCompare1(TIM3,60);//150是90度 display=1; } //定时器2中断服务函数 void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { if(++AHT10_cnt==30) //每300ms刷新一次温湿度数据 {AHT10_cnt=0;AHT10_flag=1;} if(++TimeDisplay_cnt==10) //每100ms定时器刷新时间 { TimeDisplay_cnt=0;TimeDisplay=1; } if(++BMP_cnt==5) //定时器50ms刷新太空人图片 { BMP_cnt=0; BMP_FLAG++; BMP_Display=1; if(BMP_FLAG==15){BMP_FLAG=1;} } if(++Beep_cnt==12) { Beep(0); Beep_cnt=0; } TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断 } } //RTC每秒进的中断服务函数 void RTC_IRQHandler(void) { if (RTC_GetITStatus(RTC_IT_SEC) != RESET) { /* Clear the RTC Second interrupt */ RTC_ClearITPendingBit(RTC_IT_SEC); // /* Enable time update */ // TimeDisplay = 1; // /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); } }
STM32部分功能单独展示:
KEY1 开关灯
KEY2 报警
KEY3 切换界面显示(时间界面、温湿度界面)
KEY4 驱动舵机模拟开关门
微型家庭中控设备STM32部分功能单独展示
STM32串口发送部分:
这部分的发送逻辑不是简单的发送数据就行了,这是与上位机相配合的数据包格式的发送,目前暂时不添加这个功能,等上位机写好后再编写串口回传数据的功能