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

【朱老师课程总结】

第一部分、章节目录

3.5.1.为什么会有标准外设库

3.5.2_3.外设库的结构介绍和之后的学习方法1_2

3.5.4.标准库对硬件信息的封装方式

3.5.5.使用结构体方式访问寄存器的原理

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

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

3.5.9_10.RCC模块的标准库全解析

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

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

3.5.13.RCC模块的标准库全解析5

3.5.14_15.使用库重写时钟设置函数1_2

3.5.16_17.GPIO模块的标准库全解析1_2

3.5.18.使用GPIO库函数来点亮LED

3.5.19.标准库中的面向对象思想    

第二部分、随堂记录

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)文件夹结构和主要文件的作用
在这里插入图片描述

  • _htmresc文件夹内是官方Logo图片,可以删掉。

  • Libraries 文件夹下面有 CMSIS 和 STM32F1xx_StdPeriph_Driver 两个目录,这两个目录包

    含固件库核心的所有子文件夹和文件,是代码移植的重头戏。CMSIS 文件夹存放的是符合 CMSIS 规范的一些文件, Driver 文件夹下是STM32F1 标准外设固件库源码文件和对应的头文件,说白了就是将寄存器封装好的函数接口。
    【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第1张图片

  • Project 文件夹下面有STM32F1xx_StdPeriph_Examples 和STM32F1xx_StdPeriph_Template 两个文件夹,Examples文件夹下是固件示例源码,Template文件夹下是工程模板。这些源码的学习对以后的开发学习非常重要。

  • Utilities 文件夹下就是官方评估板的一些对应源码,可以忽略不看。

  • 根目录中还有一个固件库的帮助文档 stm32f1xx_dsp_stdperiph_lib_um.chm 文件。

(3)最重要的就是Libraries

CMSIS(STM32内部ARM核心相关内容)
    CM3(Cortex-M3)
        CoreSupport(内核相关的一些设置的寄存器集合及其封装)
        DeviceSupport
            ST
                STM32F10x
                    startup(内核相关的一些设置的寄存器集合及其封装)
                    stm32f10x.h(很重要)
                    system_stm32f10x.c
                    system_stm32f10x.h
