描述
<div>
<p style="line-height: 2;"><span style="font-size: 18px;">1. 参赛者姓名:Mark</span></p>
<p style="line-height: 2;"><span style="font-size: 18px;">2. 参赛作品的名字:微型体测仪</span></p>
</div>
<div>
<p style="line-height: 2;"><span style="font-size: 18px;">3. 拟用到的立创商城在售物料:ESP8266、STM32、PL2303、LCD12864、LM1117等</span></p>
<p style="line-height: 2;"><span style="font-size: 18px;">4. 拟用到的非立创商城物料或其它补充:电阻电容等小元件</span></p>
<p style="line-height: 2;"><span style="font-size: 18px;">5. 拟用到的EDA工具软件名称(必填项):立创EDA / EasyEDA</span></p>
<p style="line-height: 2;"><span style="font-size: 18px;">6. 原文链接:<a href="http://club.szlcsc.com/article/details_16043_1.html" target="_blank">http://club.szlcsc.com/article/details_16043_1.html</a></span></p>
<p style="line-height: 2;"><span style="font-size: 18px;">7. 原工程链接:<a href="/markone/hehe" target="_blank">https://oshwhub.com/markone/hehe</a></span></p>
<p> </p>
</div>
<h3>一、作品简介</h3>
<div style="line-height: 2;"><span style="font-size: 18px;"> 随着生活质量的提高和生活节奏的加快,人们愈加需要关注自己的健康状况,本项目意在设计一种基于云平台+APP+设备端的身体参数测试系统,利用脉搏传感器、红外传感器、微弱信号检测电路等实现人体参数的采集,数据通过无线网或其他方式上传云端存储,并提供网页端交互界面,为用户构建一种人体参数管理平台。</span></div>
<div style="line-height: 2;">
<table style="border-collapse: collapse; width: 81.5207%; height: 229px; margin-left: auto; margin-right: auto;">
<tr style="height: 229px;">
<td style="width: 50.1542%; height: 229px;"><img style="float: right;" src="//image.lceda.cn/pullimage/ee8YhHLmZAAhRaYB8Ac1QoEkKrVdahV1xfeg4WDZ.png" alt="" width="364" height="203"></td>
<td style="width: 52.5238%; height: 229px;"><img style="float: left;" src="//image.lceda.cn/pullimage/2N641IzuwAxJS4gDRfI4xL67DTGyYp1zMalMu987.png" alt="" width="370" height="208"></td>
</tr>
</table>
<img src="//" alt=""></div>
<div>
<div> </div>
</div>
<div><img src="//" alt=""></div>
<h3>二、系统构架图</h3>
<div style="line-height: 2;"><span style="font-size: 18px;"> 系统的硬件部分由MCU、心率传感器、温度传感器、电源、蓝牙模块、WiFi模块构成。软件部分包括基于C语言的stm32程序设计、上位机软件设计、服务器端网页及后台程序设计、APP软件设计。硬件架构和软件架构分别如下图所示。</span></div>
<div>
<div> </div>
</div>
<div><img src="//" alt=""></div>
<div>
<div><img src="//image.lceda.cn/pullimage/1sAgxNBf48o5k6Rw4YSwYTZjD5hqVDXU1GHjCT8U.png" alt="" width="481" height="202"></div>
<div><img src="//image.lceda.cn/pullimage/n6x1S5qaouHTZ5kBKFqyz8DBZzTtpkPs6Apq1vrh.png" alt="" width="475" height="447"></div>
<div> </div>
<div> </div>
</div>
<div style="line-height: 1.8;"><span style="font-size: 18px;"> 系统工作时,通过心率传感器和温度传感器分别采集心率和体温的原始AD信号,通过相应算法计算得出心率与温度值,通过WiFi上传到云服务器端。云服务器端对传输的数据进行处理、存储、分析,同时也通过对数据的分析进行预警和提示,用户只需登录到相应网页或手机APP即可查看自己或家人的实时与历史数据,另外本系统也提供了定位功能,用户可在地图上查看测量终端所在的位置。</span></div>
<div style="line-height: 1.8;"><span style="font-size: 18px;"><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/D55BWc77LKa4RH3qAIDcA7SCPAmguu5LRA6RjG69.png" alt="" width="622" height="566"><br></span></div>
<div>
<div> </div>
</div>
<div><img src="//" alt=""></div>
<h3>三、硬件部分的描述</h3>
<p>设计软件:立创EDA</p>
<p>设计链接:https://lceda.cn/markone/hehe<br>原理图:</p>
<p><img style="float: left;" src="//image.lceda.cn/pullimage/1LsBXOLoBRiEELWbkQQTw4RK3JZ9XrQWvT21Jn4m.png" alt="" width="865" height="606"></p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>PCB:</p>
<p><img style="float: left;" src="//image.lceda.cn/pullimage/jrqaDAhYfbXkLVVcFNSOj2YgE3XJLDL34TVxlVlV.png" alt="" width="295" height="128"></p>
<div><img src="//" alt=""></div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">1.MCU系统电路 </span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 本系统采用STM32103C8T6,其作为主控芯片一方面对传感器数据进行采集,另一方面将数据通过算法进行处理,并转发到云服务器,因此在电路设计时将两个ADC接口接入传感器。对于STM32系统,其必要组成部分还包括了启动模式选择电路、晶振复位电路等,在设计时还我另外加入了指示灯与按键作为备用。STM32系统电路如图4所示。STM32的供电电压以及心率、温度传感器的电压都是3.3V,因此如果采用5V电压供电则还需要进行电压转换,本系统采用了LDO稳压器LM1117将5V转为3.3V。对于电源和开关的部分,系统采用MICO USB接口进行供电和下载程序,该部分电路如图所示。</span></div>
<div style="line-height: 2;"><img style="float: left;" src="//image.lceda.cn/pullimage/xBuFcixvaO0QwMdPy8MUv1RrLSxYbF4NZgi8531v.png" alt="" width="648" height="504"></div>
<div style="line-height: 2;"><span style="font-size: 18px;"><img src="//" alt=""></span></div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"> </div>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">2.USB转串口电路</span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 利用USB作为系统程序下载接口,需要对其电平进行转换才能与STM32的串口进行通信,本系统采用了CP2102作为转换芯片,CP2102集成度高,内置USB2.0全速功能控制器、USB收发器、晶体振荡器、EEPROM及异步串行数据总线(UART),支持调制解调器全功能信号,无需任何外部的USB器件。CP2102与其他USB-UART转接电路的工作原理类似,通过驱动程序将PC的USB口虚拟成COM口以达到扩展的目的。该部分的电路设计图如图所示。</span></div>
<div style="line-height: 2;"><img src="//image.lceda.cn/pullimage/SCWAuQvVE6ADGCDBG0IPARzzEZrZGdV5dwREYzxq.png" alt="" width="336" height="470"></div>
<div style="line-height: 2;"><span style="font-size: 18px;"><img src="//" alt=""></span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"><strong> 3.WiFi模块 </strong> </span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> WiFi模块采用了ESP8266模块,当使用该模块时需要设计其外部电路,包括电源电路、复位电路、模式选择电路等部分,设计完成的电路图如图所示。</span></div>
<div>
<div style="line-height: 2;"><img src="//image.lceda.cn/pullimage/APXNy4WnMtGNCWJ2joBo4wMfSIIAUks9S0c0Z11T.png" alt="" width="448" height="406"></div>
</div>
<div><img src="//" alt=""></div>
<h3>四、材料清单(BOM列表):</h3>
<p> </p>
<div><img src="//image.lceda.cn/pullimage/mv748ZrbbsbLJqe8QFAe6EX1nUB07mApDYTEIznq.png" alt="" width="306" height="450"></div>
<div> </div>
<div><img src="//" alt=""></div>
<h3>五、软件部分的描述</h3>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">1.主芯片程序设计</span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> STM32的程序设计基于RT-Thread行开发。系统初始化之外,在主程序中,完成如下功能:</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 1)通过内部AD接口对传感器的AD数据进行采集;</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 2)将数据通过算法进行处理;</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 3)将处理好的数据打包提供WiFi模块发送给服务器;</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 4)喂狗。按照以上4点功能进行设计,程序工作流程图如图所示。</span></div>
<div style="line-height: 2;"><img src="//image.lceda.cn/pullimage/jXUZHLsItE9LyUgEhM57lk8zQOesML9OnPaPJKl5.png" alt="" width="286" height="536"></div>
<div style="line-height: 2;"><span style="font-size: 18px;"><img src="//" alt=""></span></div>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">2.心率采集算法</span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 心率采集算法的目标是找到瞬间心跳的连续时刻,并测量两者之间的时间间隔(IBI)。通过遵循PPG波形的可预测的形状和模式,我们能够做到这一点。当心脏将血液泵入人体时,每次搏动都会有一个脉冲波(有点像冲击波)沿着所有的动脉传到脉搏传感器附着的毛细血管组织的末端。实际的血液循环比脉搏波传播慢得多。从下图所示的PPG上的T点开始跟踪事件。当脉搏波在传感器下方通过时,信号值迅速上升,然后信号回落到正常点。有时候,双向切口(向下尖峰)比其他更明显,但通常信号在下一个脉冲波冲洗之前稳定到背景噪声。由于波浪是重复的和可预测的,可以选择几乎任何可识别的特征作为参考点,比如峰值,并通过在每个峰值之间的时间计算心率。然而,这可能会从二分的切口中错误地读取,并且对基线噪声可能也是不准确的。理想情况下,想要找到心脏跳动的瞬间时刻需要准确的BPM计算,心率变异性(HRV)研究和脉搏传递时间(PTT)测量。</span></div>
<div style="line-height: 2;"><img src="//image.lceda.cn/pullimage/NClfZlLbzBN105nQiE79V6NCWWnFv6BLqUFOWIHf.png" alt="" width="266" height="174"></div>
<div style="line-height: 2;"><span style="font-size: 18px;"><img src="//" alt=""></span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 通过使用定时器中断,我们的节拍查找算法在后台运行,并自动更新变量值。整体的算法流程图如图所示</span></div>
<div style="line-height: 2;"><img src="//image.lceda.cn/pullimage/K5duucno3EEP8JLU5hFThw1QXvVPsMF0Lq1rdEK8.png" alt="" width="590" height="884"></div>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">3.服务器软件与网页设计</span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 服务器端采用阿里云提供的云服务器,其数据传输协议是MQTT协议,测量采集端作为MQTT的设备端,云服务器作为MQTT的服务端,接收的数据存入SQL并通过网页展示,MQTT协议数据传输流程如图所示。</span></div>
<div style="line-height: 2;"><img src="//image.lceda.cn/pullimage/jBoodT71E8WcEBlwR66AQXmQj5xAFqx6DUtELPt8.png" alt="" width="314" height="174"></div>
<div style="line-height: 2;"><span style="font-size: 18px;"><img src="//" alt=""></span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 设计完成的主数据界面如下图</span></div>
<div style="line-height: 2;"><img src="//image.lceda.cn/pullimage/g1vvY9tfn5UVBnvAheHyUOHZyiswhoalJhON22XH.png" alt="" width="554" height="280"></div>
<div style="line-height: 2;"><span style="font-size: 18px;"><img src="//" alt=""></span></div>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">4.APP软件设计</span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 移动终端APP第一次打开后进行手动配网,当搜索到指定的WIFI信号时进行连接,随后对TCP端口进行监听,对接受的数据包进行解析,随后将数据显示在屏幕上。设计完成的APP如下图。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> <img src="//image.lceda.cn/pullimage/cHpVGEtlflE9xof5UcrLA7aqlf4Zw5WgOf8ArzdF.png" alt="" width="194" height="334"><br></span><span style="font-size: 18px;"><img src="//" alt=""></span></div>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">5.上位机软件设计</span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 上位机软件基于JAVA进行设计,通过端口接收测量终端传输的数据包,并进行解析,通过图形形象地展示出心率的实时状态,其工作界面如图所示。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> <img src="//image.lceda.cn/pullimage/1UFIbDtqo0re9Q2gDqoTnGQav1oivjyRrOwrEFbW.png" alt="" width="386" height="326"><br></span></div>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">6.RT-Thread移植简介</span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 本部分简单介绍了本系统中使用OLED和WIFI模块所涉及的SPI和串口通信在RTT中的使用过程,对函数的调用过程、关键函数的使用、设备驱动的调用分别进行了一些介绍。</span></div>
<div style="line-height: 2;"><strong><span style="font-size: 18px;">6.1 OLED</span></strong></div>
<div style="line-height: 2;"><span style="font-size: 18px;">OLED与芯片的通过SPI协议通信,设备驱动使用流程大致如下:</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">(1)定义设备对象,调用 rt_spi_bus_attach_device() 挂载设备到SPI总线</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">此函数用于挂载一个SPI设备到指定的SPI总线,向内核注册SPI设备,并将user_data保存到SPI设备device里。</span></div>
<div style="line-height: 2;">
<table style="border-collapse: collapse; margin-left: auto; margin-right: auto;">
<tr>
<td width="259">
<p align="center"><span style="font-size: 18px;">参数</span></p>
</td>
<td width="259">
<p align="center"><span style="font-size: 18px;">描述</span></p>
</td>
</tr>
<tr>
<td width="259">
<p align="center"><span style="font-size: 18px;">device</span></p>
</td>
<td width="259">
<p align="center"><span style="font-size: 18px;">SPI设备句柄</span></p>
</td>
</tr>
<tr>
<td width="259">
<p align="center"><span style="font-size: 18px;">name</span></p>
</td>
<td width="259">
<p align="center"><span style="font-size: 18px;">SPI设备名称</span></p>
</td>
</tr>
<tr>
<td width="259">
<p align="center"><span style="font-size: 18px;">bus_name</span></p>
</td>
<td width="259">
<p align="center"><span style="font-size: 18px;">SPI总线名称</span></p>
</td>
</tr>
<tr>
<td width="259">
<p align="center"><span style="font-size: 18px;">user_data</span></p>
</td>
<td width="259">
<p align="center"><span style="font-size: 18px;">用户数据指针</span></p>
</td>
</tr>
</table>
</div>
<div style="line-height: 2;"><span style="font-size: 18px;">a. 首先需要定义好SPI设备对象device</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">b. SPI总线命名原则为spix, SPI设备命名原则为spixy,本项目的spi10 表示挂载在在 spi1设备。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">c. SPI总线名称可以在msh shell输入list_device 命令查看,确定SPI设备要挂载的SPI总线。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">d. user_data一般为SPI设备的CS引脚指针,进行数据传输时SPI控制器会操作此引脚进行片选。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">本项目的底层驱动 drv_ssd1306.c 中 rt_hw_ssd1306_config() 挂载ssd1306设备到SPI总线源码如下:</span></div>
<div style="line-height: 2;">
<table style="border-collapse: collapse; width: 97.3266%;">
<tr>
<td style="width: 98.7313%;">
<p><span style="font-size: 16px;">static int rt_hw_ssd1306_config(void) </span></p>
<p><span style="font-size: 16px;">{</span></p>
<p><span style="font-size: 16px;"> rt_err_t res; </span></p>
<p><span style="font-size: 16px;"> /* oled use PC8 as CS */</span></p>
<p><span style="font-size: 16px;"> spi_cs.pin = CS_PIN; </span></p>
<p><span style="font-size: 16px;"> rt_pin_mode(spi_cs.pin, PIN_MODE_OUTPUT); /* 设置片选管脚模式为输出 */ </span></p>
<p><span style="font-size: 16px;"> res=rt_spi_bus_attach_device(&spi_dev_ssd1306,SPI_SSD1306_DEVICE_NAME, SPI_BUS_NAME,</span><span style="font-size: 16px;">(void*)&spi_cs);</span></p>
<p><span style="font-size: 16px;"> if (res != RT_EOK) </span></p>
<p><span style="font-size: 16px;"> { </span></p>
<p><span style="font-size: 16px;"> OLED_TRACE("rt_spi_bus_attach_device!\r\n");</span></p>
<p><span style="font-size: 16px;"> return res; </span></p>
<p><span style="font-size: 16px;"> } </span></p>
<p><span style="font-size: 16px;">}</span></p>
</td>
</tr>
</table>
</div>
<div style="line-height: 2;"><span style="font-size: 18px;">(2)调用 rt_spi_configure() 配置SPI总线模式。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">挂载SPI设备到SPI总线后,为满足不同设备的时钟、数据宽度等要求,通常需要配置SPI模式、频率参数SPI从设备的模式决定主设备的模式,所以SPI主设备的模式必须和从设备一样两者才能正常通讯。</span></div>
<div><span style="font-size: 18px;">rt_err_t rt_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)</span></div>
<div>
<table style="border-collapse: collapse; margin-left: auto; margin-right: auto;">
<tr>
<td width="277">
<p><span style="font-size: 18px;">参数</span></p>
</td>
<td width="277">
<p><span style="font-size: 18px;">描述</span></p>
</td>
</tr>
<tr>
<td width="277">
<p><span style="font-size: 18px;">device</span></p>
</td>
<td width="277">
<p><span style="font-size: 18px;">SPI设备句柄</span></p>
</td>
</tr>
<tr>
<td width="277">
<p><span style="font-size: 18px;">cfg</span></p>
</td>
<td width="277">
<p><span style="font-size: 18px;">SPI传输配置参数指针</span></p>
</td>
</tr>
</table>
</div>
<div style="line-height: 2;"><span style="font-size: 18px;">此函数会保存cfg指向的模式参数到device里,当device调用数据传输函数时都会使用此配置信息。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">挂载SPI设备到SPI总线后必须使用此函数配置SPI设备的传输参数。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">本项目底层驱动 drv_ssd1306.c 中 rt_hw_ssd1306_config() 配置SPI传输参数源码如下:</span></div>
<table style="border-collapse: collapse; width: 97.3266%; height: 245px;">
<tr style="height: 245px;">
<td style="width: 98.7313%; line-height: 1.5; height: 245px;">
<p><span style="font-size: 16px;">static int rt_hw_ssd1306_config(void)</span></p>
<p><span style="font-size: 16px;">{</span></p>
<p><span style="font-size: 16px;"> /* config spi */</span></p>
<p><span style="font-size: 16px;"> {</span></p>
<p><span style="font-size: 16px;"> struct rt_spi_configuration cfg; </span></p>
<p><span style="font-size: 16px;"> cfg.data_width = 8;</span></p>
<p><span style="font-size: 16px;"> cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;</span></p>
<p><span style="font-size: 16px;"> cfg.max_hz = 20 * 1000 *1000; /* 20M,SPI max 42MHz,ssd1306 4-wire spi */</span></p>
<p><span style="font-size: 16px;"> rt_spi_configure(&spi_dev_ssd1306, &cfg); </span></p>
<p><span style="font-size: 16px;"> }<br>}</span></p>
</td>
</tr>
</table>
<div style="line-height: 2;"><span style="font-size: 18px;">(3)使用 rt_spi_transfer() 等相关数据传输接口传输数据。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">SPI设备挂载到SPI总线并配置好相关SPI传输参数后就可以调用RT-Thread提供的一系列SPI设备驱动数据传输函数。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">此函数可以传输一连串消息,用户可以很灵活的设置message结构体各参数的数值,从而可以很方便的控制数据传输方式。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">(4)通过设备驱动的调用在OLED上显示图像和文字,首先需要确定信息在OLED上的行列起始地址,调用ssd1306_write_cmd() 向SSD1306发送指令,调用 ssd1306_write_data() 向SSD1306发送数据,源代码如下:</span></div>
<table style="border-collapse: collapse; width: 97.3266%;">
<tr>
<td style="width: 98.7313%; line-height: 1.5;">
<p><span style="font-size: 18px;"><code></code><span style="font-size: 16px;">void set_column_address(rt_uint8_t start_address, rt_uint8_t end_address)</span></span><br><span style="font-size: 16px;">{</span><br><span style="font-size: 16px;"> ssd1306_write_cmd(0x15); // Set Column Address</span></p>
<p><span style="font-size: 16px;"> ssd1306_write_data(start_address); // Default => 0x00 (Start Address)</span></p>
<p><span style="font-size: 16px;"> ssd1306_write_data(end_address); // Default => 0x7F (End Address) </span></p>
<p><span style="font-size: 16px;">} </span></p>
<p><span style="font-size: 16px;"> void set_row_address(rt_uint8_t start_address, rt_uint8_t end_address) </span></p>
<p><span style="font-size: 16px;">{<br> ssd1306_write_cmd(0x75); // Set Row Address</span></p>
<p><span style="font-size: 16px;"> ssd1306_write_data(start_address); // Default => 0x00 (Start Address) </span></p>
<p><span style="font-size: 16px;"> ssd1306_write_data(end_address); // Default => 0x7F (End Address)</span></p>
<p><span style="font-size: 16px;">}</span></p>
</td>
</tr>
</table>
<div style="line-height: 2;"><span style="font-size: 18px;"><strong>6.2串口</strong></span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">串口用来与WIFI 模块ESP8266进行通信,在串口的使用过程中,主要使用了以下几个函数进行初始化:</span></div>
<table style="border-collapse: collapse; width: 97.3266%;">
<tr>
<td style="width: 98.7313%; line-height: 1.5;"><span style="font-size: 18px;">static void RCC_Configuration(void);</span><br><span style="font-size: 18px;">static void GPIO_Configuration(void);</span><br><span style="font-size: 18px;">static void NVIC_Configuration(struct stm32_uart *uart);</span><br><span style="font-size: 18px;">void rt_hw_usart_init();</span></td>
</tr>
</table>
<div style="line-height: 2;"><span style="font-size: 18px;">(1)在void rt_hw_usart_init();中对波特率、串口号、字长等进行设置。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">实际的路径调用过程如下。</span></div>
<table style="border-collapse: collapse; width: 97.3266%;">
<tr>
<td style="width: 98.7313%; line-height: 1.5;">
<pre><span style="font-size: 18px;"><code></code> startup.c main()</span><br><span style="font-size: 18px;"> ---> startup.c rtthread_startup()</span><br><span style="font-size: 18px;"> ---> board.c rt_hw_board_init()<br></span><span style="font-size: 18px;"> ---> usart.c rt_hw_usart_init()</span></pre>
</td>
</tr>
</table>
<div style="line-height: 2;"><span style="font-size: 18px;">(2)为了设备纳入到RTT的IO设备层中,需要为这个设备创建一个名为rt_device的数据结构。该数据结构在rtdef.h中定义。需要一些函数来操作逻辑设备,这些函数在rt-thread/src/device.c文件中提供,它们是:</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_err_t rt_device_register(rt_device_t dev, const char *name, rt_uint16_t flags)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">将rt_device数据结构加入到RTT的设备层中,这个过程称为“注册”。RTT的设备管理层会为这个数据结构创建唯一的device_id。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_err_t rt_device_unregister(rt_device_t dev)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">与注册相反,自然是注销了,将某个设备从RTT的设备驱动层中移除。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_device_t rt_device_find(const char *name)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">根据设备的字符串名查找某个设备。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_err_t rt_device_init(rt_device_t dev)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">通过调用rt_device数据结构中的init函数来初始设备。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_err_t rt_device_init_all(void)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">初始化RTT设备管理层中的所有已注册的设备</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">通过调用rt_device数据结构中的open函数来打开设备。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_err_t rt_device_close(rt_device_t dev)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">通过调用rt_device数据结构中的close函数来关闭设备。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">通过调用rt_device数据结构中的read函数来从设备上读取数据。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">通过调用rt_device数据结构中的write函数来向设备写入数据(比如设备是flash,SD卡等,nand or nor flash等等)。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">(3)open,read等函数的编写过程如下。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">Ⅰ..init函数完成对设备数据结构的初始化工作。 RTT的设备驱动存在大量的预定义宏,它们在rtdef.h中定义。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">Ⅱ.open</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">因为在usart.c中已经初始usart设备,然后init中通过USART_Cmd语句后,串口就会开始工作。因此open函数设置为空即可</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">close同colse,之间置空即可</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">Ⅲ.read</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">static rt_size_t rt_serial_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">pos表示读写的位置,buffer是用于存储读取到数据的缓冲区。size为字节数目。对于USART这种串行的流设备来说,pos没有意义,因此这里的pos没有意义。 rt_device数据结构dev的的 user_data域存放了(struct stm32_serial_device*)型指针。</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;">Ⅳ.write</span></div>
<div style="line-height: 2;"><span style="font-size: 18px;"> 向串口写入数据,即发送数据。</span></div>
<table style="border-collapse: collapse; width: 97.3266%;">
<tr>
<td style="width: 98.7313%; line-height: 1.5;">
<pre><span style="font-size: 16px;"> /* polling mode */
if (dev->flag & RT_DEVICE_FLAG_STREAM)
{
/* stream mode */
while (size)
{
if (*ptr == '\n')
{
while (!(uart->uart_device->SR & USART_FLAG_TXE));
uart->uart_device->DR = '\r';
/* interrupt mode Tx, does not support */
RT_ASSERT(0);
} while (!(uart->uart_device->SR & USART_FLAG_TXE));
uart->uart_device->DR = (*ptr & 0x1FF); ++ptr; --size;
}
}
else
{
/* write data directly */
while (size)
{
while (!(uart->uart_device->SR & USART_FLAG_TXE));
uart->uart_device->DR = (*ptr & 0x1FF); ++ptr; --size;
}
}</span></pre>
</td>
</tr>
</table>
<div style="line-height: 2;"><span style="font-size: 18px;">V.注册USART的rt_device结构</span></div>
<table style="border-collapse: collapse; width: 97.3266%;">
<tr>
<td style="width: 98.7313%; line-height: 1.5;">
<pre><span style="font-size: 16px;">rt_err_t rt_hw_serial_register(rt_device_t device, const char* name, rt_uint32_t flag, struct stm32_serial_device *serial)
{
RT_ASSERT(device != RT_NULL); if ((flag & RT_DEVICE_FLAG_DMA_RX) ||
(flag & RT_DEVICE_FLAG_INT_TX))
{
RT_ASSERT(0);
} device->type = RT_Device_Class_Char;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
device->init = rt_serial_init;
device->open = rt_serial_open;
device->close = rt_serial_close;
device->read = rt_serial_read;
device->write = rt_serial_write;
device->control = rt_serial_control;
device->user_data = serial; /* register a character device */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | flag);
}</span></pre>
</td>
</tr>
</table>
<h3><strong>六、作品演示</strong></h3>
<div style="line-height: 2;"><span style="font-size: 18px;">腾讯视频:<a href="http://url.cn/5LXX7JF" target="_blank">http://url.cn/5LXX7JF</a></span></div>
<h3> <strong>七、总结</strong></h3>
<div style="line-height: 2;"> <span style="font-size: 18px;">本作品的设计实现了人体体征参数的实时测量、远程查看、云端存储、分析提示等功能,适用于各种场景,可作为家庭医疗助手、个人医护设备等;相比于传统设备成本低廉、易于携带,云端数据存储的功能既方便又快捷,可以进行长期数据监测,并进行健康数据分析。同时系统的兼容性十分强,对于其他智能设备,均可快速接入云服务;对于测量终端,可增加其他体征测量设备作为辅助参考依据。同时系统具有很多改进和升级的空间,如可以增加锂电池及其充放电电路实现自带供电系统,相信在今后的使用中会发现更多的不足,我将继续完善!</span></div>
<div style="line-height: 2;"> </div>
<h3 style="line-height: 2;"><strong><span style="font-size: 18px;">更多项目详情见链接:<a href="http://club.szlcsc.com/article/details_16043_1.html" target="_blank">http://club.szlcsc.com/article/details_16043_1.html</a></span></strong></h3>
<h3 style="line-height: 2;"><strong><span style="font-size: 18px;">本项目归立创社区“MarkOne”所有</span></strong></h3>
评论(0)