再造STM32---第十一部分:GPIO—位带操作

发布者:雷电狂舞最新更新时间:2019-06-28 来源: eefocus关键字:STM32  GPIO  位带操作 手机看文章 扫描二维码
随时随地手机看文章

       本章参考资料:《STM32F4xx 中文参考手册》存储器和总线构架章节、 GPIO 章节,《Cortex®-M4 内核编程手册》 2.2.5 Bit-banding。学习本章时,配套这些参考资料学习效果会更佳。


11.1 位带简介:

       位操作就是可以单独的对一个比特位读和写,这个在 51 单片机中非常常见。 51 单片机中通过关键字 sbit 来实现位定义, F429 中没有这样的关键字,而是通过访问位带别名区来实现。

       在 F429 中,有两个地方实现了位带,一个是 SRAM 区的最低 1MB 空间,另一个是外设区最低 1MB 空间。这两个 1MB 的空间除了可以像正常的 RAM 一样操作外,他们还有自己的位带别名区,位带别名区把这 1MB 的空间的每一个位膨胀成一个 32 位的字,当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。



11.1.1 外设位带区:

      外设位带区的地址为: 0X40000000~0X400F0000,大小为 1MB,这 1MB 的大小包含了 APB1/2 和 AHB1 上所以外设的寄存器, AHB2/3 总线上的寄存器没有包括。 AHB2 总线上的外设地址范围为: 0X50000000~0X50060BFF, AHB3 总线上的外设地址范围为:0XA0000000~0XA0000FFF。外设位带区经过膨胀后的位带别名区地址为:0X42000000~0X43FFFFFF,这部分地址空间为保留地址,没有跟任何的外设地址重合。


11.1.2 SRAM 位带区:

      SRAM 的位带区的地址为: 0X2000 0000~X200F 0000,大小为 1MB,经过膨胀后的位带别名区地址为: 0X2200 0000~0X23FF FFFF,大小为 32MB。操作 SRAM 的比特位这个用得很少。


11.1.3 位带区和位带别名区地址转换:

      位带区的一个比特位经过膨胀之后,虽然变大到 4 个字节,但是还是 LSB 才有效。有人会问这不是浪费空间吗,要知道 F429 的系统总线是 32 位的,按照 4 个字节访问的时候是最快的,所以膨胀成 4 个字节来访问是最高效的。


      我们可以通过指针的形式访问位带别名区地址从而达到操作位带区比特位的效果。那这两个地址直接如何转换,我们简单介绍一下。

1. 外设位带别名区地址:

      对于片上外设位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:1 AliasAddr= =0x42000000+ (A-0x40000000)*8*4 +n*40X42000000 是外设位带别名区的起始地址, 0x40000000 是外设位带区的起始地址,(A-0x40000000)表示该比特前面有多少个字节,一个字节有 8 位,所以*8,一个位膨胀后是 4 个字节,所以*4, n 表示该比特在 A 地址的序号,因为一个位经过膨胀后是四个字节,所以也*4。

2. SRAM 位带别名区地址:

      对于 SRAM 位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:1 AliasAddr= =0x22000000+ (A-0x20000000)*8*4 +n*4公式分析同上。

3. 统一公式:

      为了方便操作,我们可以把这两个公式合并成一个公式,把“位带地址+位序号”转换成别名区地址统一成一个宏。


// 把“位带地址+位序号”转换成别名地址的宏

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr &0x000FFFFF)<<5)+(bitnum<<2))

      addr & 0xF0000000 是为了区别 SRAM 还是外设,实际效果就是取出 4 或者 2,如果是外设,则取出的是 4, +0X02000000 之后就等于 0X42000000, 0X42000000 是外设别名区的起始地址。如果是 SRAM,则取出的是 2, +0X02000000 之后就等于 0X22000000,0X22000000 是 SRAM 别名区的起始地址。

      addr & 0x00FFFFFF 屏蔽了高三位,相当于减去 0X20000000 或者 0X40000000,但是为什么是屏蔽高三位?因为外设的最高地址是: 0X2010 0000,跟起始地址 0X20000000 相减的时候,总是低 5 位才有效,所以干脆就把高三位屏蔽掉来达到减去起始地址的效果,具体屏蔽掉多少位跟最高地址有关。 SRAM 同理分析即可。 <<5 相当于*8*4, <<2 相当于*4,这两个我们在上面分析过。

      最后我们就可以通过指针的形式操作这些位带别名区地址,最终实现位带区的比特位操作。


// 把一个地址转换成一个指针

#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))

// 把位带别名区地址转换成指针

#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))

