STM32F429--位带操作

发布者:暗里著迷最新更新时间:2022-01-12 来源: eefocus关键字:STM32F429  位带操作 手机看文章 扫描二维码
随时随地手机看文章

一、位带操作的原理

51单片机可以直接对某一位IO进行读写操作,而STM32则通过位带操作来控制一个单独的IO口。


概念


位带区:支持位带操作的地址区。

位带别名区:对别名地址的访问最终作用到位带区的访问上。位带别名区对位带区的访问有个地址映射过程。


目的

对位带区的比特位进行独立的读写操作,即单独操作一个位 ,它是通过对位带别名区的操作来实现。


具体过程:

对位带别名区进行读写访问,位带别名区通过地址映射关系映射到相应的位带区,对位带区进行原始比特的读写操作。

   地址映射

对位带别名区进行读且操作  ---------------------->对位带区进行读且操作(目的)


下面是位带操作的一些说明

在这里插入图片描述
在这里插入图片描述

二、计算公式

字节地址为A,比特位序号为n(0<=n<=7)

说明:“*4”表示一个字为4个字节,“*8”表示一个字节中有8个比特。

在这里插入图片描述

将上述的外设位带区和SRAM位带区进行统一,得到下面的公式:


((addr&0xF0000000)+0x02000000+((addr & 0x000FFFFF)<<5)+(bitnum<<2))


其中4*8=32=2的5次方,4=2的2次方,相当于左移了5位和2位

公式解析


add &0xF000 0000:目的是取出4和2,用于区分是外设还是SRAM。然后再加上0x2000000就等于外设/SRAM位带别名区的起始地址。

add &0x000FFFFF:屏蔽掉高3位。外设位带区的最高地址为0x400F0000,SRAM位带区的最高地址为0x200F 0000,(0x400F 0000 - 0X4000 0000)与(0x200F 0000-0x2000 0000)在求偏移地址相减的时候只有低五位有效,所以就把剩下的高3位屏蔽掉,剩下的5位和F做与运算即可。


三、举例

假如要操作GPIOH的ODR寄存器的10位,就可以通过位带操作来访问


//1.宏定义ADDR

#define GPIOH_ODR_ADDR          (GPIOH_BASE+0X14)


//2.求出位带别名区的地址    传进两个参数  参数一:ADDR   参数二:位数(即公式里面的n)

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


//3.把求出来的位带别名区(立即数)强制类型转换为指针,编译器才知道是地址

//即将BITBAND传入MEM_ADDR中

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


假设LED_GPIO_Config()是LED灯初始化程序

此处重点是说明位带的知识点,详情可以查看我的上一篇文章:

STM32F429--标准库点亮LED灯

https://blog.csdn.net/ABCisCOOL/article/details/106170244


此时在main函数里面直接调用即可,把

void main(void)

{

while(1)

{

  LED_GPIO_Config();

  MEM_ADDR(GPIOH_ODR_ADDR,10) == 0 ;

}

}


上述的操作是不是相对来说,可读性还比较差呢,试试下面改进的写法,是不是有点像51的编程了呢?


//1.宏定义ADDR

#define GPIOH_ODR_ADDR          (GPIOH_BASE+0X14)


//2.求出位带别名区的地址    传进两个参数  参数一:ADDR   参数二:位数(即公式里面的n)

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


//3.把求出来的位带别名区(立即数)强制类型转换为指针,编译器才知道是地址

//即将BITBAND传入MEM_ADDR中

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

//4.嵌套宏

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


//相当于操作PH的端口 如 PH10  ,是不是很接近51了呢

#define PHout(n)                        BIT_ACTION(GPIOH_ODR_ADDR,bitnum) 


void main(void)

{

while(1)

{

  LED_GPIO_Config();

  PHout(10) == 0 ;

}

}


如果我们需要操作多个引脚,可以先将其封装起来,然后利用

PAout,PBout,…,PHout等方式来对单独的位进行操作。


附:

本文还说明的还不够详细,还是不清楚的朋友,可以看一下火哥的讲解视频:


https://www.bilibili.com/video/BV1Ws411c7A8?p=23


作业

1- 如何实现GPIO端口的ODR寄存器的位操作

2- 如何实现其他GPIO口的IDR寄存器的位操作

3- 重新实现GPIO输入–按键检测部分的代码,读取IO电平的哪一部分代码,用位带操作的形式操作

提示:unit16_t temp; temp == PAint(10);

4- 把绿灯和蓝灯也点亮


参考答案:


1-

/*GPIOA~GPIOH的ODR寄存器位带操作,*/

