众所周知,对于原子和同时读/写16位I / O寄存器(定时器计数器,ICR / OCR,ADC ......)的高低部分,AVR使用影子临时寄存器。例如。在ATmega8上阅读TCNT1
:
uint8_t tl, th;
tl = TCNT1L; // tl <- TCNT1L, avr_temp <- TCNT1H (atomic)
th = TCNT1H; // th <- avr_temp
(这里avr_temp
是AVR临时影子寄存器)。因此,首先阅读TCNT1H
是错误的。
将AVR-GCC与以下代码一起使用是否安全?
uint16_t ticks;
ticks = TCNT1;
TCNT1 = 0x1234;
AVR-GCC会不会为这些操作生成适当的代码?
(似乎是“不”(GCC如何知道访问由TCNT1
指向的内存使用AVR影子寄存器?),但avr-libc定义宏TCNT1以及TCNT1H,TCNT1L和{{3建议直接使用TCNT1
。我很困惑。)
我测试了AVR-GCC v4.7.2,它似乎始终生成正确的代码。即使我写'TCNT1 | = 1',它也会生成-O3
的正确代码:
$ avr-gcc -std=c99 -mmcu=atmega8 -S -O3 -o - 1.c
...
in r24,0x2c // TCNT1L
in r25,0x2c+1 // TCNT1H
ori r24,1
out 0x2c+1,r25
out 0x2c,r24
...
即使我用普通的16位变量更改TCNT1
,代码也是一样的。那么,“GCC如何知道访问由TCNT1
指向的内存使用AVR影子寄存器?” - 默认情况下,当访问任何 16位变量时,假设影子寄存器总是。
答案 0 :(得分:2)
我不知道它是怎么知道的,但是我已经在几十个程序中直接使用TCNT1
而没有问题。 FAQ you linked建议你这样做,就像我读过的每个教程一样。
FAQ的目的是确保在写入两个寄存器之间不会发生中断。虽然avr-gcc会生成以正确顺序访问它们的代码,但它不能保证它们之间不会发生中断:你必须要处理它。
答案 1 :(得分:0)
如果您阅读了例如/usr/lib/avr/include/avr/iom32u4.h你会看到这个:
#define TCNT1 _SFR_MEM16(0x84)
在/usr/lib/avr/include/avr/sfr_defs.h中展开_SFR_MEM16宏,你会看到:
#define _SFR_MEM16(mem_addr) _MMIO_WORD(mem_addr)
扩展_MMIO_WORD:
#define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr))
然后看stdint.h: typedef unsigned int uint16_t;
这是编译器介入的地方.AVR平台上的int类型是16位,AVR上的寄存器是8,因此编译器必须进行2次内存访问(没有定义16位内存访问指令)它的使用)。根据文档here:
编译器将根据被访问寄存器的地址选择正确的指令序列。
顺便说一下,我首先认为它是variable attributes,但事实证明它是通过芯片的存储器映射进入AVR架构的。