C语言的使用技巧--在IO操作中的移位和快速配置

在WB32F103(ARM cortex m3内核,96Mhz)的gpio初始化中有一段代码,充分的结合了硬件特征并使用C语言的技巧来快速的配置对应的GPIO的功能,堪称经典和楷模,代码异常简洁,执行速度快,配置任意IO方便快捷。
我们先来看看这一段源代码:

void GPIO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, uint32_t PinConfig)
{uint32_t tmp = PinConfig;/* Check the parameters */assert_param(IS_GPIO_ALL_PERIPH(GPIOx));assert_param(IS_GPIO_PIN(GPIO_Pin));GPIOx->CFGMSK = ~GPIO_Pin;GPIOx->MODER = ((tmp >> 28) & 0x3) * 0x55555555U;GPIOx->OTYPER = ((tmp >> 24) & 0x1) * 0xFFFFFFFFU;GPIOx->OSPEEDR = ((tmp >> 20) & 0x3) * 0x55555555U;GPIOx->PUPDR = ((tmp >> 16) & 0x3) * 0x55555555U;tmp = (tmp & 0xF) * 0x11111111U;GPIOx->AFRL = tmp;GPIOx->AFRH = tmp;
}

相应的调用方式如下:

GPIO_Init(GPIOB, GPIO_Pin_8|GPIO_Pin_9, GPIO_MODE_IN | GPIO_PUPD_UP|GPIO_SPEED_LOW);

一条简单的函数调用代码,就可以完成对同一组IO的多个相同功能IO的同时配置,代码不可谓不经典和优雅,高效易读易懂。
但是很多人在阅读GPIO_Init函数的源代码的时候,对里面的一些运算操作确感觉到懵懵懂懂,理解不了,搞不明白。下面我们结合该硬件的特征讲解和说明一下(注意:不同的MCU的硬件寄存器会有不同的操作方式,需要紧密结合你使用的MCU的硬件寄存器的特征来理解),可以起到举一反三和触类旁通的作用:
要完成这个“一步到位”的操作,首先要对相关的宏进行合理的有技巧的定义,看看相关IO操作的宏定义如下:

/** @defgroup GPIO_MODE_define* @{*/
#define GPIO_MODE_IN          0x00000000U     /*!< Input mode */
#define GPIO_MODE_OUT         0x10000000U     /*!< Output mode */
#define GPIO_MODE_AF          0x20000000U     /*!< Alternate function mode */
#define GPIO_MODE_ANA         0x30000000U     /*!< Analog mode */
/*** @}*//** @defgroup GPIO_OTYPE_define* @{*/
#define GPIO_OTYPE_PP         0x00000000U     /*!< Output push-pull */
#define GPIO_OTYPE_OD         0x01000000U     /*!< Output open-drain */
/*** @}*//** @defgroup GPIO_SPEED_define* @{*/
#define GPIO_SPEED_LOW        0x00100000U     /*!< Low speed */
#define GPIO_SPEED_HIGH       0x00000000U     /*!< High speed */
/*** @}*//** @defgroup GPIO_PUPD_define* @{*/
#define GPIO_PUPD_NOPULL      0x00000000U     /*!< No pull resistor */
#define GPIO_PUPD_UP          0x00010000U     /*!< Pull up resistor enabled */
#define GPIO_PUPD_DOWN        0x00020000U     /*!< Pull down resistor enabled */
/*** @}*//** @defgroup GPIO_Pin_sources * @{*/
#define GPIO_PinSource0            ((uint8_t)0x00)
#define GPIO_PinSource1            ((uint8_t)0x01)
#define GPIO_PinSource2            ((uint8_t)0x02)
#define GPIO_PinSource3            ((uint8_t)0x03)
#define GPIO_PinSource4            ((uint8_t)0x04)
#define GPIO_PinSource5            ((uint8_t)0x05)
#define GPIO_PinSource6            ((uint8_t)0x06)
#define GPIO_PinSource7            ((uint8_t)0x07)
#define GPIO_PinSource8            ((uint8_t)0x08)
#define GPIO_PinSource9            ((uint8_t)0x09)
#define GPIO_PinSource10           ((uint8_t)0x0A)
#define GPIO_PinSource11           ((uint8_t)0x0B)
#define GPIO_PinSource12           ((uint8_t)0x0C)
#define GPIO_PinSource13           ((uint8_t)0x0D)
#define GPIO_PinSource14           ((uint8_t)0x0E)
#define GPIO_PinSource15           ((uint8_t)0x0F)
/*** @}*/