11.2 GPIO 位带操作:

      外设的位带区,覆盖了全部的片上外设的寄存器,我们可以通过宏为每个寄存器的位都定义一个位带别名地址,从而实现位操作。但这个在实际项目中不是很现实,也很少人会这么做,我们在这里仅仅演示下 GPIO 中 ODR 和 IDR 这两个寄存器的位操作。

      从手册中我们可以知道 ODR 和 IDR 这两个寄存器对应 GPIO 基址的偏移是 20 和 16,我们先实现这两个寄存器的地址映射, 其中 GPIOx_BASE 在库函数里面有定义。

1. GPIO 寄存器映射:

代码 9 GPIO ODR 和 IDR 寄存器映射


// GPIO ODR 和 IDR 寄存器地址映射

#define GPIOA_ODR_Addr (GPIOA_BASE+20)

#define GPIOB_ODR_Addr (GPIOB_BASE+20)

#define GPIOC_ODR_Addr (GPIOC_BASE+20)

#define GPIOD_ODR_Addr (GPIOD_BASE+20)

#define GPIOE_ODR_Addr (GPIOE_BASE+20)

#define GPIOF_ODR_Addr (GPIOF_BASE+20)

#define GPIOG_ODR_Addr (GPIOG_BASE+20)

#define GPIOH_ODR_Addr (GPIOH_BASE+20)

#define GPIOI_ODR_Addr (GPIOI_BASE+20)

#define GPIOJ_ODR_Addr (GPIOJ_BASE+20)

#define GPIOK_ODR_Addr (GPIOK_BASE+20)

#define GPIOA_IDR_Addr (GPIOA_BASE+16)

#define GPIOB_IDR_Addr (GPIOB_BASE+16)

#define GPIOC_IDR_Addr (GPIOC_BASE+16)

#define GPIOD_IDR_Addr (GPIOD_BASE+16)

#define GPIOE_IDR_Addr (GPIOE_BASE+16)

#define GPIOF_IDR_Addr (GPIOF_BASE+16)

#define GPIOG_IDR_Addr (GPIOG_BASE+16)

#define GPIOH_IDR_Addr (GPIOH_BASE+16)

#define GPIOI_IDR_Addr (GPIOI_BASE+16)

#define GPIOJ_IDR_Addr (GPIOJ_BASE+16)

#define GPIOK_IDR_Addr (GPIOK_BASE+16)

       现在我们就可以用位操作的方法来控制 GPIO 的输入和输出了,其中宏参数 n 表示具体是哪一个 IO 口, n(0,1,2...16)。这里面包含了端口 A~K ,并不是每个单片机型号都有这么多端口,使用这部分代码时,要查看你的单片机型号,如果是 176pin 的则最多只能使用到 I 端口。

2. GPIO 位操作:

代码 10 GPIO 输入输出位操作


// 单独操作 GPIO 的某一个 IO 口, n(0,1,2...16),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) //输入

#define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //输出

#define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //输入

#define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //输出

#define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //输入

#define PJout(n) BIT_ADDR(GPIOJ_ODR_Addr,n) //输出

#define PJin(n) BIT_ADDR(GPIOJ_IDR_Addr,n) //输入

#define PKout(n) BIT_ADDR(GPIOK_ODR_Addr,n) //输出

#define PKin(n) BIT_ADDR(GPIOK_IDR_Addr,n) //输入

3. 主函数:

        该工程我们直接从 LED-库函数 操作移植过来,有关 LED GPIO 初始化和软件延时等函数我们直接用,修改的是控制 GPIO 输出的部分改成了位操作。该实验我们让相应的 IO口输出高低电平来控制 LED 的亮灭,负逻辑点亮。具体使用哪一个 IO 和点亮方式由硬件平台决定。

代码 11 main 函数


int main(void)

{

/* LED 端口初始化 */

LED_GPIO_Config();

while (1) {

// PH10 = 0,点亮 LED

PHout(10)= 0;

SOFT_Delay(0x0FFFFF);

// PH10 = 1,熄灭 LED

PHout(10)= 1;

SOFT_Delay(0x0FFFFF);

}

}



关键字:STM32  GPIO  位带操作 引用地址:再造STM32---第十一部分:GPIO—位带操作

上一篇:再造STM32---第十二部分:启动文件详解
下一篇:再造STM32---第十部分:GPIO输入—按键检测

推荐阅读最新更新时间:2024-11-07 11:58

