第13章 GPIO-位带操作—零死角玩转STM32-F429系列

第13章 ????GPIO—位带操作

全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn

野火视频教程优酷观看网址:http://i.youku.com/firege

?

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

13.1 位带简介

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

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

图 131 F429位带地址

13.1.1 外设位带区

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

13.1.2 SRAM位带区

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

13.1.3 位带区和位带别名区地址转换

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

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

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

对于片上外设位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:

1 AliasAddr=  =0x42000000+ (A-0x40000000)*8*4 +n*4

			

0X42000000是外设位带别名区的起始地址,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.????统一公式

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

 1 // 把"位带地址+位序号"转换成别名地址的宏
				
 2 #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,这两个我们在上面分析过。

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

 1 // 把一个地址转换成一个指针
			
 2 #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
				
 3
			
 4 // 把位带别名区地址转换成指针
			
 5 #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

13.2 GPIO位带操作

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

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

1.????GPIO 寄存器映射

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

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

 2 #define GPIOA_ODR_Addr    (GPIOA_BASE+20)
				
 3 #define GPIOB_ODR_Addr    (GPIOB_BASE+20)
				
 4 #define GPIOC_ODR_Addr    (GPIOC_BASE+20)
				
 5 #define GPIOD_ODR_Addr    (GPIOD_BASE+20)
				
 6 #define GPIOE_ODR_Addr    (GPIOE_BASE+20)
				
 7 #define GPIOF_ODR_Addr    (GPIOF_BASE+20)
				
 8 #define GPIOG_ODR_Addr    (GPIOG_BASE+20)
				
 9 #define GPIOH_ODR_Addr    (GPIOH_BASE+20)
				
10 #define GPIOI_ODR_Addr    (GPIOI_BASE+20)
				
11 #define GPIOJ_ODR_Addr    (GPIOJ_BASE+20)
				
12 #define GPIOK_ODR_Addr    (GPIOK_BASE+20)
				
13
			
14 #define GPIOA_IDR_Addr    (GPIOA_BASE+16)
				
15 #define GPIOB_IDR_Addr    (GPIOB_BASE+16)
				
16 #define GPIOC_IDR_Addr    (GPIOC_BASE+16)
				
17 #define GPIOD_IDR_Addr    (GPIOD_BASE+16)
				
18 #define GPIOE_IDR_Addr    (GPIOE_BASE+16)
				
19 #define GPIOF_IDR_Addr    (GPIOF_BASE+16)
				
20 #define GPIOG_IDR_Addr    (GPIOG_BASE+16)
				
21 #define GPIOH_IDR_Addr    (GPIOH_BASE+16)
				
22 #define GPIOI_IDR_Addr    (GPIOI_BASE+16)
				
23 #define GPIOJ_IDR_Addr    (GPIOJ_BASE+16)
				
24 #define GPIOK_IDR_Addr    (GPIOK_BASE+16)

			

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

2.????GPIO位操作

代码 10 GPIO 输入输出位操作

 1 // 单独操作 GPIO的某一个IO口,n(0,1,2...16),n表示具体是哪一个IO口
				
 2 #define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)
									//输出

				
 3 #define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)
									//输入

				
 4
			
 5 #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)
									//输出

				
 6 #define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)
									//输入

				
 7
			
 8 #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)
									//输出

				
 9 #define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)
									//输入

				
10
			
11 #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)
									//输出

				
12 #define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)
									//输入

				
13
			
14 #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)
									//输出

				
15 #define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)
									//输入

				
16
			
17 #define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)
									//输出

				
18 #define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)
									//输入

				
19
			
20 #define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)
									//输出

				
21 #define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)
									//输入

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

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

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

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

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

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

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

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

				

?

3.????主函数

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

代码 11 main 函数

 1 int main(void)
				
 2 {
				
 3
								/* LED 端口初始化 */
				
 4     LED_GPIO_Config();
				
 5
			
 6
							while (1) {
				
 7
								// PH10 = 0,点亮LED
				
 8         PHout(10)= 0;
				
 9         SOFT_Delay(0x0FFFFF);
				
10
			
11
								// PH10 = 1,熄灭LED
				
12         PHout(10)= 1;
				
13         SOFT_Delay(0x0FFFFF);
				
14     }
				
15
			
16 }

			

13.3 每课一问

1、利用位带操作的方法,写一个GPIO输入的例程,比如按键采集。

