第七章 GPIO——位带操作
目录
本章参考资料:《W55MH32-中文参考手册》存储器和总线构架章节、GPIO章节
1 位带操作基础
1.1 位带技术概述
位操作是指针对单个二进制比特位的独立读写操作,这一特性在 51 单片机开发中极为常见(51 单片机通过sbit关键字实现位定义)。不同于 51 单片机,W55MH32 采用位带别名区访问机制实现位操作功能。
在 W55MH32 中,位带技术主要应用于两大区域:
- SRAM 位带区:SRAM 空间的最低 1MB 区域(地址范围:0X20000000~0X200FFFFF)
- 外设位带区:片上外设空间的最低 1MB 区域(地址范围:0X40000000~0X40100000)
这两个 1MB 的基础位带区除具备常规存储操作功能外,还映射了专属的位带别名区。位带别名区通过特殊机制将基础位带区的每个比特位扩展为 32 位字(4 字节),通过访问这些扩展后的 32 位字,即可实现对原基础位带区中指定比特位的精准操作。W55MH32 的内存映射与位带区关系可参考下图:

1.2 位带区细分解析
1.2.1 外设位带区
外设位带区地址范围为 0X40000000~0X40100000(1MB),覆盖了 W55MH32 片上外设的全部寄存器(寄存器地址范围:0X40000000~0X40029FFF)。其对应的位带别名区地址范围为 0X42000000~0X43FFFFFF(32MB),该区间恰好落在 W55MH32 保留地址空间(0X40030000~0X4FFFFFFF)内,避免了与其他外设寄存器地址的冲突。
与 51 单片机仅部分寄存器支持位操作(如 SBUF 需字节操作)不同,W55MH32 的所有片上外设寄存器均支持位带操作,这一特性显著提升了底层控制的灵活性。不过需注意:实际项目中通常不会对所有寄存器进行位操作,仅在需要高频操作 IO 口等特定场景下,才会针对性地对 IO 相关寄存器启用位操作。
1.2.2 SRAM位带区
SRAM 位带区地址范围为 0X20000000~0X200FFFFF(1MB),对应的位带别名区地址范围为 0X22000000~0X23FFFFFF(32MB)。由于 SRAM 的位操作需求较少,该区域的实际应用场景相对有限。
1.3 位带地址转换机制
位带区的每个比特位扩展为 32 位字后,仅最低有效位(LSB)有效。尽管看似空间利用率不高,但这种设计与 W55MH32 的 32 位系统总线架构匹配,能确保以 4 字节为单位的访问效率最大化。
1.3.1 地址转换公式
通过指针访问位带别名区地址,即可实现对基础位带区比特位的操作。具体转换公式如下:
- 外设位带别名区地址:设基础位带区中某比特所在字节地址为A,位序号为n(0≤n≤31,实际范围由寄存器位宽决定),则其在别名区的地址为:
AliasAddr= =0x42000000+ (A-0x40000000)*8*4 +n*4
公式解析:
-
- 0x42000000:外设位带别名区起始地址
- (A - 0x40000000):目标比特前的字节数
- ×8×4:单字节 8 位,每位扩展为 4 字节
- n×4:目标比特在字节内的偏移(每位占 4 字节)
- SRAM 位带别名区地址:设基础位带区中某比特所在字节地址为A,位序号为n,则其在别名区的地址为:
AliasAddr= =0x22000000+ (A-0x20000000)*8*4 +n*4
公式解析:公式逻辑与外设位带区一致,仅起始地址(0x22000000)和基础位带区起始地址(0x20000000)不同。
1.3.2 统一宏定义
为简化操作,可将两类地址转换合并为统一宏定义:
// 把“位带地址+位序号”转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))
- addr & 0xF0000000:提取地址高位(0x40000000 或 0x20000000),用于区分外设 / SRAM 区域
- +0x02000000:将高位转换为别名区起始地址(0x42000000 或 0x22000000)
- (addr & 0x00FFFFFF) << 5:等效于(A - 基础区起始地址)×8×4(屏蔽高位后左移 5 位 =×32=×8×4)
- bitnum << 2:等效于n×4(左移 2 位 =×4)
配合以下宏可实现位带操作:
// 把一个地址转换成一个指针
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
// 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
2 GPIO位带操作实践
2.1 GPIO 寄存器映射
外设位带区覆盖了所有片上外设寄存器,理论上可通过宏为每个寄存器位定义别名地址。但实际开发中,通常仅针对高频操作的 GPIO 寄存器(如输出数据寄存器 ODR、输入数据寄存器 IDR)启用位操作。
根据手册,GPIO 的 ODR 和 IDR 寄存器相对于 GPIO 基址的偏移分别为 12 和 8(GPIOx_BASE由库函数定义)。具体寄存器地址映射如下:
代码清单:位带操作-1 GPIO ODR 和 IDR 寄存器映射
// GPIO ODR 和 IDR 寄存器地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
注:不同单片机型号的 GPIO 端口数量可能不同(如 64pin 型号最多仅支持到 C 端口),实际使用时需根据具体型号调整。
2.2 GPIO位操作宏定义
通过位带别名区地址,可直接操作指定 GPIO 端口的单个 IO 口。以下为 GPIO 输入 / 输出位操作的宏定义:
代码清单:位带操作-2 GPIO 输入输出位操作
// 单独操作 GPIO的某一个IO口,n(0,1,2...15),n表示具体是哪一个IO口
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
2.3 主函数示例
以下为通过位操作控制 GPIO 的主函数示例:
代码清单:位带操作-3 main 函数
int main(void)
{
// 程序来到main函数之前,启动文件:statup_w55mh32_hd.s已经调用
// SystemInit()函数把系统时钟初始化成72MHZ
// SystemInit()在system_w55mh32.c中定义
// 如果用户想修改系统时钟,可自行编写程序修改
LED_GPIO_Config();
while ( 1 ) {
PDout(14)= 0;
SOFT_Delay(0x0FFFFF);
}
}

306

被折叠的 条评论
为什么被折叠?



