第七章 GPIO——位带操作

第七章 GPIO——位带操作

目录

第七章 GPIO——位带操作

1 位带操作基础

1.1 位带技术概述

1.2 位带区细分解析

1.2.1 外设位带区

1.2.2 SRAM位带区

1.3 位带地址转换机制

1.3.1 地址转换公式

1.3.2 统一宏定义

2 GPIO位带操作实践

2.1 GPIO 寄存器映射

2.2 GPIO位操作宏定义

2.3 主函数示例


本章参考资料:《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);

    }
}

在STM32上使用C语言通过固件库实现GPIO输出点亮LED灯,可按以下步骤操作: ### 1. 开启GPIO时钟 使用`RCC_AHB1PeriphClockCmd`函数开启对应GPIO端口的时钟。例如,若使用GPIOF端口,代码如下: ```c RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); ``` ### 2. 定义GPIO初始化结构体 定义一个`GPIO_InitTypeDef`类型的结构体变量,用于配置GPIO的各项参数。 ```c GPIO_InitTypeDef GPIO_InitStruct; ``` ### 3. 配置GPIO初始化结构体的成员 设置结构体的各个成员,包括引脚、模式、速度和上拉/下拉等。例如,要配置GPIOF的6、7、8引脚为输出模式: ```c GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_Speed = GPIO_Low_Speed; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; ``` ### 4. 调用初始化函数 使用`GPIO_Init`函数将配置好的结构体参数写入到CRL寄存器。 ```c GPIO_Init(GPIOF, &GPIO_InitStruct); ``` ### 5. 控制LED亮灭 通过`GPIO_SetBits`和`GPIO_ResetBits`函数控制GPIO引脚输出高电平或低电平,从而控制LED的亮灭。 ```c // 灯灭 GPIO_SetBits(GPIOF, GPIO_Pin_6); // 灯亮 GPIO_ResetBits(GPIOF, GPIO_Pin_6); ``` 以下是一个完整的示例代码: ```c #include "stm32fxxx.h" // 根据实际芯片型号选择合适的头文件 void LED_GPIO_Config(void) { /* 第一步:开GPIO的时钟 */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); /* 第二步:定义GPIO初始化的结构体 */ GPIO_InitTypeDef GPIO_InitStruct; /* 第三步:配置GPIO初始化结构体的成员 */ GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_Speed = GPIO_Low_Speed; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; /* 第四步:调用初始化函数 */ GPIO_Init(GPIOF, &GPIO_InitStruct); } void Delay(uint32_t count ) { for( ;count!=0;count--); } int main(void) { // 来到这里的时候,系统的时钟已经被配置成72M LED_GPIO_Config(); while(1) { // 灯灭 GPIO_SetBits(GPIOF, GPIO_Pin_6); Delay(0xFFFFF); // 灯亮 GPIO_ResetBits(GPIOF, GPIO_Pin_6); Delay(0xFFFFF); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值