STM32F10x_StdPeriph_Driver(STM32F1外设驱动
    inc(include,头文件,.h)
    src(source,源文件, .c)

目录树:
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第2张图片
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第3张图片

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

3.5.4.标准库对硬件信息的封装方式

3.5.4.1、寄存器地址的封装
其实就是将寄存器在存储器中的地址,利用宏定义表示出来。(这里的地址是指在位带区内的地址,关于位带区和位带别名区请往后看)

  • 比如:外设总线APB1的基地址为:0x40000000,APB2的基地址为0x40010000
  • 因为APB1和APB2分别有不同的外设设备,所以基地址+偏移量,就可以把寄存器表示出来!
  • TIM5的地址是0x40000C00  = 0x40000000+0x0C00 = APB1基地值+偏移量

【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第4张图片
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第5张图片
3.5.4.2、寄存器位定义的封装
就是对于寄存器每一位的操作,有很多种情况,直接利用宏定义将不同的复制情况进行封装。
比如说,打开HSE时钟时,要操纵寄存器RCC_CR,给第16位赋值1,就可以打开HSE时钟。

【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第6张图片
RCC_TypeDef是个结构体变量,里面封装这RCC的各个寄存器,RCC_BASE是RCC寄存器组的基地址,于是RCC就是一个结构体指针,指向RCC_BASE下的RCC寄存器组。

  • 按照之前:RCC_CR |= 0x00010000;
  • 用标准库:RCC->CR |= CR_HSEON_Set;

道理一样!这样的好处就是更加直观,代码可读性更强!
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第7张图片
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第8张图片
3.5.4.3、外设操作的封装
其实就是很多的函数,比如说配置外部时钟72MHz,需要配置HSE,操纵不同的寄存器,在SPL中,这些工作都给做好了!
比如说,下面配置HSE的子函数,只要RCC_HSE准备就绪,就可以打开HSE!
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第9张图片

3.5.5.使用结构体方式访问寄存器的原理

(1)之前访问寄存器方式
C语言访问寄存器的本质是C语言访问内存
本质思路:

  • 定义一个指针(临时变量)指向这块内存,然后*p = xx这种方式去解引用指针从而向目标内存中写入内容。

缺陷:

  • 当寄存器多了之后每一个寄存器都要定义一套套路,很麻烦。

(2)解决思路:就是打包,批发式的定义,用结构体(想一下为什么不用数组?)的方式进行打包。
具体做法:
把整个一个模块的所有寄存器(地址是连接的)打包在一个结构体中,每个寄存器对应结构体中的一个元素,然后结构体基地址对应寄存器组的基地址,将来就可以通过结构体的各个元素来访问各个寄存器了。
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第10张图片
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第11张图片
(3)结构体方式来访问寄存器和指针式访问寄存器,本质上其实是一样的,区别是C语言的封装不同。
(4)volatile的作用

  • volatile修饰的变量从CPU寄存器中直接读取数值而不是从备份中读取。备份就是编译器自己拷贝的一个寄存器,它是一个软件寄存器相对于物理芯片内的寄存器来说。这个备份具体多少毫秒刷新一次我也不清楚,但是它被读取的速度相对于进程频繁的读取物理寄存器要快很多,这就是编译器为了效率对程序的优化。
  • 不加volatile,C编译器为了速度会把你的代码自动优化直接从备份中读取
  • 加了volatile就是明确告诉编译器这里不需要你自做聪明的优化给我直读,嵌入式中底板驱动编程尤为重要。

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

程序下载

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

3.5.7.1、分析标准库自带的工程模板
模板在"STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template\MDK-ARM"下,用MDK软件双击project即可打开。
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第12张图片
模板目录:
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第13张图片

  • User文件夹

    存放用户自己写的main.c和用来存放中断服务函数的stm32f10x_it.c
  • StdPeriph_Driver文件夹
    【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第14张图片
    存放\Libraries\STM32F10x_StdPeriph_Driver\src\下的与外设相关的.c文件
  • CMSIS文件夹

    存放时钟配置文件system_stm32f10x.c和内核相关的core_cm3.c
  • STM32_EVAL文件夹
    【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第15张图片
    存放基于STM32100E开发板的一些官方的开发例程。
  • MDK-ARM
    【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第16张图片
    里面放的是启动文件,会在Options中配置上需要哪个文件,比如上图,在Options的宏定义里规定使用STM32F10X_HD_VL,于是只有该选项没有被屏蔽掉!
  • Options中
    【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第17张图片

3.5.7.2、建立自己的模板
1.创建My_Template文件夹
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第18张图片
2.进入My_Template,复制\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\下的CMSIS、STM32F10x_StdPeriph_Driver到My_Template下
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第19张图片

3.创建User、Project、Listings、Output文件夹
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第20张图片

4.打开MDK,创建新Project(名字自定义),存放在My_Template/Project下
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第21张图片
5.选择自己开发板的芯片,这里选择的是“STM32F103C8”,出来的Run-Time页面,直接关闭就行
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第22张图片
6.添加各个文件夹到工程

7.添加启动文件组,启动文件在\My_Template\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm\下,STM32F103C8属于md产品,所以我选择xx_md.s
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第23张图片
8.添加CMSIS组,添加\My_Template\CMSIS\CM3\CoreSupport\下的core_cm3.c和\My_Template\CMSIS\CM3\DeviceSupport\ST\STM32F10x\下的system_stm32f10x.c
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第24张图片
9.添加StdPeriph_Driver组,把\My_Template\STM32F10x_StdPeriph_Driver\src\下的所有文件添加进来
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第25张图片
10.添加User组,在User文件夹下新建一个main.c文件,复制\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template\下的stm32f10x_conf.h、stm32f10x_it.c、stm32f10x_it.h到User文件夹下,通过MDK把这两个.c文件添加进来。
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第26张图片
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第27张图片
11.点OK,完成Group的创建,然后点击Options for Target。
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第28张图片
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第29张图片
12.Target选项,勾选上User MicroLIB
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第30张图片
13.OutPut选项
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第31张图片
14.Listing选项(可选)
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第32张图片
15.C/C++选项
添加宏定义:STM32F10X_MD,USE_STDPERIPH_DRIVER
其中STM32F10X_MD要根据自己的板子的属性,和startup下的xx_md.s对应
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第33张图片
添加include path:如下图
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第34张图片
点OK。
16.编辑User\main.c文件,添加以下内容。

#include "stm32f10x."

int main(void)
{
	
	return 0;
}

17.点击Rebulid,如果不报错,说明模板创建完毕。
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第35张图片 

3.5.7.3、使用标准库方式操作点亮LED
1.复制My_Template文件夹,重命名为3.stdlib_led
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第36张图片
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;i<5;i++)
	{
			GPIOB->ODR = 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++);
}

