当前位置:   article > 正文

基于STM32的智能手表_根据目前时间调节的stm32电子手表

根据目前时间调节的stm32电子手表

 

本设计由STM32单片机作为控制中枢,来驱动温度采集模块、液晶显示系统、记步模块、血氧心率检测模块等一系列系统。由用户操作屏幕,手环进行一系列的功能。

1、通过DS3231时钟模块实现日期显示功能。

2、通过MAX30100传感器实时监测血氧数据。

3、通过ADXL345检测使用者运动步数。

4、使用液晶显示屏实时显示日期、时间、步数、心率、以及体温等。

5、在设计电路时,保证系统的长久性、稳定性以及高效性。

总体的流程框图如图:

 

温度采集使用的是DS18B20,使用方便测量人体温度作为数据

 

  其驱动代码则为:

  1. #include "ds18b20.h"
  2. #include "delay.h"
  3. //复位DS18B20
  4. void DS18B20_Rst(void)
  5. {
  6. DS18B20_IO_OUT(); //SET PA0 OUTPUT
  7. DS18B20_DQ_OUT=0; //拉低DQ
  8. DelayUs(750); //拉低750us
  9. DS18B20_DQ_OUT=1; //DQ=1
  10. DelayUs(15); //15US
  11. }
  12. //等待DS18B20的回应
  13. //返回1:未检测到DS18B20的存在
  14. //返回0:存在
  15. u8 DS18B20_Check(void)
  16. {
  17. u8 retry=0;
  18. DS18B20_IO_IN();//SET PA0 INPUT
  19. while (DS18B20_DQ_IN&&retry<200)
  20. {
  21. retry++;
  22. DelayUs(1);
  23. };
  24. if(retry>=200)return 1;
  25. else retry=0;
  26. while (!DS18B20_DQ_IN&&retry<240)
  27. {
  28. retry++;
  29. DelayUs(1);
  30. };
  31. if(retry>=240)return 1;
  32. return 0;
  33. }
  34. //从DS18B20读取一个位
  35. //返回值:1/0
  36. u8 DS18B20_Read_Bit(void) // read one bit
  37. {
  38. u8 data;
  39. DS18B20_IO_OUT();//SET PA0 OUTPUT
  40. DS18B20_DQ_OUT=0;
  41. DelayUs(2);
  42. DS18B20_DQ_OUT=1;
  43. DS18B20_IO_IN();//SET PA0 INPUT
  44. DelayUs(12);
  45. if(DS18B20_DQ_IN)data=1;//数据1
  46. else data=0;//数据0
  47. DelayUs(50);
  48. return data;
  49. }
  50. //从DS18B20读取一个字节
  51. //返回值:读到的数据
  52. u8 DS18B20_Read_Byte(void) // read one byte
  53. {
  54. u8 i,j,dat;
  55. dat=0;
  56. for (i=1;i<=8;i++)
  57. {
  58. j=DS18B20_Read_Bit();
  59. dat=(j<<7)|(dat>>1);
  60. }
  61. return dat;
  62. }
  63. //写一个字节到DS18B20
  64. //dat:要写入的字节
  65. void DS18B20_Write_Byte(u8 dat)
  66. {
  67. u8 j;
  68. u8 testb;
  69. DS18B20_IO_OUT();//SET PA0 OUTPUT;
  70. for (j=1;j<=8;j++)
  71. {
  72. testb=dat&0x01;
  73. dat=dat>>1;
  74. if (testb)
  75. {
  76. DS18B20_DQ_OUT=0;// Write 1
  77. DelayUs(2);
  78. DS18B20_DQ_OUT=1;
  79. DelayUs(60);
  80. }
  81. else
  82. {
  83. DS18B20_DQ_OUT=0;// Write 0
  84. DelayUs(60);
  85. DS18B20_DQ_OUT=1;
  86. DelayUs(2);
  87. }
  88. }
  89. }
  90. //开始温度转换
  91. void DS18B20_Start(void)// ds1820 start convert
  92. {
  93. DS18B20_Rst();
  94. DS18B20_Check();
  95. DS18B20_Write_Byte(0xcc);// skip rom
  96. DS18B20_Write_Byte(0x44);// convert
  97. }
  98. //初始化DS18B20的IO口 DQ 同时检测DS的存在
  99. //返回1:不存在
  100. //返回0:存在
  101. u8 DS18B20_Init(void)
  102. {
  103. GPIO_InitTypeDef GPIO_InitStructure;
  104. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA口时钟
  105. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PORTA11 推挽输出
  106. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  107. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  108. GPIO_Init(GPIOA, &GPIO_InitStructure);
  109. GPIO_SetBits(GPIOA,GPIO_Pin_11); //输出1
  110. DS18B20_Rst();
  111. return DS18B20_Check();
  112. }
  113. //从ds18b20得到温度值
  114. //精度:0.1C
  115. //返回值:温度值 (-550~1250)
  116. short DS18B20_Get_Temp(void)
  117. {
  118. u8 temp;
  119. u8 TL,TH;
  120. short tem;
  121. DS18B20_Start (); // ds1820 start convert
  122. DS18B20_Rst();
  123. DS18B20_Check();
  124. DS18B20_Write_Byte(0xcc);// skip rom
  125. DS18B20_Write_Byte(0xbe);// convert
  126. TL=DS18B20_Read_Byte(); // LSB
  127. TH=DS18B20_Read_Byte(); // MSB
  128. if(TH>7)
  129. {
  130. TH=~TH;
  131. TL=~TL;
  132. temp=0;//温度为负
  133. }else temp=1;//温度为正
  134. tem=TH; //获得高八位
  135. tem<<=8;
  136. tem+=TL;//获得底八位
  137. tem=(float)tem*0.625;//转换
  138. if(temp)return tem; //返回温度值
  139. else return -tem;
  140. }

设置的四个按键分别为设置数据、加、减、重置步数。

  1. #include "key.h"
  2. #include "delay.h"
  3. void KEY_Init(void)
  4. {
  5. GPIO_InitTypeDef GPIO_InitStructure;
  6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);//使能PORTA,PORTC时钟
  7. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试
  8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
  9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
  10. GPIO_Init(GPIOB, &GPIO_InitStructure);
  11. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  12. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
  13. GPIO_Init(GPIOA, &GPIO_InitStructure);
  14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  16. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  17. GPIO_Init(GPIOB, &GPIO_InitStructure);
  18. GPIO_ResetBits(GPIOB, GPIO_Pin_9);//初始化为低电平
  19. }
  20. u8 KEY_Scan(u8 mode)
  21. {
  22. static u8 key_up=1;//按键按松开标志
  23. if(mode&&KEY0!=0&&KEY3!=0)key_up=1; //支持连按
  24. if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==0||KEY4==0))
  25. {
  26. DelayMs(65);//去抖动
  27. key_up=0;
  28. if(KEY0==0)return 1;
  29. else if(KEY1==0)return 2;
  30. else if(KEY2==0)return 3;
  31. else if(KEY3==0)return 4;
  32. else if(KEY4==0)return 5;
  33. }else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)key_up=1;
  34. return 0;// 无按键按下
  35. }

采用MAX30102心率血氧模块采集心率信号,MAX30102是一款同时对脉搏血氧和心率监测于一体的模块。具有尺寸小、低功耗、精度高等优点,其硬件内部包含LED、光电探测器、光学元件和具有表境光抑制功能的低噪声电子设备。(使用的是驱动代码,就不放在里面了)

MAX30102工作原理:

光溶积法:用人体组织在血管搏动时造成透光率不同来进行脉搏和血氧饱和度测量的。

光源:采用对动脉血中氧合血红蛋白(HbO2)和血红蛋白,(Hb)有选择性的特定波长的发光了极管。
    透光率转化为电信号:脉搏动充血容积化导到光的透光率发生改变,此时由光电变换接收经人体组织反射光线,转变为电信号并将其放大和输出。

 步数使用的是ADXL345,具体我也没了解,只是听说比MPU6050好用,步数,摔倒啥的。就不细说了。

