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 新建工程

  1. 首先先新建一个文件夹用于存放工程
  2. 点击Project->New uVersion Project,选择对应型号,这里是STM32F103C8
  3. 在工程目录中新建文件夹Start用于放置启动文件,启动文件在如下所示的三个地方,STM32工程从启动文件开始执行,将这些文件都复制下来粘贴到Start文件夹下,在打开Keil软件,将其添加到工程中。其中启动文件有很多个,我们只能添加一个,这里添加md.s的启动文件,此外,要在Keil的魔术棒->c/c++中添加Start的路径。

图01

图02

图03

图04

图05

图06

图07

4.再在工程中添加组,改名为User,放置自己写的代码文件,包括main.c,注意:这里的main函数必须是int类型,和C51的void类型不同

图08

5.如果使用寄存器进行开发,工程建到这里就可以了,点击魔术棒,进行如下配置

图09

再点击右边的Settings,进行如下配置

图10

6.配置完成后,插上STLink连接STM32(连接方式记得看图),完成之后编译程序,然后点击编译旁边的Load按钮,如果一切正常就会下载到STM32里面(这边容易出现很多异常,建议百度)

7.如果使用标准库函数进行开发,在工程文件夹新建一个Library文件夹,用于放置库函数文件,将如下路径的文件全部复制到Library文件夹下

图11

图12

再打开inc文件夹,存放的都是头文件,同样全选复制粘贴到Library文件夹下

图13

图14

同样回到Keil工程,添加Library组,将刚刚Library文件夹中的文件添加到工程

8.但是没办法直接使用,还需要在如下位置将三个文件添加到User文件夹中并添加到工程

图15

9.最后在魔术棒中进行对应的配置,包括宏定义和头文件路径

图16


启动文件选择如下所示:


整个工程的架构如下:

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. 这里的肖特基触发器应该为施密特触发器,作用是如果输入电压大于某一阈值,输出电压就会变为高电平。如果输入电压小于某一阈值,输出电压就会变为低电平。可以有效避免因信号波动造成的输出抖动现象
  2. 位设置/清除寄存器,可以保证只操作某一位而不影响其他位,如果要使某一位变成1,那么在位设置寄存器的那一位置1,其他位置0,可以保证输出的那一位被置为1,其他位不变。同理位清除寄存器也类似
  3. 在推挽输出模式下,STM32对IO口具有绝对控制权,高低电平都由STM32说了算。
  4. 在开漏输出模式下,P-MOS无效,只有N-MOS在工作,数据寄存器为1时,下管断开,相当于输出断开,也就是高阻模式;数据寄存器为0时,下管导通,输出直接接到VSS,也就是输出低电平。这种模式下只有低电平有驱动能力,高电平没有驱动能力。
  5. 关闭输出模式下,两个MOS管都无效,也就是输出关闭,端口的电平由外部信号控制


4 GPIO模式

输出的时候,输入是有效的。但是输入的时候输出是无效的

1 浮空/上拉/下拉输入

2 模拟输入(接ADC的时候专属使用)

3 开漏输出/推挽输出

4 复用开漏/推挽输出


5 LED、蜂鸣器的硬件电路


6 面包板


7 KeilKill.bat小工具

编译后产生的中间文件会占用较大内存,如果需要分享工程给别人这样会比较耗费时间,将KeilKill.bat文件放置在工程根目录下,双击会清除编译产生的中间结果,这样会使文件迅速瘦身,助于分享给别人

8 程序基本流程

  1. 开启GPIO外设时钟,使用RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE),第一个参数的要开启的APB2外设

  2. 定义结构体变量,将其三个属性进行赋值,然后进行初始化

    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);  //进行初始化
    

  3. 这样就可以操作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;

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