STM32介绍

基于ARM Cortex-M内核开发的32位微控制器

STM32F103C8T6

  • 内核:ARM Cortex-M3
  • 主频:72MHz
  • RAM:20K(SRAM)
  • ROM:64K(Flash)
  • 供电:2.0~3.6V(标准3.3V)
  • 封装:LQFP48

片上资源/外设

image-20240722112627204

  • NVIC嵌套向量中断控制器:内核里面用于管理中断的设备,比如配置中断优先级
  • SysTick系统滴答定时器:内核里的定时器,给操作系统(如FreeRTOS、UCOS)提供定时服务,定时进行任务切换的功能
  • RCC复位和时钟控制:外设在上电的情况下默认没有时钟,不给时钟情况下,操作无效,外设也不会工作,这样的目的是降低功耗。所以在操作外设前要先操作时钟,使用RCC完成时钟的使能。
  • GPIO通用IO口
  • AFIO复用IO口:完成复用功能端口的重定义,还有中断端口的配置
  • EXTI外部中断:配置好外部中断后,当引脚有电平变化时,可以触发中断,让CPU来处理任务
  • TIM定时器:高级定时器、通用定时器、基本定时器,不仅可以完成定时中断,还可以完成测频率、生成PWM波形、配置成专用的编码器接口等
  • ADC模数转换器:STM32内置了12位的AD转换器,可以直接读取IO口的模拟电压值,无需外部连接AD芯片
  • DMA直接内存访问:帮助CPU完成大量数据搬运功能
  • USART同步/异步串口通信
  • I2C通信、SPI通信:通信协议
  • CAN通信:一般用于汽车领域
  • RTC实时时钟:在STM32内部完成年月日、时分秒的计时功能,且可以接外部备用电池,即使掉电也能正常运行
  • CRC校验:用于判断数据的正确性
  • PWR电源控制:可以让芯片进入睡眠模式等状态,来达到省电的目的
  • BKP备份寄存器:存储器,当系统掉电时,仍可由备用电池保持数据
  • IWDG独立看门狗、WWDG窗口看门狗:当单片机因为电磁干扰死机或者程序设计不合理出现死循环时,看门狗可以及时复位芯片,保持系统稳定
  • DAC数模转换器,可以在IO口直接输出模拟电压
  • FSMC可变静态存储控制器:用于扩展内存,或者配置成其他总线协议,用于某些硬件操作
  • USB OTG:USB主机接口,用OTG功能,可以让STM32作为USB主机去读取其他USB设备

系统结构

image-20240722131306966

  • 左上方Cortex-M3内核,引出3条总线,ICode指令总线、DCode数据总线、System系统总线,ICode与DCode主要用于连接Flash闪存,Flash里是编写的程序,ICode用来加载程序指令,DCode用来加载数据,System系统总线连接到其他上面,比如SRAM存储程序运行数据,AHB(先进高性能总线)系统总线用于挂在主要的外设,APB(先进外设总线)用于连接一般外设,由于AHB和APB的总线协议、总线速度、还有数据传送格式的差异,所以中间需要加两个桥接,来完成数据的转换和缓存。
  • DMA可看作内核CPU的小秘书,比如一些大量的数据搬运。DMA通过DMA总线连接到总线矩阵上,可以拥有和CPU一样的总线控制权,用于访问这些外设。当需要DMA搬运数据时,外设就会通过请求线发送DMA请求,然后DMA就会获得总线控制权,访问并转运数据, 整个过程无需CPU的参与。

引脚定义

image-20240722132842690

红色是电源相关引脚,蓝色是最小系统相关引脚,绿色是IO口、功能口引脚

启动配置

image-20240722134221493

一般情况下,程序都是在Flash程序存储器开始执行,但是某些情况下可以让程序在别的地方开始执行,用来完成特殊的功能。

最小系统电路

image-20240722134832174