2、如果使用的不是GPIO这个外设,而是其他的外设,那么公式该怎么改,比如要使用的外设是ADC。

时间: 2024-10-29 19:08:06

第13章 GPIO-位带操作—零死角玩转STM32-F429系列的相关文章

第13章 GPIO—位带操作

第13章     GPIO-位带操作 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料:<STM32F4xx 中文参考手册>存储器和总线构架章节.GPIO章节,<Cortex?-M4内核编程手册>2.2.5 Bit-banding.学习本章时,配套这些参考资料学习效果会更佳. 13.1 位带简介 位操作就是可以单独的对一个比特位读和写,这个在51单片机

第12章 GPIO输入-按键检测—零死角玩转STM32-F429系列

第12章 ????GPIO输入-按键检测 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ? 本章参考资料:<STM32F4xx参考手册>.库帮助文档<stm32f4xx_dsp_stdperiph_lib_um.chm>. 按键检测使用到GPIO外设的基本输入功能,本章中不再赘述GPIO外设的概念,如您忘记了,可重读前面"GPIO框图剖析"小

第14章 启动文件详解—零死角玩转STM32-F429系列

第14章     启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料<STM32F4xx 中文参考手册>第十章-中断和事件:表 46. STM32F42xxx 和 STM32F43xxx 的向量表:MDK中的帮助手册—ARM Development Tools:用来查询ARM的汇编指令和编译器相关的指令. 14.1 启动文件简介 启动文件由汇编编写,是

《零死角玩转STM32》-17-EXTI外部中断/事件控制器

/* * 本文中的相关图片来自火哥的教程,只作为随笔记录,无侵权之意 * 21:25:46, 2017-04-08 */ EXTI:外部中断/事件控制器.STM32F429共有23根,其中EXTI0~EXTI15,分别对应GPIO中的PX0 ~ PX15,另外7根线有如下用途: (中间省略了EXTI1 ~ EXTI15) EXTI功能框图如下所示: 按照图中的序号标识,可以清晰的看出外部GPIO引脚上的电平变化是如何传递到内核,即GPIO -> EXTI -> NVIC,并触发相应的中断/事件

《零死角玩转STM32》-18-SysTick系统滴答定时器

参考资料:<STM32F4XX内核参考手册>:4.4.8-SHPRX(System Handler Priority Register)与4.5- System Tick Timer. 1. 简介 SysTick是一个24Bit的系统定时器,属于CM4内核的外设,相关寄存器与部分库函数中core_cm4.h中定义.SysTick一般用于操作系统,用于产生时基,维持OS的心跳. 如下所示:当计数器被使能(STC_CTRL: Bit0-ENABLE)并且计数器到0时,STK_LOAD中的预设值会被

《零死角玩转STM32》中的C语言之指针

关于Pointer,一定要杜绝"野指针"!下面的代码中定义了一个指向NVIC_InitTypeDef结构体类型的指针,但是并没有用"一块内存地址"来初始化该指针,因此是一个野指针! 由于没有被初始化,该指针可能指向内存的任何地方,包括0X00000000,由此带来的后果是对该指针的一切间接引用都是非法的,很有可能修改内存中代码区/数据区的数据.一定要杜绝使用这种指针! static void EXTI_NVIC_Config(void){ NVIC_InitType

08_stm32位带操作

一:位带操作介绍 1.   位带操作 在学习 51 单片机的时候就使用过位操作, 通过关键字 sbit 对单片机 IO 口进行位定义. 但是 STM32 没有这样的关键字, 而是通过访问位带别名区来实现.即将每个比特位膨胀成一个 32 位字, 当访问这些字的时候就达到了访问比特的目的. 比方说 BSRR 寄存器有 32 个位, 那么可以映射到 32 个地址上, 当我们去访问这 32 个地址就达到访问 32 个比特的目的. STM32F1 中有两个区域支持位带操作, 一个是 SRAM 区的最低 1

STM32之GPIO端口位带操作

#ifndef __SYS_H #define __SYS_H #include "stm32f10x.h" //位带操作 //把“位带地址+位序号”转换别名地址宏 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) //把该地址转换成一个指针 #define MEM_ADDR(addr) *((volatil

Duanxx的STM32学习:GPIO的位带操作

支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写.在 CM3中,有两个区中实现了位带.其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围.这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的"位带别名区",位带别名区把每个比特膨胀成一个 32 位的字.当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的. 关于位带操作的博客说明有很多,这里主要将代码贴出来,并做详细的注释 /** ************