C语言对寄存器的封装

2023-05-16

目录

1、封装总线和外设基地址

2、封装寄存器列表

3、修改寄存器的位操作的方法

把变量的某位清零

把变量的某几个连续位清零

对变量的某几位进行赋值。

对变量的某位取反


1、封装总线和外设基地址

在编程上为了方便理解和记忆,我们把总线基地址和外设基地址都以相应的宏定义起来,总线或者外设都以他们的名字作为宏名;具体代码见代码清单:

/*外设基地址*/
#define PERIPH_BASE    ((unsigned int)0x40000000)

/*总线基地址*/
#define APB1PERIPH_BASE    PERIPH_BASE
#define APB2PERIPH_BASE    (PERIPH_BASE + 0x00010000)
#define AHBPERIPH_BASE     (PERIPH_BASE + 0x00020000)

/*GPIO外设基地址*/
#define GPIOA_BASE         (APB2PERIPH_BASE + 0x0800)  
#define GPIOB_BASE         (APB2PERIPH_BASE + 0x0C00)  
#define GPIOC_BASE         (APB2PERIPH_BASE + 0x1000)  
#define GPIOD_BASE         (APB2PERIPH_BASE + 0x1400)  
#define GPIOE_BASE         (APB2PERIPH_BASE + 0x1800)  
#define GPIOF_BASE         (APB2PERIPH_BASE + 0x1C00)  
#define GPIOG_BASE         (APB2PERIPH_BASE + 0x2000)  

/*寄存器基地址,以GPIOB为例*/
#define GPIOB_CRL        (GPIOB_BASE + 0x00)
#define GPIOB_CRH        (GPIOB_BASE + 0x04)
#define GPIOB_IDR        (GPIOB_BASE + 0x08)
#define GPIOB_ODR        (GPIOB_BASE + 0x0C)
#define GPIOB_BSRR       (GPIOB_BASE + 0x10)
#define GPIOB_BRR        (GPIOB_BASE + 0x14)
#define GPIOB_LCKR       (GPIOB_BASE + 0x18)

首先定义了”片上外设“基地址PERIPH_BASE,接着在PERIPH_BASE上加入各个总线的地址偏移,得到APB1、APB2总线的基地址APB1PERIPH_BASE、APB2PERIPH_BASE,在其之上加入外设地址的偏移,得到GPIOA-G的外设地址,最后在外设地址上加入各寄存器的地址偏移,得到的特定寄存器的地址。一旦有了具体地址,就可以用指针读写。

/*控制GPIOB引脚0输出低电平(BSRR寄存器的BR0置位1)*/
*(unsigned int*)GPIOB_BRR = (0x01<<(16+0));

/*控制GPIOB引脚0输出高电平(BSRR寄存器的BS0置位1)*/
*(unsigned int*)GPIOB_BSRR = (0x01<<0);

unsigned int temp;
/*读取GPIOB端口所有引脚的电平(读IDR寄存器)*/
temp = *(unsigned int*)GPIOB_IDR;

该代码使用(unsigned int*)把GPIOB_BSRR宏的数值强制转换成了地址,然后再用”*“号做取指针操作,对该地址的赋值,从而实现了写寄存器的功能。同样,读寄存器也是用取指针操作,把寄存器中的数据取到变量里,从而获取STM32外设的状态。

2、封装寄存器列表

用上面的方法去定义地址,还是稍显繁琐,例如GPIOA-GPIOE都各有一组功能相同的寄存器,如GPIOA_ODR/GPIOB_ODR/GPIOC_ODR等等,它们只是地址不一样,但却要为每个寄存器都定义它的地址。为了方便的访问寄存器,我们引入C语言中的结构体语法对寄存器进行疯传,具体见代码。

typedef unsigned int   uint32_t       /*无符号32位变量*/ 
typedef unsigned short uint16_t       /*无符号16位变量*/