新建工程

  • 建立工程文件夹,keil中新建工程、选择型号
  • 工程文件夹中建立Start、Library、User等文件夹,复制固件库中的文件到工程文件中
  • 工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里
  • 工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹
  • 工程选项,C/C++,Define内定义USE_STDPERIPH_DRIVER
  • 工程选项,Debug,下拉列表选择对应调试器,Settings,Flash Download里勾选Reset and Run

工程架构

image-20240723125124871

C语言

宏定义

  • 关键字:#define
  • 用途:用一个字符串代替一个数字,便于理解防止出错;提取程序中经常出现的参数,便于快速修改
  • 定义宏定义:#define ABC 12345
  • 引用宏定义:int a = ABC;//等效int a = 12345;

typedef

  • 用途:将一个比较长的变量类型名换个名字,便于使用
  • 定义:typedef unsigned char uint8_t;
  • 引用 :uint8_t a;//等效于unsigned char a;

结构体

  • 关键字:struct
  • 用途:数据打包,不同类型变量的集合
  • 定义结构体变量 struct{char x;int y;float z;} StructName; 因为结构体变量类型较长,所以通常用typedef更改其变量类型名
  • 引用结构体成员: StructName.x = ‘A’; pStructName->x = ‘A’;//pStructName为结构体的地址

枚举

  • 关键字:enum
  • 用途:定义一个取值受限制的整形变量,用于限制变量取值范围;宏定义的集合
  • 定义枚举变量:enum{FALSE = 0 , TRUE = 1} EnumName;
  • 引用枚举成员:EnumName = FALSE;

GPIO

GPIO简介

  • General Purpose Input Output通用输入输出口
  • 可配置为8种输入输出模式
  • 引脚电平0-3.3V,部分引脚可容忍5V
  • 输出模式下可控制端口输出高低电平,用来驱动LED、控制蜂鸣器、模拟通信协议输出时序
  • 输入模式下可读取端口的高低电平或电压,用于读取按键输入,外界模块电平信号输入、ADC电压采集、模拟通信协议接收数据

GPIO 基本结构

image-20240723130327821

在STM32中,所有GPIO都挂载在APB2外设总线上,其中GPIO名称按照GPIOA、GPIOB…….来标注,每个GPIO外设总共有16个引脚从0到15。

寄存器是特殊的存储器,内核可以通过APB2总线对寄存器进行读写,这样可以完成输出电平和读取电平的功能。寄存器的每一位对应一个引脚,其中输出寄存器写1,对应的引脚就会输出高电平,反之输出低电平;输入寄存器读取为1,就证明对应的端口目前是高电平,反之为低电平。由于STM32为32位的单片机,所以STM32内部的寄存器都是32位的,但是端口只有16位,该寄存器只有低16位对应的有端口,高16位没用到。寄存器只负责存储数据,如要进行其他操作,还需要驱动器来负责增大驱动能力。

GPIO位结构

image-20240723131158763

左侧为三个寄存器,中间为驱动器,右侧为某一个IO口的引脚。

整体可分为两个部分,上面为输入部分,下面为输出部分。VDD、VSS的两个开关,是为了给输入提供一个默认的输入电平(上拉、下拉、浮空),因为对应一个数字的端口,输入不是高电平就是低电平,如果浮空,引脚的输入电平会受外界干扰而改变。上拉输入可称作是默认为高电平的输入模式,下拉输入是默认为低电平的输入模式。

输入部分:

TTL肖特基触发器,其实就是施密特触发器,对输入电压进行整形,当输入电压大于某一阈值,输出电压为高电平,当输入电压小于某一阈值,输出电压为低电平。可以有效避免因信号波动造成的输出抖动现象。

经过施密特触发器整形的波形可以直接写入输入数据寄存器了,再用程序读取输入寄存器对应某一位的数据,就可以知道端口的输入电平。

模拟输入连接到ADC上,因为ADC需要接受模拟量,所以要接触发器前面。

