【问题标题】:Does AVR-GCC properly work with 16-bit AVR I/O registers?AVR-GCC 能否与 16 位 AVR I/O 寄存器一起正常工作?
【发布时间】:2012-12-19 04:09:50
【问题描述】:

序言

众所周知,对于原子和同时读取/写入 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和avr-libc' FAQ建议直接使用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 影子寄存器?” -- 在访问 any 16 位变量时,默认情况下会假设影子寄存器 always

【问题讨论】:

    标签: avr avr-gcc


    【解决方案1】:

    我不知道它是怎么知道的,但我已经在几十个程序中直接使用TCNT1 没有问题。 FAQ you linked 建议你这样做,就像我读过的每个教程一样。

    FAQ 的重点是确保在两个寄存器的写入之间不会发生中断。尽管 avr-gcc 会生成以正确顺序访问它们的代码,但它不能保证它们之间不会发生中断:您必须注意这一点。

    【讨论】:

    • 投反对票的人是否愿意说出这个答案有什么问题?
    【解决方案2】:

    如果您阅读例如的来源/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 架构中的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-01
      • 1970-01-01
      • 2014-06-27
      相关资源
      最近更新 更多