/*GPIO寄存器列表*/
typedef struct{
    uint32_t CRL;    /*GPIO端口配置低寄存器      地址偏移0x00*/
    uint32_t CRH;    /*GPIO端口配置高寄存器      地址偏移0x04*/
    uint32_t IDR;    /*GPIO数据输入寄存器        地址偏移0x08*/
    uint32_t ODR;    /*GPIO数据输出寄存器        地址偏移0x0C*/
    uint32_t BSRR;   /*GPIO位设置/清除寄存器     地址偏移0x10*/
    uint32_t BRR;    /*GPIO位清除寄存器          地址偏移0x14*/
    uint32_t LCKR;   /*GPIO端口配置锁定寄存器     地址偏移0x18*/

}GPIO_Typedef;

这段代码用typedef关键字声明了名为GPIO_Typedef的结构体类型,结构体有7个成员变量,变量名正好对应寄存器的名字。C语言的语法规定,结构体内变量的存储空间是连续的,其中32位的变量占用4个字节,16位的变量占用2个字节,具体见下图:

 这样的地址偏移与STM32 GPIO外设定义的寄存器地址偏移一一对应,只要给结构体设置好首地址,就能把结构体成员的地址确定下来,然后就能以结构体形式访问寄存器,具体见代码:

GPIO_Typedef *GPIOx;        //定义一个GPIO_Typedef型结构体指针GPIOx
GPIOx = GPIOB_BASE;         //把指针地址设置为宏GPIOB_BASE地址
GPIOx->IDR = 0xFFFF;
GPIOx->ODR = 0xFFF;

uint32_t temp;
temp = GPIOx->IDR;          //读取GPIOB_IDR寄存器的值到变量temp中

3、修改寄存器的位操作的方法

使用C语言对寄存器赋值时,我们常常要求只修改寄存器的某几位的值,且其他的寄存器位不变,这个时候我们就需要用到C语言的位操作方法了。

把变量的某位清零

此处我们以变量a代表寄存器,并假设寄存器中本来已有数值,此时我们需要把变量a的某一位清零,且其它位不变,方法见代码段:
 

                                代码:寄存器对某位清零
//定义一个变量a = 1001 1111b(二进制数)
unsigned char a = 0x9f;

//对bit2清零
a &= ~(1<<2);
//括号中的1左移两位得二进制数:0000 0100b
//按位取反。~(1<<2)得1111 1011b
//假如a中原来得值为二进制数,a = 1001 1111b
//所得得数与a作与运算,a = (1001 1111b)&(1111 1011b)
//经过运算后,a = 1001 1011b
//a的bit2位被清零,而其他位不变

把变量的某几个连续位清零

由于寄存器中有时会有连续几个寄存器位用于控制某个功能,现假设我们需要把寄存器的某几
个连续位清零,且其它位不变,方法见 代码清单 :
//若把 a 中的二进制位分成 2 个一组
2 //即 bit0、bit1 为第 0 组,bit2、bit3 为第 1 组,
3 // bit4、bit5 为第 2 组,bit6、bit7 为第 3 组 4 //要对第 1 组的 bit2、bit3 清零

a &= ~(3<<2*1);
78 //括号中的 3 左移两位,(3<<2*1) 得二进制数:0000 1100 b
9 //按位取反,~(3<<2*1) 得 1111 0011 b
10 //假如 a 中原来的值为二进制数: a = 1001 1111 b
11 //所得的数与 a 作”位与&”运算,a = (1001 1111 b)&(1111 0011 b),
12 //经过运算后,a 的值 a=1001 0011 b
13 // a 的第 1 组的 bit2、bit3 被清零,而其它位不变。
14
15 //上述 (~(3<<2*1)) 中的 (1) 即为组编号; 如清零第 3 组 bit6、bit7 此处应为 3
16 //括号中的 (2) 为每组的位数,每组有 2 个二进制位; 若分成 4 个一组,此处即为 4
17 //括号中的 (3) 是组内所有位都为 1 时的值; 若分成 4 个一组,此处即为二进制数“1111 b”
18
19 //例如对第 2 组 bit4、bit5 清零
20 a &= ~(3<<2*2);

