您的位置:首页 > 产品设计 > UI/UE

Arduino代码机制-IO

2016-02-20 19:24 603 查看
AVR单片机中有三种存储器,Flash, RAM, EEPROM。这篇讨论的是RAM。

RAM编址

AVR单片机的RAM编址是这样的,最先的是32个通用寄存器,然后是64个I/O寄存器,然后是扩展I/O寄存器,最后才是Internal SRAM。以ATmega2560为例,内存为8192字节。通用寄存器地址范围是0x00到0x1f,I/O寄存器地址范围是0x20到0x5f,扩展I/O寄存器地址范围是0x60到0x1ff,内存地址范围是0x200到0x2200。如图所示。图为ATmega2560的datasheet中内容。



RAM地址是16位的,最多能寻址64k字节的地址空间。要进行RAM扩展的话,最多能扩展多大内存呢?

16位地址寻址能力是64k字节,其中已经编址了0x2200的地址空间,只能扩展剩余的地址空间,因此最多能扩展的内存为0xffff - 0x21ff = 0xde00的内存。如果使用64kB的存储器来扩展的话,那么存储器的高字节部分将会访问不到。

I/O寄存器

尽管Arduino提供了大量的函数和宏,而且大部分效率很高且使用安全,使得开发者不需要关心底层的硬件。但也会有一些特定的情况,Arduino做的不好的。例如要将某个端口全部设置为输出,例如用某个端口并行传输数据。这时候,直接对寄存器操作就比用arduino函数效率高得多。

第一个问题,将端口C全部设置为输出的话,直接对寄存器操作代码将会很简单,而且效率很高:

DDRC = 0xFF;


如果使用arduino的函数的话,将会调用8遍pinMode:

pinMode(37, OUTPUT);
pinMode(36, OUTPUT);
pinMode(35, OUTPUT);
pinMode(34, OUTPUT);
pinMode(33, OUTPUT);
pinMode(32, OUTPUT);
pinMode(31, OUTPUT);
pinMode(30, OUTPUT);


再考虑到pinMode函数里还需要读取Flash等等操作,效率就低了很多。因此有时候直接操作寄存器还是必要的。

在上面给寄存器DDRC赋值的操作是不是很方便?但是为何给寄存器名字赋值就是给这个寄存器赋值了呢?而我们应该给这个寄存器的那一块内存赋值才行。我们应该关心一下DDRC到底是什么。

如果知道DDRC的地址的话,假如是addr,那么给DDRC赋值就可以这样:

*addr = 0xff;


好了,如果知道每一个寄存器的地址,再将寄存器的名字宏定义为*addr,就像这样:

#define /*寄存器名称*/ *(/*地址*/)


那么开发者就可以用寄存器名字来进行寄存器的读取操作了。

实际上的就是这样的,但要复杂一些。

AVR的单片机存在两种编址方式,一种是从通用寄存器开始编址,就像上面说的那样,那么这时候I/O寄存器的地址就是从0x20开始。另一种是通用寄存器不编址,而从I/O寄存器开始编址,这时候I/O寄存器的地址就是从0开始。不同型号单片机采用这两种编址方式之一。为了适应着两种情况,定义了寄存器起始地址宏:

#ifndef __SFR_OFFSET
#  if __AVR_ARCH__ >= 100
#    define __SFR_OFFSET 0x00
#  else
#    define __SFR_OFFSET 0x20
#  endif
#endif


在预编译时期,根据不同型号的单片机确定起始地址。

这样,我们就能认为所有单片机的I/O寄存器都是从0开始编址,寄存器的真实地址为(addr + 起始地址)。

又定义了读取内存内容的宏:

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr))
#define _MMIO_DWORD(mem_addr) (*(volatile uint32_t *)(mem_addr))


分别用来读取一个字节、一个字、两个字。

又定义了宏SFR_IO,这个宏有两个作用,计算真实地址和取真实地址中内容:

#if _SFR_ASM_COMPAT
#define _SFR_IO8(io_addr) ((io_addr) + __SFR_OFFSET)
#define _SFR_IO16(io_addr) ((io_addr) + __SFR_OFFSET)
#else
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _SFR_IO16(io_addr) _MMIO_WORD((io_addr) + __SFR_OFFSET)
#endif


在将寄存器名称定义为SFR_IO和地址,就行了。例如:

#define PINA    _SFR_IO8(0X00)


经过几次宏定义,就能够简单的操作寄存器啦。

源代码参考:

hardware\tools\avr\avr\include\avr\io.h

hardware\tools\avr\avr\include\avr\sfr_defs.h
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: