先说一下我想要实现的功能:
* J+ d7 m# m! Y3 c1 x( `) x/ v+ u通过PC的串口助手发送一帧数据给STM32设备,STM32设备解析出某一位,若是1就点亮1个LED灯,若是0就把该LED灯熄灭。
7 A5 E2 l! Y5 u O3 W e自定义的通信协议如下:
& P. L5 N6 m9 eAA 01 01 02 BB4 ^4 }+ i+ m( ?; t5 k* F1 ^
AA —— 帧头; C0 d% a! ^0 n- C
01 —— 地址
* U1 {9 C% t/ y01 —— 控制字(是01就点亮LED,是00就熄灭LED)3 t8 d- [7 \6 {
02 —— 校验和(地址+控制字)& f; w: c. {1 L: v
BB —— 帧尾2 f" N5 I- `, r, ~7 A
' I' M# q+ G2 Z6 T* x
今天我们主要是讨论一下自定义协议的问题,在这里我就默认你的STM32设备已经可以和电脑的串口助手可以通信上了。如果还没有实现这一功能,建议参考STM32FX开发指南(库函数版),下方我也会提供出源代码供大家参考。+ L- T0 I" }& N7 [
因为我硬件采用的是STM32F407,大家可以根据自己的实际情况进行移植;$ [" V2 E# L2 C8 X* M
你准备好了吗?接下来跟着我的节奏开始.......
5 ~; L/ H/ U0 S& ^* N( ^6 h2 Y5 i第一步:替换中断服务函数;
* _5 e/ e5 M* H把原来的中断服务函数用下面的函数代替(函数可以在usart.c文件中找到)9 A+ ^4 O7 j R
- void USART1_IRQHandler(void)
0 Z8 X9 ]6 s, z" [9 H( @ - {
. K" B0 L2 k# S+ I% Q9 H& I, S$ j - u8 Res;//临时变量,存放串口接收的数据
+ F5 k0 X; W% u4 i
# F- _. {% F& k; h+ T- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断) O; k9 Y' c- y2 G# v8 G3 g
- {+ I1 P2 x O, c# r
- Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
! B9 x3 X% u! y5 p! S/ b1 T - if(rx_stack.head_flag==1)//收到了帧头
) t7 X; h* I+ F - {
6 Y1 n3 n1 O. J6 |$ w8 ^9 W3 a - if(Res==0xBB)//判断当前值是不是帧尾
0 `: X8 a3 e4 T: w G - {0 x9 o8 G4 L. C3 h2 D& B
- rx_stack.finish_flag = 1;
0 P4 N) Q2 ^+ e4 y - rx_stack.tail_flag=1;8 z6 z, m: M2 V6 K* n- U) z
- rx_stack.head_flag=0;
6 X. f) U0 J& r$ F( ` - }
1 o7 q9 c2 n( ^3 x - else N0 G3 w. ?: l L' |
- {4 E1 j* V% c+ Q0 e! ^, e" v
- rx_stack.recevie_data[rx_stack.data_pt] = Res;
0 C( K* `: Z0 @" d/ E7 [: i& \; X - rx_stack.data_pt++;
8 j! G1 X( D8 Q - if(rx_stack.data_pt > 9)( K g+ U% x: \) V4 m
- {; O8 {- e. V( x9 d
- rx_stack.data_pt = 0;
4 P9 W4 j3 I* |: @* P: s9 E) T& ~ - }
' ?6 g3 q" v: \4 P2 j; C' O. l - }
1 [0 b. h: U- O. K. t - }% D+ }) D) M8 }! C
- else//没有收到帧头
, F9 X- L# j0 I7 T0 m) T6 G V - {+ r& U$ {6 d5 S7 T6 X U
- if(Res==rx_stack.head)
2 A7 ~1 \1 B4 c/ O$ [3 R8 M% | - rx_stack.head_flag=1;6 S! O4 _. {, j# Q: d9 s
- else
5 t4 e- C6 R0 Q* E# q6 V - {
& K. S1 Z! x' x( C9 b1 ^4 x. j( l4 @/ a - CommClr();
# i5 q$ Y& ~# y; ~3 o# m - return; ' r2 j# z% A& ]2 E
- } 7 B0 K# I1 s' L7 S2 b$ d" ^
- }
& m# z5 o7 Z4 M6 }8 l- Z - }
) u: M- @7 \# y# l3 Y. g& W! |* L - USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接受中断标志) Y# t/ Z6 Y- ?. L" J
- }
复制代码 到这里你会发现,运行之后会出现很多错误,别慌别慌,问题不大,那是因为我的中断服务函数里面定义了一些原来没有的变量和函数;接着往下看.......( s: W1 m1 p \' h) n! X1 s' @2 O
$ l: z* z. P# ~. Y第二步:添加自定义的函数
$ K8 D1 P7 _, g- ( K! b u4 J8 v
- void rx_stack_init()
; c! M- F: M5 H7 v# r/ Z - {
7 }& @* A# K9 y5 T2 F - rx_stack.head = 0xAA; //协议栈头,起始位
& `& m/ x$ h5 Y, l. x. \; y - rx_stack.addr=0x01; //从机地址
8 t. N8 f) m. K - memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零* w& G. z5 {" q! b3 b. [' L6 n: F
- rx_stack.tail = 0xBB; //协议栈尾,结束位
1 Q4 o3 ?% ?; `! s- Y, M - rx_stack.head_flag = 0;
; F/ Q% F3 ]1 T0 W - rx_stack.tail_flag = 0;# G' x5 Z& j$ S9 M. Q/ z" I
- rx_stack.finish_flag = 0;
8 L) l, n4 A, g+ |- v# R8 ^, l - // rx_stack.lock_flag = 0;) V' G; H& j- B; q/ Y6 A+ N
- rx_stack.data_pt = 0;* t0 i q S# v. Q( R
- }1 a( X4 m" P# O
- struct receive_stack rx_stack;' n7 h2 v. b+ \% ]
; L' }! O, n2 _& N( M- void CommClr(void)+ s/ P9 b. w9 f- F8 z/ Z
- {$ G$ v6 Z' W# Y# G' `
- rx_stack.head_flag = 0;
# h- ?4 ?/ \* s - rx_stack.tail_flag = 0;- [' V! x* w2 k6 t" U6 s( Y
- rx_stack.finish_flag = 0;1 ~! t% ~2 q2 P$ V
- rx_stack.data_pt = 0;
$ ]; i. W( p: R; a3 S" E3 ~ - ' u- o* ?( Z( p0 n
- memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零
: I* z1 O5 U* o - rx_stack.commPack_OK_flag=0;
6 d/ W2 c6 x& T0 E3 @ - }
复制代码
$ y/ X @4 `* W7 h c5 v' ?把这两个函数放到中断服务函数前面。什么还会报错??当然了,因为我们定义的函数都没有声明,接着来...... x% L% H2 {4 `6 s
; J( b+ }" h# o, M
第三步:自定义函数的声明(可以在usart.h文件中进行声明)) x, Q$ `6 s0 ]6 @
( h( |3 e; _- {! [5 f( K" r3 X
声明自定义的结构体变量:
& A' X0 d8 Z- x: C8 A
8 Y: O0 \& {2 I* W- H* h5 f( Y6 M- struct receive_stack
' Q. |4 e# V! Q# h - {
" w- I5 B( e! f/ |+ ^& b! J - u8 head;//帧头% ^6 L4 i( |# u8 I3 N
- u8 addr;//从机地址
- ]) F% S. y% c0 j2 h, r - u8 recevie_data[10];//数据4 @( i; K+ A8 [9 [( c
- u8 check;//校验" b+ l4 Y; V3 ~
- u8 tail;//帧尾* |! J( N" f; N$ Z- e
- u8 head_flag;//接收到帧头标志位
: I" e# E% ~: s3 ]" q; Q3 Q - u8 tail_flag;//接收到帧尾标志位
# k2 F8 F( y+ O' H8 ?; A - u8 finish_flag;//接收完成标志位
, g0 u2 b- P. y/ X# }# [ - u8 data_pt;//已接收的字节数
3 u3 d7 q$ C, R( O) X - u8 commPack_OK_flag;//数据解包完成标志
6 [/ h, ^/ Z& z% I# z& j - };
0 t V7 m+ a# i3 C4 h0 Y! F& _$ C, ? - extern struct receive_stack rx_stack;
复制代码 % q( D8 L7 T, N) }* T# f! v; T! w
声明上文中自定义的两个函数:& h2 a) `! w; T
- void rx_stack_init(void);
. F% X1 u0 n! F& ^8 O - void CommClr(void);
复制代码
6 m: D- y, V6 U1 Y现在是不是就没有错误了,但是可能还有几个警告。如果还有错误的话应该是因为_sys_exit(int x) 这个函数,把他改成void _sys_exit(int x) 再试一试,错误应该就没有了吧。那警告怎么去呢?出现告警可能是因为我们使用了memset()这个函数,我们只需要在usart.c中包含#include <string.h>头文件就可以了。现在理论上应该是没有任何报警了,你的是不是这样的?
+ W. L( l- E: p7 P8 ]- e9 O' E
. W, v( _" g5 R2 y: a3 t' a第四步:验证是不是能正确返回数据- R+ g. H5 s" b- @) G& A
$ e8 |2 F* B0 L: I& l1 x" q: w
仿照第二步在usart.c中添加串口发送函数
, }: F6 p% R. Z, P8 K9 o9 S) \4 j$ B0 `6 Q
- void usart_send(u8 byte)1 Y9 t& ~6 `1 ?( N
- {* z' z; A2 ^# Z4 N% h7 r, k7 ~, n
- USART_SendData(USART1,byte);
E0 d0 ?3 [' ~4 A' K/ i$ F - while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//发送完成标志位
! f7 v0 i" _/ R+ i( _ - }
复制代码
! S7 r$ Y7 F; h7 u/ ?5 F2 T8 ?# l别忘记声明这个函数;! O% N" I" t* [4 w# r' }
: Q! n9 w$ l% a1 H. _; {+ a# n
第五步:写主函数
. A1 J9 h. ^) L6 n/ E4 Y- int main(void)3 z" {7 [& u1 j! ~, h
- { , q' e: M0 @: d) b# H
-
( z/ S: J1 k. m, D - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2. u0 h" {$ r( G- I) M6 z; f
- delay_init(168); //延时初始化
& d: Y0 \4 r/ e$ o2 X$ f* b* j1 Y - uart_init(9600); //串口初始化波特率为1152007 y/ Z0 a. g- H5 i# g2 g" _( ~1 Z
- LED_Init(); //初始化与LED连接的硬件接口 - X# ?; D t+ Q
- while(1)! V( E$ ^0 [+ s. J5 h$ w& e
- {
8 ?# s4 V3 |. B - u8 i;( B2 T- k, q% M+ O( c9 w
- u8 SendBuf[5];
$ I* r j8 @- }( z' J - if(rx_stack.finish_flag ==1)//数据接收完成+ S& _7 g+ w- ~/ f" u
- {# J6 S" f8 V' O) W: Q' f
- SendBuf[0]=0x7B;
5 n4 l- l8 q! a: y8 M) l - SendBuf[1]=rx_stack.recevie_data[0];
- D# a/ Y4 E: N* H; F0 a* ?3 t - SendBuf[2]=rx_stack.recevie_data[1];
( t. `3 X. F2 R) n5 m: b( { - SendBuf[3]=rx_stack.recevie_data[2];0 X, r: @6 u5 |
- SendBuf[4]=0x7D;$ j2 t& @' i' r: u# d
- for(i=0;i<5;i++)# `- }2 `7 F3 S! c
- {
! |1 f% h9 u- [8 v( |, r* b" r - usart_send(SendBuf[i]);- S4 }8 u7 I5 I& y
- }: w( M" D- c3 z8 D! k1 z6 {* B& X7 A* D& F
- }
% \9 q. N+ q& G& \! f - }
' w& T) ~/ f, x# D/ u9 w# s2 w. K - }
复制代码 : l% W1 Z3 u _' H
编译一下,把程序下载到板子里面试一试,当你用电脑的串口助手发送AA 01 01 02 BB 给你的目标板,它能把发的数据原样传回来吗?坛友们一块讨论一下。
; z$ F( I' o7 z3 f7 c! R
% a5 Q2 A- m9 U5 V% e5 o! M2 s+ {, g' w! L: A5 u
4 K( X( ~' b6 [8 Y- I/ t, F
7 I% W2 k. `( B" j# b/ o* `* g+ k8 `8 X
|