复用功能输入是连接到其他需要读取端口的外设上的,接触发器后面。

**输出部分:**由输出数据寄存器和片上外设控制

两种控制方式通过数据选择器连接到了输出控制部分,如果选择通过输出数据寄存器进行控制,就是普通的IO口输出,写这个数据寄存器的某一位就可以操作对应的某个端口了。

位设置/清除寄存器,可以单独操作输出数据寄存器的某一位,而不影响其他位。因为这个输出数据寄存器同时控制16个端口,并且这个寄存器只能整体读写,所以如果想单独控制其中某一个端口而不影响其他端口,需要特殊的操作方式(按位与、设置位设置/清除寄存器)。

通过信号来控制MOS管开关的导通和关闭,开关负责将IO口接到ADD或者VSS,可选择推挽、开漏或关闭三种输出方式。①在推挽输出模式下,PMOS和NMOS均有效,数据寄存器位1时,上管导通下管断开,输出直接接到VDD,就是输出高电平,反之输出低电平。在此模式下,STM32对IO口具有绝对的控制权。②在开漏输出模式下,PMOS无效,只有NMOS在工作,数据寄存器为1时,输出相当于断开也就是高阻模式,数据寄存器为0时,下管导通,输出直接接到VSS,也就是低电平。这种模式下,只有低电平有驱动能力,高电平没有驱动能力。可作为通信协议的驱动方式,在多机通信的情况下,这个模式可以避免各个设备的相互干扰,另外还可用于输出5V的电平信号③在关闭模式下,是当引脚配置为输入模式时,两个MOS管都无效,端口的电平由外部信号来控制。

8种工作模式

模式名称 性质 特征
浮空输入 数字输入 可读取引脚电平,若引脚悬空,则电平不确定
上拉输入 数字输入 可读取引脚电平,内部连接上拉电阻,悬空时默认高电平
下拉输入 数字输入 可读取引脚电平,内部连接下拉电阻,悬空时默认低电平
模拟输入 模拟输入 GPIO无效,引脚直接接入内部ADC
开漏输出 数字输出 可输出引脚电平,高电平为高阻态,低电平接VSS
推挽输出 数字输出 可输出引脚电平,高电平接VDD,低电平接VSS
复用开漏输出 数字输出 由片上外设控制,高电平为高阻态,低电平接VSS
复用推挽输出 数字输出 由片上外设控制,高电平接VDD,低电平接VSS

浮空输入、上拉输入、下拉输入的电路结构基本一致,区别就是上拉电阻和下拉电阻的连接。在输入模式下,输出驱动器断开,只能输入而不能输出

image-20240723135056437

模拟输入,是ADC模数转换器的专属配置,输出断开,输入的施密特触发器也是关闭的无效状态

image-20240723135253735

开漏/推挽输出的区别在于高电平,开漏是高阻态,推挽是VDD

image-20240723135716581

复用开漏/推挽输出的结构如下,都由片上外设控制

image-20240723135840190

常用函数以及样例

初始化样例

1
2
3
4
5
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructre);

GPIO_Init需要输入两个参数,第一个参数是指定GPIOx(其中X可以为A…G),第二个参数是一个指向GPIO_InitTypeDef结构的指针,因此我们还需要定义一个结构体,通过GPIO_InitTypeDef的方式定义,定义后需要对三个参数进行设置:

  • GPIO_Mode设置:也就是对工作模式进行选择,模拟输入AIN、浮空输入IN_FLOATING、下拉输入IPD、上拉输入IPU、开漏输出Out_OD、推挽输出Out_PP、复用开漏输出AF_OD、复用推挽输出AF_PP
  • GPIO_Pin设置:也就是对引脚进行选择,可以从GPIO_Pin_0~GPIO_Pin_15,共16个引脚,也可以使用GPIO_Pin_All对所有引脚进行选择,也可以使用或(|)的方式进行选择,GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2,使用此方式对引脚012进行选择
  • GPIO_Speed设置:输出速度可选择GPIO_Speed_10MHz,GPIO_Speed_2MHz,GPIO_Speed_50MHz

