#include "led.h" #include "delay.h" #include "sys.h" #include "stm32f10x.h" #include "stdio.h" #include "key.h" #include "usart.h" #include "drive_dht11.h" #include "bh1750.h" #include "ds1302.h" #include "oled.h" #include "ds18b20.h" #include "string.h" #include "adc.h" u8 humi=0;//湿度值 u8 temp=0;//温度值 u8 buffer[10]={0}; u16 GzVal=0; //实际光照值 char ShowBuf[16]={0}; char DateShowBuf[30]={0}; char SendBuff[50]={0}; short TEMP = 0; u16 cj_val=0;//测距值 u8 openflag=0;//手动模式开关按钮 u8 mode_flag=0; #define AM_PIN PCout(13) //按摩控制引脚 #define FAN_PIN PCout(14) //风扇控制引脚 #define ZM_PIN PCout(15) //照明控制引脚 #define FM_PIN PAout(12) //蜂鸣器报警引脚 #define LED_PIN PAout(11) //红色led引脚 #define SAMPLE_PERIOD 20 // 波形采样 #define BUFF_SIZE 50 // 数据缓冲区大小 typedef enum {FALSE, TRUE} BOOL; uint16_t VitData, LastData; // 读取到的 AD 值 uint16_t timeCount = 0; // 采样周期计数变量 uint16_t firstTimeCount = 0; // 第一个心跳时间 uint16_t secondTimeCount = 0; // 第二个心跳时间 /*adjoin_val: 相邻两个心跳的时间,bmp_val: 心率值, pulse_val: 脉象图的数值化表示*/ uint16_t adjoin_val, bmp_val, pulse_val; uint16_t data[BUFF_SIZE] = {0}; // 采样数据缓存 int buf_index = 0; // 缓存数组下标 uint16_t max, min, mid; // 最大、最小及中间值 uint16_t filter_val; // 滤波值 BOOL PULSE = FALSE; // 有效脉搏 BOOL PRE_PULSE = FALSE; // 先前有效脉搏 uint8_t pulseCount = 0; // 脉搏计数 uint32_t i; void Print_Wave(void); uint16_t Get_Array_Max(uint16_t * array, uint32_t size); uint16_t Get_Array_Min(uint16_t * array, uint32_t size); u16 Get_ADC_Average_Val(void); u8 warning_cnt=0; u16 GetTimer=0;//采集时间计数器 u8 dw_val=0;//挡位//亮度值 u8 sc_val=0;//色彩 int cnt_s1=2,cnt_f1=0,cnt_m1=0;//学习时间 时分秒 u8 zm_open_flag=0; //照明开关标志位 u8 ln_open_flag=0;//拉链开关标志位 u8 am_open_flag=0;//按摩 u8 off_time_s=22,off_time_f=0;//定时关闭时间 时分 u8 set_s1=8,set_f1=0; //设置 时间闹钟 #define RT_INPUT PBin(8)//人体红外感应 #define KEY_GPIO_PROT GPIOA #define KEY1_GPIO_PIN GPIO_Pin_1 #define KEY2_GPIO_PIN GPIO_Pin_2 #define KEY3_GPIO_PIN GPIO_Pin_3 #define KEY4_GPIO_PIN GPIO_Pin_4 #define KEY5_GPIO_PIN GPIO_Pin_5 #define KEY6_GPIO_PIN GPIO_Pin_6 #define KEY7_GPIO_PIN GPIO_Pin_7 #define KEY1_IN GPIO_ReadInputDataBit(KEY_GPIO_PROT,KEY1_GPIO_PIN) #define KEY2_IN GPIO_ReadInputDataBit(KEY_GPIO_PROT,KEY2_GPIO_PIN) #define KEY3_IN GPIO_ReadInputDataBit(KEY_GPIO_PROT,KEY3_GPIO_PIN) #define KEY4_IN GPIO_ReadInputDataBit(KEY_GPIO_PROT,KEY4_GPIO_PIN) #define KEY5_IN GPIO_ReadInputDataBit(KEY_GPIO_PROT,KEY5_GPIO_PIN) #define KEY6_IN GPIO_ReadInputDataBit(KEY_GPIO_PROT,KEY6_GPIO_PIN) #define KEY7_IN GPIO_ReadInputDataBit(KEY_GPIO_PROT,KEY7_GPIO_PIN) void key_gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN|KEY2_GPIO_PIN|KEY3_GPIO_PIN|KEY4_GPIO_PIN|KEY5_GPIO_PIN|KEY6_GPIO_PIN|KEY7_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(KEY_GPIO_PROT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); } void TIMER3_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitStrue.TIM_Period=arr; TIM_TimeBaseInitStrue.TIM_Prescaler=psc; TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CounterMode_Up; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStrue); TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//使能TIM3更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIMx 拍另外一个吧 重复拍一个有刷单嫌疑 } /* 1,照明功能与定时器模块,可以设置照明灯开启 定时关灯,设置闹钟时间,闹钟时间到蜂鸣器滴滴响报警。这里需要加入实时时钟模块 2,自动拉帘通过按钮控制,电机正反转模拟自动开合。 3,音乐播放功能通过蓝牙音频模块实现,通过蓝牙与用户手机连接播放音乐。 4,按摩功能,通过手动按键控制开关,通过振动器模拟实现按摩效果, 5,检测体表温度值,如果人体温度过高,出现发烧情况,进行蜂鸣器报警同时红色LED灯亮起, 检测环境温湿度控制是否加热和排风,当实际温度值小于设置温度值时,自动开启加热丝(这里通过继电器控制加热电阻实现), 当实际温度值大于设置值切实际湿度值大于设置湿度值,这时开启排风,这里使用小风扇实现。 6 检测心率值,当实际值大于上限值时或者小于下限值时,开启蜂鸣器报警。同时LED闪烁。\ 7 液晶显示 */ u8 set_flag=0;//设置标志位 u8 set_ok_flag=1;//设置完成标志位 u8 temp_L=30,temp_H=37;//设置温度上下限值 u8 humi_H=0;//湿度上限值 u8 xl_L=0,xl_H=120;//设置心率值 int key_Task(void) { static char key_flag=0; if(KEY1_IN==0|KEY2_IN==0|KEY3_IN==0|KEY4_IN==0|KEY5_IN==0|KEY6_IN==0|KEY7_IN==0) { if(key_flag==0) { key_flag=1; if(KEY1_IN==0) { zm_open_flag^=1;//开关控制切换 } else if(KEY2_IN==0) { ln_open_flag^=1; } else if(KEY3_IN==0) { am_open_flag^=1; } else if(KEY4_IN==0) { if(set_flag++>9) { set_flag=0; } set_ok_flag=0; } else if(KEY5_IN==0) { switch(set_flag) { case 1: if(++off_time_s>23)off_time_s=0; break; case 2: if(++off_time_f>59)off_time_f=0; break; case 3: if(++set_s1>23)set_s1=0; break; case 4: if(++set_f1>59)set_f1=0; break; case 5: if(++temp_L>99)temp_L=0; break; case 6: if(++temp_H>99)temp_H=0; break; case 7: if(++humi_H>99)humi_H=0; break; case 8: if(++xl_L>150)xl_L=0; break; case 9: if(++xl_H>150)xl_H=0; break; case 10: break; } } else if(KEY6_IN==0) { switch(set_flag) { case 1: if(--off_time_s<=0)off_time_s=23; break; case 2: if(--off_time_f<=0)off_time_f=59; break; case 3: if(--set_s1<=0)set_s1=23; break; case 4: if(--set_f1<=0)set_f1=59; break; case 5: if(--temp_L<=0)temp_L=99; break; case 6: if(--temp_H>99)temp_H=99; break; case 7: if(--humi_H<=0)humi_H=99; break; case 8: if(--xl_L<=0)xl_L=150; break; case 9: if(--xl_H<=0)xl_H=150; break; case 10: break; } } else if(KEY7_IN==0) { set_ok_flag=1; } } }else { key_flag=0; } return 20; } u16 DataUpTime=0;//时间显示计数器 u8 rt_flag=0;//红外人体标志位 u16 rt_cnt=0; u8 seccnt=0; //IN1-PA4 #define IN1_GPIO_PORT GPIOB #define IN1_GPIO_CLK RCC_APB2Periph_GPIOB #define IN1_GPIO_PIN GPIO_Pin_12 //IN2-PA5 #define IN2_GPIO_PORT GPIOB #define IN2_GPIO_CLK RCC_APB2Periph_GPIOB #define IN2_GPIO_PIN GPIO_Pin_13 //IN3-PA6 #define IN3_GPIO_PORT GPIOB #define IN3_GPIO_CLK RCC_APB2Periph_GPIOB #define IN3_GPIO_PIN GPIO_Pin_14 //IN1-PA7 #define IN4_GPIO_PORT GPIOB #define IN4_GPIO_CLK RCC_APB2Periph_GPIOB #define IN4_GPIO_PIN GPIO_Pin_15 /* 直接操作寄存器的方法控制IO */ #define digitalHi(p,i) {p->BSRR=i;} //输出为高电平 #define digitalLo(p,i) {p->BRR=i;} //输出低电平 /* 定义控制IO的宏 */ #define IN1_HIGH digitalHi(IN1_GPIO_PORT,IN1_GPIO_PIN) #define IN1_LOW digitalLo(IN1_GPIO_PORT,IN1_GPIO_PIN) #define IN2_HIGH digitalHi(IN2_GPIO_PORT,IN2_GPIO_PIN) #define IN2_LOW digitalLo(IN2_GPIO_PORT,IN2_GPIO_PIN) #define IN3_HIGH digitalHi(IN3_GPIO_PORT,IN3_GPIO_PIN) #define IN3_LOW digitalLo(IN3_GPIO_PORT,IN3_GPIO_PIN) #define IN4_HIGH digitalHi(IN4_GPIO_PORT,IN4_GPIO_PIN) #define IN4_LOW digitalLo(IN4_GPIO_PORT,IN4_GPIO_PIN) void GPIO_Config(void) { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启引脚相关的GPIO外设时钟*/ RCC_APB2PeriphClockCmd( IN1_GPIO_CLK | IN2_GPIO_CLK | IN3_GPIO_CLK | IN4_GPIO_CLK , ENABLE); /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = IN1_GPIO_PIN; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/ GPIO_Init(IN1_GPIO_PORT, &GPIO_InitStructure); /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = IN2_GPIO_PIN; /*调用库函数,初始化GPIO*/ GPIO_Init(IN2_GPIO_PORT, &GPIO_InitStructure); /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = IN3_GPIO_PIN; /*调用库函数,初始化GPIOF*/ GPIO_Init(IN3_GPIO_PORT, &GPIO_InitStructure); /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = IN4_GPIO_PIN; /*调用库函数,初始化GPIOF*/ GPIO_Init(IN4_GPIO_PORT, &GPIO_InitStructure); //所有引脚初始化为低电平 IN1_LOW; IN2_LOW; IN3_LOW; IN4_LOW; } void stepper(uint8_t dir,int speed) { if(dir == 1) { IN1_HIGH; delay_ms(speed); IN1_LOW; IN2_HIGH; delay_ms(speed); IN2_LOW; IN3_HIGH; delay_ms(speed); IN3_LOW; IN4_HIGH; delay_ms(speed); IN4_LOW; } else if(dir==2) { IN1_HIGH; delay_ms(speed); IN1_LOW; IN4_HIGH; delay_ms(speed); IN4_LOW; IN3_HIGH; delay_ms(speed); IN3_LOW; IN2_HIGH; delay_ms(speed); IN2_LOW; } } void fanzhuan(void) { int i; for(i = 0;i < 220;i++) //电机反转 stepper(2,5); } void zhengzhuan(void) { int i; for(i = 0;i < 220;i++) //电机正转 stepper(1,5); } void zhendong_task(void) //按摩器震动轮询 开 - 关 - 开 -关 时间不同模拟按摩效果 { static uint16_t cnt_time=0;//计时控制 按摩时间 if(am_open_flag) { switch(cnt_time) { case 0: AM_PIN=1;//开启 2S break; case 200: AM_PIN=0;//off 1S break; case 300: AM_PIN=1;//开启 break; //3S case 600: AM_PIN=0;//开启 break; //1S case 700: AM_PIN=1;//开启 break; //4s case 1100: AM_PIN=0;//开启 break; //1s case 1200: AM_PIN=1;//开启 break; case 1400: AM_PIN=0;//off break; case 1600: cnt_time=0; break; } cnt_time++; }else { cnt_time=0; AM_PIN=0;//关闭按摩 振动器 } } #define Nx 5 u16 value_buf[Nx]; int Rx_count=0; u16 FILTER_median(char Flag) //中位值滤波 中位值平均滤波 N为采样次数,取奇数 Flag:中位值平均滤波使能 心率数据滤波用的 { u16 temp=0; long sum=0; int count,i,j; for(j=0;j value_buf[i+1] ) { temp = value_buf[i]; value_buf[i] = value_buf[i+1]; value_buf[i+1] = temp; } } } if(Flag) { for(count=1;count=2) { LastData = VitData; // 保存前一次值 VitData = Get_ADC_Average_Val(); // 读取AD转换值 if (((VitData - LastData) < filter_val)&&VitData>1000) // 滤除突变噪声信号干扰 data[buf_index++] = VitData; // 填充缓存数组 if (buf_index >= BUFF_SIZE) { buf_index = 0; // 数组填满,从头再填 // 通过缓存数组获取脉冲信号的波峰、波谷值,并计算中间值作为判定参考阈值 max = Get_Array_Max(data, BUFF_SIZE); min = Get_Array_Min(data, BUFF_SIZE); mid = (max + min)/2; filter_val = (max - min) / 2; } PRE_PULSE = PULSE; // 保存当前脉冲状态 PULSE = (VitData > mid) ? TRUE : FALSE; // 采样值大于中间值为有效脉冲 if (PRE_PULSE == FALSE && PULSE == TRUE) // 寻找到“信号上升到振幅中间位置”的特征点,检测到一次有效脉搏 { pulseCount++; pulseCount %= 2; if(pulseCount == 1) // 两次脉搏的第一次 { firstTimeCount = timeCount; // 记录第一次脉搏时间 } if(pulseCount == 0) // 两次脉搏的第二次 { secondTimeCount = timeCount; // 记录第二次脉搏时间 timeCount = 0; if ( (secondTimeCount > firstTimeCount)) { adjoin_val = (secondTimeCount - firstTimeCount) * SAMPLE_PERIOD; // 计算相邻两次脉搏的时间,得到 adjoin_val if(iCnt20&&val2<150)bmp_val=val2; Flag1=1; }else { if(abs(val2-val1)<20)//后面计算防止突发干扰数据对比前后两次的 { if(val2>20&&val2<150)bmp_val=val2; } } // sprintf(showBuff,"BMP:%3d/min",bmp_val);//打印成字符串 // OLED_ShowString(0,2,showBuff,16);//显示到液晶 // sendbuff[0]='&';//标识符 发送到app的数据帧头 // sendbuff[1]=0;//没用上填0 // sendbuff[2]=bmp_val;//实际心率数据 // sendbuff[3]=sendbuff[0]+sendbuff[1]+sendbuff[2];//计算和校验 // UART3SendBuffer(sendbuff,4);//通过wifi串口发送过去 iCnt=0;//计数器清零 val1=val2;//保存本次值 } } } } timeCount++; // 时间计数累加 CheckCnt=0; } //delay_ms(20);//延时20ms延时再进行下一周期采样 这里的采样率是50*10=500HZ } void TIM3_IRQHandler(void)//中断服务程序 { if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)//SET为1 { GetTimer++;//采集计数器 TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3的更新中断标志 key_Task();//按键扫描 DataUpTime++; zhendong_task();//震动控制 xl_sensor_task();//检测心率功能函数 if(rt_flag) //有人 开始计时计算学习时间 { if(++seccnt>50) { seccnt=0; if(++cnt_m1>59) { cnt_m1=0; if(++cnt_f1>59) { cnt_f1=0; if(++cnt_s1>23) { cnt_s1=0; } } } } } } } u8 GetFlag=0; uint8_t sendBuff[16]={0}; void ShowTo(char* Pdat,char len)//字符串中的空格补 0 { char ixd=0; for(ixd=0;ixd>= 1; } IO_TX_GPIO_Port=1; delay_us(104); } u8 test=0; u8 ln_lock_flag=0;//拉链动作锁定标志 short tb_temp=0;//体表温度 int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 delay_init(); //延时函数初始化 LED_Init(); //初始化与连接的硬件接口 uart_init(9600); delay_ms(1000); key_gpio_init(); OLED_Init();//液晶初始化 OLED_Clear();//液晶清屏 delay_ms(10);//上电延时一会 DS18B20_Init(); Ds1302_Gpio_Init(); Ds1302_Write_Time_All(1); ADC1_Init(); TIMER3_Int_Init(10000,71);//定时器初始化 //10ms进一次中断 while(1) { if(ln_open_flag) //开启拉链 { if(ln_lock_flag==0) //锁定标志 转动一次就醒来 { ln_lock_flag=1; zhengzhuan();//控制电机 } }else //关闭拉链 { if(ln_lock_flag==1) { ln_lock_flag=0; fanzhuan(); } } if(GetTimer>100)//1S { GetTimer=0; if(dht11_read_data(buffer) == 0)//读取温湿度 { humi = buffer[0] + buffer[1] / 10;//取湿度值 temp = buffer[2] + buffer[3] / 10;//取温度值 } tb_temp = DS18B20_Get_Temp(); } if(DataUpTime>40) //400ms刷新一次数据 { DataUpTime=0;// Ds1302_Readtime(); sprintf((char*)DateShowBuf,"time:%2d:%2d:%2d", (int)ds1302Data.hour, (int)ds1302Data.min, (int)ds1302Data.sec); ShowTo(DateShowBuf,strlen(DateShowBuf)); OLED_ShowString(0,0,(u8*)DateShowBuf,12);//显示实时时间 sprintf((char*)DateShowBuf,"alarm:%2d:%2d", (int)set_s1, (int)set_f1); ShowTo(DateShowBuf,strlen(DateShowBuf)); OLED_ShowString(0,1,(u8*)DateShowBuf,12);//显示闹钟时间 sprintf((char*)DateShowBuf,"off:%2d:%2d", (int)off_time_s, (int)off_time_s);//打印字符串 ShowTo(DateShowBuf,strlen(DateShowBuf)); OLED_ShowString(0,2,(u8*)DateShowBuf,12);//显示关闭时间 sprintf((char*)DateShowBuf,"bmp:%3d L:%3d H%3d", (int)bmp_val,xl_L,xl_H); OLED_ShowString(0,3,(u8*)DateShowBuf,12);//显示体表温度 心率值 if((ds1302Data.hour==set_s1)&&(ds1302Data.min==set_f1)) //到达闹钟时间 { warning_cnt|=0x01;//置为1 需要报警 } else { warning_cnt&=~0x01; } if(zm_open_flag) //判断是否开启照明灯 { ZM_PIN=1; if(ds1302Data.hour==off_time_s&&ds1302Data.min==off_time_s) //定时关灯时间到了 { ZM_PIN=0; zm_open_flag=0;//清除标志位 } }else { ZM_PIN=0; } } } } /******************** ADC通道2转换函数 ************************/ u16 Get_ADC_Average_Val(void) { u8 i,j; u16 buff[10] = {0}; u16 temp; for(i = 0; i < 10; i ++) { /* 开始转换 */ buff[i] = Get_Adc(0); //读取转换结果 } /* 把读取值的数据按从小到大的排列 */ for(i = 0; i < 9; i++) { for(j = 0 ; j < 9-i; j++) { if(buff[j] > buff[j+1]) { temp = buff[j]; buff[j] = buff[j+1]; buff[j+1] = temp; } } } /* 求平均值 */ temp = 0; for(i = 1; i < 9; i++) { temp += buff[i]; } return temp/8; } uint16_t Get_Array_Max(uint16_t * array, uint32_t size)//计算数组数据中最大的 { uint16_t max = array[0]; uint32_t i; for (i = 1; i < size; i++) { if (array[i] > max) max = data[i]; } return max; } uint16_t Get_Array_Min(uint16_t * array, uint32_t size)//计算数组数据中最小的 { uint16_t min = array[0]; uint32_t i; for (i = 1; i < size; i++) { if (array[i] < min) min = data[i]; } return min; }