先说一下我想要实现的功能:- @, x# X) c% z4 c3 @1 Z
通过PC的串口助手发送一帧数据给STM32设备,STM32设备解析出某一位,若是1就点亮1个LED灯,若是0就把该LED灯熄灭。- _3 `$ V. F* s' R; V; N7 A; b; n
自定义的通信协议如下:
$ H1 d6 j, s1 rAA 01 01 02 BB5 Z, d6 u5 ^6 s
AA —— 帧头0 r9 D3 z# F6 f) S/ M
01 —— 地址
$ q; i- b& W; |+ B01 —— 控制字(是01就点亮LED,是00就熄灭LED)
- m& {1 a2 \. b- x1 Q) f/ r6 ?8 ^02 —— 校验和(地址+控制字)/ _$ T- b, f$ V6 [$ _
BB —— 帧尾8 d" K; r; y0 w' _, T4 W
' F8 y/ H8 g w3 i1 v3 g
今天我们主要是讨论一下自定义协议的问题,在这里我就默认你的STM32设备已经可以和电脑的串口助手可以通信上了。如果还没有实现这一功能,建议参考STM32FX开发指南(库函数版),下方我也会提供出源代码供大家参考。2 F6 H( S( N9 Q
因为我硬件采用的是STM32F407,大家可以根据自己的实际情况进行移植; k" U" R) W1 l$ n0 U& N
你准备好了吗?接下来跟着我的节奏开始.......
: F. i5 k, w/ N' k1 C第一步:替换中断服务函数;
' g) P2 ]. B; T& B把原来的中断服务函数用下面的函数代替(函数可以在usart.c文件中找到), y9 m! S5 Z/ n& j' O0 p
- void USART1_IRQHandler(void)
3 K0 y8 N% q$ j1 E3 Y8 |6 E - {
g# b) K$ A& J" ]! m' F" N7 u3 T5 W - u8 Res;//临时变量,存放串口接收的数据$ f# z7 d0 L4 [. K9 u$ V2 o
- ! i0 m6 ^/ c9 `5 p. { h, o, Q7 }
- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断* ], [9 M- D/ x T z3 W
- {. n9 i9 G8 r; k0 t
- Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
3 m; w) O7 ]5 \ - if(rx_stack.head_flag==1)//收到了帧头
# A% \4 _1 n: E; u1 ^# | - {9 A8 t; }9 L* \& p$ {# B0 l
- if(Res==0xBB)//判断当前值是不是帧尾
, @" ^* z2 G4 k7 q' l5 Y, E" t - {" d# [9 y. t& Z: \
- rx_stack.finish_flag = 1;/ \7 A+ X* h' Z1 t
- rx_stack.tail_flag=1;* L5 h" i) C; i: \& p4 x
- rx_stack.head_flag=0;
: z; f* `* \) Q5 Z - }/ ^9 P# e! [( K3 B7 p: @0 o3 V
- else! l9 w( P$ f% f' B9 m( w1 ~1 W
- {
) I# c0 q% a6 G7 } - rx_stack.recevie_data[rx_stack.data_pt] = Res;
; s. Z% \; ?8 T) ]( ?5 N7 k - rx_stack.data_pt++;
) K' A' ?1 C6 k$ y6 i; J - if(rx_stack.data_pt > 9)
% z! c6 M$ f# ~4 x# u - {) J, h3 y+ x; t& J
- rx_stack.data_pt = 0;# N% S" `; `' W5 }7 r
- }' P& P: z( V3 K# h5 ~8 d- w' ~
- } ) E# b; J9 \3 @) ?
- }
$ i# c0 G( r+ x. q - else//没有收到帧头
% D5 p! I& t8 v& N' U6 d9 M - {
w+ o5 r+ a& q# d - if(Res==rx_stack.head)* l6 u0 y `& S! k# a: B5 M3 v3 u
- rx_stack.head_flag=1;' p0 g: {7 }8 u9 K
- else% ~8 t+ Q+ g; Z' ~1 `
- {
/ ?- s* a9 Y6 A6 T* m - CommClr();) J1 Y1 Q! @9 t8 C* `" c
- return;
' H2 [; ~" @2 g+ m L - } 5 T r9 A8 g! K4 V: m, t- O+ @
- } % F: B2 ]; Y: n) ?. a: n, |
- }
: j0 r. R8 c0 e8 G1 \: e8 W! f; u - USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接受中断标志
2 h. i! v2 B, E; t% \ - }
复制代码 到这里你会发现,运行之后会出现很多错误,别慌别慌,问题不大,那是因为我的中断服务函数里面定义了一些原来没有的变量和函数;接着往下看.......' k- R4 \3 _3 ^# E
( ~ u/ L; I" Q6 K" b
第二步:添加自定义的函数- o1 p4 n' _/ ]
# P; \4 _' f6 @( y" P% u9 `- void rx_stack_init()" A9 y' t. w1 v
- {/ f; ^' T1 a4 H( k( A' E
- rx_stack.head = 0xAA; //协议栈头,起始位
& N( }5 C4 ^ N. u - rx_stack.addr=0x01; //从机地址9 b( }+ _/ g( _# ?& a
- memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零
1 P3 U& @6 ?( I# C2 l" u3 X - rx_stack.tail = 0xBB; //协议栈尾,结束位! l5 f/ ]; X& u4 u
- rx_stack.head_flag = 0;: Q2 e9 C* b: c' ]. U8 K. T
- rx_stack.tail_flag = 0;7 |2 r! {* ^) G w
- rx_stack.finish_flag = 0;
, @+ x* g# b$ k/ n - // rx_stack.lock_flag = 0;
_$ {0 X }5 G6 Q* P3 ]2 P c# z, P - rx_stack.data_pt = 0;* w& |: w! R6 g r
- }: n# V- s5 [5 n7 s2 {; f
- struct receive_stack rx_stack;% E9 k' J. w/ P& X
- . r: T# A6 }! v; p+ M
- void CommClr(void)
# y6 h+ ?. u4 A4 N - {
. |$ M9 p; o' W( d, r: d0 t8 q4 \2 @ - rx_stack.head_flag = 0;% h* f7 ]) D! |/ A( p6 J
- rx_stack.tail_flag = 0;
, v9 [3 w+ o9 T4 V/ s6 H6 d - rx_stack.finish_flag = 0;
$ A9 v& P9 l& l6 c - rx_stack.data_pt = 0;! d: @3 w# q* x& L1 S* \6 t1 ^) N
- ( V% [ x: J, X
- memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零
N7 c3 h S/ |: m$ Q! N - rx_stack.commPack_OK_flag=0;* d' b% f2 [& X
- }
复制代码
9 K( `6 C7 B6 p& G0 p/ K! @把这两个函数放到中断服务函数前面。什么还会报错??当然了,因为我们定义的函数都没有声明,接着来......
$ T2 Z! V) C( j( i& p
9 h+ d, s% ]1 c# [ ^3 B- J第三步:自定义函数的声明(可以在usart.h文件中进行声明)
2 a1 }) [7 J7 r5 A! u& x- U& k
声明自定义的结构体变量:! L* o8 ~8 z9 t" x$ \, n
$ p' y" E, J" |: v- [7 R- struct receive_stack
3 [# s9 H7 V. H* u3 ^: e - {- Z, O) r+ \* L5 M! s- j
- u8 head;//帧头
! P2 J3 T9 B% i" e( n8 o - u8 addr;//从机地址3 x) @3 ?5 y+ l Z& W
- u8 recevie_data[10];//数据 f1 M7 U& u+ f( S" V
- u8 check;//校验; W% E2 `3 b8 N! `+ L& ]
- u8 tail;//帧尾
+ f, |4 G+ N- R5 d1 J: K" p - u8 head_flag;//接收到帧头标志位
7 _9 }; ]3 J& ]4 O% h+ Y* p3 p - u8 tail_flag;//接收到帧尾标志位
& ~( ? @2 L) U$ M B/ S2 f' Q, j - u8 finish_flag;//接收完成标志位. w) A0 D( D* I/ s: B
- u8 data_pt;//已接收的字节数
, J. j5 d$ x3 L1 G( U& H6 n/ b - u8 commPack_OK_flag;//数据解包完成标志
0 {+ m/ Z' V; y" m - };
; A# d' c8 M: | - extern struct receive_stack rx_stack;
复制代码
! Y/ [; b, X3 E% |& ]4 E( Y6 q2 Z声明上文中自定义的两个函数:5 S/ g) q8 m# z1 a4 M
- void rx_stack_init(void);4 U# t) m8 C' T9 h: b2 F9 s
- void CommClr(void);
复制代码
& P+ J T9 H$ c6 S' g现在是不是就没有错误了,但是可能还有几个警告。如果还有错误的话应该是因为_sys_exit(int x) 这个函数,把他改成void _sys_exit(int x) 再试一试,错误应该就没有了吧。那警告怎么去呢?出现告警可能是因为我们使用了memset()这个函数,我们只需要在usart.c中包含#include <string.h>头文件就可以了。现在理论上应该是没有任何报警了,你的是不是这样的?. a. N! I E2 Y
' Y- J. A9 N- ~5 y第四步:验证是不是能正确返回数据1 U M* z/ ~* o& m# A* L- N7 R2 a
2 M5 [! k. [8 y8 |" `" O; F2 {仿照第二步在usart.c中添加串口发送函数) `+ H: K- l, @0 ~
$ ~, u- w i+ b; _! I- void usart_send(u8 byte): q1 S$ o' S- X/ D: v
- {: Q# P& y4 K# F1 h s) N
- USART_SendData(USART1,byte);
7 \+ I+ i0 I q( |% ] - while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//发送完成标志位
4 e1 j& R; z# R+ v' G- w$ L - }
复制代码 / ^9 v- }5 m6 d' C7 K* w( E. k
别忘记声明这个函数;; A( S6 e8 K# R% m2 }7 E
0 F6 O# L& W9 Y& N/ f0 ]3 }! j
第五步:写主函数/ r4 [# g1 c$ B6 c" i9 Z9 q% n
- int main(void); z+ G+ v" ?9 H. Z2 B4 v
- { # E. u8 n' t6 p
-
, [; A: g7 ]' B/ O: Y! U - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组27 g; Z5 ?( \7 |, d, h: G- M: R5 x
- delay_init(168); //延时初始化 * x! E; K+ p% M' f: Q4 y, s
- uart_init(9600); //串口初始化波特率为115200. b% i5 U. h; i+ J
- LED_Init(); //初始化与LED连接的硬件接口
! d8 }% f6 r2 b: \ - while(1)1 `* T( w. L2 X; v, m# S# H
- {
( [% C! f$ |) x - u8 i;
! a+ c( }$ i$ | - u8 SendBuf[5];) Z. O. b" S3 d. V
- if(rx_stack.finish_flag ==1)//数据接收完成
8 a- \/ ^- Q' u- m5 {% k - {0 n* n9 w# \- G& ^$ O, N" F$ b7 R
- SendBuf[0]=0x7B;
) y! e1 ?. u: i1 F - SendBuf[1]=rx_stack.recevie_data[0];
7 R) z4 W* P9 l0 T5 V4 ^# c! M - SendBuf[2]=rx_stack.recevie_data[1];0 t6 I0 L, @1 T% h
- SendBuf[3]=rx_stack.recevie_data[2];
& t- L+ b* M& Q4 K - SendBuf[4]=0x7D;
8 J1 \, G ^0 B8 d5 {4 b+ @8 ^ - for(i=0;i<5;i++)( Q8 n0 W5 J) b0 R3 I
- {( U, w- d) i! c" C& ?! j7 ^' F
- usart_send(SendBuf[i]);
3 S V6 y* T8 ~ f/ t( a8 ` - }3 k, o) E6 {) a
- }
/ V$ @: ~+ T9 n: [& c. I2 F; [, b - }1 c" u6 U# _/ [2 X; u/ F! z
- }
复制代码 " _ U* f H- A6 L6 }+ p7 a* V/ u
编译一下,把程序下载到板子里面试一试,当你用电脑的串口助手发送AA 01 01 02 BB 给你的目标板,它能把发的数据原样传回来吗?坛友们一块讨论一下。# n3 b& D4 p2 R: ]/ N2 B
8 c; M$ {0 t, j* Q# E( b) F
4 R* t* ?: w& n
9 e1 w3 W. \( z* Q! i
0 O& x# |: V N9 D- I; P5 w; B' a& s
|