一、基础知识点
1、课程体系介绍
单片机概述+arm体系结构+STM32开发环境搭建
STM32-GPIO编程-点亮世界的那盏灯
STM32-USART串口应用+SPI+液晶屏
STM32-中断系统
STM32-时钟系统
STM32-ADC + DMA
温湿度传感器-DHT11
2.如何学习单片机课程
多听理论、多理解、有问题及时提问
自己多扩展、多动手操作
开发环境搭建
Keil 单片机集成开发环境 - 代码编写、编译、调试、下载
STM32CubeMX ST公司提供的一款快速开发STM32的一款工程搭建软件
STlinkDriver 我们使用的下载器是STlink_V2,同样需要STlink的驱动,从ST官网上也是可以找到的
安装注意事项:
1)安装路径一定不要出现中文,你电脑的用户如果是中文也是可能出现问题的
2)关闭防火墙和所有杀毒软件(注册机)
3)安装时候使用右键以管理员身份安装!!!
4)keil使用注册机的时候一定要选择ARM,STM32内核架构就是ARM。
5)如果你使用的是最新版本,安装手册上CubeMX安装之后,需要的固件包可以联网在线下载
6)安装StLink的驱动时,先插上STlink再运行安装程序。
3.课程回顾------嵌入式相关职业
嵌入式软件工程师、嵌入式硬件工程师、QT/C++、单片机开发工程师、Linux应用层开发、 Linux底层开发(ARM\驱动开发)
C语言基础、C及Linux高级、数据结构
IO进程、网络编程、网络高级
QT/C++
STM32课程
Linux底层开发
4.硬件基础
学到什么程度:
具有基本的电子电路知识,基本的电子标号认识、会看原理图、知道PCB图、欧姆定律I=u/r
电压:
电压(voltage),也被称作电势差或电位差,是衡量单位电荷在静电场中由于电势不同所产生的能量差的物理量。电压在某点至另一点的大小等于单位正电荷因受电场力作用从某点移动到另一点所做的功,电压的方向规定为从高电位指向低电位的方向。电压的国际单位制为伏特(V,简称伏),常用的单位还有毫伏(mV)、微伏(μV)、千伏(kV)等。此概念与水位高低所造成的水压相似。需要指出,"电压"一词一般只用于电路当中,"电势差"和"电位差"则普遍应用于一切电现象当中。
电流:
电流的强弱用电流强度来描述,电流强度是单位时间内通过导体某一横截面的电荷量,简称电流,用I表示。
电阻:
电阻器(Resistor)在日常生活中一般直接称为电阻。是一个限流元件,将电阻接在电路中后,电阻器的阻值是固定的一般是两个引脚,它可限制通过它所连支路的电流大小。阻值不能改变的称为固定电阻器。阻值可变的称为电位器或可变电阻器。理想的电阻器是线性的,即通过电阻器的瞬时电流与外加瞬时电压成正比。用于分压的可变电阻器。在裸露的电阻体上,紧压着一至两个可移金属触点。触点位置确定电阻体任一端与触点间的阻值。
电容:
两个相互靠近的导体,中间夹一层不导电的绝缘介质,这就构成了电容器。当电容器的两个极板之间加上电压时,电容器就会储存电荷。电容器的电容量在数值上等于一个导电极板上的电荷量与两个极板之间的电压之比。电容器的电容量的基本单位是法拉(F)。在电路图中通常用字母C表示电容元件。
5、什么是单片机
5.1 单片机简介
单片机是单片微型计算机的简称,Mcu是Microcontroller的简称,也就是嵌入式微控制器。采用集成电路技术将具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、定时器/计时器、多种I/O口和中断系统等功能集成到一块硅片上。可以说单片机就是一个小而完善的微型计算机系统。
5.2 单片机型号
51单片机:STC89C51 宏晶科技 STC AT89C51
32单片机:STM32 意法半导体 ST
32位处理器- 处理数据宽度是32位的
处理器位数:CPU单次运算的最大处理数据的位数
6.STM32介绍
6.1 简介
STM32是意法半导体公司生成一款32位的微控制器。
6.2 STM32的优势
产品型号丰富,可选择性强;
运算速度快,功耗低;
处理器外设接口丰富;
库函数开发体系学习资料多(可以抛开寄存器),应用广泛。
6.3 STM32的应用
可穿戴、物联网、无人机、工业控制、医疗电子、汽车电子、电力系统、石油系统、燃气系统
6.4 命名规范
6.5 开发套件介绍
设备
核心板 - 处理器 + 无线通信模块(NB-IOT、Wifi、Zigbee)
底板 - 承载各种外设接口 如传感器扩展口、按键、LED灯、通信接口MiniUSB、五向按键等。
配套模块
传感器 :
执行器 :蜂鸣器、风扇、电灯、继电器
7、ARM体系结构
STM32G030采用ARM Cortex-M0+内核架构
面试题:谈谈你对ARM的认识?
答:
1-ARM是一家公司,ARM公司是一家芯片知识产权(IP)供应商,它与一般的半导体公司最大的不同就是不制造芯片且不向终端用户出售芯片,而是通过转让设计方案,由合作伙伴生产出各具特色的芯片。
2 - ARM处理器,ARM处理器是英国Acorn有限公司设计的低功耗低成本的第一款RISC微处理器。
经典处理器 ARM7\ARM9\ARM11,后续处理器开始以cortex命名
Cortex-A 高性能
Cortex-R 汽车电子
Cortex-M 低成本、低功耗
3 - ARM代表一种技术。具有性能高、成本低和能耗省的特点。在智能机、平板电脑、嵌入控制、多媒体数字等处理器领域拥有主导地位。
Tips:目前主流处理器架构?
ARM架构、Intel X86/X64架构、MIPS架构、RISC-V(开源)
Tips:精简指令集RISC和复杂指令集CISC的区别?
注:程序是指令的有序集合
int a,b,c;
a=10;
b=20;
c=a+b;
案例:
早期的CPU全部是CISC架构,它的设计目的是要用最少的机器语言指令来完成所需的计算任务。
比如对于乘法运算,在CISC架构的CPU上,您可能需要这样一条指令:
MUL ADDRA, ADDRB
就可以将ADDRA和ADDRB中的数相乘并将结果储存在ADDRA中。
将ADDRA,ADDRB中的数据读入寄存器,相乘和将结果写回内存的操作全部依赖于CPU中设计的逻辑来实现。
这种架构会增加CPU结构的复杂性和对CPU工艺的要求,但对于编译器的开发十分有利。
比如上面的例子,C程序中的a*=b就可以直接编译为一条乘法指令。
今天只有Intel及其兼容CPU还在使用CISC架构。
RISC架构要求软件来指定各个操作步骤。上面的例子如果要在RISC架构上实现,
将ADDRA, ADDRB中的数据读入寄存器,相乘和将结果写回内存的操作都必须由软件来实现,
比如:
MOV A, ADDRA;
MOV B, ADDRB;
MUL A, B;
STR ADDRA,A。
这种架构可以降低CPU的复杂性以及允许在同样的工艺水平下生产出功能更强大的CPU
但对于编译器的设计有更高的要求。
2.处理器架构
STM32G030
● 主模块 :
Cortex-M0内核及先进高性能总线 (AHB bus)
通用 DMA ( GP-DMA -- general-purpose DMA)
● 从模块 :
-- 内部FLASH
-- 内部SRAM
-- APB桥,连接AHB和APB,所有的外设都挂在APB总线上
- G0: GPIOx直接挂在IOPORT总线上。
Tips:flash和SRAM的区别?
Flash存储器是一种非易失性存储器,可以在掉电之后保存数据,通常用于存储程序代码。Flash存储器的可写入次数有限,且需要执行擦除操作才能写入新的数据,因此,在使用过程中需要注意擦写周期和数据备份问题。
SRAM存储器则是一种易失性存储器,具有相对较快的读写速度和无限的读写次数,但掉电时将会丢失所有内容。SRAM存储器主要用于暂存数据和临时变量,读写操作由CPU直接完成,访问速度较快。
单片机的Flash存储器和SRAM存储器通常都嵌入在单片机芯片内部,能够方便地实现对程序和数据的读写操作。通常,编译器会将程序代码烧录到Flash存储器中,并使用SRAM存储器来存储变量、函数堆栈和其他临时数据。
Tips: 什么是外设? 如何理解片上外设?
与传统的外设不同,片上外设通常具有以下优点:
高效性:片上外设能够与主处理器实现高速的数据传输,响应时间短,执行效率高。
集成度高:片上外设多个模块都嵌入到处理器芯片内部,极大地降低了PCB面积和电路复杂度。
低功耗:处理器和片上外设采用相同的工艺,能够满足高密度和低功耗的需求。
可靠性高:提高了整体系统的可靠性和稳定性,也降低了电磁干扰的可能。
Tips:AHB和APB的区别?
AHB是高速总线,是一种系统总线,它主要负责连接处理器、DMA等一些内部接口。
AHB系统由主模块、从模块和基础结构3部分组成,整个AHB总线上的传输都由主模块发出,由从模块负责回应。
APB是低速总线,它主要负责连接外围设备,它又分为APB1和APB2,
它的总线架构不像 AHB支持多个主模块,在APB里面唯一的主模块就是APB桥。
cortext-M0内核架构
Cortex-M 系列产品线
Cortex-M 系列产品主要包括 Cortex-M0、Cortex-M1、Cortex-M3、Cortex-M4、Cortex-M7 等,其中 Cortex-M0 主打低功耗和混合信号的处理,M3 主要用来替代 ARM7,重点侧重能耗与性能的平衡,而 M7 则重点放在高性能控制运算领域。
Cortex-M0结构框图
Cortex-M0 微处理器主要包括处理器内核、嵌套向量中断控制器(NVIC)、调试子系统、内部总线系统构成。
Cortex-M0 微处理器通过精简的高性能总线(AHB-LITE)与外部进行通信。
Cortext-M0特性
- 采用Thumb指令集 (ARM指令是32位的,而Thumb指令时16位的,如果存储空间中可以放32条ARM指令,就可以放64条Thumb指令,因此在存放Thunb指令时,代码密度高)
- 高性能,使用ARMv6-M的体系架构;
- 中断数量可配置1-32个,4级中断优先级
- 门电路少,低功耗 中断唤醒控制器(WIC),支持极低功耗休眠模式
- 兼容性好 与Cortex-M1 处理器兼容,向上兼容 Cortex-M3 和 Cortex-M4 处理器 ,可以很容易地升级、移植
- 支持多种嵌入式操作系统,也被多种开发组件支持
Cortex-M0工作模式
线程模式(Thread Mode)- 芯片复位后,执行用户程序
处理模式(Handler Mode)- 当处理器发生了异常或者中断,处理完成后返回线程模式。
Cortex-M0工作状态
Thumb状态:正常运行时处理器的状态
调试状态:调试程序时处理器的状态
Cortex-M0的寄存器
通用寄存器
R0-R12:13个通用寄存器。其中 R0-R7为低端寄存器,可作为16位或32 位指令操作数,R8-R12为高端寄存器,只能用作32位操作数
R13:栈指针寄存器 SP(the stark pointer),Cortex-M0 在不同物理位置上存在两个栈指针,主栈指针 MSP,进程栈指针 PSP。
在处理模式下,只能使用主堆栈,在线程模式下,可以使用主堆栈也可以使用进程堆栈。 系统上电的默认栈指针是MSP。这样设计的目的是为了在进行模式转换的时候,减少堆栈的保存工作。同时也可以为不同权限的工作模式设置不同的堆栈。
R14:链接寄存器LR(the link register),用于存储子程序或者函数调用的返回地址
R15:程序计数器PC(the program counter register)存储下一条将要执行的指令的地址。
特殊寄存器
xPSR:组合程序状态寄存器,该寄存器由三个程序状态寄存器组成
应用PSR(APSR):保存程序计算结果的状态标志 N负数标志 Z零标志 C进位借位标志 V溢出标志
中断PSR(IPSR):包含当前ISR的异常编号
执行PSR(EPSR):包含Thumb状态位
CONTROL:控制寄存器
控制处理器处于线程模式时,使用哪个堆栈
=0,使用MSP 处理器模式时,固定使用MSP
=1,使用PSP
Cortex-M0的中断和异常
Cortex-M0 处理器最多支持32个外部中断(通常称为 IRQ)和一个不可屏蔽中断(NMI),另外Cortex-M0还支持许多系统异常(Reset、HardFault、SVCall、PendSV、SysTick),它们主要用于操作系统和错误处理
二、GPIO ( 通用输入输出端口**)**
1.基本概念
在嵌入式系统中,经常需要控制许多结构简单的外部设备或者电路,这些设备有的需要通过CPU控制,有的需要CPU读取其输入信号,因此在嵌入式微处理器上提供了一种"通用可编程I/O端口",也就是GPIO(General-purpose input/output) 。
GPIO编程:
通用输入输出接口GPIO是嵌入式系统、单片机开发过程中最常用的接口,用户可以通过编程灵活的对接口进行控制,实现对电路板上LED、数码管、按键等常用设备控制驱动,也可以作为串口的数据收发管脚,或AD的接口等复用功能使用。因此其作用和功能是非常重要的。
电源标识:
VCC-通常表示的是高电平(3.3V)
VDD-通常表示内部参考电源(3.3V)
GND-通常表示电源的参考平面(地)
VSS-内部参考平面(内部的地)
2.实际应用
Input 输入 - 数据采集
Output 输出 - 设备控制
3. 功能描述
查看技术手册 G030 - 173页 F051 - 117页 功能描述
3.1 IO结构框图
3.2 知识铺垫
问:模拟信号和数字信号的区别?
问:VDD、VSS、VCC分别表示什么意思?
VCC :接入电路的电压
VDD : 元器件内部的工作电压
VSS : 公共接地端电压
问:施密特触发器的作用?
由于外部输入的信号,可能会出现脉冲等噪声的影响,为了让信号更加清晰,所以就设置了TTL施密特触发器来进行整形。
3.3 功能详述:
● 浮空输入
通俗讲就是让管脚什么都不接,悬空着。
此时VDD和VSS所在路径的两个开关同时断开。因为没有上拉和下拉,所以当IO口没有接输入的时候,此时的电平状态会是一个不确定的值,完全由外部输入决定。
一般实际运用时,引脚不建议悬空,易受干扰。
优势:
这一种输入模式的电平会完全取决于外部电路而与内部电路无关。
缺点:
在没有外部电路接入的时候,IO脚浮空会使得电平不确定
应用:
该模式是STM32复位之后的默认模式,一般用作对开关按键的读取或用于标准的通讯协议,比如IIC、USART的等。
● 上拉输入
IO端口 - 上拉电阻 - 施密特触发器 - 输入数据寄存器 - 读
输入的电平不会因上下浮动而导致输入信号不稳定,当外部没有信号输入时,上拉电阻会将输入信号钳在高电平,此时引脚始终读到高电平信号。
● 下拉输入
IO端口 - 下拉电阻 - 施密特触发器 - 输入数据寄存器 - 读
输入的电平不会因上下浮动而导致输入信号不稳定,当外部没有信号输入时,下拉电阻会将输入信号钳在低电平,此时引脚始终读到低电平信号。
● 模拟输入
信号进入后不经过上拉电阻或者下拉电阻,关闭施密特触发器,经由另一线路把电压信号传送到片上外设模块。 所以可以理解为模拟输入的信号是未经处理的信号,是原汁原味的信号。
应用: 当 GPIO 引脚用于 ADC 采集电压的输入通道时,则需要选择"模拟输入"功能,因为经过施密特触发器后信号只有 0、1 两种状态,所以 ADC 外设要采集到原始的模拟信号,信号源输入必须在施密特触发器之前。
推挽和开漏.docx
● 开漏输出
输出寄存器上的'0'激活 N-MOS,而输出寄存器上的'1'将端口置于高阻状态 (P-MOS 从不被激活 )。
无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动。
可以利用改变上拉电源的电压来适应所需,进而提高外部电路的驱动能力。
● 推挽输出
输出寄存器上的'0'激活 N-MOS,而输出寄存器上的'1'将激活 P-MOS
具备输出高低电平的能力。
4. GPIO相关寄存器
4 个 32 位 配 置 寄 存 器
GPIOx_MODER 模式寄存器
GPIOx_OTYPER 输出模式寄存器
GPIOx_ OSPEEDR 输出速度寄存器
GPIOx_PUPDR 上拉下拉寄存器
2 个 32 位数据寄存器
GPIOx_IDR 输入数据寄存器
GPIOx_ODR 输出数据寄存器
1个 32 位置位 / 复位寄存器
GPIOx_BSRR 置位 / 复位寄存器
2 个 32 位复用功能寄存器
GPIOx_AFRH
GPIOx_AFRL
5. 点亮一盏LED灯
STM32单片机课程-LED灯实验.pdf
5.1 实验步骤
1.查看开发板实物,找到LED灯
2.查看底板原理图
以D7为例
//只要LED2为低电平,LED灯D7则被点亮
3.查看核心板原理图
//只要将单片机的PB2引脚配置输出模式并输出低电平信号即可点亮D7
5.2 编程实现
5.2.1 寄存器分析
版本1: 寄存器版本
//先使能GPIOB端口的时钟
RCC->IOPENR |= 1<<1 ;
//配置GPIO为输出模式
GPIOB->MODER &= ~(0x3<<4); //先清零
GPIOB->MODER |= 1<<4; //后置位
//选择推挽输出模式
GPIOB->OTYPER &= ~(1<<2);
//控制引脚输出低电平
GPIOB->ODR &= ~(1<<2);
5.2.2 代码编写
RCC->IOPENR |= 1<<1 ;
GPIOB->MODER &= ~(0x3<<4);
GPIOB->MODER |= 1<<4;
GPIOB->OTYPER &= ~(1<<2);
GPIOB->ODR &= ~(1<<2);
5.2.3编译下载
版本2:使用STM32CubeMX工具
第一件事先把烧录模式配置上
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能GPIOB组的时钟*/
__HAL_RCC_GPIOB_CLK_ENABLE();
/*配置PB1 输出低电平*/
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
/*PB1引脚的初始化配置 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
6. HAL库函数分析
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
功能: 设置或清除指定的端口位
参数: GPIO_TypeDef *GPIOx 端口号
uint16_t GPIO_Pin 引脚号
GPIO_PinState PinState 电平状态
GPIO_PIN_SET 1
GPIO_PIN_RESET 0
返回值:无
void HAL_GPIO_TogglePin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin)
功能: 切换指定的引脚电平状态
参数: GPIO_TypeDef * GPIOx 端口号
uint16_t GPIO_Pin 引脚号
返回值:无
GPIO_PinState HAL_GPIO_ReadPin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin)
功能: 读取指定的引脚电平状态
参数: GPIO_TypeDef * GPIOx 端口号
uint16_t GPIO_Pin 引脚号
返回值: GPIO_PinState 电平状态
GPIO_PIN_RESET 0
GPIO_PIN_SET 1
利用HAL库函数实现LED灯闪烁
练习:实现流水灯效果
7. 实验:按键控制LED灯亮灭
1.查看实物找到按键和控制的LED灯
五向按键 - S1
LED灯 - D7 D9 D10
2.查看原理图
S1 -
//五向按键任意方向键按下,PA8一定读到高电平信号
写法一:
` `if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==1)`
`{`
`HAL_Delay(100);`
`if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==1)`
`{`
`HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);`
`}`
`}`
`
写法二:
` `if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==1)`
`{`
`HAL_Delay(100);`
`if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==0)`
`{`
`HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);`
`}`
`}`
`
三. 串口通信
1. 通信的基本概念
两个通信对象,一个发送方,一个接收方。
2.通信的属性划分:
1)有无时钟源
同步通信:通信双发根据同步信号进行通信(IIC\SPI)
异步通信:双方都有各自的独立的时钟,约定好通信速度进行通信。(UART)
通信速度:单位时间内发送或接收的数据位数
波特率:一秒发送多少个bit位 bit/s bps
常用波特率:115200bps
2)通信方式
串行通信:指的是同一时刻只能收或发一个bit位信息。因此只用1根信号线即可。
优点:占用引脚资源少
缺点:通信效率低
并行通信:指的是同一时刻可以收或发多个bit位的信息,因此需要多根信号线才行。
优点:通信效率高
缺点:占用引脚资源多
3)传输方向
单工:只能作为发送设备或接收设备,发送方发送数据,接收方只能接收数据。(收音机)
半双工:可以作为发送设备也可以作为发送设备,但是同一时刻只能接收或者发送,不能同时收发。(对讲机)
全双工:在同一时间既可以接收也可以发送。(打电话)
3. USART ( 通用同步异步收发器**)**
Universal Synchonous Asynchronous receiver transmitter
中文参考手册 564页
USART: 支持同步\异步通信、全双工、串行
UART :没有信号线,只支持异步通信、全双工、串行
4. UART ( 通用异步收发器**)**
中文参考手册568页 图226
数据发生过程:由CPU或者DMA将要发送的数据写入到数据发送寄存器(TDR),发送寄存器将数据给到发送移位寄存器,再由发送移位寄存器将数据按照顺序发送出去,最终由Tx输出。
数据接收过程:由RX接收到数据之后,写入接收数据移位寄存器,再由移位寄存器按顺序搬移到数据接收寄存器,由CPU或DMA从数据接收寄存器读取即可。
5. 串口通信协议
一个数据帧的格式(异步通信)
空闲状态是高电平
起始位:1位 低电平表示数据包的起始
数据位:8-9位
校验位:1位
奇偶校验 (检验数据是否发送正确)
奇校验:数据位中1的个数 + 校验位上1的个数 = 奇数
偶校验:数据位中1的个数 + 校验位上1的个数 = 偶数
停止位:1-2位 将电平信号拉高,代表一个数据包发送结束,回到空闲状态
6. 相关寄存器
串口控制寄存器
(设备功能初始化、通信帧格 式配置)
USART_CR1 USART_CR2 USART_CR3
波特率寄存器
USART_BRR
中断和状态寄存器
USART_ISR
数据发送寄存器
USART_TDR
数据接收寄存器
USART_RDR
7. USART功能框图
Tx:数据发送端
Rx:数据接收端
流控概念?(简单了解就行了)
在两个设备正常通信时,由于处理速度不同,就存在这样一个问题,有的快,有的慢,在某些情况下,就可能导致丢失数据的情况。如台式机与单片机之间的通讯,接收端数据缓冲区已满,则此时继续发送来的数据就会丢失。流控制能解决这个问题
nRTS:请求以发送(Request To Send),n表示低电平有效。当本设备准备好接收新数据时就会将nRTS变成低电平;当接收寄存器已满时,nRTS将被设置为高电平。
nCTS:清除以发送(Clear To Send) 为输入信号,低电平有效。用于判断是否可以向对方发送数据,低电平说明本设备可以向对方发送数据。
该引脚只适用于硬件流控制
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
发送过程:由CPU和DMA向数据发送寄存器(TDR)中写入要发送的数据,由发送移位寄存器将数据按位移到发送端口输出。
接收过程:由CPU和DMA读取接收数据寄存器(RDR)中的数据。
8. 串口发送实验
实验要求
通过单片机向电脑(串口助手)发送数据
1.观察实物
找到通信接口 丝印"P4"
2.分析原理图
CH340 : 电平转换芯片,可以转换TTL电平-USB电平。
数据选择器/多路复用器
3.STM32CubeMX配置
4、寄存器方式编写发送接受一个字节的函数
//写一个单字节发送的函数`
`void` `put(uint8_t ch)`
`{`
`//while((USART1->ISR &0x40)==0){}`
`while(!(USART1->ISR &` `1<<7)){}`
`//判断发送数据寄存器是不是空了`
` USART1->TDR=ch;`
`//将数据写入到TDR寄存器中`
`}`
`int` `get()`
`{`
`uint8_t ch;`
`while(!(USART1->ISR &` `1<<5)){}`
`//判断接受数据寄存器是不是空了`
` ch=USART1->RDR;`
`//将数据从RDR寄存器中读出来`
`return ch;`
`}`
`
5、寄存器方式编写发送接受一个字节的函数
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:串口发送数据
参数:
huart -》串口选择(USART1)pData ->需要发送的数据
Size-》发送多少个数据 Timeout-》超时时间
返回值:成功返回HAL_OK 失败返回HAL_TIMEOUT
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:串口接受数据函数
参数:
huart -》串口选择(USART1)pData ->接收数据的地址
Size-》发送多少个数据 Timeout-》超时时间
返回值:成功返回HAL_OK 失败返回HAL_TIMEOUT
6、串口发送接收函数验证
HAL_UART_Receive(&huart1,buf,2,100);`
`HAL_UART_Transmit(&huart1,buf,strlen(buf),100);`
`memset(buf,0,sizeof(buf));`
`
7、不定长接受
声明`
`uint8_t buf[128]={"0 "};//数据缓存区`
`uint8_t byte;//单个字节缓存区`
`uint8_t len=0;//接收到的数据长度`
`写在while内`
`if(HAL_UART_Receive(&huart1,&byte,1,100)==HAL_OK)`
`//判断接收一个字节是否成功`
`{`
`if(byte=='\n')//判断是否结束`
`{//如果结束处理数据`
`HAL_UART_Transmit(&huart1,buf,len,100);//原封不动回回去`
`memset(buf,0,len);//清空缓存区`
` len=0;` `//长度清0`
`}`
`else//如果没有接收完成`
`{`
` buf[len++]=byte;//继续接收,把接收到的数据存储到buf`
`}`
`}`
`
8、重定向
//printf的重定向`
`int` `fputc(int ch,FILE *f)`
`{`
`while(!(USART1->ISR &` `1<<7)){}`
`//判断发送数据寄存器是不是空了`
` USART1->TDR=ch;`
`//将数据写入到TDR寄存器中`
`}`
`//scanf的重定向`
`int` `fgetc(FILE *f)`
`{`
`uint8_t ch;`
`while(!(USART1->ISR &` `1<<5)){}`
`//判断接受数据寄存器是不是空了`
` ch=USART1->RDR;`
`//将数据从RDR寄存器中读出来`
`return ch;`
`}`
`
scanf("%s",buf);`
`printf("%s",buf);`
`HAL_Delay(500);`
`
四、SPI
1.SPI通信
SPI接口是Motorola 首先提出的全双工三线/四线同步串行外设接口(Serial Peripheral Interface),采用主从模式(Master Slave)架构。
时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first);SPI接口有2根单向数据线,为全双工通信。
SPI总线被广泛地使用在FLASH、ADC、LCD等设备与MCU间,要求通讯速率较高的场合。
四线制:
(1)MOSI(数据线):主器件数据输出,从器件数据输入
(2)MISO(数据线):主器件数据输入,从器件数据输出
(3)SCLK (时钟线):主器件产生时钟信号
(4)/SS(片选线):从器件使能信号,由主器件控制
2.SPI通信协议
起始信号: NSS信号线由高变低,是SPI通讯的起始信号
结束信号:NSS信号由低变高,是SPI通讯的停止信号
数据传输:SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。MOSI及MISO数据线在SCK的每个时钟周期传输一位数据高位在前低位在后,且数据输入输出是同时进行的。SPI每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。
极性(Clock Polarity ,CPOL) : 决定着时钟起始电平
相位(Clock Phase ,CPHA) :决定采样边沿
在SPI操作中,最重要的两项设置就是时钟极性(CPOL)和时钟相位(CPHA)这两项即是主从设备间数据采样的约定方式。
SPI通信模式图:
SPI有四种通信模式:
当CPHA为0,是sck时钟线为奇数边沿采样
- CPOL=0,空闲状态是时钟为低电平
- CPOL=1,空闲状态是时钟为高电平
当CPHA为1,是SCK时钟线为偶数边沿采样
(1)CPHA=0,奇数边沿采样
(2)CPHA=1,偶数边沿采样
数据线被采样,都是等待数据线变化稳定半个时钟周期进行采样。
3. LCD液晶显示屏
液晶的形成:
某些物质在熔融状态或被溶剂溶解之后,尽管失去固态物质的刚性,却获得了液体的易流动性,并保留着部分晶态物质分子的各向异性有序排列,形成一种兼有晶体和液体的部分性质的中间态,这种由固态向液态转化过程中存在的取向有序流体称为液晶。
物理特点:当通电时导通,排列变得有秩序,使光线容易通过;不通电时排列混乱,阻止光线通过。
液晶显示屏内部构造:
颜色深度
① R,G,B三基色组合形成各种颜色。
②能显示的颜色数由RGB的数字信号的位数来决定
例如,以3位数字信号来表示颜色深度
RGB24表示的意思是(24位真彩色)
R:8
G:8
B:8
所以他能显示的颜色深度就是2^8*2^8*2^8
STM32G030开发板板载的显示屏是RGB16也称为RGB565
颜色深度: 2^5 * 2^6 *2^5
由一个16位的数据控制一个像素点的颜色显示
让一个像素点显示正红色: 1111 1000 0000 0000
4. 点亮LCD显示屏
1.查看原理图
底板原理图
打开STM32CubeMX新建工程配置对应引脚
将驱动文件放到工程目录下
颜色填充
图片显示及英文显示
使用画图工具修改图片为像素128*128
另存为BMP格式
取模工具打开,配置16位真彩色,大小128*128
保存并复制生成的数组,放到程序中
使用函数显示图片
效果展示
汉字显示
定义一个char类型的二维数组,并初始化为汉字字模数据
调用汉字显示函数,按要求传递参数即可。
五、STM32中断系统
中断的基本概念
嵌套向量控制器NVIC
中断及异常向量表
中断优先级
1. 中断
1.1 处理器中的中断
在处理器中,中断是一个过程,即CPU在正常执行程序的过程中,遇到外部/内部的紧急事件需要处理,暂时中止当前程序的执行,转而去为处理紧急的事件,待处理完毕后再返回被打断的程序处继续往下执行。
中断在计算机多任务处理,尤其是即时系统中尤为重要。比如uCOS,FreeRTOS等。
1.2 意义
中断能提高CPU的效率,同时能对突发事件做出实时处理
实现程序的并行化,实现嵌入式系统进程之间的切换
1.3 中断处理过程
进入中断
处理器自动保存现场到堆栈里
{PC, xPSR, R0-R3, R12, LR}
一旦入栈结束,ISR(中断服务程序)便可开始执行
退出中断
中断前的现场被自动从堆栈中恢复
一旦出栈完成,继续执行被中断打断的指令
出栈的过程也可被打断,使得随时可以响应新的中断而不再进行 现场保存
1.4 中断的体系结构
2. NVIC
NVIC-- **Nested Vectored Interrupt Controller--**嵌套中断控制器
NVIC的主要功能
中断管理
支持异常及中断向量化处理
支持嵌套中断
2.1 中断管理
Cortex-M0 处理器中,每一个外部中断都可以被使能或者禁止,并且可以被设置为挂起状态或者清除状态。处理器的中断可以电平的形式的,也可以是脉冲形式的,这样中断控制器就可以处理任何中断源。
2.2 中断和异常向量表
STM32G030中断和异常向量
Cortex-M0内核 可以处理15个内部异常和32个外部中断
STM32G030 只使用了6个内部异常和28个外部中断
异常向量表
当异常或中断发生时,处理器会把PC设置为一个特定地址,这一地址就称为异常向量
每一类异常源都对应一个特定的入口地址,这些地址按照优先级排列以后组成异常向量表
向量化处理中断的好处
传统的处理方式需要软件去完成。采用向量表处理异常,M0处理器会从存储器的向量表中,自动定位异常的程序入口。从发生异常到异常的处理中间的时间被缩减。
注:中断和异常的区别
中断是微处理器外部发送的,通过中断通道送入处理器内部,一般是硬件引起的,比如串口接收中断,而异常通常是微处理器内部发生的,大多是软件引起的,比如除法出错异常,特权调用异常等待。不管是中断还是异常,微处理器通常都有相应的中断/异常服务程序。
2.3 中断优先级
3个固定的优先级,都是负值,不能改变
四个可编程优先级,用两个bit位表示,00,01,10,11
优先级越小优先级越高
注意
不同优先级的中断同时发生,优先处理优先级编号较小的那个
同样优先级的中断同时发生,中断向量号较小的那个优先响应
3. 外部中断控制器EXTI
在 STM32G030 中,共有最多 28 中断 / 事件线可用:
GPIO 口连接到 16 个外部中断 / 事件线
3.1 系统配置控制器 (SYSCFG)
该器件具有一组配置寄存器
系统配置控制器的主要用途如下:
在部分 IO 口上启用或禁用 I2C 超快模式 (Fast Mode Plus)
重映射部分从 TIM16 和 TIM17,USART1 和 ADC 的 DMA 触发源到其它不同的 DMA 通道上
重映射存储器到代码起始区域
管理连接到 GPIO 口的外部中断
管理系统的可靠性特性
3.2 外部中断 / 事件框图
4. 按键中断实例
按键中断------LED亮灭
4.1 按键原理图分析
核心板图
4.2 编程步骤分析
- 使能相应的时钟
- 配置GPIO管脚为中断功能
- 设置中断优先级
- 使能相应的中断
- 实现中断服务程序
4.3 配置流程
4.4 编码流程
gpio.c代码
/* USER CODE BEGIN 2 */`
`void` `HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)`
`{`
`if` `(GPIO_Pin == GPIO_PIN_8)`
`HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);`
`}`
`/* USER CODE END 2 */`
`
main.c代码
` `while` `(1)`
`{`
`/* USER CODE END WHILE */`
`/* USER CODE BEGIN 3 */`
`HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);`
`HAL_Delay(3000);`
`}`
`/* USER CODE END 3 */`
`
4.5 拓展练习
实现按键中断,LED灯亮灭并向串口发送数据
思想:Cube MX 中多配置一个USART1
5. 串口发送中断实例
5.1 串口原理图分析
核心板图
5.2 编程步骤分析
- 使能相应的时钟
- 配置GPIO管脚为串口功能
- 设置中断优先级
- 使能相应的中断
- 实现中断服务程序
5.3 配置流程
5.4 编码流程
实现发送中断
1、启动发送中断
HAL_UART_Transmit_IT(&huart1,"HELLO",sizeof("HELLO"));`
`
- 找发送完成回调函数(不断跳转)
3、重新编写回调函数
uint8_t i=0;`
`void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)`
`{`
` if(huart->Instance ==USART1)`
` {`
` i++;`
` }`
`}`
`
实现接收中断函数
- 接收中断使能
HAL_UART_Receive_IT(&huart1,buf,2);`
`
2、找接收完成回调函数
3、到这里发现只有一个变量,所以我们需要寻找这个变量在哪里被赋值了
4、跳转这个接收使能函数
编写回调函数
extern uint8_t buf[128];`
`void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)`
`{`
` /* Prevent unused argument(s) compilation warning */`
` if(huart->Instance==USART1)`
` {`
` HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,0);`
` HAL_UART_Receive_IT(&huart1,buf,2);`
` }`
`}`
`
六、ADC 模数转换器
Analog-to-Digital Converter模数转换器(将模拟信号转换为数字信号的转换器)
1. ADC作用
ADC是一个逐次逼近型的模数转换器,可以将连续的模拟信号(电压、温度、光照、压力....),转换成离散的数字信号
(传感器与之不同:将非电学量转换成电学量)
最直观的体现:模拟信号是连续变化的曲线,而数字量是不连续的一个个离散的点
2. STM32的ADC简介
12 位 ADC 是一种逐次逼近型模拟数字转换器
它有多达 19 个通道,可测量 16 个外部通道(从外部 GPIO 口连接的16 通道模拟输入)
3 个内部信号源:
- 分别为内部温度传感 (VSENSE) 输入
- 内部参考电压 (VREFINT) 输入
- 外部电池 VBAT 供电引脚输入
各通道的 A/D 转换可以单次、连续、扫描或间断模式执行
ADC的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中
3. ADC特性
量程:
能测量的电压范围 0 ~ 3.6V
分辨率:
ADC的分辨率通常以输出二进制数的位数表示,位数越多,分辨率越高,一般来说分辨率越高,转化时间越长
可配置的转换精度:
6位,8位,10位,12位
转化时间:
模拟输入电压在允许的最大变化范围内,从转换开始到获得稳定的数字量输出所需要的时间
转换得到一个12位精度的数据,需要1us,转换速度可以达到1MHZ
4. ADC的时钟
APB时钟的 2 或 4 分频,最高14MHz
优点:
不会有时钟域之间的同步带来的抖动,触发事件和转换的起始时刻之间的延迟是确定的,从 而保证转换之间的时间间隔是固定的
缺点:
ADC的转换时间和系统时钟频率相关,受系统频率的影响较大
片上14MHZ HSI RC振荡器
优点:
无论MCU的运行频率,都可以保证最高的ADC工作频率可以使用自动节电模式(自动开启或关闭14MHz的内部振荡器)
缺点:
触发信号的同步会带来抖动,触发事件和转换的起始时刻之间的延迟不确定
5. 五种工作模式
单通道单次转换
单通道连续转换
多通道单次转换
多通道连续转换
间隔转换
6. 程序设计
6.1 单通道单次采集实验
光敏模块控制LED亮灭
6.1.1 原理图分析
光敏模块 - OUT - D1 - 高低电 - IN - A1 - 模拟量`
`底板原理图 - A1 - 核心板底座`
`核心板原理图 - 核心板插针 - A1 - PA4`
`
6.1.2 Cube MX 环境配置
光敏模块 - A1 - PA4 - ADC_IN4`
`LED - LED2/3/4` `- PB2/1/0` `- GPIO_Output`
`
6.1.3 代码编写
main.c
/* USER CODE BEGIN Includes */`
`uint16_t buf;`
`/* USER CODE END Includes */`
`while (1)`
`{`
`/* USER CODE END WHILE */`
`/* USER CODE BEGIN 3 */`
` // 1.start`
` HAL_ADC_Start(&hadc1);`
` // 2.wait`
` HAL_ADC_PollForConversion(&hadc1, 100);`
` // 3.getvalue`
` buf = HAL_ADC_GetValue(&hadc1);`
` // 4.stop`
` HAL_ADC_Stop(&hadc1);`
` if (buf > 0x0DDD)`
` HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, (GPIO_PinState)0);`
` else if (buf < 0xDDD)`
` HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, (GPIO_PinState)1);`
`}`
`
6.2 多通道扫描
按键控制五个方向来发送五句话(上下左右中)
不同的方向灯亮的不同
采集光敏的数据发送到串口
6.2.1 原理图分析
光敏模块ADC
光敏模块 - OUT - D1 - 高低电 - 模拟量 -` `A1 `
`光敏模块实物 -` `A1 - P1从上往下第4个`
`底板传感器拓展模块接口 - P1从上往下第4个` `-` `A1 -` `开发板上核心板底座 -` `CON2的3号接口`
`核心板原理图 - 核心板底座(插针) - A1 - PA4`
`
五向按键ADC
五向按键 - OUT`
`底板原理图`
`核心板原理图`
`
五向按键外部中断
五向按键 - 高低电 -` `高电平导通 - OUT `
`底板原理图 -` `D3&KEY - CON2`
`核心板原理图 -` `CON2 - 核心板插针 -` `D3&KEY - PA8`
`
6.2.2 Cube MX 环境配置
光敏模块 - A1 - PA4 - ADC_IN4`
`LED - LED2/3/4 - PB2/1/0 - GPIO_Output`
`按键 -` `ADC_KEY - PA1 -` `ADC_IN1`
`
光敏模块
A1对应PA4设置成 模数转换ADC1_IN4
五项按键控制串口和灯
- 按键PA8配置外部中断(使能、下降沿)
- 三个灯PB0、PB1、PB2
- 串口USART1配置(波特率9600)
按键ADC
ADC KEY对应PA1设置成 模数转换ADC1_IN1
加上第一步配置的ADC1_IN4,此时有两个ADC------多通道
时钟RCC
48MHz------时钟影响cpu存取速度------速度越快,ADC采集速度越快
6.2.3 代码编写
main.c
/* USER CODE BEGIN Includes */`
`uint16_t buf[2];`
`/* USER CODE END Includes */`
`
gpio.c
/* USER CODE BEGIN 0 */`
`#include "adc.h"`
`#include "usart.h"`
`#include `
`/* USER CODE END 0 */`
`/* USER CODE BEGIN 2 */`
`extern` `uint16_t buf[2];` `// 数组能将两条通道的 GetValue 都存储下来`
`int` `fputc(int ch, FILE* f)`
`{`
`while((USART1->ISR &` `1<<7)` `==` `0){}`
` USART1->TDR = ch;`
`return ch;`
`}`
`// 外部中断 下降沿 回调函数`
`void` `HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)`
`{`
`if` `(GPIO_Pin == GPIO_PIN_8)`
`{`
`// 1.start`
`HAL_ADC_Start(&hadc1);`
`// 2.wait 可以不用了,ADC_ISR中断寄存器`
`// 位3:EOC序列转换结束标志`
`// 位2:EOS转换结束标志,单个结束是此位`
`// HAL_ADC_PollForConversion(&hadc1, 100);`
`while((ADC1->ISR &` `1<<2)` `==` `0){}` `// 如果没结束一直wait`
`// 3.1 Get ADC1 Value`
` buf[0]` `=` `HAL_ADC_GetValue(&hadc1);` `// 第一个通道获取`
`// 结束了可以用位3序列转换结束标志EOC进行判断`
`// 也可以使用位2的转换结束标志EOS来判断 `
`while((ADC1->ISR &` `1<<3)` `==` `0){}`
`// 3.2 Get ADC4 Value`
` buf[1]` `=` `HAL_ADC_GetValue(&hadc1);` `// 第二个通道获取`
`// 4.stop`
`HAL_ADC_Stop(&hadc1);`
`printf("按键数据: %d\r\n", buf[0]);`
`printf("光照数据: %d\r\n", buf[1]);`
`if((buf[0])>=2990` `&&` `(buf[0]<=3000))` `// 中:2990-3000`
`{`
`printf("中");`
`HAL_GPIO_WritePin(GPIOB,`
`GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_Pin_RESET);`
`}`
`if` `((buf[0])>=2975` `&&` `(buf[0]<=2985))` `// 上:2975-2985`
`{`
`printf("上");`
`HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_Pin_SET);`
`}`
`if` `((buf[0])>=2920` `&&` `(buf[0]<=2940))` `// 下:2920-2940`
`{`
`printf("下");`
`HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_Pin_SET);` `}`
`if` `((buf[0])>=2950` `&&` `(buf[0]<=2970))` `// 左:2950-2970`
`{`
`printf("左");`
`HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_Pin_SET);`
`}`
`else` `if` `(buf[0]` `>` `3000)` `// 右:3000-3020`
`{`
`printf("右");`
`HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_Pin_RESET);`
`HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_Pin_RESET);`
`}`
`}`
`}`
`/* USER CODE END 2 */`
`
6.2.4 实现效果
七、DMA - 数据的搬运工
1. DMA作用
DMA的传输方式无需CPU参与,可以直接控制传输
DMA给外部设备和内存开辟了一条直接数据传输的通道
2. 目的
给CPU节省资源,使CPU的工作效率提高
3. DMA特性
1)同一个DMA模块可以有多个优先级请求:很高 高 中等 低
2)每个通道有3个事件标志: DMA半传输 DMA传输完成 DMA传输出错
3)数据源 目标源 数据传输宽度对齐
4)传输数据 字节8位 半字16位 全字32位
5)存储器<->存储器 外设<->存储器 外设<->外设
6)闪存(flash) SRAM APB AHB 外设均可以作为源或者目标
7)搬移数据的最大长度为65535字节
4. DMA寄存器
DMA_CPARx :设置外设地址的寄存器
DMA_CMARx :设置存储器地址的寄存器
DMA_CCRx :设置数据传输方向
DMA_CNDTRx:设置传输的数据量
5. DMA的增量或者循环模式
增量: 外设搬移到存储器的时候,不希望覆盖上一个会将内存设置为增量模式
循环: DMA不停循环的搬移数据,一组的数据传输完成时,计数寄存器将会自动地被恢复成配置该通道时设置的初值.
6. DMA-ADC串口发送程序设计
6.1 Cube MX 环境配置
USART串口配置省略
6.2 代码编写
main.c
uint16_t buf;`
`while` `(1)`
`{`
`/* USER CODE END WHILE */`
`/* USER CODE BEGIN 3 */`
`HAL_ADC_Start_DMA(&hadc1,` `(uint32_t` `*)&buf,` `1);`
`HAL_Delay(500);`
`}`
`
adc.c
/* USER CODE BEGIN 0 */`
`#include `
`#include "usart.h"`
`/* USER CODE END 0 */`
`extern` `uint16_t buf;` `// 光照强度缓冲区`
`int` `fputc(int ch, FILE* f)`
`{`
`while` `((USART1->ISR &` `1<<7)` `==` `0){}`
` USART1->TDR = ch;`
`return ch;`
`}`
`void` `HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)`
`{`
`if` `(hadc->Instance == ADC1)`
`printf("光照强度为: %d", buf);`
`HAL_ADC_Stop(&hadc1);`
`}`
`
6.3 运行结果
7. DMA-ADC串口发送--按键中断程序设计
7.1 流程图
7.2 Cube MX 环境配置
RCC - 48M(速度更快)
USART1 - 波特率 - 115200
PA8 - GPIO_EXTI8 - NVIC勾选按键(4-15)中断
PA1 - ADC1_IN1
DMA打开: DMA设置
1) ADC里DMA请求要使能
2) 点击 ADD - ADC
CHANNEL 1 方向:外设->内存
模式: 普通(不选择循环)
增量模式: 在内存一侧要打钩
数据传输宽度: 选择半字(16bit - ADC也是16bit的)
7.3 代码编写
main.c
/* USER CODE BEGIN Includes */`
`注释掉上次项目 遗留的参数声明`
`//uint32_t buf; `
`/* USER CODE END Includes */`
`
gpio.c
/* USER CODE BEGIN 0 */`
`#include "adc.h"`
`// CubeMX配置中Data Width为Half World,所以必须为16,单通道使用32无伤大雅,但双通道会出现错误,保持好习惯,使用和配置的数据宽度一致的ADC缓存区大小。`
`uint16_t buf;`
`/* USER CODE END 0 */`
`/* USER CODE BEGIN 2 */`
`void` `HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)`
`{`
`if` `(GPIO_Pin == GPIO_PIN_8)` `// 按下按键开始DMA-ADC`
`{`
`HAL_ADC_Start_DMA(&hadc1,` `(uint32_t` `*)&buf,` `1);`
`}`
`}`
`/* USER CODE END 2 */`
`
adc.c
/* USER CODE BEGIN 0 */`
`#include `
`#include "usart.h"`
`#include `
`/* USER CODE END 0 */`
`/* USER CODE BEGIN 1 */`
`// CubeMX配置中Data Width为Half World,所以必须为16,单通道使用32无伤大雅,但双通道会出现错误,保持好习惯,使用和配置的数据宽度一致的ADC缓存区大小。`
`extern` `uint16_t buf;`
`int` `fputc(int ch, FILE* f)`
`{`
`while` `((USART1->ISR &` `1<<7)` `==` `0){}`
` USART1->TDR = ch;`
`return ch;`
`}`
`void` `HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)`
`{`
`//进入这个函数说明DMA搬移完成,所以只需要打印搬移的数据即可`
`printf("光照强度为: %d", buf);`
`HAL_ADC_Stop_DMA(hadc);`
`memset(&buf,` `0,` `sizeof(buf));` `// 清空`
`}`
`/* USER CODE END 1 */`
`
7.4 运行结果
8. DMA-ADC多通道采集实验(DMA不定长接收)
实验思路
在按键中断服务程序中启动ADC进行采集,ADC采集结束之后
自动发起一个DMA搬运请求,DMA请求控制总线,搬移数据,搬移完成之后触发一个搬移完成中断。
在DMA搬移完成中断回调函数中,打印数据数组中ADC采集的光敏
8.1 Cube MX 环境配置
8.2 可能使用到的函数
找函数去HAL英文手册 HAL_UART_Generic Driver 587页`
`1)HAL_UART_Receive_DMA(UART_HandleTypeDef * huart,`
`uint8_t` `* pData,` `uint16_t Size)` `//开启DMA通道并设定通道长度`
`2)__HAL_UART_ENABLE_IT(__HANDLE__,__INTERRUPT__)//开启串口空闲中断`
`3)__HAL_UART_GET_FLAG(__HANDLE__,__FLAG__)//获得串口空闲中断标志`
`4)__HAL_UART_CLEAR_FLAG(__HANDLE__,__FLAG__)` `//清除串口空闲中断`
`5)HAL_UART_DMAStop(UART_HandleTypeDef * huart)` `//关闭串口DMA通道`
`6)设定的传输长度-剩余传输数量(DMA_CNDTRx)=实际长度`
`7)HAL_UART_Transmit_DMA(UART_HandleTypeDef * huart,` `uint8_t` `* pData,` `uint16_t Size)` `//使用DMA通道发送指定长度的字符到串口中`
`8)HAL_UART_Receive_DMA(UART_HandleTypeDef * huart,` `uint8_t` `* pData,` `uint16_t Size)` `//再次开启DMA通道并设定通道长度`
`
8.3 代码编写
main.c
HAL_UART_Receive_DMA(&huart1,buf,128);`
` __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);`
`
中断处理函数
extern uint8_t buf[128];`
`uint16_t len=0;`
`void USART1_IRQHandler(void)`
`{`
`/* USER CODE BEGIN USART1_IRQn 0 */`
`/* USER CODE END USART1_IRQn 0 */`
` HAL_UART_IRQHandler(&huart1);`
`/* USER CODE BEGIN USART1_IRQn 1 */`
`if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))`
`{`
` __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE);`
` HAL_UART_DMAStop(&huart1);`
` len=128-hdma_usart1_rx.Instance->CNDTR;`
` HAL_UART_Transmit_DMA(&huart1,buf,len);`
` HAL_UART_Receive_DMA(&huart1,buf,128);`
`}`
`/* USER CODE END USART1_IRQn 1 */`
`}`
`
八. 时钟系统
1. 什么是时钟系统?
1.1概念
时钟系统是由振荡器(信号源)、定时唤醒器、分频器、倍频器等组成的电路
常用的信号源有晶体振荡器和RC振荡器
意义
时钟是嵌入式系统的脉搏,处理器内核在时钟驱动下完成指令执行,状态变换等动作,外设部件在时钟的驱动下完成各种工作,比如串口数据的发送、A/D转换、定时器计数等等
因此时钟对于计算机系统是至关重要的,通常时钟系统出现问题也是致命的,比如振荡器不起振、振荡不稳、停振等
1.2 常见振荡器简介
概念
振荡器是用来产生重复电子讯号的电子元件
其构成的电路叫振荡电路,能将直流电转换为具有一定频率交流信号输出的电子电路或装置
分类
振荡器主要分为RC、LC、晶体
RC振荡器 是采用RC网络作为选频移相网络的振荡器
LC振荡器 是采用LC振荡回路作为移相和选频网络的正反馈振荡器
晶体振荡器 的振荡频率受石英晶体控制
|----|-----------------|------------|
| | RC振荡器 | 晶体振荡器 |
| 构成 | 电阻电容 | 石英晶体 |
| 优点 | 成本低 | 稳定、精度高 |
| 缺点 | 震荡频率会有误差、受温湿度影响 | 价格高、需接起振电容 |
1.3 四个时钟源
HSI:高速内部时钟,RC振荡器,频率为 8MHz
HSE:高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz
LSI:低速内部时钟,RC振荡器,频率为 40KHz
独立看门狗时钟源只能是这个,还可做RTC时钟源
LSE:低速外部时钟,接 32.768KHz 的石英晶体。
主要是RTC的时钟源
1.4 时钟树
1.5 Cube MX 配置时钟
2. SysTick定时器
2.1 概念
能够定时、计数的器件称为定时器
SysTick 称作系统滴答定时器,简称滴答定时器
SysTick 是一个定时设备,位于 Cortex-M0 内核中,可以对输入的时钟进行计数
如果时钟信号是周期性的,计数也就是计时
系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳
根据这个中断,系统就可以实现时间片的计算从而切换进程
2.2 工作原理
滴答定时器是一个24位定时器,也就是最多能计数2^24
使用
- 我们一般给计数器送一个初始的计数值,计数器向下计数
- 每来一个时钟信号,计数初值就减 1
- 计数值减到 0 的时候,就会发出一次中断
- 然后重新从计数初值再减一计数,循环不断
原理图
2.3 SysTick 寄存器
3. HAL_Delay实现
void` `delay_us(uint32_t t)`
`{`
` SysTick->LOAD =48*t;` `// 48对应Hz`
` SysTick->VAL =0;` `// 记住设置为0`
`while((SysTick->CTRL &` `1<<16)` `==` `0){}`
`}`
`void` `delay_ms(uint32_t t)`
`{`
`while(t--)`
`delay_us(1000);`
`}`
`
九. STM32TIM定时器
1. TIM简介
定时器本质上是一个计数器,可对输入的时钟进行计数,并在计数值达到设定值时触发中断
当这个计数器的输入是一个准确可靠的基准时钟时,对基准时钟计数的过程就是计时的过程
计数器:执行计数定时的寄存器,每来一个时钟,计数器加 1
预分频器:可对计数器的时钟进行分频,让计数更加灵活
自动重装寄存器:计数的目标值------计多少个,时钟申请中断(了解)
CNT(计数器)与CCR(自动重载寄存器)的值相等时,会产生一次定时中断,可选择接收或不接受,PWM波中就忽略了定时中断,如果是万年历、电子时钟就需要接收定时中断
这些 16 位寄存器构成了定时器最核心的部分,这一块电路称为时基单元
不仅具备基本的定时中断,还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式
定时器的基本结构是通用的,很多模块电路都能用到,所以STM32定时上扩展了非常多的功能
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
|--------|--------|------------|-------------|-------------|----------|-------------------------|
| 种类 | 位数 | 计数器模式 | 产生DMA请求 | 捕获/比较通道 | 互补输出 | 特殊应用场景 |
| 高级 | 16 | 向上,向下,向上/下 | 可以 | 4 | 有 | 带死区控制盒紧急刹车,可应用于PWM电机控制 |
| 通用 | 16 | 向上,向下,向上/下 | 可以 | 4 | | 通用。定时计数,PWM输出,输入捕获,输出比较 |
| 基本 | 16 | 向上,向下,向上/下 | 可以 | 0 | 无 | 主要应用于驱动DAC |
2. 时基单元
2.1 预分频器
2.2 计数器
2.3 自动重载寄存器
3. 通用定时器
主要看Internal Clock (CK_INT)
对理解有帮助
4. 程序设计
定时 1s 发送 "打小就聪明"
4.1 Cube MX 环境配置
4.2 代码
main.c
/* USER CODE BEGIN 2 */`
` HAL_TIM_Base_Start_IT(&htim3);`
` /* USER CODE END 2 */`
`
tim.c
htim3.Instance = TIM3;`
` htim3.Init.Prescaler =` `48000-1;`
` htim3.Init.CounterMode = TIM_COUNTERMODE_UP;`
` htim3.Init.Period =` `1000-1;`
`/* USER CODE BEGIN 1 */`
`int` `fputc(int ch, FILE* f)`
`{`
`while` `((USART1->ISR &` `1<<7)` `==` `0);`
` USART1->TDR = ch;`
`return ch;`
`}`
`void` `HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)`
`{`
`if` `(htim->Instance == TIM3)`
`{`
`printf("晓超打小就聪明\r\n");`
`HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);`
`}`
`}`
`/* USER CODE END 1 */`
`
4.3 运行结果
十. PWM
1. 定时器中断实验相关寄存器
1.1 计数器当前值寄存器CNT
1.2 预分频寄存器TIMx_PSC
1.3 自动重装载寄存器(TIMx_ARR)
1.4 控制寄存器1(TIMx_CR1)
2. 程序设计
蜂鸣器
有源蜂鸣器:内部有振荡器 直接导通就能发声
无源蜂鸣器:没有振荡器
2.1 蜂鸣器概述
2.1.1 工作发声原理
蜂鸣器的发声原理由振动装置和谐振装置组成,而蜂鸣器又分为无源他激型与有源自激型
蜂鸣器的发声原理:
- 无源他激型蜂鸣器的工作发声原理:方波信号输入谐振装置转换为声音信号输出
- 有源自激型蜂鸣器的工作发声原理:直流电源输入经过振荡系统的放大取样电路在谐振装置作用下产生声音信号
2.1.2 差别
有源蜂鸣器和无源蜂鸣器的主要差别是:二者对输入信号的要求不一样,有源蜂鸣器工作的理想信号是直流电,一般标示为VDD、VDC等。因为蜂鸣器内部有一个简单的振荡电路,可以把恒定的直流电转变成一定频率的脉冲信号,从而产生磁场交变,带动钼片振动发出声音
2.1.3 分类
按其驱动方式的原理可分为:
- 有源蜂鸣器(内含驱动线路,也叫自激式蜂鸣器)
- 无源蜂鸣器(外部驱动,也叫他激式蜂鸣器)
按构造方式的不同可分为:
- 电磁式蜂鸣器
- 压电式蜂鸣器;
按封装的不同可分为:
- DIP BUZZER(插针蜂鸣器)
- SMD BUZZER(贴片式蜂鸣器)
按电流的不同可分为:
直流蜂鸣器
交流蜂鸣器
其中,以直流最为常见压电式蜂鸣器,用的是压电材料,即当受到外力导致压电材料发生形变时压电材料会产生电荷。同样,当通电时压电材料会发生形变。
2.2 Cube MX 环境配置
2.3 代码
main.c
` `/* USER CODE BEGIN 2 */`
`HAL_TIMEx_PWMN_Start(&htim17, TIM_CHANNEL_1);`
`/* USER CODE END 2 */`
`
tim.c
` `// 定时器选择`
` htim17.Instance = TIM17;`
`// 预分频器初始化`
` htim17.Init.Prescaler =` `48-1;`
`// 计数模式初始化`
` htim17.Init.CounterMode = TIM_COUNTERMODE_UP;`
`// 自动重装载寄存器初始化:重载目标值`
` htim17.Init.Period =` `5000-1;`
`// 时钟分频初始化:1分频`
` htim17.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;`
` htim17.Init.RepetitionCounter =` `0;`
` htim17.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;`
`if` `(HAL_TIM_Base_Init(&htim17)` `!= HAL_OK)`
`{`
`Error_Handler();`
`}`
`if` `(HAL_TIM_PWM_Init(&htim17)` `!= HAL_OK)`
`{`
`Error_Handler();`
`}`
` sConfigOC.OCMode = TIM_OCMODE_PWM1;`
`// 脉冲`
` sConfigOC.Pulse =` `2500;`
`
·
·