金牌会员
- 积分
- 1181
- 金钱
- 1181
- 注册时间
- 2018-4-13
- 在线时间
- 170 小时
|
楼主 |
发表于 2018-7-4 14:47:28
|
显示全部楼层
本帖最后由 xiatianyun 于 2018-7-4 17:24 编辑
来看看原子提供的例程sys.h文件关于位带操作的宏定义。
主要是GPIO端口的读写位操作实现。
读写GPIO端口是通过ODR和IDR寄存器来实现的。
所以,需要映射的寄存器就是ODR和IDR。
库里面关于GPIO寄存器的分装是用结构来实现的,并没有独立映射某个寄存器。
不过库里面映射了GPIOA~GPIOG,可以看作是GPIO的首地址,而GPIO寄存器地址是这些首地址的偏移。
所以有如下关于ODR和IDR寄存器地址的宏定义:
[mw_shl_code=c,true]//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
......
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 [/mw_shl_code]
映射完寄存器地址后来看看是怎么和位带区进行映射的:
外设位带区开始地址是0x4200,0000,如果需要映射的是A.n,则公式是:
AliasAddr=0x42000000+ (A‐0x40000000)*32 + n*4
这里,0x4000,0000是外设低1MB开始地址,也即外设可以映射位带的开始地址。0x4000,0000映射到0x4200,0000。
(A-0x4000,0000)意味着A和起始地址的偏移,(A-0x4000,0000)*32是A之前寄存器所映射的位带区所占空间字节数,也即A映射到位带区的开始偏移地址。
n*4就是第nbit映射的偏移地址(1bit-->4byte)。bit0就意味着是A-0x4000,0000。也即n的值是0开始,bit0~bit7,当然n也可以超过7,一样计算。
这个公式计算了A.n这个位在外设位带区的映射地址。
*32就是<<5,而*4就是<<2。
所以,映射就是:
[size=14.6667px]AliasAddr=0x42000000+ ((A‐0x40000000) << 5 + n << 2)
看看如何把SRAM位带也用同一个映射表示。
他们的共同点都是位带区位于偏移0x0200,0000开始地址,所以先取得高4位,然后+0x200,0000.
(A & 0xF000,0000) + 0x200,0000 就是位带基址。
SRAM开始地址为0x2000,0000,所以首偏移可以用(A - (A & 0xF000,0000))表示。不过sys.h的做法是简单地(A & 0xFFFFF),也就是直接屏蔽了高12位的所有针对位段的偏移地址,只对低20位有效。
也就是A的地址如果除了高4位为位段外其他高8位有效的话这个定义就失效了。比如0x2010,0000,高12位为0x201,映射失效,只能是0x200x,xxxx开始的地址有效。外设地址是0x400x,xxxx有效。
不过GPIO的地址一般都是0x400x,xxxx。比如GPIOA的ODR寄存器是0x4001080C。
简单粗暴。
这样就有了宏定义:
[mw_shl_code=c,true]//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) [/mw_shl_code]
第二个MEM_ADDR是把刚才的数值转换为指向unsigned long的指针,并定义为指针所指单元的数据值(取值)。第三个BIT_ADDR是实际需要使用的宏定义,使用BIT_ADDR(A,n)就得到了指向位带的指针,对该位带指针操作也就操作了位带映射的寄存器位或是内存某个位。
BIT_ADD()用在表达式左边是写操作,用在右边是读操作。
之后为了简单使用,又直接定义了读写GPIO端口的宏,比如
[mw_shl_code=c,true]//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 [/mw_shl_code]
这样读写就很明确了,看名字就可以了。
PAout(2) = 1;
sb = PAin(2);
--------------
不过对于ODR来说是可读可写的,所以PAout(2)既可以在=左边也可以在右边。
[size=14.6667px]
|
|