点击build,没有错误就可以烧录到板子上运行结果了。
 

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中的位带别名区地址
(1)RCC的位带操作
不记得什么叫位带操作可以看一下这个链接:【ARM】---STM32位带操作总结---浅显易懂
我们只要记住下面这张图的公式即可:

【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第37张图片

外设位带别名区地址 = 0x42000000(PERIPH_BB_BASE) + 偏移量*32 + 位数*4
(2)RCC_OFFSET:RCC相对于外设位带区基地值的偏移量


(3)时钟控制寄存器(RCC_CR)的各个位在位带别名区的地址
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第38张图片

  • CR寄存器的基地值和RCC的基地值一样,所以相对于外设位带区基地值偏移量 = RCC_OFFSET + 0x00
  • HSION是CR寄存器的第0位,所以n = 0x00
  • 代入公式,CR_HSION位的位带别名区地址CR_HSION_BB = 0x42000000(PERIPH_BB_BASE) + CR_OFFSET*32 + n*4 = 0x42000000 + 0x21000*32 + 0x00*4 = 0x42672000
  • 其他位的计算方法,和上面一样。PLLON是第24位,即第0x18位,CSSON是第19位,即第0x13位。

【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第39张图片
(4)时钟配置寄存器(RCC_CFGR)的各个位在位带别名区的地址

  • RCC_CFGR的偏移量是0x04个字节
  • USBPRE是RCC_CFGR的第22位,即第0x16位。

【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第40张图片

后面一直到116行都是上面的分析方法。

4.RCC.c中的位掩码
位掩码作用:利用Reset进行|=~运算给某一位清零,然后再Set利用&运算就可以设置某位为1了。

默认:SET--写1,RESET--写0
所以:ReSet---第n位写0,其他位写1,Set---第n位写1,其他位写0.

以CR寄存器的HSEBYP、HSEON、HSITRIM为例。HSEBYP的Reset为0xFFFBFFFF
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第41张图片
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第42张图片
其他的类似分析方法。

在193、194行有两个预分频的全局变量,等用到了再看

 

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

5.RCC.c里面的函数

(1)void RCC_DeInit(void)
作用:将 RCC 时钟配置重置为默认重置状态。平时用的比较少!

(2)void RCC_HSEConfig(uint32_t RCC_HSE)
作用:配置外部高速振荡器(HSE)
Note:如果 HSE 直接使用或通过 PLL 作为系统时钟,则不能关闭HSE时钟
参数:RCC_HSE:指定HSE的新状态。此参数可以是以下值之一:

  •  RCC_HSE_OFF:HSE振荡器关闭
  •  RCC_HSE_ON:HSE振荡器打开
  •  RCC_HSE_Bypass:HSE振荡器旁路外部时钟

(3)assert_param解析
assert叫断言,assert机制是C语言里用来判断一个东西是对的还是错的,并且如果是对的那就直接忽略过去,如果是错的就以某种方式告诉我们(warrning error)让我们去改。
断言机制使用最多的就是:库函数中用断言来检查用户调用该库函数时传参到底对不对
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第43张图片
判断断言表达式是否对,如果对就正常运行,如果不对,就调用assert_failed函数,这个函数可以自己编辑,比如可以报错:xx行有变量值错误
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第44张图片

(4)ErrorStatus RCC_WaitForHSEStartUp(void)
作用:等待 HSE 启动稳定
返回值:

  •  SUCCESS:HSE振荡器稳定并准备使用
  •  ERROR:HSE振荡器尚未准备好

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

5.(5)FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG)
作用:检查指定的 RCC 标志是否设置。
(6)void RCC_HSICmd(FunctionalState NewState)
作用:启用或禁用内部高速振荡器 (HSI)。
NewState: HSI的新状态。此参数可以是:启用或禁用。使用最原始的给寄存器某一位写值的方式

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