基于STM32F100VBT6的32MCU开发设计方案
STM32F100VBT6采用ARM Cortex™-M3 32位RISC内核,工作频率24MHz,集成了高速嵌入式存储器(闪存高达128kB、SRAM高达8kB)以及各种增强外设和连接到两条APB总线的I/O。所有器件提供两个I2C、两个SPI、一个HDMI CEC和多达3个USART标致通信接口以及一个12位ADC、两个12位DAC和六个通用16位定时器和PWM定时器。主要用在控制和用户接口、医疗设备、PC和游戏机外设、GPS平台、工业应用、PLC、逆变器、打印机、视频通信和HVAC等。 图1 STM32F100xx系列方框图 STM32F100xx简介 低/中密度、基于ARM的高级32位MCU,带有16 kB ~128
[单片机]
stm32 stm8 I2C相关总结
一、I2C协议简介 I2C是两线式串行总线,用于连接微控制器及其外围设备。两根信号线分别是: 时钟信号线SCL和数据信号线SDA。 二、I2C总线传输时序 2.1 I2C传输协议的三种信号 I2C在数据传输过程中有三种信号类型,分别是:起始信号、结束信号和应答信号。 ①起始信号:在时钟信号SCL为高电平时,数据线SDA由高电平跳变为低电平,开始传输数据; ②结束信号:在时钟信号SCL为高电平时,数据线SDA由低电平跳变为高电平,数据传输结束; ③应答信号:接收数据的IC在接收8位(一个字节)数据后,向发送数据的IC发出特定的低电平信号,表示已经收到数据。准确的说法是:发送设备在时钟信号SCL的8个脉冲的驱动下发送了8个bit,即一
[单片机]
<font color='red'>stm32</font> stm8 I2C相关总结
stm32 通过串口进行信息交流 并控制led亮灭
h 关闭黄色 l 关闭蓝色 g 关闭绿色 a 打开全部 int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */
[单片机]
STM32学习笔记-点亮第一个led
点亮第一个led灯之前要先大概了解一下单片机的工作方式并且有一定的编程基础,能看懂程序 单片机上有很多引脚,被称作io口,io口是通过总线(信号传输的通路)与内部处理系统(执行程序的硬件的总称)相连接,内部处理系统执行c语言程序可以直接控制io口,硬件机器只能识别机器码,但是已经有人一层一层的从底层封装,封装好了stm32 c语言库函数.因此,我们只需调用库函数即可. 点亮第一个io口的核心问题就是io口的配置(初始化) void GPIOC13(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph
[单片机]
STM32新建keil工程具体步骤(详细)
1. 新建本地工程文件夹 在本地电脑上新建一个“工程模板”文件夹,在它之下再新建 6 个文件夹: 2.添加库文件到相应文件夹 把 ST 标准库必要的文件复制到工程模版对应文件夹的目录下 3.KEIL5新建工程 打开 KEIL5,新建一个工程,文件名自拟,工程放在Project目录下。 保存后弹出芯片选择,要根据自己芯片的型号提前按照PACK包,选择型号后点击OK。 随后弹出在线添加库文件,关闭即可。 4.添加组文件夹 在新建的工程中添加常用的文件夹,用来存放不同的文件。 5.添加文件 在新建的工程中添加这些文件,双击组文件夹就会出现添加文件的路径,然后选择文件即可。 6.配置魔术棒选
[单片机]
<font color='red'>STM32</font>新建keil工程具体步骤(详细)
基于STM32和CC2520的TinyOS移植方法
TinyOS系统以其组件结构模型、事件驱动、并发型等优点成为目前最受关注的无线传感器网络操作系统。但TinyOS不支持STM32和CC2 520芯片。因此在分析TinyOS基本原理、NesC编程语言实现机制及其编译过程的基础上,介绍了基于STM32和CC2520的TinyOS移植方法,完成了STM32的I/O组件、Timer组件、USART组件、SPI组件和CC2520芯片驱动的移植。在实现CC2520的基本通信功能基础上,实现简单MAC协议。最后测试了各组件的移植效果。实验测试结果表明,节点可以稳定可靠地通信。 无线传感器网络(Wireless Sensor Network,WSN)是一种应用相关的网络。需要对某些操
[单片机]
基于<font color='red'>STM32</font>和CC2520的TinyOS移植方法
STM32之HAL库和标准库的GPIO
HAL库 使用CubeMX自动生成需要的代码。 一、初始化GPIO 自动生成的HAL库GPIO初始化代码: void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO
[单片机]
STM32串口中断的一些资料
在研究STM32串口接收发送中断的时候找到不少不错的资料,现在备份在这里。以供自己查阅,以及方便其他人。 TC ====TXE 顺便预告下最近会写个有关串口处理数据的帖子,从查询和中断方面以及数据处理的方式,从队列以及FIFO方面写起。 SECTION 1 SECTION 2 先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为 发送后中断 。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下 /* 功能:中断方式发送字符串.采用判断TC的方式.即 判断 发送后中断 位. 输入:字符串的首地址 输出:无 */ void U
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved