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

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

[复制链接]
xiaojie0513 发布时间:2018-6-4 09:49
本帖最后由 xiaojie0513 于 2018-6-4 09:55 编辑
- D' |0 ~  S+ f7 I0 K: P" k& V! m0 K) h- _$ ]/ }9 `
队列的概念: ], A' W8 g0 o' j' }* o( P. ^; X
在此之前,我们来回顾一下队列的基本概念:
! Z6 C- F1 @7 B- f  队列 (Queue):是一种先进先出(First In First Out ,简称 FIFO)的线性表,只允许在一端插入(入队),在另一端进行删除(出队)。/ w. R  x( n( |1 p% f5 P% {

8 q: ?' ?% R; u( S- k* }
队列的特点
类似售票排队窗口,先到的人看到能先买到票,然后先走,后来的人只能后买到票
6 B2 N7 `: S( |! A( u& t
队列的常见两种形式
! y# O4 [5 x3 }! Z$ i5 `6 n: _8 K2 e2 Z, M4 j# m
普通队列3 Z) N# Z- [, A& A/ u: V
, c4 I5 b5 h/ J. ~; {/ ?
  在计算机中,每个信息都是存储在存储单元中的,比喻一下吧,上图的一些小正方形格子就是一个个存储单元,你可以理解为常见的数组,存放我们一个个的信息。$ O; Q, V$ t9 @% P

7 G- }) @8 r& {- I   当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,那么处理完后呢,就会把数据释放掉,再处理下一个。那么,已经处理的数据的内存就会被浪费掉。因为后来的数据只能往后排队,如过要将剩余的数据都往前移动一次,那么效率就会低下了,肯定不现实,所以,环形队列就出现了。4 u( N+ R+ R9 L
环形队列
7 K' j- d$ t9 f3 V- ^2 d  它的队列就是一个环,它避免了普通队列的缺点,就是有点难理解而已,其实它就是一个队列,一样有队列头,队列尾,一样是先进先出(FIFO)。我们采用顺时针的方式来对队列进行排序。
7 ?, ^; |  Z! ]; V# T+ K- Q/ H! ~) @0 E' m, b/ A
队列头 (Head) :允许进行删除的一端称为队首。
6 W. K4 B7 S: m# I2 V, {5 g- ~队列尾 (Tail) :允许进行插入的一端称为队尾。
6 t# ^& e% L* |) V4 @. I( O' Q' v2 X* M
  环形队列的实现:在计算机中,也是没有环形的内存的,只不过是我们将顺序的内存处理过,让某一段内存形成环形,使他们首尾相连,简单来说,这其实就是一个数组,只不过有两个指针,一个指向列队头,一个指向列队尾。指向列队头的指针(Head)是缓冲区可读的数据,指向列队尾的指针(Tail)是缓冲区可写的数据,通过移动这两个指针(Head) &(Tail)即可对缓冲区的数据进行读写操作了,直到缓冲区已满(头尾相接),将数据处理完,可以释放掉数据,又可以进行存储新的数据了。) _  r3 \! l. a$ h4 y3 }3 S& N/ h5 Y

6 s. B2 u. h9 |& W) G 实现的原理:初始化的时候,列队头与列队尾都指向0,当有数据存储的时候,数据存储在‘0’的地址空间,列队尾指向下一个可以存储数据的地方‘1’,再有数据来的时候,存储数据到地址‘1’,然后队列尾指向下一个地址‘2’。当数据要进行处理的时候,肯定是先处理‘0’空间的数据,也就是列队头的数据,处理完了数据,‘0’地址空间的数据进行释放掉,列队头指向下一个可以处理数据的地址‘1’。从而实现整个环形缓冲区的数据读写。
, c. |- _! a( p  |9 p4 }$ h* l) v- ~5 E
  看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个CPU处理的数据就是1;而队列尾则指向可以进行写数据的地址。当1处理了,就会把1释放掉。并且把队列头指向2。当写入了一个数据6,那么队列尾的指针就会指向下一个可以写的地址。
