历史上的今天
返回首页

历史上的今天

今天是:2024年09月28日(星期六)

2019年09月28日 | 再造STM32---第十一部分:GPIO—位带操作

2019-09-28 来源:eefocus

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

}

}


推荐阅读

史海拾趣

台湾固锝(GD)公司的发展小趣事
通过合理设计反馈电路和保护电路,确保放大器的稳定性和可靠性。
Acopian Power Supplies公司的发展小趣事
检查所有元件和电路的连接是否正确无误。
比亚迪(BYD)公司的发展小趣事

随着国内市场的逐步稳定,比亚迪开始将目光投向海外市场。1998年,比亚迪在欧洲荷兰成立了第一家海外分公司,这是其全球化战略的重要一步。随后,比亚迪相继在香港和美国设立分公司,进一步扩大了其国际影响力。这些海外分公司的成立不仅为比亚迪带来了更多的商机,也使其能够更好地了解和学习国际先进技术和市场经验。通过全球化的布局,比亚迪逐渐在电子行业树立了良好的品牌形象。

EREM公司的发展小趣事

随着全球市场的不断融合,EREM公司开始寻求国际合作机会。通过与国外知名企业的合作,EREM不仅将产品销售到了全球各地,还学到了许多先进的管理经验和技术。同时,EREM也积极参与国际展览和交流活动,提升了品牌知名度和影响力。这些努力使得EREM在国际市场上逐渐崭露头角。

EUDYNA公司的发展小趣事

随着技术的不断进步和市场需求的日益增长,EUDYNA开始实施全球化战略。公司不仅在全球范围内建立了多个研发中心和生产基地,还积极拓展海外市场,与众多国际知名企业建立了紧密的合作关系。EUDYNA的全球化战略不仅为公司带来了更广阔的市场空间,也进一步提升了其品牌影响力和竞争力。

统宇电研(Coilmaster)公司的发展小趣事

统宇电研公司成立于1995年,初创时期面临着重重挑战。当时,电子行业正值技术革新的关键时期,无源组件市场竞争激烈。统宇电研凭借对技术的敏锐洞察力和对市场趋势的准确判断,决定专注于高性能功率电感等无源组件的研发和生产。公司团队夜以继日地攻克技术难题,不断优化产品设计,最终成功推出了一系列具有竞争力的产品,为公司赢得了市场的认可。

问答坊 | AI 解惑

用DVS功能实现系统节能

今日电子 | 作者:李晓延  便携设备的发展可以说是一日千里,各种功能不断地整合其中。单以智能手机为例,其功能就完全可以媲美笔记本电脑。但复杂的功能需要功能强大的处理器,智能手机中已集成了模拟基带、数字基带、图像处理器和CPU等多个分处 ...…

查看全部问答>

MSP430单片机与CPU卡接口函数设计

MSP430单片机与CPU卡接口函数设计…

查看全部问答>

H.264的技术亮点

三、H.264的技术亮点 1、 分层设计 H.264的算法在概念上可以分为两层:视频编码层(VCL:Video Coding Layer)负责高效的视频内容表示,网络提取层(NAL:Network Abstraction Layer)负责以网络所要求的恰当的方式对数据进行打包和传送。在VC ...…

查看全部问答>

谷歌IO对Androd的架构和运行机理分析

谷歌IO对Androd的架构和运行机理分析 Android Anatomy and Physiology By Google IO Android 架构剖析和机能分析 翻译:JeefJiang 2009 年8 月14 日 ·  Android Anatomy    Android 架构剖析 ·    Li ...…

查看全部问答>

怎样在ALTIUM DESIGEER中显示路径

在ALTIUM V6.9中,会显示文件保存的路径,以下截图为V8.3版本,请教怎么显示路径? [ 本帖最后由 lzcqust 于 2010-1-27 23:11 编辑 ]…

查看全部问答>

修复移动硬盘

我的移动硬盘不显示盘符,不显示发现新硬件,早上好好的,放在口袋里办点事就出了这毛病!希望大家看看是什么毛病,是不是数据线虚了!…

查看全部问答>

想找一份这方面的工作,不要求多少待遇

想做windows驱动开发,也想做linux驱动,但是linux不太熟悉,希望推荐一个工作机会…

查看全部问答>

isp下载线

用在s52上的下载线,自己做的 接到电脑上后,可以检测到器件 可以很顺利的完成,没有任何报错 但实际程序没写进去 不知道为什么 ??? 很奇怪…

查看全部问答>

想配个电脑

我想配个电脑。 主机价格2500以下。 今天去电脑市场问了下,说什么盘正主版,不知道好不好, 还有显卡不知道什么好, 大家给我个陪单,谢谢了啊。 最好带上具体的型号…

查看全部问答>

msp430在加入温度传感器测温后出现复位怎么回事

用的msp430f437,用到了12路ad通道测量电压,利用flash信息段存储PT变比,id号,电压计算系数等装置参数,精度校准后参数写入flash信息段保存,可以比较准的测量工频电压,但是现在我在这个基础上加入了温度补偿程序,即增加一个ad通道利用430的温 ...…

查看全部问答>