对变量的某几位进行赋值。

寄存器位经过上面的清零操作后,接下来就可以方便地对某几位写入所需要的数值了,且其它位
不变,方法见 代码清单 : 寄存器 -11 ,这时候写入的数值一般就是需要设置寄存器的位参数。

 

//a = 1000 0011 b
2 //此时对清零后的第 2 组 bit4、bit5 设置成二进制数“01 b ” 34 a |= (1<<2*2);
5 //a = 1001 0011 b,成功设置了第 2 组的值,其它组不变

对变量的某位取反

某些情况下,我们需要对寄存器的某个位进行取反操作,即 1 0 0 1 ,这可以直接用如下
操作,其它位不变,见 代码清单
//a = 1001 0011 b
2 //把 bit6 取反,其它位不变
34 a ^=(1<<6);
5 //a = 1101 0011 b

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言对寄存器的封装 的相关文章

  • mavsdk_api

    MAVSDK中offboard一些API Value 其中是offboard需要用到的一些参数定义 offboard PositionNedYaw class mavsdk offboard PositionNedYaw north m e
  • strstr()函数的使用说明(C语言)

    头文件 lt string h gt 函数作用 xff1a 1 strstr 函数搜索一个字符串在另一个字符串中的第一次出现 2 找到所搜索的字符串 xff0c 则该函数返回第一次匹配的字符串的地址 xff1b 3 如果未找到所搜索的字符串
  • Linux硬件信息查看命令

    查看磁盘 df 查看CPU信息 cat proc cpuinfo 查看显卡 lspci grep VGA 查看OS采用核心号 lsb release cs Linux查看当前操作系统版本信息 cat proc version Linux查看
  • DockerFile的使用

    DockerFile的使用 一 DockerFile构建过程解析1 1 Dockerfile内容基础知识1 2 Docker执行Dockerfile的大致流程 二 DockerFile常用保留字指令三 DockerFile案例3 1 先准备
  • myeclipse2019 4.0配置tomcat9的两个问题

    myeclipse2019 4 0配置tomcat9的两个问题 问题一 xff1a myeclipse配置tomcat端口无效 图中提示Tomcat的一个或多个端口无效 xff08 并非占用 xff0c 端口占用看问题二 xff09 出现这
  • MySQL-约束(三)

    MySQL 约束 约束的定义常见的约束类型表级约束与列级约束例子非空约束 NOT NULL 唯一约束 UNIQUE 联合唯一约束 默认约束 DEFAULT 主键约束 PRIMARY KEY 复合主键约束复合主键与联合主键 外键约束 FORE
  • 双系统之kali

    双系统之kali 所用工具制作kali的引导盘硬盘分区安装kali设置启动引导设置root密码U盘恢复 下面文章多为图片形式 xff0c 每个图片中包含详细的步骤 所用工具 16G的U盘一个 xff0c 电脑一台 所有的软件均已上传至百度网
  • 连接GitHub提示远程主机关闭连接

    连接GitHub提示远程主机关闭连接 发现问题排查问题反思 错误代码 xff1a kex exchange identification Connection closed by remote host 发现问题 今天晚上使用git连接了G
  • Linux - Nginx安装

    Linux Nginx安装 下载 amp 配置启动优化nginx添加新模块 本文由于是学习 xff0c 所以使用tar包安装 xff0c Linux版本CentOS7 xff0c nginx 1 20 2 xff0c 本次安装路径为 usr
  • 异常记录-3

    异常记录 3 异常类名描述问题原因解决方案异常重现 异常类名 org springframework beans BeanInstantiationException 描述 Failed to instantiate javax servl
  • 异常记录-22

    异常记录 22 异常描述问题原因解决方案异常重现 异常 ssl SSLEOFError EOF occurred in violation of protocol ssl c 997 描述 urllib3 exceptions MaxRet
  • Linux - RabbitMQ部署

    Linux RabbitMQ部署 准备部署账户创建使用 准备 span class token function cat span etc redhat release 查看自己的服务器什么版本的 xff0c 本人使用的CentOS8 安装
  • Linux - MongoDB部署

    MongoDB部署 准备部署mongoDB部署 启动优化角色 参考 准备 MongoDB下载 官方下载 选择自己需要的版本 xff0c 以及服务器版本 xff0c 以及安装方式 xff0c 本篇文章我们安装6 0MongoDB使用CentO
  • 异常记录-23

    异常记录 23 异常描述问题原因解决方案异常重现 异常 64 WebFilter 描述 Java 过滤器 64 WebFilter不起作用 问题原因 64 WebFilter需要注册到Bean中 xff0c 只写注解不起作用的 解决方案 在
  • Vscode黑色主题很难看到鼠标

    Vscode黑色主题很难看到鼠标 xff0c 像瞎了一样 xff0c 找半天不知道鼠标在哪里 xff0c 解决办法 xff1a 控制面板 鼠标 指针 看 自定义 选择 文本选择 点击 浏览 选择 beam r xff0c 这个是光标周围带白
  • python中变量前面加单下划线和双下划线的区别&python的私有属性

    一 在py文件中 变量名字前面加单下划线和双下划线都是对变量的保护 xff0c 一种约定 xff0c 用来指定变量私有 程序员用来指定私有变量的一种方式 不能用from module import 导入 xff0c 其他方面和公有一样访问
  • APM、PIXHAWK、PX4的关系