按样例定义完成后,GPIOB外设的12号引脚就自动被配置为推挽输出、50MHz的速度了,它内部的主要执行逻辑就是读取结构体的参数,执行判断和运算,最后写入到GPIO配置寄存器中

输出操作

常用的四个操作如下

1
2
3
4
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);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
  • SetBits完成对某GPIO外设的某引脚置1
  • ResetBits完成对某GPIO外设的某引脚置0
  • WriteBit(GPIOx,GPIO_Pin_X,Bit_SET/Bit_RESET)
  • Write第一个参数是选定一个GPIO外设,第二个参数是一个4位16进制数,为16个二进制,对应着16个端口,最低位为PA0,如0x0001对应0000 0000 0000 0001即Pin0置1,0x0003对应0000 0000 0000 0011即Pin0\Pin1置1

输入操作

按键介绍

按键:常见的输入设备,按下导通,松手断开

按键抖动:由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴有一连串的抖动

初始化按键可采用上拉输入IPU

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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.......//按其他端口,KeyNum为其他值
    return KeyNum;
}

传感器介绍

传感器元件的电阻会随外界模拟量的变化而变化,通过与定值电阻分压即可得到模拟电压输入,再通过电压比较器进行二值化即可得到数字电压输出

image-20240724090618696

硬件电路

下接按键方式(通常普遍采用)

若采用左侧方式,必须采用上拉输入,否则会出现引脚悬空,输出不确定

image-20240724091049107

上接按键方式

image-20240724091117460

连接方式:VCC接3.3V提供电源,GND接地,DO数字输出随便接一个端口,用于读取数字量,AO模拟输出。

传感器端口可采用上拉输入IPU

image-20240724091554461

输入代码

带有Bit是只读取一个位,无Bit是读取一整个的,每一个都以0/1表示

1
2
3
4
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

样例

image-20240725101848324

函数 作用
OLED_Init(); 初始化
OLED_Clear(); 清屏
OLED_ShowChar(1, 1, ‘A’); 显示一个字符
OLED_ShowString(1, 3, “HelloWorld!”); 显示字符串
OLED_ShowNum(2, 1, 12345, 5); 显示十进制数字
OLED_ShowSignedNum(2, 7, -66, 2); 显示有符号十进制数字
OLED_ShowHexNum(3, 1, 0xAA55, 4); 显示十六进制数字
OLED_ShowBinNum(4, 1, 0xAA55, 16); 显示二进制数字

中断系统

中断概念

  • 中断:在主程序运行进程中,出现了特定的中断触发条件,使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
  • 中断优先级:当有多个中断源同时申请中断,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
  • 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

STM32中断

  • 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
  • 使用NVIC统一管理中断,每个中断通道都有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

NVIC基本结构

image-20240725125614446

  • NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
  • 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
分组方式 抢占优先级 响应优先级
分组0 0位,取值为0 4位,取值为0~15
分组1 1位,取值为0~1 3位,取值为0~7
分组2 2位,取值为0~3 2位,取值为0~3
分组3 3位,取值为0~7 1位,取值为0~1
分组4 4位,取值为0~15 0位,取值为0

EXTI外部中断

  • EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
  • 支持的触发方式:上升沿/下降沿/双边沿/软件触发
  • 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
  • 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
  • 触发响应方式:中断响应/事件响应(中断响应是正常的流程,引脚电平变化触发中断;事件响应不会触发中断,而是触发别的外设操作,属于外设之间的联合工作)

image-20240725130858792

image-20240725131722207

旋转编码器

  • 旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
  • 类型:机械触点式/霍尔传感器式/光栅式

image-20240725132855657

