c标准库源码分析 【STM32单片机学习】第五课:STM32标准外设库(SPL库)

【朱老师课程总结】第一部分,章节目录第二部分,课堂笔记3.5.1。 为什么要有标准外设库?

3.5.1.1. 传统的单片机软件开发方法

(1)芯片厂商提供数据手册、示例代码、开发环境

(2)单片机软件工程师关注产品功能,查阅数据手册,参考官方示例代码进行开发

(3)硬件操作的方法是用C语言读写寄存器来操作硬件。

(4)主要工作量:一是调整各种外设(可读可写),二是实现产品功能

(5)这套方法在简单的单片机(如51单片机)上效果很好,但是随着单片机变得越来越复杂,就会产生一些问题。

3.5.1.2. 外设库的价值是什么?

(1)外设库实际上是以前芯片公司提供的示例代码的标准化产品。

(2)外设库简化了我们开发产品时“调整各种外设”。

(3) 外设库以源代码形式提供。 源码本身非常规范,可以作为学习资料。

3.5.1.3。 外围库学习和使用困难

(1)必须具备标准化编程的意识和能力

(2)C语言技能必须通过

(3)要有一定的框架和理解水平

(4)必须懂得在没有外设库的情况下如何用C语言直接操作寄存器(看原理图、查数据手册、位操作等)

3.5.1.4. 再次强调

(1)外设库只是帮助我们简化编程。 简化的主要是人工量。

(2)外设库在一定程度上降低了编程的难度,但是只知道库,没有库就无法编程,那还是没有用。 如果库函数调用出现问题,你将束手无策。 (难度降低是针对所有人的,你不会从中受益)

3.5.2_3。 外设库的结构介绍及后续学习方法1_2

3.5.2.1. 外设库结构介绍

(1)下载并解压最新版本库

官网下载链接

博客提供链接(百度网盘,解压密码:neu1)

(2)文件夹结构及主要文件的作用

(3)最重要的是图书馆

CMSIS(STM32内部ARM内核相关内容)

CM3(皮质-M3)

CoreSupport(一些内核相关设置的寄存器集合和封装)

设备支持

英石

STM32F10x

启动(一些内核相关设置的寄存器收集和封装)

stm32f10x.h(非常重要)

系统_stm32f10x.c

系统_stm32f10x.h

STM32F10x_StdPeriph_Driver(STM32F1外设驱动程序)

inc(包含、头文件、.h)

src(源代码、源文件、.c)

目录树:

3.5.2.2. 后续学习方法

(1)首先搞清楚该库是如何封装和表达STM32硬件的

(2) 彻底理解库中使用的结构体如何访问硬件寄存器

(3)初步建立面向对象编程的概念并体验

(4)以模块为单位学习本模块的库函数,并利用库函数进行编程、实验结果,并对代码进行分析,了解和熟悉库函数的使用方法。

(5)最终达到什么程度? 眼中有宝,心中无宝。 用人的话来说:思维可以穿透库函数,到达寄存器的内部操作。

3.5.4. 标准库如何封装硬件信息

3.5.4.1. 寄存器地址封装

其实就是用宏定义来表达寄存器在内存中的地址。 (这里的地址是指位带区域中的地址,请稍后阅读位带区域和位带别名区域)

3.5.4.2. 寄存器位定义的封装

寄存器每一位的操作有很多种情况。 直接用宏定义来封装不同的复制情况。

例如,打开HSE时钟时,需要操作寄存器RCC_CR,将bit 16赋值1来打开HSE时钟。

RCC_TypeDef是一个结构体变量,它封装了RCC的各个寄存器。 RCC_BASE是RCC寄存器组的基地址,因此RCC是指向RCC_BASE下的RCC寄存器组的结构体指针。

原理一样! 这样做的好处是更直观,代码可读性更强!

3.5.4.3. 外围操作的封装

其实功能有很多,比如配置外部时钟72MHz、配置HSE、操作不同的寄存器等。 在SPL中,这些任务都完成了!

例如,配置如下HSE子功能。 只要RCC_HSE准备好了,就可以打开HSE了!

3.5.5。 使用结构体访问寄存器的原理

(1)以前的访问寄存器方法