6 p6 }- o, ]) N" z4 M, [& b
* |9 n* V! B# L8 u
如果你懂了环形队列,那就跟着歌曲来一步步用代码实现吧:
从队列到串口缓冲区的实现% D% h  D( G4 q6 w
  串口环形缓冲区收发:在很多入门级教程中,我们知道的串口收发都是:接收一个数据,触发中断,然后把数据发回来。这种处理方式是没有缓冲的,当数量太大的时候,亦或者当数据接收太快的时候,我们来不及处理已经收到的数据,那么,当再次收到数据的时候,就会将之前还未处理的数据覆盖掉。那么就会出现丢包的现象了,对我们的程序是一个致命的创伤。5 X0 ^8 ^) ^$ C- J! b

% D( w) \% ~$ o7 O& V" R
' q4 I) K* c, x  H  那么如何避免这种情况的发生呢,很显然,上面说的一些队列的特性很容易帮我们实现我们需要的情况。将接受的数据缓存一下,让处理的速度有些许缓冲,使得处理的速度赶得上接收的速度,上面又已经分析了普通队列与环形队列的优劣了,那么我们肯定是用环形队列来进行实现了。下面就是代码的实现:0 K) v/ ?7 L/ A7 F& p/ n( J

! K0 ^4 R! k- i- g6 q①定义一个结构体:
- [) o  m; \6 r: }1typedef struct
% {  q9 d. a% d+ b! a. f2{

- ^# {: P8 }; d5 H4 J" ^3    u16 Head;           & `+ \* K/ P9 O+ e0 M1 k+ q/ B: A
4    u16 Tail;) Z4 L% ^9 B( I& u6 @
5    u16 Lenght;4 z) h6 V5 x$ b, n6 V& r
6    u8 Ring_Buff[RINGBUFF_LEN];
: y! u6 r4 N# f( Y- g* v7}RingBuff_t;, X, x2 Y7 V- x' Y, f
8RingBuff_t ringBuff;//创建一个ringBuff的缓冲区( O; j# I7 S9 u. s

4 J, O- B3 J/ A) F②初始化结构体相关信息:使得我们的环形缓冲区是头尾相连的,并且里面没有数据,也就是空的队列。
+ ~" C' b+ ?9 d" T 1/**
& ?! O3 {: x& p7 Z 2* @brief  RingBuff_Init# n$ L- N8 o7 ?' c
3* @param  void
5 ]  z* [5 a0 N( D6 ^7 G- M 4* @return void# a1 F: A4 }) S
5* @author 杰杰
- \% `. z3 i; o" S: j 6* @date   2018: x8 ~4 V: f) j6 |3 g5 j3 x
7* @version v1.0  D9 a: f' D% d% Z* e
8* @note   初始化环形缓冲区3 y( X5 a0 R$ M3 T% x+ W
9*/