时钟我没有用32写的,一开始没想那么多,就拿了DS1302时钟模块,直接外接,因为我用的是32的最小系统板(硬件不到家)。断电没法保护,所以要是外加电池,那就太麻烦了,所以我直接给板子通电!外接时钟模块。用的也是商家给的,我自己写闰年啥的。

  1. void DisplayTime(void)//显示时间函数
  2. {
  3. unsigned char i=0,x=0;
  4. u16 nian_temp;
  5. if(setn==0)DS1302_DateRead(&SysDate);//读时间
  6. nian_temp=2000+SysDate.year;
  7. if((nian_temp%400==0)||((nian_temp%100!=0)&&(nian_temp%4==0))) //判断是否为闰年
  8. p_r=1;
  9. else
  10. p_r=0;
  11. if(setn<8)
  12. {
  13. OLED_ShowChar((x++)*8,0,'2',2,setn+1-1);
  14. OLED_ShowChar((x++)*8,0,'0',2,setn+1-1);
  15. OLED_ShowChar((x++)*8,0,SysDate.year/10+'0',2,setn+1-1);
  16. OLED_ShowChar((x++)*8,0,SysDate.year%10+'0',2,setn+1-1);
  17. OLED_ShowChar((x++)*8,0,'-',2,0);
  18. OLED_ShowChar((x++)*8,0,SysDate.mon/10+'0',2,setn+1-2);
  19. OLED_ShowChar((x++)*8,0,SysDate.mon%10+'0',2,setn+1-2);
  20. OLED_ShowChar((x++)*8,0,'-',2,0);
  21. OLED_ShowChar((x++)*8,0,SysDate.day/10+'0',2,setn+1-3);
  22. OLED_ShowChar((x++)*8,0,SysDate.day%10+'0',2,setn+1-3);
  23. OLED_ShowCN(i*16+88,0,0,setn+1-4);//测试显示中文:周
  24. switch(SysDate.week)
  25. {
  26. case 1:
  27. OLED_ShowCN(i*16+104,0,1,setn+1-4);//测试显示中文:一
  28. break;
  29. case 2:
  30. OLED_ShowCN(i*16+104,0,2,setn+1-4);//测试显示中文:二
  31. break;
  32. case 3:
  33. OLED_ShowCN(i*16+104,0,3,setn+1-4);//测试显示中文:三
  34. break;
  35. case 4:
  36. OLED_ShowCN(i*16+104,0,4,setn+1-4);//测试显示中文:四
  37. break;
  38. case 5:
  39. OLED_ShowCN(i*16+104,0,i+5,setn+1-4);//测试显示中文:五
  40. break;
  41. case 6:
  42. OLED_ShowCN(i*16+104,0,6,setn+1-4);//测试显示中文:六
  43. break;
  44. case 7:
  45. OLED_ShowCN(i*16+104,0,7,setn+1-4);//测试显示中文:日
  46. break;
  47. }
  48. x=0;
  49. OLED_ShowChar((x++)*8,2,SysDate.hour/10+'0',2,setn+1-5);
  50. OLED_ShowChar((x++)*8,2,SysDate.hour%10+'0',2,setn+1-5);
  51. OLED_ShowChar((x++)*8,2,':',2,0);
  52. OLED_ShowChar((x++)*8,2,SysDate.min/10+'0',2,setn+1-6);
  53. OLED_ShowChar((x++)*8,2,SysDate.min%10+'0',2,setn+1-6);
  54. OLED_ShowChar((x++)*8,2,':',2,0);
  55. OLED_ShowChar((x++)*8,2,SysDate.sec/10+'0',2,setn+1-7);
  56. OLED_ShowChar((x++)*8,2,SysDate.sec%10+'0',2,setn+1-7);
  57. }
  58. }

 读取心率模块显示我就不放了,按键模块拿出来参考吧。

  1. void KeySettings(void)//按键设置函数
  2. {
  3. unsigned char keynum = 0,i;
  4. keynum = KEY_Scan(1);//获取按键值
  5. if(keynum==1)//设置
  6. {
  7. setn++;
  8. if(setn > 11)
  9. {
  10. setn=0;
  11. sendSetValueFlag=1;
  12. OLED_CLS();//清屏
  13. for(i=0;i<2;i++)OLED_ShowCN(i*16+16,4,i+16,1);//测试显示中文:心率
  14. for(i=0;i<2;i++)OLED_ShowCN(i*16+79,4,i+20,1);//测试显示中文:步数
  15. OLED_ShowCentigrade(112, 2); //℃
  16. DelayMs(50);
  17. Store_Data();
  18. }
  19. if(setn==8)
  20. {
  21. OLED_CLS();//清屏
  22. for(i=0;i<4;i++)OLED_ShowCN(i*16+32,0,i+22,0);//测试显示中文:设置心率
  23. for(i=0;i<2;i++)OLED_ShowCN(i*16,4,i+26,0); //测试显示中文:下限
  24. for(i=0;i<2;i++)OLED_ShowCN(i*16,6,i+28,0); //测试显示中文:上限
  25. OLED_ShowChar(34,4,':',2,0);
  26. OLED_ShowChar(34,6,':',2,0);
  27. }
  28. if(setn==10)
  29. {
  30. for(i=0;i<4;i++)OLED_ShowCN(i*16+32,0,i+34,0);//测试显示中文:设置温度
  31. for(i=0;i<2;i++)OLED_ShowCN(i*16,4,i+26,0); //测试显示中文:下限
  32. for(i=0;i<2;i++)OLED_ShowCN(i*16,6,i+28,0); //测试显示中文:上限
  33. OLED_ShowChar(34,4,':',2,0);
  34. OLED_ShowChar(34,6,':',2,0);
  35. }
  36. DisplaySetValue();
  37. }
  38. if(keynum==2)//加
  39. {
  40. if(setn == 1)//设置年
  41. {
  42. SysDate.year ++;
  43. if(SysDate.year == 100)SysDate.year=0;
  44. DS1302_DateSet(&SysDate);//设置时间
  45. }
  46. if(setn == 2)//设置月
  47. {
  48. SysDate.mon ++;
  49. if(SysDate.mon == 13)SysDate.mon = 1;
  50. if((SysDate.mon==4)||(SysDate.mon==6)||(SysDate.mon==9)||(SysDate.mon==11))
  51. {
  52. if(SysDate.day>30)SysDate.day=1;
  53. }
  54. else
  55. {
  56. if(SysDate.mon==2)
  57. {
  58. if(p_r==1)
  59. {
  60. if(SysDate.day>29)
  61. SysDate.day=1;
  62. }
  63. else
  64. {
  65. if(SysDate.day>28)
  66. SysDate.day=1;
  67. }
  68. }
  69. }
  70. DS1302_DateSet(&SysDate);//设置时间
  71. }
  72. if(setn == 3)//设置日
  73. {
  74. SysDate.day ++;
  75. if((SysDate.mon==1)||(SysDate.mon==3)||(SysDate.mon==5)||(SysDate.mon==7)||(SysDate.mon==8)||(SysDate.mon==10)||(SysDate.mon==12))//大月
  76. {
  77. if(SysDate.day==32)//最大31天
  78. SysDate.day=1;//从1开始
  79. }
  80. else
  81. {
  82. if(SysDate.mon==2)//二月份
  83. {
  84. if(p_r==1)//闰年
  85. {
  86. if(SysDate.day==30)//最大29天
  87. SysDate.day=1;
  88. }
  89. else
  90. {
  91. if(SysDate.day==29)//最大28天
  92. SysDate.day=1;
  93. }
  94. }
  95. else
  96. {
  97. if(SysDate.day==31)//最大30天
  98. SysDate.day=1;
  99. }
  100. }
  101. DS1302_DateSet(&SysDate);//设置时间
  102. }
  103. if(setn == 4)//设置星期
  104. {
  105. SysDate.week ++;
  106. if(SysDate.week == 8)SysDate.week=1;
  107. DS1302_DateSet(&SysDate);//设置时间
  108. }
  109. if(setn == 5)//设置时
  110. {
  111. SysDate.hour ++;
  112. if(SysDate.hour == 24)SysDate.hour=0;
  113. DS1302_DateSet(&SysDate);//设置时间
  114. }
  115. if(setn == 6)//设置分
  116. {
  117. SysDate.min ++;
  118. if(SysDate.min == 60)SysDate.min=0;
  119. DS1302_DateSet(&SysDate);//设置时间
  120. }
  121. if(setn == 7)//设置秒
  122. {
  123. SysDate.sec ++;
  124. if(SysDate.sec == 60)SysDate.sec=0;
  125. DS1302_DateSet(&SysDate);//设置时间
  126. }
  127. if((setn==8)&&(xinlvMax-xinlvMin>1))xinlvMin++;
  128. if((setn==9)&&(xinlvMax<300))xinlvMax++;
  129. if((setn==10)&&(tempMax-tempMin>1))tempMin++;
  130. if((setn==11)&&(tempMax<999))tempMax++;
  131. DisplaySetValue();
  132. }
  133. if(keynum==3)//减
  134. {
  135. if(setn == 1)//设置年
  136. {
  137. if(SysDate.year == 0)SysDate.year=100;
  138. SysDate.year --;
  139. DS1302_DateSet(&SysDate);
  140. }
  141. if(setn == 2)//设置月
  142. {
  143. if(SysDate.mon == 1)SysDate.mon=13;
  144. SysDate.mon --;
  145. if((SysDate.mon==4)||(SysDate.mon==6)||(SysDate.mon==9)||(SysDate.mon==11))
  146. {
  147. if(SysDate.day>30)
  148. SysDate.day=1;
  149. }
  150. else
  151. {
  152. if(SysDate.mon==2)
  153. {
  154. if(p_r==1)
  155. {
  156. if(SysDate.day>29)
  157. SysDate.day=1;
  158. }
  159. else
  160. {
  161. if(SysDate.day>28)
  162. SysDate.day=1;
  163. }
  164. }
  165. }
  166. DS1302_DateSet(&SysDate);
  167. }
  168. if(setn == 3)//设置日
  169. {
  170. SysDate.day --;
  171. if((SysDate.mon==1)||(SysDate.mon==3)||(SysDate.mon==5)||(SysDate.mon==7)||(SysDate.mon==8)||(SysDate.mon==10)||(SysDate.mon==12))
  172. {
  173. if(SysDate.day==0)
  174. SysDate.day=31;
  175. }
  176. else
  177. {
  178. if(SysDate.mon==2)
  179. {
  180. if(p_r==1)
  181. {
  182. if(SysDate.day==0)
  183. SysDate.day=29;
  184. }
  185. else
  186. {
  187. if(SysDate.day==0)
  188. SysDate.day=28;
  189. }
  190. }
  191. else
  192. {
  193. if(SysDate.day==0)
  194. SysDate.day=30;
  195. }
  196. }
  197. DS1302_DateSet(&SysDate);
  198. }
  199. if(setn == 4)//设置星期
  200. {
  201. if(SysDate.week == 1)SysDate.week=8;
  202. SysDate.week --;
  203. DS1302_DateSet(&SysDate);
  204. }
  205. if(setn == 5)//设置时
  206. {
  207. if(SysDate.hour == 0)SysDate.hour=24;
  208. SysDate.hour --;
  209. DS1302_DateSet(&SysDate);
  210. }
  211. if(setn == 6)//设置分
  212. {
  213. if(SysDate.min == 0)SysDate.min=60;
  214. SysDate.min --;
  215. DS1302_DateSet(&SysDate);
  216. }
  217. if(setn == 7)//设置秒
  218. {
  219. if(SysDate.sec == 0)SysDate.sec=60;
  220. SysDate.sec --;
  221. DS1302_DateSet(&SysDate);
  222. }
  223. if((setn==8)&&(xinlvMin>0))xinlvMin--;
  224. if((setn==9)&&(xinlvMax-xinlvMin>1))xinlvMax--;
  225. if((setn==10)&&(tempMin>0))tempMin--;
  226. if((setn==11)&&(tempMax-tempMin>1))tempMax--;
  227. DisplaySetValue();
  228. }
  229. if(keynum==4)//步数清零
  230. {
  231. bushu = 0;
  232. Store_Data();//存储数据
  233. }
  234. }

总结 :

        采用STM32为主控,通过串口的方式将体温检测、血氧心率检测、运动步数的记录;显示采用I2C通信的OLED屏幕,可显示血氧等数据,各项指标基本完成。即包括年月日在内的时间显示、用户体温、血氧心率检测及步数显示。虽然满足了初始要求,但仍有不足。例如测得血氧数据时常不稳定以及无测试对象时,仍有数据显示。系统虽然满足了题目的要求,但其中人有一些不足,令人无法释怀,例如测得血氧数据时常不稳定以及无测试对象时,仍有数据显示。此外,由于时间原因,未能将其做成一款可穿戴产品,希望在未来可做进一步改进。谢谢观看!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/421313
推荐阅读
相关标签
  

闽ICP备14008679号