(7)void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul) 
作用:配置 PLL 时钟来源和倍频因子。
Note:此功能必须仅在 PLL 被禁用时使用。
RCC_PLLSource有三个来源:RCC_PLLSource_HSI_Div2、RCC_PLLSource_HSE_Div1、RCC_PLLSource_HSE_Div2
RCC_PLLMul:[2,16]
(8)void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource)
作用: 配置系统时钟 (SYSCLK)
参数RCC_SYSCLKSource有三个来源:

  • RCC_SYSCLKSource_HSI
  • RCC_SYSCLKSource_HSE

  • RCC_SYSCLKSource_PLLCLK

3.5.13.RCC模块的标准库全解析5

5.(9)void RCC_HCLKConfig(uint32_t RCC_SYSCLK)
作用:配置AHB 时钟 (HCLK).
参数RCC_SYSCLK:/1,2,4,8,16,64,128,256,512
(10)void RCC_PCLK1Config(uint32_t RCC_HCLK)
作用:配置APB1 时钟 (PCLK1).
参数RCC_HCLK:/1,2,4,8,16
(11)void RCC_PCLK2Config(uint32_t RCC_HCLK)
作用:配置APB2 时钟 (PCLK2).
参数RCC_HCLK:/1,2,4,8,16
(12)void RCC_ITConfig(uint8_t RCC_IT, FunctionalState NewState)
作用:使能或者不使能 RCC中断,传入时钟源,当就绪之后,就使能触发中断。
参数RCC_IT:选择中断源:LSI、LSE、HSI、HSE、PLL
参数NewState:DISABLE / ENABLE
(13)后面依次是RCC_ADCCLKConfig、RCC_LSEConfig、RCC_LSICmd、RCC_RTCCLKConfig、RCC_RTCCLKCmd、RCC_GetClocksFreq......比较简单。

(14)1063行:void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
作用:用于使能AHB外设时钟
可以使能的外设如下图
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第45张图片
(15)void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
作用:用于使能APB2外设时钟
可以使能的外设如下图
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第46张图片
(16)void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)
作用:用于使能APB1外设时钟
可以使能的外设如下图
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第47张图片

3.5.14_15.使用库重写时钟设置函数1_2
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第48张图片

哪一行取消注释,上电后,默认以什么频率工作。
main函数
 

#include "stm32f10x.h"


/*---------接线-------------

		PB8--PB15接到LED1--LED8

---------------------------*/

void led_flash(void);
void led_init(void);
void delay(void);
void Set_SysClockTo72M(void);

int main(void)
{
	led_init();
	led_flash(); 	//内部时钟8MHz
	
	Set_SysClockTo72M();
	led_flash();	外部时钟72MHz
	
	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;i<5;i++)
	{
			GPIOB->ODR = 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++);
}

新建clock.c,放在User下!

#include "stm32f10x.h"

#define PLL_STARTUP_TIMEOUT   ((uint16_t)0x1000)

//等待PLL倍频后输入稳定
ErrorStatus RCC_WaitForPLLStartUp(void)
{
  __IO uint32_t StartUpCounter = 0;
  ErrorStatus status = ERROR;
  FlagStatus PLLStatus = RESET; //没就位
  
  /* Wait till PLL is ready and if Time out is reached exit */
  do
  {
    PLLStatus = RCC_GetFlagStatus(RCC_FLAG_PLLRDY);
    StartUpCounter++;  
  } while((StartUpCounter != PLL_STARTUP_TIMEOUT) && (PLLStatus == RESET));
  
  if (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) != RESET)
  {
    status = SUCCESS;
  }
  else
  {
    status = ERROR;
  }  
  return (status);
}


