STM32 学习
资料下载
链接:https://pan.baidu.com/s/1xROXFM0g7Kxlptd9NBlW-A 提取码:k2qu --来自百度网盘超级会员V6的分享
1. 入门
1.1 STM32简介
什么是STM32
STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器
STM32常应用在嵌入式领域,如智能车、无人机、机器人、无线通信、物联网、工业控制、娱乐电子产品等
STM32功能强大、性能优异片上资源丰富、功耗低,是一款经典的嵌入式微控制器
目前STM32有如下产品系列覆盖:
- 主流系列:STM32F0、STM32F1
- 高性能系列:STM32F4、STM32F7、STM32H7
- 低功耗系列:STM32L0、STM32L1、STM32L4
- 混合信号系列:STM32G0、STM32G4
- 无线系列:STM32WB、STM32WL
每一个产品系列下都有不同型号的芯片,对应着不同的资源与性能,具体各型号的资源性能参数可以在官网查询。
如何选择开发板
大多数人会选择STM32F103ZET6或者STM32F407ZGT6作为学习STM32系列的第一块芯片,下面我们将这两个类型做一个简单比较。
项目 | STM32F103ZET6 | STM32F407ZGT6 |
---|---|---|
内核 | ARM Cortex®-M3 | ARM Cortex®-M4 |
主频 | 72MHz | 168MHz |
浮点运算单元 | 无 | 单精度FPU |
DSP指令集 | 无 | 有 |
16位定时器 | 8个,6个可配置四路通道 | 12个,每个都可配置四路通道 |
32位定时器 | 无 | 2个 |
SPI | 3个,18M/s | 3个,42M/s |
IIC | 2个 | 3个 |
串口 | 5个 | 6个 |
CAN | 1个 | 2个 |
ADC | 3×12位,21通道 | 3×12位,24通道 |
DAC | 2×12位 | 2×12位 |
编程方法 | 寄存器、库函数 | 寄存器、库函数 |
从以上对比可以看出来,STM32F407相对于STM32F103性能更加强大,外设更加丰富,最主要的是加入了FPU浮点运算单元和DSP指令集,这是Cortex®-M4内核的一个较为强大的升级。而两者都可以基于寄存器和库函数编程,虽然两个芯片寄存器不同,但是现阶段基本都是基于库函数进行编程,尤其是ST近几年主推的HAL库,两者编程十分相似,几乎没有区别,都可以胜任初学的要求。而在电子设计竞赛之中,拥有DSP指令集的STM32F407可以做FFT(快速傅里叶变化)等数学运算,优势非常明显,所以我的初学推荐是STM32F407,且以HAL库为主体,使用CubeMX+Keil或CubeIDE软件学习。
ARM
- ARM既指ARM公司,也指ARM处理器内核提供商,
- ARM公司是全球领先的半导体知识产权(IP),全世界超过95%的智能手机和平板电脑都采用ARM架构
- ARM公司设计ARM内核,半导体厂商完善内核周边电路并生产芯片
STM32命名规则
STM32F103C8T6
STM32=基于ARM@核心的32位微控制器
产品类型
F=通用类型
产品子类型
101=基本型 102=USB基本型,USB2.0全速设备 103=增强型 105或107=互联型
引脚数目:
T=36引脚
C=48引脚
8 闪存存储器容量
4=16K字节的闪存存储器 6=32K字节的闪存存储器 8=64K字节的闪存存储器 B=128K字节的闪存存储器 C=256K字节的闪存存储器 D=384K字节的闪存存储器 E=512K字节的闪存存储器
T封装
H=BGA T= LQFP U=VFQFPN Y=WLCSP64
6-温度范围
6=工业级温度范围,-40°C~85°C 7=工业级温度范围,-40°C~105°C
开发板
我在网上买的开发板
1.2 环境准备
- STM面包板入门套件
- Windows电脑
- 万用表,示波器,镊子,剪刀
1.3 开发环境安装
- 安装Keil5 MDK
- 安装器件支持包
- 软件注册
- 安装STLINK驱动
- 安装USB转串口驱动
1) 安装Keil5 MDK
第一步:
解压一下MDK,出现一下目录
如果想用最新版本的,请使用以下链接:Arm Keil | MDK-Community edition
点击download就行了
填写个人信息
2024年5月18号,最新版本的就是539.exe,大概1g左右耐心下载。
第二步:
双击exe文件
安装软件。选择安装路径
next,随便填写一下信息
这里会弹出安装驱动的东西
接下来会弹出安装 器件支持包的提示
2) 安装器件支持包
这个支持包是Keil5才需要安装的,像Keil4和之前的老版本,是不需要安装的。
这是因为,现在ARM的芯片型号非常多,升级换代的速度非常快。同时支持所有型号的芯片,这个占用内存非常大。
所以把器件支持包独立出来了,安装分为2种方式,第一种就是离线安装,第二种就是在线安装
下面介绍,如何进行离线安装
离线安装的数据包,如下图所示。双击即可安装成功
如果打不开就选择打开方式:我的是"C:\Keil_v5\UV4\PackUnzip.exe"。或者换成中文路径
。
接下来,就是离线安装.
点击next
这样子我们就安装好了
3) 软件注册
keil5是付费软件,现在开始破解
第一步:复制CID
第二步,解压注册机
第三步:关掉音量,友情提示!!! 输入CID,选择ARM。然后点击Generate。
然后点击lince management
最后出现
说明安装成功
4) 安装STLINK驱动(二选一)
什么是ST-Link
ST-LINK是专门针对意法半导体STM8和STM32系列芯片的仿真器。ST-LINK /V2指定的SWIM标准接口和JTAG/SWD标准接口。
JTAG (Joint Test Action Group,联合测试行动小组)是一种国际标准测试协议(IEEE 1149.1兼容)主要用于芯片内部测试。现在多数的高级器件都支持JTAG协议。
串行调试(SerialWire Debug),应该可以算是一种和JTAG不同的调试模式,使用的调试协议也应该不一样,所以最直接的体现在调试接口上,与JTAG的20个引脚相比,SWD只需要4个(或者5个)引脚结构简单。
https://www.bilibili.com/video/BV1DE411F72E
如何烧录程序?
- 驱动安装
- 硬件连接
- 更改设置
安装ST-LINK驱动
ST-LINK烧录测试
打开电脑的设备管理器,能看到这个就说明成功了。
4) DAP安装(二选一)
如果买的面包板的话,给的都是DAP仿真器。没有使用STLINK
什么是DAP?
特点:
- 在线仿真调试功能,采用高速的ARM内核处理协议,性能稳定、快速!
- DAP支持所有C0TEX-M系列的ARM处理器!STLINKV2只支持STM32和STM8!
- 与MDK、GCC、IAR等环境无缝配合,插上即可识别!
- 支持SWD仿真和USB虚拟串口接口!
- 为目标板供电。
DAP接线
下面的图为详细接线图
下图为实物接线图
DAPmini&DAP都为免驱仿真器。DAPmini&DAP的虚拟串口win10是免驱,win7需要安装虚拟串口驱动。注意:DAPmini 没有供电灯,信号灯只在烧录的时候闪烁。
买的面包板,这个就是芯片
DAP烧录测试
DAP仿真器在KEIL中下载程序,DEBUG设备是cmsis-dap,点击setting会识别出DAP设备,如设备未识别请检查接线,接线错误,设备也会识别不出来
然后一定要按一下复位键,黄色按钮,让程序重头运行。
5) 安装USB转串口驱动
把这个插上,如果没有驱动的情况下
解压完工具软件后,找到CH340驱动
最小系统电路
单片机光有芯片是无法进行工作的,需要连接最基本的电路
1.4 库函数开发
库函数开发流程
- 建立工程文件夹,Keil中新建工程,选择型号
- 工程文件夹里建立Start、Library、User等文件夹,复制固件库里面的文件到工程文件夹
- 工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里
- 工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹
- 工程选项,C/C++Define内定义USE STDPERIPH DRIVER
- 工程选项,Debug,下拉列表选择对应调试器,Settings,Flash,Download里勾选Reset and Run
如果使用库函数开发的话,需要用到写好的库函数
解压固件库文件
解压后的文件
新建工程
复制以下的3组文件
第一组
第二组
还有复制以下的东西。
第三组
在自己新建的工程文件夹下 新建一个Start文件夹,文件夹名字随意。
复制文件如下
点击Source Group ,然后单击一下,重新命名一下 Start
然后右键单击,把所有文件都添加进来
文件类型选择All Files。选择md结尾的启动文件,
添加结束后的效果
然后需要配置头文件路径
点击ok
然后新建User文件夹
再新建组
然后右键插入 stm32f10x.h
新建Library,打开
找到以下目录
把所有东西都复制到 Library目录下
在把inc目录的头文件全部复制到Library中
然后添加组
把刚刚复制的所有东西都添加进来了。效果如下所示。
再把所有以下三个文件,添加到User目录下
然后添加宏定义。
下面的字符串是啥意思?如果你定义了USE_STDPERIPH_DRIVER
这个宏定义,下面的配置才起作用。
按照一下配置信息
点击箱子按钮,设置编译的优先级
点击编译按钮,测试
库函数开发测试
#include "stm32f10x.h" // Device header
int main(void)
{
// 配置端口模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); // 设置函数的时钟
// GPIO_SetBits(GPIOC,GPIO_Pin_13); // 设置为高电平
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
while(1)
{
}
}
然后重新插拔一下,
问题汇总
找不到core_cm3.c的错误
如何找不到core_cm3文件,是软件太新,同样方法把路径:keil5的安装目录的PACK/ARM/CMSIS/Include包含即可
针对2-2新建工程中写了main函数进行编译时出现错误的问题: D:\Keil5\ARM\PACK\Keil\STM32F1xx_DFP\2.2.0\Device\Include\stm32f10x.h(483): error: #5: cannot open source input file "core_cm3.h": No such file or directory 这是因为你装的MDK版本太新了,比如MDK5.12/5.13,它们不会从MDK安装目录去查找头文件。 所以导致这个错误。 解决方法如下: 把core_cm3.h文件复制到keil软件安装的include目录下: D:\MDK\install\ARM\PACK\Keil\STM32F1xx_DFP\2.2.0\Device\Include
error: non-ASM statement in naked 错误
问题分析
keil5版本更新了的缘故,使用了keil5默认的版本6的编译器,而固件库还是支持版本5的编译器。
解决办法
安装Compiler Version 5
在将keil升级到5.37版本后使用的默认编译器位compiler version6 但是使用这个编译器编译stm32等芯片之前的库文件会有许多报错,但是正点原子,野火等stm32开发板的资料大多都采用的stm32的库文件
而且keil5.37后没有自带arm compiler v5版本的编译器,显示miss compiler version 5 ,可以自行在arm官网上下载v5版编译器
但是需要注册登录,而且账号要审核后才能使用,大概1天,操作非常麻烦
如果嫌麻烦,直接在百度云下载即可 百度网盘下载链接:https://pan.baidu.com/s/1q3XS6ch6PLfJHW3a3P1RwQ?pwd=uezi 提取码:uezi
- 将安装包解压到本地
- 找到Keil MDK的ARM 文件夹
- 在ARM文件夹中创建
ARMCC
文件夹
运行之前解压的文件中的ARMCompiler506_b960.msi
文件
运行之后一直点击Next,直到碰到这个界面
点击Browse
并将文件夹换成我们刚才创建的ARMCC
那个文件夹
然后配置文件
出现一下界面
结束
最后再次编译,就一点问题没得
1.5 面包板的使用
https://www.bilibili.com/video/BV1p14y1y7qi
上面为面包板正面图,下面为面包板撕掉的样子。
中间的金属是竖放的,5个相通的
2. GPIO输出
2.1 GPIO介绍
什么是GPIO
GPlO,全称为 General Purpose Input Output(通用输入输出),是一种通用的数字输入/输出端口。在嵌入式系统中,GPIO被设计为灵活的引脚,可以被配置为输入或输出,以满足不同的应用需求。
- 可配置为8种输入输出模式。
- 引脚电平:0V~3.3V,部分引脚可容忍5V
- 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器,模拟通信协议输出时序等。
- 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等。
GPIO特点
IO口数量:
不同型号的微控制器,芯片,或者开发板上的 GPIO 数量可能不同。通过查阅相应的选型手册、数据手册或者开发板文档,可以了解每个型号上可用的 GPIO 引脚数量和功能。
快速翻转:
GPIO 可以快速地翻转其输出状态。每次翻转的速度可能取决于具体的芯片型号和工作频率。一些微控制器甚至可以在极短的时间内完成翻转,例如在几个时钟周期内。
中断支持:
每个 GPIO 引脚都可以配置为中断触发源。当引脚的电平状态变化(上升沿、下降沿、或者边沿触发)时,可以触发相应的中断服务程序。这使得 GPIO 可以用于实时事件处理。
工作模式:
GPIO 支持多种工作模式,具体的工作模式可能因芯片型号而异。常见的工作模式包括输入模式、输出模式、复用功能模式(例如,配置 GPIO 为串口、SPI、I2C 等功能)、模拟输入模式等。
在STM32 F1系列中,每个GPIO端口都包含了通用输入和通用输出功能。以下是F1系列IO端口基本结构和工作流程的详细介绍:
F1系列IO端口基本结构:
通用输入:
- 通用输入的外部引脚信号首先经过保护二极管,然后进入输入驱动器。
- 在输入驱动器内部,信号可以经过上拉电阻、下拉电阻、模拟输入(如果配置为模拟输入引脚)、TTL(肖特基触发器)等。
通用输出:
- 通用输出的数据可以通过输出控制器进行读写。
- 输出控制器包含输出数据寄存器(ODR),它存储了输出端口的当前状态。通过对ODR的写入,可以改变输出端口的状态。
执行机构:
执行机构包括P-MOS(P型金属氧化物半导体场效应晶体管)和N-MOS(N型金属氧化物半导体场效应晶体管)管。这些管通过输出控制器的控制机构来进行控制。
控制机构:
控制机构是一个逻辑电路,通过外部输入(例如输入数据寄存器的值)来控制执行机构中的P-MOS和N-MOS管的通断状态。
复用功能:
GPIO引脚可以配置为复用功能输入或输出。在复用功能输入模式下,TTL(肖特基触发器)用于将引脚连接到片上外设,例如USART、SPI、I2C等。
工作流程
通用输入流程:
- 外部引脚信号通过保护二极管进入输入驱动器。
- 输入驱动器内部可能包括上拉电阻、下拉电阻、模拟输入等功能。
- TTL肖特基触发器将输入信号传递到复用功能输入或者通过输入数据寄存器(IDR)读取。
通用输出流程:
- 数据可以通过输出数据寄存器(ODR)进行写入,改变输出端口的状态。
- 输出控制器的执行机构控制P-MOS和N-MOS管,决定引脚的输出状态。
- 复用功能输出可以通过片上外设进行配置。
这样的结构和流程使得F1系列的GPIO引脚可以适应多种应用场景,既可以用于普通的输入输出,也可以配置为连接各种片上外设,提供更多的灵活性和功能。
通过配置GPIO的端口配置寄存器,端口可以配置成以下8种模式。
2.2 LED闪烁&LED流水灯&蜂鸣器
LED闪烁
代码
#include "stm32f10x.h" // Device header
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 设置函数的时钟
// 配置端口模式
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// GPIO_SetBits(GPIOC,GPIO_Pin_13); // 设置为高电平
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
while(1)
{
}
}
注意事项:
注意!!!这里一定要在魔术棒/ C/C++的页面下把C99mode勾选上!否则会报错,就算是拉到前面也不会有正确的反馈。
A0引脚是负极,负极是高电平的话,和正极相等,不会产生电流,灯就不亮
用“指南者”板子的,要把GPIOA改为GPIOB,可以亮绿灯,具体的亮灯规则还需要参考《零死角》那本书
下面是实物图
下面开始LED闪烁的步骤。需要延时函数
#include "stm32f10x.h" // Device header
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 设置函数的时钟
// 配置端口模式
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// GPIO_SetBits(GPIOC,GPIO_Pin_13); // 设置为高电平
// GPIO_ResetBits(GPIOA,GPIO_Pin_0);
//GPIO_SetBits(GPIOA,GPIO_Pin_0);
// GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET); // 设置A0口为低电平,低电平点亮。
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);// 设置A0口为高电平,LED熄灭。
while(1)
{
}
}
复制以下这两个文件
文件内容如下:Delay.c
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
Delay.h
#ifndef __DELAY_H
#define __DELAY_H
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
复制Delay文件
点击ADD Files按钮,然后选择system中的Delay.h 和Delay.c 添加进去。
添加头文件。
输入下面的代码,就变成点灯大师了。
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 设置函数的时钟
// 配置端口模式
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// GPIO_SetBits(GPIOC,GPIO_Pin_13); // 设置为高电平
// GPIO_ResetBits(GPIOA,GPIO_Pin_0);
//GPIO_SetBits(GPIOA,GPIO_Pin_0);
// GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET); // 设置A0口为低电平,低电平点亮。
// GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);// 设置A0口为高电平,LED熄灭。
while(1)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET); // 设置A0口为低电平,低电平点亮。
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);// 设置A0口为高电平,LED熄灭。
Delay_ms(500);
}
}
LED流水灯
首先看一下面包板的接线图
实物图
代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 设置函数的时钟
// 配置端口模式
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1)
{
GPIO_Write(GPIOA,~0x0001); // 0000 0000 0000 0001
Delay_ms(500);
GPIO_Write(GPIOA,~0x0002); // 0000 0000 0000 0010
Delay_ms(500);
GPIO_Write(GPIOA,~0x0004); // 0000 0000 0000 0100
Delay_ms(500);
GPIO_Write(GPIOA,~0x0008); // 0000 0000 0000 1000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0010); // 0000 0000 0001 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0020); // 0000 0000 0010 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0040); // 0000 0000 0100 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0080); // 0000 0000 1000 0000
Delay_ms(500);
}
}
蜂鸣器
面包板接线图
A15,B3,B4这几个接口不能选,如果要用作普通端口的话,需要进行一些配置
实物图
代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 设置函数的时钟
// 配置端口模式
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12); // 0000 0000 0000 0001
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12); // 0000 0000 0000 0010
Delay_ms(100);
GPIO_ResetBits(GPIOB,GPIO_Pin_12); // 0000 0000 0000 0001
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12); // 0000 0000 0000 0010
Delay_ms(700);
}
}
3. GPIO输入
按键介绍:常见的输入设备,按下导通,松手断开
按键抖动:由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴随有一连串的抖动
传感器模块
传感器模块:传感器元件(光敏电阻/热敏电阻/红外接收管等)的电阻会随外界模拟量的变化而变化,通过与定值电阻分压即可得到模拟电压输出,再通过电压比较器进行二值化即可得到数字电压输出。
按键控制LED
接线图
实物图
代码
使用模块化编程,新建Hardware文件夹,
Key.c
#include "stm32f10x.h"
#include "Delay.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 设置函数的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
uint8_t Key_GetNum(void)
{
uint8_t KeyNum=0;
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0);
Delay_ms(20);
KeyNum=1;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0);
Delay_ms(20);
KeyNum=2;
}
return KeyNum;
}
Key.h
#ifndef __KEY_H
#define __KEY_H
void Key_Init(void);
uint8_t Key_GetNum(void);
#endif
LED.c
#include "stm32f10x.h"
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 设置函数的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
void LED1_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void LED1_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1)==0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}else{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
}
void LED2_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2)==0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}else{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
}
void LED1_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void LED2_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
void LED2_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
LED.h
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED1_ON(void);
void LED1_OFF(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED2_Turn(void);
void LED1_Turn(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
uint8_t KeyNum;
int main(void)
{
LED_Init();
Key_Init();
while(1)
{
KeyNum=Key_GetNum();
if(KeyNum==1)
{
LED1_Turn();
}
if(KeyNum==2)
{
LED2_Turn();
}
}
}
光敏传感器控制蜂鸣器
接线图
实物图
代码
Buzzer.c
#include "stm32f10x.h"
void Buzzer_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 设置函数的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
void Buzzer_ON(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_OFF(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12)==0)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}else{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
}
Buzzer.h
#ifndef __BUZZER_H
#define __BUZZER_H
void Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
void Buzzer_Turn(void);
#endif
LightSensor.c
#include "stm32f10x.h"
void LightSensor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 设置函数的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
uint8_t LightSensor_Get(void)
{
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13);
}
LightSensor.h
#ifndef __LIGHTSENSOR_H
#define __LIGHTSENSOR_H
void LightSensor_Init(void);
uint8_t LightSensor_Get(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"
int main(void)
{
Buzzer_Init();
LightSensor_Init();
while(1)
{
if(LightSensor_Get()==1)
{
Buzzer_ON();
}else{
Buzzer_OFF();
}
}
}
4. OLED
4.1 简介
OLED(Organic Light Emitting Diode) 有机发光二极管 OLED显示屏:性能优异的新型显示屏,具有功耗低、相应速度快、 宽视角、轻薄柔韧等特点 0.96寸OLED模块:小巧玲珑、占用接口少、简单易用,是电子设计 中非常常见的显示屏模块 供电:3~5.5V,通信协议:I2C/SPI,分辨率:128x64
调试方式
- 串口调试:通过串口通信,将调试信息发送到电脑端,电脑使用串口助手显示调试信息
- 显示屏调试:直接将显示屏连接到单片机,将调试信息打印在显示屏上
- Keil调试模式:借助Keil软件的调试模式,可使用单步运行、设置断点、查看寄存器及变量等功能
4.2 OLED显示屏幕
接线图
实物图
代码
复制这3个文件
然后添加到驱动文件目录
OLED.c
#include "stm32f10x.h"
#include "OLED_Font.h"
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C开始
* @param 无
* @retval 无
*/
void OLED_I2C_Start(void)
{
OLED_W_SDA(1);
OLED_W_SCL(1);
OLED_W_SDA(0);
OLED_W_SCL(0);
}
/**
* @brief I2C停止
* @param 无
* @retval 无
*/
void OLED_I2C_Stop(void)
{
OLED_W_SDA(0);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_SDA(Byte & (0x80 >> i));
OLED_W_SCL(1);
OLED_W_SCL(0);
}
OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
OLED_W_SCL(0);
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x00); //写命令
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x40); //写数据
OLED_I2C_SendByte(Data);
OLED_I2C_Stop();
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //上电延时
{
for (j = 0; j < 1000; j++);
}
OLED_I2C_Init(); //端口初始化
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
OLED.h
#ifndef __OLED_H
#define __OLED_H
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
#endif
OLED_Font.h
#ifndef __OLED_FONT_H
#define __OLED_FONT_H
/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 0
0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2
0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,
0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3
0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,
0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4
0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,
0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5
0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,
0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6
0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 7
0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,
0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 8
0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,
0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 9
0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,
0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 10
0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,
0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 11
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 12
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 13
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 14
0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,
0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 15
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 16
0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 17
0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,
0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 18
0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,
0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 19
0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,
0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 20
0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,
0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 21
0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,
0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 22
0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,
0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 23
0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,
0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 24
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 25
0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 26
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 27
0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,
0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 28
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 29
0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 30
0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,
0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 31
0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,
0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 32
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,
0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 33
0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,
0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 34
0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,
0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 35
0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,
0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 36
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 37
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 38
0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,
0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 39
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 40
0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 41
0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,
0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 42
0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,
0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 43
0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,
0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 44
0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,
0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 45
0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,
0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 46
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 47
0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,
0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 48
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 49
0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,
0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 50
0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,
0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 51
0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,
0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 52
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 53
0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,
0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 54
0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,
0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 55
0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,
0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 56
0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,
0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 57
0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,
0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 58
0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,
0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 59
0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 60
0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,
0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 61
0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 62
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 63
0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 64
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 65
0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,
0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 66
0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,
0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 67
0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,
0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 68
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 69
0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 70
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 71
0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,
0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 72
0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 73
0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,
0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 74
0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,
0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 75
0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 76
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 77
0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,
0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 78
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 79
0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,
0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 80
0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,
0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 81
0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 82
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 83
0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,
0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 84
0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,
0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 85
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 86
0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,
0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 87
0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 88
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 89
0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 90
0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,
0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 91
0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 92
0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,
0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 93
0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
int main(void)
{
OLED_Init();
OLED_ShowChar(1,1,'A');
OLED_ShowString(1,3,"HelloWorld!");
OLED_ShowNum(2,1,12345,6);
OLED_ShowSignedNum(2,7,-66,2);
OLED_ShowHexNum(3,1,0xAA55,4);
OLED_ShowBinNum(4,1,0xAA55,16);
while(1)
{
}
}
5. EXIT外部中断
5.1 中断系统
本节讲解中断系统
什么是中断
在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回。
中断执行流程图
STM32中断
68个可屏蔽中断通道,包含EXTI、TIM、ADC,USART、SPI、I2C、RTC等多个外设
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级 进行分组,进一步设置抢占优先级和响应优先级
什么是NVIC?
NVIC是内核外设,是CPU的小助手
NVIC优先级分组
的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
EXTI外部中断
EXTI(Extern Interrupt)可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
支持的触发方式:上升沿/下隆沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
触发响应方式:中断响应/事件响应
什么样的设备,适合使用外部中断呢??
对于STM32而言,想要获取的信号是外部驱动的很快的突发信号。
旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
类型:机械触点式/霍尔传感器式/光栅式
5.2 对射式红外传感器计次
接线图
实线图
代码
CountSensor.c
#include "stm32f10x.h"
uint16_t Num;
void CountSensor_Init(void)
{
// 第一步,配置RCC,把EXTI设计到的外设时钟都打开
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 设置函数的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// 第二步,配置GPIO,选择我们的端口为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
// 第三步,配置AFIO, 选择GPIO,连接到后面的EXTI
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
// 第四步,配置EXTI,选择边沿触发方式,比如上升沿,下降沿,或者双边沿
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd=ENABLE; // 开始中断
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; // 指定外部中断线的模式。第一种是中断模式,第二种是事件模式
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //选择边沿触发方式,比如上升沿,下降沿,或者双边沿
EXTI_Init(&EXTI_InitStructure); // 因为上面是GPIO_Mode_IPU设置为高电平,所以触发中断是下降
// 第五步,配置NVIC,给中断选择一个合适的优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
}
uint16_t CountSensor_Get(void)
{
return Num;
}
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14)==SET)
{
Num++;
EXTI_ClearITPendingBit(EXTI_Line14); // 清除中断标志位
}
}
CountSensor.h
#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H
void CountSensor_Init(void);
uint16_t CountSensor_Get(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
OLED_Init();
CountSensor_Init();
OLED_ShowString(1,1,"Count:");
while(1)
{
OLED_ShowNum(1,7,CountSensor_Get(),5);
}
}
5.3 旋转编码器计次
接线图
实线图
代码
Encoder.c
#include "stm32f10x.h"
int16_t Encoder_Num;
void Encoder_Init(void)
{
// 第一步,配置RCC,把EXTI设计到的外设时钟都打开
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 设置函数的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// 第二步,配置GPIO,选择我们的端口为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; // 设置GPIO口模式,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
// 第三步,配置AFIO, 选择GPIO,连接到后面的EXTI
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
// 第四步,配置EXTI,选择边沿触发方式,比如上升沿,下降沿,或者双边沿
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line0 | EXTI_Line1;
EXTI_InitStructure.EXTI_LineCmd=ENABLE; // 开始中断
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; // 指定外部中断线的模式。第一种是中断模式,第二种是事件模式
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //选择边沿触发方式,比如上升沿,下降沿,或者双边沿
EXTI_Init(&EXTI_InitStructure); // 因为上面是GPIO_Mode_IPU设置为高电平,所以触发中断是下降
// 第五步,配置NVIC,给中断选择一个合适的优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStructure);
}
int16_t Encoder_GetNum(void)
{
int16_t Temp;
Temp=Encoder_Num;
Encoder_Num=0;
return Temp;
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)==SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
{
Encoder_Num--;
}
EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志位
}
}
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1)==SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)
{
Encoder_Num++;
}
EXTI_ClearITPendingBit(EXTI_Line1); // 清除中断标志位
}
}
Encoder.h
#ifndef __ENCODER_H
#define __ENCODER_H
void Encoder_Init(void);
int16_t Encoder_GetNum(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t Num;
int main(void)
{
OLED_Init();
Encoder_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
Num+=Encoder_GetNum();
OLED_ShowSignedNum(1,5,Num,5);
}
}
6. TIM定时中断
参考资料
【STM32】STM32系列教程汇总(更新...)_stm32教程-CSDN博客
https://blog.csdn.net/qq_63090569/article/details/127939523
STM32f103c8t6小白学习笔记(一)从点灯开始之USB转TTL_usb转ttl怎么接线-CSDN博客
STM32学习笔记(一)从点灯开始之ST-LINK V2_stlink v2接线-CSDN博客
【正点原子STM32】GPIO(简介、IO端口基本结构、GPIO的八种模式、GPIO寄存器、通用外设驱动模型、GPIO配置步骤、编程实战)_gpio 驱动能力-CSDN博客