1 前言
% P, r' [$ S* B d4 ^( yBMD101传感器是神念科技开发的心电传感器。目前有一些商家开发了BMD101传感器模块,基本上都是利用蓝牙直接传输数据到PC或手机端。而本文介绍通过串口将数据传送给单片机,并在单片机上完成数据解析和心电波形LCD显示的功能。7 U! c2 {! I' S! g$ L' w2 e
0 A$ r$ t/ G8 }7 Y" g
1 Q4 F# a/ [% e7 a0 o2 L) s2 通信协议解读
" I3 p# B: }' @BMD101 通过 UART 接口通信。这是一个标准的 UART 接口,定义为 1 个起始位,8 个数据位,1 停止位格式,波特率 57600。2 i( t* c: L) [+ [$ G# ~8 C
串口输出的数据包格式如下:
( Z4 f) i- Z4 j, S0 a
) F* c0 i; N2 v2 K
8 D/ `( H9 z- r( h' e
9 |- a. G8 T, X% z' R* z包括Header(帧头)、data payload(数据有效载荷)、CRC校验字节三个部分。6 d' k/ M' i7 \
# Q! p8 J; W. m
: n2 R e- l4 q3 q帧头包括两个SYNC字节(其值均为0xAA),用于指示一帧数据的开始。帧头的plength字节用于指示数据有效载荷部分的字节数。它的值可能是0-169的某一值。/ _( U* L, {9 B. P/ d+ l
2 T/ l1 s9 \7 x Y+ u& J$ a
5 C: f' i& ]+ V8 CData Payload 是由一系列连续的 DataRow 组成。分析 Data Payload 涉及到解析每个 DataRow。DataRow的组成如下:
2 d: t/ @$ g3 C; o* o" f
7 D) F# H5 O6 q: y7 F" F) S1 n! w3 l7 ~- J- T9 e+ ~
DataRow 起始位可能有零个或多个[EXCODE](扩展代码)字节,这些字节的值均为 0x55。EXCODE 字节数表示 Extended Code Level。Extended Code Level 是用来与- 字节一起确定DataRow的数据表示的是什么方面的信息。 如下表所示,例如:Extended Code Level为0和[CODE]=0x02决定了当前DataRow表示的是信号质量的信息.[/size][/font][font=微软雅黑][size=3]
[/size][/font]4 a, n( [9 G+ ^+ H' \; k6 I
- [font=微软雅黑][size=3]如果[CODE]字节在 0x00 和 0x7F 之间,那么DataRow就没有[LENGTH]字节,紧跟着[CODE]的是一个字节的[DATA]值,然后 DataRow 结束。[/size][/font]1 x2 e6 S* J9 ], x7 `9 U) d% Q
- [font=微软雅黑][size=3]( _9 o) O9 H1 A9 Q4 G
- [/size][/font]
6 `% b( B" u: J+ `2 @ - [font=微软雅黑][size=3]如果[CODE]字节的值在 0x80 和 0xFF 之间,那么紧跟着是[LENGTH],它表示[DATA]的字节数。[/size][/font] t4 L- \- T/ Y: W2 V: Q
- [font=微软雅黑][size=3]
7 e% J1 v) w! S) V - [/size][/font]
$ @; p+ \" ^' x" w& [ V, o4 _) D - [font=微软雅黑][size=3]通常我们从BMD101接收到的DataRow主要有三种:[CODE]=0x02,即信号质量数据,信号质量是一个介于0-200之间的数据,数值越大表示传感器采集到的信号质量越好,等于0时可能是因为电极与人体接触不良。[/size][/font]
1 Z/ y! Y. s. }+ `, ? - [font=微软雅黑][size=3]( y4 S, s0 q7 j; j' j- y
- [/size][/font]
) S6 E4 S9 W1 ]. q2 @1 q - [font=微软雅黑][size=3][CODE]=0x03,即实时的心率数据,用一个字节表示。这也是BMD101的方便之处,开发者不必再通过算法求出心率,只需通过读取串口发送的心率数据即可,心率数据一般每秒发送一次,但在信号质量不佳时仍然会输出心率数据,因此在使用心率数据前应该验证信号质量是否过差。[/size][/font]
" v+ G0 @% Q2 d# s& m. o! d/ C - [font=微软雅黑][size=3]
0 }2 n7 `2 C8 g) O+ Z - [/size][/font]
- ^ c ~! D C1 I% L$ m - [font=微软雅黑][size=3][CODE]=0x80时,输出的数据[DATA]表示原始的心电波形数据,每一个数据由16位补码组成,其值范围为-32768到32767之间。这16位数据的第一个字节是高8位字节,第二个字节是低8位,为了通过这两个字节还原心电波形数据值,可以通过以下代码完成:[/size][/font]" O5 ?/ U2 _! G
- [font=微软雅黑][size=3]short raw =(Value[0]<<8) | Value[1];[/size][/font]
( t( d) ]. L3 }0 Z* j - [font=微软雅黑][size=3][DATA]可能包含了很多个心电数据,因此可能由很多个字节组成,通常BMD101每秒输出512个原始心电波形数据,即采样率为512Hz。[/size][/font]
; t/ f5 V' t7 V4 }. }" \ - [font=微软雅黑][size=3]
$ E7 v" a! c8 Q3 j - [/size][/font]
& g- p4 J) A9 Y \$ W# f2 M! Y2 x - [font=微软雅黑][size=3]数据包解析步骤[/size][/font]
# ]1 U; Z# o3 D5 b' d - [font=微软雅黑][size=3]从数据流中连续读取字节,直到[SYNC]字节(0xAA)时。[/size][/font]7 `$ A8 A; N9 Z
- [font=微软雅黑][size=3]读取下一个字节,并确保它也是一个[SYNC]字节(如果没有,返回步骤 1)。[/size][/font]4 J! F6 @/ x! F
- [font=微软雅黑][size=3]读[PLENGTH]字节。[/size][/font]
% f2 _0 [& x4 d - [font=微软雅黑][size=3]从[PAYLOAD…]中读取下一个 PLENGTH 字节,把它们保存在一个存储区域(如[/size][/font]
% _" c; V+ j- n8 n - [font=微软雅黑][size=3]unsigned char payload [ 256 ]数组)。按照接收的顺序累加每一个字节到校验器中。[/size][/font]
$ k5 c0 y7 I* a$ ^- o0 N - [font=微软雅黑][size=3]使校验器中的低 8 位取反。这里是 C 代码:[/size][/font]
) ^ s) D6 U* X( d$ G @8 u% D* s3 A - [font=微软雅黑][size=3]checksum &= 0xff;[/size][/font]- g" m3 S k: b
- [font=微软雅黑][size=3]checksum = ~ checksum & 0xff;[/size][/font], n {2 m; ]4 I9 q
- [font=微软雅黑][size=3]读取[CRC]字节并验证它是否符合你的计算校验和(如果没有,返回到步骤 1)。[/size][/font]- l) a- N5 M0 W9 V! ^' f. S, M
- [font=微软雅黑][size=3]循环,直到 payload[]数组中所有字节(也就是 DataRows)被解析:[/size][/font]
& ~* r( w# C6 c. [7 ? - [font=微软雅黑][size=3]a)解析和计算[EXCODE]字节的数值(0x55),一般在当前 DataRow 的开始。[/size][/font]
& [% @5 f% m8 a# N$ v4 G6 p' { - [font=微软雅黑][size=3]b)解析当前 DataRow 中的[CODE]字节数据。[/size][/font]
( R) l" d( V- |' T - [font=微软雅黑][size=3]c)如果适用,解析当前 DataRow 中的[LENGTH]字节数据。[/size][/font]
O% {1 f6 o" E2 {% c, R - [font=微软雅黑][size=3]d)分析和处理 当前 DataRow 中的[DATA…]字节(或数组),基于 DataRow 的 [EXCODE]等级、[CODE]和[LENGTH]。 [/size][/font]! J2 i4 J- @0 I) P ?6 q
- [font=微软雅黑][size=3]e)如果不是所有的字节都从 payload []数组中解析完成,返回 a)解析下一个DataRow[/size][/font]6 p7 ~$ s4 @6 d4 h+ t0 S9 p0 f
- [font=微软雅黑][size=3]
( d1 p( E$ [3 Y* f4 U - [/size][/font]/ {9 l5 ]4 A) i% g1 \
- [font=微软雅黑][size=3]3 STM32实现数据包解析[/size][/font]
" w6 A( ^' S0 R' N l# l* c - [font=微软雅黑][size=3]配置STM32串口为中断接收方式,在中断服务函数中,完成数据包的接收与保存工作,代码如下:[/size][/font]
! l2 u* \7 J$ I( R" G) B1 _+ P1 z
8 w7 q6 w$ d. g- % Y, o5 Z4 ], r" O( i
- [font=微软雅黑][size=3][code]<blockquote>u8 Uart2_Buffer[256]; //接收缓冲区
复制代码 下面给出数据解析的函数:( j. _# O' O* a3 ], _+ }- \
- <blockquote>void parse_payload(void) //对有效负载的内容作分析处理
复制代码 LCD显示用的是正点原子Mini板,以及相应的LCD库函数。
; R& t$ b* u |3 W! L( L
+ m3 U- ]6 S% {8 x. Z9 l o实现效果如下:
. v8 E" C0 n. t
2 j6 ]5 K( d, D1 L+ m& W0 \
& C+ j) b* S' l- |9 E- X
: N3 Y4 Q, \8 z: u+ |由于没有滤波处理,BMD101的输出信号噪声还是比较多的。另外BMD101对噪声十分敏感,传感器的串口不应该直接与开发板的串口通过杜邦线直接连接,否则波形失真严重。可以在两者之间增加一个数字隔离模块。( {6 r- @# Y+ f; d* R7 {$ [
( s/ ~9 W' S/ Q, }9 j
; q. t0 w( F) x7 b* F' Q- G B
) k/ S0 W ?7 b& Y) m5 ~/ n; L2 N/ L0 T4 l# n& |2 L0 b
5 ]3 `4 w4 q M" x |