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-10 10:58

STM32 | STM32F429的USB有坑?
最近某项目需要用到USB与CAN: 拿到这样的需求,我们当然是先得保证通讯正常。于是我找了一个USB例程与一个CAN例程,分别调试验证。 经过几番折腾已经保证了USB与上位机能正常通讯了,也能保证了CAN的正常收发(拿了两块开发板做验证)。 两头都没有问题了,再加上一些数据处理就差不多完成了。USB与CAN我都是第一次用,没想到那么顺利,美滋滋,正准备放松的时候,问题就来了。这是一个整体的东西,最终都要把这两部分集合起来吧。 我把CAN工程里关于CAN的部分移到USB工程里,这时候CAN竟然用不了了。这时候我就开始在怀疑自己是不是手贱误删了哪里了,于是重新来一遍,发现还是不行。 查了代码很久也没找出什么错误了,
[单片机]
STM32 | <font color='red'>STM32F429</font>的USB有坑?
关于STM32中的(bit-band)操作说明
支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在 CM3 中,有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB范围。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。 位带操作的概念其实 30 年前就有了,那还是8051 单片机开创的先河,如今,CM3 将此能力进化,这里的位带操作是 8051 位寻址区的威力大幅加强版。 CM3 使用如下术语来表示位带存储的相关地址: 位带区:支
[单片机]
关于STM32中的<font color='red'>位</font><font color='red'>带</font>(bit-band)<font color='red'>操作</font>说明
第11章 STM32F429移植SEGGER的硬件异常分析
11.1 初学者重要提示 MDK本身也是支持硬件异常分析的,就是不够直观,此贴是MDK的硬件异常分析文档:http://www.armbbs.cn/forum.php?mod=viewthread&tid=21940 。 IAR8带的硬件异常分析比较好用,在本章11.6小节有说明。 11.2 移植方法 直接移植SEGGER的硬件异常代码会有错误警告,这里针对IAR和MDK版本做了些简单修改,方便大家移植到自己的工程里面。 MDK版本移植 源文件位于本章配套例子的UserseggerHardFaultHandlerMDK文件夹,添加如下两个文件到工程里面即可。 IAR版本移植 源文件位于本章配套例子的Userseg
[单片机]
第11章 <font color='red'>STM32F429</font>移植SEGGER的硬件异常分析
stm32F429 LTDC优化
针对,SDRAM作为Framdbuffer时的几点优化。 1.尽量增加SDRAM带宽(使能突发访问,增加突发访问的长度,增加数据总线宽度)。 2.framebuffer的行长度64字节对齐,因为LTDC突发访问长度为64字节。 3.分时复用FSMC的总线,DMA2D、CPU、LTDC、DMA等Master同时访问设备时带来总线仲裁时间,造成带宽降低。 4.多个framebuffer时放在不同的SDRAM bank里面,显示buffer所放的bank最好只由LTDC访问。 5.将CPU所使用的堆栈放在CCM,减少CPU对总线矩阵的访问。
[单片机]
STM32F429 >> 20. CAN 通讯(Code)
本工程配置CAN 为回环模式。 编程指南 1. can 基本模式配置; 2. can 筛选器配置; 3. 发送数据结构体配置并发送; 4. 接收数据结构体配置并接收; 若采用中断进行数据读取,则加入中断配置,并在中断中进行数据接收即可。 bsp_can.h /** ****************************************************************************** * @file bsp_can.h * @author Waao * @version V1.0.0 * @date 21-Feb-2019 * @brief This file co
[单片机]
第17章 STM32F429之GPIO的HAL库API
17.1 初学者重要提示 1、如何阅读HAL库源码的问题 HAL库实现的函数有复杂的,也有简单的,简单的可以直接阅读代码。复杂的代码阅读起来比较耗时间,如果再配合参考手册抠每个寄存器的配置,那就更消耗时间了。所以对于这种函数,用户仅需了解每个部分实行的功能即可,而且HAL库都做了关键注释,以说明这部分实现的功能。所以用户没有必要去抠每个配置是如何实现的,仅需知道实现了什么功能。以后工程项目有需要了解具体配置时,再看即可。 2、学习本章节前,务必保证已经学习了第15章。 17.2 GPIO涉及到的寄存器 GPIO外设涉及到的寄存器比较少,也容易理解,推荐大家阅读GPIO源码的时候将参考手册中对应的寄存器功能做一个了解。
[单片机]
STM32f429开发中USB读写文件涉及到的库移植
第一步 USB_HID移植(原创 http://blog.csdn.net/xbl1986/article/details/17577685#comments ) ├── STM32_USB_Device_Library USB从设备库 │ │ ├── Class │ │ │ └── hid │ │ │ ├── inc │ │ │ │ └── usbd_hid_core.h │ │ │ └── src │ │ │ └── usbd_hid_core.c │ │ └── Core │ │ ├── inc │ │ │ ├
[单片机]
STM32F429的USB有坑?
最近某项目需要用到USB与CAN: 拿到这样的需求,我们当然是先得保证通讯正常。于是我找了一个USB例程与一个CAN例程,分别调试验证。 经过几番折腾已经保证了USB与上位机能正常通讯了,也能保证了CAN的正常收发(拿了两块开发板做验证)。 两头都没有问题了,再加上一些数据处理就差不多完成了。USB与CAN我都是第一次用,没想到那么顺利,美滋滋,正准备放松的时候,问题就来了。这是一个整体的东西,最终都要把这两部分集合起来吧。 我把CAN工程里关于CAN的部分移到USB工程里,这时候CAN竟然用不了了。这时候我就开始在怀疑自己是不是手贱误删了哪里了,于是重新来一遍,发现还是不行。 查了代码很久也没找出什么错误了,
[单片机]
<font color='red'>STM32F429</font>的USB有坑?

推荐帖子

请教arm的普通内核与Cortex内核有何区别啊!
请教arm的普通内核与Cortex内核有何区别啊?编程上有何区别呢?请教arm的普通内核与Cortex内核有何区别啊!1)ARM的普通内核是指哪些?ARM有不下几十种内核,哪些是普通的?2)任何两个内核之间的比较,都可以写出很多篇论文。3)如果使用C编程,在编程方面,内核之间基本无差别。楼主的问题有点大了cortex也属于普通内核。呵呵,我菜鸟了,对不起!我菜鸟了!但是
f33 stm32/stm8
丝印“SAL”,5脚贴片,封装跟SOT23差不多大,是啥型号?
图示5个引脚位置,贴片的,封装跟SOT23差不多大。补充:刚才查了一下,这个封装是TSOP-5关键的是上面就丝印了“SAL”,再无其他提示。呵呵这是我在一个LED驱动器上看到的,很是好奇,不知道大家看见过没有? 丝印“SAL”,5脚贴片,封装跟SOT23差不多大,是啥型号?这种封装的升压芯片很多的凭你的经验能猜出来是哪家公司么?猜不出!刚才我查了下,TSOP-5的封装引脚应该是这样子的。按照我看到SAL的位置摆放出来,我觉得是不是引脚号就对应不上了。
Michael_Fei LED专区
哪位大神帮忙分析一下这个电路是干什么的
WE-VOUT为一个信号输入到LM321,R1,R4可以选择焊或者不焊,这个电路是用来干嘛的哪位大神帮忙分析一下这个电路是干什么的R1,R4可以选择焊或者不焊,这个电路是用来干嘛的电路是用来干嘛的,这可很难看出来。AUX怎么感觉是车上的接口什么的瞎猜的哈:hug:就是普通的运放,虚短虚断,虚短,weout=C2-P,功能就是信号条调理,用运放增强输出驱动能力焊或者不焊?是可以短路还是直接断开?几乎所有涉及运放问题,都可以用虚短,V+=V-;虚断,运放正负输入电流近似为
li6666 模拟电子
在WINCE5.0下,USB不连接,设备上也会跳出ACTIVESYNC的连接窗口,怎么解决呢
这种情况在设备启动或者设备睡眠唤醒后并且FFUART未被打开时会出现如果FFUART被打开,就不会有那个窗口弹出我们的FFUART是com1,USB是com2如果USB是插上的,开机和唤醒后可以实现activesync的自动连接貌似设备启动或唤醒后,如果com1未打开,系统会把com1当作USB的com2进行连接很郁闷的问题,有人能帮忙解决吗在WINCE5.0下,USB不连接,设备上也会跳出ACTIVESYNC的连接窗口,怎么解决呢
PWP1013 WindowsCE
IMX6开发板
有人懂这个开发板的么,想请问点问题。有懂的留个联系方式,急。IMX6开发板
hy2320020 ARM技术
接地与屏蔽技术
接地与屏蔽技术非常好的电磁兼容方面的电子资料,清晰度高,谢谢分享。感谢分享,下来看一下,这个原理没去深入的学过,对有电磁兼容问题的,现在我就是把地做的超大就解决了。。。 的确是关键,核心因数你是懂的、、、、👍接地和屏蔽技术这方面的资料还是非常少的,感谢楼主提供的资料,非常有价值,值得下载学习 学习了吸收了更有价值、、、非常好的电磁兼容方面的电子资料,很应该收藏学习非常好的电磁兼容方面的电子资料终于找到能下载的了
btty038 RF/无线
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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