先说一下我想要实现的功能:
3 F- H9 ]; M) ~5 d- G: N通过PC的串口助手发送一帧数据给STM32设备,STM32设备解析出某一位,若是1就点亮1个LED灯,若是0就把该LED灯熄灭。
, a& F5 o% L6 L8 k7 S& a0 y自定义的通信协议如下:
- O& Z- A$ U! T- ?. N; ]9 BAA 01 01 02 BB
7 r# u. W: c- V L# mAA —— 帧头
' ~- j X: q' z4 B# w! j01 —— 地址
. _+ M; O" t W3 M6 o; ~* }0 N* z01 —— 控制字(是01就点亮LED,是00就熄灭LED)7 g3 G6 h2 z6 c/ ?& d
02 —— 校验和(地址+控制字)3 J( P1 M0 P! _4 P2 x3 R% w7 |
BB —— 帧尾
5 p9 w+ r2 E9 d( \
m* Q9 X3 @1 q# N" f) K7 C今天我们主要是讨论一下自定义协议的问题,在这里我就默认你的STM32设备已经可以和电脑的串口助手可以通信上了。如果还没有实现这一功能,建议参考STM32FX开发指南(库函数版),下方我也会提供出源代码供大家参考。! W5 _" d l/ {: O- {8 y4 }
因为我硬件采用的是STM32F407,大家可以根据自己的实际情况进行移植;/ M# u& }, x8 ^% Q* d
你准备好了吗?接下来跟着我的节奏开始.......- \' s/ c* J- \! G h% }
第一步:替换中断服务函数;. s; U1 f; t; @
把原来的中断服务函数用下面的函数代替(函数可以在usart.c文件中找到)$ Q* V% J: J, u+ H% m8 B
- void USART1_IRQHandler(void)
* `6 P3 I1 j& R# V } - {& f6 K# d' r" u" k- e3 P0 ]
- u8 Res;//临时变量,存放串口接收的数据& j7 N) F' p% Y- V+ Z6 r+ m
# G- G) ?# o* _% N" V- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断0 e6 b. I G% P. W7 d
- {
( C# A! n# n" t - Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据- l- r9 h4 |4 x; ]! P- k- q
- if(rx_stack.head_flag==1)//收到了帧头' q! x9 U* d6 I
- {. y, i0 C7 x: L/ x
- if(Res==0xBB)//判断当前值是不是帧尾: T# } C4 x9 c7 P
- {
( L" k" i% o' H5 p - rx_stack.finish_flag = 1;
4 }$ i) g4 z, M! \ - rx_stack.tail_flag=1;
' @/ l) \" j+ o; ?6 v - rx_stack.head_flag=0;
7 z" M& d/ Y g. l; t! Y - }
$ U- m, l1 J# I - else
' X c% E8 z3 Y% r - {
6 Q9 ^8 t3 ?6 ]! H! g! { - rx_stack.recevie_data[rx_stack.data_pt] = Res;
' ]9 ?1 M( I# U - rx_stack.data_pt++;
: |+ g" w& p7 i6 e0 k( n - if(rx_stack.data_pt > 9)
/ O# ?0 \' X: ~( ? - {! S. x! {5 N% U9 j1 P$ [
- rx_stack.data_pt = 0;
/ ]) W2 } u1 g+ o. E - }9 \9 }0 j0 L: s9 L% E% f! B' |, {: [
- }
! [. E1 n! ^4 G% e B9 P - }
$ [$ A( M" G$ X- ^* z, J+ R - else//没有收到帧头
( M& M# j( w% i - {
) u+ p7 d `1 a. ] - if(Res==rx_stack.head)
) F) J" e# N0 Z& b - rx_stack.head_flag=1;
' _' v) v' V$ p E) e) V2 N - else9 p! K2 ^5 f' g" L4 a5 t
- {& V2 E) i7 I) a0 T# X
- CommClr();5 b j( u: Y- T' E" ]1 y! m' ?
- return;
/ m y8 z0 B8 ^9 i5 ? - } ; H$ ]8 S, }. L
- }
" L, \6 L. |6 s& }3 z - }/ G% O/ U5 u5 Y5 ]. m7 y p# q
- USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接受中断标志
8 q G( G* |1 [& Z1 A3 | - }
复制代码 到这里你会发现,运行之后会出现很多错误,别慌别慌,问题不大,那是因为我的中断服务函数里面定义了一些原来没有的变量和函数;接着往下看.......8 ~+ T9 z( G+ W7 e& \; G
9 Z% E5 a. K, f
第二步:添加自定义的函数
( t( L$ M: \* p, J/ p
1 B V c# I4 J- void rx_stack_init()
: @5 }1 h1 Q0 `1 [. O4 ? - {8 Y+ D2 }! i8 E8 ~
- rx_stack.head = 0xAA; //协议栈头,起始位. J4 p! _, E. c
- rx_stack.addr=0x01; //从机地址# W4 V5 ^- \% a
- memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零+ `& s, y. w) f2 |2 J4 ^' B) G; {' b
- rx_stack.tail = 0xBB; //协议栈尾,结束位
W, h; Y8 w" ^* {- V6 K) { - rx_stack.head_flag = 0;
9 G. e; U1 d# p+ U - rx_stack.tail_flag = 0;
' g& c& u- O- M, K- y - rx_stack.finish_flag = 0;3 p: e! }9 W0 W% i+ ]2 p. T9 S. U
- // rx_stack.lock_flag = 0;
$ ^- r$ m. K [, |$ i - rx_stack.data_pt = 0;' l A+ R5 h- k2 B+ Z, I
- }
# p. W8 m4 j- J; F8 L - struct receive_stack rx_stack;
" ~/ X4 K- O- l, j
" d' y/ m" B1 N" Y C- void CommClr(void)
' s8 j; B- q& _0 a* @5 D - {
# k) W1 _# V$ _8 y - rx_stack.head_flag = 0;) y( R# u7 J; J3 D
- rx_stack.tail_flag = 0;
- r9 E' T9 N! m- u. C - rx_stack.finish_flag = 0;
0 g8 }: S: e. ]6 Z5 S: w: n4 @ - rx_stack.data_pt = 0;5 b% n# F, t4 u8 V' L% _) I9 ~
- ( B1 b. s, w* u( L+ B* G3 p% y
- memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零5 Q) ~% K+ \' `* X0 m& c: G) @
- rx_stack.commPack_OK_flag=0;- g8 e1 l1 G! x5 s- d
- }
复制代码 ' E* s$ r3 n v
把这两个函数放到中断服务函数前面。什么还会报错??当然了,因为我们定义的函数都没有声明,接着来......3 s) t0 g( g9 n. x
- v0 n+ E' @7 G# ^# h
第三步:自定义函数的声明(可以在usart.h文件中进行声明)
- F5 w+ n* X3 V0 g# S( l* F9 X$ \' z/ A) C! k+ s
声明自定义的结构体变量:% T: A+ A4 n! P, n7 ^! v
@2 V* s# }* J; g' L. a- struct receive_stack) @4 J4 X- h/ T9 z7 |5 `& M/ E
- {
2 D# P* @! K: {# J5 |9 O - u8 head;//帧头
5 T, n& {3 o$ ?. L1 E! @4 } - u8 addr;//从机地址
8 C8 T; L5 r" p2 c L2 h - u8 recevie_data[10];//数据
2 z$ A* U, p" }/ u) O: \ - u8 check;//校验
{5 L6 y& k- C+ B - u8 tail;//帧尾9 W) R- l7 L9 `- ^1 C" {+ G
- u8 head_flag;//接收到帧头标志位
0 D9 b2 v! i. v4 W2 ~% K" b - u8 tail_flag;//接收到帧尾标志位
9 a+ L( w' }* `, F9 ^% f' F, @ - u8 finish_flag;//接收完成标志位
2 O/ [5 D7 }7 k! {8 ?0 v/ O - u8 data_pt;//已接收的字节数# E, b* p$ k, ~- k0 u" G9 ?
- u8 commPack_OK_flag;//数据解包完成标志, q0 n3 W# T# u6 m+ c, R1 Z
- };" x& |7 q9 R' v$ {
- extern struct receive_stack rx_stack;
复制代码 ' V8 ^" O2 @) I1 i
声明上文中自定义的两个函数:
$ r. d; o4 b0 E$ w' @* B- void rx_stack_init(void);
( p5 L" L) {3 u t% f - void CommClr(void);
复制代码
. w" J, V4 q8 X4 {7 b& S0 L, m( W现在是不是就没有错误了,但是可能还有几个警告。如果还有错误的话应该是因为_sys_exit(int x) 这个函数,把他改成void _sys_exit(int x) 再试一试,错误应该就没有了吧。那警告怎么去呢?出现告警可能是因为我们使用了memset()这个函数,我们只需要在usart.c中包含#include <string.h>头文件就可以了。现在理论上应该是没有任何报警了,你的是不是这样的?
' }. A0 O# z/ h: ]' O, i# l: ^8 {6 g- @7 ]% `
第四步:验证是不是能正确返回数据
2 y6 O1 A: u# _% P8 I+ X. j
6 c, p- a2 I1 I4 ]& C仿照第二步在usart.c中添加串口发送函数1 Y* b; O( z5 }$ Z/ a7 }5 i
: ?+ p3 n- y1 h
- void usart_send(u8 byte)
( d1 i( Q' G0 c5 o x - {
- E4 c: _8 D9 t5 n* w - USART_SendData(USART1,byte);( h8 O4 N9 A! W/ Z8 C
- while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//发送完成标志位# j6 H6 M) L+ r- ?0 j, w3 z! G
- }
复制代码
7 W6 F" }# q2 b7 A& v$ x别忘记声明这个函数;% U( S: S" u% N
+ T* R4 \: b# v第五步:写主函数
7 ]5 K! G6 c7 |7 }5 x- int main(void)2 V5 o. V. q; J0 H ^
- { 2 k( H/ A5 }& u+ M! O
- ; K) Q R( r. F0 ]/ h& [
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2 N& O0 x: L) a% T2 ]$ p. x
- delay_init(168); //延时初始化 ( ^1 I8 S/ w- }) s# ?
- uart_init(9600); //串口初始化波特率为1152000 E; x1 {) S4 ?! K
- LED_Init(); //初始化与LED连接的硬件接口
1 J! g( m2 p" W- ?2 w - while(1)" n% g U4 }$ k4 S, q( |- X" ^
- {3 x) c$ |- F$ B/ b: }
- u8 i;
( \5 c/ h( t# n X - u8 SendBuf[5];0 f) T1 ~/ E8 V) w; ]2 ]/ I( q
- if(rx_stack.finish_flag ==1)//数据接收完成
# N: ]$ N$ Z8 X" |* A - {' H, |1 ^4 A. U
- SendBuf[0]=0x7B;
5 a, ?4 u* _5 p0 X6 M - SendBuf[1]=rx_stack.recevie_data[0];
# p/ Z6 a% | J3 R - SendBuf[2]=rx_stack.recevie_data[1];6 Z* p9 _: l8 i }% u. _ z
- SendBuf[3]=rx_stack.recevie_data[2];7 v; N+ J7 o' ]0 |1 E! O
- SendBuf[4]=0x7D;0 g+ k( B: S' e4 m$ A9 l) c$ Y
- for(i=0;i<5;i++)0 D- V E6 F, g, \) D. s% @
- {. [) m8 F( F# s) f
- usart_send(SendBuf[i]);
9 Z3 I& e) X& s* ^ - }3 c7 l% z+ I6 O& a M
- }
. X# @) u+ p+ `. p7 S9 E( b1 Q - }" E, Z! k* C& d; o. N
- }
复制代码
3 E/ q. b1 k ^. J0 r" x) z编译一下,把程序下载到板子里面试一试,当你用电脑的串口助手发送AA 01 01 02 BB 给你的目标板,它能把发的数据原样传回来吗?坛友们一块讨论一下。
9 l: w; G% q* ?/ A1 @! f/ Y
5 X* h0 k- _; a8 k( v1 k; k2 ]/ |
6 q3 R: [! n/ Y1 W3 p) h0 S3 Y
, c% T l. X1 T7 I
j. C- C& {" D& a
( Q* ?- d" }3 {( C |