C语言访问寄存器的本质是C语言访问内存。

基本思想:

缺点:

(2)解决思路:就是封装,批发定义,以结构体的形式封装(想想为什么不用数组?)。

具体方法:

将整个模块的所有寄存器(地址相连)打包成一个结构体。 每个寄存器对应结构体中的一个元素,那么结构体的基地址就对应寄存器组的基地址。 以后可以通过结构体的Each元素来访问各个寄存器。

(3)通过结构体访问寄存器和通过指针访问寄存器本质上是相同的。 不同的是C语言的封装。

(4)挥发性的作用

3.5.6. 使用结构体访问寄存器的实践

程序下载

3.5.7_8。 使用标准库重写LED程序

3.5.7.1. 分析标准库自带的项目模板

该模板位于“STM32F10x_StdPeriph_Lib_V3.5.0ProjectSTM32F10x_StdPeriph_TemplateMDK-ARM”下。 使用MDK软件双击工程打开。

模板目录:

3.5.7.2. 创建您自己的模板

1.创建My_Template文件夹

2、进入My_Template,将STM32F10x_StdPeriph_Lib_V3.5.0Libraries下的CMSIS和STM32F10x_StdPeriph_Driver复制到My_Template中

3. 创建用户、项目、列表、输出文件夹

4、打开MDK,新建Project(自定义名称),存放在My_Template/Project下

5、选择自己开发板的芯片。 这里选择的是“STM32F103C8”。 当运行时页面出现时,只需将其关闭即可。

6. 将各个文件夹添加到项目中

7.添加启动文件组。 启动文件位于My_TemplateCMSISCM3DeviceSupportSTSTM32F10xstartuparm下。 STM32F103C8属于md产品,所以我选择xx_md.s

8.添加CMSIS组,在My_TemplateCMSISCM3CoreSupport下添加core_cm3.c,在My_TemplateCMSISCM3DeviceSupportSTSTM32F10x下添加system_stm32f10x.c

9、添加StdPeriph_Driver组,添加My_TemplateSTM32F10x_StdPeriph_Driversrc下的所有文件

10、添加User组,在User文件夹下新建main.c文件,将STM32F10x_StdPeriph_Lib_V3.5.0ProjectSTM32F10x_StdPeriph_Template下的stm32f10x_conf.h、stm32f10x_it.c、stm32f10x_it.h复制到User文件夹下,使用MDK这两个.c 文件已添加。

11. 单击“确定”完成组的创建,然后单击“目标选项”。

12.Target选项c标准库源码分析,勾选User MicroLIB

13.输出选项

14.列表选项(可选)

15.C/C++选项

添加宏定义:STM32F10X_MD,USE_STDPERIPH_DRIVER

其中STM32F10X_MD根据自己板子的属性,应该对应启动下的xx_md.s。

添加包含路径:如下图

单击“确定”。

16. 编辑Usermain.c 文件并添加以下内容。

#include "stm32f10x."
int main(void)
{
	
	return 0;
}

17. 单击重建。 如果没有报告错误,则模板已创建。

3.5.7.3. 使用标准库方法点亮LED。

1.复制My_Template文件夹并重命名为3.stdlib_led

2、打开Project/下的工程文件,修改main.c,粘贴以下内容

#include "stm32f10x.h"
/*---------接线-------------
		PB8--PB15接到LED1--LED8
---------------------------*/
void led_flash(void);
void led_init(void);
void delay(void);
int main(void)
{
	led_init();
	led_flash(); //内部时钟8MHz
	
	while(1);
}
void led_init(void)
{
		//GPIO时钟使能
		RCC->APB2ENR = 0x00000008;
		
		//GPIOB设置成推挽输出模式,速度是50MHz
		GPIOB->CRH  = 0x33333333;
}
void led_flash(void)
{
	unsigned int i = 0;
	for(i = 0;iODR = 0x00000000;
			delay();
			GPIOB->ODR = ~GPIOB->ODR;
			delay();
	}
}
void delay(void)
{
	unsigned int i,j;
	for(i = 0;i<1000;i++)
		for(j = 0;j<1000;j++);
}

单击构建。 如果没有错误,就可以烧录到板上并运行结果。

