STM32—位带操作

发布者:Serendipity66最新更新时间:2021-09-01 来源: eefocus关键字:STM32  位带操作  32位 手机看文章 扫描二维码
随时随地手机看文章

STM32中的位带操作: 名字为位带操作,实际上是对位的操作,位操作就是可以单独的对一个比特位读和写,这个在 51 单片机中非常常见。 51 单片机中通过关键字 sbit 来实现位定义, STM32 没有这样的关键字,而是通过访问位带别名区来实现。STM32 的全部寄存器都可以通过访问位带别名区的方式来达到访问原始寄存器比特位的效果,这比 51 单片机强大很多。因为 51 单片机里面并不是所有的寄存器都是可以比特位操作,有些寄存器还是得字节操作,比如 SBUF。


51单片机中的位操作:

51单片机中可以对寄存器实现单个位的操作,靠的就是关键字sbit,如

sbit led=P1^0; led=1;就可实现对P1.0位置1的效果。


为什么STM32不推崇直接进行位操作?

本人认为STM32是32位MCU,一次处理32位数据,所以一次只处理一位的数据未必大材小用了,除非特殊情况,否则都以32位处理。


如何处理STM32中要对某一位进行操作时的情况?

要知道STM32中采用库函数编程,所以有很多的对位操作的任务都用具体的函数来完成,而这些函数都已经做好了我们只需要知道怎么用就行。但我们仍然可以自己实现位操作,这种神操作就是位带操作 。


位带区与位带区别名:

在 STM32 中,有两个地方实现了位带,一个是 SRAM 区的最低 1MB 空间,令一个是外设区最低 1MB 空间。这两个 1MB 的空间除了可以像正常的 RAM 一样操作外,他们还有自己的位带别名区,位带别名区把这 1MB 的空间的每一个位膨胀成32 位 (要知道 STM32 的系统总线是 32 位的,按照 4 个字节访问的时候是最快的,所以膨胀成 4 个字节来访问是最高效的。),当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。


位带区就是就是可以进行位带操作的寄存器的映射地址。


位带区别名可以理解为将位带区每一个位都膨胀32倍(用一个字节代表一个位,以便于32位MCU操作)后的地址。


如何实现位带操作?

要进行位带操作需要知道被操作的位的地址,因为SRAM和外设中都可以位带操作,所以形式上可以将位带操作归纳为俩个公式。


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


外设:AliasAddr= =0x42000000+ (A-0x40000000)*8*4 +n*4

SRAM:AliasAddr= =0x22000000+ (A-0x20000000)*8*4 +n*4


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

当然,也可以将俩个公式合二为一:


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

AliasAdd = (addr & 0xF0000000)+0x02000000+((addr &0x00FFFFFF)<<5)+(bitnum<<2)


知晓了位带区别名的地址,然后将此地址转换为指针类型就可以通过位带操作对原始的为进行操作。

附上野火的位带操作代码:


#include "stm32f10x.h"

#include "./led/bsp_led.h"


#define PCout(n) (*(unsigned int*)(((GPIOC_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOC_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))

#define PBout(n) (*(unsigned int*)(((GPIOB_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOB_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))


void delay(uint32_t count)

{

for(; count!=0; count--);

}


int main(void)

