当前位置: 首页 > news >正文

STM32Fxx位带操作还不会?哲学三问让你实现位带自由(含位带操作核心代码)以LED与键盘为例

 本文将从以下几点进行讲解:1.什么是位带操作?2.位带操作的实际意义。3.如何实现位带操作。分别对应哲学三问,是什么?为什么?怎么做?

目录

什么是位带操作

什么是位带区

什么是位带别名区

位带操作的实际意义

如何实现位带操作

整体思路

核心代码(以LED与键盘为例)

寄存器封装代码

LED与键盘初始化代码


什么是位带操作

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

什么是位带区

 我们可以看到图中有两个位带区,分别是SRAM区里的0X20000000-0X200FFFFF地址段和片内外设区里的0X40000000-0X400FFFFF地址段(图中标号①处),它们的地址空间大小都是1M字节,在SRAM和外设地址段内的这1M大小的空间就是位带区,说白了就是支持位带操作的区域就是位带区。我们上面已经说过了,内核的最小寻址单位是字节,那么怎么将寻址单位缩小到bit?要弄明白这个问题,那就要先弄明白什么是位带别名区了。

什么是位带别名区

从位带别名区名字上理解,感觉它像是别人的替身,实际使用上它就是别人的替身(电影里出名的武打明星都有自己的替身,STM32这么出名,肯定也有自己的替身啦,危险的操作都让替身去做,自己躺着就行了),位带别名区就是为位带区服务的,对位带别名区的操作最终都会反映在位带区上,我们操作位带别名区的时候就等价于在操作位带区地址,那么位带别名区与位带区又是什么样的关联关系呢?

从上面映射图上可以看到,SRAM区里的0X22000000-0X23FFFFFF地址段和外设区里0X42000000-0X43FFFFFF地址段都是位带别名区,两个别名区空间大小都是32M。那么,这32M的位带别名区地址空间是怎么与1M的位带区地址空间对应起来的呢?其实,工程师们想出了一个很好的办法,地址映射,将1M字节里面的每一个bit映射到32M字节里面去,那么怎么映射呢?看到这里可能有些小伙伴就亿脸懵逼了,懵逼的话可以看一下下面的演算。

各种单位运算关系

1字节 = 8bit

1字 = 4字节

如果对这些单位没有什么概念,可以看下图

将1bit映射到1个字空间里(如下图)

映射前的一个字节 = 映射后的8个字

那么就有

映射前的一个字节 = 映射后的32个字节

映射前的1M字节 = 映射后的32M字节

好奇的小伙伴可能就要问了,为什么要将1bit空间要映射到一个字空间里去呢?我映射到1字节或者2字节的地址空间不行吗?我只能说,STM32是一个32位的机器,内核按字寻址的话寻址速度是最快的,所以别问这么多为什么,如果问了,答案就是为了速度。

位带操作的实际意义

学过51的话,理解起来就简单了,我们都知道使用51时,我们可以直接读写gpio的某一位,像这样就属于位操作,如下:

sbit led0 = P3 ^ 0;
sbit led1 = P3 ^ 1;
led0 = 1;
led1 = 0;

这样操作起来就非常方便,但是STM32没有这样的关键字,只能通过访问位带别名区的位带操作来实现。也就是上面介绍的两个概念,这样一来,STM32的全部寄存器都可以通过访问位带别名区的方式来达到访问原始寄存器位的效果,这比51单片机强大很多。因为51单片机里面并不是所有的寄存器都可以进行位操作,有些寄存器还是得用字节操作,比如SBUF。还有一个就是上面说的,STM32安字寻址速度很快。

如何实现位带操作

整体思路

1.对GPIO的IDR和ODR寄存器位操作进行封装

2.编写对应模块的驱动程序

        2.1.编写头文件:宏定义连接模块的端口、端口引脚、端口时钟、引脚位带,函数声明

        2.2.编写驱动文件

3.主函数使用

核心代码(以LED与键盘为例)

寄存器封装代码

//system.c文件
#include "system.h"		//头文件中把GPIO的IDR和ODR寄存器位操作进行了封装



//system.h文件
#ifndef __system_H__
#define __system_H__
 
#include "stm32f10x.h"
 
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    
 
#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 
 
#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 
 
#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 
 