#define GPIOA_ODR_ADDR           (GPIOA_BASE+0X14)

#define GPIOB_ODR_ADDR           (GPIOB_BASE+0X14)

#define GPIOC_ODR_ADDR           (GPIOC_BASE+0X14)

#define GPIOD_ODR_ADDR           (GPIOD_BASE+0X14)

#define GPIOF_ODR_ADDR           (GPIOE_BASE+0X14)

#define GPIOG_ODR_ADDR           (GPIOF_BASE+0X14)

#define GPIOH_ODR_ADDR           (GPIOH_BASE+0X14)


/*中间这三个不需要改动*/

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

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

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


#define PAout(n)                  BIT_ACTION(GPIOA_ODR_ADDR,bitnum) 

#define PBout(n)                  BIT_ACTION(GPIOB_ODR_ADDR,bitnum) 

#define PCout(n)                  BIT_ACTION(GPIOC_ODR_ADDR,bitnum) 

#define PDout(n)                  BIT_ACTION(GPIOD_ODR_ADDR,bitnum) 

#define PEout(n)                  BIT_ACTION(GPIOE_ODR_ADDR,bitnum) 

#define PFout(n)                  BIT_ACTION(GPIOF_ODR_ADDR,bitnum) 

#define PGout(n)                  BIT_ACTION(GPIOG_ODR_ADDR,bitnum) 

#define PHout(n)                  BIT_ACTION(GPIOH_ODR_ADDR,bitnum) 


2-

/*GPIOA~GPIOH的IDR寄存器位带操作,*/

#define GPIOA_IDR_ADDR           (GPIOA_BASE+0X10)

#define GPIOB_IDR_ADDR           (GPIOB_BASE+0X10)

#define GPIOC_IDR_ADDR           (GPIOC_BASE+0X10)

#define GPIOD_IDR_ADDR           (GPIOD_BASE+0X10)

#define GPIOE_IDR_ADDR           (GPIOE_BASE+0X10)

#define GPIOF_IDR_ADDR           (GPIOF_BASE+0X10)

#define GPIOG_IDR_ADDR           (GPIOG_BASE+0X10)

#define GPIOH_IDR_ADDR           (GPIOH_BASE+0X10)


#define PAint(n)                  BIT_ACTION(GPIOA_IDR_ADDR,bitnum) 

#define PBint(n)                  BIT_ACTION(GPIOB_IDR_ADDR,bitnum) 

#define PCint(n)                  BIT_ACTION(GPIOC_IDR_ADDR,bitnum) 

#define PDint(n)                  BIT_ACTION(GPIOD_IDR_ADDR,bitnum) 

#define PEint(n)                  BIT_ACTION(GPIOE_IDR_ADDR,bitnum) 

#define PFint(n)                  BIT_ACTION(GPIOF_IDR_ADDR,bitnum) 

#define PGint(n)                  BIT_ACTION(GPIOG_IDR_ADDR,bitnum) 

#define PHint(n)                  BIT_ACTION(GPIOH_IDR_ADDR,bitnum) 


3-按键接的是PA0端口号


uint8_t Key_Scan(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)

{

  if (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON)

{

//如果按下了,KEY_ON 1

while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);

//做相应的动作

return KEY_ON;

}

else return KEY_OFF;


}


重新实现如下

uint8_t Key_Scan(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)

{

  unit16_t temp;

  temp = PAint(0);

  if (temp== 1)

  {

    printf("有按键按下,做相应的动作");

  }


}

4-

红灯点亮的程序

#define LED_GPIO_PIN   GPIO_Pin_10

#define LED_GPIO_PORT  GPIOH

#define LED_GPIO_CLK   RCC_AHB1Periph_GPIOH


//配置函数

void LED_GPIO_Config(void)

{


GPIO_InitTypeDef GPIO_InitStruct;

//0-打开系统时钟

RCC_AHB1PeriphClockLPModeCmd(LED_GPIO_CLK,ENABLE);

//1-设置引脚

GPIO_InitStruct.GPIO_Pin   = LED_GPIO_PIN;

//2-设置为输出模式

GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_OUT;

//3-设置为推挽输出类型

GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

//4-设置为上拉

GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_UP;

//5-设置速度50MHZ

GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;

//把引脚写进寄存器的函数

GPIO_Init(LED_GPIO_PORT,&GPIO_InitStruct);

}


void  LED_R_LIGHT(void)

{

    PHout(10)==0;

}


绿灯:

#define LED_GPIO_PIN   GPIO_Pin_11


void LED_GPIO_Config(void)

