先说一下我想要实现的功能:5 H( t" J( }: j% r8 t/ T
通过PC的串口助手发送一帧数据给STM32设备,STM32设备解析出某一位,若是1就点亮1个LED灯,若是0就把该LED灯熄灭。+ H5 U2 m& V& p* ^% p
自定义的通信协议如下:
# \4 N5 i& o5 x2 w- v0 ], yAA 01 01 02 BB7 T9 C+ n* @! Y
AA —— 帧头: B7 y9 Z+ D3 j4 K% l
01 —— 地址% ~: N6 v# g8 V+ C- X3 b7 l
01 —— 控制字(是01就点亮LED,是00就熄灭LED)! K' y* D. v" Z1 ]( m
02 —— 校验和(地址+控制字)% {& L0 h' `* Q% }* ]/ M# [2 V+ H
BB —— 帧尾/ F: e) q s3 L2 ]
0 r; o( N! ]' h7 B0 S4 D6 f今天我们主要是讨论一下自定义协议的问题,在这里我就默认你的STM32设备已经可以和电脑的串口助手可以通信上了。如果还没有实现这一功能,建议参考STM32FX开发指南(库函数版),下方我也会提供出源代码供大家参考。- H5 g3 n7 F# ~9 j' s6 C& r
因为我硬件采用的是STM32F407,大家可以根据自己的实际情况进行移植;
6 l, m7 E$ r& H( o1 S你准备好了吗?接下来跟着我的节奏开始.......& |/ A% |' m0 ~4 k
第一步:替换中断服务函数;
. }4 I$ @! F$ [/ Z A9 _$ }把原来的中断服务函数用下面的函数代替(函数可以在usart.c文件中找到)3 l% A# N+ h0 ] M. A" G
- void USART1_IRQHandler(void) 3 D) F. g# O0 ~6 }) `& q7 \+ d
- {. {! Y6 u# s( |
- u8 Res;//临时变量,存放串口接收的数据
& z) `; V! U, w6 _+ e& t; N - : [& ^% X; z* K" B# j' H% \7 R
- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断: P) @- ?# k- z0 O4 r/ A
- {
$ `+ m, J0 v) V, \+ B6 ` - Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据3 y2 l9 F5 ~8 y! K7 R
- if(rx_stack.head_flag==1)//收到了帧头
; O% _" t3 _( a0 y6 z - {8 L4 s' F% N! o' H) j
- if(Res==0xBB)//判断当前值是不是帧尾
0 C" ?" ~$ l& ?5 M2 b6 {8 ?2 }3 e - {
) A+ b, u4 P) A7 D - rx_stack.finish_flag = 1;: E, ^# e) v: Z! N, s1 b8 T
- rx_stack.tail_flag=1;
) J4 O, `* a& u: X8 |- f2 y' z - rx_stack.head_flag=0;
! P3 [* T. y$ I" r! t - }
( R ]$ U1 V* M0 g/ B8 @& L0 M - else- T. ?# `0 ?5 v0 Y
- {) ]% p* n) @, p
- rx_stack.recevie_data[rx_stack.data_pt] = Res;6 M$ ?" W9 _1 V- {. B. {
- rx_stack.data_pt++;1 b4 ^# T, o( W8 A# r5 h% }
- if(rx_stack.data_pt > 9)
# R6 M, G7 h1 G2 s+ e) k - {% J. P5 f$ `! J4 A6 U
- rx_stack.data_pt = 0;& e1 ], H; e# s0 H
- }
$ O2 `) ~& R4 U& _( Z% E - } . K7 l) Y; T; b, l( I
- }0 Z" W% s, a; ?$ I7 f' C
- else//没有收到帧头' c' ]# L) I. Y" ^& n: _5 h0 _2 z
- {
8 ?: u6 E7 z3 u- h# Q, A1 W - if(Res==rx_stack.head)
# S( D/ F9 V. k6 V3 S( E+ b - rx_stack.head_flag=1;
# j8 b% K3 ` y" @ d; H- p% p - else7 K, Y7 `+ ^* C# h% l2 E
- {5 n) R/ R, u! f; v! A, n
- CommClr();- q- G3 }1 b8 i7 Q
- return;
4 t& L+ J9 ]$ n$ ~' J2 g - }
4 T" }6 I, j, D$ H; O - }
7 E+ c4 ^$ B% A8 z, @ - }
2 e) y4 B. ?0 r7 T0 X9 | - USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接受中断标志
7 Z! n. t/ V5 `% d - }
复制代码 到这里你会发现,运行之后会出现很多错误,别慌别慌,问题不大,那是因为我的中断服务函数里面定义了一些原来没有的变量和函数;接着往下看.......
6 r2 i1 b2 k+ w/ m" |# ^- c4 S$ E4 c) Z$ Q# X6 h& \2 y
第二步:添加自定义的函数: K2 I9 {( l) R4 W9 q& S
+ {& Z9 E0 o1 }+ v" D- ^$ g- k- void rx_stack_init()
' m) ]$ f, h& @ - {" k! e/ L, \0 O6 O, i' U% j
- rx_stack.head = 0xAA; //协议栈头,起始位" S( D' O! V0 _" m, W0 n" U
- rx_stack.addr=0x01; //从机地址& x# ~2 f9 f( ~3 i! J' b, z
- memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零
. A5 f4 l: T- Z+ g- \, T2 e - rx_stack.tail = 0xBB; //协议栈尾,结束位
" s/ x* U5 C M' E( u/ s8 d - rx_stack.head_flag = 0;9 b, H, O b( H9 ?2 h; G; p$ P7 |
- rx_stack.tail_flag = 0;
2 {% X# J. E0 N - rx_stack.finish_flag = 0;
# d; ]) R1 ^' T/ K, u - // rx_stack.lock_flag = 0;
: ?5 ^, k6 z% Z1 U7 c9 f- {. J - rx_stack.data_pt = 0;
& @0 Z, e, z3 \# p4 t X( O/ o - }6 o, |! O% u1 J1 ?; I3 x8 z
- struct receive_stack rx_stack;: \9 a% {8 {& X1 Y
" y; {3 b5 ?7 C- void CommClr(void)
5 I! R# A5 ]2 q' N) ` j - { P& l [* @2 }# L* L8 s6 P$ ? T
- rx_stack.head_flag = 0;' ^1 z) X- a0 ?7 W! H6 G* B+ @& K
- rx_stack.tail_flag = 0;8 X& @5 W7 C& B0 _
- rx_stack.finish_flag = 0;
T4 a& x# T3 h1 A% X2 W - rx_stack.data_pt = 0;! u8 v+ q9 M' x! g' {
-
) m- o1 ?( L- m0 }. R - memset(rx_stack.recevie_data, 0, sizeof(rx_stack.recevie_data));//把tx_stack.data[]全部初始化为零
& G. S0 g4 y/ U) }( `, J - rx_stack.commPack_OK_flag=0;* j0 a% w* @, u! w! l2 V/ J' \
- }
复制代码 - g( u w9 w: Q7 P7 q' m7 {
把这两个函数放到中断服务函数前面。什么还会报错??当然了,因为我们定义的函数都没有声明,接着来......! q$ B6 m, e) O; E8 T( R
) S: @6 D: M5 w9 }' [ q6 @; t7 D
第三步:自定义函数的声明(可以在usart.h文件中进行声明)
& R/ M. i) W1 L$ T
. T6 U8 V9 x7 w- z4 S% [- Q声明自定义的结构体变量:
! o* O; w8 ?& x) E, f" c
) U5 a/ _8 ^0 a2 a% i# W- struct receive_stack- Z/ O! R# ]7 H/ ]7 W9 r
- {
# N$ i8 Z4 t" L2 r3 i! B) C6 ^2 F0 m - u8 head;//帧头
* D' q* ~8 O; b n; s - u8 addr;//从机地址* f. i/ e8 |& q2 g) S, J
- u8 recevie_data[10];//数据
; S. y: _7 K i& f5 n - u8 check;//校验
' j% l9 k4 V$ e& D3 y - u8 tail;//帧尾
% b+ N$ Y, T. Y2 T - u8 head_flag;//接收到帧头标志位
4 U% O& h* T' t$ T. d3 B" |, b - u8 tail_flag;//接收到帧尾标志位
" O; E9 c6 a0 t: g) M( Z4 G* T - u8 finish_flag;//接收完成标志位- p( a H, v0 B: S% \ ~2 K( |) S
- u8 data_pt;//已接收的字节数8 ]$ Q- V; T( A* L% r
- u8 commPack_OK_flag;//数据解包完成标志
1 Y3 t/ F& I& t3 T) D, E" P - };
/ L# O8 e4 T+ n - extern struct receive_stack rx_stack;
复制代码
& k3 G2 N1 D1 J3 v声明上文中自定义的两个函数:7 y5 x: X( z& T
- void rx_stack_init(void);2 z) h9 L$ J- l5 A+ r V- M; G
- void CommClr(void);
复制代码 8 y d- E8 D: Z
现在是不是就没有错误了,但是可能还有几个警告。如果还有错误的话应该是因为_sys_exit(int x) 这个函数,把他改成void _sys_exit(int x) 再试一试,错误应该就没有了吧。那警告怎么去呢?出现告警可能是因为我们使用了memset()这个函数,我们只需要在usart.c中包含#include <string.h>头文件就可以了。现在理论上应该是没有任何报警了,你的是不是这样的?! A/ r8 A$ t7 C
2 t& T) V3 [4 U7 V7 e4 K第四步:验证是不是能正确返回数据
5 _( }4 A5 h4 w' U4 q8 n4 k+ ]" d# s$ E9 H
仿照第二步在usart.c中添加串口发送函数
) b4 P7 D i3 V# |* }. ?$ _" G% \' z: `" U) G: N
- void usart_send(u8 byte)/ d! ] Q; ^* o* h/ p
- {; U8 {) b% I4 h4 i7 {
- USART_SendData(USART1,byte);! J5 c9 U2 ?$ i! ]0 N
- while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//发送完成标志位6 v" i; D' ~3 }' g& U
- }
复制代码
) \0 y9 C* [' B3 ?7 n别忘记声明这个函数;& [, E6 _2 R: A0 n
2 [( m- o+ Q: l1 b第五步:写主函数: F' F% L7 o4 }: e5 u4 |; l c4 a
- int main(void)
1 e. \( N( c8 @3 Y9 i# W - { - z. H2 ?, @) x. F3 [& h
- 1 i; G. P4 g8 i- j" Q
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组26 ]/ Q* |# @3 p, V* J8 K# u
- delay_init(168); //延时初始化
3 z. E" F& O' q- y6 \) e+ | - uart_init(9600); //串口初始化波特率为115200
6 K" _3 X. N: F; c6 y! q- c3 | - LED_Init(); //初始化与LED连接的硬件接口 ' y& R# j4 {, ?# w
- while(1)0 ?- k% w3 ~6 Z- z& W: Y- q
- {2 P% M3 g2 B2 d: L& i
- u8 i;* A1 U1 A; D0 |
- u8 SendBuf[5];
4 ]0 o6 }1 S( Q- y - if(rx_stack.finish_flag ==1)//数据接收完成
& ^+ m$ l& u3 u: g' J8 u - {
. B5 k! ~6 t+ m - SendBuf[0]=0x7B;. H. R' z/ S% x* U G& P
- SendBuf[1]=rx_stack.recevie_data[0];
+ g& I* _/ l. v. v5 G* `, i d! u - SendBuf[2]=rx_stack.recevie_data[1];
- Q3 e0 i% o# }+ F" j j* { - SendBuf[3]=rx_stack.recevie_data[2];' ?2 M5 O' H' z& `5 x! P" W
- SendBuf[4]=0x7D;
. l! \6 y9 ]5 }8 C% E# y - for(i=0;i<5;i++)3 _" e. Z: U: b
- {
9 R Q7 ^* \$ a! D1 O - usart_send(SendBuf[i]);
3 }5 Z- T% Z/ f7 q - }
! ^ S4 M& O7 @ - }
1 U# P! U3 @: Y" r9 J- h - }
0 g4 x+ E# X4 d# o+ ~' Y - }
复制代码 : t+ a) m: v2 \# B5 K* D
编译一下,把程序下载到板子里面试一试,当你用电脑的串口助手发送AA 01 01 02 BB 给你的目标板,它能把发的数据原样传回来吗?坛友们一块讨论一下。
9 z9 _) H+ V) X" ~% C
2 Y4 }$ U- C1 @; \* P5 T5 r5 f( c& K" v8 x
" f: T4 Z; C6 e* w
4 m' @3 T0 @# ~* W
' d5 z% i6 f: Y! v# X& B% v |