完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
功能描述:
使用STM32F103R8T6,红外遥控器,数码管,串口,预留ADC(4~20mA输入、0~10V输入)、485、以太网、WiFi、SD卡、USB_OTG等功能。单总线的方式采集温湿度(因整个系统时序要求,所以使用状态机采集),ST自带的RTC时钟。单片机采集到温湿度数值通过串口自制的4G模块通讯,上传数据到指定服务器,然后在微信小程序关注蓝星物联,就可以看到设备数据,我们还做了一个网站,专门管理底层设备。现在分享单片机程序和部分原理图。程序源码在一个资源里面,下载需要积分,没有积分的可以私信我。 硬件设计: 1)电源及MCU部分 使用LM2596DC-DC,降压后放上一个LDO输出3.3V。预留SWD下载,纽扣电池,BOOT脚选择,红外接收头,温湿度模块,多预留一路时钟模块,防止内部RTC随温漂的误差。以及其他一些外设接口预留。 2)通讯及数据保存部分 预留485接口,232接口,USB接口,WiFi接口,以太网接口。通信接口只要做数据汇总、数据上传使用。数据保存有SD卡保存和flash保存两种,SD卡保存使用模块外接的形式,可以把采集到的数据直接保存成文件的形式。flash芯片可以保存需要的字库等等。 3)输入输出部分 预留ADC采集4~20mA及0~10V模拟信号。按键输入,不是全隔离,只是防止客户使用的时候会接错烧坏MCU引脚。继电器输出,可带12V声光报警器。 4)数码管显示部分 使用驱动芯片JXI5020级联,做到三线带多位数码管。 5)PCB展示 软件设计: 1)main.c main函数进来,先是各种外设初始化,第一个while(1)的循环是发送消息给服务器或者上层设备注册设备用的,设备长时间不按照协议通讯的话,设备会软复位,重新注册。在第二个while(1)的循环是处理注册后的数据,包括温湿度和时间的显示,按键(红外遥控和轻触按键)设置,报警状态设置,设置定时,每1S钟发送一次实时数据到服务器,处理串口2接收到服务器发送的数据(JSON格式,使用CJSON库)。 /********************************************************************************* * 文件名 :main.c * 描述 : * 实验平台: * 库版本 :ST3.5.0 * 作者 : * 论坛 : * 版本 ?VV1.0 * 时间 :2017.6.6 备注 : STM32F103_R8T6 工程模版 **********************************************************************************/ #include "Header_File.h" u8 Send_Buff1[114]; u16 Crc_Send_Num=0; u8 Crc_Send_H=0; u8 Crc_Send_L=0; u32 Set_MS_Num=0; #define SW_RESET() NVIC_SystemReset() int processMessage(char *msg) { cJSON *jsonObj=cJSON_Parse(msg); cJSON *method; cJSON *typ; // cJSON *temmin; // cJSON *temmax; // cJSON *temcalibrate; // cJSON *tembkwd; // cJSON *hummin; // cJSON *hummax; // cJSON *humcalibrate; // cJSON *humbkwd; char *m; int x; //json字符串解析失败,直接退出 if(!jsonObj) { cJSON_Delete(jsonObj); return 0; } method = cJSON_GetObjectItem(jsonObj, "TYPE"); m = method->valuestring; if(strncmp(m, "AUTHERROR", 9) == 0 ) //设备未注册,重新注册 { cJSON_Delete(jsonObj); SW_RESET(); return 1; // char *content = cJSON_GetObjectItem(jsonObj, "DATA")->valuestring; // if(strncmp(content, "No identity", 4) == 0) // { // // } } if(strncmp(m, "HSOK", 4) == 0 ) //注册信息返回 { char *content = cJSON_GetObjectItem(jsonObj, "DATA")->valuestring; if(strncmp(content, "HSOK", 4) == 0) { cJSON_Delete(jsonObj); return 1; } } if(strncmp(m, "DATAOK", 6) == 0 ) // { char *content = cJSON_GetObjectItem(jsonObj, "TYPE")->valuestring; if(strncmp(content, "DATAOK", 6) == 0) { Resert_Cnt_10S=0; //重启时间清零 cJSON_Delete(jsonObj); return 1; } } if(strncmp(m, "CDATA", 5) == 0 ) // { typ = cJSON_GetObjectItem(jsonObj, "ctype"); x=typ->valueint; // if(strncmp(m, "1", 1) == 0 ) if(x==1) { int ye = cJSON_GetObjectItem(jsonObj, "y")->valueint; int mo = cJSON_GetObjectItem(jsonObj, "m")->valueint; int da = cJSON_GetObjectItem(jsonObj, "d")->valueint; int ho = cJSON_GetObjectItem(jsonObj, "h")->valueint; int mi = cJSON_GetObjectItem(jsonObj, "i")->valueint; int se = cJSON_GetObjectItem(jsonObj, "s")->valueint; RTC_Set(ye,mo,da,ho,mi,se); // Set_MS_Num=atoi(ye); } else if(x==2) // { double temmin = cJSON_GetObjectItem(jsonObj, "tem_min")->valuedouble; double temmax = cJSON_GetObjectItem(jsonObj, "tem_max")->valuedouble; double temcalibrate = cJSON_GetObjectItem(jsonObj, "tem_calibrate")->valuedouble; double tembkwd = cJSON_GetObjectItem(jsonObj, "tem_bkwd")->valuedouble; double hummin = cJSON_GetObjectItem(jsonObj, "hum_min")->valuedouble; double hummax = cJSON_GetObjectItem(jsonObj, "hum_max")->valuedouble; double humcalibrate = cJSON_GetObjectItem(jsonObj, "hum_calibrate")->valuedouble; double humbkwd = cJSON_GetObjectItem(jsonObj, "hum_bkwd")->valuedouble; // Temperature_Down=atoi(temmin); // Temperature_Up=atoi(temmax); Temperature_Up=(s16)(temmax*10); Temperature_Down=(s16)(temmin*10); Temperature_Temp=(s16)(temcalibrate*10); Temperature_hc=(s16)(tembkwd*10); Humidity_Up=(s16)(hummax*10); Humidity_Down=(s16)(hummin*10); Humidity_Temp=(s16)(humcalibrate*10); Humidity_hc=(s16)(humbkwd*10); } } // if(jsonObj) cJSON_Delete(jsonObj); return 0; } int main(void) { // char* out="{"TYPE":"fengxin","passwd":123,"y":2020,"m":6,"d":3,"h":23,"minu":33}"; // ,"miao":20 u8 ack_Usart1=0; u8 cclen=0; u16 Cnt_500S=0; u16 Cnt_500Ms=0; SystemInit(); //系统时钟初始化为72M SYSCLK_FREQ_72MHz TIM3_Cnt_Init(999,71); //配置定时器3 1Ms中断一次 RTC_Init(); //RTC时钟初始化 IO_Init(); //GPIO初始化 Uart1_Init(115200); //串口1初始化 // Uart3_Init(9600); HC595_IO_Init(); //74HC595使用初始化 Init_AOSONG(); //温湿度模块初始化 Init_Infrared(); //红外模块初始化,开一个定时器和一个外部中断 // Adc_Init(); Key_IO(); //按键输入初始化 就是这么优秀 Uart2_Init(115200); //数据上传 // Interface_Changes=1; W25QXX_Init(); //W25QXX初始化 FLASH_SIZE=32*1024*1024; //FLASH 大小为xxM字节 Read_Data_From_Flash(); Time_Num[8]=calendar.week%10; delay_ms(100); show_led12(); while(1) //注册设备信息,待服务器返回后退出 { if(System1S==1) { System1S=0; cclen=sprintf((char *)Send_Server,"{"TYPE":"HS","CID":"WJVSAIL1"}"); Usart2_Printf((uint8_t *)Send_Server,cclen); } if(USART2_RX_STA == 1) { USART2_RX_STA = 0; ack_Usart1=processMessage((char*)Usart2_RxBuff); Clear_Buffer((uint8_t*)Usart2_RxBuff,strlen((char*)Usart2_RxBuff)); rx_counter_Usart2 = 0; if(ack_Usart1==1) break; } } while(1) { show_led12(); //数码管显示 Key_use(); //按键处理 Data_Change(); if(Display_Complete_Flag==1) { if(((Temp_001>Temperature_Up)||(Temp_001 if(Temp_001>Temperature_Up) Temperature_Up_Flag=1; if(Humi_001>Humidity_Up) Humidity_Up_Flag=1; if(Temp_001 } else if((Set_Position==0)&&(Key_Set_Position==0)) { if(Temp_001<=(Temperature_Up-Temperature_hc)) Temperature_Up_Flag=0; if(Temp_001>=(Temperature_Down+Temperature_hc)) Temperature_Down_Flag=0; if(Humi_001<=(Humidity_Up-Humidity_hc)) Humidity_Up_Flag=0; if(Humi_001>=(Humidity_Down+Humidity_hc)) Humidity_Down_Flag=0; if((Temperature_Up_Flag==0)&&(Temperature_Down_Flag==0)&&(Humidity_Up_Flag==0)&&(Humidity_Down_Flag==0)) Out2=0; if(Mute_Flag==1) Out2=0; } } if((Set_Position==0)&&(Key_Set_Position==0)) { Time_Num[8]=calendar.week%10; if(Time_Num[8]==0) Time_Num[8]=7; } if(System10Ms==1) { System10Ms=0; if(++Cnt_500Ms>=50) { Cnt_500Ms=0; LED1=~LED1; if((Set_Position==0)&&(Key_Set_Position==0)) { RTC_LED=~RTC_LED; } else RTC_LED=0; } } if(System1S==1) { System1S=0; Clear_Buffer((uint8_t*)Send_Server,strlen((char*)Send_Server)); cclen=sprintf((char *)Send_Server,"{"TYPE":"DATA","Tmp":"%d.%01d","Hum":"%d.%01d"}",Temp_001/10,Temp_001%10,Humi_001/10,Humi_001%10); //,Temp_001/10,Temp_001%10,Humi_001/10,Humi_001%10 Usart2_Printf((uint8_t *)Send_Server,cclen); if(++Resert_Cnt_10S>=20) //10s后没有接到来自服务器的数据进行软重启,再次注册 { Resert_Cnt_10S=0; SW_RESET(); } } if(USART2_RX_STA == 1) { USART2_RX_STA = 0; processMessage((char*)Usart2_RxBuff); Clear_Buffer((uint8_t*)Usart2_RxBuff,strlen((char*)Usart2_RxBuff)); rx_counter_Usart2 = 0; } } } 2)Infrared.c 红外接收模块处理文件。在主函数里面只需要直接访问InfraredCode变量就可以知道是否有按键按下。开启一个定时器和一个外部中断,检测接收头(HS0038B)是否收到遥控器的信号,并按照固定的格式给信号做一个处理。识别对应的按钮按下对应的码值,做一个赋值,供主函数读取使用。 #include "infrared.h" //#include "Timer2_Cnt.h" u8 timercnt_up=0; u8 timercnt_down=0; u8 Infraredbuf[4]; u8 InfraredCode=0; // 红外遥控器 编码格式 // START + 8Bit厂商编码 + 8Bit反码 + 8Bit数据 + 8Bit反码 // START L=9.00ms H=4.500ms 引导码 // BIT0 L=0.56ms H=0.565ms 数据0 // BIT1 L=0.56ms H=1.690ms 数据1 // PRESS L=9.00ms H=2.250ms 重复码 u8 irdata[33]; u8 irok; u16 irtime; static u8 ir_flag; //是否开始处理标志位 void TIM2_Cnt_Init(u16 arr,u16 psc) // TIM2_Cnt_Init(999,71); 1ms中断一次 { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //①时钟TIM2使能 //定时器TIM2初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //②初始化TIM2 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE ); //③允许更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级1级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能 NVIC_Init(&NVIC_InitStructure); //④初始化NVIC寄存器 TIM_Cmd(TIM2, ENABLE); //⑤使能TIM2 } //定时器2中断服务程序 void TIM2_IRQHandler(void) //TIM2中断 { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //检查TIM2更新中断发生与否 { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新中断标志 irtime++; //用于计数2个下降沿之间的时间,0.1ms进一次定时器中断 if(irtime>=1000) //irtime>=1000说明按键已经松开有1000*0.1ms=100ms了,故可以使能红外接收功能 { ir_flag=1; //使能红外接收功能 } // if(INIR) timercnt_up++; // else timercnt_down++; // System1Ms=1; // if(++i>=10) // { // i=0; // System10Ms=1; // } } } void EXTIX_Init(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能PORTA,PORTC时钟 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;//PC5 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOC5 //GPIOC.1 中断线以及中断初始化配置 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,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寄存器 NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //使能按键所在的外部中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //子优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道 NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 } void EXTI4_IRQHandler(void) { static u8 RecvCnt; //接收红外信号处理 u8 a,b,c,d,i,e,f,tmp; u32 RecvData = 0; u16 VS1838_KEY_VALUE; if(EXTI_GetITStatus(EXTI_Line4)!=RESET) { EXTI_ClearITPendingBit(EXTI_Line4); //清除EXTI0线路挂起位 if(ir_flag==0) { irtime=0; //清零计数器 return; } else if(ir_flag==1) { if((irtime>IR_START) && (irtime<(IR_START + DataTolerance_IR))) //接收到引导码 9ms+4.5ms { RecvCnt=0; } irdata[RecvCnt]=irtime; //存储每个电平的持续时间,用于以后判断是0还是1 irtime=0; //清零irtime变量 RecvCnt++; //数组下标自增1 if(RecvCnt>=33) { irok=1; //接收完33个红外数据 RecvCnt=0; //数组下标清零 ir_flag=0; //失能红外接收功能 VS1838_KEY_VALUE=0; for(i=1;i<33;i++){ if((irdata > IR_BIT1) && (irdata < (IR_BIT1 + DataTolerance_IR))){ // BIT1 RecvData <<= 1; RecvData |= 1; }else if((irdata > IR_BIT0) && (irdata < (IR_BIT0 + DataTolerance_IR))){ // BIT0 RecvData <<= 1; } } a=(RecvData>>24)&0xFF; b=(RecvData>>16)&0xFF; c=(RecvData>>8)&0xFF; d= RecvData&0xFF; e=~b; f=~d; if((a==e)&&(c==f)) { VS1838_KEY_VALUE=(a<<8)|c; } switch(VS1838_KEY_VALUE){ case 0x0098:InfraredCode=Infrared_0;break; // 按键 0 case 0x00A2:InfraredCode=Infrared_1;break; // 按键 1 case 0x0062:InfraredCode=Infrared_2;break; // 按键 2 case 0x00E2:InfraredCode=Infrared_3;break; // 按键 3 case 0x0022:InfraredCode=Infrared_4;break; // 按键 4 case 0x0002:InfraredCode=Infrared_5;break; // 按键 5 case 0x00C2:InfraredCode=Infrared_6;break; // 按键 6 case 0x00E0:InfraredCode=Infrared_7;break; // 按键 7 case 0x00A8:InfraredCode=Infrared_8;break; // 按键 8 case 0x0090:InfraredCode=Infrared_9;break; // 按键 9 case 0x0068:InfraredCode=Infrared_Xing;break; // 按键 * case 0x00B0:InfraredCode=Infrared_Jing;break; // 按键 # case 0x0010:InfraredCode=Infrared_Left;break; // 按键 < case 0x005A:InfraredCode=Infrared_Right;break; // 按键 > case 0x0018:InfraredCode=Infrared_Up;break; // 按键 ^ case 0x004A:InfraredCode=Infrared_Down;break; // 按键 v case 0x0038:InfraredCode=Infrared_OK;break; // 按键 OK default: tmp=0;break; // NO_KEY } } } // irok=0; } } /* 红外解码,返回按键键值 */ void HS0038B_DeCode(void) { // u8 a,b,c,d,i,tmp; // u32 RecvData = 0; // u16 VS1838_KEY_VALUE; // // if(irok){ // irok=0; // VS1838_KEY_VALUE=0; // for(i=1;i<33;i++){ // if((irdata > IR_BIT1) && (irdata < (IR_BIT1 + DataTolerance_IR))){ // BIT1 // RecvData <<= 1; // RecvData |= 1; // }else if((irdata > IR_BIT0) && (irdata < (IR_BIT0 + DataTolerance_IR))){ // BIT0 // RecvData <<= 1; // } // } // a=(RecvData>>24)&0xFF; // b=(RecvData>>16)&0xFF; // c=(RecvData>>8)&0xFF; // d= RecvData&0xFF; // if((a==~b)&&(c==~d)) // VS1838_KEY_VALUE=(a<<8)|c; // // switch(VS1838_KEY_VALUE){ // case 0x0098:InfraredCode=Infrared_0;break; // 按键 0 // case 0x00A2:InfraredCode=Infrared_1;break; // 按键 1 // case 0x0062:InfraredCode=Infrared_2;break; // 按键 2 // case 0x00E2:InfraredCode=Infrared_3;break; // 按键 3 // case 0x0022:InfraredCode=Infrared_4;break; // 按键 4 // case 0x0002:InfraredCode=Infrared_5;break; // 按键 5 // case 0x00C2:InfraredCode=Infrared_6;break; // 按键 6 // case 0x00E0:InfraredCode=Infrared_7;break; // 按键 7 // case 0x00A8:InfraredCode=Infrared_8;break; // 按键 8 // case 0x0090:InfraredCode=Infrared_9;break; // 按键 9 // case 0x0068:InfraredCode=Infrared_Xing;break; // 按键 * // case 0x00B0:InfraredCode=Infrared_Jing;break; // 按键 # // case 0x0010:InfraredCode=Infrared_Left;break; // 按键 < // case 0x005A:InfraredCode=Infrared_Right;break; // 按键 > // case 0x0018:InfraredCode=Infrared_Up;break; // 按键 ^ // case 0x004A:InfraredCode=Infrared_Down;break; // 按键 v // case 0x0038:InfraredCode=Infrared_OK;break; // 按键 OK // default: tmp=0;break; // NO_KEY // } return tmp; // } // return 0; } void Init_Infrared(void) { TIM2_Cnt_Init(99,71); //100us EXTIX_Init(); } 3)其他 具体使用到的可以看下面的工程的目录,下面有源码分享的链接。 协议及调试效果 1)硬件实物图 2)微信小程序检测界面 博客就只是分享了原理图和部分程序,一些注释也不全。有需要的可以下载源码。底层的设备都是做好和服务器直接对接的,要是连接服务器的话可以直接使用。或者加上中转设备,有WiFi,以太网或者4G。协议也是有的,因最近比较忙,等我回苏之后贴出来。谢谢阅览。 |
|
|
|
只有小组成员才能发言,加入小组>>
3311 浏览 9 评论
2994 浏览 16 评论
3493 浏览 1 评论
9058 浏览 16 评论
4087 浏览 18 评论
1178浏览 3评论
605浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
599浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2335浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1896浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 00:13 , Processed in 1.023102 second(s), Total 47, Slave 38 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号