B001-Atmega16-16位寄存器的读写步骤

时间:2022-09-04 20:06:55

临时寄存器TEMP

B001-Atmega16-16位寄存器的读写步骤

8位总线一次只能读写取8位数据,所以读写16位寄存器时、使用8位的临时寄存器TEMP来保存另一半的8位数据。

一个定时器只有1个临时寄存器TEMP,所以读写其他16位寄存器时,临时寄存器TEMP将被修改成当前的16位寄存器的高字节或低字节。

所以读取16位寄存器的过程中需要禁止中断,避免中断处理过程中有读写其他16位寄存器的操作,

这会修改临时寄存器TEMP的当前值,导致中断返回后临时寄存器TEMP已被修改。

读写低字节、将触发16位的读写。

--> 为了简化问题,在中断中不要读写16位寄存器


读16位定时器的步骤:

in  r18,SREG    ;   保存全局中断标志
cli ;   禁止全局中断
in r16,TCNT1L  ;   读低字节的同时、高字节被复制到 临时寄存器TEMP -- 触发16位读操作
in r17,TCNT1H  ;   此时读的高字节、其实读的是 临时寄存器TEMP
out SREG,r18   ;   恢复全局中断标志

写16位定时器的步骤:

in  r18,SREG    ;   保存全局中断标志
cli ; 禁止全局中断
out TCNT1H,r17 ; 写高字节、其实只是将数据写到 临时寄存器TEMP
out TCNT1L,r16 ; 写低字节的同时、临时寄存器TEMP 和低字节将一起组成16位数据、写入16位寄存器 -- 触发16位写操作
out SREG,r18 ; 恢复全局中断标志


不能只读写高字节

1、所以如果只写高字节,数据仅仅是被写入临时寄存器TEMP,而不是写入16位寄存器的高字节。

      必须是在写低字节的时候,高字节才会和低字节一起被写入16位寄存器。

      例如、将定时器1初始化为普通模式、COM1A不启用、COM1B不启用、8预分频,溢出周期=65.536ms

      同时使用PA2来测量溢出周期,每次TOV1中断时、PA2的电平翻转一次。

      如果我们在中断中修改TCNT1H、而不修改TCNT1L,那么这个修改不会生效。

测试代码( 写 ):

// ==========================================================================================================
// TIMER1 溢出中断服务程序
//
// ==========================================================================================================
ISR(TIMER1_OVF_vect)
{
TCNT1H = 0xFF;
// TCNT1L = 0;
PORTA ^= (1 << PA2); // 使用PA2来测量溢出周期
}

测试结果( 写 )

      PA2引脚的电平翻转周期将永远是65.536ms

      只有同时修改TCNT1LTCNT1H的修改才会生效,此时PA2的电平翻转周期才会变化。


2、同样,读高字节时、是从临时寄存器TEMP中读取数据,而不是从16位寄存器的高字节部分直接读取。

      所以只读高字节,得到的永远是临时寄存器TEMP中的同一个值,如果临时寄存器TEMP不被修改。

      要得到高字节,必须先读低字节,触发16位的读操作,这样、16位寄存器的高字节会被复制到临时寄存器TEMP

      此时再去读取高字节,虽然依然是从临时寄存器TEMP中读取数据,但这个数据已经是16位寄存器的高字节的备份了。

测试代码( 读 ):

volatile uint8_t  data_H = 0;
volatile uint8_t data_L = 0;
volatile uint16_t data = 0;

    while(1)    {        data_H = TCNT1H;    }


1、定时器初始化不变,进入DEBUG模式,在任意时间暂停运行,查看data_H的值。

测试结果( 读 ):

1、每次暂停运行后,data_H的值都是0,而不是TCNT1H的实时值:

B001-Atmega16-16位寄存器的读写步骤

2、仅当读取过TCNT1L时,TCNT1H的值才会被读出:

    while(1)
{
data_H = TCNT1H;
data_L = TCNT1L; // 增加这一句和下面一句都可
// data = TCNT1;
}
      实际上应该是要先读 TCNT1L,才可以读到 TCNT1H,但这里先读 TCNT1H居然也能读的对:

B001-Atmega16-16位寄存器的读写步骤

      而且从汇编代码可以看出、编译器也没自己调整过位置,所以奇怪(0x2D就是TCNT1H的地址,data_H的地址是0x0060):

B001-Atmega16-16位寄存器的读写步骤

      但可以确定的是:只读TCNT1H是得不到TCNT1H的数据的。