你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32进阶之串口环形缓冲区实现  

[复制链接]
xiaojie0513 发布时间:2018-6-4 09:49
本帖最后由 xiaojie0513 于 2018-6-4 09:55 编辑 ! }6 f" U6 ^+ C5 Z8 N

, u4 Y0 U; L1 o5 }+ R2 @. Z/ q队列的概念9 ]5 N- C$ i, K$ [: t
在此之前,我们来回顾一下队列的基本概念:
5 |: C! ?: I, O$ ]  队列 (Queue):是一种先进先出(First In First Out ,简称 FIFO)的线性表,只允许在一端插入(入队),在另一端进行删除(出队)。
2 Q1 ?# L3 [5 Y* i
' |% `1 V/ ]/ p2 N! z+ l3 A
队列的特点
类似售票排队窗口,先到的人看到能先买到票,然后先走,后来的人只能后买到票

# Y8 F" _6 A" E1 H队列的常见两种形式
9 p2 l5 p5 l0 y* u! y$ O* ?/ \' A) J% S2 h7 F; J  _9 t, H) b! P
普通队列! r4 }8 ~. O8 S3 d6 I8 U6 N$ }
4 y: n* x9 o! s8 ^/ a4 C
  在计算机中,每个信息都是存储在存储单元中的,比喻一下吧,上图的一些小正方形格子就是一个个存储单元,你可以理解为常见的数组,存放我们一个个的信息。2 w. W6 H% }9 q2 k( e- z

; k( C2 j3 ?" g4 _. E   当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,那么处理完后呢,就会把数据释放掉,再处理下一个。那么,已经处理的数据的内存就会被浪费掉。因为后来的数据只能往后排队,如过要将剩余的数据都往前移动一次,那么效率就会低下了,肯定不现实,所以,环形队列就出现了。9 _4 r7 \! U7 k2 c+ S
环形队列
, {, F8 @4 N3 m6 T4 S+ [  它的队列就是一个环,它避免了普通队列的缺点,就是有点难理解而已,其实它就是一个队列,一样有队列头,队列尾,一样是先进先出(FIFO)。我们采用顺时针的方式来对队列进行排序。
6 T) a- d/ G6 J0 _+ @( H' k* O" @5 N4 B5 I5 n5 m
队列头 (Head) :允许进行删除的一端称为队首。
% i, p$ w4 r& R$ g8 p' e队列尾 (Tail) :允许进行插入的一端称为队尾。) A( V' T' M: b) ^  I  j
+ {, D1 u+ j& {- p& _! X
  环形队列的实现:在计算机中,也是没有环形的内存的,只不过是我们将顺序的内存处理过,让某一段内存形成环形,使他们首尾相连,简单来说,这其实就是一个数组,只不过有两个指针,一个指向列队头,一个指向列队尾。指向列队头的指针(Head)是缓冲区可读的数据,指向列队尾的指针(Tail)是缓冲区可写的数据,通过移动这两个指针(Head) &(Tail)即可对缓冲区的数据进行读写操作了,直到缓冲区已满(头尾相接),将数据处理完,可以释放掉数据,又可以进行存储新的数据了。/ @( p3 |5 Q3 o3 W6 t+ y
2 K: v7 {( s) B9 \6 ]9 a0 }% _# _
实现的原理:初始化的时候,列队头与列队尾都指向0,当有数据存储的时候,数据存储在‘0’的地址空间,列队尾指向下一个可以存储数据的地方‘1’,再有数据来的时候,存储数据到地址‘1’,然后队列尾指向下一个地址‘2’。当数据要进行处理的时候,肯定是先处理‘0’空间的数据,也就是列队头的数据,处理完了数据,‘0’地址空间的数据进行释放掉,列队头指向下一个可以处理数据的地址‘1’。从而实现整个环形缓冲区的数据读写。. j: M! B8 O* h! ^3 ]; j7 |

: o6 t) I2 [; k3 C  看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个CPU处理的数据就是1;而队列尾则指向可以进行写数据的地址。当1处理了,就会把1释放掉。并且把队列头指向2。当写入了一个数据6,那么队列尾的指针就会指向下一个可以写的地址。
5 l$ {$ A. V$ B0 d( A
( e  G9 j+ M# x
如果你懂了环形队列,那就跟着歌曲来一步步用代码实现吧:
从队列到串口缓冲区的实现; m' f! r' o, H' x: N( V
  串口环形缓冲区收发:在很多入门级教程中,我们知道的串口收发都是:接收一个数据,触发中断,然后把数据发回来。这种处理方式是没有缓冲的,当数量太大的时候,亦或者当数据接收太快的时候,我们来不及处理已经收到的数据,那么,当再次收到数据的时候,就会将之前还未处理的数据覆盖掉。那么就会出现丢包的现象了,对我们的程序是一个致命的创伤。& ]9 ~8 o  R  B

( x' D& E1 U% U( R
1 m  D5 o2 f' \' p0 m  那么如何避免这种情况的发生呢,很显然,上面说的一些队列的特性很容易帮我们实现我们需要的情况。将接受的数据缓存一下,让处理的速度有些许缓冲,使得处理的速度赶得上接收的速度,上面又已经分析了普通队列与环形队列的优劣了,那么我们肯定是用环形队列来进行实现了。下面就是代码的实现:8 D  u# G4 N; Z* R8 f% Q# b. F
8 Q+ _' Y0 f  M( O! X
①定义一个结构体:" d1 C3 Y' F; c$ p/ E6 W
1typedef struct
; V4 n8 g8 @6 F9 a, w. m$ y' S2{

0 l: K, p' ]% D" P+ C3    u16 Head;           
) d2 r, c3 k/ Y  G8 n0 N; \0 w+ C4    u16 Tail;
8 L6 T" Z2 X. {' @5    u16 Lenght;
+ @" |9 m/ J# a  `( @" r' C1 }6    u8 Ring_Buff[RINGBUFF_LEN];; w1 a* g$ M- I: Y
7}RingBuff_t;
/ q2 E1 N1 {  n/ Y# V8RingBuff_t ringBuff;//创建一个ringBuff的缓冲区2 |# q. V1 r8 {. \: k1 L% v0 [
; l1 [8 x( V8 k3 P& z
②初始化结构体相关信息:使得我们的环形缓冲区是头尾相连的,并且里面没有数据,也就是空的队列。- e0 N: ?; ~! d
1/**
( n6 ~. T) J/ z# Q- j 2* @brief  RingBuff_Init
6 b4 h( Y' p+ {! Y( ~* s& D 3* @param  void
9 b9 k" p/ t$ u! O2 Z8 M! x$ I 4* @return void
/ ^: a7 w" t5 S" U% c: x5 I 5* @author 杰杰2 }1 j) I* T1 W
6* @date   2018& {( ], p$ K. R  l0 u+ o
7* @version v1.0* O. y# \  s2 e/ I. q
8* @note   初始化环形缓冲区& c% v+ q& [, G: M7 y
9*/
7 w6 e. I+ [# m3 Z0 W* k
10void RingBuff_Init(void)
  R1 f$ W  Z: n7 O8 k11
{
+ s2 N* C6 p8 M5 d9 F  A12   //初始化相关信息
/ b' d& U- {! |& O* t! m- M13   ringBuff.Head = 0;
# ~) a  n: k. W5 E14   ringBuff.Tail = 0;
' p* C  x/ w* J$ U: Y9 [7 q15   ringBuff.Lenght = 0;
% L# L: U0 K' W7 X; t  |2 d16}, f: Z( w# ^  s$ \" Y9 a+ `" [
初始化效果如下:2 I6 l0 M2 D* \7 v2 Y

! k' J6 C% P6 M& U% E4 q9 l
/ V1 T, X; T' K- d写入环形缓冲区的代码实现: 1/**
: F7 r& n# _5 j- M1 u 2* @brief  Write_RingBuff
3 |( z7 [% ^% |" Y( g 3* @param  u8 data
; s/ J8 t3 r; Z# z4 X* d 4* @return FLASE:环形缓冲区已满,写入失败;TRUE:写入成功
9 o9 ~7 S3 [0 t5 _: T 5* @author 杰杰
1 i7 M5 q7 N5 @: K6 ? 6* @date   2018! Y5 X4 r8 I  Y( H( i
7* @version v1.0
5 _  f2 H$ q8 P/ s- p 8* @note   往环形缓冲区写入u8类型的数据' K3 d4 A* q$ W3 ]# u0 D( A$ ^# G
9*/

. ]) A/ @' P; W3 C& I" V10u8 Write_RingBuff(u8 data)  L5 N3 {$ c0 [4 M2 \) l
11{% B: x" ^$ m0 P& U2 R, D
12   if(ringBuff.Lenght >= RINGBUFF_LEN) //判断缓冲区是否已满
0 N" d( k2 @2 m/ a2 p  M13    {
0 Q8 U4 x4 a* [/ P) ^$ H14      return FLASE;. f% V7 U2 Q2 Z, c/ R
15    }! e5 x7 z- x! ]+ ?
16    ringBuff.Ring_Buff[ringBuff.Tail]=data;: E& `4 `+ w8 {. j; k" J) W& _; L- [
17//    ringBuff.Tail++;
4 `! Q6 i7 B- J! p9 T9 @8 D18    ringBuff.Tail = (ringBuff.Tail+1)%RINGBUFF_LEN;//防止越界非法访问/ }9 @  m8 W$ V9 l- D1 `4 w3 I/ _
19    ringBuff.Lenght++;
2 ?: c2 W9 M" A  l  C20    return TRUE;; A9 M/ F- }+ g7 s5 k- V
21}
0 B5 |6 u% I2 J3 Y% o4 X* J. K3 j$ R读取缓冲区的数据的代码实现: 1/**
& [: y: Z1 J8 D 2* @brief  Read_RingBuff  w5 n! o& C: e- P% e" T4 T
3* @param  u8 *rData,用于保存读取的数据
) H% G1 A1 s) w; M5 _* A3 ~ 4* @return FLASE:环形缓冲区没有数据,读取失败;TRUE:读取成功: U( W3 B; {& `- w) W  S8 g5 X8 F
5* @author 杰杰
; l$ T. f! T9 D; W4 |- m% F3 E 6* @date   20181 K- U; s$ M+ g6 r2 s
7* @version v1.0
5 s4 j+ K) `, e" T! p8 {2 h 8* @note   从环形缓冲区读取一个u8类型的数据
  X: {) X7 C$ {+ ~2 V* w' m+ A 9*/
% s0 o" c# t$ q) S; T
10u8 Read_RingBuff(u8 *rData)
) M) e4 R: ~# q" I; ~8 S# n1 I11{/ z0 ?' `# j8 S( A9 c6 N
12   if(ringBuff.Lenght == 0)//判断非空
( n/ n( L1 c* v1 r& A2 p13    {+ D; P9 [" w8 O4 v" K1 d
14       return FLASE;3 ^' G9 x! t  v2 O% A
15    }
& j, E! B2 g0 C1 \- T( t16   *rData = ringBuff.Ring_Buff[ringBuff.Head];//先进先出FIFO,从缓冲区头出/ P4 x& K% z  G" m7 n
17//   ringBuff.Head++;$ e/ p0 i  w, o3 i" d
18   ringBuff.Head = (ringBuff.Head+1)%RINGBUFF_LEN;//防止越界非法访问
( C; B5 L: E) b' t5 G& z6 J1 \% K4 ~" q19   ringBuff.Lenght--;% M+ {7 H; m9 u" e+ @) o! C/ ^
20   return TRUE;
3 [5 x$ Q- ], z* [21}对于读写操作需要注意的地方有两个:
+ {2 a+ d0 y- V. h, @1:判断队列是否为空或者满,如果空的话,是不允许读取数据的,返回FLASE。如果是满的话,也是不允许写入数据的,避免将已有数据覆盖掉。那么如果处理的速度赶不上接收的速度,可以适当增大缓冲区的大小,用空间换取时间。" {1 J& V' A+ L0 s6 N' e8 Q( r
2:防止指针越界非法访问,程序有说明,需要使用者对整个缓冲区的大小进行把握。, X/ d1 W$ X$ W0 ]
那么在串口接收函数中:5 @0 j. o9 p5 a7 T4 e

1 L* p: B2 e$ @) i1void USART1_IRQHandler(void)   % R% z; @* O' f4 n/ q1 q3 H
2
{; {/ r3 S! W  b. u+ B  W0 B  W
3   if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断6 S2 i0 I- F& f# p4 `* h
4                   {% H" u6 `7 G% C' x3 f: ?
5           USART_ClearITPendingBit(USART1,USART_IT_RXNE);       //清楚标志位& ]0 p- Z# q& u+ b1 f4 y
6           Write_RingBuff(USART_ReceiveData(USART1));      //读取接收到的数据
6 W! q9 t. {5 J+ }% c. g7       }
+ i/ i' Z. u# F, u9 ]6 x8}
% |- R: i' G+ L4 ~/ _测试效果
5 \) \9 e3 w2 p! d* `' y1 B测试数据没有发生丢包现象2 p+ U4 `4 @& Q7 |" G
补充  对于现在的阶段,杰杰我本人写代码也慢慢学会规范了。所有的代码片段均使用了可读性很强的,还有可移植性也很强的。我使用了宏定义来决定是否开启环形缓冲区的方式来收发数据,移植到大家的代码并不会有其他副作用,只需要开启宏定义即可使用了。
- z8 ]6 _: J1 V/ U" b) C5 [3 A 1#define USER_RINGBUFF  1  //使用环形缓冲区形式接收数据- c9 b3 _8 a6 T* I3 A2 i7 L
2#if  USER_RINGBUFF4 p1 E: a4 y, D0 o0 d3 e
3/**如果使用环形缓冲形式接收串口数据***/( d  {  y# s# L
4#define  RINGBUFF_LEN          200     //定义最大接收字节数 200: }/ ?8 ]0 W* J+ n$ y1 j
5#define  FLASE   1   L! ^) L, J0 ?
6#define  TRUE    0 ) |+ s3 p" A5 V$ O$ k
7void RingBuff_Init(void);
" I( e  C# W( ?- W9 [3 X# _  C- q 8u8 Write_RingBuff(u8 data);
" D% I5 r; J2 U% h! \* y- w% T 9u8 Read_RingBuff(u8 *rData);
) c" o; ?* I4 p8 O10#endif
1 M3 a) E6 Y' z$ b% U5 T2 [- ^
/ x+ n9 D  B$ v+ z' e* B    当然,我们完全可以用空闲中断与DMA传输,效率更高,但是某些单片机没有空闲中断与DMA,那么这种环形缓冲区的作用就很大了,并且移植简便。
5 o9 e& U2 C3 a" r; A
说明:文章部分截图来源慕课网james_yuan老师的课程
小编:CK
往期精彩回顾
创客:
创客飞梦空间是开源公众号6 }; M2 d: z( ~) v3 a) o9 P
欢迎大家分享出去
! g7 _& ~2 q, H也欢迎大家投稿* p. ]9 j% u0 v- P7 j2 G# O' n

2 p8 p, E* Y9 u# N8 o* Z3 U- b
0 n4 |- I$ B. X( |% t" G( T  c' Y$ d. l  C7 L

实验4 串口实验.zip

下载

2.62 MB, 下载次数: 934

评分

参与人数 1 ST金币 +3 收起 理由
努力的人 + 3 加油,欢迎继续分享

查看全部评分

收藏 8 评论112 发布时间:2018-6-4 09:49

举报

112个回答
roguebear2012 回答时间:2018-6-7 21:11:46
给lz看一个移植的linux的串口fifo 用在stm32上的。' ?7 \  m, D- `3 k' H& P2 q
#include <kfifo.h>* T" e+ g: u# ^" O% p
) B+ p7 o- {+ f2 F  u, ], A
. ]) m1 Z3 F0 ~+ d( ?! C6 c5 A% X
) N) Q. b0 g5 g

) @8 V( t2 x  P/ g7 ^+ Oint __kfifo_init(struct __kfifo *fifo, void *buffer, unsigned int size). i% B$ [- T7 S
{
* x; T& L) V3 f* R    fifo->in = 0;, M  A  H1 t! K" O( X
    fifo->out = 0;3 L& Y5 |7 U, S2 u
    fifo->mask = 0;
" _* e" b& J( L) B0 o    fifo->data = buffer;
0 H6 i- x  B) Y" E0 L! Q1 s, b+ G0 ]+ q
    if (size < 2) {
$ [/ k  P6 V; ~        return -1;
: [" C- y$ e- r1 G! ^2 A/ S    }
2 [* p' U7 {0 \7 U" J0 |$ F% U+ @
3 C7 x. n# h- Q; G! _8 N! @& X% a    if ((size & (size-1)) != 0)
( u. l. H/ J$ J: a+ j0 `    {  `2 R& W, b0 W) V8 `) D- W" q
        return -2;            /*** Must be a power of 2 (2,4,8,16,32,64,128,256,512,...) ***/( U- B1 E: b$ \' Y% o
    }
; |2 r: R5 M% Z) M; C6 Y, G  S  E3 _3 i; V, Q$ {$ N
    fifo->mask = size - 1;
, w) q" J7 R. c, t- y9 o
# n" h0 |) `: V' A0 q0 t+ C6 F) D    return 0;, P2 c' O! [( N1 ~$ u* O& p# w6 R' K: {
}7 i: j( o4 s1 t" |1 {

7 A, j3 B7 h1 B' Q* Y+ P6 I( J. o4 a0 v: S
' v7 ^$ s+ P: X( b

* `' o2 v! [, W0 a
6 B) x# A8 ?7 z+ \3 ^unsigned int __kfifo_push_char(struct __kfifo *fifo,  const char *p)
, U0 ~3 @2 G$ [+ X+ J  Z{+ ?8 l6 s- H' N, q+ n3 e+ O
. p$ I8 ]* ~( x
    if(( ( fifo->in - fifo->out ) & ( ~fifo->mask ) ) == 0 )
2 W! n5 w1 O5 o1 g  q    {
! _+ S& t, I# f$ l/ j9 ^1 H/ C        *( (char*)fifo->data + (fifo->in++ & fifo->mask) ) = *p;- E' E5 B' F; y

# L9 V- d2 z: j        return 1;
" `9 I# Q. |8 P. A& j' w4 U    }1 P/ x( A9 ^# s) d' U$ U! P0 p

  V( P- `6 \8 j- Z( I    return 0;
# X+ `0 P% m( ~1 ?}
9 h; l; c. a* Z" J4 i" s2 D" x) T7 l, m3 l9 ]5 D

  ~2 b6 d- w. R& z7 m0 |5 n$ y8 c% y! @) X8 i% }& K/ P

+ V& S, i1 d! a( ]) _: R- [" |$ c' Z0 i* _7 v% E1 Z: p
unsigned int __kfifo_pop_char(struct __kfifo *fifo, char* p )
1 J) i+ y. a! Q/ u4 o: A{
8 p' o3 O7 U1 S2 K% Q7 W    if( fifo->in != fifo->out )
. K3 x4 a5 a4 T# O# u$ b5 s, d    {8 A$ k+ j- Y& a, }
        *p =  *(  (char*)fifo->data + (fifo->out++ &  fifo->mask) ) ;4 ]$ z  f& M1 Q& T# J
% r* I- x3 u9 U9 J& k% J
        return 1;
% v1 d7 u- I4 Z% Z, X; \1 d. B    }
$ u: }$ B7 y) r4 j; H. U' v9 S7 y6 D, _, Z  W: L: G
    return 0;7 O! \4 c0 V) x0 U; C3 v
}$ b. e; b/ A. s) n6 @

% U# q7 t; x3 R  z3 @* M5 k
7 O6 M3 y% V. {8 g/ h* G% r+ v
) E6 z& W  v, @7 ~2 P
hi201803 回答时间:2018-6-14 09:52:46
本帖最后由 hi201803 于 2018-6-14 10:03 编辑 0 m9 t0 _0 P& u- S& h* F

) ~( N- B. Z0 Uok,  了解了解! e1 G$ p& D9 [: _
=========================================- z. ?3 C4 a# z& S& b' \2 g
看了源代码, * R8 b+ R) o7 R% h
. P+ q# L' n7 m' P- D7 Q
串口接收与发送缓冲区FIFO 的实现 , 在 keil 51 里面 有一个简单例子, 非常好. 适用于单一资源提供者与资源消费者, 简单的代码里面就解决了资源提供者与消费者之间协调的问题.  可以去看看." y4 i. P2 f8 C, r1 P# C
! x' g. T4 O* P6 J
' g7 ]( ?, \2 x
七哥 回答时间:2018-6-4 12:50:10
本帖最后由 toofree 于 2018-6-4 16:42 编辑 " ]5 E0 r4 @; Z9 E6 Q. K
xiaojie0513 发表于 2018-6-4 11:092 t& {4 ^: n" }+ R% v# r0 K0 s
数据结构是个好东西,我还得去学
6 I% @- Y6 K2 Y- i6 p, a( D# d
我也没正规学过数据结构,是考3级数据库时,数据结构是必须的。
' t& ^- j& l' G( y# k% I0 C3 J一个暑假自学完一本数据结构课本,做完一本题库。
9 D2 S1 ~3 y0 A4 X# }7 k& E2 v9 G; P& i3 f/ }, Q+ j$ @1 \5 }+ ?
最近让破总给我买了几本书,C++课本、习题、C++数据结构。
MrJiu 回答时间:2018-6-4 09:54:39
还不错。。。其实呢,搞懂数据结构这本书后,这些都是小意思,难点高的是树什么的!!!
电子星辰 回答时间:2018-6-4 09:54:41
有意思,先看看
xiaojie0513 回答时间:2018-6-4 09:56:03
MrJiu 发表于 2018-6-4 09:542 v/ y# E; S- J; f: R; H$ V/ A1 I
还不错。。。其实呢,搞懂数据结构这本书后,这些都是小意思,难点高的是树什么的!!! ...

  ^* I+ r: b; v5 X7 U; L5 H嘻嘻嘻我瞎搞的
xiaojie0513 回答时间:2018-6-4 09:56:14
电子星辰 发表于 2018-6-4 09:54+ }8 L. v: a4 `9 c
有意思,先看看

3 `6 P  M( y% y8 _: t/ V
勿忘心安110 回答时间:2018-6-4 10:01:12
看看再说
七哥 回答时间:2018-6-4 10:09:15
本帖最后由 toofree 于 2018-6-4 10:14 编辑 ( `5 K/ j4 Q; _8 ]7 D
. u( J# x2 T7 c
新上任的版主们最近都很活跃
xiaojie0513 回答时间:2018-6-4 10:15:23
toofree 发表于 2018-6-4 10:09* o8 U9 d  i: |
新上任的版主们最近都很活跃

0 g* T- J+ p: a7 N% t+ O是吗是吗
七哥 回答时间:2018-6-4 10:24:14
MrJiu 发表于 2018-6-4 09:54
1 D6 k. V/ k/ m, j# p还不错。。。其实呢,搞懂数据结构这本书后,这些都是小意思,难点高的是树什么的!!! ...
2 s; K( E- i0 R5 A
自从考完试再没摸过,就记得个“二叉树”名字了
hunyuanqi 回答时间:2018-6-4 11:02:21
看看,学习学习!!!!!!!!!!!!!!!!!
xiaojie0513 回答时间:2018-6-4 11:09:47
toofree 发表于 2018-6-4 10:24  \' L/ C' F3 ?' f3 P
自从考完试再没摸过,就记得个“二叉树”名字了

" f2 \7 n* U& ?$ l数据结构是个好东西,我还得去学
kunchen 回答时间:2018-6-4 11:55:53
MrJiu 回答时间:2018-6-4 13:42:11
toofree 发表于 2018-6-4 10:24
. V% T; ^& a' ~: k  m$ R0 X自从考完试再没摸过,就记得个“二叉树”名字了
. h6 h7 @2 x" t) _7 \: A0 ~( u
数据结构还是用处很大的!!!
yqsqqq 回答时间:2018-6-4 14:01:11
........................\

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版