{


GPIO_InitTypeDef GPIO_InitStruct;

//0-打开系统时钟

RCC_AHB1PeriphClockLPModeCmd(LED_GPIO_CLK,ENABLE);

//1-设置引脚

GPIO_InitStruct.GPIO_Pin   = LED_GPIO_PIN;

//2-设置为输出模式

GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_OUT;

//3-设置为推挽输出类型

GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

//4-设置为上拉

GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_UP;

//5-设置速度50MHZ

GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;

//把引脚写进寄存器的函数

GPIO_Init(LED_GPIO_PORT,&GPIO_InitStruct);

}

void  LED_G_LIGHT(void)

{

    PHout(11)==0;

}

蓝灯:

#define LED_GPIO_PIN   GPIO_Pin_12


void LED_GPIO_Config(void)

{


GPIO_InitTypeDef GPIO_InitStruct;

//0-打开系统时钟

RCC_AHB1PeriphClockLPModeCmd(LED_GPIO_CLK,ENABLE);

//1-设置引脚

GPIO_InitStruct.GPIO_Pin   = LED_GPIO_PIN;

//2-设置为输出模式

GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_OUT;

//3-设置为推挽输出类型

GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

//4-设置为上拉

GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_UP;

//5-设置速度50MHZ

GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;

//把引脚写进寄存器的函数

GPIO_Init(LED_GPIO_PORT,&GPIO_InitStruct);

}


void  LED_B_LIGHT(void)

{

    PHout(12)==0;

}

关键字:STM32F429  位带操作 引用地址:STM32F429--位带操作

上一篇:STM32F429--启动文件简单分析
下一篇:STM32F429--按键检测

推荐阅读最新更新时间:2024-11-16 20:43

第1章 初学STM32F429的准备工作
1.1 初学者重要提示 关于学习方法问题,可以看附件章节A。 学会 STM32F429相关资源的获取方法,做到心中有数,否则心里老是没底。 关于MDK和IAR两种编译器,推荐都掌握,以其中一个为主,另一个为辅。因为很多时候我们需要参考的工程代码不是自己熟悉的编译器,就会很被动。 这几年涌现出好几款非常棒的调试工具(如Event Recoder,SEGGER RTT),教程的后面章节会为大家做讲解。 1.2 开发环境说明 1、 IDE:支持两种IDE开发环境,MDK和IAR MDK推荐使用MDK5.26正式版及其以上版本。 IAR固定使用IAR8.3版本,由于IAR向下兼容性稍差,其它版本未做支持。 2
[单片机]
第1章 初学<font color='red'>STM32F429</font>的准备工作
STM32F429接MAX6675读取热电偶温度
第一次编写STM32程序,有很多问题请教,我想把MAX6675的SO的数据读出来转换成实际温度,用串口工具读出来。 是不是还有哪些没配置好?小弟刚开始弄这个,着急用,在此感谢了! #include sys.h #include delay.h #include usart.h #define SPI1_CS_Pin GPIO_PIN_4 void Delay(__IO uint32_t nCount); void Delay(__IO uint32_t nCount) { while(nCount--){} } int main(void) { uint16_t
[单片机]
<font color='red'>STM32F429</font>接MAX6675读取热电偶温度
擦掉STM32F429芯片上的数据的一个方法
刚入手一块STM32F429Discovery。手痒痒的,准备写个程序进去。一不小心,把MCU的调试接口SW、JTAG全部给禁用了。这下可坏了,写不进去程序,擦不掉数据。愁的某家一头大汗。突然想起了当年玩F1的时候有个IAP功能,F4应该也有着功能的。于是找到了这个: http://www.mcuisp.com/chinese mcuisp web/index.htm 没有针对F4的软件。下载F1的FlyMCU IAP软件之后,直接连还是连不上,软件是一直在等待芯片回应。 之后查了下ST的文档AN2606,STM32™ microcontroller system memory boot mode。看见这样一段换: The
[单片机]
第42章 STM32F429的LTDC应用之点阵字体和字符编码(重要)
42.1 初学者重要提示 本章节讲解的GB2312和GBK比较容易理解,而Unicode是全球统一码,所以涉及到的知识点比较多,也复杂些。 当前Windows系统字符管理已经全部采用Unicode字符集,编码形式是UTF-16 LE (LE表示小端格式)编码。比如我们通过记事本保存的时候选择编码类型Unicode,其实就是选择的UTF-16 LE。而我们平时所说的中文版Windows系统是指用户在选择不支持Unicode的程序中显示文本时所使用的语言。 区分清楚什么是字符,字符集。字符(Character)是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。字符集(Character set)是多个字符的集
[单片机]
第42章 <font color='red'>STM32F429</font>的LTDC应用之点阵字体和字符编码(重要)
STM32F429(HAL库)_USART串口发送&接收(使用STM32CubeMX)
一、HAL库相关函数 1、串口发送/接收函数 HAL_UART_Transmit();串口轮询模式发送,使用超时管理机制 HAL_UART_Receive();串口轮询模式接收,使用超时管理机制 HAL_UART_Transmit_IT();串口中断模式发送 HAL_UART_Receive_IT();串口中断模式接收 HAL_UART_Transmit_DMA();串口DMA模式发送 HAL_UART_Transmit_DMA();串口DMA模式接收 2、串口中断函数 HA
[单片机]
<font color='red'>STM32F429</font>(HAL库)_USART串口发送&接收(使用STM32CubeMX)

