请教马老师:使用C语言开发AVR,关于复位时编译器会将RAM清0?
在马老师的“看门狗复位后RAM数据会清空?”的讨论一文中:"所以,大部分(几乎全部)的高级语言平台,在编译用户程序前要增加自己的初始化代码,其中包括内存以及相关指针的分配和RAM的清另。RAM清另,保证了用户定义的变量初始为另,程序能可靠的从头执行。"
是不是只要使用高级语言开发单片机,编译器这段初始代码不管在什么复位情况下(如M88有四个复位源),都会清RAM为0?
以前使用KEIL 时都有一段初始代码“STARTUP。A51”可以看见并修改内容,现在我使用CODEVISIONAVR,没有看见这样的初始代码?我找了半天都没有找到。请马老师指点。 I FOUND IT:
In every program the CodeVisionAVR C compiler automatically generates a code sequence to make the following initializations immediately after the AVR chip reset:
1. interrupt vector jump table
2. global interrupt disable
3. EEPROM access disable
4. Watchdog Timer disable
5. external SRAM access and wait state enable if necessary
6. clear the R2...R14 registers
7. clear the SRAM
8. initialize the global variables located in SRAM
9. initialize the Data Stack Pointer register Y
10. initialize the Stack Pointer register SP
11. initialize the UBRR register if necessary
The automatic generation of code sequences 2 to 8 can be disabled by checking the Code Generation|Use an External Startup Initialization File check box in the Project|Configure|C Compiler|Code Generation dialog window. The C compiler will then include, in the generated .asm file, the code sequences from an external file that must be named STARTUP.ASM . This file must be located in the directory where your main C source file resides.
You can write your own STARTUP.ASM file to customize or add some features to your program. The code sequences from this file will be immediately executed after the chip reset.
A basic STARTUP.ASM file is supplied with the compiler distribution and is located in the ..\BIN directory.
Here's the content of this file:
;CodeVisionAVR C Compiler
;(C) 1998-2004 Pavel Haiduc, HP InfoTech s.r.l.
;EXAMPLE STARTUP FILE FOR CodeVisionAVR V1.24.1 OR LATER
.EQU __CLEAR_START=0X60 ;START ADDRESS OF SRAM AREA TO CLEAR
;SET THIS ADDRESS TO 0X100 FOR THE
;ATmega128 OR ATmega64 CHIPS
.EQU __CLEAR_SIZE=256 ;SIZE OF SRAM AREA TO CLEAR IN BYTES
CLI ;DISABLE INTERRUPTS
CLRR30
OUTEECR,R30 ;DISABLE EEPROM ACCESS
;DISABLE THE WATCHDOG
LDIR31,0x18
OUTWDTCR,R31
OUTWDTCR,R30
OUTMCUCR,R30 ;MCUCR=0, NO EXTERNAL SRAM ACCESS
;CLEAR R2-R14
LDIR24,13
LDIR26,2
CLRR27
__CLEAR_REG:
ST X+,R30
DECR24
BRNE __CLEAR_REG
;CLEAR SRAM
LDIR24,LOW(__CLEAR_SIZE)
LDIR25,HIGH(__CLEAR_SIZE)
LDIR26,LOW(__CLEAR_START)
LDIR27,HIGH(__CLEAR_START)
__CLEAR_SRAM:
ST X+,R30
SBIW R24,1
BRNE __CLEAR_SRAM
;GLOBAL VARIABLES INITIALIZATION
LDIR30,LOW(__GLOBAL_INI_TBL*2)
LDIR31,HIGH(__GLOBAL_INI_TBL*2)
__GLOBAL_INI_NEXT:
LPM
ADIW R30,1
MOVR24,R0
LPM
ADIW R30,1
MOVR25,R0
SBIW R24,0
BREQ __GLOBAL_INI_END
LPM
ADIW R30,1
MOVR26,R0
LPM
ADIW R30,1
MOVR27,R0
LPM
ADIW R30,1
MOVR1,R0
LPM
ADIW R30,1
MOVR22,R30
MOVR23,R31
MOVR31,R0
MOVR30,R1
__GLOBAL_INI_LOOP:
LPM
ADIW R30,1
ST X+,R0
SBIW R24,1
BRNE __GLOBAL_INI_LOOP
MOVR30,R22
MOVR31,R23
RJMP __GLOBAL_INI_NEXT
__GLOBAL_INI_END:
The __CLEAR_START and __CLEAR_SIZE constants can be changed to specify which area of SRAM to clear at program initialization.
The __GLOBAL_INI_TBL label must be located at the start of a table containing the information necessary to initialize the global variables located in SRAM. This table is automatically generated by the compiler. 有一个新问题,如何让不同的复位启动不同的启动文件。比如说WATCHDOG RESET 时让一个启动文件启动系统,而上电复位时启动原始的启动文件?请马老师指点? 该初始化的代码你可以不用,或自己编写。请查看CVAVR的使用说明。 谢谢马老师,我再去看看CVAVR的HELP 马老师您好,我用bascom和fastavr,发现fastavr在程序开始的时候并未对内部资源作任何初始化,但是我在350KW大功率焊接电源主控系统中使用,正常,通过电磁兼容,生产的产品质量大大超过国家标准,并未发现任何问题,这是否说明,用硬件复位(外部RCD复位+内部上电+65mS)可以可靠保证MCU复位,并不需要软件初始化,或者说在上述情况下软件初始化并不能使程序健壮性增加,不知道我这个观点是否成立??请批评指正,谢谢马老师!! 实际上任何高级语言都需要和自动(可以不用)使用一个所谓的“启动”程序,该程序的主要工作有:
1.初始化堆栈指针寄存器
2.初始化和设置中断向量
3.初始化何设置变量的初值等。
其中1、2两部分肯定需要的,这样可以让程序员集中在编写自己的程序。
fastavr我没使用过,你可以查看它生成的汇编代码,或将HEX文件反汇编查看,应该开始执行的第一条语句(在FLASH的0000H单元)是转移指令,跳转到的地方应该就是堆栈的初始化部分。在0000H后边应该是中断向量。如果你自己的程序没有对堆栈的初始化语句,那么这些部分就是系统填加的必要的“启动”程序。
所以我一直强调,真正能掌握AVR、以及用好AVR,必须从汇编入手。有了汇编的基础,你才能正确的分析和使用其他的高级语言开发平台。因为所有的高级语言系统都必须首先将源程序编译成汇编程序,然后由汇编生成可运行的机器代码。 这是一个读写EEPROM的程序,fastavr4.1.3版本编写的:
'///////////////////////////////////////////////////////////////////////
'//这个程序用于对EEPROM操作指令的测试以及MemFill指令运行情况
'//编译软件FASTAVR4.1.3
'//编写者JAMESKING
'//程序运行情况:
'//编译正常,实物测试正常
'///////////////////////////////////////////////////////////////////////
$Device = m16 '
$Clock = 7.3728 '
$Stack = 125
$Lcd =PORTB.0 , RS = PORTD.3 , EN = PORTB.4, 16, 2 'LCD连接方式DB4、5、6、7连接PD4、5、6、7,此处必须要连续端口,其余应该可以看得懂的
$Baud = 115200 '波特率设置为115200
$Source=On 'asm输出
Dim N As Byte
Dim i As Byte
Dim G As Byte
Dim k As Byte
Dim M As Byte
Do
InitLcd( )
Cls
MemFill (&h80,10,&h55)
N=Peek(&h080) '
Locate 2,8
Lcd "N=HEX";Hex(N)
InitEE =0,1,2,3,4,5,6,7 '需要初始化EEPROM的地址0、1、2、3、4、5、6、7
WaitMs 100
M=ReadEE(&h01) '读取以上列举的地址1(即EEPROM地址22)的内容,这时候由于已经
'对EEPROM初始化完毕,所以以上EEPROM地址的内容全部是0FFH或者255
Locate 2,1
Lcd "M="; M
Wait 1 '
' Cls '
WaitMs 500 '
Cursor On '
Cursor Blink '
k=60
For i=8 To 15
k=k+1
WriteEE(i,k)
Next i
For i=0 To 15
WaitMs 200
G=ReadEE(i)
Locate 1,1: Lcd "G="; G; ""
Nop 10 '在液晶的第一行显示字符
Wait 1
Next
Loop
End
其asm初始化代码如下(原asm代码较长,我贴上初始化部分asm代码):
;FastAVR Basic Compiler, ver.4.1.3, by MicroDesign
;Created 7:58:42, 22-04-2006
;
.include "C:\FastAVR\inc\m16def.inc"
;
.DSEG
n: .Byte 1
i: .Byte 1
g: .Byte 1
k: .Byte 1
m: .Byte 1
.CSEG
.ORG 0
_Reset:
ldi yl,Low(RAMEND)
out SPL,yl
ldi yh,high(RAMEND)
out SPL+1,yh
sbiw yl,63
sbiw yl, 62
ldi zl,0x18
out UCSRB,zl
ldi zh,high(3)
ldi zl,Low(3)
out UBRRL,zl
out UBRRH,zh
call LcdIni '此句话是初始化LCD1602的子程序调用
从这个程序可以看出,马老师所讲述的1、2点可以在初始化程序中自动实现,关于第3点设置变量的初始值,需要程序员人工干预,自行设置相应的初始值,而fastavr并没有对内部SRAM进行填充00H的任何操作(即清零操作),个人认为对内部SRAM清零并无太多必要。
关于对I/O端口初始化的问题,我的理解是:在初始化时,对I/O端口方向设置为输入,并且为高阻态,同时对I/O端口寄存器清零,这在硬件复位的过程中由硬件自动完成的,所以,从这个意义上来说,fastavr完成了马老师所说的那3点初始化要求,比较CVAVR的初始化和bascom初始化程序,看样子并无对SRAM清零的必要(CVAVR和bascom也就是多了这几点)。
实际上,我认为MCU复位就保证了马老师提到的1、2点功能的正确性,不知道马老师是否也这样认为的??所以,我认为fastavr可以完成基本的初始化功能,并不会对程序的健壮性造成重大影响。请马老师和各位同仁批评指正,谢谢!!!
再次感谢马老师的详细解说,谢谢!!!
-----此内容被JAMESKING于2006-04-22,21:20:40编辑过 你的分析不全面。
1。“我认为MCU复位就保证了马老师提到的1、2点功能的正确性”错!
a) MCU复位并不会设置堆栈指针寄存器的!
ldi yl,Low(RAMEND)
out SPL,yl
ldi yh,high(RAMEND)
out SPL+1,yh
以上4条汇编指令对应的BASIC语句你写了吗?它就是fastavr的启动程序,你了解他们的作用吗?
b) MCU复位并不会设置中断向量的的!
在你的BASIC程序中,没有使用中断。如果你写一段有中断的BASIC程序后,再分析其生成的ASM文件的话,你就会发现有不同了。
2。“并不会对程序的健壮性造成重大影响”不全面!
关于这个问题不是一两句能解释清楚的,而且在你目前的水平上也不可能深入的认识。我只能简单的举个比喻:
看到你使用fastavr生成的ASM,我认为它的“启动”程序的“健壮性”为80%,作为一般的应用和学习可以。而CVAVR或其他的系统可能要高一些。
系统程序的“健壮性”也不是由你所使用的环境的“启动程序”决定的,它只是一部分,你自己编写的程序也应该考虑。如在一个工业控制的系统中,需要打开和关闭一个控制开关,间隔5秒钟(5秒开,5秒关),请比较下面两种处理方式:
a)开--延时5秒--关--延时5秒--开--......
b)开--延时1秒--开--延时1秒--开--延时1秒--开--延时1秒--开--延时1秒--关--延时1秒--关--延时1秒--关--延时1秒--关--延时1秒--关--延时1秒--开--......
那种方式的“健壮性”好? 首先感谢马老师在百忙之中抽空回答我的问题,谢谢!!
这两天事情比较多,没有及时回复马老师的回答,是我失礼了,在此向马老师及各位朋友们表示诚挚歉意!!
关于马老师的回复,我认为我所举的例子不是很合适,现在举出一个例子说明一个问题,就是那个中断复位的问题:
[此程序转贴自www.basicavr.com首页,地址如下:http://www.basicavr.com/liusuiden_int0_lizi.htm,感谢阿贵站长编写这个程序]
'/////////////////////////////////////////////////////////
'///工程:FastAVR Basic 使用定时器0的流水灯试验
'///芯片:mega8515
'///简介:LED从PORTB.0流动到PORTB.7然后流回来
'///编译:通过,131 words
'///实践:实物测试通过
'///作者:agui2008
'///时间:2005/8/11
'/////////////////////////////////////////////////////////
$Device = m8515 '声明AVR型号
$Stack = 32 '堆栈大小
$Clock = 1 '时钟频率1M
$Timer0 = Timer, Prescale = 1024 'timer0为定时器,预分频1024
Declare Interrupt Ovf0() '声明使用timer0溢出中断
Dim n As Byte '定义循环变量n
Dim m As Byte '定义计数方向变量m
n=0
m=0 'm=0,n=n+1;m=1,n=n-1
DDRB=255 '定义PORTB为输出口
Enable Interrupts '允许全局中断
Timer0=&h3d 'timer0中断溢出时间=200ms
Enable Ovf0 '允许timer0中断
Start Timer0 '启动timer0
Do
Select Case n
Case 0
PORTB=&b00000001
Case 1
PORTB=&b00000010
Case 2
PORTB=&b00000100
Case 3
PORTB=&b00001000
Case 4
PORTB=&b00010000
Case 5
PORTB=&b00100000
Case 6
PORTB=&b01000000
Case 7
PORTB=&b10000000
End Select
Loop
End
'//////////////定时器0中断服务程序///////////////
Interrupt Ovf0() 'timer0中断服务
Timer0=&h3d '重装初值,其实可以用比较模式实现自动重装
If n=7 Then '控制计数方向
m=1
ElseIf n=0 Then m=0
End If
If m=0 Then
Incr n 'n=n+1
ElseIf m=1 Then '根据m决定对n进行加减1
Decr n 'n=n-1
End If
Enable Interrupts 'AVR在进入中断之后会关闭全局中断,所以在这里要重新打开全局中断
End Interrupt '中断服务程序结束
我将此程序编译后,生成asm文件如下,由于篇幅限制,我仅仅贴上来初始化的程序:
;FastAVR Basic Compiler, ver.4.1.3, by MicroDesign
;Created 13:39:42, 24-04-2006
;
.include "C:\FastAVR\inc\m8515def.inc"
;
.DSEG
n: .Byte 1
m: .Byte 1
.CSEG
.ORG 0
rjmp _Reset
.ORG INT0addr
reti
.ORG INT1addr
reti
.ORG ICP1addr
reti
.ORG OC1Aaddr
reti
.ORG OC1Baddr
reti
.ORG OVF1addr
reti
.ORG OVF0addr
rjmp IntN7
_Reset:
ldi yl,Low(RAMEND)
out SPL,yl
ldi yh,high(RAMEND)
out SPL+1,yh
sbiw yl,32
从asm可以很明显看出,fastavr确实对中断和向量初始化了,就是这几句:
.ORG INT0addr
reti
.ORG INT1addr
reti
.ORG ICP1addr
reti
.ORG OC1Aaddr
reti
.ORG OC1Baddr
reti
.ORG OVF1addr
reti
.ORG OVF0addr
rjmp IntN7
.............
;-Line--0048----Interrupt Ovf0() 'timer0中断服务--
;-Line--0049----Timer0=&h3d '重装初值,其实可以用比较模式实现自动重装--
IntN7:
in r2,SREG
ldi zl,Low(61)
out TCNT0,zl
对比以上两个例子可以看出,fastavr的初始化是编程者需要什么,就加入初始化语句,由编译器自动生成初始化语句,这个意义上算半自动初始化,需要一定的人工干预。
因此马老师这句话“1。“我认为MCU复位就保证了马老师提到的1、2点功能的正确性”错! ”我认为确实是我错了,我理解初始化的意思有些问题,我原来的理解是:“初始化,就是对内部SFR和资源清零或使之回复到厂家手册推荐的初始值”,谢谢马老师教导!!
马老师b)中“b) MCU复位并不会设置中断向量的的!”这句话我认为正确,复位只能使全局中断禁止,并且禁止所有的中断源。
ldi yl,Low(RAMEND)
out SPL,yl
ldi yh,high(RAMEND)
out SPL+1,yh
这四句话的意思是对堆栈的位置进行设置,一般AVR设置在SRAM的顶端,根据fastavr的使用手册,堆栈起始于SRAM顶端,并向下延伸,所以这四句话定义了堆栈的栈底位置,对应的basic语句为$Stack=125这句话。
由于原来我转贴的程序没有使用堆栈,所以堆栈指针寄存器并未被初始化,如果找到使用堆栈的fastavr程序,那就可以看看到底fastavr如何对堆栈按照编程者的意图来初始化的。但是根据fastavr对中断操作的情况来看,可能对于堆栈的初始化操作也类似。今后我找到fatsavr关于堆栈的初始化操作的程序后,我贴上来,请马老师和各位高手共同分析,谢谢!!
程序的健壮性确实不只是几句初始化就完事的,需要在程序编写中通盘考虑,比如说:
1。对于内部EEPROM存储的设定数据,在读取的时候至少需要3取2比较判断比较好,高可靠性的需要5取3(我现在就是5取3)甚至7取5判断,有条件的更加需要用累加和校验;
2。比如I/O端口操作的抗干扰,模仿asm的操作方法,可以用如下程序来实现(fastavr):
DDRB.1=1
NOP 5
DDRB.1=1
NOP 5
DDRB.1=1
NOP 5
SET PORTB.1
NOP 5
SET PORTB.1
NOP 5
SET PORTB.1
NOP 5
此程序已经应用到实际生产中,效果比较好,比较可靠,这个程序的作用是置位PORTB.1为输出,并且置PORTB.1为高电平输出,程序中使用了多次重复置位和NOP语句,使I/O端口状态稳定,这在一定程度上提高了程序的健壮性(也就是原来51年代说的程序的抗干扰性和容错性),在这点上我认为马老师提到的“ b)开--延时1秒--开--延时1秒--开--延时1秒--开--延时1秒--开--延时1秒--关--延时1秒--关--延时1秒--关--延时1秒--关--延时1秒--关--延时1秒--开--...... ”这种操作方法比较好,原理上那两种方法类似;
3。另外,在程序分支和跳转前后采用NOP语句,也有利于程序稳定性的提高和防止跑飞;
其余还有很多,建议大家可参考北航出版社周航慈先生的大作《单片机程序设计基础》,用asm软件抗干扰的思路,用高级语言实现,这种做法兼顾比较好。
但是,任何采用指令冗余和校验程序来提高程序健壮性都会使程序代码量增大,程序执行速度减慢,需要编程者权衡系统考虑。
我正在学习CVAVR,发现初始化代码长度很大,而且对SRAM也进行初始化清零,个人认为无此必要,SRAM作为变量的存储空间,在变化的时候自然会改变,如果不改变或改变错误,那么很可能是SRAM硬件问题了,不是程序的问题了。
因此,我认为由于现在举例暂时不足,只能说明马老师6楼帖子中
“1.初始化堆栈指针寄存器
2.初始化和设置中断向量
3.初始化何设置变量的初值等。”
的第2、3点可以实现,第一点有待证实。
上面是我对初始化的一点不成熟的见解,观点可能多有偏颇,请马老师和各位朋友们批评指正,谢谢!!
再次感谢马老师在百忙之中抽空回答我的疑问,谢谢!!! 1.初始化堆栈指针寄存器-------只要你使用子程序,必须要!
2.初始化和设置中断向量-------只要你使用中断,必须要!
3.初始化和设置变量的初值等------如果使用C的话,肯定有。C里面有全局变量、局部变量等,需要进行内存管理。BASIC中都使用全局变量,管理比较简单,所以“启动程序”也简单。 谢谢马老师的热情解说,也谢谢各位朋友们的大力关怀!!
我也再试试fastavr的局部变量定义看看,看看是否也会改变初始化值。
-----此内容被JAMESKING于2006-04-27,01:17:19编辑过 fastavr是属于BASIC语言吧,有局部变量? 马老师您好:
fastavr确实属于AVR的basic语言,也确实有局部变量和全局变量之分,请参考本站FASTAVR/BASCOM-AVR专栏,那里有中文使用说明书和最新版应用程序下载,需要参考编写的程序,请光临本网站FASTAVR/BASCOM-AVR专栏以及www.basicavr.com网站论坛的fastavr栏目,谢谢!!
用Local语句,可以实现fastavr对局部变量的定义和使用,简单例子如下,参考fastavr中文使用说明书P108页:
例子:
'//////////////////////////////////////////////////
Sub Test(n As Byte)
Local a As Byte '局部字节变量名称 a
Local w As Word '局部字变量名称 w
....子程序主体....
End Sub
'Arrays 数组, Bits 位 和 Strings 字符串不能作为局部变量! 那你再试一试 这几天现场调试的事情比较多,可能要到五·一才有时间编写关于fastavr局部变量使用的程序。马老师和各位朋友们,真的对不起了,请等几天吧,不好意思。 高手就是不同啊
佩服ing!!!!
页:
[1]