全局变量在中断和主循环中共同使用时异常问题

问题描述

在串口接收中断中调用ring\_buf\_push()函数向环形数组ring\_buf写入数据,在while(1)循环中调用ring\_buf\_pop()从环形数组ring\_buf读取数据。通过串口每隔10ms大量写入数据时,然后停止发送数据,此时环形数组ring\_bufin\_pointout\_point成员不相等,out\_point总是在in\_point成员的后面一个数据单元内(即in\_point总是指向out_point的前面一个数据单元)。

代码

头文件ring\_buf.h和源文件ring\_buf.c代码如下

/**
 * @file ring_buf.h
 */
#ifndef RING_BUF_H
#define RING_BUF_H

#ifdef __cplusplus
extern "C"
{
#endif

#define RINGBUF_SIZE 200        

typedef struct
{
  uint8_t  buf[RINGBUF_SIZE];
  uint32_t in_point;
  uint32_t out_point;
  uint32_t length;
  uint8_t  full_flag;
}RING_BUF_T;

void ring_buf_init(void);
uint16_t get_ring_buf_len(void);
uint8_t ring_buf_pop(void);
uint8_t ring_buf_push(uint8_t byte);
uint8_t read_ring_buf(uint8_t * dst,uint32_t size);
uint8_t write_ring_buf(uint8_t * buf,uint8_t size);


#ifdef __cplusplus
}
#endif

#endif  // RING_BUF_H
/**
 * @file ring_buf.c
 */
#include "type_define.h"
#include "ring_buf.h"
#include "stm32l0xx_hal.h"

RING_BUF_T ring_buf;
/**
 * @brief 
 * 
 */
void ring_buf_init(void)                 
{
  uint32_t loop = 0;

  ring_buf.in_point  = 0;
  ring_buf.out_point = 0;
  ring_buf.length    = 0;
  ring_buf.full_flag = 0;           
  for(loop = 0;loop < RINGBUF_SIZE;loop ++)
  {
     ring_buf.buf[loop] = 0;
  }
}

/**
 * @brief Get the uart buf len object
 * 
 * @return uint16_t 
 */
uint16_t get_ring_buf_len(void)
{
    return ring_buf.length;
}

uint8_t ring_buf_push(uint8_t byte)
{  

  if(1 == ring_buf.full_flag)
  {
    return RET_FAILED; 
  }
  else   
  {    
    ring_buf.buf[ring_buf.in_point] = byte;
    ring_buf.in_point ++;
    ring_buf.length ++;
    if(ring_buf.in_point == RINGBUF_SIZE)
    {
      ring_buf.in_point = 0;
    } 
    if(ring_buf.length == RINGBUF_SIZE)
    {
      ring_buf.full_flag = 1;                                              
    }
  }
  return 1;             
}

/**
 * @brief 
 * 
 * @return uint8_t 
 */
uint8_t ring_buf_pop(void)
{
  uint8_t data = 0;

  if(ring_buf.length)
  {
    data = ring_buf.buf[ring_buf.out_point ++];
    ring_buf.length --;
    if(ring_buf.out_point == RINGBUF_SIZE)
    {
      ring_buf.out_point = 0;
    }
    ring_buf.full_flag = 0;
  }

  return data;
}

问题分析

由上述代码段可以看到ring\_buf\_pop()在执行的过程中未屏蔽中断,故在该函数执行时可被中断打断,即在ring\_buf\_pop()函数的任何位置ring\_buf\_push()函数都有可能执行(由于ring\_buf\_push()函数在中断接收函数中执行)。
图1为ring\_buf\_pop()函数中ring\_buf.length --;操作的汇编代码,图2为ring\_buf\_push()函数中ring\_buf.length ++;操作的汇编代码。
在这里插入图片描述

                                                                                       图1  ring\_buf\_pop函数执行汇编

在这里插入图片描述

                                                                                                    图2  ring\_buf\_push函数执行汇编

由图,假设ring_buf.length值为2。
在图1ring_buf_pop()执行完LDR R5,[R3]串口产生了中断,则R5寄存器和的值为2,由于还未执行减操作,故ring_buf.length值仍为2。中断后程序会调到ring_buf_push()函数处执行,当执行ring_buf.length ++;时,由于ring_buf.length的值仍为2,故执行后ring\_buf.length为3,该语句执行结束后退到SUBS R5,R5,#1语句处继续执行。此时R5寄存器的值为1,然后将1写入到ring_buf.length地址处,此时ring\_buf.length的值变为1。
若不产生中断,正常的执行流程,先执行ring_buf.length -- 然后执行ring_buf.length ++,则最终的结果应为2,故若在ring_buf.length --执行过程中产生中断会导致长度数据异常。
而读数据时,写入数据时in\_point等于实际的写入数量即3,而读数据时采用ring\_buf.length来判断实际需要读取的数据量,故out\_point由于被中断打断实际能读取到的数据只有2个,故out\_point会永远读取不到最后一个数据单元的数据。

解决方法

  • 可以在执行ring_buf_pop()时关闭串口中断,此处可能会导致串口接收数据丢失。
  • 读取数据时,实际数据长度不以ring_buf.length为计数量,而采用((in_point+RINGBUF_SIZE) - out_point) % RINGBUF_SIZE为实际数据长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值