STM32 笔记 _《GPIO配置从低层走向高层》

article/2025/6/24 11:09:16

目录

一.寄存器直接地址写入法

二.寄存器地址命名写入法

三.其它命名、及使用结构体 /枚举来归类 (逐步走向库函数)

三.GPIO一些参考图


一.寄存器直接地址写入法

操作IO口分三步:   1.打开相应的时钟;

                                2. 配置相应的I/O模式和频率(内部会以对应频率的驱动电路来匹配输出,输入时频率无效)

                                3. 输出或输入数据(给相应的IO寄存器写或读数据)

1.   配置时钟寄存器RCC地址  :示例  打开PC/PB口时钟

偏移地址:0x18, 

 

*(unsigned int*) 0x4002  1018 |=0x00000018 ; //0x4002 1018地址的寄存器IOPC/IOPB设置为1,其余不变。

(时钟打开的是A-E中其中一组或多组所有16个Pin脚)。

2.   配置PC13、PB0口IO模式寄存器:   

        A--E各自的基址如上上图。

        A--E各相同Pin脚的配置寄存器、包括下面的数据寄存器的 偏移地址都是一样的。

    *(unsigned int*)   0x40011004   &=~(0x0F<<20);      //先将23-20位置0,其余不变。*(unsigned int*)   0x40011004   |= 0x00100000;      // 再将MODE13置01,PC13为10M、推挽输出模式*(unsigned int*)0x40010C00  &=~0x0F;				//先将PB:3-0位置0,其余不变。*(unsigned int*)0x40010C00  |= 0x00000004;			// 再将MODE0置01,PB0为浮空输入模式

注1:复位后各IO口为 0100 即浮空输入模式。 

注2:RCC时钟配置是整个PC口的时钟;IO模式配置只是某个Pin脚 的输入/出模式。

注3:这些与或操作都是为了保持其余IO口配置不被改变,&1、|0 都是不改变原位状态。&0是置0、| 1是置1。 

3.  输出数据寄存器:  

         

 *(unsigned int*)   0x4001100C &=0xFFFFDFFF;              //将13位置0,其余位不变。D:1101*(unsigned int*)   0x4001100C  |=  0x00002000;           //将13位置1,其余位不变。2: 0010while(1){if ((*(unsigned int*)0x40010C08&0x00000001)>0)	  //取PB0*(unsigned int*)0x4001100C |=0x00002000;	  //输出高电平;else *(unsigned int*)0x4001100C &=0xFFFFDFFF;	  //输出低电平}

二.寄存器地址命名写入法

直接地址法就是对照手册配置对应地址的寄存器写入或读出。地址不好记,程序不直观。

下面所有看似高大上的所有工作都是为了让程序的可读性变强。

先从地址命名法开始,就是预告把这些难记的地址用 #define重新命名:#define 木易电子   xxxxxxxxx

首先,我们把总总线和三大分总线基地址各个起名:

 PERIPH_BASE :翻译为:外设 基地

/*把下面的命名保存一个头文件里"stm32f10x.h" ,方便统一管理*/#define PERIPH_BASE 0x40000000								//总总线基址  (中国)
#define APB1PERIPH_BASE PERIPH_BASE							//APB1总线基址(北京)
#define APB2PERIPH_BASE (PERIPH_BASE+0x00010000)			//APB2总线基址(上海)
#define AHBPERIPH_BASE  (PERIPH_BASE+0x00020000)			//AHB总线基址 (广东)#define RCC_BASE     (AHBPERIPH_BASE+0x1000)			    //RCC分支基址  (广东东莞)
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18)			//RCC的APB2ENR (东莞石龙镇)#define GPIOC_BASE  (APB2PERIPH_BASE+0x1000)			    //GPIOC分支基址 (上海某区)
#define GPIOC_CRL   *(unsigned int*)(GPIOC_BASE+0x00)		//PC配置与输入输出寄存器(上海某区某街)
#define GPIOC_CRH   *(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOC_IDR   *(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOC_ODR   *(unsigned int*)(GPIOC_BASE+0x0C)#define GPIOB_BASE  (APB2PERIPH_BASE+0x0C00)			    //GPIOB分支基址  (上海某区)
#define GPIOB_CRL   *(unsigned int*)(GPIOB_BASE+0x00)		//PB配置与输入输出寄存器(上海某区某街)
#define GPIOB_CRH   *(unsigned int*)(GPIOB_BASE+0x04)
#define GPIOB_IDR   *(unsigned int*)(GPIOB_BASE+0x08)
#define GPIOB_ODR   *(unsigned int*)(GPIOB_BASE+0x0C)

这样以后新建工程时将这个头文件#include "xxx.h  " 就行了,程序里直接用地址名称来赋值即可。

如上一章节的例子:没有区别,只是将地址更换成名称