推荐帖子

关于单周期CPU的问题
这学期学的计算机组成与设计,关于MIPS的单周期实现,一直有一个问题没有想明白,所以过来请教一下各位高手.对于单周期的CPU来说,每条指令执行都需要一个周期,一条指令执行完再执行下一条指令。就是说,单周期CPU来说处理指令的5个阶段是串行执行的。这里的这个5个阶段串行执行怎么理解啊,怎么保证这5个阶段是串行执行的呢??也就是说,对于一个CLOCK信号(比如说上升沿触发的),在这个上升沿到来之后,CPU中的每个部分工作的顺序是怎样的呢??比如说,是不是得保证在里面的任何一MUX单元要在输入信号进
swustboy 嵌入式系统
做个无线路由器,可以发射距离是3km
大家觉得能做到这个程度吗?做个无线路由器,可以发射距离是3km有难度呵呵发生功率好像有限制的吧,不给弄那么大的啊。难,关键是,对人体有害长距离的都是点对点的。微波炉?关键是发射出去了。人家弄个感应线圈用来充电,怎么办?电费都吓傻··
gina RF/无线
A题 为什么我一接电池,ir2101驱动的NMOS管波形就乱呢???
求组A题为什么我一接电池,ir2101驱动的NMOS管波形就乱呢???楼主我以前用过电池给屏幕供电,屏就会一闪一闪,后来测试出那个电池的保护板输出是脉冲输出,我都是找的没用的电池和充电保上的保护板.电池也有阻值hanwenli123发表于2015-8-1509:48楼主我以前用过电池给屏幕供电,屏就会一闪一闪,后来测试出那个电池的保护板输出是脉冲输出,我都是找的没用... 就是要保护板么?小帆子哥发表于2015-8-1510:35电池也有阻
Markpoer 电子竞赛
【PCB知识分享】你想要的268条PCB Lauout设计规范都在这里!
你想要的268条PCBLauout设计规范都在这里!序号按部位分类技术规范内容1PCB布线与布局PCB布线与布局隔离准则:强弱电流隔离、大小电压隔离,高低频率隔离、输入输出隔离、数字模拟隔离、输入输出隔离,分界标准为相差一个数量级。隔离方法包括:空间远离、地线隔开。2PCB布线与布局晶振要尽量靠近IC,且布线要较粗3PCB布线与布局晶振外壳接地4PCB布线与布局时钟布线经连接器输出时,连
ohahaha PCB设计
单片机C语言的编程中可否使用 LONGLONG型变量
单片机C语言的编程中可否使用LONGLONG型变量,需要引入什么文件吗单片机C语言的编程中可否使用LONGLONG型变量居然没有人会? 新的数据类型:C99引入了longlong类型,用于支持更大的整数范围。只要你支持C99就可以。long类型有的是32位有的是64位。比如gcc里面就是64位,如果你用esp32用idfgcc编译long类型就是64位的具体要看你的编译器。和编译器的支持。 非常感谢。请问51单片机的C语
一沙一世 stm32/stm8
OTA测试暗箱的技术原理和应用场景
OTA测试暗箱在无线通信设备的研发与测试中扮演着至关重要的角色。以下是对OTA测试暗箱技术原理和应用场景的详细阐述:一、OTA测试暗箱的技术原理OTA测试暗箱的技术原理主要基于电磁波在封闭空间内的传播特性。测试暗箱内部经过特殊处理,以减少电磁波的反射和干扰,从而提供一个相对稳定的测试环境。测试时,被测设备(如手机、无线基站等)被置于暗箱内部,通过无线方式与其通信,以评估设备在无线传输环境中的性能表现。具体来说,OTA测试暗箱的技术原理包括以下几个方面: 电磁波屏蔽:测试暗
维立信测试仪器 测试/测量
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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