历史上的今天
今天是:2024年12月18日(星期三)
2018年12月18日 | STM32问题记录:这回Keil编译器背锅
2018-12-18 来源:eefocus
最近写了个用环形缓冲区发送数据的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
史海拾趣
|
请问大家用C开发AVR时使用的是哪个开发环境,哪个更好用. 刚刚从C51转到AVR我这里有三个开发环境,AVR Studio | ICCAVR | cvavr | 其中,AVR Studio是买仿真器自带的,现在准备用Mega64做个。产品不知哪个开发环境好用更适合开发呢? 请大家指点 ...… 查看全部问答> |
|
FPGA可综合性对初学着的一些建议一、HDL不是硬件设计语言 过去笔者曾碰到过不少VHDL或Verilog HDL的初学者问一些相似的问题,诸如如何实现除法、开根号,如何写循环语句等等。在这个论坛上,也时常能看到一些网友提出这一类的问题。 对于这些问 ...… 查看全部问答> |
|
有哪位弄过ES56031混响吗? 我按照datasheet的时序,采用UCOM模式,依次发送D4,D3,D2,D1,SHEEP = 10110,但是就是得不到正确的延时啊,总是得到最小的那个延时12MS,我怀疑是一个初始值,也就是说我没有设置成功。… 查看全部问答> |
|
“外包在线”网络技术有限公司CEO喻烜为大家讲述了她鲜为人知的创业经历,从初识“外包”到立志创业,从寻求投资到初有成就,借由这朵铿锵玫瑰坚韧不拔的毅力和非凡的智慧,国内首家外包服务网络平台终于问世,自此“外包”服务更加平民化,从 ...… 查看全部问答> |
|
地址总线A15—A0(低),存储器地址空间为3000H-67FFH,按字节编址。其中3000H-4FFH为ROM区,选用EPROM芯片(4K*2b/片);5000H-67FFH为RAM区,选用DRAM芯片(2K*4位/片) 解释3000H是如何推出(A15A14为00),而 4FFFH又是如何推出(A15A14为01) ...… 查看全部问答> |
|
国内站点: 华恒公司的主页,里面有很多的相关资料,有待大家去发现 http://www.hhcn.com/chinese/embedlinux-res.html SkyEye嵌入式硬件仿真项目 www.skyeye.org http://gro.clinux.org/projects/skyeye/ 公社的SkyEye项目专栏 http://www.linuxfa ...… 查看全部问答> |




