STM32问题记录:这回Keil编译器背锅

最近写了个用环形缓冲区发送数据的STM32串口程序:使用头指针(front)指向下一个要发送数据,使用尾指针(rear)指向新数据存储的地方。中断里面会判断front和rear是否相等,如果相等则表示缓冲区为空,发送已经完成,关闭中断;反过来说,front和rear相等表示缓冲区还有数据要发送,那么就在中断里面把数据一个一个地发送出去。

那么问题来了,我在存数据的时候写了这么一行代码:

USART1_SendQueue[USART1_SendRear ++] = data;

也就是说,把数据存入缓冲区后,尾指针自+1。这看起来没毛病,但编译器给出汇编代码是这样的:

0x0800213C  LDR      r0,[pc,#492]      ;取出USART1_SendRear的地址存到r0
……
0x08002148  LDR      r1,[r0,#0x00]     ;将USART1_SendRear的值存入r1
0x0800214A  LDR      r0,[r0,#0x00]     ;将USART1_SendRear的值存入r0
0x0800214C  ADDS     r0,r0,#1	       ;将r0自加1
0x0800214E  LDR      r2,[pc,#476]      ;取出USART1_SendRear的地址存到r2
0x08002150  STR      r0,[r2,#0x00]     ;将r0的值存入USART1_SendRear的地址
0x08002152  LDR      r0,[pc,#480]      ;取出USART1_SendQueue的首地址存到r0
0x08002154  STRH     r5,[r0,r1,LSL #1] ;将data(r5)存入r0+(USART1_SendRear<<1)的地址中(左移1位相当于*2,是因为数组一个元素有两个字节)

在C的代码的逻辑中

USART1_SendQueue[USART1_SendRear ++] = data; 

应该等效于:

USART1_SendQueue[USART1_SendRear] = data; 
USART1_SendRear ++;

然而汇编的代码的逻辑转换成C等效于:

temp = USART1_SendRear;
USART1_SendRear ++;
USART1_SendQueue[temp] = data;

那么问题来了,如果按照原来的C逻辑,因为数据存入后指针才会自+1,所以即使发送过程中触发中断,也只有数据存入缓冲区以后才会判断出缓冲区非空。而在汇编的逻辑中, 指针+1后才把数据存入缓冲区。如果指针+1之后突然来了个中断,那么有可能会把数据即将写入的那个地址的数据先发出去,然后再写入,发送就出错了!

形象一点就是说:

temp = USART1_SendRear;
USART1_SendRear ++;
//这个位置突然来了一个中断
//中断发送了当前USART1_SendQueue[temp] 的数据
//中断返回后再执行下面那句
USART1_SendQueue[temp] = data;

好吧,如果不考虑中断的话,汇编代码这么做不会带来错的结果,可惜中断来得就是这么巧。

这个问题找了好多天,今天终于找到了。编译器这么编译,也是很无奈。所以代码改成如下就好了:

//把这两步拆开写,免得编译器整出幺蛾子
USART1_SendQueue[USART1_SendRear] = data; 
USART1_SendRear ++;

T=T


猜你喜欢

转载自blog.csdn.net/u014695839/article/details/80498162