随机推荐

  • QT小记:QT程序异常结束的可能原因

    一 问题 xff1a 程序异常结束 二 解决 1 解决思路 xff08 1 xff09 QT程序异常结束的原因之一可能引用了某个库 xff0c 但是某个库QT找不到 xff0c 就会出现崩溃的问题 解决 xff1a 1 检查pro文件是否正
  • C语言:十进制转换成十六进制字符串和数组

    1 十进制转换成十六进制字符串 include lt stdio h gt 函数 xff1a 将十进制数字转换为十六进制 xff0c 并将转换后的数字存储在字符串中并输出 void dec2hex int n char str 100 in
  • 【统计计算】课程总结笔记

    传统的主成分分析法 xff08 PCA xff09 缺陷在于 xff1a 对于 小样本问题 xff08 样本维数d远大于样本个数N xff09 xff0c 样本协方差矩阵规模 d d 太大了 xff0c 在后续求特征值问题中计算量较大 于是
  • ros --- 双目相机内参与外参标定

    ros 双目相机内参与外参标定 小觅相机直接获取参数手动重新标定1 双目相机内外参标定生成标定板录制 stereo calibra bag标定标定结果标定验证 2 双目 43 imu外参标定录制 stereo 43 imu calibra
  • RT-Thread之入门跑代码

    本文将讲述如何在window10中利用tensorflow跑代码 xff0c 并且编译成bin文件 xff0c 最后在k210中运行 一 在window上安装tensorflow框架 xff08 python3 7 xff09 1 安装An
  • keil5的基本使用

    项目文件后缀 硬件下载 1 编译 2 下载 软件仿真 一 基本知识 1 选择软件仿真选项 2 编译 3 启动软件仿真 4 运行代码 二 软件仿真的具体使用 1 我们想运行到 哪一步就在那一步添加断点 xff08 在左边的空白处左键点一下就可
  • pytorch之池化层

    实际图像里 xff0c 我们感兴趣的物体不会总出现在固定位置 xff1a 即使我们连续拍摄同一个物体也极有可能出现像素位置上的偏移 这会导致同一个边缘对应的输出可能出现在卷积输出 Y中的不同位置 xff0c 进而对后面的模式识别造成不便 在
  • 串口通信协议

    通信协议的基本概念 用于定义通信过程及细节规则的协议称为通信协议 xff0c 通信系统之间为了完成通信所必须遵循的规则和约定 xff08 数据包格式 字段的内容 字段的含义 发送的时间 接收的时间等细节 xff09 个人理解 xff1a 就
  • gazebo的安装

    安装ros 1 1设置软件源 xff1a 清华源或者阿里都可 sudo sh c 39 etc lsb release amp amp echo 34 deb Index of ros ubuntu 清华大学开源软件镜像站 Tsinghua
  • 驱动开发基础

    1 Hello驱动 我们应用程序使用open函数的时候 xff0c 会调用内核的sys open函数 xff0c 然后接下来 1 然后打开普通文件的话会使用文件系统操作硬件 xff0c 2 要是打开驱动文件 xff0c 会使用驱动程序对应的
  • ARM架构与编程 · 基于IMX6ULL

    一 嵌入式系统硬件介绍 cpu 43 RAM xff08 内存 xff09 43 FALSH 集成 xff08 flash存储设备 xff09 61 MCU 单片机 AP MPU 进化之后可以外接内存和存储设备 跑复杂的操作系统 xff0c
  • 嵌入式常用算法

    1 冒泡排序 1 两两之间对比 xff0c 要是顺序排 xff0c 一轮过后最大的就是最后一个 2 下一轮参加排序的数比上一轮少一个 include lt iostream gt using namespace std void paixu
  • 实习面试的总结

    2023 4 3 阿凡达机器人 驱动开发实习生 1 怎么注册一个字符设备 注销 1 注册一个设备号 2 设备号加载进内核 3 创建类 4 创建设备 注销 1 从内核中删除 2 删除设备 3 删除类 2 怎么将新加入的网络设备加入到内核中去
  • 字符设备结构体与probe函数

    1 设备结构体 设备结构体 struct ap3216c dev dev t devid 设备号 主设备号 43 次设备号 struct cdev cdev cdev 字符设备对象 xff0c 字符设备驱动的一种结构体类型 struct c
  • SLAM --- VIO 基于 EKF 开源

    1 VIO based on EKF 已知一致性的Visual Inertial EKF SLAM 实现添加链接描述
  • 暗夜精灵7 linux

    Ubuntu18 04 安装nvidia显卡驱动 distro non free 小乌坞的博客 CSDN博客 注意在关闭显示界面的时候需要输入密码 xff0c 不然会一直卡着 在验证是否屏蔽驱动的时候 xff0c 要先重启一下 cuda L
  • linux应用编程

    项目内容 开发板内部使用c语言调用硬件驱动实现各种测试功能 xff0c 保存测试结果 外部程序通过socket接口使用tcp协议与开发板通信进行信息传输 xff0c 最后使用python GUI构造一个界面按照测试顺序逐步显示出各个模块的测
  • NUC10快乐装机

    NUC10装机 由于为了RoboMaster比赛 xff0c 身为全队唯一一个视觉队员兼队长的我 xff0c 经过疫情期间的再三斟酌 xff0c 最后决定工控机选择为nuc10 为什么选择nuc10 作为第一年参赛的新队伍 xff0c 视觉
  • 什么是PID?讲个故事,通俗易懂

    什么是PID xff1f PID xff0c 就是 比例 xff08 proportional xff09 积分 xff08 integral xff09 微分 xff08 derivative xff09 xff0c 是一种很常见的控制算
  • C语言对寄存器的封装

    目录 1 封装总线和外设基地址 2 封装寄存器列表 3 修改寄存器的位操作的方法 把变量的某位清零 把变量的某几个连续位清零 对变量的某几位进行赋值 对变量的某位取反 1 封装总线和外设基地址 在编程上为了方便理解和记忆 xff0c 我们把