【STM32】标准库的引入

news/2024/6/3 17:22:36 标签: stm32, 嵌入式硬件, 单片机, 1024程序员节

一、为什么要会有标志外设库

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


http://www.niftyadmin.cn/n/5121514.html

相关文章

2023年美国代理IP推荐,哪家IP代理好用?

美国代理IP指的是代理服务器位于美国的IP地址&#xff0c;对于跨境业务来说&#xff0c;这些代理IP地址可以用于隐藏用户的真实IP地址&#xff0c;将其网络流量路由通过美国的服务器&#xff0c;以实现一些特定的目的。由于近年来&#xff0c;面向美国市场的跨境商家越来越多&a…

【python入门】异常、文件操作

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍python入门的异常、文件操作。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下吧】 上一篇文章&#xff1a;《【python入门】函数&#xff…

选择合适的项目管理系统来支持专业产品研发团队

专业产品研发团队的公司离不开其严谨的管理和高效的研发流程&#xff0c;为了进一步提升研发效率和管理水平&#xff0c;产研团队需要一个全流程的项目管理系统来支持其研发团队的协同合作。 一、系统需求 IT行业的研发工作涵盖了从立项、项目变更到项目的进程计划等多个环节。…

【多线程】Java如何实现多线程?如何保证线程安全?如何自定义线程池?

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 多线程 Java多线程1. 进程与线程2. 多线程1&am…

nginx源码安装yum 安装官方安装步骤

1编译安装 wget https://nginx.org/download/nginx-1.24.0.tar.gz tar -zxf nginx-1.24.0.tar.gz yum install -y gcc gcc-c pcre pcre-devel zlib zlib-devel openssl openssl-devel #yum install -y pcre pcre-devel #yum install -y gcc gcc-c #yum install -y zlib-devel…

【算法中的Java】— 变量、运算符、表达式、输入与输出

&#x1f4d2;博客首页&#xff1a;Sonesang的博客 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ❤️ &#xff1a;热爱Java与算法学习&#xff0c;期待一起交流&#xff01; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xf…

Transformer详解学习

1. Transformer 原理 1.1 Transformer整体结构 Transformer的结构图&#xff0c;拆解开来&#xff0c;主要分为图上4个部分&#xff0c;其中最重要的就是2和3Encoder-Decoder部分&#xff0c;对咯&#xff0c;Transformer是一个基于Encoder-Decoder框架的模型。 接下来我将按照…

25-数据结构-稀疏矩阵-三元组

简介&#xff1a;三元组&#xff0c;意思是一个结构体里&#xff0c;有三个变量&#xff0c;存储矩阵信息。而三元组顺序存储&#xff0c;则是用顺序表&#xff0c;存储三元组&#xff0c;里面包含&#xff0c;三元组类型的一维数组&#xff0c;存储非零元素&#xff1b;总的行…