基本概念
STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器
本课程使用图中红框的型号STM32F103C8T6
系列:主流系列STM32F1
内核:ARM Cortex-M3
主频:72MHz
RAM:20K(SRAM)
ROM:64K(Flash)
供电:2.0~3.6V(标准3.3V)
封装:LQFP48
什么是ARM
ARM既指ARM公司,也指ARM处理器内核
ARM公司是全球领先的半导体知识产权(IP)提供商,全世界超过95%的智能手机和平板电脑都采用ARM架构
ARM公司设计ARM内核,半导体厂商完善内核周边电路并生产芯片
产品名称解析
-
安装Keil5 MDK
-
安装器件支持包
-
软件注册
-
安装STLINK驱动
-
安装USB转串口驱动
启动文件的选取
根据不同的型号,选择对应缩写 选择启动文件 。
- 建立工程文件夹,Keil中新建工程,选择型号
- 工程文件夹里建立Start、Library、User等文件夹,复制固件库里面的文件到工程文件夹
- 工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里
- 工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹
- 工程选项,C/C++,Define内定义USE_STDPERIPH_DRIVER
- 工程选项,Debug,下拉列表选择对应调试器,Settings,Flash Download里勾选Reset and Run
系统结构:
GPIO位结构的介绍
施密特触发器的作用
施密特触发器的作用:对输入电压进行整形,原理:当电压大于某个阈值,输出就会瞬间升为高电平,小于就是瞬间降为低电平
因为这个引脚时外界输入的,虽然是数字信号,但也可能产生各种失真。
一个夹杂了波动的高低变化的电平信号,如果没有施密特触发器很可能因为干扰而导致误判。
而如果有的话,设置一个阈值,高于上限输出高,低于上限输出低电平。当瞬间变为高,输出就不会再变化,只有下次低于阈值的下线才会变为低电平。反之亦然。
GPIO(General Purpose Input Output)通用输入输出口
可配置为8种输入输出模式
typedef enum
{ GPIO_Mode_AIN = 0x0, //模拟输入
GPIO_Mode_IN_FLOATING = 0x04, //浮空输入
GPIO_Mode_IPD = 0x28, //下拉输入
GPIO_Mode_IPU = 0x48, //上拉输入
GPIO_Mode_Out_OD = 0x14,//开漏输出
GPIO_Mode_Out_PP = 0x10, //推挽输出
GPIO_Mode_AF_OD = 0x1C, //复用开漏
GPIO_Mode_AF_PP = 0x18 //复用推挽
}GPIOMode_TypeDef;
-
引脚电平:0V~3.3V,部分引脚可容忍5V
-
输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等、
- stm32的寄存器时32位 ,而输入端口只有16位,因此只能用到低16位,高16位用不到
- 使用APB2来操作寄存器
- 电灯操作,需要使用驱动器来增大驱动能力
推挽模式和开漏输出模式
推挽模式:P-MOS和N-MOS均有效,数据寄存器为1时,上管通,下开,输出接VDD,即高电平
数据寄存器为0则相反,即输出低电平。这种模式高低电平均为较强的驱动能力,因此也叫强推输出模式。且该模式下,stm32对I/O口具有绝对的控制权,高低电平都由stm32说了算。
开漏输出模式:P-MOS无效,只有N-MOS工作,数据寄存器为1时,下管断开,这时的输出相当于断开,也就是高阻模式。数据寄存器为0时,下管导通,输出直接接VSS,也就是输出低电平。这种模式,只有低电平有驱动能力,高电平没有。
作用:开漏模式可以作为通信协议的驱动方式,比如I2C通信的引脚就采用开漏模式,在多机通信情况下,该模式避免了各个设备之间的干扰。
开漏输出的主要用途:
可以用于输出5V的电平信号,比如在I/O口外接一个上拉电阻到5V的电源。当输出低电平时,由内部的N-MOS直接接VSS,当输出高电平时,由外部的上拉电阻拉高到5V用于兼容一些5V的设备。这就是开漏输出的主要用途
复用开漏输出和推免输出与开漏输出和推免输出的区别:
上拉和下拉的原因:控制电平的高低
上拉通就是上拉输入模式 高电平
下拉通就是下拉输入模式 低电平
没接触,就是浮空状态,极易受外界影响,为了避免这种不确定性,因此加入了上拉和下拉电阻
这里的阻值比较大,是一种弱上拉
操作STM32的GPIO分3个步骤
使用RCC开启GPIO的时钟
使用GPIO_Init函数初始化GPIO
使用输入输出的函数控制GPIO口
(共涉及RCC和GPIO两个外设)
RCC的常用库函数
在Llibrary文件下找到stm32f10x_rcc.h文件,一般在该文件的最下面都是所有库函数的声明。
其实里面的大多数用不到,最常用的只有三个函数,
RCC AHB 外设时钟控制(启用或禁用AHB外设时钟)
RCC_AHBPeriph:选择那个外设
NewState: ENABLE or DISABLE:使能或失能
RCC APB2外设时钟控制
RCC APB1外设时钟控制
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
如果不清楚哪个外设连接在哪个总线上,就可以在下面的列表中去找
GPIO的库函数
进入下面文件拉到最底下拉到最下面:
//调用该函数,所指定的GPIO外设会被复位
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);//也是复位AFIO外设
//GPIO_Init非常重要!!!!
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);//用结构体的参数来初始化GPIO口,首先定义结构体变量,然后再给结构体赋值,最后调用这个函数
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);//可以把结构体变量赋一个默认值
//接下来的四个就是GPIO的读取函数,然后跟着的4个是GPIO的写入函数,剩下的暂时用不到
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
//第一个参数设置外设,这个函数可以把指定的端口设置位高电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//指定为低电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//通过第三个参数来设定指定的端口
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
//第二个参数,使这个函数可以同时对16个端口进行写入操作
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
//下面的暂时用不到
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
全选I/O口
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|..;
原理
每一位对应一个外设
使用库函数的方法:
固件手册
直接搜错GPIO初始化,参考别人的文章。
LED和蜂鸣器简介
LED:发光二极管,正向通电点亮,反向通电不亮
有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定
无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音
GPIO输入
实现功能:按键:消抖 ,延时处理
按键简介
按键:常见的输入设备,按下导通,松手断开
按键抖动:由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴随有一连串的抖动
传感器模块简介
传感器模块:传感器元件(光敏电阻/热敏电阻/红外接收管等)的电阻会随外界模拟量的变化而变化,通过与定值电阻分压即可得到模拟电压输出,再通过电压比较器进行二值化即可得到数字电压输出
数据类型:
C语言宏定义
关键字:#define
用途:用一个字符串代替一个数字,便于理解,防止出错;提取程序中经常出现的参数,便于快速修改
定义宏定义:
#define ABC 12345
引用宏定义:
int a = ABC; //等效于int a = 12345;
C语言typedef
关键字:typedef
用途:将一个比较长的变量类型名换个名字,便于使用
定义typedef:
typedef unsigned char uint8_t;
引用typedef:
uint8_t a; //等效于unsigned char a;
关键字:struct
用途:数据打包,不同类型变量的集合
C语言结构体
定义结构体变量:
struct{char x; int y; float z;} StructName;
因为结构体变量类型较长,所以通常用typedef更改变量类型名
引用结构体成员:
StructName.x = ‘A’;
StructName.y = 66;
StructName.z = 1.23;
或 pStructName->x = ‘A’; //pStructName为结构体的地址 pStructName->y = 66;
pStructName->z = 1.23;
按键读取函数//读取输入寄存器的某一位数据
C语言枚举
枚举关键字:
enum
用途:定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合
定义枚举变量:
enum{FALSE = 0, TRUE = 1} EnumName;
因为枚举变量类型较长,所以通常用typedef更改变量类型名
引用枚举成员:
EnumName = FALSE;
EnumName = TRUE;
调试方式
串口调试:通过串口通信,将调试信息发送到电脑端,电脑使用串口助手显示调试信息 (信息流,一行一行)
显示屏调试:直接将显示屏连接到单片机,将调试信息打印在显示屏上
Keil调试模式:借助Keil软件的调试模式,可使用单步运行、设置断点、查看寄存器及变量等功能
电灯调试 调试部分加个灯
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//读取整个输入寄存器的数据
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
//读取输输出寄存器的某一位数据
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//读取整个输出寄存器的数据
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
实现功能:
遮住,输出指示灯灭,代表输出高电平
松手,输出指示灯亮,代表输出低电平
OLED简介
OLED(Organic Light Emitting Diode):有机发光二极管
OLED显示屏:性能优异的新型显示屏,具有功耗低、相应速度快、宽视角、轻薄柔韧等特点
0.96寸OLED模块:小巧玲珑、占用接口少、简单易用,是电子设计中非常常见的显示屏模块
供电:3~5.5V,通信协议:I2C/SPI,分辨率:128*64