#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 
 
#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入
 
#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入
 
#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入
 
#endif

LED与键盘初始化代码

void LED0_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	//使能GPIOB/能GPIOE/AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO, ENABLE);
	//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
	//RCC->APB2ENR|=1<<0;					//使能AFIO 时钟
	
	WRITE_REG(AFIO->MAPR, AFIO_MAPR_SWJ_CFG_JTAGDISABLE);	//允许SW-DP调试,禁止JYAG
	//AFIO->MAPR |= 0x02000000;				//JTAG-DP Disabled and SW-DP Enabled
	
	//配置GPIOB-->推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;						//3号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;		//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//快速
	GPIO_Init(GPIOB, &GPIO_InitStructure);							//根据GPIO_InitStructure中指定的参数初始化外设 GPIOB 寄存器
	
	//配置GPIOE-->推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;		//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//快速
	GPIO_Init(GPIOE, &GPIO_InitStructure);							//根据GPIO_InitStructure中指定的参数初始化外设 GPIOB 寄存器
	
	GPIO_ResetBits(GPIOE, GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_3);										//LED_SEL=1
//	GPIOB->ODR |= (1<<3);
//	PBout(3)=1;
	
//	GPIOE->ODR |= 1;
//	PEout(1) = 1;
}

如果有帮助的话,欢迎点赞收藏哦~🤩,有不同见解或更好的观点也可以在评论区留言,也可以笔者点点关注,互通有无,互相进步。

相关文章:

  • 大厂笔试面试总汇目录
  • ESP32 LVGL8.1 M5 Core2 + LVGL + IDF 详细的移植教程 (30)
  • 【论文阅读】Search-Based Testing Approach for Deep Reinforcement Learning Agents
  • c++版模板匹配与特征金字塔结构
  • 软件工程和Maven
  • 基于haarlike特征提取和Adaboost 的红绿灯/人行道检测识别matlab仿真
  • React的高阶组件详解
  • Python基础快速入门
  • FigDraw 22. SCI文章中绘图之核密度及山峦图 (ggridges)
  • 并联四足机器人项目开源教程(一)--- 机器人学导论的学习
  • 【CSS】笔记6-精灵图、字体图标
  • 秒懂如何使用SpringBoot+Junit4进行单元测试
  • 【C++】模板进阶 — 模板的特化
  • 11:跨域访问-Java Spring
  • 数据挖掘:降低汽油精制过程中的辛烷值损失模型(二)
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • 【comparator, comparable】小总结
  • js中的正则表达式入门
  • mysql innodb 索引使用指南
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • TCP拥塞控制
  • 从PHP迁移至Golang - 基础篇
  • 工作手记之html2canvas使用概述
  • 构建二叉树进行数值数组的去重及优化
  • 面试遇到的一些题
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • 选择阿里云数据库HBase版十大理由
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • # Java NIO(一)FileChannel
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (Forward) Music Player: From UI Proposal to Code
  • (TOJ2804)Even? Odd?
  • (第61天)多租户架构(CDB/PDB)
  • (紀錄)[ASP.NET MVC][jQuery]-2 純手工打造屬於自己的 jQuery GridView (含完整程式碼下載)...
  • (一)基于IDEA的JAVA基础10
  • (转) Android中ViewStub组件使用
  • (转载)Linux 多线程条件变量同步
  • (轉)JSON.stringify 语法实例讲解
  • .NET Core 版本不支持的问题
  • .NET 的静态构造函数是否线程安全?答案是肯定的!
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • @media screen 针对不同移动设备
  • [ vulhub漏洞复现篇 ] AppWeb认证绕过漏洞(CVE-2018-8715)
  • []我的函数库
  • [3300万人的聊天室] 作为产品的上游公司该如何?
  • [52PJ] Java面向对象笔记(转自52 1510988116)
  • [Assignment] C++1
  • [Bada开发]初步入口函数介绍
  • [BZOJ] 2006: [NOI2010]超级钢琴
  • [C#]使用DlibDotNet人脸检测人脸68特征点识别人脸5特征点识别人脸对齐人脸比对FaceMesh
  • [CF407E]k-d-sequence
  • [idea]关于idea开发乱码的配置
  • [JavaWeb学习] tomcat简介、安装及项目部署