3.5.9_10。 RCC模块标准库全解析1_2

1.一般模块是成对存在的(xxx.c+xxx.h)

一般来说,.h包含宏定义和函数声明,.c包含函数的实现。

2、RCC.c和RCC.h中有宏定义。 为什么不把它们都放在 RCC.h 中?

RCC.h 中放置的宏定义可供 RCC.c 和其他文件使用。 RCC.c 中的宏定义仅在 RCC.c 中使用。 这是一种数据封装,比较合理。

3.RCC.c中的Bitband别名区地址

(1) RCC的位带操作

如果你不记得什么是位带操作,可以看一下这个链接:【ARM】—STM32位带操作总结—简单易懂

我们只需要记住下图的公式即可:

外设位带别名区地址 = 0x42000000(PERIPH_BB_BASE) + 偏移量 * 32 + 位数 * 4

(2)RCC_OFFSET:RCC相对于外设位带基值的偏移量

(3)时钟控制寄存器(RCC_CR)别名区各位的地址

(4) 具有别名区域的时钟配置寄存器(RCC_CFGR)中各位的地址

沿用上面的分析方法直到116行。

4. RCC.c 中的位掩码

位掩码的作用:使用Reset执行|=~操作将某个位清零,然后使用Set使用&操作将某个位设置为1。

默认值:SET–写1,RESET–写0

所以:ReSet—第n位写0,其他位写1,Set—第n位写1,其他位写0。

以CR寄存器的HSEBYP、HSEON、HSITRIM为例。 HSEBYP 复位为 0xFFFBFFFF

其他类似的分析方法。

第193行和第194行有两个预分频器全局变量,我们稍后再看它们的使用情况。

3.5.11.RCC模块标准库全解析3

5. RCC.c 中的函数

(1)voidRCC_DeInit(无效)

功能: 将 RCC 时钟配置重置为默认重置状态。 我平时用的比较少!

(2)voidRCC_HSEConfig(uint32_tRCC_HSE)

功能:配置外部高速振荡器(HSE)

注:如果直接或通过 PLL 使用 HSE 作为系统时钟,则无法关闭 HSE 时钟。

参数: RCC_HSE:指定HSE的新状态。 该参数可以是以下值之一:

(3)assert_param分析

断言称为断言。 C语言中使用assert机制来判断某件事是对还是错。 如果是对的,就忽略它。 如果是错误的,请以某种方式告诉我们(警告错误)让我们知道。 去改变吧。

最常用的断言机制是:断言用于库函数中c标准库源码分析,用于检查用户在调用库函数时传递的参数是否正确。

判断断言表达式是否正确。 如果正确的话就可以正常运行了。 如果不正确,将调用assert_failed函数。 这个函数可以自己编辑。 例如,它可以报告错误:第 xx 行的变量值不正确。

(4)ErrorStatusRCC_WaitForHSEStartUp(void)

功能:等待HSE启动并稳定

返回值:

3.5.12. RCC模块标准库全解析4

5.(5)FlagStatusRCC_GetFlagStatus(uint8_tRCC_FLAG)

功能:检查指定的RCC标志是否置位。

(6)voidRCC_HSICmd(FunctionalStateNewState)

功能: 启用或禁用内部高速振荡器(HSI)。

NewState:恒生指数的新状态。 该参数可以是:启用或禁用。使用最原始的方式向寄存器中的某个位写入值

  *(__IO uint32_t *) CR_HSION_BB = (uint32_t)NewState;

(7)voidRCC_PLLConfig(uint32_tRCC_PLLSource,uint32_tRCC_PLLMul)

功能:配置PLL时钟源和倍频系数。

注:此功能必须仅在 PLL 禁用时使用。

RCC_PLLSource 有 3 个源:RCC_PLLSource_HSI_Div2、RCC_PLLSource_HSE_Div1、RCC_PLLSource_HSE_Div2

RCC_PLLMul:[2,16]

(8)voidRCC_SYSCLKConfig(uint32_tRCC_SYSCLKSource)

功能:配置系统时钟(SYSCLK)

© 版权声明
THE END
喜欢就支持一下吧
点赞250赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容