搜索
bottom↓
回复: 30

写了一个串口接收的 FIFO ,一直都看不出有什么问题!

[复制链接]

出0入0汤圆

发表于 2016-8-15 16:34:05 | 显示全部楼层 |阅读模式
bsp_US1_RX_int (u8 Data)       这一条是串口接收中断的时候调用,并把出带入。
bsp_US1_RX    (u8 * Data)      这一条是取出缓冲区的数据

现在问题是这样的,是不是的数据会丢一两个位,并且丢了之后,以后收到的数据都会出现错位!
正确的应该是  0123456789  ,但错位后收到的数据  5012345678。  



u16     bsp_US1_RX       (u8 * Data)
{   
    short temp=0;
    while(US1_RX_FIFO_user)
    {
        *Data++ = US1_RX_FIFO[US1_RX_FIFO_tail++];              
        if(US1_RX_FIFO_tail == US1_RX_FIFO_Siz)     
            US1_RX_FIFO_tail = 0;                  
        US1_RX_FIFO_user--;

        temp++;
    }
   
    return temp;
}
inline s8      bsp_US1_RX_int   (u8 Data)
{   
    if(US1_RX_FIFO_Siz - US1_RX_FIFO_user -1 )
    {
        US1_RX_FIFO[US1_RX_FIFO_head++] = Data;      
        if(US1_RX_FIFO_head == US1_RX_FIFO_Siz)     
            US1_RX_FIFO_head = 0;                  
        US1_RX_FIFO_user++;
    }
    else
    {
        return RXbuff_overflow;
    }
   
    return 0;
}

出0入89汤圆

发表于 2016-8-15 16:43:57 | 显示全部楼层
没加锁啊

出0入0汤圆

发表于 2016-8-15 16:50:16 | 显示全部楼层
学习学习

出0入0汤圆

 楼主| 发表于 2016-8-15 16:54:34 | 显示全部楼层

是什么意思呢?

我怎么看 逻辑都没有错的呀!

出0入0汤圆

发表于 2016-8-15 16:54:39 | 显示全部楼层
路过帮顶。

出200入2554汤圆

发表于 2016-8-15 17:21:25 来自手机 | 显示全部楼层
中断和主程序可以认为是两个线程,访问公用的数据(临界区)时,需要通过加锁来确保原子操作。否则数据可能不正确加载

出0入0汤圆

发表于 2016-8-15 17:23:41 | 显示全部楼层
t3486784401 发表于 2016-8-15 17:21
中断和主程序可以认为是两个线程,访问公用的数据(临界区)时,需要通过加锁来确保原子操作。否则数据可能 ...

他们操作的只是同一个数组,但不是同一个数据啊~~

出0入0汤圆

发表于 2016-8-15 18:12:57 | 显示全部楼层
同一个数组不就是同一个数据么.
可能会存在读的时间,中断里又修改了(你不确定中断什么时候发生).

出200入2554汤圆

发表于 2016-8-15 22:28:14 来自手机 | 显示全部楼层
yyliu 发表于 2016-8-15 17:23
他们操作的只是同一个数组,但不是同一个数据啊~~

随便看看 US1_RX_FIFO_user 就公用了,类似还会有  siz 那个,一个访问到一半被另一个中断改掉了,就是这么样

出0入0汤圆

发表于 2016-8-15 22:48:26 | 显示全部楼层
这个有bug的,明显US1_RX_FIFO_user 这个变量就是冲突所在,根本不需要这个变量。head和tail的差值就cnt,看看linux的kfifo

出0入0汤圆

发表于 2016-8-16 02:04:38 | 显示全部楼层
linux内核数据结构之kfifo

http://www.cnblogs.com/Anker/p/3481373.html



环形缓冲区的详细介绍及实现方法可以参考http://en.wikipedia.org/wiki/Circular_buffer,介绍的非常详细,列举了实现环形队列的几种方法。

环形队列的不便之处在于如何判断队列是空还是满。维基百科上给三种实现方法。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2016-8-16 08:38:57 | 显示全部楼层
shangdawei 发表于 2016-8-16 02:04
linux内核数据结构之kfifo

http://www.cnblogs.com/Anker/p/3481373.html

非常感谢

出0入0汤圆

发表于 2016-8-16 08:49:12 | 显示全部楼层
我好像遇到类似问题:先将引脚配置为USART功能,再初始化USART,顺序问题。
楼主试一下。

出0入0汤圆

发表于 2016-8-16 08:50:07 | 显示全部楼层
shangdawei 发表于 2016-8-16 02:04
linux内核数据结构之kfifo

http://www.cnblogs.com/Anker/p/3481373.html