int main()
{RCC_APB2ENR |=0x00000018;			//打开GPIOC和GPIOB时钟GPIOC_CRH &=~(0x00F00000);			//先将PC:23-20位置0,其余不变。GPIOC_CRH  |= 0x00100000;			// 再将MODE13置01,为10M、推挽输出模式GPIOB_CRL  &=~0x0000000F;			//先将PB:3-0位置0,其余不变。GPIOB_CRL  |= 0x00000004;      	   	// 再将MODE0置01,为浮空输入模式GPIOC_ODR &=0xFFFFDFFF;	            //输出低电平delay_ms(100);GPIOC_ODR |=0x00002000;	            //输出高电平;delay_ms(100);while(1){if ((GPIOB_IDR & 0x00000001)>0) //仅取PB0一位GPIOC_ODR |= 0x00002000;    //输出高电平;else GPIOC_ODR &= 0xFFFFDFFF;    //输出低电平}
}

三.其它命名、及使用结构体 /枚举来归类 (逐步走向库函数)

从上一章看出,寄存器的名字好记了,但其中哪一位什么值还是要去查资料,可以继续深入命名 位名称和值名称 并加以同属性归类;

可以看出各组的很多寄存器是相同属性的,那么就可以用归类一统一管理(结构体或枚举)。

1. 定义2个结构体类型,并用一命名(结构体变量名)指向上一章所述的对应基址。