基本流程

  • RCC时钟启用

    1
    2
    3
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//启用GPIOB
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//启用AFIO
    //EXTI与NVIC本身时钟就启用,无需启动
    
  • GPIO端口配置

    1
    2
    3
    4
    5
    
    GPIO_InitTypeDef GPIO_InitStructure;//定义结构体
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);//初始化
    
  • AFIO引脚选择

    1
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//14脚使用GPIOB的Pin14
    
  • EXTI配置

    1
    2
    3
    4
    5
    6
    
    EXTI_InitTypeDef EXTI_InitStructure;//定义结构体
    EXTI_InitStructure.EXTI_Line = EXTI_Line14;//启用14引脚
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断响应
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
    EXTI_Init(&EXTI_InitStructure);//初始化
    
  • NVIC配置

    1
    2
    3
    4
    5
    6
    7
    
    NVIC_InitTypeDef NVIC_InitStructure;//定义结构体
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组形式,2位抢占、2位响应
    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);//初始化
    
  • NVIC配置后,可配置中断响应函数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
    //对射式红外传感器计次
    void EXTI15_10_IRQHandler(void)//名称与通道名对应
    {
      if(EXTI_GetITStatus(EXTI_Line14) == SET)
      {
          CountSensor_Count++;
          Delay_ms(20);
          EXTI_ClearITPendingBit(EXTI_Line14);
      }
    }
    ------------------------------------------------------------
    //旋转编码器计次
    int16_t Encoder_Get(void)
    {
      int16_t Temp;
      Temp = Encoder_Count;
      Encoder_Count = 0;
      return Temp;
    }
    
    void EXTI0_IRQHandler(void)
    {
      if(EXTI_GetITStatus(EXTI_Line0) == SET)
      {
          if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
              Encoder_Count --;
          EXTI_ClearITPendingBit(EXTI_Line0);	
      }
    
    }
    
    void EXTI1_IRQHandler(void)
    {
      if(EXTI_GetITStatus(EXTI_Line1) == SET)
      {
          if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0)
              Encoder_Count ++;
          EXTI_ClearITPendingBit(EXTI_Line1);	
      }
    }
    

TIM定时器

简介

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
  • 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
类型 编号 总线 功能
高级定时器 TIM1、TIM8 APB2 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能
通用定时器 TIM2、TIM3、TIM4、TIM5 APB1 拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器 TIM6、TIM7 APB1 拥有定时中断、主模式触发DAC的功能
  • STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
  • 基本计时器只支持向上计数,通用定时器和高级定时器支持向上计数、向下计数、中央对齐三种模式

image-20240726100337802

image-20240726100344350

image-20240726100348286

定时中断基本结构如下

image-20240726100901682

预分频器时序如下(PSC)

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

image-20240726130217934

计数器时序如下(ARR)

计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)= CK_PSC / (PSC + 1) / (ARR + 1)

image-20240726130720632

计数器无预装与有预装时序

image-20240726131213925

image-20240726131217864

定时中断

  • RCC使能

    1
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    
  • 内部时钟模式

    1
    
    TIM_InternalClockConfig(TIM2);//内部时钟
    
  • 时基单元

    1
    2
    3
    4
    5
    6
    7
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//定义结构体
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式
    TIM_TimeBaseInitStructure.TIM_Period = 10000-1;//周期
    TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1;//预分频器
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重计数值
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
    
  • 使能中断

    1
    
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//更新时触发中断
    
  • NVIC

    1
    2
    3
    4
    5
    6
    7
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    
  • 启动定时器

    1
    
    TIM_Cmd(TIM2,ENABLE);
    

外部时钟

  • RCC使能

    1
    2
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
  • GPIO配置

    1
    2
    3
    4
    5
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
  • 外部时钟模式设置

    1
    
    TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0f);
    
  • 时基单元配置

    1
    2
    3
    4
    5
    6
    7
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_Period = 10-1;
    TIM_TimeBaseInitStructure.TIM_Prescaler = 1-1;
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
    
  • 使能中断

    1
    
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
    
  • NVIC配置

    1
    2
    3
    4
    5
    6
    7
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    
  • 启动定时器

    1
    
    TIM_Cmd(TIM2,ENABLE);
    