void Set_SysClockTo72M(void)
{
	ErrorStatus sta = ERROR; //HSE就绪标志位
	
	/*上电复位RCC_CR*/
	 RCC->CR = 0x00000083; 
		
	/*开启外部时钟HSE*/
	RCC_HSEConfig(RCC_HSE_OFF);
	RCC_HSEConfig(RCC_HSE_ON);
	
	//等待HSE稳定	
	sta = RCC_WaitForHSEStartUp();
	
	if(sta == SUCCESS) //HSE准备就绪
	{
		/*启用半周期访问、两个等待状态,当 48MHz < SYSCLK ≤ 72MHz*/
		FLASH->ACR |= 0x10;
		FLASH->ACR &= (~0x03);
		FLASH->ACR |= (0x02);
		
		
		/*选择HSE不分频作为PLL的输入时钟,HSE = 8M,设置PLL倍频系数为9倍*/
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
		
		/*AHB、APB2不分频,APB1二分频*/
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		RCC_PCLK1Config(RCC_HCLK_Div2);
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		/*---开启PLL时钟---*/
		RCC_PLLCmd(ENABLE);
		
		sta = ERROR;
		/*--等待PLL稳定--*/
		sta = RCC_WaitForPLLStartUp();
		
		if(sta == SUCCESS)
		{
			/*---设置PLL为系统时钟来源---*/
			
			RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		}
		else
		{
			while(1);	//PLL配置超时
		}

		/***检查SWS位,等待PLL作为SYSCLK时钟启动成功***/
		while(RCC_GetSYSCLKSource()!=0x08)
		{
		}
	}
	else
	{
		while(1);	//HSE配置超时
	}
}


 

3.5.16_17.GPIO模块的标准库全解析1_2

和RCC分析一样,我们也是着重分析.c和.h文件
1.GPIO的位带操作
和RCC分析一样,就是套公式。
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第49张图片
2.函数
(1)void GPIO_DeInit(GPIO_TypeDef* GPIOx)
作用:将 GPIOx(x=A:G) 外围寄存器降级到其默认重置值
GPIO_TypeDef是一个结构体类型:
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第50张图片
(2)void GPIO_AFIODeInit(void)
作用:将 AFIO外围寄存器降级到其默认重置值
(3)void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
作用:根据参数GPIO_InitStruct来初始化GPIOx的外设
GPIO_InitTypeDef也是一个结构体类型:
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第51张图片

  • 引脚选择:
    【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第52张图片
  • 引脚输出速率(只有输出时需要配置)
    【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第53张图片
  • 引脚输入输出模式
    【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第54张图片
  • 小结一下
    GPIOx有GPIOA--GPIOG这7个端口
    每个端口都有CRL、CRH、IDR、ODR..寄存器
    每个端口都有16个引脚
    每个引脚都可以配置自己的速率和输入输出模式。
    所以在初始化GPIO时,一般都是先配置某个引脚再去选端口。

(4)void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct)
作用:用默认值填充每个GPIO_InitStruct成员。
【STM32单片机学习】第五课:STM32标准外设库(SPL 库)_第55张图片
(5)uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
作用:读取指定的输入端口引脚的值。
返回值:读取的值
(6)uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
作用:读取指定GPIO输入端口的值。
返回值:读取的值
(7)uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
作用:读取指定的输出端口引脚的值。
返回值:读取的值
(8)uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
作用:读取指定GPIO输出端口的值。
返回值:读取的值
(9)void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
作用:给指定端口的指定引脚置1
(10)void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
作用:给指定端口的指定引脚置0
(11)void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
作用:设置或清除选定的数据端口位。和上面重复了!
(12)void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
作用:给端口赋值
(13)void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
作用:锁定GPIO引脚配置寄存器。
(14)void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
作用:AFIO选定指定引脚作为事件输出。
(15)void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) ★
作用:更改指定管脚的映射。
(16)void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)

作用:选择用作EXTI线的GPIO引脚。和中断相关。

3.5.18.使用GPIO库函数来点亮LED

代码下载:下载地址

3.5.19.标准库中的面向对象思想    

3.5.19.1、面向对象介绍
(1)一种编程思想(面向过程、面向对象)
(2)什么是对象
(3)面向对象三大特征:封装、继承、多态
(4)面向对象编程思想和面向对象语言是两码事
3.5.19.2、标准库的面向对象特征
(1)各种数据类型结构体就是一种封装
(2)标准库是为了被复用
(3)GPIO的编程模式是典型的面向对象式编程
典型面向对象的编程模式:
第1步:先构建对象(可以理解为定义一个结构体类型)
第2步:用对象构造实例(可以理解为用结构体类型来定义结构体变量)malloc
第3步:填充实例(其实就是给结构体的各个元素赋值)
第4步:使用实例(其实就是把结构体变量作为参数传给某个函数使用)
第5步:销毁实例(其实就是把前面第2步定义的机构体变量给销毁掉)free

本节课结束!

你可能感兴趣的:(【STM32单片机学习】,标准外设库,STM32,嵌入式开发)