【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标准外设软件库: 相关产品

【STM32】标准库的引入_第1张图片

(2)建立SourceInsight工程

【STM32】标准库的引入_第2张图片

1)先在盘符中建立一个文件夹,然后再sourceInsight中打开这个文件夹

【STM32】标准库的引入_第3张图片

【STM32】标准库的引入_第4张图片

【STM32】标准库的引入_第5张图片

【STM32】标准库的引入_第6张图片

【STM32】标准库的引入_第7张图片

【STM32】标准库的引入_第8张图片

【STM32】标准库的引入_第9张图片

(3)文件夹结构和主要文件的作用

【STM32】标准库的引入_第10张图片

【STM32】标准库的引入_第11张图片

1.Project

【STM32】标准库的引入_第12张图片

2.Libraries

【STM32】标准库的引入_第13张图片

【STM32】标准库的引入_第14张图片

CMSIS(STM32内部ARM核心相关内容)
    CM3(Cortex-M3)
        CoreSupport
            内核相关的一些设置的寄存器集合及其封装

【STM32】标准库的引入_第15张图片
        DeviceSupport
            ST
                STM32F10x
                    startup(起始文件)

【STM32】标准库的引入_第16张图片

【STM32】标准库的引入_第17张图片

https://www.cnblogs.com/amanlikethis/p/3989989.html
                    stm32f10x.h【STM32常见数据结构的封装】
                    system_stm32f10x.c
                    system_stm32f10x.h

【STM32】标准库的引入_第18张图片
STM32F10x_StdPeriph_Driver(外设驱动)
    inc(include,头文件,.h)
    src(source,源文件, .c)

【STM32】标准库的引入_第19张图片

2.system_stm32f10x.c

在这个文件中定义了2个函数和一个全局变量:时钟相关的

【STM32】标准库的引入_第20张图片

3、后续的学习方法

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

三、标准库对硬件信息的封装方法

1.寄存器地址的封装

1.手动操作寄存器地址

【STM32】标准库的引入_第21张图片

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

【STM32】标准库的引入_第22张图片

【STM32】标准库的引入_第23张图片

【STM32】标准库的引入_第24张图片

2.寄存器位定义的封装

1.直接通过移位操作位寄存器

【STM32】标准库的引入_第25张图片

2.使用宏定义进行位寄存器

不用直接写数字,而使用宏定义

【STM32】标准库的引入_第26张图片

【STM32】标准库的引入_第27张图片

3.外设操作的封装

1.自定义函数

【STM32】标准库的引入_第28张图片

2.使用库函数

.c文件对其进行封装

【STM32】标准库的引入_第29张图片

【STM32】标准库的引入_第30张图片

四、使用结构体方式访问寄存器的原理

1.最原始的方法

1)C语言访问寄存器的本质是c语言访问内存,本质思路是:

定义一个指针指向这块内存,然后*p=xx这种方式去解引用指针从而向没有标内存中写入内容。

2)缺陷:当寄存器多了之后每一个寄存器都要定义一套套路,很麻烦

3)解决思路:就是打包,批发式定义,用结构体(为什么不用数组)的方式来进行打包。具体方法:把整个模块的所有寄存器(地址是连续的)打包在一个结构体中,每一个寄存器对应结构体中的一个元素,然后结构体基地址对应寄存器组的基地址,将来就可以通过结构体的各个元素来访问各个寄存器了。

【STM32】标准库的引入_第31张图片

2.使用结构体

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

TIM的

【STM32】标准库的引入_第32张图片

【STM32】标准库的引入_第33张图片

GPIO的

【STM32】标准库的引入_第34张图片

【STM32】标准库的引入_第35张图片

【STM32】标准库的引入_第36张图片

五、使用结构体方式访问寄存器的实践

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.使用结构体的方式对寄存器访问

注意点:

我们在定义相关寄存器的结构体的时候要注意顺序问题,一定要按照寄存器偏移量从低到高,要不然会出现问题。

【STM32】标准库的引入_第37张图片

3.两者对比

【STM32】标准库的引入_第38张图片

【STM32】标准库的引入_第39张图片

代码

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.将基地址指针作为全局变量

因为在每一个函数开始之前,都要使用到基地址

【STM32】标准库的引入_第40张图片

2.结构体元素填充

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

【STM32】标准库的引入_第41张图片

3.寄存器可操作位数不同

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

【STM32】标准库的引入_第42张图片

【STM32】标准库的引入_第43张图片

【STM32】标准库的引入_第44张图片

//用结构体的方式对寄存器访问
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.分析标准库自带的文件模板

【STM32】标准库的引入_第45张图片

1.完整目录

【STM32】标准库的引入_第46张图片

2.User,Driver

【STM32】标准库的引入_第47张图片

3.CMSIS.startup

【STM32】标准库的引入_第48张图片

【STM32】标准库的引入_第49张图片

4.配置注意点:

【STM32】标准库的引入_第50张图片

【STM32】标准库的引入_第51张图片

【STM32】标准库的引入_第52张图片

2.stm32f10x.h

【STM32】标准库的引入_第53张图片

【STM32】标准库的引入_第54张图片

【STM32】标准库的引入_第55张图片

选择芯片类型

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

【STM32】标准库的引入_第56张图片

2.宏定义的设置

【STM32】标准库的引入_第57张图片

【STM32】标准库的引入_第58张图片

3.外部晶振时钟设置

【STM32】标准库的引入_第59张图片

3.stm32f10x_conf.h

1.包含所有外设的头文件

这个头文件是标准库的模板中的,将所有需要使用到的头文件包含进去

【STM32】标准库的引入_第60张图片

【STM32】标准库的引入_第61张图片

【STM32】标准库的引入_第62张图片

所以在整个工程中,无论我们创建一个什么类型的.c文件,只要我们定义了

【“stm32f10x.h”】

则就等价于将所有的外设器件的.h文件包含进来了

【STM32】标准库的引入_第63张图片

2.assert_param(断言机制)

【STM32】标准库的引入_第64张图片

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

1.文件结构

【STM32】标准库的引入_第65张图片

【STM32】标准库的引入_第66张图片

2.文件导入

1.startup

【STM32】标准库的引入_第67张图片

【STM32】标准库的引入_第68张图片

2.CMSIS

【STM32】标准库的引入_第69张图片

3.stdperiph_driver

【STM32】标准库的引入_第70张图片

4.user

【STM32】标准库的引入_第71张图片

【STM32】标准库的引入_第72张图片

【STM32】标准库的引入_第73张图片

3.配置

【STM32】标准库的引入_第74张图片

【STM32】标准库的引入_第75张图片

【STM32】标准库的引入_第76张图片

【STM32】标准库的引入_第77张图片

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

【STM32】标准库的引入_第78张图片

【STM32】标准库的引入_第79张图片

【STM32】标准库的引入_第80张图片

【STM32】标准库的引入_第81张图片

【STM32】标准库的引入_第82张图片

4.出现问题

【STM32】标准库的引入_第83张图片

【STM32】标准库的引入_第84张图片

将配置中的”断言“去除

【STM32】标准库的引入_第85张图片

【STM32】标准库的引入_第86张图片

5.代码修改

【STM32】标准库的引入_第87张图片

#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,stm32,嵌入式硬件,单片机,1024程序员节)