宏里面分别定义了GPIO的pin引脚序号(GPIO_PinSource0-15),输入输入模式配置(输入,输出,特殊功能,模拟),输出类型(推挽,开漏),IO速度(高速,低速),输入电阻配置(无上下拉,上拉,下拉)。他们的定义bit位置是经过精心的安排和计算的(比如不同的功能定义占用的bit位置不重叠,方便进行移位运算,和对应的寄存器的操作有一一的对应关系),以便于后续代码设计和简化代码的操作。好了,准备好这些原材料后,我们具体看看代码的实现过程:

  GPIOx->CFGMSK = ~GPIO_Pin;

这第一行代码,非常关键,要明白它的作用,要对应的查看mcu的规格书,我们发现该mcu有一个操作gpio配置寄存器的特殊功能,其说明如下:
在这里插入图片描述
以上说明再翻译一下:这一行代码的作用,就是允许后续写其他IO配置寄存器的时候,只对本次要配置的gpio的对应bit进行写操作,不影响无需配置的其他bit(后面代码解释再说明)。
对应的寄存器定义如下:
在这里插入图片描述好了,接下来看看第二行代码的作用和操作技巧:

  GPIOx->MODER = ((tmp >> 28) & 0x3) * 0x55555555U;

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

该代码操作的对象是端口模式寄存器,对应的寄存器功能如上图(用一个32bit的寄存器来表示16个io的模式配置,每一个io的模式配置位占2bit,并且按照顺序排列).

#define GPIO_MODE_IN          0x00000000U     /*!< Input mode */
#define GPIO_MODE_OUT         0x10000000U     /*!< Output mode */
#define GPIO_MODE_AF          0x20000000U     /*!< Alternate function mode */
#define GPIO_MODE_ANA         0x30000000U     /*!< Analog mode */

结合前面模式定义的宏来理解:
(tmp >> 28) & 0x3-— 该代码的作用就是取到传入的模式配置数据(模式配置定义在最高4个bit,所以先右移位28bit,然后与3,取出来其值),其结果可能的数据为:0,1,2,3,刚好和寄存器的2个bit的4种组合对应:
00 --对应输入模式
01–对应输出模式
10–复用功能模式
11–模拟模式
比较让人疑惑或者难以理解就是后续这个乘以0x55555555U,要理解这个作用,我们把代码换一种写法来看看:

GPIOx->MODER = 0x55555555U  * ((tmp >> 28) & 0x3);

0x55555555U用二进制来看看是什么样子:0101 0101 0101 0101 0101 0101 0101 0101
我们结合寄存器的每两个bit表示一个gpio的模式配置来看看,也就是对应于每一个gpio的配置位初始值为01,如果把这个01和前面的模式值(((tmp >> 28) & 0x3))进行运算,我们发现得到如下结果:
0101 0101 0101 0101 0101 0101 0101 0101: 和0相乘,结果为0000 0000 0000 0000 0000 0000 0000 0000 ,所有GPIO的配置bit为输入模式(00)
0101 0101 0101 0101 0101 0101 0101 0101: 和1相乘,结果不变,所有GPIO的配置bit为输出模式(01)
0101 0101 0101 0101 0101 0101 0101 0101: 和2相乘,相当于左移1位,结果为:1010 1010 1010 1010 1010 1010 1010 1010 所有GPIO的配置bit为复用功能模式(10)
0101 0101 0101 0101 0101 0101 0101 0101: 和3相乘,结果为:1111 1111 1111 1111 1111 1111 1111 1111 ,所有GPIO的配置bit为模拟模式(11)
这真是一个非常高效和简洁,优雅的设计技巧(包括硬件和软件)。
关键点来了,这个值的修改是对所有16个gpio进行同时操作的,如果我只是设置某一个gpio,会不会影响到其他gpio的配置呢?答案是肯定不会。
回到前面我们看看有一个关键寄存器GPIOx->CFGMSK,英文全称应该是config mask,中文翻译为配置辅助寄存器,直译为配置屏蔽寄存器可能更容易理解一些。
GPIOx->CFGMSK = ~GPIO_Pin;通过前面这一条代码的操作,屏蔽了不需要操作的gpio配置位(也就是说关闭了对无关gpio的bit写的作用),比如你本次只是操作gpio0,这条代码就会把对gpio1-15的操作屏蔽,以后写其他配置寄存器(比如前面的MODER寄存器),就只有gpio0对应的bit起作用,其他bit不会影响原来的值。
接下来的其他几条语句的作用类似,参考规格书就可以分析和看明白。
这再一次说明了一个道理,嵌入式开发,软件和硬件要充分结合,才能设计高效的代码。
根据硬件特征,设计合理和简洁的操作代码,也算是一种算法–一种针对硬件进行操作优化的算法。

