先说一下我想要实现的功能:
9 v- v9 \$ F7 r( J通过PC的串口助手发送一帧数据给STM32设备,STM32设备解析出某一位,若是1就点亮1个LED灯,若是0就把该LED灯熄灭。5 d4 ]3 D. `2 g( o0 Q/ Q+ D Q0 ~
自定义的通信协议如下:
; h# H* C1 x$ d n2 vAA 01 01 02 BB: ^9 ^$ m7 T5 Y
AA —— 帧头
' V5 H- B0 K! R0 Z/ \9 j) _01 —— 地址
% e B* s4 D, Z" [0 r% [% `- l01 —— 控制字(是01就点亮LED,是00就熄灭LED)
9 [$ a& U+ G2 S6 k0 w- o02 —— 校验和(地址+控制字)
$ P* C$ B- J: Y" b Y' rBB —— 帧尾
6 g, B6 b) E" G% n' F2 g }* {
6 v5 s* N: M# }1 y今天我们主要是讨论一下自定义协议的问题,在这里我就默认你的STM32设备已经可以和电脑的串口助手可以通信上了。如果还没有实现这一功能,建议参考STM32FX开发指南(库函数版),下方我也会提供出源代码供大家参考。& o+ T2 k% `5 t! s! ~
因为我硬件采用的是STM32F407,大家可以根据自己的实际情况进行移植;
. Z( R" n$ y" [4 Y7 ?* ?+ p你准备好了吗?接下来跟着我的节奏开始.......
; p6 C4 A; G+ U4 d, l6 ?6 m第一步:替换中断服务函数;
/ M4 e( U* V8 P2 ~9 B# I把原来的中断服务函数用下面的函数代替(函数可以在usart.c文件中找到)8 W' n- k g0 g* @
- void USART1_IRQHandler(void)
& g5 ~$ k- \4 M - {
- n% F2 I/ F k: o8 T1 B - u8 Res;//临时变量,存放串口接收的数据2 K0 v+ v' q/ ]) d( y/ m3 V
5 O8 a" d1 K" k' \# c- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断! ?* @( m; G" E" C; e/ L* B
- {; }9 B. z, \4 W$ Q( [$ w5 t
- Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
6 w2 M+ Y! Q9 i3 Q4 F7 \, l3 _; q5 G - if(rx_stack.head_flag==1)//收到了帧头2 @0 t, k }7 y1 O2 o4 ?
- {- ?: X! j/ i% j, H# k$ `
- if(Res==0xBB)//判断当前值是不是帧尾
" [* h8 Q( z5 c( Z - {. @, {+ T( z0 `9 W! z
- rx_stack.finish_flag = 1;& S" x% m" {; X. K: Q
- rx_stack.tail_flag=1;
\/ Q8 G: P2 Q8 |# a - rx_stack.head_flag=0;
$ {+ |0 C8 l, \% o; @ - }
% v# F4 T5 ^# | - else
% Q5 `( i8 O+ X: u# [6 Z" M - {( z/ x, f& h* s: `& I+ z$ g8 |
- rx_stack.recevie_data[rx_stack.data_pt] = Res;0 S. q8 s* k1 W0 I/ [$ l, V0 T
- rx_stack.data_pt++;
9 d1 N1 m9 D, G# S6 `, b - if(rx_stack.data_pt > 9)6 j- f* [( i$ i4 z m
- {5 N m: Z, a# k( r |+ ^5 h8 x
- rx_stack.data_pt = 0;& e3 f$ i0 Q. i' y2 V8 c: S
- }' y+ ?' z+ J; ?
- }
* i% f) J; D) } - }
' `2 b/ F' c3 W* A: ?! e1 j - else//没有收到帧头
' q* }: q ^" R2 c( P- b - {- h4 V" p+ E4 v8 z
- if(Res==rx_stack.head)2 d. j. b, q+ c$ K- c
- rx_stack.head_flag=1;
2 m7 _9 R) T+ z/ W. X6 Z - else
' O( N7 X( Y z3 n - {. G5 \: q; z9 u+ w
- CommClr();
& ?# l$ A2 `! Y! J- q5 l - return;
0 W8 l. `; \2 s4 ? - } ; h! }; r3 u8 C( F
- }
5 z% s( f! x: Z" \ - }
% L$ O! I! x x0 a: C- D3 k - USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接受中断标志, T% m( ]% t9 h1 n: V! |7 q; r* V! |
- }
复制代码 到这里你会发现,运行之后会出现很多错误,别慌别慌,问题不大,那是因为我的中断服务函数里面定义了一些原来没有的变量和函数;接着往下看.......
/ I5 @0 H! d1 O0 Z/ b" _
- s, z E# D7 P7 d* `( l( j第二步:添加自定义的函数
0 T& U" q2 I: h5 e
$ ^; k3 t9 b# Q- void rx_stack_init()
5 ^2 v: T; Y9 @- w - {, B9 p5 B% H |( j: o) \# T7 ]' E5 P
- rx_stack.head = 0xAA; //协议栈头,起始位
: e' T* W" n5 O2 V) t, C - rx_stack.addr=0x01; //从机地址& x) \7 q8 f1 V7 i" C7 @* o, h3 d
- memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零
) C' ]6 S, X! o. ^$ ?. q - rx_stack.tail = 0xBB; //协议栈尾,结束位
) _: u& _; c- V& J - rx_stack.head_flag = 0;
' [4 Y+ q; j8 B# C$ i7 c - rx_stack.tail_flag = 0;* g5 B( z _ F; f* k
- rx_stack.finish_flag = 0;
3 D2 g5 V( f9 }' `9 i3 Y - // rx_stack.lock_flag = 0;
" X6 A8 T4 d% M! Z2 ~ - rx_stack.data_pt = 0;
) s) L# [; o2 A8 Z, w+ T7 h0 h; P - }' {4 V3 H& I' M1 _. ]; n8 ^
- struct receive_stack rx_stack;
5 n$ u' {; ^! Z( s
, y5 a: b8 s3 h. U3 X- void CommClr(void), _9 [: W% X% m6 n' d
- {# C9 }1 i" h. a( s; k) n" K
- rx_stack.head_flag = 0;
d! O3 d0 X! v0 D% Y - rx_stack.tail_flag = 0;
7 [0 |( A/ r6 h5 g# w3 G. g9 U - rx_stack.finish_flag = 0;* f1 `7 A) j: E4 c0 O$ [7 X& s' P" |
- rx_stack.data_pt = 0;
$ v1 ^; f. D( x- E7 Y7 k6 e i, c; B -
% ]0 e+ b3 u' _8 a3 m' S; U: n, D& g - memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零
2 w) {& j0 _ X- X$ d$ m - rx_stack.commPack_OK_flag=0;% }! X6 b9 _9 D1 ]/ a7 n
- }
复制代码 % B2 z4 s1 B& Q4 h( L5 F& L% R, F" _
把这两个函数放到中断服务函数前面。什么还会报错??当然了,因为我们定义的函数都没有声明,接着来......3 u" n) @* o; w/ a$ q6 b5 E
& B2 P2 ]- q! J; o; Q* ~第三步:自定义函数的声明(可以在usart.h文件中进行声明)
5 U* T: u3 m& T9 P; i0 D! r9 E; N+ L. ?) B" _! I3 d" a8 F8 u2 n, t
声明自定义的结构体变量:4 I0 W# o, i w5 y' W
8 p$ w/ h" _' u, r q5 R- struct receive_stack3 Q* D+ d8 }7 a6 Q
- {! N% z! ^$ ]6 j5 E; b
- u8 head;//帧头4 M7 j$ `% b' l% S7 \4 W
- u8 addr;//从机地址6 f1 v' Z' A) D7 R; i( B8 q5 J- h
- u8 recevie_data[10];//数据( b4 r) ~( O1 f
- u8 check;//校验/ H& {! G: j- d# G7 G4 z
- u8 tail;//帧尾4 @2 F- ^: p0 _
- u8 head_flag;//接收到帧头标志位/ }- v: t( t+ r! ~
- u8 tail_flag;//接收到帧尾标志位
`5 s$ `8 ^& A* O0 L% Y - u8 finish_flag;//接收完成标志位; t4 j9 B2 L: c) r1 n
- u8 data_pt;//已接收的字节数
( W/ {) G, M6 z3 V' W - u8 commPack_OK_flag;//数据解包完成标志
, O+ t9 I% y, `5 t7 ? - };% O# X6 K" f- U, v' ]% k( n
- extern struct receive_stack rx_stack;
复制代码
) [* G( J1 u7 W' O声明上文中自定义的两个函数:) p. @- @+ x: e
- void rx_stack_init(void);* |& y1 s9 A- Z# t4 V: u9 ]
- void CommClr(void);
复制代码
( ]) ^& a7 { X* x" M6 C现在是不是就没有错误了,但是可能还有几个警告。如果还有错误的话应该是因为_sys_exit(int x) 这个函数,把他改成void _sys_exit(int x) 再试一试,错误应该就没有了吧。那警告怎么去呢?出现告警可能是因为我们使用了memset()这个函数,我们只需要在usart.c中包含#include <string.h>头文件就可以了。现在理论上应该是没有任何报警了,你的是不是这样的?
' u' D- l! c" P4 b& N
3 |6 U7 Y3 ]0 o0 F5 _1 G% H; x第四步:验证是不是能正确返回数据' z8 `3 P: o+ Z: L# Q/ w: ]
$ Z' Y4 n1 x/ j$ V8 B! u% m
仿照第二步在usart.c中添加串口发送函数
1 z* }1 S1 q1 l2 N) {& Y
7 O; j/ Z/ X$ ^: H9 F, {$ U- void usart_send(u8 byte)8 X! u: ~1 W) k' _# C! h# s
- {
4 M9 q! Z9 D. Q* j9 Z/ v- b/ E - USART_SendData(USART1,byte);$ |5 n7 M& P; j5 ]7 H. U
- while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//发送完成标志位
( }* m! p! I5 r r$ p - }
复制代码 . q) T& k3 a- y. ~, X% [6 \3 l6 u+ w
别忘记声明这个函数;0 R7 K7 }. T5 R. ?6 [
3 y! |, t- ~# w第五步:写主函数
5 ^" Y) d3 a$ w" M: ]" ]5 x7 Q* m8 T- int main(void)
) G/ T) Y9 O( A9 I, T! H# l - {
t" B% \3 G1 p) {$ G: T* V) r - * \3 p# E/ W( g9 V' R6 _1 ?5 ^
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2" b& q: R2 A4 p' o' i ~0 a
- delay_init(168); //延时初始化 6 _1 {! K+ N8 u
- uart_init(9600); //串口初始化波特率为115200" f, C; f( b9 v) Q! [7 F" m
- LED_Init(); //初始化与LED连接的硬件接口 $ j+ R0 J' F" F* b
- while(1)
% C1 L6 F& p( e! N8 }& z$ O - {! D. p- e2 F6 X4 B+ d
- u8 i;# c9 y' M; s: a1 f
- u8 SendBuf[5];
* D/ Y- F* @ ~; a0 B - if(rx_stack.finish_flag ==1)//数据接收完成
2 F. L" ]. g3 N3 b( h - {0 @, x' ]5 B# b
- SendBuf[0]=0x7B;1 M' f6 }: B7 C; ^/ {
- SendBuf[1]=rx_stack.recevie_data[0];
+ h3 m' I0 Q6 s9 I. u6 O$ T - SendBuf[2]=rx_stack.recevie_data[1];
! E1 t6 v2 x' ~0 F8 f) q9 t% c - SendBuf[3]=rx_stack.recevie_data[2];
' x2 r0 v$ P M" a - SendBuf[4]=0x7D;: J: I+ r' l x" }
- for(i=0;i<5;i++)
" f* k1 d, t6 N# x2 m/ r - {: X3 y. P5 n7 k1 ~* ^- K% ^/ ~
- usart_send(SendBuf[i]);2 o* C5 F- a9 ~( [& \
- }
' G* d5 v% O0 m - }, P7 s; P5 b, p# H
- }
2 r' V. h' r+ e( B" Q: a8 Z! g - }
复制代码
7 M# E) K% \% P0 h6 H$ s编译一下,把程序下载到板子里面试一试,当你用电脑的串口助手发送AA 01 01 02 BB 给你的目标板,它能把发的数据原样传回来吗?坛友们一块讨论一下。
/ Q3 P7 e# D0 h& V7 }- ?. y( V2 ~8 m- b, |: ^1 _
( @# O9 k, g+ p+ g( C8 k+ _2 d0 W+ L
6 x$ x; | [) v! j! b- e
, {" u L( o6 j# U
3 U' }8 W2 B4 h0 W |