非常感谢提供这个资源,我要好好看一下

出0入0汤圆

发表于 2016-8-16 09:14:53 | 显示全部楼层
学习了  谢谢分享

出0入0汤圆

 楼主| 发表于 2016-8-16 09:24:48 | 显示全部楼层
pazulin 发表于 2016-8-15 22:48
这个有bug的,明显US1_RX_FIFO_user 这个变量就是冲突所在,根本不需要这个变量。head和tail的差值就cnt, ...

我之前也写过,不用user 变量的,就是直接从in out 指针得出,用了一段时间,挺好的没有什么问题。
用串口波特率460800 压力测试也没有问题。
但是总感觉效率不是最高的,每次查询user 的时候都要运行加减操作,这样速度就变低了!
我用 user 变量的目的是“用内存换速度” !

出0入0汤圆

 楼主| 发表于 2016-8-16 09:35:13 | 显示全部楼层
t3486784401 发表于 2016-8-15 17:21
中断和主程序可以认为是两个线程,访问公用的数据(临界区)时,需要通过加锁来确保原子操作。否则数据可能 ...

我这个是跑裸机的,没有什么锁的怎么办呢?

出0入0汤圆

 楼主| 发表于 2016-8-16 09:36:31 | 显示全部楼层
AlertTao 发表于 2016-8-16 08:49
我好像遇到类似问题:先将引脚配置为USART功能,再初始化USART,顺序问题。
楼主试一下。 ...

我感觉不会,我感觉是程序的问题

出0入0汤圆

发表于 2016-8-16 09:48:41 | 显示全部楼层
shangdawei 发表于 2016-8-16 02:04
linux内核数据结构之kfifo

http://www.cnblogs.com/Anker/p/3481373.html

牛X,学习了。

出0入0汤圆

发表于 2016-8-16 09:53:16 | 显示全部楼层
t3486784401 发表于 2016-8-15 17:21
中断和主程序可以认为是两个线程,访问公用的数据(临界区)时,需要通过加锁来确保原子操作。否则数据可能 ...

  中断只写   main只读     需要强行上锁吗?

出0入0汤圆

发表于 2016-8-16 18:53:17 | 显示全部楼层


区分缓冲区满或者空 -- 镜像指示位

缓冲区的长度如果是n,逻辑地址空间则为0至n-1;那么,规定n至2n-1为镜像逻辑地址空间。

本策略规定读写指针的地址空间为0至2n-1,其中低半部分对应于常规的逻辑地址空间,高半部分对应于镜像逻辑地址空间。

当指针值大于等于2n时,使其折返(wrapped)到ptr-2n。

使用一位表示写指针或读指针是否进入了虚拟的镜像存储区:置位表示进入,不置位表示没进入还在基本存储区。


在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空;

如果二者的指示位不同,说明缓冲区为满。

这种方法优点是测试缓冲区满/空很简单;

不需要做取余数操作;读写线程可以分别设计专用算法策略,能实现精致的并发控制。
缺点是读写指针各需要额外的一位作为指示位。


如果缓冲区长度是2的幂,则本方法可以省略镜像指示位。
如果读写指针的值相等,则缓冲区为空;如果读写指针相差n,则缓冲区为满,这可以用条件表达式(写指针 == (读指针 异或 缓冲区长度))来判断。

  1. /* This approach adds one bit to end and start pointers */

  2. /* Circular buffer object */
  3. typedef struct {
  4.     int         size;   /* maximum number of elements           */
  5.     int         start;  /* index of oldest element              */
  6.     int         end;    /* index at which to write new element  */
  7.     ElemType   *elems;  /* vector of elements                   */
  8. } CircularBuffer;

  9. void cbInit(CircularBuffer *cb, int size) {
  10.     cb->size  = size;
  11.     cb->start = 0;
  12.     cb->end   = 0;
  13.     cb->elems = (ElemType *)calloc(cb->size, sizeof(ElemType));
  14. }

  15. void cbPrint(CircularBuffer *cb) {
  16.     printf("size=0x%x, start=%d, end=%d\n", cb->size, cb->start, cb->end);
  17. }

  18. int cbIsFull(CircularBuffer *cb) {
  19.     return cb->end == (cb->start ^ cb->size); /* This inverts the most significant bit of start before comparison */ }

  20. int cbIsEmpty(CircularBuffer *cb) {
  21.     return cb->end == cb->start; }

  22. int cbIncr(CircularBuffer *cb, int p) {
  23.     return (p + 1)&(2*cb->size-1); /* start and end pointers incrementation is done modulo 2*size */
  24. }

  25. void cbWrite(CircularBuffer *cb, ElemType *elem) {
  26.     cb->elems[cb->end&(cb->size-1)] = *elem;
  27.     if (cbIsFull(cb)) /* full, overwrite moves start pointer */
  28.         cb->start = cbIncr(cb, cb->start);
  29.     cb->end = cbIncr(cb, cb->end);
  30. }

  31. void cbRead(CircularBuffer *cb, ElemType *elem) {
  32.     *elem = cb->elems[cb->start&(cb->size-1)];
  33.     cb->start = cbIncr(cb, cb->start);
  34. }
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2016-8-16 18:56:27 | 显示全部楼层
环形缓冲器