/*上一章节的命名这里省略了。。。。*/typedef unsigned int uint32_t;typedef struct
{uint32_t CRL;							//第一个成员首地址就是些结构体变量的首地址uint32_t CRH;							//以下成员地址依次增加,因为是32位,故+0x4uint32_t IDR;uint32_t ODR;uint32_t BSRR;uint32_t BSR;uint32_t LCKR;}GPIO_TypeDef;							//GPIO结构体变量:给GPIO寄存器同属性归类typedef struct
{uint32_t CR;uint32_t CFGR;uint32_t CIR;uint32_t APB2RSTR;uint32_t APB1RSTR;uint32_t AHBENR;uint32_t APB2ENR;uint32_t APB1ENR;uint32_t BDCR;uint32_t CSR;
}RCC_TypeDef;								//RCC结构体变量:给RCC寄存器同属性归类#define GPIOC ((GPIO_TypeDef*)GPIOC_BASE)	//命名:将0x40021000 指定为一结构体类型的地址
#define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)	//结构体类型地址:说明此地址是成员0的首址,共有N个成员的地址
#define RCC ((RCC_TypeDef*)RCC_BASE)

然后,主程序里就可以这样了:

    RCC->APB2ENR |=0x00000018;           //打开GPIOC和GPIOB时钟    
    GPIOC->CRH &=~(0x00F00000);          //先将PC:23-20位置0,其余不变。
    GPIOC->CRH  |= 0x00100000;              // 再将MODE13置01,为10M、推挽输出模式
    
    GPIOC->CRL &=~(0x0FF);                    //先将PC:8-0位置0,其余不变。
    GPIOC->CRL  |= 0x11;                          // 再将MODE1/0置01,为10M、推挽输出模式

看出来了,只是变了下 ->, 感觉更高大上而已,没啥意思吧,哈哈,接着来...

如果这样:配置时:我告诉一个函数:PC组的13脚,推挽输出,10MHZ。然后此函数自动去配置,我们不需要查哪个寄存器,也不需要查哪个位?置1还是置0。好,下面的工作都是为了达到这个目的而繁琐。开始苦点,未来可幸福了,#include即可。

/*上部分就是上程序图,这里省略了。。。*/
#define GPIO_Pin_0  ((uint16_t)0x0001)		//16个Pin脚在对应的16位寄存器值
#define GPIO_Pin_1  ((uint16_t)0x0002)
//Pin2.3.4........14略
#define GPIO_Pin_15 ((uint16_t)0x8000)typedef enum								//GPIO输入输出模式对应值用命名后用枚举归类
{GPIO_Mode_AIN = 0x00,					//GPIOMode_TypeDef就是一变量类型名,GPIO_Mode_IN_FLOATING = 0x04,			//此类型变量只能是此枚举中所列的成员GPIO_Mode_IPD = 0x28,GPIO_Mode_IPU = 0x48,GPIO_Mode_Out_OD = 0x14,GPIO_Mode_Out_PP = 0x10,GPIO_Mode_AF_OD  = 0x1C,GPIO_Mode_AF_PP  = 0x18,
}GPIOMode_TypeDef;							
typedef enum								//GPIO输出频率对应值用命名后用枚举归类
{GPIO_Speed_10MHZ=1,GPIO_Speed_2MHZ   ,GPIO_Speed_50MHZ  ,
}GPIOSpeed_TypeDef;typedef struct								//GPIO配置结构体:把配置所需的共性部分集合在一起
{uint16_t          GPIO_Pin;				//GPIOSpeed_TypeDef GPIO_Speed;GPIOMode_TypeDef  GPIO_Mode;
}GPIO_InitTypeDef;/*以上命名、枚举、结构体都只是给寄存器、管脚、设置值 归类并起个能读懂的名字:给烹饪材料和菜肉起名分门别类
以下函数就是:将上面的素材进行加工,直接做成菜单上的菜,外人不需要知道怎么做的,更不需要了解那些讨厌的素材*/
void GPIO_SetBits(GPIO_TypeDef*GPIOx,uint16_t GPIO_Pin)
{GPIOx->BSRR |=GPIO_Pin;					//GPIOx组的GPIO_Pin_y脚=1
}void GPIO_ResetBits(GPIO_TypeDef*GPIOx,uint16_t GPIO_Pin)
{GPIOx->BRR |=GPIO_Pin;					//GPIOx组的GPIO_Pin_y脚=0
}//配置输入输出模式数
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;uint32_t tmpreg = 0x00, pinmask = 0x00;/* Check the parameters */assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

还有配置函数GPIO_Init().配置的主要思路可以这样:根据Mode取得3、2二位的配置,根据Speed取得1、0位;

根据Pin的值可取得是CRH还是CRL; 再根据Pin的1在哪个位上,对应的CRH/L的对应的4位写入上面的Mode | Speed即可。

具体略了。还有RCC配置函数等,可以直接调用库,你需不需要知道内部原理,想了解更好。不知道也能用,比如不懂汽车原理也能当个老司机一样。

另外,上面写的命名、结构体、函数等都在一个文件里是不合C语言约定的,命名申明等有.h;

函数功能用.c。主程序只需要#incelud ''xxxxx.h"即可,C语言约定会自动寻找同名的.c文件

三.GPIO一些参考图


http://www.hkcw.cn/article/iHbbMBLJag.shtml

相关文章

c++继承

继承的概念及定义介绍 什么是继承&#xff0c;继承是面向对象而言的&#xff0c;他的地基是基于类而言的。最简单的介绍就是&#xff0c;一个类继承了另一个的成员&#xff0c;可以使用这个类成员&#xff0c;并且可以在此基础上&#xff0c;定义自己的成员&#xff0c;从而…

【后端高阶面经:架构篇】56、大数据技术架构的思想和原理是什么?

一、大数据架构的核心思想:分而治之的分布式哲学 (一)分布式存储与计算的本质 大数据架构的终极目标是解决单机存储与计算的性能瓶颈,其核心在于将数据与计算任务分散到多台廉价服务器,通过协同工作突破物理限制。这一思想的实现依赖两大基石: 数据分片(Data Sharding…

联想发布C55数码相机 6400万像素新选择

联想在京东上架了一款型号为C55的6400万像素数码相机,现已开售。该相机提供64GB和128GB两种版本,售价分别为499元和559元,首发价则为424元和475元。部分地区还支持叠加15%的国家补贴。这款相机采用了6400万像素的索尼CMOS传感器,尺寸为1/3英寸,配备2.8英寸液晶屏,支持ISO…

律师:厂家远程锁电动车侵权 经销商财产权受损

厂家远程锁电动车侵权 经销商财产权受损!最近,盐城市民王先生遇到了一个棘手的问题。去年他加盟销售一款品牌电动车,但由于销量不佳,最终自行关闭了店铺。在与厂家协商店铺装修赔偿事宜时,厂家通过技术手段将王先生已经付全款的库存电动车全部锁死,导致这批车辆无法正常销…

武汉女足主帅:王霜是核心,展现强大心脏

率队夺得女足亚冠冠军后,武汉车谷江大女足主教练常卫魏在接受采访时说:“赢得亚冠的历程,值得我一辈子回忆。陈晨成为扑点球的专家了,王霜展现了核心的价值。”关于门将陈晨在决赛中的表现,常卫魏提到,首发门将丁旋在上半场与对方球员有一次碰撞,中场休息时她表示腰部非…

【软件测试】火狐驱动下载镜像

CNPM Binaries Mirrorhttps://registry.npmmirror.com/binary.html?pathgeckodriver/

windows 缓冲区溢出实验关于VS的一些配置 (逆向与漏洞分析 (魏强) (Z-Library))

使用vs编写缓冲区溢出demo 的配置 最近在看 逆向与漏洞分析 (魏强) (Z-Library) 这本书&#xff0c;书上的关于缓冲区溢出的实验代码&#xff0c;使用vs 编写代码编译出来的可执行程序默认情况下就会存在一系列保护&#xff0c;如何不修改的话真的就调试不了书上的实验。主要是…

西红柿番茄成熟度目标检测数据集介绍

随着智能农业的发展&#xff0c;果蔬成熟度识别逐渐成为自动化采摘与质量控制中的核心问题之一。为了支持基于深度学习的目标检测算法对西红柿不同成熟阶段进行识别与分类&#xff0c;我们构建了一个西红柿成熟度目标检测数据集&#xff0c;该数据集包含三种成熟度类别&#xf…

大数据-275 Spark MLib - 基础介绍 机器学习算法 集成学习 随机森林 Bagging Boosting

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大模型篇章已经开始&#xff01; 目前已经更新到了第 22 篇&#xff1a;大语言模型 22 - MCP 自动操作 FigmaCursor 自动设计原型 Java篇开…

贪心算法应用:线性规划贪心舍入问题详解

贪心算法应用&#xff1a;线性规划贪心舍入问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;从而希望导致结果是全局最优的算法策略。在线性规划问题中&#xff0c;贪心算法特别是贪心舍入技术有着广泛的应用。下面我将全面详细地讲解这一主题。…

【LLM vs Agent】从语言模型到智能体,人工智能迈出的关键一步

目录 一、什么是 LLM&#xff1f;语言的天才&#xff0c;思维的起点 ✅ 特点小结&#xff1a; 二、什么是 Agent&#xff1f;智能的执行者&#xff0c;自主的决策者 ✅ 特点小结&#xff1a; 三、LLM 与 Agent 的关系&#xff1a;是工具&#xff0c;更是大脑 四、案例实战…

esp32 platformio lvgl_gif的使用和踩坑情况

踩坑一&#xff1a;白屏 不显示 开启custom内存这里 以及 gif显示 踩坑二&#xff1a;只有图片 不显示动态 没开时间 要打开这个时基 网站:转成c数组的官方网站 Image Converter — LVGL 以及显示代码&#xff1a;在setup里面调用 LV_IMG_DECLARE(my_gif);lv_obj_t *img;img…

华南沿海等地较强降雨持续 局地伴有强对流天气

今明两天(6月3日至4日),我国降雨主要出现在云南和华南沿海、东北地区等地,局地还可能伴有强对流天气。随着高压脊东移,北方大部气温逐渐升高,华北、黄淮等地高温天气将发展增多,南方多地5日起也将加入高温行列。昨天,冷空气南下导致南方强降雨区域南压至华南和云南一带…

黄金白银原油大涨 市场热度持续升温

黄金原油市场收盘大涨,贵金属与能源品种展现出强劲涨势。COMEX黄金期货和白银期货分别以显著涨幅收盘,其中黄金期货收涨2.74%,报3406.4美元/盎司;白银期货收涨5.76%,报34.93美元/盎司。原油市场同样表现抢眼,WTI原油期货收于每桶62.52美元,上涨1.73美元,涨幅为2.85%;布…

“蹦床外长”当选联大主席 外交新挑战

蹦床外长当选联大主席!一夜醒来,联合国大会发生了重大变化。周一下午,在纽约联合国总部举行的全体会议上,被称为“蹦床外长”的安娜莱娜贝尔伯克以167票当选为联合国大会主席。这距离她离开德国外长之位不到一个月。这份工作需要相当强的外交技巧,一些批评人士认为这是贝尔…

python依赖库管理工具

软件名称&#xff1a;Python依赖管理工具 版本&#xff1a;1.0适用系统&#xff1a;Windows 7/10/11&#xff0c;Python 项目环境管理辅助工具 开发语言&#xff1a;Python 3 PyQt5 开发者&#xff1a;Thebzk 软件功能简介&#xff1a; 本工具是专为Python 开发者设计的图…

labuladong刷题之前缀和与差分数组技巧(空间换时间)

Q1题目链接&#xff1a;https://leetcode.com/problems/range-sum-query-immutable/ leetcode 303 看到题目的第一思路&#xff1a; 代码随想录之后的想法和总结&#xff1a; 如何用 prefix 查询区间和&#xff1f; 如果你想查询&#xff1a;nums[left] nums[left1] ... …

基于爬取的典籍数据重新设计前端界面

1.BooksView(书籍列表页) 2.ClassicsView&#xff08;目录页&#xff09; 3.管理员端

Origin将杂乱的分组散点图升级为美观的带颜色映射的气泡图

图形初步了解 带颜色映射的气泡图&#xff0c;是一种含有三个变量的高级散点图&#xff0c;前两个变量分别为横纵坐标&#xff0c;第三个变量通过气泡的大小和颜色来呈现。在视觉上辅助读者直观地比较三个变量的关系。 在本例图中&#xff0c;通过pH、Group和Solubility三个指…

初识Linux指令(笔记2)

&#xff08;1&#xff09;man指令&#xff08;重要&#xff09; Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助。访问Linux手册页的命令是man 语法: man [选项] 命令 &#xff08;2&#xff09;cp指令&#xff08;重要&…