文章为原创,欢迎转载,请注明出处

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/39275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python pycparser(c文件解析)模块使用教程

文章目录 安装 pycparser 模块模块开发者网址获取抽象语法树1. 需要导入的模块2. 获取 不关注预处理相关 c语言文件的抽象语法树ast3. 获取 预处理后的c语言文件的抽象语法树ast 语法树组成1. 数据类型定义 Typedef2. 类型声明 TypeDecl3. 标识符类型 IdentifierType4. 变量声明…

语聚AI公测发布,大语言模型时代下新的生产力工具

语聚AI 公测发布 距离语聚AI内测上线已经过去近1个月。 这期间&#xff0c;我们共邀请了近百位资深用户与行业专家加入语聚AI产品体验。通过大家的热情参与积极反馈&#xff0c;我们不断优化并完善了语聚AI的功能与使用体验。 经过研发团队不懈的努力&#xff0c;今天语聚AI终…

梅赛德斯-奔驰将成为首家集成ChatGPT的汽车制造商

ChatGPT的受欢迎程度毋庸置疑。OpenAI这个基于人工智能的工具&#xff0c;每天能够吸引无数用户使用&#xff0c;已成为当下很受欢迎的技术热点。因此&#xff0c;有许多公司都在想方设法利用ChatGPT来提高产品吸引力&#xff0c;卖点以及性能。在汽车领域&#xff0c;梅赛德斯…

代码随想录算法训练营第59天|动态规划part16|583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结篇

代码随想录算法训练营第59天&#xff5c;动态规划part16&#xff5c;583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结篇 583. 两个字符串的删除操作 583. 两个字符串的删除操作 思路&#xff1a; 思路见代码 代码&#xff1a; python class Solution(object):de…

2023年国赛数学建模思路 - 案例:FPTree-频繁模式树算法

文章目录 算法介绍FP树表示法构建FP树实现代码 建模资料 ## 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模式树算法&#xff0c…

QT-Mysql数据库图形化接口