输出比较

  • OC(Output Compare)输出比较

输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形

每个高级定时器和通用定时器都拥有4个输出比较通道

高级定时器的前3个通道额外拥有死区生成和互补输出的功能

  • PWM脉冲宽度调制

在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域

参数:频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距

image-20240729143627862

image-20240729143631960

输出比较通道

image-20240729145715682

image-20240729145720153

输出比较模式

模式 描述
冻结 CNT=CCR时,REF保持为原状态
匹配时置有效电平 CNT=CCR时,REF置有效电平
匹配时置无效电平 CNT=CCR时,REF置无效电平
匹配时电平翻转 CNT=CCR时,REF电平翻转
强制为无效电平 CNT与CCR无效,REF强制为无效电平
强制为有效电平 CNT与CCR无效,REF强制为有效电平
PWM模式1 向上计数:CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平向下计数:CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平
PWM模式2 向上计数:CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平向下计数:CNT>CCR时,REF置有效电平,CNT≤CCR时,REF置无效电平

PWM基本结构

image-20240729161134432

image-20240729161222837

参数计算

  • PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)
  • PWM占空比:Duty = CCR / (ARR + 1)
  • PWM分辨率:Reso = 1 / (ARR + 1)

LED呼吸灯

  • 内部时钟配置

    1
    
    TIM_InternalClockConfig(TIM2);
    
  • 时基单元

    1
    2
    3
    4
    5
    6
    7
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_Period = 100-1;//ARR
    TIM_TimeBaseInitStructure.TIM_Prescaler = 720-1;//PSC
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
    
  • 输出比较单元

    1
    2
    3
    4
    5
    6
    7
    
    TIM_OCInitTypeDef TIM_OCinitStructure;
    TIM_OCStructInit(&TIM_OCinitStructure);//给结构体赋初始值
    TIM_OCinitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式
    TIM_OCinitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性
    TIM_OCinitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能
    TIM_OCinitStructure.TIM_Pulse = 50;//CCR的值
    TIM_OC1Init(TIM2,&TIM_OCinitStructure);
    
  • GPIO

    1
    2
    3
    4
    5
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
  • 启动定时器

    1
    
    TIM_Cmd(TIM2,ENABLE);
    
  • 控制PMW调亮度

    1
    2
    3
    4
    
    void PWM_SetCompare1(uint16_t Compare)
    {
      TIM_SetCompare1(TIM2,Compare);
    }
    

舵机

舵机是一种根据输入PWM信号占空比来控制输出角度的装置

输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms

image-20240730090717165

  • PMW配置:内部时钟配置——时基单元——输出比较单元——GPIO——启动定时器
  • 舵机角度函数配置

直流电机

直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转

直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作

TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向

image-20240730091800308

image-20240730091804235

  • PMW配置:内部时钟配置——时基单元——输出比较单元——GPIO——启动定时器
  • 电机配置:复用推挽输出配置GPIO——速度配置函数

输入捕获

  • IC(Input Capture)输入捕获
  • 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
  • 每个高级定时器和通用定时器都拥有4个输入捕获通道
  • 可配置为PWMI模式,同时测量频率和占空比
  • 可配合主从触发模式,实现硬件全自动测量

image-20240805103302644

测频率

image-20240801111809291

  • 测频法:在闸门时间T内,对上升沿计次,得到N,则频率f_x=N / T
  • 测周法:两个上升沿内,以标准频率fc计次,得到N ,则频率f_x=f_c / N
  • 中界频率:测频法与测周法误差相等的频率点f_m=√f_c / T

测频法要求信号频率高一些,测周法要求信号频率低一些。都存在±1误差

image-20240805111707938

image-20240805111715049

测占空比

image-20240805103527042