奇怪,往xdata的一个数组里写数,第0个元素的值会自动变成0,改成idata就不会出现
小弟在做一个项目,往数组里边写入一个要显示的数据,送1602LCD进行显示。定义的了一个在xdata区的数组:
xdata uchar ber;
然后某段程序往里边赋值
for(i=0;i<7;i++)
ber=array;
结果发现ber的值被变成了十进制的0,导致LCD显示函数认为已经到了最后一个字符而不显示。
即使我把代码改成:
for(i=0;i<7;i++)
ber=i+ '0';//(变成asccii码)
一样的,就ber的值为0,数组其他元素的值是对的。
后来干脆把ber[]定义在idata区内。
竟然没有问题了!
想不明白为什么。
一开始怀疑为xdata区的这一字节坏掉了。
坏了个新一点儿的片子也这样。
如果xdata区不稳定,应该不只这一字节坏掉,我其他还有很多定义在Xdata区域内的变量,也是正常的。就这一个不正常。
请教高手这是为什么。
一开始也怀疑过,data的某些区域会被中断占用,可是xdata区域是不会被占的丫!
我的data=198字节,XDATA=912字节,code=22334字节
XDATA是片内自带的1KB XRAM
WINBOND W77E58 52单片机
keil uv 8.03版
另外有一个问题不明白,如果编译器告诉我,data使用量不超过256字节,是不是代表data占用量不会受中断影响(我是说会不会由于中断导致data溢出)? data用量不超过256字节不代表你的内存仍够用,很多人喜欢函数套函数,一层层地套,甚至中断里头也是一堆函数调用,美其名曰"结构化",三两下就把堆栈堆满了,程序运行起来时不时地出点错,作BUG定位时根本找不到.
至于你碰到的问题,十有八九是你的程序工作不正常,最好能将代码发出来给大家看看,毕境大家都是上班族,没太多时间帮你瞎蒙,你说呢? 应一楼的要求,把整个工程文件拿出来。
之前想过上传,但是考虑到本程序有一定代码量,怕太麻烦大家,所以就没放上,
简要说明一下:
整个程序运行的大循环叫
autotest();
全局变量的定义在rfsw.h中,第228行。ber[]数组是从recvstring1[]数组中解包出来的纯字符串信息。
recvstring1[]在串口中断中被写入数据。串口中断按状态对接收到的数据进行接收和分类存放。填入recvstring1[]数组。
在autotest中对接收的数据进行解包。由于接收到的数据流(字符串)可能是: " &31.6|42.7|1.0e+07#"这样的数据,所以在
rfsw.c的第600行处进行分解。
最后的1.0e+07按字符存入ber[];
曾经写过调试程序把recvstring1[]中的部分数据发往LCD上,发现recvstring1[]的数据流是对的,ber[]里被放赋的字符其他都是对的,唯一错的就是那个1.0e+07中的“1”老是为0值(在另一个观察窗里看到的,LCD无法显示0值)。
即使把第628行改成 ber=w+'0';依然,发现最后ber的元素值为0;
而一旦把ber[]定义在idata段,马上就正常了,可以看到液晶上显示解出来的 1.0e+07 一个字都不差。
点击此处下载 ourdev_571213.rar(文件大小:211K) (原文件名:智能射频开关_ZL_04.rar) 你的程序从编译角度来看没任何问题,但导致你的程序与运行结果偏差的主要原因是因为XDATA的使用上出了问题,你不能完全信任 Keil 在Compiling时给你显示的信息,它说你用了912个XDATA,你完全相信它吗?不敢怀疑它?那你就只能出错了....
找一个你定义的XDATA数组来看看吧:
//主程序160行处
for(r=0;r<7;r++)
strtointer=0;
for(r=0;r<30;r++)
recvstring1=0;
你在头文件里是这样定义的
xdata uchar strtointer; //字符转成整数,临时数组,第个元素无效
编译结果:
assembling STARTUP.A51...
compiling rfsw.c...
linking...
Program Size: data=186.0 xdata=907 code=23298
creating hex file from "rfsw_snmp"...
"rfsw_snmp" - 0 Error(s), 0 Warning(s).
看一下它在XDATA内的绝对地址在哪?
;162: for(r=0;r<7;r++)
C:0x3905 90046C MOV DPTR,#r(0x046C)
C:0x3908 F0 MOVX @DPTR,A
C:0x3909 90046C MOV DPTR,#r(0x046C)
C:0x390C E0 MOVX A,@DPTR
C:0x390D FF MOV R7,A
C:0x390E C3 CLR C
C:0x390F 9407 SUBB A,#0x07
C:0x3911 5014 JNC C:3927
给你定位到0x046C去了,超过1K了吧?电路中有外扩XRAM没?如果没有不出错才怪呢! 其它的就不用分析了,还有好几个数组定义在1K之外了,取出的数据自然也是就0了 感谢落叶的这么细心的帮我看代码,还给我这么详细的回复。感觉你们在应时的纯熟程度就像在用记事本写字一样,清晰了然,我们就像在使用word,永远搞不懂我们一个动作到底会带来什么样的结果。起码不是很明确地知道!
那追问下落叶,我如何才能避免这种情况的出现呢。
单片机自带的XRAM,为1KB大小。
只要让PMR=0x01 XRAM区就生效了。是不是要在keil里设置下,告诉他:
1 我要使用片内自带的XRAM;
2 XRAM的地址是从0x00到0x200;这样?
另外,如果我写的某个程序占用data达到256字节,会不会由于中断和堆栈等导致溢出?(排除编译器欺骗我的情况下),嘿嘿 为什么xdata=907
xdata会用到0x046C,xdata应该是从0x0000顺序往下分配的。到底是什么问题。还没碰到keil出错的情况,而且错的那么离谱。 造成此问题的原因是Keil并不知道你是否只使用内部1K,所以对内存分配不做优化,就象WIN存文件一样,会有很多碎片,如果你定义了一个BUFFER,它首先要找一片连续的空间去存放,即使低地址有空白它也会跳过,如果由于分配先后次序或为了找合适的空区域就会造成有一些内存碎片未使用,如果不优化Keil也不会把它统计在使用内存之中。
其实这也并不怪KEIL,因为你没指明要使用片内1K内存,也没限定只使用1K,所以它就会以为你的XRAM很大,浪费点也无关紧要。说了这么多,其实都是费话,不了解这些也没关系,只要按下图设置一下即可:
http://cache.amobbs.com/bbs_upload782111/files_31/ourdev_571350.JPG
(原文件名:未命名.JPG)
按以上设置吧 懂了,按照落叶的指示,我对比测试了下,结果如下:
1 如果什么都不告诉他,那么地址分配是随机的,可能会出错。
2 如果勾上使用片内XDATA(看来keil对产品信息掌握比较全,已经预置了这种配置),抽样检查发现没有超出范围的事情发生
3 如果不勾上面的,而是手填下面的,0,1024,抽样没的超范围的。
4 勾选上面的Use on-chip Xram,同时下边填个错值,256,1024。不会出错。看来以上面勾选的优先。
5 不勾选上面的 Use on-chip Xram,同时下边填个错值,256,1024。会出错。因为地址范围填错了。(我竟然晕晕的认为XRAM地址从256开始的。)
谢谢落叶的提示。
如果data在紧凑的情况下的用量不超过256,是不是不会存在因中断、堆栈而导致溢出? 回复【5楼】zhanliana
...
另外,如果我写的某个程序占用data达到256字节,会不会由于中断和堆栈等导致溢出?(排除编译器欺骗我的情况下),嘿嘿
-----------------------------------------------------------------------
报歉,刚才在忙,没看到还有一问
如果你有函数调用,要压栈出栈,肯定会导致溢出,这个值也不要全看编译器给显示,有水分,包括FLASH也是如此(如果用到绝对定位会更明显)。 回复【7楼】flywater落叶
-----------------------------------------------------------------------
我从来没有设置过,从来都是用多少xdata基本上就是多大的范围,都是从0x0000开始的,连续的。
真还没碰到过这种情况。
而且keil编译链接的过程不会像动态内存分配似的还会产生碎片吧。
aaa1982 可以找个小程序试试:
Build target 'Target 1'
compiling Ctest2.c...
linking...
Program Size: data=232.0 xdata=0 code=1962
"CTest1" - 0 Error(s), 0 Warning(s).
按说DATA只占232个字节,不应该会有问题
http://cache.amobbs.com/bbs_upload782111/files_31/ourdev_571374.JPG
(原文件名:1.JPG)
但SP已在0xfe处了再看下面执行完函数返回后的SP到哪了?
http://cache.amobbs.com/bbs_upload782111/files_31/ourdev_571376.JPG
(原文件名:2.JPG)
此时程序已飞了.... 回复【10楼】aaa1982
-----------------------------------------------------------------------
不好意思,我上传的工程文件如果你打开后会发现,设置的地方,我把XRAM地址填成了256开始,size:1024,在这种情况下,编译是从256开始的,而我用了913字节,所以总共会超过1024的总寻址空间。由于你没有填写给编译器的习惯,所以我填错的地方,你也忽略了。呵呵。
当时发晕了,认为XDATA从256开始编址,现在想来,那是我想当然的结果。 回复【10楼】aaa1982
回复【7楼】flywater落叶
-----------------------------------------------------------------------
我从来没有设置过,从来都是用多少xdata基本上就是多大的范围,都是从0x0000开始的,连续的。
真还没碰到过这种情况。
而且keil编译链接的过程不会像动态内存分配似的还会产生碎片吧。
aaa1982
-----------------------------------------------------------------------
呵呵,DATA也会有奇怪的问题 程序ourdev_571388.rar(文件大小:15K) (原文件名:SSD1305 TSET(24M-2).rar)
落叶能不能也给我看看?这事让我头痛好很久,通宵了好几晚都不知怎会事,我现在在移植一下zlggui,这个gui要在51单片机开辟一个1K的显示缓存,我用的是12864单色的OLED显示屏,这个程序在不开辟1K的显示缓存是能正常显示,很正常的,但开辟了这个1K的显示缓存,显示屏显示出来的东西就乱了,麻烦给些意见,谢谢~ 回复【14楼】maidilong
程序 (原文件名:ssd1305 tset(24m-2).rar)
落叶能不能也给我看看?这事让我头痛好很久,通宵了好几晚都不知怎会事,我现在在移植一下zlggui,这个gui要在51单片机开辟一个1k的显示缓存,这个程序在不开辟1k的显示缓存是能正常显示,很正常的,但开辟了这个1k的显示缓存,显示屏显示出来的东西就乱了,麻烦给些意见,谢谢~
-----------------------------------------------------------------------
我刚刚按七楼设置了一下,还是不行,郁门 回复【15楼】maidilong
-----------------------------------------------------------------------
用的什么片子呀?能不能把问题说得详细些 回复【16楼】flywater 落叶
回复【15楼】maidilong
-----------------------------------------------------------------------
用的什么片子呀?能不能把问题说得详细些
-----------------------------------------------------------------------
我使用的是STC89C58RD+
Package:PLCC44,PDIP40,PQFP44
Operating TEMP:0 to +70;
Power Supply:4.5V~5.5V
Operating Frequency:0 to 80 MHz,
an 8051 compatible controller
32 I/O Lines, Dual DPTR,
ISP/IAP,X2 mode,
3 Timer/Counter,
8 Interrupts/4 priority levels,
Watchdog Timer,
1280 bytes on-chip RAM,
32K Flash memory,
8K E2PROM. 奇怪的地方就在下面这个函数,我要是把里面的那个二维数组的显示缓冲区填充数据的话,显示屏就会乱了,要是把这个二维数组屏蔽了,倒能正常显示,但这个对我想开劈的显示缓冲区就开劈不出来了
/****************************************************************************
* 名称:GUI_FillSCR()
* 功能:全屏填充。直接使用数据填充显示缓冲区。
* 入口参数:dat 填充的数据
* 出口参数:无
* 说明:用户根据LCM的实际情况编写此函数。
****************************************************************************/
voidGUI_FillSCR(TCOLOR dat)
{unsigned int i,j;
for(i=0; i<8; i++)
{
for(j=0; j<128; j++)
{
gui_disp_buf=dat;//dat;
}
}
// 填充LCM
LCM_DispFill(dat);
} 你用的是大模式编译,即编译器会把变量都放在XDATA内,而你又开了1K的显示缓冲,使用XDATA肯定超过1K了,而这片子只有1024的XRAM,所以要出错了 呵呵~~~我解决了,我定义的二维数组是uchar buf编译的时候说太大了,编译不过去,于是我把存储模式改成large Xdata,这样就能编译过去了,但是只要一用这个二维数组显示就会乱,现在的解决方法是把二维数组改为uchar xdata buf 就可以使用了,哈哈~~谢谢!1 回复【20楼】maidilong
呵呵~~~我解决了,我定义的二维数组是uchar buf编译的时候说太大了,编译不过去,于是我把存储模式改成large xdata,这样就能编译过去了,但是只要一用这个二维数组显示就会乱,现在的解决方法是把二维数组改为uchar xdata buf 就可以使用了,哈哈~~谢谢!1
-----------------------------------------------------------------------
不要轻易用大模式编译,51的速度本来就不敢恭维。
另:恭喜!这么快解决了... 晕倒,原以为这个问题到这里就结束了,结果呢,今天一来到公司,按照落叶的提示进行了设置之后,烧片,故障依旧!!
我还对比了下,也看看汇编,到底把ber定位到哪里了。
按落叶的设置,ber被定义在XDATA 0x0155的地址上。
ber[]值是从recvstring[]过来的,recvstring[]被定义到了0x00001开始的30字节长度。
仍然是ber=0;
另外,曾经把recvstring[]里的元素直接读出来往LCD上发,正常。一旦放到ber[]里再送LCD上发就不正常。
如果把ber[]定义成data或者idata 则没有这种情况。
郁闷!!
新设置的工程文我也上传如下:
点击此处下载 ourdev_571469.rar(文件大小:211K) (原文件名:智能射频开关_ZL_04.rar)
请高手指导。虽然现在改为data区了以后不影响使用,可是道理上讲不通。影响心情。
./emotion/em001.gif 自己写的程序,全靠别人找错是不现实的,因为每个人都有自己的工作,再则没硬件环境,具体情况也不好搞清楚,因为一些变量及输入值无法确定,程序分枝也不确定,而且完全读懂别人的程序并能帮助治疗一些顽症更不容易,所以即便坛里高手很多,也未必会给你回复,因为没太多空闲时间看程序并找错---工程师最头疼的就是找BUG....
以下这段程序思路上有点问题:
while(recvstring1!='|') //读取分隔符前边的数据
{
if(recvstring1!='.')
strtointer=recvstring1-'0';
else
strtointer='.';
r++;
w++;
} //指针往后移
level=caculate(w); //字符串转整型
.......
while(recvstring1!='#')
{
ber=recvstring1 ;
//ber=w+'0';
r++;
w++;
}
这些循环未对数组是否越界进行检测,而且我调试过程中未在recvstring1中找到所谓的“|”或“#”,是不是没考虑接收字符串中无特定离符的情况?
程序不大,建议看仔细点,锻炼一下排错能力....只有你现在具备排错的最优越条件 其实我看了很多遍了。recvstring1[]是在串口接收中断中接收到另一个仪器传回的数据,以"&" 代表开头,"|"代表间隔 "#"代表结束。我的串口中断程序会每个字节中断,等到接收到结束标志,会置一个标志位,在大循环里边处理,并且按照间隔将数据读出来。
主程序里有问题的可能性不大。反复对比,发现只要改ber[]从XDATA区改到内部RAM区就没问题了。理论上讲,不改也应该没问题的。所以一直想不通。产生这一情况的原因可能有哪些。
keil和51单片机都这么多年了,不大可能有这么简单的BUG;
可能,随便放弃对这个问题的追究确实又不甘心。因为这个问题现象如此简单而明了。
仍然期待高手参与,进行研究。
顶起,以便高手能够看到。 建议你检查一下你是不是ber[]这个数组的物理地址被另外的数组操作,也就是说如果编译器在ber[]这个物理地址前面连续分配了一个数据,而你刚好前面这个数组操作时多操作了一个元素,造成你一直在修改这个单元,而改到data区之后就完全正确。当然只是猜测,检查一下看看 回复【25楼】jason1927
建议你检查一下你是不是ber[]这个数组的物理地址被另外的数组操作,也就是说如果编译器在ber[]这个物理地址前面连续分配了一个数据,而你刚好前面这个数组操作时多操作了一个元素,造成你一直在修改这个单元,而改到data区之后就完全正确。当然只是猜测,检查一下看看
-----------------------------------------------------------------------
首先我不知道如何查看ber[]数组前面一个地址被谁占用了, keil软件有这个功能么,如果一个一个看哪个变量各占哪些置,未免速度太慢。而且,我试过,貌似不是个切实可行的办法。
另外,对于一个确定元素个数的数组,在编译阶段如果有越界行为,应该会编译通不过。
如果在程序运行阶段越界,还真不知道会有什么样的结果,应该是复位。 回复【25楼】jason1927
建议你检查一下你是不是ber[]这个数组的物理地址被另外的数组操作,也就是说如果编译器在ber[]这个物理地址前面连续分配了一个数据,而你刚好前面这个“数组操作时多操作了一个元素,”造成你一直在修改这个单元,而改到data区之后就完全正确。当然只是猜测,检查一下看看
-----------------------------------------------------------------------
多谢jason1927的提醒,我百度一下,发现数组越界确实有存在的可能。
查看方法为:用记事打开*.M51文件,这里存放着各种存储器映像。经过搜索关键字"BER"定位
X:00D1H PUBLIC star_data
X:0151H PUBLIC ber
看,star_data就是ber前边的一个数组,这个数组
在rfsw.h 第329行定义:xdata uchar star_data;//用于存储星座图,共128字节
那么再回到主程序中看下各种对stat_data进行操作的地方:
有一个:
在rfsw.c中2147行,是我当时自作聪明的在数组最后添加了一个'\0'为表示数组结束!
2147:star_data='\0';
而star_data[]最大支撑到star_data!!越界刚好越到ber!!!
后天去公司我就改过来试试,这正是问题之所在了!
感谢jason1927的提示!感谢 flywater 落叶 细心的指点!
重要的是解决了问题(我通过改变量存储类型已经搞定了问题,但是心里有阴影)
更重要的是,恢复了我自己对51的信心!
特写出来供广大网友学习!感觉amork的论坛!感谢百度!
另外,这是不是C语言的经典陷阱啊,编译器也不报错。 【27楼】 zhanliana
...
另外,这是不是C语言的经典陷阱啊,编译器也不报错。
你说对了,这就是C语言的"经典陷阱",编译器不仅不会报错,也不应该报错,因为它无法辩断这是不是程序员有意识的行为----C语言是用高级语言的语法干低级语言的事,除法语法问题外,其余一切都交给程序员来处理. 请教 flywater 落叶
在STARTUP.A51中指定
XDATALEN EQU 0x0400
是否也可达到设置那个参数的效果?
我一直是这样做的,里面都没有设置什么,只是在STARTUP.A51中修改一下就行了。 这个贴子不错, 既有分析思路,又有解决办法. 回复【29楼】xiaobendan 仲跻东
请教 flywater 落叶
在startup.a51中指定
xdatalen equ 0x0400
是否也可达到设置那个参数的效果?
我一直是这样做的,里面都没有设置什么,只是在startup.a51中修改一下就行了。
-----------------------------------------------------------------------
在startup.a51中设置 xdatalen equ 0x0400是不行的,这地方设置只影响MCU启动时,要清除XDATA区的字节长度,即这样设置后程序中会多以下代码:
MOV DPTR,#0x0000
MOV R7,#LOW (XDATALEN)
MOV R7,#0x00
MOV R6,#HIGH (XDATALEN)
MOV R6,#0x04
CLR A
CLR A
MOVX @DPTR,A
MOVX @DPTR,A
INC DPTR
INC DPTR
DJNZ R7,XDATALOOP
DJNZ R7,XDATALOOP(C:065D)
DJNZ R6,XDATALOOP
DJNZ R6,XDATALOOP(C:065D)
作用只是清0~0x400段的XDATA 回复【30楼】sipueay
这个贴子不错, 既有分析思路,又有解决办法.
-----------------------------------------------------------------------
这才是讨论的意义。既有主题,又有高手参与,同时没那么多口水战。以技术论技术。解决问题,利人利已。 不错分析的太有理了先收下了 收下了 mark MARK mark mark 此贴甚好 mark,此帖有意义 MARK! 用数组,尽量随时确保不要越界! 好贴!刚好遇到类似情况 多谢楼上各位。 mark 。。。门在哪啊高深莫测 回复【楼主位】zhanliana
-----------------------------------------------------------------------
大家要当心了!
C语言不对数组做越界检查!例如连续定义两个数组,例如:
char a,b;
两个数组在地址上市连在一起的,如果对a数组操作的时候越界了,就会改变b数组里的值!
例如不小心向a,a,a里面写了数据,你就会很奇怪的发现b数组里面的数改变了! mark.很有益处啊。谢谢了 好帖,路过顶起! mark ,对我帮助很大!感谢! dupeiyuan 发表于 2012-2-15 11:47
回复【楼主位】zhanliana
-----------------------------------------------------------------------
是的 不过很多时候i使用的是公式,如果每次都进行越界检查,也会相当降低效率 此问题最终没有解决吗? shuidiyu 发表于 2014-2-25 14:54
此问题最终没有解决吗?
己解決,數組越界,我以前也發生很多次了 谢谢 受教了
页:
[1]