QT sql mysqloper.h qsqlrelationaltablemodelview.h /************************************************************************* 接口描述&#xff1a;Mysql数据库图形化接口 拟制&#xff1a; 接口版本&#xff1a;V1.0 时间&#xff1a;20230727 说明&#xff1a;支…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)九:自定义组件封装下

一、本章内容 续上一张,本章实现一些自定义组件的封装,包括文件上传组件封装、级联选择组件封装、富文本组件封装等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 基于VUE3+Layui从头搭建通用后台管

mov转mp4格式怎么转?

mov转mp4格式怎么转&#xff1f;众所周知&#xff0c;MOV视频格式是由苹果公司推出的常用的视频格式&#xff0c;能够在苹果软件及设备上使用。但是&#xff0c;如果将其应用于其他软件和设备上的话&#xff0c;可能会遇到文件无法正常播放的情况。在这个时候&#xff0c;我们需…

Linux MQTT智能家居项目(LED界面的布局设置)

文章目录 前言一、LED界面布局准备工作二、LED界面布局三、逻辑实现总结 前言 上篇文章我们完成了主界面的布局设置那么这篇文章我们就来完成各个界面的布局设置吧。 一、LED界面布局准备工作 首先添加LED灯光控制的图标。 将选择好的LED图标添加进来&#xff1a; 图标可以…

drawio导出矢量图

1.选中要导出的图 2.导出为pdf 3.用adobe打开pdf&#xff0c;另存为eps

华为认证含金量如何

华为认证是指通过华为技术有限公司官方认证考试所获得的认证资格。华为认证主要分为三个级别&#xff1a;华为认证工程师&#xff08;HCIE&#xff09;、华为认证专家&#xff08;HCNP&#xff09;和华为认证技术专家&#xff08;HCNA&#xff09;&#xff0c;每个级别都有不同…

你真的了解数据结构与算法吗?

数据结构与算法&#xff0c;是理论和实践必须紧密结合的一门学科&#xff0c;有关数据结构和算法同类的课程或书籍&#xff0c;有些只是名为“数据结构”&#xff0c;而非“数据结构与算法”&#xff0c;它们在内容上并无很大区别。 实际上&#xff0c;数据结构和算法&#xf…

深入源码分析kubernetes informer机制(零)简单了解informer

[阅读指南] 基于kubernetes 1.27 stage版本 为了方便阅读&#xff0c;后续所有代码均省略了错误处理及与关注逻辑无关的部分。 文章目录 关于client-goInformer是什么为什么需要informerInformer工作流程后续分析计划 关于client-go client-go是kubernetes节点与服务端进行资源…

揭秘热门工作秘籍:ChatGPT大显身手!轻松提升工作效率的高效Prompt技巧曝光!

目录 01 背景 福利&#xff1a;文末有chat-gpt纯分享&#xff0c;无魔法&#xff0c;无限制 02 AI 可以帮助程序员做什么&#xff1f; 2.1 技术知识总结 2.2 拆解任务 2.3 阅读代码/优化代码 2.4 代码生成 2.5 生成单测 2.6 更多 AI 应用/插件 AIPRM Voice Control for Ch…

2023企业微信0day漏洞复现以及处理意见

2023企业微信0day漏洞复现以及处理意见 一、 漏洞概述二、 影响版本三、 漏洞复现小龙POC检测脚本: 四、 整改意见 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#x…

论文阅读:《Waymo Public Road Safety Performance Data》

文章目录 1 背景2 方法2.1 数据来源2.2 碰撞数据 3 碰撞事件分析4 讨论 1 背景 这篇文章是讲waymo道路安全性能数据分析的&#xff0c;主要想表达的是waymo自动驾驶系统在安全上面的出色表现&#xff0c;以向政府、大众提高自己产品的公信力。 这篇文章分析的数据是自从2019年到…

MBR400100CT-ASEMI肖特基模块MBR400100CT

编辑&#xff1a;ll MBR400100CT-ASEMI肖特基模块MBR400100CT 型号&#xff1a;MBR400100CT 品牌&#xff1a;ASEMI 封装&#xff1a;M2 正向电流&#xff1a;400A 反向电压&#xff1a;100V 引线数量&#xff1a;2 芯片个数&#xff1a;2 芯片尺寸&#xff1a;102MIL…

OpenCV-Python中的图像处理-霍夫变换

OpenCV-Python中的图像处理-霍夫变换 霍夫变换霍夫直线变换霍夫圆环变换 霍夫变换 霍夫(Hough)变换在检测各种形状的技术中非常流行&#xff0c;如果要检测的形状可以用数学表达式描述&#xff0c;就可以是使用霍夫变换检测它。即使要检测的形状存在一点破坏或者扭曲也是可以使…

(7)(7.1) 使用航点和事件规划任务

文章目录 前言 7.1.1 设置Home位置 7.1.2 视频&#xff1a;制作并保存多路点任务 7.1.3 视频&#xff1a;加载已保存的多航点任务 7.1.4 使用说明 7.1.5 提示 7.1.6 自动网格 7.1.7 任务指令 7.1.8 任务结束 7.1.9 任务重置 7.1.10 MIS_OPTIONS 7.1.11 任务再出发 …

04 - 分离头指针情况、理解HEAD和branch

查看所有文章链接&#xff1a;&#xff08;更新中&#xff09;GIT常用场景- 目录 文章目录 1. 分离头指针2. HEAD和branch2.1 branch的一些操作2.2 HEAD 1. 分离头指针 分离头指针detached HEAD是一种HEAD指针指向了某一个具体的 commit id&#xff0c;而不是分支的情况。 切换…