5 ]+ e! m, W3 ?: u; ?10void RingBuff_Init(void)( X) t6 p$ a& z; i) d2 D! V
11
{
7 Q: _. y3 z' T12   //初始化相关信息+ g! \0 Q, k. f6 {
13   ringBuff.Head = 0;- Q' w/ G+ r( V5 E* `
14   ringBuff.Tail = 0;1 q0 l& m0 V: a$ N) b: T6 q6 N0 s
15   ringBuff.Lenght = 0;# d3 d, \- V: ^+ G8 ?( u
16}
0 s4 k) X( R" |) j/ l& n初始化效果如下:
/ y7 t( [* ^7 j& `1 x
$ T2 C- J8 H, j, ]: z% u& ]7 t# }# M7 \$ \9 `( T
写入环形缓冲区的代码实现: 1/**2 _+ S2 f. Y) T: \( Z. l5 M
2* @brief  Write_RingBuff0 X0 D% k1 D9 b& H' H% P
3* @param  u8 data
) D. V% @3 J- |4 \% _ 4* @return FLASE:环形缓冲区已满,写入失败;TRUE:写入成功
/ T+ k+ _% C& K  X- D 5* @author 杰杰1 u- v9 i5 ]' E; R& M$ Z& R
6* @date   2018
. t& B' m; z% ^ 7* @version v1.0% W' T/ U! d5 V+ W0 j( @; m
8* @note   往环形缓冲区写入u8类型的数据
  V; n" I2 K3 {# p% o 9*/
: [1 _6 R6 D9 A" W6 U# |
10u8 Write_RingBuff(u8 data)
3 E  E7 N' _$ b3 \' x$ E11{' f: p: Q1 q  C% s5 I$ r
12   if(ringBuff.Lenght >= RINGBUFF_LEN) //判断缓冲区是否已满
) S  V! o, F& t8 {13    {; Z. G7 E; Z+ @8 ^
14      return FLASE;
) m$ a/ V3 }# X& f1 O& n15    }
3 a% b5 q2 E/ [7 T5 b16    ringBuff.Ring_Buff[ringBuff.Tail]=data;
* x. C1 ]; s. V% i; }17//    ringBuff.Tail++;
2 h7 r) n8 O, R2 |5 B+ r- }6 P18    ringBuff.Tail = (ringBuff.Tail+1)%RINGBUFF_LEN;//防止越界非法访问
) {7 J( ]( G4 x- C4 s0 {19    ringBuff.Lenght++;
* d4 P0 {: z( x( w1 V! S% h20    return TRUE;
4 |/ E. L5 U: F5 J21}- g: \+ {% E+ a! r: X
读取缓冲区的数据的代码实现: 1/**! ?; F7 r- K$ c; D7 U& p' y9 d# {
2* @brief  Read_RingBuff, S$ C# R4 l* @
3* @param  u8 *rData,用于保存读取的数据
& B# s& o% Y! i* O" R/ k" ], l2 r 4* @return FLASE:环形缓冲区没有数据,读取失败;TRUE:读取成功& u2 H* y; U! o3 @5 F% D- Q
5* @author 杰杰+ x0 T7 b, Q$ m, r3 W% W. N
6* @date   2018
: ^- Q7 Z1 J( i# v 7* @version v1.04 U* R" ?* p! R8 v
8* @note   从环形缓冲区读取一个u8类型的数据& f/ z+ n3 d  e/ c& t
9*/
/ L0 C3 w1 E! B- q% R
10u8 Read_RingBuff(u8 *rData)
$ O1 e! o9 e  @7 A* U% O11{$ H& ?1 E) v* N+ }3 ~
12   if(ringBuff.Lenght == 0)//判断非空
3 w5 o5 e& {% o6 Z- x13    {
( d$ P5 U' r: g9 o) F0 ^14       return FLASE;! V* o- f8 S( c, h
15    }
8 K2 X+ W, l% Y16   *rData = ringBuff.Ring_Buff[ringBuff.Head];//先进先出FIFO,从缓冲区头出
7 W) V" d6 e* n7 l" V17//   ringBuff.Head++;# d, b6 |$ K$ H$ S9 p% S
18   ringBuff.Head = (ringBuff.Head+1)%RINGBUFF_LEN;//防止越界非法访问9 s; r. y6 d  F' o) f+ W
19   ringBuff.Lenght--;8 B& ]3 \: Z1 e" p, K5 ?! K
20   return TRUE;
6 @* e9 Y+ W2 h5 v. y# I21}对于读写操作需要注意的地方有两个:4 i; P$ S; l2 u9 A
1:判断队列是否为空或者满,如果空的话,是不允许读取数据的,返回FLASE。如果是满的话,也是不允许写入数据的,避免将已有数据覆盖掉。那么如果处理的速度赶不上接收的速度,可以适当增大缓冲区的大小,用空间换取时间。
; N! o4 W) D; V, U, b2:防止指针越界非法访问,程序有说明,需要使用者对整个缓冲区的大小进行把握。
1 s  S7 T8 |  K2 C( w那么在串口接收函数中:* o5 M5 T9 z( ]( l7 d2 j5 [# y

* q- _, w; ^7 K  {1void USART1_IRQHandler(void)   
. d1 O4 p- t+ k: `& W9 H2
{
3 b7 n9 j* \* n3   if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断( t6 q: }: S7 s! a
4                   {/ M. {+ K9 ^% f2 _
5           USART_ClearITPendingBit(USART1,USART_IT_RXNE);       //清楚标志位
6 G: l! |: i: ?# i+ P9 M6           Write_RingBuff(USART_ReceiveData(USART1));      //读取接收到的数据7 z3 W0 j! G2 c/ H8 b  c1 l( p$ E
7       }
5 ]2 {% j6 @- `4 o+ m' i8}
: y# V* b$ |7 Z3 e7 `测试效果$ F1 e0 v! k  q, ]
测试数据没有发生丢包现象
8 b; i5 A# e+ |- A补充  对于现在的阶段,杰杰我本人写代码也慢慢学会规范了。所有的代码片段均使用了可读性很强的,还有可移植性也很强的。我使用了宏定义来决定是否开启环形缓冲区的方式来收发数据,移植到大家的代码并不会有其他副作用,只需要开启宏定义即可使用了。$ o: w# y; d4 \/ w
1#define USER_RINGBUFF  1  //使用环形缓冲区形式接收数据
( K, B$ d0 F. e 2#if  USER_RINGBUFF; Q7 [" |( d% r7 I& {; C
3/**如果使用环形缓冲形式接收串口数据***/6 ~0 ]- ~" o3 K! @, U
4#define  RINGBUFF_LEN          200     //定义最大接收字节数 200
, z! O9 ^# }/ m, |0 ?+ ~ 5#define  FLASE   1 1 ]4 s3 L& T, X* L+ x, P
6#define  TRUE    0 . |- \, I5 s/ p( t
7void RingBuff_Init(void);9 G! M" W9 N1 u& w8 n- |! f: q7 [
8u8 Write_RingBuff(u8 data);# Z  ~3 t  ~0 s0 g! N
9u8 Read_RingBuff(u8 *rData);; ?' y7 \9 n* z1 I* @. p
10#endif7 ^- _# e: N3 T8 x6 g

( V9 r7 P1 E1 f, T; [, w' W" K    当然,我们完全可以用空闲中断与DMA传输,效率更高,但是某些单片机没有空闲中断与DMA,那么这种环形缓冲区的作用就很大了,并且移植简便。
# S2 R/ X- u& s: ]! T  y
说明:文章部分截图来源慕课网james_yuan老师的课程
小编:CK
往期精彩回顾
创客:
创客飞梦空间是开源公众号$ t6 _" f! U6 S3 W% p/ j
欢迎大家分享出去, M  a7 c2 z; ^0 C
也欢迎大家投稿: v8 V' q2 n0 h* C( v0 z9 `

5 V: ?9 \$ O7 Z% [0 f) n0 ]+ u, |; ]0 X0 g( i! C. G$ U2 k7 O* R
0 S  n' S4 O1 n0 H2 I3 _5 g, Z

实验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上的。
! d9 k- s1 L1 Q( q#include <kfifo.h>
. S% d+ Q* C2 V7 l5 T( U' L" D6 m! Q; }8 l
2 u4 Z6 K8 ^1 b! _% Q
% j& A1 \% h0 b' @, ^( Z
: g$ i6 M7 F5 d% f
int __kfifo_init(struct __kfifo *fifo, void *buffer, unsigned int size)% X# g! M5 p" g, P
{
1 c4 W& N: L( X6 ?* n8 [    fifo->in = 0;
( Y, c" N$ H" A  s    fifo->out = 0;
$ Z' W6 s1 }; L0 V% I    fifo->mask = 0;
6 {: n5 P0 d: k5 f- D    fifo->data = buffer;
! H5 @; M. A9 r" ^8 Z& c; V! ~  r0 B/ R  ]- |
    if (size < 2) {* ]5 S3 B$ m8 P! v7 I% a/ A
        return -1;
, m% d# s9 T- s( v- J( B1 ~& p: x    }
* a5 B. Y: A# H: ^+ L' [+ Y# p% @5 f4 g2 _( V
    if ((size & (size-1)) != 0)7 F! L/ L1 V! h3 }1 @( |$ k4 t2 y- [2 i
    {' D) `1 I7 n' H# E8 f5 e
        return -2;            /*** Must be a power of 2 (2,4,8,16,32,64,128,256,512,...) ***/
, f0 ~( a, H$ j& g    }) [9 S5 ?, b0 ]& C

0 P; L' l6 t+ G6 a& o    fifo->mask = size - 1;
/ v+ ~. B# @) i' K& ^6 T) ]  ~- r, ^5 }
    return 0;
( W. }7 T* T' @' c0 h}
' b$ I9 @% [0 k6 q5 ]$ o0 O9 G' G; H
( x9 w" h# l9 J0 V; g
! N7 E/ K9 p+ h: g: X$ p! a8 {5 Q7 h. h1 V

/ K1 u1 ~3 Q6 G) G
# y5 r$ c4 j4 b1 y9 x3 r9 Qunsigned int __kfifo_push_char(struct __kfifo *fifo,  const char *p)% N/ F8 l! y. z9 U1 s
{4 K$ {( X" C0 S- J- P  F
/ X' L  t. u5 K& x/ p
    if(( ( fifo->in - fifo->out ) & ( ~fifo->mask ) ) == 0 )
- x9 ^& ]. R5 i    {/ p& i- X7 D5 T% I
        *( (char*)fifo->data + (fifo->in++ & fifo->mask) ) = *p;$ \1 s8 X: U6 t4 F5 v% Z: C
) C7 K* e# B& @
        return 1;7 R+ l! t- z' N& Q/ K7 ~
    }% \4 h8 ^: }$ R4 t

' b+ A1 W) T' Y% ?4 l* \4 p8 Z& c% d    return 0;
' h4 l$ p  q) D) x; D; }7 H}
/ u! |  E6 @& \9 Q  i! O3 B: P$ @1 U

7 ]- I) \% i. J4 I0 X4 R
! O6 ^2 D2 W/ o3 x- Q* l# K# _( n- H1 r6 l* w0 G
8 m+ |2 T8 {9 Z
unsigned int __kfifo_pop_char(struct __kfifo *fifo, char* p )
9 P0 N7 o# w+ T1 L6 v( Z* G{
7 p4 m; J+ A% h8 x( G% x    if( fifo->in != fifo->out )
4 Z& e+ ~/ F; Y" A6 j1 g2 b9 J    {
% M, F3 X; ^% q: S        *p =  *(  (char*)fifo->data + (fifo->out++ &  fifo->mask) ) ;5 q) p7 N( t& v  B7 Z

  C! b9 W8 [0 _$ O! j, v        return 1;5 I+ b9 A) o& x; o1 m* c1 j# P. [
    }
$ |1 a6 e$ `8 L" K  t
4 [7 h# ?1 t6 _# f& C" c! f; H    return 0;
3 U" W; U" w/ S! b1 B}1 U+ h6 Z# A' ~; n5 H% [
  z% x5 J7 B3 e+ Q* W: c: N' d

) s4 G% h! Q8 Z# q2 Q& f  _& K7 ]" ^7 W
hi201803 回答时间:2018-6-14 09:52:46
本帖最后由 hi201803 于 2018-6-14 10:03 编辑
! I$ Y9 p7 X9 L  u$ X; g& v: K6 s$ _9 m
ok,  了解了解
& m/ ]5 q1 y; S=========================================
1 m2 Q- @; F" l' Q; h' k看了源代码,
  n/ c' x% |. D& e3 E5 J4 u) Q+ K# [7 X# o6 f9 ]+ D
串口接收与发送缓冲区FIFO 的实现 , 在 keil 51 里面 有一个简单例子, 非常好. 适用于单一资源提供者与资源消费者, 简单的代码里面就解决了资源提供者与消费者之间协调的问题.  可以去看看.' Q# ?7 E0 D  I/ H  R  X
$ M. R1 P6 G. ^( C' O

# k, x  J4 _) l9 \- G. ^  C3 a
七哥 回答时间:2018-6-4 12:50:10
本帖最后由 toofree 于 2018-6-4 16:42 编辑 + S: n0 z# f9 q* E: a! o( |. {$ J+ e
xiaojie0513 发表于 2018-6-4 11:09
5 L1 F$ S/ R" B& v数据结构是个好东西,我还得去学
  D* N9 C& D2 s  U7 A% ~' k  V
我也没正规学过数据结构,是考3级数据库时,数据结构是必须的。! [8 Y  Z! B/ t, x& z( P
一个暑假自学完一本数据结构课本,做完一本题库。
3 h# ^  ]! {! q! X% r' {, q- t: S: {! G2 R( M  R; s8 q+ N
最近让破总给我买了几本书,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:54$ N9 h6 p+ J) B' o: v- N
还不错。。。其实呢,搞懂数据结构这本书后,这些都是小意思,难点高的是树什么的!!! ...

2 F+ A  c+ C3 T嘻嘻嘻我瞎搞的
xiaojie0513 回答时间:2018-6-4 09:56:14
电子星辰 发表于 2018-6-4 09:542 a2 c) t# O* t+ n4 {5 d
有意思,先看看
- W1 F/ i3 ^. T; n! t* x3 r3 N
勿忘心安110 回答时间:2018-6-4 10:01:12
看看再说
七哥 回答时间:2018-6-4 10:09:15
本帖最后由 toofree 于 2018-6-4 10:14 编辑 8 f8 M# l7 b. R' M

6 T5 Q9 W- L) H- d+ k9 a新上任的版主们最近都很活跃
xiaojie0513 回答时间:2018-6-4 10:15:23
toofree 发表于 2018-6-4 10:09
( ]# n; C( {2 h9 v/ S& Z5 P8 @新上任的版主们最近都很活跃

9 a- c& e: k" Q是吗是吗
七哥 回答时间:2018-6-4 10:24:14
MrJiu 发表于 2018-6-4 09:541 |2 K, E; g' @+ P+ P$ Q
还不错。。。其实呢,搞懂数据结构这本书后,这些都是小意思,难点高的是树什么的!!! ...
. E. `- P- k: J9 E# Q+ C7 S" w; L+ C- {6 D
自从考完试再没摸过,就记得个“二叉树”名字了
hunyuanqi 回答时间:2018-6-4 11:02:21
看看,学习学习!!!!!!!!!!!!!!!!!
xiaojie0513 回答时间:2018-6-4 11:09:47
toofree 发表于 2018-6-4 10:24
. ]) g7 q. P( c* ~' W自从考完试再没摸过,就记得个“二叉树”名字了

* y7 J- m/ g( B" }/ A数据结构是个好东西,我还得去学
kunchen 回答时间:2018-6-4 11:55:53
MrJiu 回答时间:2018-6-4 13:42:11
toofree 发表于 2018-6-4 10:24
+ L& ^, F2 i0 r) x; F2 @自从考完试再没摸过,就记得个“二叉树”名字了

' D, |6 b) w+ Z9 h1 A; t1 }数据结构还是用处很大的!!!
yqsqqq 回答时间:2018-6-4 14:01:11
........................\

所属标签

相似分享

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