【STM32】标准库的引入
一、为什么要会有标志外设库
1、传统单片机软件开发方式
(1)芯片厂商提供数据手册、示例代码、开发环境
(2)单片机软件工程师面向产品功能,查阅数据手册,参考官方示例代码进行开发
(3)硬件操作的方式是用C语言对寄存器进行读写以操作硬件
(4)主要工作量分2块:一是调通各种外设,二是实现产品功能
(5)在简单单片机(如51单片机)上这一套工作的很好,但是随着单片机变复杂就带来一些问题
2、外设库有什么价值
(1)外设库其实就是以前芯片公司提供的示例代码的标准化产物
(2)外设库简化了我们开发产品的2大工作量的第一个
(3)外设库以源码方式提供,这个源码本身写的很标准,可以用作学习素材
3、学习和使用外设库的难点
(1)要有规范化编程的意识和能力
(2)C语言功底要过关
(3)要有一定的框架和层次认识
(4)要会没有外设库时直接C语言操作寄存器的方式(看原理图、查数据手册、位操作等)
二、外设库的结构介绍和之后的学习方法
1.外设库的结构介绍
(1)最新版本库的下载和解压
官方网站:STM32标准外设软件库: 相关产品
(2)建立SourceInsight工程
1)先在盘符中建立一个文件夹,然后再sourceInsight中打开这个文件夹
(3)文件夹结构和主要文件的作用


1.Project

2.Libraries

CMSIS(STM32内部ARM核心相关内容)
CM3(Cortex-M3)
CoreSupport
内核相关的一些设置的寄存器集合及其封装
DeviceSupport
ST
STM32F10x
startup(起始文件)
https://www.cnblogs.com/amanlikethis/p/3989989.html
stm32f10x.h【STM32常见数据结构的封装】
system_stm32f10x.c
system_stm32f10x.h
STM32F10x_StdPeriph_Driver(外设驱动)
inc(include,头文件,.h)
src(source,源文件, .c)
2.system_stm32f10x.c
在这个文件中定义了2个函数和一个全局变量:时钟相关的

3、后续的学习方法
(1)先搞清楚库对STM32这个硬件的封装和表达方式【对外设进行封装】
(2)再彻底理解库中使用的结构体式访问硬件寄存器的方式
(3)初步建立起面向对象式编程的概念并且去体会
(4)以模块为单位去研究这个模块的库函数,并且用库函数去编程,并且实验结果,并且分析代码,去体会去熟悉库函数使用的方法
(5)最终达到什么程度?眼里有库心中无库。用人话说就是:思维能够穿透库函数直达内部对寄存器的操作。
三、标准库对硬件信息的封装方法
1.寄存器地址的封装
1.手动操作寄存器地址

2.库文件中的寄存器地址(stm32f10x.h)



2.寄存器位定义的封装
1.直接通过移位操作位寄存器


2.使用宏定义进行位寄存器
不用直接写数字,而使用宏定义


3.外设操作的封装
1.自定义函数

2.使用库函数
.c文件对其进行封装


四、使用结构体方式访问寄存器的原理
1.最原始的方法
1)C语言访问寄存器的本质是c语言访问内存,本质思路是:
定义一个指针指向这块内存,然后*p=xx这种方式去解引用指针从而向没有标内存中写入内容。
2)缺陷:当寄存器多了之后每一个寄存器都要定义一套套路,很麻烦
3)解决思路:就是打包,批发式定义,用结构体(为什么不用数组)的方式来进行打包。具体方法:把整个模块的所有寄存器(地址是连续的)打包在一个结构体中,每一个寄存器对应结构体中的一个元素,然后结构体基地址对应寄存器组的基地址,将来就可以通过结构体的各个元素来访问各个寄存器了。

2.使用结构体
结构体方式来访问寄存器和指针式访问寄存器,本质上其实是一样的,区别是c语言的封装不同。
TIM的


GPIO的



五、使用结构体方式访问寄存器的实践
1.原始方式
gpio.h
#define GPIOB_CRH 0x40010C04
#define GPIOB_CRL 0x40010C00
#define GPIOB_ODR 0x40010C0C
#define GPIOB_BSRR 0x40010C10
#define GPIOB_BRR 0x40010C14#define RCC_APB2ENR 0x40021018#define rGPIOB_CRH (*((unsigned int *)GPIOB_CRH))
#define rGPIOB_ODR (*((unsigned int *)GPIOB_ODR))
#define rGPIOB_BSRR (*((unsigned int *)GPIOB_BSRR))
#define rGPIOB_BRR (*((unsigned int *)GPIOB_BRR))
#define rRCC_APB2ENR (*((unsigned int *)RCC_APB2ENR))void led_flash(void);void led_init();
void delay();
gpio.c
/**点亮led灯
*/#include "gpio.h"void delay(){unsigned int i=0,j=0;for(i=0;i<1000;i++){for(j=0;j<4000;j++){}}
}void led_init(){rRCC_APB2ENR = 0x00000008;rGPIOB_CRH = 0x33333333;rGPIOB_ODR = 0x0000aa00;//全灭}
void led_flash(void){unsigned int i=0;for(i=0;i<3;i++){rGPIOB_ODR = 0x00000000;//全亮delay();rGPIOB_ODR = 0x0000ff00;//全灭delay();}
}
void main(void){led_init();led_flash();
}
2.使用结构体的方式对寄存器访问
注意点:
我们在定义相关寄存器的结构体的时候要注意顺序问题,一定要按照寄存器偏移量从低到高,要不然会出现问题。

3.两者对比


代码
gpio.h
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000#define GPIOB_CRH (GPIOB_BASE + 0x04)
#define GPIOB_ODR (GPIOB_BASE + 0x0C)
#define GPIOB_BSRR (GPIOB_BASE + 0x10)
#define GPIOB_BRR (GPIOB_BASE + 0x14)#define GPIOC_CRL (GPIOC_BASE + 0x00)
#define GPIOC_ODR (GPIOC_BASE + 0x0C)
#define GPIOC_BSRR (GPIOC_BASE + 0x10)
#define GPIOC_BRR (GPIOC_BASE + 0x14)#define RCC_APB2ENR 0x40021018//-------------------------------------------------#define rGPIOB_CRH (*((unsigned int *)GPIOB_CRH))
#define rGPIOB_ODR (*((unsigned int *)GPIOB_ODR))
#define rGPIOB_BSRR (*((unsigned int *)GPIOB_BSRR))
#define rGPIOB_BRR (*((unsigned int *)GPIOB_BRR))#define rGPIOC_CRL (*((unsigned int *)GPIOC_CRL))
#define rGPIOC_ODR (*((unsigned int *)GPIOC_ODR))
#define rGPIOC_BSRR (*((unsigned int *)GPIOC_BSRR))
#define rGPIOC_BRR (*((unsigned int *)GPIOC_BRR))#define rRCC_APB2ENR (*((unsigned int *)RCC_APB2ENR))//用结构体的方式对寄存器访问
typedef struct{unsigned int CRL;unsigned int CRH;unsigned int IDR;unsigned int ODR;unsigned int BSRR;unsigned int BRR;unsigned int LCKR;
}GPIO_Typedef;void led_flash(void);void led_init();
void delay();
gpio.c
/**点亮led灯
*/#include "gpio.h"void delay(){unsigned int i=0,j=0;for(i=0;i<1000;i++){for(j=0;j<4000;j++){}}
}/*
原始方法
void led_init(){rRCC_APB2ENR = 0x00000008;rGPIOB_CRH = 0x33333333;rGPIOB_ODR = 0x0000aa00;//隔一个亮一个}
void led_flash(void){unsigned int i=0;for(i=0;i<3;i++){rGPIOB_ODR = 0x00000000;//全亮delay();rGPIOB_ODR = 0x0000ff00;//全灭delay();}
}
*/void led_init(){GPIO_Typedef *p=(GPIO_Typedef *)GPIOB_BASE;//因为RCC部分还没有定义结构体,所以还是按照原来的方式去操作rRCC_APB2ENR = 0x00000008;//结构体只能使用【->】p->CRH=0x33333333;p->ODR=0x0000aa00;
}
void led_flash(void){GPIO_Typedef *p=(GPIO_Typedef *)GPIOB_BASE;unsigned int i=0;for(i=0;i<3;i++){p->ODR = 0x00000000;//全亮delay();p->ODR = 0x0000ff00;//全灭delay();}
}void main(void){led_init();led_flash();
}
4.小技巧
1.将基地址指针作为全局变量
因为在每一个函数开始之前,都要使用到基地址

2.结构体元素填充
我们前面提到说,在定义结构体的时候一定一定要按照寄存器的偏移量从小到大的顺序定义要不然出错,但是我们在一些地方可能空缺出来,没有寄存器,所以我们需要一个占位,将其补充,要不然后面的其他寄存器可能受到影响。

3.寄存器可操作位数不同
我们在学习RCC时钟的时候遇到一些寄存器可以操作32位,但是有一些只能操作16位。此时我们可以将其直接写入(不用管是否用足32位),也可以分成2个16位写入。



//用结构体的方式对寄存器访问
typedef struct{unsigned int CRL;unsigned int CRH;//unsigned int IDR;//将32位的IDR分为2个16位unsigned short IDR;unsigned short paddingIDR;//此16位用不到unsigned int ODR;unsigned int BSRR;unsigned int BRR;unsigned int LCKR;
}GPIO_Typedef;
六、使用标准库重写LED的程序
1.分析标准库自带的文件模板

1.完整目录

2.User,Driver

3.CMSIS.startup


4.配置注意点:




2.stm32f10x.h



选择芯片类型
在“stm32f10x.h”有可以查看,要修改可以在options--》c/c++中进行修改宏定义

2.宏定义的设置


3.外部晶振时钟设置

3.stm32f10x_conf.h
1.包含所有外设的头文件
这个头文件是标准库的模板中的,将所有需要使用到的头文件包含进去



所以在整个工程中,无论我们创建一个什么类型的.c文件,只要我们定义了
【“stm32f10x.h”】
则就等价于将所有的外设器件的.h文件包含进来了

2.assert_param(断言机制)

4.正式自己搭建文件目录
1.文件结构


2.文件导入
1.startup


2.CMSIS

3.stdperiph_driver

4.user



3.配置




自定义宏定义,在stm32f10x.h,stm32f10_conf.h文件中查找






4.出现问题


将配置中的”断言“去除


5.代码修改

#include "stm32f10x.h"/**使用标准库重写LED的程序
*//*
//原始代码
void led_init(){//GPIO_Typedef *p=(GPIO_Typedef *)GPIOB_BASE;//因为RCC部分还没有定义结构体,所以还是按照原来的方式去操作rRCC_APB2ENR = 0x00000008;//结构体只能使用【->】//p->CRH=0x33333333;pGPIOB->CRH=0x33333333;//p->ODR=0x0000aa00;pGPIOB->ODR=0x0000aa00;
}
*///第一步:先去”stm32f10x.h"文件中查找相关的寄存器
// 比如RCC,GPIO
//第二步:找到相关的寄存器的宏定义,将其直接复制过来//使用HAL库
void led_init(){//GPIO_Typedef *p=(GPIO_Typedef *)GPIOB_BASE;//因为RCC部分还没有定义结构体,所以还是按照原来的方式去操作RCC->APB2ENR = 0x00000008;//结构体只能使用【->】//p->CRH=0x33333333;GPIOB->CRH=0x33333333;//p->ODR=0x0000aa00;GPIOB->ODR=0x0000aa00;
}int main(){led_init();return 0;
}
七、RCC模块的标准库全解析
【STM32】RCC时钟模块(使用HAL库)-CSDN博客
八、CPIO模块的标准库全解析
【STM32】GPIO控制LED(HAL库版)-CSDN博客
九、标准库中的面向对象思想
1、面向对象介绍
(1)一种编程思想(面向过程、面向对象)
(2)什么是对象
(3)面向对象三大特征:封装、继承、多态
(4)面向对象编程思想和面向对象语言是两码事
2、标准库的面向对象特征
(1)各种数据类型结构体就是一种封装
(2)标准库是为了被复用
(3)GPIO的编程模式是典型的面向对象式编程
典型面向对象的编程模式:
第1步:先构建对象(可以理解为定义一个结构体类型)
第2步:用对象构造实例(可以理解为用结构体类型来定义结构体变量)malloc
第3步:填充实例(其实就是给结构体的各个元素赋值)
第4步:使用实例(其实就是把结构体变量作为参数传给某个函数使用)
第5步:销毁实例(其实就是把前面第2步定义的机构体变量给销毁掉)free
相关文章:
【STM32】标准库的引入
一、为什么要会有标志外设库 1、传统单片机软件开发方式 (1)芯片厂商提供数据手册、示例代码、开发环境 (2)单片机软件工程师面向产品功能,查阅数据手册,参考官方示例代码进行开发 (3)硬件操作的方式是用C语言对寄存器进行读写以操作硬件 (4)主要工作量…...
Redis的淘汰策略
Redis是一种基于内存的高性能键值存储数据库,由于内存资源有限,当Redis中的数据量增大时,会导致内存占用过多,可能会影响系统的性能和稳定性。为了解决这个问题,Redis提供了一些淘汰策略来自动清理不再使用的key&#…...
Linux友人帐之日志与备份
一、日志 1.1概述 日志文件是重要的系统信息文件,其中记录了许多重要的系统事件,包括用户的登录信息、系统的启动信息、系统的安全信息、邮件相关信息、各种服务相关信息等。日志对于安全来说也很重要,它记录了系统每天发生的各种事情&#…...
git中如何在父仓库提交子仓库的修改
子仓库在父仓库中进行了修改,你需要按照以下步骤提交子仓库的修改: 切换到子仓库目录:使用cd命令进入子仓库所在的目录。拉取子仓库的最新更改:使用git pull命令拉取子仓库的最新更改,确保你的本地是最新的版本。提交…...
【【萌新的SOC学习之SD卡DMA回路读写大数据的实验】】
萌新的SOC学习之SD卡读写大数据的实验 记得先设定 FIFO 的位宽和深度 还有DMA 的大小 其他基本结构设计参照上一个SD卡读写小数据的实验 #include "xparameters.h" //包含vivado所导出的信息包含vivado的基地址 #include "xil_printf.h" //调用打印函…...
在k8s中 ,数据包是怎么从外部流转进入到pod的?
在 Kubernetes 中,当您创建 NodePort 类型的服务时,流量不会直接从主机的 IP 和端口转发到特定 Pod 的 IP 和端口。相反,流量被转发到集群中的一个节点,然后从那里转发到相应的 Pod。 1、当您创建 NodePort 类型的服务时…...
微信小程序设置 wx.showModal 提示框中 确定和取消按钮的颜色
wx官方提供的 showModal 无疑是个非常优秀的选择提示工具 但是 我们还可以让他的颜色更贴近整体的小程序风格 cancelColor 可以改变取消按钮的颜色 confirmColor 则可以控制确定按钮的颜色 参考代码如下 wx.showModal({cancelColor: #0000FF,confirmColor: #45B250,content:…...
【Chrome】使用k8s、docker部署无头浏览器Headless,Java调用示例
什么是无头浏览器? 无头浏览器是一种没有图形用户界面的浏览器。无头浏览器不通过其图形用户界面(GUI)控制浏览器的操作,而是使用命令行。 为什么要用Chrome无头? Chrome Headless用于抓取(谷歌)、测试(开发者)和黑客(黑客)。搜索引擎&…...
springmvc http请求,支持get,post,附件传输和参数传输
主要解决http请求支持get,post,put,delete等常规方法,支持RequestParam,RequestBody,PathVariable等参数格式传输,支持传输附件同时传递参数等,主体代码如下: package mes.client.action;import cn.hutool.crypto.digest.DigestUt…...
linux性能分析(七)CPU性能篇(二)怎么理解平均负载
一 怎么理解平均负载 ① 如何查看平均复杂 查看系统负载的命令: top、uptime、w、cat /proc/loadavg、tload /proc/loadavg 思考: uptime每列输出的含义?重点: 当前时间、系统运行时间、正在登录用户数、平均负载 ② 思考࿱…...
PostgreSQL12中浮点数输出算法优化带来的小问题
最近碰到同事发来这样两个SQL,开发反馈输出的结果异常。 bill# select 0.1284*100::float;?column? --------------------12.839999999999998 (1 row)bill# select (0.1284*100)::float;float8 --------12.84 (1 row) 乍一看其实能看出明显的区别,由于…...
Hive安装配置笔记
版本说明 hadoop-3.3.6(已安装) mysql-8(已安装) hive-3.1.3 将hive解压到对应目录后做如下配置: 基本配置与操作 1、hive-site <configuration><!-- jdbc连接的URL --><property><name>ja…...
前端数据可视化之【Echarts下载使用】
目录 🌟下载🌟浏览器引入🌟模块化引入 🌟使用🌟基本使用步骤 🌟绘制一个简单的图表🌟写在最后 🌟下载 🌟浏览器引入 官网下载界面:官方网站 或 Echarts中文…...
本机计算机上的mysql启动后停止
本机计算机上的mysql启动后停止 原因:mysql5.0和mysql8.0配置不同 把my.ini改成以下设置然后再重新 mysqld --initialize-insecure --usermysql然后再启动 net start mysqlmysql8.0 下面的路劲可自定义 [client] default-character-setutf8 [mysql] default-cha…...
Java中ReentrantLock测试线程的安全
使用场景 当需要在多线程环境下保证共享资源的安全访问时,可以使用Java中的ReentrantLock来实现线程安全。ReentrantLock是一个可重入的互斥锁,它提供了与synchronized关键字类似的功能,但更加灵活和扩展性强。 下面是一个使用ReentrantLoc…...
Vue-dvadmin-d2-crud-plus-常用配置-row-handle-columns-options
文章目录 1.row-handle columnHeader width minWidth fixed align renderHeader edit view remove remove.confirm remove.confirmTitle remove.confirmText custom 范例1 范例2 2.columns title key width minWidth fixed renderHeader sortable sortMethod sortBy sortOrders…...
【OpenCV实现图像的算数运算,性能测试和优化,改变颜色空间】
文章目录 OpenCV功能概要图像的算数运算性能测试和优化改变颜色空间对象追踪 OpenCV功能概要 OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习库,提供了丰富的图像处理和计算机视觉算法。它支持多种编程语言&…...
多级缓存入门
文章目录 什么是多级缓存JVM进程缓存环境准备安装MySQL导入Demo工程导入商品查询页面 初识Caffeine Lua语法初识Lua第一个lua程序变量和循环Lua的数据类型声明变量循环 条件控制、函数函数条件控制 多级缓存安装OpenRestyOpenResty快速入门反向代理流程OpenResty监听请求编写it…...
CentOS卸载LVM磁盘的方法
在客户环境上遇到一个问题,本身的磁盘满了,需要把没有用的lvm逻辑卷卸载掉,然后挂上去,下面记录一下过程。 卸载原磁盘 umount /data # 如果/data目录正在被其他进程使用中,则使用fuser强制关闭,然后Umou…...
ChatGPT:Spring Boot和Maven——Java应用开发的关键工具和区别
ChatGPT:Spring Boot和Maven——Java应用开发的关键工具和区别 Springboot是什么? ChatGPT: Spring Boot是一个用于构建Java应用程序的开源框架,它是Spring Framework的一部分,但旨在简化Spring应用程序的开发。Sprin…...
新手福音:通过快马平台零代码基础理解qun329群聊应用开发
作为一个刚接触编程的新手,想要理解群聊应用开发确实容易一头雾水。最近我在尝试用InsCode(快马)平台搭建类似qun329的简单群聊网页时,发现整个过程比想象中简单很多。下面分享我的学习过程,希望能帮到同样零基础的朋友。 项目结构规划 首先明…...
OPCUA结构体数据处理全解析:C#如何高效读写ExtensionObject中的复杂数据
OPCUA结构体数据处理全解析:C#如何高效读写ExtensionObject中的复杂数据 在工业自动化与物联网系统中,OPCUA协议已成为设备间数据交换的事实标准。当面对复杂的自定义结构体数据时,ExtensionObject的处理往往成为开发者的痛点。本文将深入剖析…...
Android安全漏洞案例分析:血淋淋的教训
Android安全漏洞案例分析:血淋淋的教训 Android安全漏洞案例分析:血淋淋的教训 案例一:Secret Token泄露导致账户劫持 漏洞危害:攻击者获取用户全部权限 某社交App在客户端硬编码了API密钥,攻击者通过反编译获取密钥…...
Agent在零售行业能解决哪些痛点?——深度解析零售企业智能自动化转型路径
在2026年零售行业加速迈向智能化的背景下,AI Agent(人工智能智能体)已不再仅仅是技术实验室的产物,而是演变为重构行业价值链的核心驱动力。传统的零售运营长期受困于人力密集型模式,面临着全球化运营复杂度高、数据孤…...
3步打造高效右键菜单:让Windows操作提速50%
3步打造高效右键菜单:让Windows操作提速50% 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是否也曾在右键点击文件时,面对长达20个选项…...
猫抓插件:浏览器资源嗅探的革命性解决方案
猫抓插件:浏览器资源嗅探的革命性解决方案 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 你是否曾在浏览网页时,看到心仪的…...
从选型到焊接:一份给嵌入式新手的晶振避坑指南(含32.768KHz实例)
从选型到焊接:嵌入式开发者的晶振实战避坑手册 第一次点亮自己设计的电路板时,那颗小小的晶振就像电子世界的心跳起搏器。记得三年前我为一个智能家居项目调试STM32时,连续三天卡在"晶振不起振"的问题上——电路图反复检查无误&…...
告别重复编码:用Copaw结合快马平台,自动化生成你的常用工具模块
作为一名经常需要整理会议纪要的开发者,我一直在寻找能提升效率的工具。最近尝试用Copaw结合InsCode(快马)平台做了一个会议纪要自动生成器,效果出乎意料地好。整个过程几乎没写代码,却实现了核心功能,分享下具体实现思路…...
DeepSeek风格迁移降AI怎么用?从0到1完整操作教程
第一次操作的话,照着下面的步骤来,15分钟内搞定DeepSeek风格迁移降AI、降AI、降AIGC率。 工具选嘎嘎降AI(www.aigcleaner.com),达标率99.26%,有退款保障,操作也不复杂。 准备工作 需要准备的&…...
OpenMP实战避坑:你的C++并行程序为什么跑得比单线程还慢?
OpenMP实战避坑:你的C并行程序为什么跑得比单线程还慢? 第一次在C代码里加上#pragma omp parallel for时,那种期待性能飙升的心情,相信每个开发者都经历过。但现实往往很骨感——程序运行速度不升反降,甚至出现莫名其妙…...















