ATmega16 指令执行时序
这一节介绍指令执行过程中的访问时序。AVR CPU 由系统时钟clkCPU 驱动。此时钟直接来自选定的时钟源。芯片内部不对此时钟进行分频。
Figure 6 说明了由Harvard 结构决定的并行取指和指令执行,以及可以进行快速访问的寄存器文件的概念。这是一个基本的流水线概念,性能高达1 MIPS/MHz,具有优良的性价比、功能/ 时钟比、功能/ 功耗比。
Figure 7 演示的是寄存器文件内部访问时序。在一个时钟周期里,ALU 可以同时对两个寄存器操作数进行操作,同时将结果保存到目的寄存器中去。
ATmega16 复位与中断处理
AVR有不同的中断源。每个中断和复位在程序空间都有独立的中断向量。所有的中断事件都有自己的使能位。当使能位置位,且状态寄存器的全局中断使能位I 也置位时,中断可以发生。根据程序计数器PC 的不同,在引导锁定位BLB02 或BLB12 被编程的情况下,中断可能被自动禁止。这个特性提高了软件的安全性。详见 P247“ 存储器编程” 的描述。
汇编代码例程
C 代码例程
ATmega16 Flash程序存储器
系统内可编程的Flash 程序存储器
ATmega16 SRAM数据存储器
SRAM数据存储器
SRAM数据存储器访 ATmega16 EEPROM数据存储器
ATmega16 包含512 字节的EEPROM 数据存储器。它是作为一个独立的数据空间而存在的,可以按字节读写。EEPROM 的寿命至少为100,000 次擦除周期。EEPROM 的访问由地址寄存器、数据寄存器和控制寄存器决定。
? Bits 15..9 – Res: 保留
? Bits 7..0 – EEDR7.0: EEPROM 数据
EEPROM 控制寄存器- EECR
? Bits 7..4 – Res: 保留
下面的代码分别用汇编和C 函数说明如何实现EEPROM 的写操作。在此假设中断不会在执行这些函数的过程当中发生。同时还假设软件没有Boot Loader。若Boot Loader 存在,则EEPROM 写函数还需要等待正在运行的SPM 命令的结束。
防止EEPROM 数据丢失
若电源电压过低,CPU 和EEPROM 有可能工作不正常,造成EEPROM 数据的毁坏( 丢失)。这种情况在使用独立的EEPROM 器件时也会遇到。因而需要使用相同的保护方案。
ATmega16 I/O存储器
程序存储区的最低地址缺省为复位向量和中断向量。完整的向量列表请参见 P43“中断”。列表也决定了不同中断的优先级。向量所在的地址越低,优先级越高。RESET 具有最高的优先级,第二个为INT0 – 外部中断请求0。通过置位MCU 控制寄存器 (MCUCR) 的IVSEL,中断向量可以移至引导Flash的起始处。编程熔丝位BOOTRST也可以将复位向量移至引导Flash的起始处。具体参见P234“支持引导装入程序 – 在写的同时可以读(RWW,Read-While-Write) 的自我编程能力”。
任一中断发生时全局中断使能位I 被清零,从而禁止了所有其他的中断。用户软件可以在中断程序里置位I 来实现中断嵌套。此时所有的中断都可以中断当前的中断服务程序。执行RETI 指令后I 自动置位。
从根本上说有两种类型的中断。第一种由事件触发并置位中断标志。对于这些中断,程序计数器跳转到实际的中断向量以执行中断处理程序,同时硬件将清除相应的中断标志。中断标志也可以通过对其写”
第二种类型的中断则是只要中断条件满足,就会一直触发。这些中断不需要中断标志。若中断条件在中断使能之前就消失了,中断不会被触发。
AVR 退出中断后总是回到主程序并至少执行一条指令才可以去执行其他被挂起的中断。要注意的是,进入中断服务程序时状态寄存器不会自动保存,中断返回时也不会自动恢复。这些工作必须由用户通过软件来完成。
使用CLI 指令来禁止中断时,中断禁止立即生效。没有中断可以在执行CLI 指令后发生,即使它是在执行CLI 指令的同时发生的。下面的例子说明了如何在写EEPROM 时使用这个指令来防止中断发生以避免对EEPROM 内容的可能破坏。
in r16, SREG ; 保存SREG
cli ; 禁止中断
sbi EECR, EEMWE ; 启动 EEPROM 写操作
sbi EECR, EEWE
out SREG, r16 ; 恢复SREG (I 位)
char cSREG;
cSREG = SREG; /* 保存SREG */
/* 禁止中断*/
_CLI();
EECR |= (1<<EEMWE); /* 启动 EEPROM 写操作*/
EECR |= (1<<EEWE);
SREG = cSREG; /* 恢复SREG (I 位) */
使用SEI 指令使能中断时,紧跟其后的第一条指令在执行任何中断之前一定会首先得到执行。
汇编代码例程
sei ; 置位全局中断使能标志
sleep ; 进入休眠模式,等待中断发生
; 注意: 在执行任何被挂起的中断之前MCU 将首先进入休眠模式
C 代码例程
_SEI(); /* 置位全局中断使能标志*/
_SLEEP(); /* 进入休眠模式,等待中断发生*/
/* 注意: 在执行任何被挂起的中断之前MCU 将首先进入休眠模式*/
AVR 中断响应时间最少为4 个时钟周期。4 个时钟周期后,程序跳转到实际的中断处理例程。在这4 个时钟期期间PC 自动入栈。在通常情况下,中断向量为一个跳转指令,此跳转需要3 个时钟周期。如果中断在一个多时钟周期指令执行期间发生,则在此多周期指令执行完毕后MCU 才会执行中断程序。若中断发生时MCU 处于休眠模式,中断响应时间还需增加4 个时钟周期。此外还要考虑到不同的休眠模式所需要的启动时间。这个时间不包括在前面提到的时钟周期里。
中断返回需要4 个时钟。在此期间PC( 两个字节) 将被弹出栈,堆栈指针加二,状态寄存器SREG 的I 置位。
ATmega16具有16K字节的在线编程Flash,用于存放程序指令代码。因为所有的AVR指令为16 位或32 位,故而Flash 组织成8K x 16 位的形式。用户程序的安全性要根据Flash程序存储器的两个区:引导(Boot) 程序区和应用程序区,分开来考虑。
Flash存储器至少可以擦写10,000次。ATmega16的程序计数器(PC)为13位,因此可以寻址8K 字的程序存储器空间。引导程序区以及相关的软件安全锁定位请参见 P234“ 支持引导装入程序 – 在写的同时可以读(RWW, Read-While-Write) 的自我编程能力” ,而 P247“存储器编程” 详述了用SPI 或JTAG 接口实现对Flash 的串行下载。
常数可以保存于整个程序存储器地址空间( 参考LPM 加载程序存储器指令的说明)。取指与执行时序图请参见 P11“ 指令执行时序”。
Figure 9 给出了ATmega16 SRAM 空间的组织结构。
前1120 个数据存储器包括了寄存器文件、I/O 存储器及内部数据SRAM。起始的96 个地址为寄存器文件与64 个I/O 存储器,接着是1024 字节的内部数据SRAM。
数据存储器的寻址方式分为5 种:直接寻址、带偏移量的间接寻址、间接寻址、带预减量的间接寻址和带后增量的间接寻址。寄存器文件中的寄存器R26 到R31 为间接寻址的指针寄存器。
直接寻址范围可达整个数据区。
带偏移量的间接寻址模式能够寻址到由寄存器Y 和 Z 给定的基址附近的63 个地址。
在自动预减和后加的间接寻址模式中,寄存器X、Y 和Z 自动增加或减少。
ATmega16的全部32个通用寄存器、64个I/O寄存器及1024个字节的内部数据SRAM可以通过所有上述的寻址模式进行访问。寄存器文件的描述见 P9“ 通用寄存器文件” 。
本节说明访问内部存储器的时序。如Figure 10 所示,内部数据SRAM 访问时间为两个clkCPU 时钟。 
通过SPI和JTAG及并行电缆下载EEPROM数据的操作请分别参见 P260、 P265及P250。
EEPROM 读/ 写访问
EEPROM 读/ 写访问EEPROM 的访问寄存器位于I/O 空间。
EEPROM的写访问时间由Table 1给出。自定时功能可以让用户软件监测何时可以开始写下一字节。用户操作EEPROM 需要注意如下问题:在电源滤波时间常数比较大的电路中,上电/ 下电时VCC 上升/ 下降速度会比较慢。此时CPU 可能工作于低于晶振所要求的电源电压。请参见 P20“ 防止EEPROM 数据丢失” 以避免出现EEPROM 数据丢失的问题。
为了防止无意识的EEPROM 写操作,需要执行一个特定的写时序。具体参看EEPROM控制寄存器的内容。
执行EEPROM 读操作时, CPU 会停止工作4 个周期,然后再执行后续指令;执行EEPROM 写操作时, CPU 会停止工作2 个周期,然后再执行后续指令。
EEPROM 地址寄存器- EEARH和EEARL
保留位,读操作返回值为零。
? Bits 8..0 – EEAR8..0: EEPROM 地址
EEPROM地址寄存器– EEARH和EEARL指定了512字节的EEPROM空间。EEPROM地址是线性的,从0 到511。EEAR 的初始值没有定义。在访问EEPROM 之前必须为其赋予正确的数据。
EEPROM 数据寄存器 - EEDR
对于EEPROM 写操作, EEDR 是需要写到EEAR 单元的数据;对于读操作, EEDR 是从地址EEAR 读取的数据。
保留位,读操作返回值为零。
? Bit 3 – EERIE: 使能EEPROM 准备好中断
若SREG 的I 为"1",则置位EERIE 将使能EEPROM 准备好中断。清零EERIE 则禁止此中断。当EEWE 清零时EEPROM 准备好中断即可发生。
? Bit 2 – EEMWE: EEPROM 主机写使能
EEMWE决定了EEWE置位是否可以启动EEPROM写操作。当EEMWE为"1"时,在4个时钟周期内置位EEWE 将把数据写入EEPROM 的指定地址;若EEMWE 为"0“,则操作EEWE 不起作用。EEMWE 置位后4 个周期,硬件对其清零。见EEPROM 写过程中对EEWE 位的描述。
? Bit 1 – EEWE: EEPROM 写使能
EEWE 为EEPROM 写操作的使能信号。当EEPROM 数据和地址设置好之后,需置位EEWE以便将数据写入EEPROM。此时EEMWE必须置位,否则EEPROM写操作将不会发生。写时序如下( 第3 步和第4 步的次序并不重要):
1. 等待EEWE 位变为零
2. 等待SPMCSR 中的SPMEN 位变为零
3. 将新的EEPROM 地址写入EEAR( 可选)
4. 将新的EEPROM 数据写入EEDR( 可选)
5. 对EECR 寄存器的EEMWE 写"1",同时清零EEWE
6. 在置位EEMWE 的4 个周期内,置位EEWE
在CPU 写Flash 存储器的时候不能对EEPROM 进行编程。在启动EEPROM 写操作之前软件必须检查 Flash 写操作是否已经完成。步骤(2) 仅在软件包含引导程序并允许CPU对Flash 进行编程时才有用。如果CPU 永远都不会写Flash,步骤(2) 可省略。请参见P234“支持引导装入程序 – 在写的同时可以读(RWW, Read-While-Write)的自我编程能力” 。
注意:如果在步骤5 和6 之间发生了中断,写操作将失败。因为此时EEPROM 写使能操作将超时。如果一个操作EEPROM的中断打断了另一个EEPROM操作,EEAR 或EEDR寄存器可能被修改,引起EEPROM 操作失败。建议此时关闭全局中断标志I。经过写访问时间之后,EEWE 硬件清零。用户可以凭借这一位判断写时序是否已经完成。EEWE 置位后, CPU 要停止两个时钟周期才会运行下一条指令。
? Bit 0 – EERE: EEPROM 读使能
EERE为EEPROM读操作的使能信号。当EEPROM地址设置好之后,需置位EERE以便将数据读入EEAR。EEPROM 数据的读取只需要一条指令,且无需等待。读取EEPROM后CPU 要停止4 个时钟周期才可以执行下一条指令。
用户在读取EEPROM 时应该检测EEWE。如果一个写操作正在进行,就无法读取EEPROM,也无法改变寄存器EEAR。 经过校准的片内振荡器用于EEPROM定时。
Table 1 为CPU访问EEPROM的典型时间。
汇编代码例程
EEPROM_write:
; 等待上一次写操作结束
sbic EECR,EEWE
rjmp EEPROM_write
; 设置地址寄存器 (r18:r17)
out EEARH, r18
out EEARL, r17
; 将数据写入数据寄存器(r16)
out EEDR,r16
; 置位EEMWE
sbi EECR,EEMWE
; 置位EEWE 以启动写操作
sbi EECR,EEWE
ret
C 代码例程
void EEPROM_write(unsigned int uiAddress, unsigned char ucData)
{
/* 等待上一次写操作结束 */
while(EECR & (1<<EEWE))
;
/* 设置地址和数据寄存器*/
EEAR = uiAddress;
EEDR = ucData;
/* 置位EEMWE */
EECR |= (1<<EEMWE);
/* 置位EEWE 以启动写操作*/
EECR |= (1<<EEWE);
}
下面的例子说明如何用汇编和C 函数来读取EEPROM,在此假设中断不会在执行这些函数的过程当中发生。
汇编代码例程
EEPROM_read:
; 等待上一次写操作结束
sbic EECR,EEWE
rjmp EEPROM_read
; 设置地址寄存器 (r18:r17)
out EEARH, r18
out EEARL, r17
; 设置EERE 以启动读操作
sbi EECR,EERE
; 自数据寄存器读取数据
in r16,EEDR
ret
C 代码例程
unsigned char EEPROM_read(unsigned int uiAddress)
{
/* 等待上一次写操作结束 */
while(EECR & (1<<EEWE))
;
/* 设置地址寄存器*/
EEAR = uiAddress;
/* 设置EERE 以启动读操作*/
EECR |= (1<<EERE);
/* 自数据寄存器返回数据 */
return EEDR;
}
在掉电休眠模式下的EEPROM写操作
若程序执行掉电指令时EEPROM 的写操作正在进行, EEPROM 的写操作将继续,并在指定的写访问时间之前完成。但写操作结束后,振荡器还将继续运行,单片机并非处于完全的掉电模式。因此在执行掉电指令之前应结束EEPROM 的写操作。
由于电压过低造成EEPROM 数据损坏有两种可能:一是电压低于EEPROM 写操作所需要的最低电压;二是CPU 本身已经无法正常工作。
EEPROM 数据损坏的问题可以通过以下方法解决:
当电压过低时保持AVR RESET信号为低。这可以通过使能芯片的掉电检测电路BOD来实现。如果BOD 电平无法满足要求则可以使用外部复位电路。若写操作过程当中发生了复位,只要电压足够高,写操作仍将正常结束。
ATmega16所有的I/O及外设都被放置于I/O空间。所有的I/O位置都可以通过IN 与OUT指令来访问,在32 个通用工作寄存器和I/O 之间传输数据。 地址为0x00 - 0x
为了与后续产品兼容,保留未用的未应写"0",而保留的I/O 寄存器则不应进行写操作。
一些状态标志位的清除是通过写"1" 来实现的。要注意的是,与其他大多数AVR 不同,CBI 和SBI 指令只能对某些特定的位进行操作,因而可以用于包含这些状态标志的寄存器。CBI 与SBI 指令只对0x00 到0x
I/O 和外设控制寄存器在后续其他章节进行介绍。 




