单片机
返回首页

STM32位带操作实现GPIO输入输出

2021-12-14 来源:eefocus

前言

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


提示:以下是本篇文章正文内容


一、示意图

在这里插入图片描述

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


二、位带区

1.外设位带区

外设外带区的地址为:0X40000000~0X40100000,大小为 1MB,这 1MB 的大小在 103系列大/中/小容量型号的单片机中包含了片上外设的全部寄存器,这些寄存器的地址为:0X40000000~0X40029FFF 。


外设位带区地址为: AliasAddr= =0x42000000+ (A-0x40000000)*8*4 +n*4

0X42000000 是外设位带别名区的起始地址,0x40000000 是外设位带区的起始地址,(A-0x40000000)表示该比特前面有多少个字节,一个字节有 8 位,所以8,一个位膨胀后是 4 个字节,所以4,n 表示该比特在 A 地址的序号,因为一个位经过膨胀后是四个字节,所以也*4。


2.SRAM 位带区

SRAM 的位带区的地址为:0X2000 0000~X2010 0000,大小为 1MB,经过膨胀后的位带别名区地址为:0X2200 0000~0X23FF FFFF,大小为 32MB。

SRAM 位带区地址为: AliasAddr= =0x22000000+ (A-0x20000000)*8*4 +n*4,分析同上。


3.统一公式

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


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

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


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

实际应用:


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

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

3

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

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


三、GPIO 位带操作

1.GPIO 寄存器映射

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

2 #define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C

3 #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C

4 #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C

5 #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C

6 #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C

7 #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C

8 #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C

9

10 #define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808

11 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08

12 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008

13 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408

14 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808

15 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08

16 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08


现在我们就可以用位操作的方法来控制 GPIO 的输入和输出了,其中宏参数 n 表示具体是哪一个 IO 口。


2. 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) //输入


3. 主函数

该工程我们直接从 LED-库函数 操作移植过来,有关 LED GPIO 初始化和软件延时等函数我们直接用,修改的是控制 GPIO 输出的部分改成了位操作。


main 函数

1 int main(void)

2 {

3 // 程序来到 main 函数之前,启动文件:statup_stm32f10x_hd.s 已经调用

4 // SystemInit()函数把系统时钟初始化成 72MHZ

5 // SystemInit()在 system_stm32f10x.c 中定义

6 // 如果用户想修改系统时钟,可自行编写程序修改

7

8 LED_GPIO_Config();

9

10 while ( 1 ) {

11 // PB0 = 0,点亮 LED

12 PBout(0)= 0;

13 SOFT_Delay(0x0FFFFF);

14

15 // PB1 = 1,熄灭 LED

16 PBout(0)= 1;

17 SOFT_Delay(0x0FFFFF);

18 }

19 }


该实验我们让 IO 口输出高低电平来控制 LED 的亮灭,负逻辑点亮。


四、下载验证

把编译好的程序下载到开发板并复位,按下按键可以控制 LED 灯亮、灭状态。

在这里插入图片描述

在这里插入图片描述

五、收获

1.初学者写一部分代码,编译一部分, 不要等到写完一起编译。

2.位带操作可以实现操作寄存器的某一位不改变其他位。

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • PIC单片机控制的遥控防盗报警器电路

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 用NE555制作定时器

  • 如何构建一个触摸传感器电路

  • 基于ICL296的大电流开关稳压器电源电路

  • 基于TDA2003的简单低功耗汽车立体声放大器电路

    相关电子头条文章