https://zh.wikipedia.org/wiki/%E ... 9%E8%A1%9D%E5%8D%80



Linux内核的kfifo

在Linux内核文件kfifo.h和kfifo.c中,定义了一个先进先出圆形缓冲区实现。

如果只有一个读线程、一个写线程,二者没有共享的被修改的控制变量,那么可以证明这种情况下不需要并发控制。

kfifo就满足上述条件。

kfifo要求缓冲区长度必须为2的幂。

读、写指针分别是无符号整型变量。把读写指针变换为缓冲区内的索引值,仅需要“按位与”操作:
(指针值 按位与 (缓冲区长度-1))。这避免了计算代价高昂的“求余”操作。且下述关系总是成立:

读指针 + 缓冲区存储的数据长度 == 写指针

即使在写指针达到了无符号整型的上界,上溢出后写指针的值小于读指针的值,上述关系仍然保持成立(这是因为无符号整型加法的性质)。
kfifo的写操作,首先计算缓冲区中当前可写入存储空间的数据长度:

len = min{待写入数据长度, 缓冲区长度 - (写指针 - 读指针)}

然后,分两段写入数据。

第一段是从写指针开始向缓冲区末尾方向;
第二段是从缓冲区起始处写入余下的可写入数据,这部分可能数据长度为0即并无实际数据写入。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2016-8-16 18:59:48 | 显示全部楼层

POSIX优化实现


  1. #include <sys/mman.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>

  4. #define report_exceptional_condition() abort ()

  5. struct ring_buffer
  6. {
  7.   void * address;

  8.   unsigned long count_bytes;
  9.   unsigned long write_offset_bytes;
  10.   unsigned long read_offset_bytes;
  11. };

  12. //Warning order should be at least 12 for Linux
  13. void ring_buffer_create( struct ring_buffer * buffer, unsigned long order )
  14. {
  15.   char path[ ] = "/dev/shm/ring-buffer-XXXXXX";
  16.   int file_descriptor;
  17.   void * address;
  18.   int status;

  19.   file_descriptor = mkstemp( path );
  20.   if ( file_descriptor < 0 )
  21.     report_exceptional_condition();

  22.   status = unlink( path );
  23.   if ( status )
  24.     report_exceptional_condition();

  25.   buffer->count_bytes = 1UL << order;
  26.   buffer->write_offset_bytes = 0;
  27.   buffer->read_offset_bytes = 0;

  28.   status = ftruncate( file_descriptor, buffer->count_bytes );
  29.   if ( status )
  30.     report_exceptional_condition();

  31.   buffer->address = mmap( NULL, buffer->count_bytes << 1, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE,  - 1, 0 );

  32.   if ( buffer->address == MAP_FAILED )
  33.     report_exceptional_condition();

  34.   address = mmap( buffer->address, buffer->count_bytes, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, file_descriptor,
  35.     0 );

  36.   if ( address != buffer->address )
  37.     report_exceptional_condition();

  38.   address = mmap( buffer->address + buffer->count_bytes, buffer->count_bytes, PROT_READ | PROT_WRITE, MAP_FIXED |
  39.     MAP_SHARED, file_descriptor, 0 );

  40.   if ( address != buffer->address + buffer->count_bytes )
  41.     report_exceptional_condition();

  42.   status = close( file_descriptor );
  43.   if ( status )
  44.     report_exceptional_condition();
  45. }

  46. void ring_buffer_free( struct ring_buffer * buffer )
  47. {
  48.   int status;

  49.   status = munmap( buffer->address, buffer->count_bytes << 1 );
  50.   if ( status )
  51.     report_exceptional_condition();
  52. }

  53. void * ring_buffer_write_address( struct ring_buffer * buffer )
  54. {
  55.   /*** void pointer arithmetic is a constraint violation. ***/
  56.   return buffer->address + buffer->write_offset_bytes;
  57. }

  58. void ring_buffer_write_advance( struct ring_buffer * buffer, unsigned long count_bytes )
  59. {
  60.   buffer->write_offset_bytes += count_bytes;
  61. }

  62. void * ring_buffer_read_address( struct ring_buffer * buffer )
  63. {
  64.   return buffer->address + buffer->read_offset_bytes;
  65. }

  66. void ring_buffer_read_advance( struct ring_buffer * buffer, unsigned long count_bytes )
  67. {
  68.   buffer->read_offset_bytes += count_bytes;

  69.   if ( buffer->read_offset_bytes >= buffer->count_bytes )
  70.   {
  71.      /*如果读指针大于等于缓冲区长度,那些读写指针同时折返回[0, buffer_size]范围内  */
  72.     buffer->read_offset_bytes -= buffer->count_bytes;
  73.     buffer->write_offset_bytes -= buffer->count_bytes;
  74.   }
  75. }

  76. unsigned long ring_buffer_count_bytes( struct ring_buffer * buffer )
  77. {
  78.   return buffer->write_offset_bytes - buffer->read_offset_bytes;
  79. }

  80. unsigned long ring_buffer_count_free_bytes( struct ring_buffer * buffer )
  81. {
  82.   return buffer->count_bytes - ring_buffer_count_bytes( buffer );
  83. }

  84. void ring_buffer_clear( struct ring_buffer * buffer )
  85. {
  86.   buffer->write_offset_bytes = 0;
  87.   buffer->read_offset_bytes = 0;
  88. }

  89. /*
  90. * Note, that initial anonymous mmap() can be avoided - after initial mmap() for descriptor fd,
  91. * you can try mmap() with hinted address as (buffer->address + buffer->count_bytes)
  92. * and if it fails - another one with hinted address as (buffer->address - buffer->count_bytes).
  93. *
  94. * Make sure MAP_FIXED is not used in such case, as under certain situations it could end with segfault.
  95. * The advantage of such approach is, that it avoids requirement to map twice the amount
  96. * you need initially (especially useful e.g. if you want to use hugetlbfs and the allowed amount is limited)
  97. * and in context of gcc/glibc - you can avoid certain feature macros
  98. * (MAP_ANONYMOUS usually requires one of: _BSD_SOURCE, _SVID_SOURCE or _GNU_SOURCE).
  99. */
