STM32学习笔记
在文章伊始:
请你铭记,任何时候不想学、看不懂其实都是因为学习方法不对
可能是你参考的资料太难,这时候你应该去寻找更基础的资料
可能是你自信心不足,请你相信自己,珍惜自己,重视自己!
file:///C:/Users/admin/Desktop/%E6%80%BB%E4%BD%93%E5%AD%A6%E4%B9%A0%E8%A6%81%E6%B1%82.html
视频&配套的PPT和程序为主要参考
零死角那本书入门其实挺难看懂
数据手册主要用于芯片选型和设计原理图时参考,参考手册主要用于在编程的时候查阅
内部功能图
母线busmatrix
编译出错解决办法
对于error,可以双击则会自动跳到出错位置,在该位置上下几行寻找问题
实在找不到可以右键复制到剪贴板再百度求助
对于warning,如果不影响运行有一些可以忽略(比如定义的变量未使用),而若影响运行则需要同error一样解决
基本概念
keil的理解
Keil是公司名,uVision是集成开发环境(IDE),uVision5是其中最新的一个版本
搭建环境,就是需要编程用的语言和用什么进行编程,用什么进行调试(调试就是找有没有bug,所以调试器的英文叫做debugger)的这几个条件的总和
在我们这儿,就是用keil5提供的软件以汇编语言进行编程,用开发工具进行调试
开发工具包括MDK——支持ARM内核;c51——支持8051内核
软件本身的基础性作用就是把汇编语言编译生成单片机可执行的二进制代码,这样就可以烧写到单片机里面
封装
F103指的是设计的晶圆型号,晶圆经过激光蚀刻(形成上亿的晶体管)每一小块滑下来就是一个MCU【感觉类似AD中的拼版】,晶圆取下来后就需要封装,下图就是双列直插式封装,连接金属引线的过程就是ponding[绑定]
BGA,LQFP是按照外形来划分的封装类型
这个是BGA(底部有点)
这个是LQFP(四周有脚),应用更为广泛
一般来说引脚少,功能少,内存也小,其他各方面均配套降低
图:数据手册中给出的封装使用说明
STM32及ARM
STM32 使用ARM内核CPU
ARM属于一个微控制器,STM32自带了各种常用通信接口,比如 USART、I2C、SPI 等,
1、串口—USART,用于跟跟串口接口的设备通信,比如:USB转串口模块、ESP8266
WIFI、GPS模块,GSM 模块,串口屏、指纹识别模块
2、内部集成电路—I2C,用于跟I2C接口的设备通信,比如:EEPROM、电容屏、陀螺
仪MPU6050、0.96寸OLED模块
3、串行通信接口—SPI,用于跟SPI接口的设备通信,比如:串行FLASH、以太网W5500、音频模块VS1053
4、SDIO、FSMC的超级、I2S、ADC、GPIO
CPU,MCU,嵌入式系统联系与区别
MCU选型
一个原则:花最少的钱,做最多的事
在确定项目需求的情况下,一般按照下面的顺序来选择合适的MCU
1、选择哪种内核的芯片,内核越高意味着功耗也越高
2、选择多少引脚的芯片,引脚多少决定了资源的多少,也影响价格
3、选择多少RAM和FLASH的芯片,FLASH越大,价格越贵
4、还要考虑所选型号采购是否容易,供货是否稳定
存储器概念辨析
存储器:
分类
内存一般指的RAM,SRAM无需刷新,所以速度比DRAM快,SRAM 一般只用于 CPU 内部的高速缓存(Cache),而外部扩展的内存一般使用 DRAM【但是霸道扩展用的SRAM】。
闪存指的是FLASH,容量比ROM大
其中NOR FLASH 一般应用在代码存储的场合
即我们平常所说的从FLASH读取到内存,可以简单粗暴的理解为从NOR FLASH读取到SRAM
SD 卡、U 盘以及固态硬盘是NAND FLASH
机械硬盘:
非易失性存储器种类非常多,半导体类的有 ROM 和 FLASH,而其它的则包括光盘、
软盘及机械硬盘。
一般说的硬盘都是指的机械硬盘,硬盘应当是计算机的“外存”,储存空间 (Storage)指的就是硬盘的容量
DAP及串口ISP
串口isp是成本低的下载方式,现在在F4,7系列已经很少使用,DAP完全可以取代它(下载+调试+仿真)
debugger直译为调试器,但是一般却叫做仿真器
高速版HS——5M,全速版FS——1M
串口,JTAG,SW均是下载模式的国际标准
高速版支持JTAG和SW,全速版仅支持SW【SW和SWD是一样的】
内置ARM的SWJ接口,包含了SW和JTAG接口
注意:ARM【内核】是采用了一种叫做SWJ的接口,包含了SW和JTAG两种接口;DAP【仿真器】是分成两种版本,支持不同接口
可见JTAG包含SWD,SWCLK是时钟线,SWDIO是数据线,NJTRST是复位线
串口【COM口】功能只有下载程序
JTAG,SW可在线调试和硬件仿真
串口中BOOT就是引导的意思,Boot模式设实际指的就是选择启动的起始地址区域,在STM32中存在以下三种模式可供选择,分别为片内Flash、系统内存、片内SRAM
寄存器
可以理解为CPU的零级缓存,它内置于各个IP外设中,是一种用于配置外设功能的存储器,就是一种内存,并且有相对应的地址。
存储器映射
存储器本身没有地址,给存储器分配地址的过程叫存储器映射
寄存器映射
寄存器本身就有地址
给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
如sfr p0=0x80
野火寄存器点亮LED灯教程中避开了寄存器映射,采用对地址进行位操作
偏移地址
它的存在是历史原因所致
把存储单元的实际地址与其所在段的段地址之间的距离称为段内偏移,也称为“有效地址或偏移量”。 亦: 存储单元的实际地址与其所在段的段地址之间的距离。本质其实就是“实际地址与其所在段的段地址之间的距离”
更通俗一点讲,内存中存储数据的方式是:一个存储数据的“实际地址”=段首地址+偏移量,
也可以这样理解:就像我们现实中的“家庭地址”=“小区地址”+“门牌号”
上面的“偏移量”就好比“门牌号”
其实就相当于C++的指针一样啦,指出确切的地址而已……
汇编语言
Heap(堆)
因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长。Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。
Stack(栈)
除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简单说,Stack 是由于函数运行而临时占用的内存区域。
帧(frame)
帧是栈的一部分
C语言
宏定义
条件编译
条件编译是一种宏定义,故有#,它的目的就是防止函数二次定义
最常用的方式就是
#ifndefine //如果未定义此函数
#define //则定义它
续行符
语法:“\”
表示续行符的下一行与续行符所在的代码是连接起来
应用续行符的时候要注意,在“\”后面不能有任何字符(包括注释、空格),只能直接
回车
指针
指针意思是通过它能找到以它为地址的内存单元
作个比喻,假设将电脑存储器当成一本书,一张内容记录了某个页码加上行号的便利贴,可以被当成是一个指向特定页面的指针
枚举
从同一类型的数据中抽出来一部分,就是枚举
Enum
结构体
把不同类型的数据重组在一块
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。
init为初始化函数
浅析C语言之uint8_t / uint16_t / uint32_t /uint64_t
常用的结构体使用方法是:
1.在开头typedef struct
2.在后面分别对结构体中的数据类型进行细化定义
@defgroup
Static
在函数的返回类型前加上static,就是静态函数。其特性如下:
静态函数只能在声明它的文件中可见,其他文件不能引用该函数
不同的文件可以使用相同名字的静态函数,互不影响
变量
基本数据类型
数组
易变/修饰 只读变量
编译器有可能会对没有执行程序的变量进行优化
volatile 表示易变的变量,防止编译器优化
参数与变量区别
参数也是变量。变量很多种,参数变量是其中一种。
普通变量是你自己初始化的,参数变量是程序自动为你初始化的(就在你调用函数的一瞬间)
追问
那带参函数是什么意思
它与不带参数的有什么区别
追答
从道理上讲,既然有 带参数,那就有不带参数的情况。
货车可以装货,也可以不装货啊,尽管货车是用来装货的。
从技术讲,定义一个函数实际上是定义了一段代码,一段可以重复利用的代码
这段代码只有入口和出口,就像工厂的生产线,你送进去棉花,出来就是毛线。
定义一个函数
int plus(int a, int b) { retuan a + b};
定义一个函数
成品 生产(原料 棉花) <---- 成品代表int 生产单表plus 原料代表int 棉花代表 a。
{
return (棉花在生产线上经过七七四十九关处理变成——毛线);
}
有时候函数没有参数噻,这个就跟自然数为什么要发明一个0呢,一样道理。
函数这段代码有一个专门传入参数的地方,如果没有参数,这个地方就没用而已。
简单来说,形参就是无赋值,而实参就是有赋值
总线
I2C总线
SCL时钟控制线 SDA串行数据线
Serial clock data
固件库
中英文对照
Firmware library
模板:template
bsp:Board support package板级支持包;就是只针对特定的开发板
为什么用固件库
固件库就是底层驱动程序,用来操作寄存器
Doxygen
/**
* @brief 初始化控制LED的IO
* @param 无
* @retval 无
*/
这一段是代码书写的规范,名为“Doxygen”,如果在工程文件中按照这种规范去注释,可
以使用 Doxygen 软件自动根据注释生成帮助文档。
函数简介(@brief)、参数说明(\'@param\')和返回值说明(@retval)四部分
文件类型
.s是汇编语言
.h是头文件
.h中一般放的是宏定义以及同名.c文件中定义的变量、数组、函数的声明,需要让.c外部使用的声明。
.c文件一般放的是变量、数组、函数的具体定义。
用法
.c文件,以c为扩展名,一般存储具体功能的实现。
.h文件,称为头文件,一般存储类型的定义(宏定义),函数的声明等。通常,头文件被.c文件包含,使用#include 语句。但值得注意的是,这只是一种约定,而非强制。
Ps:为防止.h被不同.c文件调用而导致其中宏定义被多次定义【类似一个函数可以被声明多次,但是只能被定义一次】,在.h文件中宏定义时一定要用条件编译
文件作用
1-汇编编写的启动文件
startup_stm32f10x_hd.s:设置堆栈指针、设置PC指针、初始化中断向量表、配置系统时钟、使用库函数_main最终去到C的世界
2-时钟配置文件
system_stm32f10x.c:把外部时钟HSE=8M,经过PLL倍频为72M。
3-外设相关的
stm32f10x.h:实现了内核之外的外设的寄存器映射
本质还是头文件
但是一般来说由于功能重要,故在main.c中必须要定义
xxx:GPIO、USRAT、I2C、SPI、FSMC
stm32f10x_xx.c:外设的驱动函数库文件
stm32f10x_xx.h:存放外设的初始化结构体,外设初始化结构体成员的参数列表,外设固件库函数的声明
4-内核相关的
CMSIS - Cortex 微控制器软件接口标准
core_cm3.h:实现了内核里面外设的寄存器映射
core_cm3.c:内核外设的驱动固件库
NVIC(嵌套向量中断控制器)、SysTick(系统滴答定时器)
misc.h
misc.c
5-头文件的配置文件
stm32f10x_conf.h:头文件的头文件
//stm32f10x_usart.h
//stm32f10x_i2c.h
//stm32f10x_spi.h
//stm32f10x_adc.h
//stm32f10x_fsmc.h
......
6-专门存放中断服务函数的C文件
stm32f10x_it.c
stm32f10x_it.h
中断服务函数可以随意放在其他的地方,并不是一定要放在stm32f10x_it.c
固件库调用逻辑
STARTUP文件启动[汇编语言,效率高]
调用CMSIS中的core_cm3.c[ARM内核相关程序]以及system_stm32f10x.c来配置时钟
使用FWLib添加固件库来关联main.c
在头文件main.c中对固件库进行调用
Ps:在main.c前声明stm32f10x.h就可以调用这个库文件[其中对所有功能配置寄存器的地址进行了定义,之后就可以通过定义名来操作地址]
由于我们基本的函数执行均在main.c中完成,所以编写头文件的过程一般称作初始化xxx,因为头文件里面的内容是不需要全部调用的,而.c文件中如果有变量定义了而未进行使用,则会warning
野火新建工程文件存放方式
Fwlib-Template\Libraries\CMSIS\startup
Fwlib-Template\Libraries\CMSIS\core_cm3.c+stm32f10x.c
Fwlib-Template\Libraries\STM32F10x_StdPeriph_Driver\inc(库.h)+src(函数.c)
Fwlib-Template\User\main.c+stm32f103_it.c
洋桃新建工程文件存放方式
CMSIS\core_cm3.c+stm32f10x.c
Lib\STM32F10x_StdPeriph_Driver\inc(库.h)+src(函数.c)
startup
user\main.c+stm32f103_it.c
CMSIS:微控制器软件接口标准
———————————
GPIO
GPIO其实是厂商的说辞,本质还是I/O端口
翻转 toggle
中英文对照
PWM脉冲宽度调制
LSB,英文 least significant bit,最低有效位
MSB,英文 most significant bit,最高有效位
Alias,别名
引脚与I/O端口
I/O端口是引脚的一大类使用方式
引脚有3个要注意的地方:
- 大部分引脚也会有复用
- 少部分引脚可以自己映射(重定义)
- 大部分引脚兼容5V
GPIO中所用到的寄存器
CR:配置寄存器
DR:数据寄存器
每个GPI/O端口有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH):LOW代表低电平,HIGH代表高电平方向控制
两个32位数据寄存器 (GPIOx_IDR和GPIOx_ODR):input output电平控制
一个32位置位/复位寄存器(GPIOx_BSRR):B代表bit set reset
一个16位复位寄存器(GPIOx_BRR)
一个32位锁定寄存器(GPIOx_LCKR):lock
GPIO的8种输入方式
翻转速度指的是方波脉冲翻转速度
模拟输入即ADC输入
模拟和浮空都是断开上下拉电阻和输出端(绿色放大器),只有输入(黄色放大器)只不过输入信号不同
上拉/下拉的目的是保持端口悬空时的电平状态【上拉/下拉的电阻很大,故不会影响实际输入高/低电平】
推挽电流较大,有驱动外设工作的能力
开漏电流较小,只是一个输出电流
GPIO的分组
C51中以1.1,1.2;3.1,3.2这样的方式分组
STM32中以PA,PB,PC这样的方式分组,且并非所有端口都会列出来(有一些是内部自己使用了)
GPIO初始化配置函数解析
延时函数代码解析
1.void Delay(__IO uint32_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}//具体时间与#define SOFT_DELAY Delay(0xFFFFF)以及时钟频率有关
初始化配置代码解析
1 void LED_GPIO_Config(void)
2 {
3 /*定义一个 GPIO_InitTypeDef 类型的结构体*/
4 GPIO_InitTypeDef GPIO_InitStructure;
5
6 /*开启 LED 相关的 GPIO 外设时钟*/
7 RCC_APB2PeriphClockCmd( LED1_GPIO_CLK|
8 LED2_GPIO_CLK|
9 LED3_GPIO_CLK, ENABLE);
10 /*选择要控制的 GPIO 引脚*/
11 GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
12
13 /*设置引脚模式为通用推挽输出*/
14 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
15
16 /*设置引脚速率为 50MHz */
17 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
18
19 /*调用库函数,初始化 GPIO*/
20 GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
21
22 /*选择要控制的 GPIO 引脚*/
23 GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
24
25 /*调用库函数,初始化 GPIO*/
26 GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
27
28 /*选择要控制的 GPIO 引脚*/
29 GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;
30
31 /*调用库函数,初始化 GPIOF*/
32 GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);
33
34 /* 关闭所有 led 灯 */
35 GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
36
37 /* 关闭所有 led 灯 */
38 GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
39
40 /* 关闭所有 led 灯 */
41 GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
42 }
附件:上述代码原图
位带操作(偏重寄存器,可忽略)
计算机位的理解
我们常常看到 32位 CPU、64位 CPU 这样的名称,其实指的就是寄存器的大小
32位是说计算机一次最多能够处理32位数据,1byte=8bit,也就是说32位就是4字节
不同位支持的运行内存不同。
每个存储单元可以存放八个二进制位,即一个零到二百五十五之间的整数、一个字母或一个标点符号等,叫做一个字节),即1字节=8 位。存储器的容量就是以字节为基本单位的,每个单元都有唯一的序号,叫做地址。中央处理器凭借地址,准确地操纵着每个单元,处理数据。
Eg:比如外设外带区的地址为:0X40000000~0X40100000,大小就为 1MB
因为1在第6位,即16^5=2^20,单位byte,1MB=2^10KB,1KB=2^10byte
指针的寻址能力是4byte,段地址+偏移地址,可以通过一个地址找到整个寄存器
但是一个地址仍然对应着的是一个字节
参考手册原文:
寻址本身也是一种操作,所以寻址时时必须对整个32位寄存器进行操作
位操作和位带操作
位操作就是可以单独的对一个比特位【地址最末位】读和写
51单片机中通过关键字 sbit 来实现位定义
51 单片机里面并不是所有的寄存器都是可以比特位操作,有些寄存器还是得字节操作,比如 SBUF
STM32 没有这样的关键字,但是通过访问位带别名区可以实现对所有寄存器位操作
在 STM32 中,有两个特定的地方,一个是 SRAM 区的最低 1MB 空间,另一个是外设区最低 1MB 空间,称为位带,位带经过膨胀后得到位带别名区
对位带别名区的操作称为位带操作
RCC
RCC :reset clock control 复位和时钟控制器【注意该控制器有时钟和复位两种功能】
时钟树
STM32中的高速时钟是给内核和外设用的,而低速时钟是给RTC和IWDG用的
高速单位一般是MHz,低速是KHz
HSE
高速外部时钟信号HSE(High Speed External Clock signal)
来源:无源晶振(4-16M),通常使用8M。经过9倍频后得到72M
有源和无源区别:
有源晶振需要电源,但不需要附加的电容。它内部含有起振器。通常接到CPU的一根引脚即可【OSC_IN】。无源晶振不需要电源,但常需要和电容配合使用,它通常接到CPU的两根引脚上。【OSC_IN 和 OSC_OUT】
目的:产生时钟信号(一般是方波)
控制:RCC_CR 时钟控制寄存器的位16:HSEON控制使能;位17:HSERDY控制结束
时钟一般都有一个位使能,一个位读取状态(使能是否成功)
时钟控制寄存器 CR :control register
HSEON:外部高速时钟使能 (External high-speed clock enable)
HSERDY:外部高速时钟就绪标志 (External high-speed clock ready flag)
HSI
HSI:Low Speed Internal Clock signal,高速的内部时钟。
来源:芯片内部,大小为8M,当HSE故障时,系统时钟会自动切换到HSI,直到HSE启动成功。但是内部时钟会产生温漂
控制: RCC_CR 时钟控制寄存器的位0:HSION控制使能;位1:HSIRDY控制结束
锁相环时钟
分频prescaler
倍频PLL
锁相环时钟:PLLCLK
来源:(HSI/2、HSE)经过倍频所得 。
控制:CFGR:PLLXTPRE、PLLMUL
注意:PLL时钟源头使用HIS/2的时候,PLLMUL最大只能是16,这个时候PLLCLK最大只能是64M,小于ST官方推荐的最大时钟72M。
时钟配置寄存器 CFGR :configuration register
- PLLMUL:PLL倍频系数 (PLL multiplication factor)
- PLLXTPRE:HSE分频器作为PLL输入 (HSE divider for PLL entry)PRE想说的是预分频
- PLLSRC:PLL输入时钟源 (PLL entry clock source)
备份域控制寄存器BDCR:Backup Domain Control Register
控制/状态寄存器 CSR:control/status register
系统时钟
锁相环时钟:SYSCLK,最高为72M(ST官方推荐的)
来源:HSI、HSE、PLLCLK。
控制:CFGR:SW系统时钟切换
注意:通常的配置是SYSCLK=PLLCLK=72M。
系统时钟是配置的总线AHB,APB1,APB2的时钟,外设时钟在具体外设.c文件中单独配置
HCLK时钟
HCLK:AHB高速总线时钟,速度最高为72M。为AHB总线的外设提供时钟、为Cortex系统定时器提供时钟(SysTick)、为内核提供时钟(FCLK)。
AHB:advanced high-performance bus。
来源:系统时钟分频得到,一般设置HCLK=SYSCLK=72M
控制: CFGR:HPRE
PCLK1时钟
PCLK1:APB1低速总线时钟,最高为36M。为APB1总线的外设提供时钟。2倍频之后则为APB1总线的定时器2-7提供时钟,最大为72M。
来源:HCLK分频得到,一般配置PCLK1=HCLK/2=36M
控制: RCC_CFGR 时钟配置寄存器的PPRE1位
PCLK2时钟
PCLK2:APB2高速总线时钟,最高为72M。为APB1总线的外设提供时钟。为APB1总线的定时器1和8提供时钟,最大为72M。
来源:HCLK分频得到,一般配置PCLK1=HCLK=72M
控制: RCC_CFGR 时钟配置寄存器的PPRE2位
时钟配置函数
其他时钟
RTC时钟
RTC时钟:为芯片内部的RTC外设提供时钟。
来源:HSE_RTC(HSE分频得到)、LSE(外部32.768KHZ的晶体提供)、LSI(32KHZ)。
控制: RCC备份域控制寄存器RCC_BDCR:RTCSEL位控制
独立看门狗时钟
IWDGCLK,由LSI提供
MCO时钟输出
MCO:microcontroller clock output,微控制器时钟输出引脚,由PA8复用所得
来源:PLLCLK/2,HSE、HSI、SYSCLK
控制:CRGR:MCO
主要作用是可以对外提供时钟,相当于一个有源晶振,可以用来监控系统时钟
中断
Interrupt
对中断的理解
有关单片机中断系统的概念:什么是中断,我们从一个生活中的例程引入。你正在家中看书,突然电话铃响了,你放下书本,去接电话,和来电话的人交谈,然后放下电话,回来继续看你的书。这就是生活中的“中断”的现象,就是正常的工作过程被外部的事件打断了。仔细研究一下生活中的中断,对于我们学习单片机的中断也很有好处。
第一、什么可经引起中断,生活中很多事件能引起中断:有人按了门铃了,电话铃响了,你的闹钟闹响了,你烧的水开了….等等诸如此类的事件,我们把能引起中断的称之为中断源。
第二、中断的嵌套与优先级处理:设想一下,我们正在看书,电话铃响了,同时又有人按了门铃,你该先做那样呢?如果你正是在等一个很重要的电话,你一般不会去理会门铃的,而反之,你正在等一个重要的客人,则可能就不会去理会电话了。如果不是这两者(即不等电话,也不是等人上门),你可能会按你常常的习惯去处理。总之这里存在一个优先级的问题,单片机中也是如此,也有优先级的问题。优先级的问题不仅仅发生在两个中断同时产生的情况,也发生在一个中断已产生,又有一个中断产生的情况,比如你正接电话,有人按门铃的情况,或你正开门与人交谈,又有电话响了情况。考虑一下我们会怎么办吧。
第三、中断的响应过程:当有事件产生,进入中断之前我们必须先记住现在看书的第几页了,或拿一个书签放在当前页的位置,然后去处理不一样的事情(因为处理完了,我们还要回来继续看书):电话铃响我们要到放电话的地方去,门铃响我们要到门那边去,也说是不一样的中断,我们要在不一样的地点处理,而这个地点常常还是固定的。计算机中也是采用的这种办法,多个中断源,每个中断产生后都到一个固定的地方去找处理这个中断的程序,当然在去之前首先要保存下面将执行的指令的地址,以便处理完中断后回到原来的地方继续往下执行程序。具体地说,中断响应能分为以下几个步骤:
1、保护断点,即保存下一将要执行的指令的地址,就是把这个地址送入堆栈。
2、寻找中断入口,根据不一样的中断源所产生的中断,查找不一样的入口地址。
3、执行中断处理程序。
4、中断返回:执行完中断指令后,就从中断处返回到主程序,继续执行。
中断类型
STM32具有十分强大的中断系统,将中断分为两个类型:系统异常&外部中断
并将所有中断通过一个表编排起来,称之为stm32中断向量表
系统异常体现在内核水平,故也叫内核异常(10个)
外部中断体现在外设水平(60个)
内核异常不能够被打断,不能被设置优先级(也就是说优先级是凌驾于外部中断之上的)。常见的内核异常有以下几种:复位(reset),不可屏蔽中断(NMI),硬错误(Hardfault),其他的也可以在表上找到。
外部中断包含定时器中断,I2C,SPI等所有的外设中断,可配置优先级
一般情况下中断和异常可以混用,不做区分
注意中断一定是终止内核的工作
NVIC
NVIC:嵌套向量中断控制器,属于内核外设,管理着包括内核和片上所有外设的中断相关的功能(除了SYSTICK之外)
NVIC是主要的中断控制器,外部内部中断均能处理。它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3内核里面的 NVIC进行裁剪,把不需要的部分去掉,所以说 STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一个子集。
两个重要的库文件:core_cm3.h和misc.h
异常服务例程(ESR):Exception service routine
当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行
中断优先级寄存器IP:interrupt priority
优先级
分类
分成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占 抢占优先级低的 优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高
优先级设定:NVIC->IPRx(ARM中将IP定义为8位,而stm32仅仅使用了高四位)
分组
优先级分组:SCB->AIRCR:PRIGROUP[10:8]
系统控制块(System Control Block):也是内核里面的外设
应用程序中断及复位控制寄存器 AIRCR:Application Interrupt and Reset Control register
PRIGROUP:PriorityGroup
第0组:所有的4位都有来表示响应优先级,能够配置16种不同的响应优先级。中断优先级则都相同。
第1组:最高一位用来配置抢占优先级,剩余三位用来表示响应优先级。那么就有两种不同的抢占优先级(0和1)和8种不同的响应优先级(0~7)。
第2组:高两位用来配置抢占优先级,低位用来配置响应优先级。那么两种优先级就各有4种。
第3组:高三位用来配置抢占优先级,低位用来配置响应优先级。有8种抢占优先级和2种相应优先级。
第4组:所有位都用来配置抢占优先级,即有16种抢占优先级,没有响应属性
这5种不同的分配方式,根据项目的实际需求来配置。
配置的API如下:
NVIC_PriorityGroupConfig();
【API(Application Programming Interface,应用程序接口):是一些预先定义的函数】
其中括号内可以输入以下一个参数,代表不同的分配方式:
NVIC_PriorityGroup_0
NVIC_PriorityGroup_1
NVIC_PriorityGroup_2
NVIC_PriorityGroup_3
NVIC_PriorityGroup_4
中断编程
1-使能中断请求
外设相应寄存器发出使能中断请求,NVIC中的中断使能寄存器接收请求,产生中断
2-配置中断优先级分组
使用固件库函数NVIC_PriorityGroupConfig()
3-配置NVIC寄存器,初始化NVIC_InitTypeDef
NVIC_IRQChannel:中断源
NVIC_IRQChannelPreemptionPriority:抢占优先级
NVIC_IRQChannelSubPriority:子优先级
NVIC_IRQChannelCmd:使能或者失能
4-编写中断服务函数
中断服务函数名要怎么写?写错了怎么办?
在启动文件中有中断服务函数,并且进行了弱定义【weak】,即其他地方定义的函数先执行,若写错了无法执行则执行此中断服务函数,故编译器不会因为中断服务函数错误而报错
中断服务函数要写在什么地方?
专门存放中断服务函数的C文件
stm32f10x_it.c
stm32f10x_it.h
EXTI
EXTI(External interrupt/event controller)—外部中断/事件控制器
负责管理所有外部中断/事件,然后交给NVIC处理
输入
输入线总共有多少,具体是哪一些?
EXTI 控制器有 19 个中断/事件输入线【第20个是以太网唤醒事件(只适用互联型)
互联型指的是F105,107等系列】,其中,EXTI0 至 EXTI15 用于 GPIO
当中断被触发后,程序要马上跳转到中断处理函数去执行中断操作,这个函数在工程创建时默认时没有的,需要手动添加。这个函数在startup_stm32f10x_hd.s中
通过配置哪个寄存器来选择?
外部中断配置寄存器(AFIO_EXTICR)
AFIO_EXTICR寄存器组,总共有4 个,EXTICR1~ EXTICR 4。每个 EXTICR只用了其低16 位。
AFIO
stm32的引脚有两种用途:GPIO(general purpose io通用功能)和AFIO(alternate function io复用功能)
对于一些引脚(视芯片而定),这两种用途都没有,如在64脚产品中,OSC_IN/OSC_OUT【OSC是外接石英晶体组成的振荡器,供给单片机时钟信号】与作为GPIO端口的PD0/PD1共用一样的引脚,而在100、144引脚产品中,这四个功能各有引脚与之对应,不互相冲突,所以OSC_IN/OSC_OUT既不作GPIO也不作AFIO
EXTI寄存器
- EXTI本身的寄存器
IMR:中断屏蔽寄存器
这是一个 32 寄存器。但是只有前 19 位有效。当位 x 设置为1 时,则开启这个线上的中断,否则关闭该线上的中断。
EMR:事件屏蔽寄存器
同IMR ,只是该寄存器是针对事件的屏蔽和开启。
RTSR:上升沿触发选择寄存器
该寄存器同IMR ,也是一个32为的寄存器,只有前 19位有效。位 x 对应线x 上的上升沿触发,如果设置为 1 ,则是允许上升沿触发中断/ 事件。否则,不允许。
FTSR:下降沿触发选择寄存器
同 PTSR,不过这个寄存器是设置下降沿的。下降沿和上升沿可以被同时设置,这样就变成了任意电平触发了。
SWIER:软件中断事件寄存器
通过向该寄存器的位x 写入 1 ,在未设置 IMR 和EMR的时候,将设置PR中相应位挂起。如果设置了IMR 和EMR时将产生一次中断。被设置的SWIER位,将会在PR中的对应位清除后清除。
PR:挂起寄存器
0 ,表示对应线上没有发生触发请求。
1,表示外部中断线上发生了选择的边沿事件。通过向该寄存器的对应位写入 1 可以清除该位。
在中断服务函数里面经常会要向该寄存器的对应位写1 来清除中断请求。
EXTI功能框图
GPIO中断实验
编程
1-初始化要连接到EXTI的GPIO
2-初始化EXTI用于产生中断/事件
3-初始化NVIC,用于处理中断
4-编写中断服务函数
5-main函数
SYSTICK:嘀嗒定时器
中英文对照
Tick:发出嘀嗒声
DMA
中英文对照
Memory:存储器 Peripheral:外设 Configuration:配置
SRC:(source):源头 DST(destination):目的地
DIR:(direction):方向
DMA_CPARx(DMA channel x-Peripheral address register):DMA通道x外设地址寄存器(x=1...7)
理解为DMA的第x个通道的外设地址寄存器
同理
DMA_CCARx(DMA channel x-Configuration address register):DMA通道x配置地址寄存器(x=1...7)
PINC:外设地址增量模式 (Peripheral increment mode)
DMA_CNDTRx:(DMA channel x-Number of data to transfer register)DMA通道x(数据)传输数量寄存器(x = 1…7)
DMA配置
DMA(Direct Memory Access,直接存储器访问)用来提供在外设【Peripheral】和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。
数据分为常量和变量,常量放在FLASH中,CPU通过ICode总线读取
变量放在SRAM中,CPU通过DCode总线读取
数据还可被DMA总线读取
取数时经过总线矩阵仲裁,决定哪个总线读取
同时,不同外设之间还可以通过DMA互相读取数据
ADC
中英文对照
ADC-DR :Analog-to-Digital Converter-Data Rigister模拟-数字转换器 数据寄存器
CR:Configuration register配置寄存器
WDG
RTC
RTC:Real_Time Clock 实时时钟
32.768kHz是对应1秒
通讯功能
通信基本概念
串行与并行
串行信号:USART,I2C,SPI,W500
并行信号:SDIO,FSMC(16位并行),W5100
全双工、半双工&单工
全双工:USART,SPI RX,TX同时进行
半双工:SPI(全/半均可)
单工:I2C
注意很多通讯接口使用哪种不是唯一确定的
同步通讯&异步通讯
同步通讯:
异步通讯:
把时钟信号带入特性方程得到数据状态方程
通讯速率
Bitrate—比特率:每秒钟传输的二进制位数,单位为比特每秒(bit/s)
Baudrate—波特率:表示每秒钟传输的码元个数
因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示
比特率
USART
中英文对照
TX transmit 传送
RX receive接收
串口通讯协议
分层
物理层规定我们用嘴巴还是用肢体来交流(硬件部分)
协议层则规定我们用中文还是英文来交流(软件部分)
物理层标准
RS232&RS485标准
可见传输速率差别很大
一般直接从单片机中出来的电平都是TTL电平
51单片机中是5V,stm32中是3.3V
RS232标准串口主要用于工业设备直接通信,因为它的电平差值30V,容错能力很强
电平转换芯片一般有MAX3232(这个就是我们电路板上面所使用的),SP3232
USB转串口
USB常用TTL电平,串口常用电平RS232
1、USB转串口主要用于设备跟电脑通信
2、电平转换芯片一般有CH340、PL2303、CP2102、FT232
3、使用的时候电脑端需要安装电平转换芯片的驱动
原生的串口到串口
TTL到TTL
1、原生的串口通信主要是控制器跟串口的设备或者传感器通信,不需要经过电平转换芯片来转换电平,直接就用TTL电平通信
2、GPS模块、GSM模块、串口转WIFI模块、HC04蓝牙模块
协议层标准
协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准
起始位:由1个逻辑 0 的数据位表示
结束位:由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示
有效数据:在起始位后紧接着的就是有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 位长
STM32串口功能框图讲解
引脚
均为GPIO引脚
TX:数据发送
RX:是数据接收
SCLK:时钟,仅同步通信时使用(而我们一般都是用的异步,故不用看)
nRTS:请求发送(Request To Send)
nCTS:允许发送(Clear To Send)
注意串口1挂载到APB2,而其他的挂载到APB1,所以对应时钟也不同
对于可复用的引脚,在初始化时可以通过AFIO重映射配置成所需功能引脚
数据寄存器
USART_DR:9位有效,包含一个发送数据寄存器TDR和一个接收数据寄存器RDR。一个地址对应了两个物理内存。
控制器
数据发送&接受状态和控制寄存器
USART_CR1:M,0:8bit,1:9bit
USART_CR2:STOP
USART_CR1:PCE(Parity control enable检验控制使能)、PS(Parity Selection检验选择)、PEIE(PE中断使能)
USART_SR :PE(Parity error校验错误)
USART_CR1:UE(USART enable)、TE(transmitter enable)、RE(receiver enable)
编程时要令UE=1,TE=1
USART_SR:TXE,Transmit data register empty发送数据寄存器空
USART_CR1:TXEIE发送缓冲区空中断使能
USART_SR:TC,Transmission complete发送完成
USART_CR1:TCIE发送完成中断使能
USART_SR:RXNE,Read data register not empty读数据寄存器非空
USART_CR1:RXNEIE接收缓冲区非空中断使能
波特率
USART_BRR:波特率寄存器
USARTDIV:无符号的定点数
FCK:串口的时钟,注意区分APB2和APB1两条总线
常用波特率:115200 9600 4800
写入时4位精度1/16
Eg:USART:USART1,时钟为72M
波特率:115200
代码讲解
DeInit(default init) 默认初始化:将所有寄存器复位成初始时状态
cmd是command的缩写.即命令提示符(CMD)
中断接收和发送
串口控制RGB灯亮灭
串口初始化结构体
SPI
串行外设接口
Serial peripheral interface
CAN
Controller Area Network
USB
CRC