1 STM32简介
- ST是ST公司,M就是MCU,32就是32位,STM32是ST公司基于ARM Core-M内核开发的32位微控制器
- 应用在嵌入式领域,如智能车、物联网、工业控制、娱乐电子产品
1 ARM
- 既指ARM公司,也指ARM处理器内核
- ARM是全球领先的半导体知识产权(IP)提供商,全世界超过95%的智能手机和平板电脑都采用ARM架构
- ARM公司设计ARM内核,半导体厂商完善内核周边电路并生产芯片(开放合作)
本次学习采用:STM32F103C8T6型号
- 产品类型:F = 通用类型
- 产品子系列:101 = 基本型 102 = USB型 103 = 增强型 105或107 = 互联型
- 引脚数目:T=36脚 C=48脚 R=64脚 V=100脚 Z=144脚
- 闪存存储器容量:4=16K 6=32K 8=64K B=128K C=256K D=384K E=512K
- 封装:H=BGA T=LQFP U=VFQFPN Y=WLCSP64
- 温度范围:6=工业级,-40~85℃ 7=工业级,-40~105℃
2 外设
STM32F1型号的所有外设如下:
3 系统结构
4 引脚定义
红色:电源相关
蓝色:最小系统相关
绿色:IO口、功能口相关
5 启动配置
Boot引脚的值在上电的一瞬间有效,之后就随意了
6 最小系统电路
只有一块芯片是无法工作的,需要些别的元器件,能工作的最小系统叫做最小系统电路
2 软件安装和新建工程
1 软件安装
- 安装Keil5 MDK
- 安装器件支持包
- 软件注册(破解)
- 安装STLINK驱动(Keil安装目录->ARM->STLink->USBDriver->dpinst_amd64.exe)
- 安装USB转串口驱动(C51已经安装过)
2 新建工程
-
首先先新建一个文件夹用于存放工程
-
点击Project->New uVersion Project,选择对应型号,这里是STM32F103C8
-
在工程目录中新建文件夹Start用于放置启动文件,启动文件在如下所示的三个地方,STM32工程从启动文件开始执行,将这些文件都复制下来粘贴到Start文件夹下,在打开Keil软件,将其添加到工程中。其中启动文件有很多个,我们只能添加一个,这里添加md.s的启动文件,此外,要在Keil的魔术棒->c/c++中添加Start的路径。
-
再在工程中添加组,改名为User,放置自己写的代码文件,包括main.c,注意:这里的main函数必须是int类型,和C51的void类型不同
-
如果使用寄存器进行开发,工程建到这里就可以了,点击魔术棒,进行如下配置
再点击右边的Settings,进行如下配置
-
配置完成后,插上STLink连接STM32(连接方式记得看图),完成之后编译程序,然后点击编译旁边的Load按钮,如果一切正常就会下载到STM32里面(这边容易出现很多异常,建议百度)
-
如果使用标准库函数进行开发,在工程文件夹新建一个Library文件夹,用于放置库函数文件,将如下路径的文件全部复制到Library文件夹下
再打开inc文件夹,存放的都是头文件,同样全选复制粘贴到Library文件夹下
同样回到Keil工程,添加Library组,将刚刚Library文件夹中的文件添加到工程
-
但是没办法直接使用,还需要在如下位置将三个文件添加到User文件夹中并添加到工程
-
最后在魔术棒中进行对应的配置,包括宏定义和头文件路径
启动文件选择如下所示:
整个工程的架构如下:
3 GPIO
1 GPIO简介
- GPIO(General Purpose Input Output) 通用输入输出口
- 配置8种输入输出模式
- 引脚电平:0-3.3v,部分引脚可以容忍5v
- 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
- 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接受数据等
2 GPIO基本结构
所有GPIO都是挂载在APB2总线上,命名是GPIOA、GPIOB…引脚命名就是PA0-PA15、PB0-PB15…
这里的寄存器是32位寄存器,但是端口只有16位,所以寄存器低16位对应有端口,而高16位没有
寄存器负责存储数据,驱动器用来增大驱动能力
3 GPIO位结构
这里的肖特基触发器应该为施密特触发器,作用是如果输入电压大于某一阈值,输出电压就会变为高电平。如果输入电压小于某一阈值,输出电压就会变为低电平。可以有效避免因信号波动造成的输出抖动现象
位设置/清除寄存器,可以保证只操作某一位而不影响其他位,如果要使某一位变成1,那么在位设置寄存器的那一位置1,其他位置0,可以保证输出的那一位被置为1,其他位不变。同理位清除寄存器也类似
在推挽输出模式下,STM32对IO口具有绝对控制权,高低电平都由STM32说了算。
在开漏输出模式下,P-MOS无效,只有N-MOS在工作,数据寄存器为1时,下管断开,相当于输出断开,也就是高阻模式;数据寄存器为0时,下管导通,输出直接接到VSS,也就是输出低电平。这种模式下只有低电平有驱动能力,高电平没有驱动能力。
关闭输出模式下,两个MOS管都无效,也就是输出关闭,端口的电平由外部信号控制
4 GPIO模式
输出的时候,输入是有效的。但是输入的时候输出是无效的
1 浮空/上拉/下拉输入
2 模拟输入(接ADC的时候专属使用)
3 开漏输出/推挽输出
4 复用开漏/推挽输出
5 LED、蜂鸣器的硬件电路
6 面包板
7 KeilKill.bat小工具
编译后产生的中间文件会占用较大内存,如果需要分享工程给别人这样会比较耗费时间,将KeilKill.bat文件放置在工程根目录下,双击会清除编译产生的中间结果,这样会使文件迅速瘦身,助于分享给别人
8 程序基本流程
-
开启GPIO外设时钟,使用RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE),第一个参数的要开启的APB2外设
-
定义结构体变量,将其三个属性进行赋值,然后进行初始化
GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //12号引脚 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //进行初始化
-
这样就可以操作GPIO了,以上开启的是GPIOB,所以可以操作GPIOB了,以下是些示例代码
//GPIO控制蜂鸣器响
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
Delay_ms(100);
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
Delay_ms(700);
//GPIO控制LED流水灯效果
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);
//GPIO控制LED单个闪烁
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); Delay_ms(500);
9 按键
- 按键:常见的输入设备,按下导通,松手断开
- 按键抖动:由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴随有一连串的抖动
10 传感器模块
传感器模块:传感器元件(光敏电阻/热敏电阻/红外接收管等)的电阻会随外界模拟量的变化而变化,通过与定值电阻分压即可得到模拟电压输出,再通过电压比较器进行二值化即可得到数字电压输出
11 按键、传感器模块的硬件电路
12 C语言内容补充
1 数据类型
STM32和51单片机不同,多了short,51中int是16位的。
这里long和int范围一样,只用long long是64位
2 宏定义
-
关键字:#define
-
用途:用一个字符串代替一个数字,便于理解,防止出错;提取程序中经常出现的参数,便于快速修改
-
定义宏定义:
#define ABC 12345 -
引用宏定义:
int a = ABC; //等效于int a = 12345;
3 typedef
- 关键字:typedef
- 用途:将一个比较长的变量类型名换个名字,便于使用
- 定义typedef:
typedef unsigned char uint8_t; - 引用typedef:
uint8_t a; //等效于unsigned char a; - 和宏定义的区别:
- 宏定义新名字在左边,而typedef新名字在右边
- 宏定义不需要分号,typedef需要分号
- 宏定义可以换任何名字,而typedef只能专门换变量类型(会对变量名字进行检查,不是变量名字不行)
4 结构体
-
关键字:struct
-
用途:数据打包,不同类型变量的集合
-
定义结构体变量:
struct{char x; int y; float z;} StructName; struct后面花括号里面的是结构体的内容,也就是结构体中需要存放哪些类型的哪些变量
因为结构体变量类型较长,所以通常用typedef更改变量类型名
使用typedef定义如上结构体:
typedef struct{ char x; int y; float z; } StructName_t; StructName_t c; c.x = 'A'; c.y = 123; c.z = 3.14;
-
引用结构体成员:
StructName.x = ‘A’;
StructName.y = 66;
StructName.z = 1.23;或 pStructName->x = ‘A’; //pStructName为结构体的地址
pStructName->y = 66;
pStructName->z = 1.23;
5 枚举
-
关键字:enum
-
用途:定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合
-
定义枚举变量:
enum{FALSE = 0, TRUE = 1} EnumName;
因为枚举变量类型较长,所以通常用typedef更改变量类型名 使用typedef更改变量类型名使用方法:
typedef enum{ FALSE=0, TRUE=1 } EnumName_t; EnumName_t a; a = FALSE; a = TRUE;
-
引用枚举成员:
EnumName = FALSE;
EnumName = TRUE;