复制代码

出0入0汤圆

发表于 2016-8-16 19:49:26 来自手机 | 显示全部楼层
我是只能写n-1个免得判断相等时候是空还是满 为了一个元素多费功夫

出0入0汤圆

发表于 2016-8-16 20:42:49 | 显示全部楼层
感觉那个US1_RX_FIFO_user用得太乱了,我都是一个IP只管往里写,一个IP只管往外读,如果写到IP重合那就溢出丢数据了,只要两个IP不等则有数据缓存

出0入0汤圆

发表于 2016-10-7 11:01:42 | 显示全部楼层
shangdawei 发表于 2016-8-16 02:04
linux内核数据结构之kfifo

http://www.cnblogs.com/Anker/p/3481373.html

这个解释真是漂亮啊,很形象啊

出0入0汤圆

发表于 2016-10-7 15:27:28 来自手机 | 显示全部楼层
学了几招,感谢各位分享

出0入0汤圆

发表于 2016-10-8 08:59:01 | 显示全部楼层
US1_RX_FIFO_user   确实不能用,我之前这样用过,程序长时间跑就会丢数据,后面只判断 head tail 就OK了

出0入8汤圆

发表于 2016-10-9 09:46:45 | 显示全部楼层
我的,简单易懂:
#define UART0_FIFO_SIZE        12
typedef struct  
{
        volatile uint8 in;
        volatile uint8 out;
        uint8 fifo[UART0_FIFO_SIZE];
}UART_FIFO;                                       

UART_FIFO uart0;

void uart0_fifopush(int8 dat)
{
          uart0.fifo[uart0.in] = dat;
          if (uart0.in < (UART0_FIFO_SIZE - 1)) uart0.in++;        //判断队列是否已满
          else uart0.in = 0;        //队列满,覆盖头部
}
int8 uart0_fifopop(void)
{
            int8 tmp = uart0.fifo[uart0.out];
        if (uart0.out < (UART0_FIFO_SIZE - 1)) uart0.out++;
        else uart0.out = 0;
        return tmp;
       
}

出140入115汤圆

发表于 2017-5-18 14:47:56 | 显示全部楼层
rube 发表于 2016-10-9 09:46
我的,简单易懂:
#define UART0_FIFO_SIZE        12
typedef struct  

好的,谢谢

出0入0汤圆

发表于 2017-9-12 12:11:37 | 显示全部楼层
学习了,顶一下。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 12:40

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表