{

LED_GPIO_Config();  //LED初始化函数

while(1)

{

PCout(2) = 1;

//GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);

delay(0xfffff);

PCout(2) = 0;

//GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);

delay(0xfffff);

PCout(3) = 1;

//GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);

delay(0xfffff);

PCout(3) = 0;

//GPIO_ResetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);

delay(0xfffff);

}


代码简述:用宏定义的方法来操作GPIOC中的ODR寄存器和IDR寄存器中的某一位来实现led灯的亮灭


#define PCout(n) (*(unsigned int*)(((GPIOC_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOC_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))


关键字:STM32  位带操作  32位 引用地址:STM32—位带操作

上一篇:利用ST-LINK配合ST-LINK Utility 将bin文件下载到STM32的FLASH中
下一篇:STM32—重定向printf和getchar函数到串口

推荐阅读最新更新时间:2024-11-18 20:57

Keil MDK STM32系列(七) STM32F4基于HAL的PWM和定时器
配置 PWM 输出 选择芯片 System Core - SYS- Debug: Serial Wire 防止下次无法烧录 System Core - RCC- High Speed Clock (HSE): Crystal/Ceramic Resonator 启用外接高速晶振 Clock Configuration: (配置为最高84MHz)选择外部晶振, 把HSE和PLLCLK连上, 在HCLK上输入84回车, 软件会自动调节各节点倍数 Timers - TIM2 Clock Source: Internel Clock, 使用系统的时钟源 Channelx: PWM Generation CHx PWM输出 Counter
[单片机]
关于STM32 通用定时器初始化参数的理解
定时器初始化结构体定义为 typedef struct { uint16_t TIM_Prescaler; uint16_t TIM_CounterMode; uint16_t TIM_Period; uint16_t TIM_ClockDivision; uint8_t TIM_RepetitionCounter; } TIM_TimeBaseInitTypeDef; 其中: TIM_Prescaler为预分频因子,取值范围为0x0000 到 0xFFFF,决定了定时器时钟频率。定时器时钟频率 = 定时器时钟源频率 /
[单片机]
关于<font color='red'>STM32</font> 通用定时器初始化参数的理解
STM32的PWM输入模式设置并用DMA接收数据
环境: 主机:WIN7 开发环境:MDK4.72 MCU:STM32F103 说明: 项目中需要进行红外学习,如果采用输入捕获的方式,因为定时器只能捕获上升沿或者下降沿,所以只能获得周期,而不能得到具体的红外波的高低电平的时间.所以采用PWM输入的方式进行捕获. 采用的是PA8脚,对应TIM1的通道1. 源代码: /********************************************************************* * 函数 ***********************************************************
[单片机]
STM32学习之外部中断
之前看了网上有关外部中断的使用介绍,觉得很简单,现在想想有那种想法真是浮躁,不要做浮躁的嵌入式工程师,要脚踏实地。今天把外部中断实验做了一下,不做不知道,一作就是没做出来,网上参考别人的程序和有关资料还是没做出来,最后到群里问了问,被一个叫STM32的网友发现了我的问题,改过来之后程序按照自己的想法执行。 下面介绍一下今天的使用总结: STM32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组位一个单位的,同组间的外部中断同一时间只能使用一个。比如说,PA0,PB0,PC0,PD0,PE0,PF0,PG0这些为1组,如果我们使用PA0作为外部中断源,那么别的就不能够再使用了,在此情况下,我们智能使用类似于PB1
[单片机]
stm32 ssD1306 OLED驱动架构
#include oled.h #include stdlib.h #include oledfont.h #include delay.h //OLED的显存 //存放格式如下. // 0 1 2 3 ... 127 // 0 1 2 3 ... 127 // 0 1 2 3 ... 127 // 0 1 2 3 ... 127 // 0 1 2 3 ... 127 // 0 1 2 3 ... 127 // 0 1 2 3 ... 127 // 0 1 2 3 ... 127 u8 OLED_GRAM ; //更新显存到LCD void OLED_Re
[单片机]
STM32使用systick实现精确延时
SYSTICK寄存器初始化 void SysTick_Configuration(void) { if (SysTick_Config(SystemCoreClock / 100)) { while (1); } NVIC_SetPriority(SysTick_IRQn, 0x0); } SysTick_Config默认时钟为SysTick_CLKSource_HCLK,所以在这之前使用SysTick_CLKSourceConfig()选择系统时钟不会改变systick的时钟 static __INLINE uint32_t SysTick_Config(uin
[单片机]
STM32学习之串口
第一步:把串口用的引脚设置。接收的为 GPIO_Mode_IN_FLOATING; //浮空输入 发送的为GPIO_Mode_AF_PP; // 复用推挽输出 第二部设置 void NVIC_Configuration(void) NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel ; // 全局中断、、 NVIC_InitStructure.NVIC_IRQChannelPreemptio
[单片机]
STM32 ucos 下添加CAN模块
1.CAN IO RX TX 的设定和重映射 GPIO_PinRemapConfig(GPIO_Remap2_CAN, ENABLE); //端口重映射到PD0,PD1 2.clock CAN IO CLOCK 的开启 、*-------gpio for can------------*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE); CAN 自身CLOCK 的开启、 /* CAN Periph clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN
[单片机]
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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