江科大STM32学习笔记(详细版)——2023持续更新

STM32F103xx

  • 前言
  • 基础篇
    • 寄存器映射
    • 时钟树
  • 外设篇
    • GPIO输出
      • GPIO位结构
      • GPIO模式
      • 外设的GPIO配置查看
      • 实战1: 如何进行基本的GPIO输入输出
    • OLED显示屏及调试
      • Keil的调试模式演示
    • EXTI外部中断
      • NVIC基本结构
      • EXTI结构
      • 代码实战2:如何使用中断和对射式红外传感器&旋转编码器
    • TIM(Timer)定时器
        • 1.1 基本定时器(TIM6和TIM7)
          • 1.1_1_ 时基单元
        • 1.2 通用定时器(TIM2、3、4、5)
          • 1.2_1_ 计数器模式
          • 1.2_2_ 时钟选择(电路讲解)
            • 内部时钟(CK_INT)
            • 外部时钟模式1:外部输入脚(TIx)
            • 外部时钟模式2:外部触发输入(ETR),
            • 内部触发输入(ITRx)(定时器同步)
            • 编码器模式
          • 输入捕获输出比较电路粗讲
          • 了解:通用定时器中异或门的作用
        • 1.3 高级定时器
      • 2.TIM定时中断
        • 代码实战3:定时中断和内外时钟源选择
      • 3.TIM输出比较
        • 代码实战:PWM的实际使用
      • 4.TIM输入捕获
          • 输入捕获电路的工作流程
          • 代码实战:输入捕获模式测频率和占空比
      • TIM编码器接口
        • 代码实战
  • 模块篇
    • 按键
    • 传感器模块
    • 旋转编码器
    • 舵机
    • 直流电机
  • 一些注意问题
    • 关于在外设接线中注意的问题——共地
    • 补充:[C语言](https://www.bilibili.com/video/BV1th411z7sn?p=7&vd_source=ea89d6dd8f9167ceceda04bccc7ea0cb)

前言

基础篇是到时候我自己找其它视频补充,可以先不看;
外设篇目前是跟着江科大来学习的,大家可以直接看这篇;大家可以根据我这篇来做适合自己的笔记;我这篇会做的比较详细
中间有时间会补充基础篇,因为我自己还有很多东西要学,可能更的慢,但一定会更的。
有错误,模糊的欢迎大家一起讨论
诸君共勉

建议:大家在学习的时候可以看下手册,比如函数怎么使用的,在前面一段,对于出现过的函数,我是进行的解释,后面我就直接上手册或者是库里的解释了,因为我发现自己也能大致看懂,它的那种格式了;并且有时候手册更详细,还可以比对一下库里面的解释一起看

基础篇

寄存器映射

时钟树

40:49


外设篇

片上外设就是 iic, TIM定时器,EXTI中断…等等一些。复用了那些外设就可以连接到IO端口了,而不是外部设备键盘,鼠标之类的。
片内外设、片上外设和片外外设的区别

GPIO输出

江科大STM32学习笔记(详细版)——2023持续更新_第1张图片
江科大STM32学习笔记(详细版)——2023持续更新_第2张图片

GPIO位结构

江科大STM32学习笔记(详细版)——2023持续更新_第3张图片

江科大STM32学习笔记(详细版)——2023持续更新_第4张图片

I/O引脚的保护二极管是对输入电压进行限幅的上面的二极管接VDD, 3.3V,下面接VSS, 0V,当输入电压

  • >3.3V
    那上方这个二极管就会导通,输入电压产生的电流就会直接充入VDD而不会流入内部电路;
  • <0V(这个电压是相对于VSS的电压,所以是可以有负电压的)
    那这时下方这个二极管就会导通,电流会从VSS直接流出去,而不会从内部电路汲取电流,也是可以保护内部电路的;
  • 在0~3.3V之间
    那两个二极管均不会导通,这时二极管对电路没有影响,这就是保护二极管的用途。
    江科大STM32学习笔记(详细版)——2023持续更新_第5张图片

开关:如果上面导通、下面断开,就是上拉输入模式,如果下面导通、上面断开,就是下拉输入模式,如果两个都断开,就是浮空输入模式

上拉和下拉的作用——>为了给输入提供一个默认的输入电平
因为对应一个数字的端口,输入不是高电平就是低电平,那如果输入引脚什么都不接,那就不确定算高电平还是低电平。而实际情况是,如果啥也不接,这时输入就会处于一种浮空的状态,引脚的输入电平极易受外界干扰而改变。为了避免引脚悬空导致的输入数据不确定,我们就需要在这里加上拉或者下拉电阻了,如果接入上拉电阻,当引脚悬空时,还有上拉电阻来保证引脚的高电平,所以上拉输入又可以称作是默认为高电平的输入模式。下拉也是同理,就是默认为低电平的输入方式。

这个上拉电阻和下拉电阻的阻值都是比较大的,是一种弱上拉和弱下拉,目的是尽量不影响正常的输入操作。
江科大STM32学习笔记(详细版)——2023持续更新_第6张图片

英文原文档是施密特触发器,(模电里这叫迟滞/滞回比较器,也就是施密特触发器的电路)

施密特触发器的作用就是对输入电压进行整形的,它的执行逻辑是,如果输入电压大于某一阈值,输出就会瞬间升为高电平,如果输入电压小于某一阈值,输出就会瞬间降为低电平。

江科大STM32学习笔记(详细版)——2023持续更新_第7张图片

接下来经过施密特触发器整形的波形就可以直接写入输入数据寄存器了,我们再用程序读取数据输存器对应某一位的数据,就可以知道端口的输入电平了。最后上面这还有两路线路,这些就是连接到片上外设的一些端口,其中有模拟输入,这个是连接到ADC上的,因为ADC需要接收模拟量,所以这根线是接到施密特触发器前面的;另一个是复用功能输入,这个是连接到其他需要读取端口的外设上的,比如串口的输入引脚等,这根线接收的是数字量,所以在施密特触发器后面

输出部分:
江科大STM32学习笔记(详细版)——2023持续更新_第8张图片
输出部分可以由 输出数据寄存器片上外设 控制,两种控制方式通过这个数据选择器接到了输出控制部分。
如果选择通过输出数据寄存器进行控制,就是普通的IO口输出,写这个数据寄存器的某一位就可以操作对应的某个端口了。
位设置/清除寄存器:这个可以用来单独操作输出数据寄存器的某一位,而不影响其它位。因为这个输出数据寄存器同时控制16个端口,并且这个寄存器只能整体读写,所以如果想单独控制其中某一个端口而不影响其他端口的话,就需要一些特殊的操作方式。

  • 第一种方式是先读出这个寄存器,然后用 按位与 和 按位或 的方式更改某一位,最后再将更改后的数据写回去,在C语言中就是&=和 |=的操作,这种方法比较麻烦,效率不高,对于IO口的操作而言不太合适;
  • 第二种方式是通过设置这个位设置和位清除寄存器,如果我们要对某一位进行置1的操作,在位设置寄存器的对应位写1便可,剩下不需要操作的位写0,这样它内部就会有电路,自动将输出数据寄存器中对应位置为1,而剩下写0的位则保持不变,这样就保证了只操作其中某一位而不影响其它位,并且这是一步到位的操作。如果想对某一位进行清0的操作,就在位清除寄存器的对应位写1即可,这样内部电路就会把这一位清0了,这就是第二种方式也就是这个位设置和位清除寄存器的作用。【作用:将设置/清除寄存器的某一位写1/0就能达到单独影响输出寄存器的某一位,从而单独影响某个端口】
  • 第三种操作方式【了解即可】 ,就是读写STM32中的“位带”区域,这个位带的作用就跟51单片机的位寻址作用差不多,在STM32中,专门分配的有一段地址区域,这段地址映射了RAM和外设寄存器所有的位,读写这段地址中的数据,就相当于读写所映射位置的某一位,这就是位带的操作方式,这个方式我们本课程暂时不会用到。我们的教程主要使用的是库函数来操作的,库函数使用的是读写位设置和位清除寄存器的方法

江科大STM32学习笔记(详细版)——2023持续更新_第9张图片
上面是P-MOS,下面是N-MOS,这个MOS管就是一种电子开关,我们的信号来控制开关的导通和关闭,开关负责将IO口接到VDD或者VSS,
在这里可以选择推挽、开漏或关闭三种输出方式。

  • 推挽输出模式
    在推挽输出模式下,P-MOS和N-MOS均有效,数据寄存器为1时,上管导通,下管断开,输出直接接到VDD,就是输出高电平,数据寄存器为0时,上管断开,下管导通,输出直接接到VSS,就是输出低电平,这种模式下,高低电平均有较强的驱动能力,所以推挽输出模式也可以叫强推输出模式。在推挽输出模式下,STM32对IO口具有绝对的控制权,高低电平都由STM32说的算。
  • 开漏输出模式
    在开漏输出模式下,这个P-MOS是无效的,只有N-MOS在工作,数据寄存器为1时,下管断开,这时输出相当于断开,也就是高阻模式;数据寄存器为0时,下管导通,输出直接接到VSS,也就是输出低电平;这种模式下,只有低电平有驱动能力,高电平是没有驱动能力的。那这个模式有什么用呢,这个开漏模式可以作为通信协议的驱动方式,比如12C通信的引脚,就是使用的开漏模式,在多机通信的情况下,这个模式可以避免各个设备的相互干扰,另外开漏模式还可以用于输出5V的电平信号。江科大STM32学习笔记(详细版)——2023持续更新_第10张图片
    比如在I0口外接一个上拉电阻到5V的电源,当输出低电平时,由内部的N-MOS直接接VSS,当输出高电平时,由外部的上拉电阻拉高至5V,这样就可以输出5V的电平信号,用于兼容一些5V电平的设备,这就是开漏输出的主要用途。

开漏模式下,输出1时,两个mos管都相当于关断,左侧相当于断路。外接5V的电能只能流向右侧,故输出5V。反之,输出0时,左下方mos管导通,外接5V的电能流到左下方Vss,且两者之间几乎没有电压降,可看做5V电压降在了上拉电阻上,故引脚输出0V

  • 关闭
    剩下的一种状态就是关闭,这个是当引脚配置为输入模式的时候,这两个MOS管都无效,也就是输出关闭,端口的电平由外部信号来控制。

GPIO模式

江科大STM32学习笔记(详细版)——2023持续更新_第11张图片

输入模式:
江科大STM32学习笔记(详细版)——2023持续更新_第12张图片
首先是前三个,浮空输入、上拉输入和下拉输入。这三个模式的电路结构基本是一样的,区别就是上拉电阻和下拉电阻的连接,它们都属于数字的输入口,那特征就是,都可以读取端口的高低电平,当引脚悬空时,上拉输入默认是高电平,下拉输入默认是低电平,而浮空输入的电平是不确定的,所以在使用浮空输入时,端口—定要接上一个连续的驱动源,不能出现悬空的状态。
那我们来看一下这三种模式的电路结构,这里可以看到,在输入模式下,输出驱动器是断开的,端口只能输入而不能输出,上面这两个电阻可以选择为上拉工作、下拉工作或者都不工作,对应的就是上拉输入、下拉输入和浮空输入,然后输入通过施密特触发器进行波形整形后,连接到输入数据寄存器。

另外右边这个输入保护这里,上面写的是VDD或者VDD_FT,这就是3.3V端口和容忍5V端口的区别。这个容忍5V的引脚,它的上边保护二极管要做一下处理,要不然这里直接接VDD 3.3V的话,外部再接入5V电压就会导致上边二极管开启,并且产生比较大的电流,这个是不太妥当的。

接着我们再来看一下下面这一个模拟输入,特征是GPIO无效,引脚直接接入内部ADC,这个模拟输入可以说是ADC模数转换器的专属配置了。
江科大STM32学习笔记(详细版)——2023持续更新_第13张图片
这里输出是断开的,输入的施密特触发器也是关闭的无效状态,所以整个GPIO的这些都是没用的,那么只剩下从引脚直接接入片上外设,也就是ADC,所以,当我们使用ADC的时候,将引脚配置为模拟输入就行了,其他时候,一般用不到模拟输入。

输出模式:
在这里插入图片描述
开漏输出推挽输出,这两个电路结构也基本一样,都是数字输出端口,可以用于输出高低电平,区别就是开漏输出的高电平呈现的是高阻态,没有驱动能力,而推挽输出的高低电平都是具有驱动能力的。 这时候,输出是由输出数据寄存器控制的,如果P-MOS无效,就是开漏输出;如果P-MOS和N-MOS都有效,就是推挽输出。另外我们还可以看到,在输出模式下,输入模式也是有效的,但是在我们刚才的电路图,在所有输入模式下,输出都是无效的,这是因为,一个端口只能有一个输出,但可以有多个输入,所以当配置成输出模式的时候,内部也可以顺便输入一下,这个也是没啥影响的。

最后我们再来看一下复用开漏输出复用推挽输出,这俩模式跟普通的开漏输出和推挽输出也差不多。
江科大STM32学习笔记(详细版)——2023持续更新_第14张图片
可以看到通用的输出/数据寄存器没有连接的,引脚的控制权转移到了片上外设,由片上外设来控制,在输入部分,片上外设也可以读取引脚的电平,同时普通的输入也是有效的,顺便接收一下电平信号其实在GPIO的这8种模式中,除了模拟输入这个模式会关闭数字的输入功能,在其他的7个模式中,所有的输入都是有效的。

GPIO寄存器讲解23:45~26:32
江科大STM32学习笔记(详细版)——2023持续更新_第15张图片
江科大STM32学习笔记(详细版)——2023持续更新_第16张图片

那这高电平驱动和低电平驱动两种驱动方式应该如何选择呢?
这就得看这个I0口高低电平的驱动能力如何了,我们刚才介绍,这个GPIO在推挽输出模式下,高低电平均有比较强的驱动能力,所以在这里,这两种接法均可。但是在单片机的电路里,一般倾向使用第一种接法(低电平驱动),因为很多单片机或者芯片,都使用了高电平弱驱动,低电平的强驱动的规则,这样可以一定程度上避免高低电平打架。所以如果高电平驱动能力弱,那就不能使用第一种连接方法了

本节内容跟手册第8章的GPIO相关,AFIO暂时不用管。

外设的GPIO配置查看

STM32F10xxx参考手册 P110有列出了各个外设的引脚配置,例如:
江科大STM32学习笔记(详细版)——2023持续更新_第17张图片

实战1: 如何进行基本的GPIO输入输出

GPIO输出&GPIO输入讲解

操作STM32的GPIO总共需要3个步骤:

  • 第一步,使用RCC开启GPIO的时钟
    涉及的函数如下:
    void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
    作用:使能(开启)或失能(关闭)APB2外设时钟
    参数说明:
参数 说明
RCC_APB2Periph 门控 APB2 外设时钟:指明需要开启的是哪一个APB2外设,取值范围在下图表明
NewState NewState:指定外设时钟的新状态 ,这个参数可以取:ENABLE(打开) 或者 DISABLE(关闭)

江科大STM32学习笔记(详细版)——2023持续更新_第18张图片
其它两个外设时钟函数也是大差不差的,根据不同外设选择相应的函数开启就行。
在这里插入图片描述

  • 第二步,使用GPIO_Init函数初始化GPIO
    涉及的函数如下:
    void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
    作用:根据GPIO_InitStruct中的指定参数初始化GPIOx外设。
    参数说明:
参数 说明
GPIOx 其中x可以为(A…G)选择GPIO外设。
GPIO_InitStruct 指向GPIO InitTypeDef结构的指针,该结构包含指定GPIO外设的配置信息。

指定要配置的GPIO引脚。
其中 GPIO InitTypeDef结构体配置信息如下:

typedef struct
{
  uint16_t GPIO_Pin;           
  GPIOSpeed_TypeDef GPIO_Speed;                             
  GPIOMode_TypeDef GPIO_Mode;   
}GPIO_InitTypeDef;

参数说明:

参数 说明
GPIO_Pin 指定要配置的GPIO引脚。例如:GPIO_Pin_14
GPIO_Speed 指定所选引脚的速率。在GPIO_Speed_10MHz,GPIO_Speed_2MHz,GPIO_Speed_50MHz中选择,库里已经定义好了
GPIO_Mode 指定所选引脚的工作模式。

引脚的工作模式如下:
江科大STM32学习笔记(详细版)——2023持续更新_第19张图片

举例:根据LED闪烁接线图设置

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	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口
涉及的函数如下:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
作用:设置所选数据端口位。对某个端口写1,也就是高电平
参数说明:

参数 说明
GPIOx 其中x可以为(A…G)选择GPIO外设。
GPIO_Pin 指定要写入的端口位,该参数可以是GEIo_Pin_x的任意组合,其中x可以是(0…15)。

类似的还有:GPIO_ResetBits 函数,同样的用法,只不过这个函数是写0
3-1.LED闪烁
接线图:
江科大STM32学习笔记(详细版)——2023持续更新_第20张图片
江科大STM32学习笔记(详细版)——2023持续更新_第21张图片
3-2.LED流水灯
江科大STM32学习笔记(详细版)——2023持续更新_第22张图片

江科大STM32学习笔记(详细版)——2023持续更新_第23张图片
3-3.蜂鸣器
江科大STM32学习笔记(详细版)——2023持续更新_第24张图片
江科大STM32学习笔记(详细版)——2023持续更新_第25张图片

OLED显示屏及调试

调试方式:

  • 串口调试:通过串口通信,将调试信息发送到电脑端,电脑使用串口助手显示调试信息
  • 显示屏调试:直接将显示屏连接到单片机,将调试信息打印在显示屏上
  • Keil调试模式:借助Keil软件的调试模式,可使用单步运行、设置断点、查看寄存器及变量等功能

江科大STM32学习笔记(详细版)——2023持续更新_第26张图片
江科大STM32学习笔记(详细版)——2023持续更新_第27张图片

接线图:
江科大STM32学习笔记(详细版)——2023持续更新_第28张图片

改引脚配置和端口初始化,就可以直接使用OLED驱动函数了
江科大STM32学习笔记(详细版)——2023持续更新_第29张图片
比如我这里SCL接在了PB8,那这个地方就是GPIOB,GPIO_Pin_8,如果你换个端口,比如接在PA6上,那这个地方就要改成GPIOA,GPIO_Pin_6;下面这个SDA的引脚配置也是一样,SDA接在了哪个位置,就改成GPIO啥,GPIO_Pin_啥。
江科大STM32学习笔记(详细版)——2023持续更新_第30张图片
具体更改就是,使用到的GPIO外设都先用RCC开启一下时钟,然后下面初始化GPIOB的Pin8,再初始化GPIOB的Pin9

Keil的调试模式演示

Keil的调试模式演示11:43

EXTI外部中断

江科大STM32学习笔记(详细版)——2023持续更新_第31张图片
江科大STM32学习笔记(详细版)——2023持续更新_第32张图片

江科大STM32学习笔记(详细版)——2023持续更新_第33张图片
表的详细内容在STM32F10xxx参考手册132页有,

然后右边这里还有个中断的地址,这个地址是干什么的呢?这个是因为我们程序中的中断函数,它的地址是由编译器来分配的,是不固定的。但是我们的中断跳转由于硬件的限制,只能跳到固定的地址执行程序,所以为了能让硬件跳转到一个不固定的中断函数里,这里就需要在内存中定义一个地址的列表。这个列表地址是固定的,中断发生后,就跳到这个固定位置,然后在这个固定位置由编译器,再加上一条跳转到中断函数的代码,这样中断跳转就可以跳转到任意位置。这个中断地址的列表,就叫中断向量表

NVIC基本结构

江科大STM32学习笔记(详细版)——2023持续更新_第34张图片
这个NVIC的名字叫做嵌套中断向量控制器,在STM32中,它是用来统一分配中断优先级和管理中断的。
NVIC是一个内核外设,是CPU的小助手。STM32的中断非常多,如果把这些中断全都接到CPU上,那CPU还得引出很多线进行适配,设计上就很麻烦,并且如果很多中断同时申请,或者中断很多产生了拥堵,CPU也会很难处理,毕竟CPU主要是用来运算的,中断分配的任务就放到别的地方吧,所以NVIC就出现了。
NVIC有很多输入口,你有多少个中断线路,都可以接过来,比如这里可以接到EXTI、TIM、ADC、USART等等,这里线上画了个斜杠,上面写个n,这个意思是一个外设可能会同时占用多个中断通道,所以这里有n条线。然后NVIC只有一个输出口,NVIC根据每个中断的优先级分配中断的先后顺序,之后,通过右边这一个输出口就告诉CPU,你该处理哪个中断。对于中断先后顺序分配的任务,CPU不需要知道。
13:18~14:00举了例子 && 14:00讲了下面的NVIC中断分组
江科大STM32学习笔记(详细版)——2023持续更新_第35张图片

EXTI结构

江科大STM32学习笔记(详细版)——2023持续更新_第36张图片
但相同的Pin不能同时触发中断:这个意思就是,比如PAO和PBO不能同时用,或者,PA1、PB1、PC1这样的,端口GPIO_Pin一样的。
然后再看一下外部中断占用的通道,其中有16个GPIO_Pin,这就对应GPIO_Pin_0到GPIO_Pin_15,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒,这些加起来总共有20个中断线路。这里的16个GPIO_Pin是外部中断的主要功能,后面跟着的这四个东西其实是来“蹭网”的。因为这个外部中断有个功能,就是从低功耗模式的停止模式下唤醒STM32,那对于PVD电源电压监测,当从电源从电压过低恢复时,就需要PVD借助一下外部中断退出停止模式;对于RTC闹钟来说,有时候为了省电,RTC定一个闹钟之后,STM32会进入停止模式,等到闹钟响的时候再唤醒,这也需要借助外部中断;还有USB唤醒、以太网唤醒,也都是类似的作用。
中断响应,就是申请中断,让CPU执行中断函数;事件响应是STM32对外部中断增加的一种额外的功能。当外部中断检测到引脚电平变化时,正常的流程是选择触发中断,但是在STM32中,也可以选择触发一个事件,如果选择触发事件,那外部中断的信号就不会通向CPU了,而是通向其它外设,用来触发其它外设的操作,比如触发ADC转换、触发DMA等。所以总结一下:中断响应是正常的流程,引脚电平变化触发中断;事件响应不会触发中断,而是触发别的外设操作,属于外设之间的联合工作。
江科大STM32学习笔记(详细版)——2023持续更新_第37张图片
这里注意一下,本来20路输入,应该有20路中断的输出,但是可能ST公司觉得这20个输出太多了,比较占用NVIC的通道资源,所以就把其中外部中断的 9~5 和15 ~ 10给分到一个通道里。也就是说,外部中断的9~5会触发同一个中断函数,15~10也会触发同一个中断函数。在编程的时候,我们在这两个中断函数里,需要再根据标志位来区分到底是哪个中断进来的。
江科大STM32学习笔记(详细版)——2023持续更新_第38张图片
江科大STM32学习笔记(详细版)——2023持续更新_第39张图片
外部中断的使用场景:
就是对于STM32来说,想要获取的信号是外部驱动的很快的突发信号。比如旋转编码器的输出信号,你可能很久都不会拧它,这时不需要STM32做任何事,但是我一拧它,就会有很多脉冲波形需要STM32接收。这个信号是突发的,STM32不知道什么时候会来,同时它是外部驱动的,STM32只能被动读取,最后这个信号非常快,STM32稍微晚一点来读取,就会错过很多波形。那对于这种情况来说,就可以考虑使用STM32的外部中断了。有脉冲过来,STM32立即进入中断函数处理,没有脉冲的时候,STM32就专心做其它事情。
另外还有,比如红外遥控接收头的输出,接收到遥控数据之后,它会输出一段波形,这个波形转瞬即逝,并且不会等你,所以就需要我们用外部中断来读取。
最后还有按键,虽然它的动作也是外部驱动的突发事件,但我并不推荐用外部中断来读取按键。因为用外部中断不好处理按键抖动和松手检测的问题,对于按键来说,它的输出波形也不是转瞬即逝的。所以要求不高的话可以在主程序中循环读取,如果不想用主循环读取的话,可以考虑一下定时器中断读取的方式。这样既可以做到后台读取按键值、不阻塞主程序,也可以很好地处理按键抖动和松手检测的问题。
37:17NVIC手册讲解

代码实战2:如何使用中断和对射式红外传感器&旋转编码器

注意:我们这里是用到了PB14来做外部中断的
5-1 对射式红外传感器计次接线图
江科大STM32学习笔记(详细版)——2023持续更新_第40张图片
当挡光片在对射式红外传感器中间经过时,DO输出电平跳变信号,触发PB14号口的中断,在中断断数执行Num++
江科大STM32学习笔记(详细版)——2023持续更新_第41张图片

  • 第一步,配置RCC,将程序涉及外设的时钟都打开
    提示:有GPIOB和AFIO

  • 第二步,配置GPIO,选择端口为输入模式
    江科大STM32学习笔记(详细版)——2023持续更新_第42张图片

  • 第三步,配置AFIO,选择硬件所用用的那一路GPIO,连接到后面的EXTI
    涉及函数如下:
    void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
    作用:选择用作EXTI线的GPIO引脚。
    参数说明:

参数 说明
GPIO_PortSource 选择要用作EXTI线路源的GPIO端口。取值为GPIO_PortSourceGPIOx,其中x为(A…G)。
GPIO_PinSource GPIO_PinSource:要配置的EXTI线路。该参数可以为GPIO_PinSourcex,其中x可以为(0…15)。
  • 第四步,配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,还有选择触发响应方式,可以选择中断响应和事件响应
    涉及函数如下:
    void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
    作用:根据EXTI InitStruct中的指定参数初始化EXTI外设。
    参数说明:
参数 说明
EXTI_InitStruct 指向EXTI InitTypeDef结构体的指针包含ExTI外设的配置信息。

EXTI InitTypeDef结构体说明:

typedef struct
{
  uint32_t EXTI_Line;          
  EXTIMode_TypeDef EXTI_Mode;     
  EXTITrigger_TypeDef EXTI_Trigger; 
  FunctionalState EXTI_LineCmd;                        
}EXTI_InitTypeDef;

参数说明以及举例
江科大STM32学习笔记(详细版)——2023持续更新_第43张图片
举例:

/* Enables external lines 12 and 14 interrupt generation on falling 
edge */ 
EXTI_InitTypeDef EXTI_InitStructure; 
EXTI_InitStructure.EXTI_Line = EXTI_Line12 | EXTI_Line14; 
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 
EXTI_InitStructure.EXTI_LineCmd = ENABLE; 
EXTI_Init(&EXTI_InitStructure); 
  • 第五步,配置NVIC,给我们这个中断选择一个合适的优先级
    涉及函数如下:
    void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
    作用:配置优先级分组:抢占优先级和子优先级。
    参数说明:
参数 说明
NVIC_PriorityGroup 指定优先级分组位长度。

取值范围:
江科大STM32学习笔记(详细版)——2023持续更新_第44张图片
例如:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  • 最后,通过NVIC,外部中断信号就能进入CPU了,这样CPU才能收到中断信号,才能跳转到中断函数里执行中断程序
    涉及函数如下:
    void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
    作用:根据NVIC InitStruct中指定的参数初始化NVIC外设。
    参数说明:
参数 说明
NVIC_InitStruct 指向NVIC InitTypeDef结构体的指针指定NVic外设的配置信息。

江科大STM32学习笔记(详细版)——2023持续更新_第45张图片

举例:
江科大STM32学习笔记(详细版)——2023持续更新_第46张图片

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);

中断函数的格式:
江科大STM32学习笔记(详细版)——2023持续更新_第47张图片
根据中断向量表,找到所需中断函数,这里面以IRQHandler结尾的字符串就是中断函数的名字,再根据名字写中断函数。
例如:void EXTI15_10_IRQHandler(void){ }
这就是中断函数的格式,中断函数都是无参无返回值的,中断函数的名字不要写错了,写错了就进不了中断了,最好是直接从启动文件复制过来,这样就不会有问题了。
注:启动文件为在这里插入图片描述
然后在中断函数里,一般都是先进行一个中断标志位的判断,确保是我们想要的中断源触发的这个函数,因为这个函数EXTI10到EXTI15都能进来,所以要先判断一下是不是我们想要的EXTI14进来的。所用函数:EXTI_GetITStatus(uint32_t EXTI_Line)
最后,中断程序结束后,一定要再调用一下清除中断标志位的函数,因为只有中断标志位置1了,程序就会跳转到中断函数。如果你不清除中断标志位,那它就会一直申请中断,这样程序就会不断响应中断,执行中断函数,那程序就卡死在中断函数里了。所用函数:EXTI_ClearITPendingBit(uint32_t EXTI_Line)
中断函数就不用声明了,因为中断函数不需要调用,它是自动执行的。

其它涉及函数:
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
作用:检查指定的 EXTI 线路触发请求发生与否(是不是我们想要的中断触发源)
返回值:(SET或RESET)
参数说明:

参数 说明
EXTI_Line EXTI_Line:要检查的EXTI行。EXTI_Linex:外部中断线x,其中x(0…19)

void GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
作用:读取指定端口管脚的输入
参数说明:

参数 说明
GPIOx GPIOx:其中x可以为(A…G)选择GPIO外设。
GPIO_Pin 指定要读取的端口位。该参数是GPIO_Pin_x,其中x可以是(0…15)。

void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
作用:清除EXTI线路挂起位
参数说明:

参数 说明
EXTI_Line 指定要清除的EXTI行。该参数可以是ExTI Linex的任意组合,其中x可以是(0…19)

EXTI和NVIC两个外设,这两个外设的时钟是一直都打开着的,不需要我们再开启时钟了。EXIT模块是由NVIC模块直接控制的,并不需要单独的外设时钟。NVIC也不需要开启时钟,是因为NVIC是内核的外设,内核的外设都是不需要开启时钟的。

代码如下:
蓝线部分是我自己需要注意的地方
江科大STM32学习笔记(详细版)——2023持续更新_第48张图片
5-2 旋转编码器计次37:30
在写中断函数的核心思想:
只有在B相下降沿和A相低电平时,才判断为正转
在A相下降沿和B相低电平时,才判断为反转
江科大STM32学习笔记(详细版)——2023持续更新_第49张图片
代码如下:
江科大STM32学习笔记(详细版)——2023持续更新_第50张图片

TIM(Timer)定时器

江科大STM32学习笔记(详细版)——2023持续更新_第51张图片
72M/65536/65536,得到的是中断频率,然后取倒数,就是59.65秒多,大家可以自己算一下。

江科大STM32学习笔记(详细版)——2023持续更新_第52张图片
接下来,我们就依次来看一下高级定时器、通用定时器和基本定时器的结构图,看一下这三种定时器是怎么样来工作的,设计这些结构都能完成哪些任务。

1.1 基本定时器(TIM6和TIM7)

江科大STM32学习笔记(详细版)——2023持续更新_第53张图片

1.1_1_ 时基单元

江科大STM32学习笔记(详细版)——2023持续更新_第54张图片
这个可编程定时器的主要部分是一个带有自动重装载的16位累加计数器,计数器的时钟通过一个预分频器得到。
软件可以读写计数器、自动重装载寄存器和预分频寄存器,即使计数器运行时也可以操作。时基单元包含:

  • 预分频寄存器(TIMx_PSC)
    预分频器
    预分频可以以系数介于1至65536之间的任意数值对计数器时钟分频,就是对输入的基淮频率提前进行一个分频的操作。它是通过一个16位寄存器(TIMx-PSC)的计数实现分频。因为TIMx-PSC控制寄存器具有缓冲,可以在运行过程中改变它的数值,新的预分频数值将在下一个更新事件时起作用
    假设这个寄存器写0,就是不分频,或者说是1分频,这时候输出频率=输入频率=72MHz;如果预分频器写1,那就是2分频,输出频率=输入频率/2=36MHz,所以预分频器的值和实际的分频系数相差了1,即实际分频系数=预分频器的值+1
    江科大STM32学习笔记(详细版)——2023持续更新_第55张图片

时序图讲解32:34
注意:实际的设置计数器使能信号CNT_EN相对于CEN滞后一个时钟周期。

  • 计数器寄存器(TIMx_CNT)
    计数器由预分频输出CK_CNT驱动,设置TIMx_CR1寄存器中的计数器使能位(CEN)使能计数器计数。这个计数器可以对预分频后的计数时钟进行计数,计数时钟每来一个上升滑,计数器的值就加1,由于这个计数器也是16位的,所以里面的值可以从0一直加到65535,如果再加的话,计数器就会回到0重新开始。所以计数器的值在计时过程中会不断地自增运行,当自增运行到目标值时,产生中断,那就完成了定时的任务,所以现在还需要一个存储目标值的寄存器,那就是自动重装寄存器了。

江科大STM32学习笔记(详细版)——2023持续更新_第56张图片
时序图讲解36:36

  • 自动重裝载寄存器(TIMx_ARR)
    自动重装载寄存器是预加载的,每次读写自动重装载寄存器时,实际上是通过读写预加载寄存器实现。根据TIMx CR1寄存器中的自动重装载预加载使能位(ARPE),写入预加载寄存器的内容能够立即或在每次更新事件时,传送到它的影子寄存器。当TIMx CR1寄存器的UDIS位为’0’,则每当计数器达到溢出值时,硬件发出更新事件;软件也可以产生更新事件;关于更新事件的产生,随后会有详细的介绍。

38:47讲解
江科大STM32学习笔记(详细版)——2023持续更新_第57张图片
39:27讲解
江科大STM32学习笔记(详细版)——2023持续更新_第58张图片

1.2 通用定时器(TIM2、3、4、5)

通用计时器库函数
江科大STM32学习笔记(详细版)——2023持续更新_第59张图片
江科大STM32学习笔记(详细版)——2023持续更新_第60张图片
关于图中引脚对应可以参考引脚定义图
江科大STM32学习笔记(详细版)——2023持续更新_第61张图片

红框所标出来的意思:这个TIM2的CH1和ETR脚都复用在PA0引脚,下面还有CH2、CH3、CH4(CH是通道)和其他定时器的一些引脚,也都可以在这里找到。
江科大STM32学习笔记(详细版)——2023持续更新_第62张图片
中间由红框标出来的寄存器是捕获/比较寄存器,是输入捕获和输出比较电路共用的,因为输入捕获和输出比较不能同时使用,所以这里的寄存器是共用的,引脚也是共用的。
)

1.2_1_ 计数器模式

在这里插入图片描述
像这样带一个黑色阴影的寄存器,都是有影子寄存器这样的的缓冲机制的,包括预分频器,自动重装寄存器和下面的捕获比较寄存器,所以计数的这个ARR自动重装寄存器,也是有一个缓冲寄存器的,并且这个缓冲寄存器是用还是不用,是可以自己设置的
38:45计数器有无缓冲寄存器的情况

1.2_2_ 时钟选择(电路讲解)

时钟源的输入——时钟源
预分频器之前,连接的就是基准计数时钟的输入,由于基本定时器只能选择内部时钟,所以你可以直接认为时基单元直接连到了输入端,也就是内部时钟CK_INT。内部时钟的来源是RCC_TIMXCLK,这里的频率值一般都是系统的主频72MHz,所以通向时基单元的计数基准频率就是72M。
计数器的时钟由内部时钟(CK_INT)提供。TIMx CR1寄存器的CEN位和TIMx EGR寄存器的UG位是实际的控制位, (除了UG位被自动清除外)只能通过软件改变它们。一旦置CEN位为’1’,内部时钟即向预分频器提供时钟。下图示出控制电路和向上计数器在普通模式下,没有预分频器时的操作。
江科大STM32学习笔记(详细版)——2023持续更新_第63张图片

计数器时钟可由下列时钟源提供:

  • 内部时钟(CK_INT)
  • 外部时钟模式1:外部输入脚(TIx)
  • 外部时钟模式2:外部触发输入(ETR)
  • 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

【注:编码器接口可以读取编码器的输出波形】

内部时钟(CK_INT)

江科大STM32学习笔记(详细版)——2023持续更新_第64张图片
江科大STM32学习笔记(详细版)——2023持续更新_第65张图片

外部时钟模式1:外部输入脚(TIx)

当TIMx_SMCR寄存器的SMS=111时,此模式被选中。计数器可以在选定输入端的每个上升沿或下降沿计数。

江科大STM32学习笔记(详细版)——2023持续更新_第66张图片
当这个TRGI当做外部时钟来使用的时候,这一路就叫做“外部时钟模式1”,那通过这一路的外部时钟都有哪些呢?

  • 第一个,就是ETR引脚的信号
    江科大STM32学习笔记(详细版)——2023持续更新_第67张图片
  • 然后第二个,就是ITR信号,这一部分的时钟信号是来自其他定时器,从右边可以看出,这个主模式的输出TRGO可以通向其他定时器,那通向其他定时器的时候,就接到了其他定时器的ITR引脚上来了。
    江科大STM32学习笔记(详细版)——2023持续更新_第68张图片
    这个ITRO到ITR3分别来自其他4个定时器的TRGO输出,至于具体的连接方式是怎么的,手册的这个位置有一张表。这里可以看到,TIM2的ITRO是接在了TIM1的TRGO上,ITR1接在了TIM8,ITR2接在了TIM3,ITR3接在了TIM4,其他定时器也都可以参照一下这个表,这就是TR和定时器的连接关系。通过这一路我们就可以实现定时器级联的功能.比如我可以先初始化TIM3,然后使用主模式把它的更新事件映射到TRGO上,接着再初始化TIM2,这里选择ITR2,对应的就是TIM3的TRGO,然后后面再选择时钟为外部时钟模式1,这样TIM3的更新事件就可以驱动TIM2的时基单元,也就实现了定时器的级联.
    江科大STM32学习笔记(详细版)——2023持续更新_第69张图片
  • 这里还可以选择TI1F_ED,这里连接的是这里输入捕获单元的CH1引脚,也就是从CH1引脚获得时钟,这里后缀加一个ED(Edge)就是边沿的意思,也就是通过这一路输入的时钟,上升沿和下降沿均有效
    江科大STM32学习笔记(详细版)——2023持续更新_第70张图片
  • 最后,这个时钟还能通过TI1FP1和TI2FP2获得
    江科大STM32学习笔记(详细版)——2023持续更新_第71张图片

江科大STM32学习笔记(详细版)——2023持续更新_第72张图片
总结一下就是,外部时钟模式1的输入可以是ETR引脚、其他定时器,CH1引脚的边沿、CH1引脚和CH2引脚,这还是比较复杂的,一般情况下外部时钟通过ETR引脚就可以了。上面设置这么复杂的输入,不仅仅是为了扩大时钟输入的范围,更多的还是为了某些特殊应用场景而设计的,比如为了定时器的级联而设计的ITRx引脚,最后的一部分,我们之后讲输入捕获和测频率时,还会继续讲到。
注:对于时钟输入而言,最常用的还是内部的72MHz的时钟,如果要使用外部时钟,首选ETR引脚外部时钟模式2的输入;如果要使用外部时钟,首选ETR引脚外部时钟模式2的输入,这一路最简单、最直接。

外部时钟模式2:外部触发输入(ETR),

计数器能够在外部触发ETR的每一个上升沿或下降沿计数。
在这里插入图片描述
这个ETR(External)引脚的位置,可以参考一下引脚定义表。
江科大STM32学习笔记(详细版)——2023持续更新_第73张图片
可以看到这里有TIM2_CH1_ETR,意思就是这个TIM2的CH1和ETR都是复用在了这个位置,也就是PAO引脚,下面还有CH2,CH3,CH4和其他定时器的一些引脚,也都可以在这里找到。
那这里我们可以在这个TIM2的ETR引脚,也就是PAO上接一个外部方波时钟,然后配置一下内部的极性选择、边沿检测和预分频器电路,再配置一下输入滤波电路,这两块电路可以对外部时钟进行一定的整形。因为是外部引脚的时钟,所以难免会有的毛刺,那这些电路就可以对输入的波形进行滤波,同时也可以选择一下极性和预分频器。最后,滤波后的信号,兵分两路,上面一路ETRF进入触发控制器,紧跟着就可以选择作为时基单元的时钟了。
如果你想在ETR外部引脚提供时钟或者想对ETR时钟进行计数,把这个定时器当做计数器来用的话,那就可以配置这一路的电路,在STM32中,这一路也叫做“外部时钟模式2“。
江科大STM32学习笔记(详细版)——2023持续更新_第74张图片例如,要配置在ETR下每2个上升沿计数一次的向上计数器,使用下列步骤:

  • 1,本例中不需要滤波器,置TIMx_SMCR寄存器中的ETF(握)= 0000
  • 2,设置预分频器,置TIMx_SMCR寄存器中的早期胸腺祖细胞(1:0)= 1
  • 3.设置在ETR的上升沿检测,置TIMx_SMCR寄存器中的ETP=0
  • 4,开启外部时钟模式2,置TIMx_SMCR寄存器中的ECE=1
  • 5.,启动计数器,置TIMx_CR1寄存器中的CEN=1

计数器在每2个ETR上升沿计数一次。
在ETR的上升沿和计数器实际时钟之间的延时取决于在ETRP信号端的重新同步电路。
在这里插入图片描述

内部触发输入(ITRx)(定时器同步)

所有TIMx定时器在内部相连,用于定时器同步或链接。当一个定时器处于主模式时,它可以对另一个处于从模式的定时器的计数器进行复位、启动、停止或提供时钟等操作。
江科大STM32学习笔记(详细版)——2023持续更新_第75张图片

  • 配置定时器1为主模式,它可以在每一个更新事件UEV时输出一个周期性的触发信号。在TIM1_CR2寄存器的MMS='010’时,每当产生一个更新事件时在TRGO1上输出一个上升沿信号。

  • 连接定时器1的TRGO1输出至定时器2,设置TIM2_SMCR寄存器的TS =‘000’,配置定时器2为使用ITR1作为内部触发的从模式。(为什么是‘000’,硬件底层已经根据不同选择定义好了)
    江科大STM32学习笔记(详细版)——2023持续更新_第76张图片

  • 然后把从模式控制器置于外部时钟模式1(TIM2 SMCR寄存器的SMS-111):这样定时器2即可由定时器1周期性的上升沿(即定时器1的计数器溢出)信号驱动。

  • 最后,必须设置相应(TIMx_CR1寄存器)的CEN位分别启动两个定时器。如果OCx已被选中为定时器1的触发输出(MMS=1xx),它的上升沿用于驱动定时器2的计数器。
    注:如果OCx已被选中为定时器1的触发输出(MMS=1xx),它的上升沿用于驱动定时器2的计数器。
    这一段内容是涉及参考手册14.3.15的内容,关于这个模式还有更多功能,比如:使用一个定时器使能另一个定时器;使用一个定时器去启动另一个定时器;使用一个定时器作为另一个的预分频器;使用一个外部触发同步地启动2个定时器,感兴趣的可以自己去了解

编码器模式

最后这里还有一块没有讲到,这个是定时器的一个编码器接口,可以读取正交编码器的输出波形,这个我们后续课程也会再讲。

江科大STM32学习笔记(详细版)——2023持续更新_第77张图片
这部分电路可以把内部的一些事件映射到这个TRGO引脚上,比如我们刚才讲基本定时器分析的,将更新事件映射到TRGO,用于触发DAC。这里也是一样,它可以把定时器内部的一些事件映射到这里来,用于触发其它定时器、DAC或者ADC,可见这个触发输出的范围是比基本定时器更广一些的。
江科大STM32学习笔记(详细版)——2023持续更新_第78张图片

输入捕获输出比较电路粗讲

江科大STM32学习笔记(详细版)——2023持续更新_第79张图片
那有关输入捕获和输出比较这部分电路,在之后具体分析

了解:通用定时器中异或门的作用

江科大STM32学习笔记(详细版)——2023持续更新_第80张图片

1.3 高级定时器

36:00这里内容根据需求学习

2.TIM定时中断

这一段的内容主要搞懂定时中断和内外时钟源选择及如何配置。
江科大STM32学习笔记(详细版)——2023持续更新_第81张图片
首先中间最重要的还是PSC(Prescaler)预分频器、CNT (Counter)计数器、ARR (AutoReloadRegister)自动重装器这三个寄存器构成的时基单元。下面这里是运行控制,就是控制寄存器的一些位,比如启动停止、向上或向下计数等等,我们操作这些寄存器就能控制时基单元的运行了。
江科大STM32学习笔记(详细版)——2023持续更新_第82张图片
左边是为时基单元提供时钟的部分,这里可以选择RCC提供的内部时钟,也可以选择ETR引脚提供的外部时钟模式2。在本小节示例程序里,第一个定时器定时中断就是用的内部时钟这一路,第二个定时器外部时钟就是用的外部时钟模式2这一路。当然还可以选择这里的触发输入当做外部时钟,即外部时钟模式1,对应的有ETR外部时钟、TTRX其他定时器、TlX输入捕获通道,这些就是定时器的所有可选的时钟源了。最后这里,还有个编码器模式,这一般是编码器独用的模式,普通的时钟用不到这个。
在这里插入图片描述
接下来右边这里,就是计时时间到,产生更新中断后的信号去向。那这里中断信号会先在状态寄存器里置一个中断标志位,这个标志位会通过中断输出控制,到NVIC申请中断。
为什么会有一个中断输出控制呢?
江科大STM32学习笔记(详细版)——2023持续更新_第83张图片
因为这个定时器模块有很多地方都要申请中断。比如上面这个图不仅更新要申请中断,这里触发信号也会申请中断,还有下面的输入捕获和输出比较匹配时也会申请。所以这些中断都要经过中断输出控制,如果需要这个中断,那就允许,如果不需要,那就禁止。简单来说,这个中断输出控制就是一个中断输出的允许位。

代码实战3:定时中断和内外时钟源选择

6-1 定时器定时中断
江科大STM32学习笔记(详细版)——2023持续更新_第84张图片
江科大STM32学习笔记(详细版)——2023持续更新_第85张图片

  • 第一步,RCC开启时钟,这个基本上每个代码都是第一步。在这里打开时钟后,定时器的基准时钟和整个外设的工作时钟就都会同时打开了
  • 第二步,选择时基单元的时钟源。对于定时中断,我们就选择内部时钟源
    江科大STM32学习笔记(详细版)——2023持续更新_第86张图片
    注:没选择时钟,会默认内部时钟

然后最后一个函数,TIM_ETRConfig,这个不是用来选择时钟的,就是单独用来配置ETR引脚的预分频器、极性、滤波器这些参数的

涉及函数如下:
void TIM_InternalClockConfig(TIM_TypeDef* TIMx)
作用:配置TIMx内部时钟
参数说明:

参数 说明
TIMx 所选择的 TIM 外设
  • 第三步,配置时基单元。包括这里的预分频器、自动重装器、计数模式等等,这些参数用一个结构体就可以配置好了。
    涉及函数如下:
    void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
    作用:根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx时基单元外设。
    参数说明:
参数 说明
TIMx 所选择的 TIM 外设
TIM_TimeBaseInitStruct 指向结构 TIM_TimeBaseInitTypeDef 的指针,包含了 TIMx 时间基数单位的配置信息

江科大STM32学习笔记(详细版)——2023持续更新_第87张图片
18:49~20:17
如何确定时间参数讲解
在这里插入图片描述

江科大STM32学习笔记(详细版)——2023持续更新_第88张图片
假设定时1s,也就是定时频率为1Hz,那我们就可以PSC给一个7200,ARR给一个10000,然后两个参数都再减一个1,因为预分频器和计数器都有1个数的偏差,所以这里要再减个1。然后注意这个PSC和ARR的取值都要在0~65535之间,不要超范围了

  • 第四步,配置输出中断控制,允许更新中断输出到NVIC(开启更新中断到NVIC的通路)
    涉及函数如下:
    void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
    作用:启用或禁用指定的TIM中断。
    参数说明:
参数 说明
TIMx 所选择的 TIM 外设
TIM_IT 待使能或者失能的 TIM 中断源。该参数参考下图
NewState TIMx 中断的新状态。这个参数可以取:ENABLE 或者 DISABLE

江科大STM32学习笔记(详细版)——2023持续更新_第89张图片
注:TIM_IT_Update 更新中断
在STM32库里还提及其它中断源
江科大STM32学习笔记(详细版)——2023持续更新_第90张图片

  • 第五步,配置NVIC,在NMC中打开定时器中断的通道,并分配一个优先级。这部分在上节我们也用过,流程基本是一样的
    涉及函数:
    void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
    void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
  • 第六步,就是运行控制了。整个模块配置完成后,我们还需要使能一下计数器。要不然计数器是不会运行的。当定时器使能后,计数器就会开始计数了,当计数器更新时,触发中断。
    涉及函数如下:
    void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
    作用:启用或禁用指定的TIM外设。
    参数说明:
参数 说明
TIMx 所选择的 TIM 外设
NewState TIMx 中断的新状态。这个参数可以取:ENABLE 或者 DISABLE

这样初始化基本上就OK了,接下来,我们再看几个函数,因为在初始化结构体里有很多关键的参数,比如自动重装值和预分频值等等,这些参数可能会在初始化之后还需要更改,如果为了改某个参数还要再调用一次初始化函数,那太麻烦了。所所以这里有一些单独的函数,可以方便地更改这些关键参数。
比如这里的TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode),就是用来单独写预分频值的,看一下参数,Prescaler,就是要写入的预分频值;后面还有个参数,PSCReloadMode,写入的模式。我们上一小节说了,预分频器有一个缓冲器,写入的值是在更新事件发生后才有效的,所以这里有个写入的模式,可以选择是听从安排,在更新事件生效,或者是,在写入后,手动产生一个更新事件,让这个值立刻生效。
TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);,用来改变计数器的计数模式,参数CounterMode,选择新的计数器模式。
TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);自动重装器预装功能配置
TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);,给计数器写入一个值。如果你想手动给一个计数值,就可以用这个函数
TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);给自动重装器写入一个值,如果你想手动给一个自动重装值,就可以用这个函数
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);获取当前计数器的值,如果你想看当前计数器计到哪里了,就可以调用一下这个函数,返回值就是当前的计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);获取当前的预分频器的值
在这里插入图片描述

  • 最后我们再写一个定时器的中断函数。这样这个中断函数每隔一段时间就能自动执行一次了。

江科大STM32学习笔记(详细版)——2023持续更新_第91张图片

6-2 定时器外部时钟31:35
江科大STM32学习笔记(详细版)——2023持续更新_第92张图片
提示:
这里推荐配置是浮空是输入,但是我一般不太喜欢浮空输入平因为一旦悬空,电平就会跳个没完,所以我准备给上拉输入,这也是可以的。
那什么时候需要用浮空输入呢?就是如果你外部的输入信号功率很小,内部的这个上拉电阻可能会影响到这个输入信号,这时就可以用一下浮空输入,防止影响外部输入的电平。
江科大STM32学习笔记(详细版)——2023持续更新_第93张图片
在6-1的基础上更改,尤其注意在第二步更改时基单元的时钟源,通过ETR引脚的外部时钟模式2配置。
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter)
作用:配置TIMx外部时钟模式2
参数说明:

参数 说明
TIMx 所选择的 TIM 外设
TIM_ExtTRGPrescaler 外部触发预分频
ExtTRGFilter 外部时钟极性(视频说这里暂时就不用滤波器了,写0x00就行了,但是根据实测,针对不同的传感器其敏感度不同,会发生噪音,还是写上值比较好

江科大STM32学习笔记(详细版)——2023持续更新_第94张图片
江科大STM32学习笔记(详细版)——2023持续更新_第95张图片

江科大STM32学习笔记(详细版)——2023持续更新_第96张图片

3.TIM输出比较

江科大STM32学习笔记(详细版)——2023持续更新_第97张图片

捕获/比较寄存器是输入捕获和输出比较共用的,当使用输入捕获时,它就是捕获寄存器;当使用输出比较时,它就是比较寄存器。那在输出比较这里,这块电路会比较CNT和CCR的值,CNT计数自增,CCR是我们给定的一个值,当CNT大于CCR、小于CCR或者等于CCR时,这里输出就会对应的置1、置0、置1、置0,这样就可以输出一个电平不断跳变的PWM波形了。这就是输出比较的基本功能。
江科大STM32学习笔记(详细版)——2023持续更新_第98张图片
江科大STM32学习笔记(详细版)——2023持续更新_第99张图片

使用这个PWM波形,是用来等效地实现一个模拟信号的输出
问题:数字输出端口控制LED,按理说LED只能有完全亮和完全灭两种状态,怎么能实现控制亮度大小呢?
通过PWM就可以实现,我们让LED不断点亮、熄灭、点亮、熄灭,当这个点亮、熄灭的频率足够大时,LED就不会闪烁了,而是呈现出一个中等亮度。当我们调控这个点亮和熄灭的时间比例时,就能让LED呈现出不同的亮度级别。对于电机调速也是一样。

当然,PWM的应用场景必须要是一个惯性系统,就是说LED在熄灭的时候,由于余晖和人眼视觉暂留现象,LED不会立马熄灭,而是有一定的惯性,过一小段时间才会熄灭。电机也是,当电机断电时,电机的转动不会立马停止,而是有一定的惯性,过一会才停。

那接下来我们就来具体地分析一下,定时器的输出比较模块是怎么来输出PWM波形的,我们先看一下通用定时器的这个结构。
江科大STM32学习笔记(详细版)——2023持续更新_第100张图片
接下来我们还需要看一下这个输出模式控制器,它具体是怎么工作的。什么时候给REF高电平,什么时候给REF低电平。我们看一下下面的这个表,这就是输出比较的8种模式,也就是这个输出模式控制器里面的执行逻辑。这个模式控制器的输入是CNT和CCR的大小关系,输出是REF的高低电平,里面可以选择多种模式来更加灵活地控制REF输出。这个模式可以通过寄存器来进行配置,具体操作看下面的表
江科大STM32学习笔记(详细版)——2023持续更新_第101张图片

  • 冻结
    那这个模式也比较简单,它根本就不管CNT谁大谁小,直接REF保持不变、维持上一个状态就行了,这有什么用呢?比如你正在输出PWM波,突然想暂停一会儿输出,就可以设置成这个模式,一但切换为冻结模式后,输出就暂停了,并且高低电平也维持为暂停时刻的状态,保持不变。这就是冻结模式的作用

这个有效电平和无效电平,一般是高级定时器里面的一个说法,是和关断、刹车这些功能配合表述的,它说的比较严谨,所以叫有效电平和无效电平。在这里为了理解方便,你可以直接认为置有效电平就是置高电平,置无效电平就是置低电平.

  • 匹配时…
    这三个模式都是当CNT与CCR值相等时,执行操作。
    这些模式就可以用做波形输出了,比如相等时电平翻转这个模式,这个可以方便地输出一个频率可调,占空比始终为50%的PWM波形。比如你设置CCR为0,那CNT每次更新清0时,就会产生一次CNT=CCR的事件,这就会导致输出电平翻转一次,每更新两次,输出为一个周期,并且高电平和低电平的时间是始终相等的,也就是占空比始终为50%,当你改变定时器更新频率时,输出波形的频率也会随之改变。它俩的关系是输出波形的频率=更新频率/2,因为更新两次输出才为一个周期。这就是匹配时电平翻转模式的用途。江科大STM32学习笔记(详细版)——2023持续更新_第102张图片

那上面这两个相等时置高电平和低电平,感觉用途并不是很大,因为它们都只是一次性的,置完高或低电平后,就不管事了,所以这俩模式不适合输出连续变化的波形。如果你想定时输出一个一次性的信号,那可以考虑一下下这两个模式。

  • 强制为无效电平|有效电平
    如果你想暂停波形输出,并且在暂停期间保持低电平或者高电平,那你就可以设置这两个强制输出模式。
  • PWM模式1|2
    它们可以用于输出频率和占空比都可调的PWM波形,也是我们主要使用的模式。这个情况比较多,一般我们都只使用向上计数,PWM模式2实际上就是PWM模式1输出的取反(改变PWM模式1和PWM模式2,就只是改变了REF电平的极性而已),是因为REF输出之后还有一个极性的配置,所以使用PWM模式1的正极性和PWM模式2的反极性最终的输出是一样的。所以使用的话,我们可以只使用PWM模式1,并且是向上计数,这一种模式就行了。

那PWM模式1向上计数是怎么输出频率和占空比都可调的PWM波形的呢?
在这里,我给出了输出PWM的基本结构,这也是我们本节课的重点内容
江科大STM32学习笔记(详细版)——2023持续更新_第103张图片
我们就再来看一下PWM的参数是如何计算的
江科大STM32学习笔记(详细版)——2023持续更新_第104张图片
25:46高级定时器的输出比较电路了解即可

代码实战:PWM的实际使用

6-3 PWM驱动LED呼吸灯
江科大STM32学习笔记(详细版)——2023持续更新_第105张图片
现象:在PA0端口接入LED,LED在不断地变换亮度,实现了一个呼吸灯的效果
江科大STM32学习笔记(详细版)——2023持续更新_第106张图片

  • 第一步,RCC开启时钟,把我们要用的TIM外设和GPIO外设的时钟打开
  • 第二步,配置时基单元
  • 第三步,配置输出比较单元,里面包括这个CCR的值、输出比较模式、极性选择、输出使能这些参数
    涉及函数:
    void TIM_OCXInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct)其中TIM_OCXInit的X为1、2、3、4,对应4个输出比较单元,或者说输出比较通道。你需要初始化哪个通道,就调用哪个函数。不同的通道对应的GPIO口也是不一样的,所以这里要按照你GPIO口的需求来。这里使用的是PAO口,对应的就是第一个输出比较通道。对于TIM2来说,就是下图对应引脚
    江科大STM32学习笔记(详细版)——2023持续更新_第107张图片
    你要使用哪个外设,就只能用对应的引脚,不过,但是虽然它是定死的,STM32还是给了我们一次更改的机会的,这就是重定义,或者叫重映射。比如如果你既要用USART2的TX引脚,又要用TIM2的CH3通道,它俩冲突成,没办法同时用,那我们就可以在这个重映射的列表里找一下,比如这里我们找到了TIM2的CH3,那TIM2的CH3就可以从原来的引脚,换到这里的引脚,这样就避免了两个外设引脚的冲突。如果这个重映射的列表里找不到,那外设复用的GPIO就不能挪位置.这就是重映射的功能,配置重映射是用AFIO来完成的,重映射在最后会讲
    江科大STM32学习笔记(详细版)——2023持续更新_第108张图片

作用:根据TIM_OCInitStruct中指定的参数初始化TIMx channel。
参数说明:

参数 说明
TIMx 所选择的TIM外设,对于通用定时器来说是2、3、4
TIM_OCInitStruct 指向结构 TIM_OCInitTypeDef 的指针,包含了 TIMx 时间基数单位的配置信息

TIM_OCInitTypeDef structure结构体说明:
江科大STM32学习笔记(详细版)——2023持续更新_第109张图片

实际上通用计时器只用到了这些结构体成员,但结构体里面还有些成员是面向高级定时器,比如:
江科大STM32学习笔记(详细版)——2023持续更新_第110张图片
但是如果当你中途想把高级定时器当做通用定时器输出PWM时,那你自然就会把TIM_OCXInit的TIM2改成TIM1。这样的话,这个结构体原本没有用到的成员,现在需要使用,但是对于那些成员并没有赋值,那就会导致高级定时器输出PWM出现一些奇怪的问题最终找到的原因,就是因为这里结构体成员没有配置完整。所以为了避免程序中出现不确定的因素,把结构体所有的成员都配置完整;需要么就先给结构体成员都赋一个初始值,再修改部分的结构体成员,
所以void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct)有了用武之地。
作用:TIM_OCInitStruct 中的每一个参数按缺省值填入
参数说明:

参数 说明
TIMx 所选择的TIM外设,对于通用定时器来说是2、3、4
TIM_OCInitStruct 指向结构 TIM_OCInitTypeDef 的指针,待初始化
  • 第四步,配置GPIO.把PWM对应的GPIO口,初始化为复用推挽输出的配置。为什么选择这个模式呢?对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的
    江科大STM32学习笔记(详细版)——2023持续更新_第111张图片

那通过刚才看到引脚定义表,我们就知道了,这里片上外设引脚连接的就是TIM2的CH1通道。所以,只有把GPIO设置成复用推挽输出,引脚的控制权才能交给片上外设,PWM波形才能通过引脚输出。

  • 那最后,第五步,就是运行控制了.启动计数器,这样就能输出PWM了

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)(通道1 )
作用:设置TIMx捕获比较寄存器值(CCR)
参数说明:

参数 说明
TIMx 所选择的TIM外设,对于通用定时器来说是2、3、4
Compare1 指定捕获比较程序寄存器的新值。

重映射:
根据你所要重映射的引脚,在下图找到所需要的模式,比如:如果我们想把PAO改到PA15,就可以选择这个部分重映射方式1,或者完全重映射。
江科大STM32学习笔记(详细版)——2023持续更新_第112张图片江科大STM32学习笔记(详细版)——2023持续更新_第113张图片在但是PA15在引脚定义图里没有加粗,因为它上电后已经默认复用为了调试端口JTDI,所以如果想让他作为普通的GPIO或者复用定时器的通道。那还需要先关闭调试端回的复用,也是用这个GPIO PinRemapConfig函数江科大STM32学习笔记(详细版)——2023持续更新_第114张图片
如果你想让PA15、PB3、PB4这三个引脚当做GPIO来使用的话,那就加一下这里的第一句和第三句,先打开AFIO时钟,再用AFIO将JTAG复用解除掉,这样就行了;
如果你想重映射定时器或者其他外设的复用引脚,那就加一下这里的第一句和第二句,先打开AFIO时钟,再用AFIO重映射外设复用的引脚,这样就行了;
如果你重映射的引脚又正好是调试端口,那这三句就都得加上,打开AFIO时钟,重映射引脚,解除调试端口,这样才行。
江科大STM32学习笔记(详细版)——2023持续更新_第115张图片

6-4PWM驱动舵机
!!这里一定要注意正负极!!接错可能会烧坏电脑!!

接线说明:

SG90舵机,它有三根线,第一个GND,就是棕色线,接在面包板的GND;第二个5V正极,就是红色线,这里要接5V的电机电源,大家不要把它接在面包板的正极了,这个STM32芯片正极只有3.3V的电压,而且输出功率不太,带不动电机的,所以我们需要把它接在STLINK的5V输出引脚;然后看第三个引脚,PWM信号,就是橙色线,接在PA1引脚上(这里用的是PA1的通道2)【看数据手册,里面的引脚定义表,PA0的复用功能是TIM2_CH1(通道一),PA1的复用功能是TIM2_CH2(通道2)】
那最后,再在PB1接一个按键,用来控制舵机,这样这个电路就完成了。
江科大STM32学习笔记(详细版)——2023持续更新_第116张图片

6-5PWM驱动直流电机
江科大STM32学习笔记(详细版)——2023持续更新_第117张图片

4.TIM输入捕获

输入捕获对于PID控制算法很重要,没有输入捕获就不能完成闭环控制,要做平衡车的一定要认真学

江科大STM32学习笔记(详细版)——2023持续更新_第118张图片
8:38~18:18频率测量的相关知识讲解
江科大STM32学习笔记(详细版)——2023持续更新_第119张图片
测频法:定时器中断,并记录捕获次数;测周法:捕获中断,并记录定时器次数。

输入捕获电路的工作流程

由四个问题来深入输入捕获的工作流程

  • 输入捕获和输出比较的区别?
  • 为什么要进行一个交叉连接呢?
  • 滤波器具体是怎么工作的呢?
  • 如何自动清零CNT呢?
  1. 输入捕获和输出比较的区别?
    江科大STM32学习笔记(详细版)——2023持续更新_第120张图片

对比一下输出比较,就是:
输出比较,引脚是输出端口;输入捕获,引脚是输入端口;
输出比较,是根据CNT和CCR的大小关系来执行输出动作;输入捕获,是接收到输入信号,执行CNT锁存到CCR的动作。

交叉连接的目的:
江科大STM32学习笔记(详细版)——2023持续更新_第121张图片
江科大STM32学习笔记(详细版)——2023持续更新_第122张图片

  1. 为什么要进行一个交叉连接呢?
    这样做的目的,个人认为主要有两个,第一个目的,可以灵活切换后续捕获电路的输入;第二个目的,也是它交叉的主要目的,就是可以把一个引脚的输入,同时映射到两个捕获单元,这也是PWMI模式的经典结构。第一个捕获通道,使用上升沿触发,用来捕获周期,第二个通道,使用下降沿触发,用来捕获占空比。两个通道同时对一个引脚进行捕获,就可以同时测量频率和占空比,这就是PWMI模式,等会儿再来继续分析。一个通道灵活切换两个引脚,和两个通道同时捕获一个引脚,这就是这里交叉一下的作用和目的。同样,下面通道3和通道4,也是一样的结构,可以选择各自独立连接,也可以选择进行交叉。另外,这里还有一个TRC信号,也可以选择作为捕获部分的输入,这样设计,也是为了无刷电机的驱动。

到这里,电路的整个工作流程讲完了。比如我们可以配置上升沿触发捕获,每来一个上升沿,CNT转运到CCR一次,又因为这个CNT计数器是由内部的标准时钟驱动的,所以CNT的数值,其实就可以用来记录两个上升沿之间的时间间隔,这个时间间隔,就是周期,再取个倒数,就是测周法测量的频率了。另外这里还有个细节问题,就是每次捕获之后,我们都要把CNT清0一下,这样下次上升沿再捕获的时候,取出的CNT才是两个上升沿的时间间隔,这个在一次捕获后自动将CNT清零的步骤,我们可以用主从触发模式,自动来完成

接下来就是执行细节的问题,把电路执行的细节都了解清楚,这样写程序的时候才能得心应手。好,那接着看一下这里,这是输入捕获通道1的一个更详细的框图,基本功能都是一样的。

江科大STM32学习笔记(详细版)——2023持续更新_第123张图片

  1. 滤波器具体是怎么工作的呢?
    江科大STM32学习笔记(详细版)——2023持续更新_第124张图片
    可以看一下手册,在CCMR1寄存器这里有IC1F位,根据它的描述简单理解,这个滤波器工作原理就是:以采样频率对输入信号进行采样,当连续N个值都为高电平,输出才为高电平,连续N个值都为低电平,输出才为低电平。如果你信号出现高频抖动,导致连续采样N个值不全都一样,那输出就不会变化,这样就可以达到滤波的效果。采样频率越低,采样个数N越大说滤波效果就越好,那下面这些描述,就是每个参数对应的采样频率和采样个数。在实际应用中,如果波形噪声比较大入100,就可以把这个参数设置大一些,这样就可以过滤噪声了。
    江科大STM32学习笔记(详细版)——2023持续更新_第125张图片
  2. 如何自动清零CNT呢?
    看一下这里,这个TI1FP1信号和TI1的边沿信号,都可以通向从模式控制器,比如TI1FP1信号的上升沿触发捕获,那通过这里,TI1FP1还可以同时触发从模式,这个从模式里面,就有电路,可以自动完成CNT的清零。所以可以看出,这个从模式就是完成自动化操作的利器。
    那接下来我们就来研究一下这个主从触发模式。主从触发模式有什么用,如何来完成硬件自动化的操作。
    江科大STM32学习笔记(详细版)——2023持续更新_第126张图片
    主从触发模式,就是主模式、从模式。
    如果想完成我们刚才说的任务,想让TI1FP1信号自动触发CNT清零,那触发源选择,就可以选中这里的TI1FP1,从模式执行的操作,就可以选择执行Reset的操作。这样TI1FP1的信号就可以自动触发从模式,从模式自动清零CNT,实现硬件全自动测量,这就是主从触发模式的用途。
    那有关这些信号的具体解释,可以看手册
    江科大STM32学习笔记(详细版)——2023持续更新_第127张图片
    江科大STM32学习笔记(详细版)——2023持续更新_第128张图片

那回到PPT,总结下来就是这三个图,主模式,触发源选择,从模式,在库函数里也非常简单。这三块东西,就对应三个函数,调用函数,给个参数,就行了,这些就是主从触发模式的内容。接下来,我们就来最后理一下思路,把之前的东西组合在一起,得到这两个图。这两个图也分别对应了我们演示两个代码的逻辑,先看第一个,输入捕获基本结构:
江科大STM32学习笔记(详细版)——2023持续更新_第129张图片然后还有几个注意事项说明一下,首先是这里CNT的值是有上限的,ARR—般设置为最大65535,那CNT最大也只能计65535个数。如果信号频率太低,CNT计数值可能会溢出(因为CNT计数的快慢是根据时基单元的时钟频率而变化的,如果时钟频率很高,CNT增长非常快,如果被测信号频率太低,完全有可能CNT计满65536都不到被测信号的一个周期)。另外还有就是,这个从模式的触发源选择,在这里看到,只有TI1FP1和TI2FP2,没有TI3和TI4的信号,所以这里如果想使用从模式自动清零CNT,就只能用通道1和通道2。对于通道3和通道4,就只能开启捕获中断,在中断里手动清零了,不过这样,程序就会处于频繁中断的状态,比较消耗软件资源,这个注意一下。

好,接下来我们继续来看最后一个PPT,这里展示的是PWMI基本结构。
江科大STM32学习笔记(详细版)——2023持续更新_第130张图片
这个PWMI模式,使用了两个通道同时捕获一个引脚,可以同时测量周期和占空比。
我们来看一下,上面这部分结构,和刚才演示的一样,下面这里多了一个通道。
首先,TI1FP1配置上升沿触发,触发捕获和清零CNT,正常地捕获周期,这时我们再来一个TI1FP2,配置为下降沿触发,通过交叉通道,去触发通道2的捕获单元,这时会发生什么呢?
我们看一下左上角的这个图,最开始上升沿,CCR1捕获,同时清零CNT,之后CNT一直++,然后,在下降沿这个时刻,触发CCR2捕获,所以这时CCR2的值,就是CNT从这里到这里的计数值,就是高电平期间的计数值,CCR2捕获,并不触发CNT清零,所以CNT继续++。
江科大STM32学习笔记(详细版)——2023持续更新_第131张图片
直到下一次上升沿,CCR1捕获周期,CNT清零,这样执行之后CCR1就是一整个周期的计数值,CCR2就是高电平期间的计数值,我们用CCR2/CCR1,是不是就是占空比了。这就是PWMI模式,使用两个通道来捕获频率和占空比的思路。
江科大STM32学习笔记(详细版)——2023持续更新_第132张图片
另外这里,你可以两个通道同时捕获第一个引脚的输入,这样通道2的前面这一部分就没有用到。
江科大STM32学习笔记(详细版)——2023持续更新_第133张图片
当然也可以配置两个通道同时捕获第二个引脚的输入,这样我们就是使用TI2FP1和TI2FP2这两个引脚了,这两个输入可以灵活切换。
在这里插入图片描述
好,到这里,我们本小节的内容差不多就结束了,最后大致看一下手册37:28

代码实战:输入捕获模式测频率和占空比

6-6 输入捕获模式测频率

现象:在这里,为了测量外部信号的频率,我们先得有个信号源,产生一个频率和占空比可调的波形,但是考虑到大家可能没有信号发生器,所以我这里就借用了一下上一小节的代码。先用PWM模块,在PAO端口输出一个频率和占空比可调的波形,然后我们本节的代码,测量波形的输入口是PA6,所以我们直接用一根线,把PAO和PA6连在一起,这样就能测量自己PWM模块产生波形的频率了。
目前这个程序只能测频率,还不能测量占空比,如果想同时测量频率和占空比,STM32的输入捕获还设计了一个PWM模式,即PWM输入模式。

在6-3 PWM驱动LED呼吸灯的工程基础上写
前置操作

PWM模块这里,我们还要再进行一些改进。目前这个代码的逻辑是初始化TIM2的通道1,产生一个PWM波形,输出引脚是PA0。然后通过SetCompare1函数,可以调节CCR1寄存器的值,从而控制PWM的占空比。但是目前PWM的频率,是在初始化里写好了的,是固定的,运行的时候调节不太方便,所以我们在最后再加一个函数,用来便捷地调节PWM频率。
如何调节PWM频率呢?
通过公式,我们知道PWM频率=更新频率=72M/(PSC+1/(ARR+1),所以PSC和ARR都可以调节频率,但是占空比=CCR/(ARR+1),所以通过ARR调节频率,还同时会影响到占空比,而通过PSC调节频率,不会影响占空比,显然比较方便。所以我们的计划是,固定ARR为100-1,通过调节PSC来改变PWM频率,另外ARR为100-1,CCR的数值直接就是占空比,用起来比较直观。
当然实际使用也是有技巧的,一般我们可以根据分辨率的要求,先确定好ARR,比如分辨率,1%就足够了;那ARR给100-1,这样PSC决定频率,CCR决定占空比。如果我想要更高的分辨率,比如0.1%,那ARR就先固定1000-1,这样频率就是72M/预分频/1000,占空比就是CCR/1000,这样也好算。

在这里,目前ARR我们固定给100-1,初始化操作的PSC就先不管,我们后面再写一个函数,在初始化之后单独修改PSC。
例如:定义一个void PWM_SetPrescaler(uint16_t Prescaler)函数,在自定义函数里面,我们就要调用库函数里单独写入PSC的函数了,TIM_PrescalerConfig,就是单独写入PSC的函数。因为这个函数还有一个重装模式的参数,所以它并不叫SetPrescaler,而叫PrescalerConfig。这是这个库的命名规范。
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode)
江科大STM32学习笔记(详细版)——2023持续更新_第134张图片
可能是因为手册版本太低了,并没有提到中间参数,那我们就看库里面的注释
江科大STM32学习笔记(详细版)——2023持续更新_第135张图片
参数Prescaler:要写入PSC的值。

接下来就可以写输入捕获的代码

第一步,RCC开启时钟,把GPIO和TIM的时钟打开
注意:我们这个代码还需要TIM2输出PWM,所以输入捕获的定时器要换一个,我们就换到TIM3(这里在组建IC捕获模块,TIM2是PWM已经定义好的,捕获模块要重新定义一个)。其次我们这里用到的是TIM3通道1,查引脚定义表,你就知道为什么连PA6。
第二步,GPIO初始化,把GPIO配置成输入模式,一般选择上拉输入或者浮空输入模式
第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行,这一步和之前的代码是一样的

ARR自动重装值,根据之前的分析,arr越大,输入捕获越能更精准地测更小的频率,其次防止计数溢出。
72M/预分频,就是计数器自增的频率,就是计数标准频率。这个需要根据你信号频率的分布范围来调整,我暂时先给72-1,这样标准频率就是72M/72=1MHz。

第四步,配置输入捕获单元,包括滤波器、极性、直连通道还是交叉通道、分频器这些参数,用一个结构体就可以统一进行配置了
江科大STM32学习笔记(详细版)——2023持续更新_第136张图片
第五步,选择从模式的触发源。触发源选择为TI1FP1,这里调用一个库函数,给一个参数就行了
江科大STM32学习笔记(详细版)——2023持续更新_第137张图片

第六步,选择触发之后执行的操作。执行Reset操作,这里也是调用一个库函数就行了
江科大STM32学习笔记(详细版)——2023持续更新_第138张图片

最后,当这些电路都配置好之后,调用TIM_Cmd函数,开启定时器,这样所有的电路就能配合起来,按照我们的要求工作了。直接读取CCR寄存器,然后按照fc/N,(N是读取CCR的值)计算一下就行了。这就是整个程序的思路

fc=72M/(PSC+1)
江科大STM32学习笔记(详细版)——2023持续更新_第139张图片

江科大STM32学习笔记(详细版)——2023持续更新_第140张图片

6-7 PWMI模式测频率占空比

在6-6 输入捕获模式测频率做修改
需要将输入捕获初始化的部分,需要进行一下升级,配置成两个通道同时捕获同一个引脚的模式,怎么配置呢?
两种方法:
第一种,把这个通道初始化的部分,复制一份,这个结构体定义的不要复制了。然后呢,通道1是直连输入,上升沿触发,沿用这个配置。接着下面,通道1改成通道2,直连输入,改成这个交叉输入,上升沿触发,改成下降沿触发,这样看一下,是不是就对应我们PPT的这个结构了。通道1,直连输入,上升沿触发;通道2,交叉输入,下降沿触发,这样就可以了。
江科大STM32学习笔记(详细版)——2023持续更新_第141张图片
第二种:库里有专门的封装函数。只针对于通道1和通道2
江科大STM32学习笔记(详细版)——2023持续更新_第142张图片
写一个获取占空比的函数,根据上一小节的分析,高电平的计数值存在CCR2里,整个周期的计数值存在CCR1里,我们用CCR2/CCR1,就能得到占空比了
在这里插入图片描述

CCR总少1,应该是CCR从0开始计数的
江科大STM32学习笔记(详细版)——2023持续更新_第143张图片

32:39 测频率的性能讲解
最后,我们来研究一下这个测频率的性能。
首先是测频率的范围,目前我们给的标准频率是1MHz,计数器最大只能计到65535。所以所测量的最低频率是1M/65535,这个值算一下大概是15Hz。如果信号频率再低,计数器就要溢出了,所以最低频率就是15Hz左右。那如果想要再降低一些最低频率的限制,我们可以把这个预分频再加大点,这样标准频率就更低,所支持测量的最低频率也就更低。这是测量频率的下限。

测得的频率等于fc/N,这里的N值就是CNT里面过去的,当N越大,频率越小,但是CNT最大不能超过ARR的值(最大为65535)所以测量的最小频率大概是15Hz

然后是测量的上限,就是支持的最大频率。这个最大频率,并没有一个明显的界限,因为随着待测频率的增大,误差也会逐渐增大,如果非要找个频率上限,那应该就是标准频率1MHZ,超过1MHz,信号频率比标准频率还高,那肯定测不了了。但是这个1MHz的上限并没有意义,因为信号频率接近1MHz时,误差已经非常大了,所以最大频率要看你对误差的要求。上一小节我们说到了正负1误差,计100个数,误差1个,相对误差就是百分之一;计1000个数,误差1个,相对误差就是千分之一,所以正负1误差可以认为是1/计数值。在这里,如果要求误差等于千分之一时,频率为上限那这个上限就是1M/1000=1KHz;如果要求误差可以到百分之一,那频率上限就是1M/100=10KHz,这就是频率的上限.如果想提高频率的上限,那我们在这里(时基单元初始化时),就要把PSC给降低一点.,提高标准频率,上限就会提高。除此之外,如果频率还要更高,那我们就要考虑一下测频法了。测频法适合高频,测周法适合低频,我们这里是测周法,所以对于非常高的频率,还是交给测频法来解决吧。

然后呢,还有一个就是误差分析。除了我们之前说的正负1误差外,在实际测量的时候,还会有晶振误差。比如我们STM32的晶振不是那么准,在计次几百几万次之后,误差累积起来,也会造成一些影响

TIM编码器接口

江科大STM32学习笔记(详细版)——2023持续更新_第144张图片
江科大STM32学习笔记(详细版)——2023持续更新_第145张图片
那使用正交信号相比较单独定义一个方向引脚,有什么好处呢?
首先就是正交信号精度更高,因为A、B相都可以计次,相当于计次频率提高了一倍;其次就是正交信号可以抗噪声,因为正交信号,两个信号必须是交替跳变的,所以可以设计一个抗噪声电路。如果一个信号不变,另一个信号连续跳变,也就是产生了噪声,那这时计次值是不会变化的。
在这里插入图片描述

所以我们编码器接口的设计逻辑就是,首先把A相和B相的所有边沿作为计数器的计数时钟,出现边沿信号时,就计数自增或自减,然后到底是增还是减呢,这个计数的方向由另一相的状态来确定。当出现某个边沿时,我们判断另一相的高低电平,如果对应另一相的状态出现在上面这个表里,那就是正转,计数自增;反之,另一相的状态出现在下面这个表里那就是反转,计数自减,这样就能实现编码器接口的功能了,这也是我们STM32定时器编码器接口的执行逻辑。

接下来,我们就来看一下这个定时器的框图,看一下这个编码器接口的电路是如何设计的。
江科大STM32学习笔记(详细版)——2023持续更新_第146张图片

注意使用编码器模式的时候,我们之前一直在使用的72MHz内部时钟,和我们在时基单元初始化时设置的计数方向,并不会使用。因为此时计数时钟和计数方向都处于编码器接口托管的状态,计数器的自增和自减,受编码器控制.

然后我们看一下这里,我给出的一个编码器接口基本结构。
江科大STM32学习笔记(详细版)——2023持续更新_第147张图片
输入捕获的前两个通道,通过GPIO口接入编码器的A、B相,然后通过滤波器和边沿检测极性选择 ,产生TI1FP1和TI2FP2,通向编码器接口。编码器接口通过预分频器控制CNT计数器的时钟,同时,编码器接口还根据编码器的旋转方向,控制CNT的计数方向,编码器正转时,CNT自增,编码器反转时,CNT自减。
另外这里ARR也是有效的,一般我们会设置ARR为65535,最大量程,这样的话,利用补码的特性,很容易得到负数。比如CNT初始为0,我正转,CNT自增,0、 1、2、3、4、5、6、7等等,显示都没问题,但是我反转呢,CNT自减,0下一个数就是65535,接着是65534、65533等等这里负数不应该是-1、-2吗,65535是不是就出问题了。但是没关系,直接把这个16位的无符号数转换为16位的有符号数。根据补码的定义,这个65535就对应-1,65534就对应-2(有符号编码时负数按补码计算,2^16 的补码= -1)等等,这样就可以直接得到负数,非常方便,这就是我们读出数据得到负数的一个小技巧。

最后我们来看一些工作细节,和两个小例子。
这个工作描述的表,描述的就是我们刚才说什么时候正转、反转的,编码器接口的工作逻辑
江科大STM32学习笔记(详细版)——2023持续更新_第148张图片
江科大STM32学习笔记(详细版)——2023持续更新_第149张图片

这个实例展示的是极性的变化对计数的影响。
TI1反相是什么意思呢?江科大STM32学习笔记(详细版)——2023持续更新_第150张图片
此时看下这个图,这里TI1和TI2进来,都会经过这个极性选择的部分。
在这里插入图片描述
在输入捕获模式下,这个极性选择是选择上升没有效还是下降沿有效的。但是根据我们刚才的分析,编码器接口,显然始终都是上升沿和下降沿都有效的,上升沿和下降沿都需要计次,所以在编码器接口模式下,这里就不再是边沿的极性选择了而是高低电平的极性选择。如果我们选择上升沿的参数,就是信号直通过来,高低电平极性不反转;如果选择下降沿的参数,就是信号通过一个非门过来,高低电平极性反转,所以这里就会有两个控制极性的参数,选择要不要在这里加一个非门,反转一下极性。
江科大STM32学习笔记(详细版)——2023持续更新_第151张图片
显然,这两个实例图的计数方向是相反的,这有什么作用呢?
比如你接一个编码器,发现它数据的加减方向反了,你想要正转的方向,结果它自减了,你想要反转的方向,结果它自增了,这时,就可以调整一下极性,把任意一个引脚反相,就能反转计数方向了。当然如果想改变计数方向的话,我们还可以直接把A、B相两个引脚换一下。

我们本节的内容(4.编码器接口),对应手册这里的14.3.12 编码器接口模式

代码实战

这里编码器测速一般应用在电机控制的项目上,使用PWM驱动电机,再使用编码器测量电机的速度,然后再用PID算法进行闭环控制。
现象:接了一个旋转编码器模块,这个代码和之前我们写的旋转编码器计次的代码,实现的功能基本都是一样的。目前我们这个代码,本质上也是旋转编码器计次,只不过这个代码是通过定时器的编码器接口,来自动计次。而我们之前的代码是通过触发外部中断,然后在中断函数里手动进行计次,使用编码器接口的好处就是节约软件资源,
如果使用外部中断来计次,那当电机高速旋转时,编码器每秒产生成千上万个脉冲,程序就得频繁进中断,然后进中断之后,完成的任务又只是简单的加—减一,是不是我们的软件资源就被这种简单而又低级的工作给占用了。所以,对于这种需要频繁执行,操作又比较简单的任务,一般我们都会设计一个硬件电路模块,来自动完成。那我们本节这个编码器接口,就是用来自动给编码器进行计次的电路。如果我们每隔一段时间取一下计次值,就能得到编码器旋转的速度了。

江科大STM32学习笔记(详细版)——2023持续更新_第152张图片
在这里插入图片描述
江科大STM32学习笔记(详细版)——2023持续更新_第153张图片

第一步,RCC开启时钟,开启GPIO和定时器的时钟
第二步,配置GPIO,这里需要把PA6和PA7配置成输入模式
第三步,配置时基单元,这里预分频器我们一般选择不分频
第四步,配置输入捕获单元。不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到,与编码器无关
第五步,配置编码器接口模式。这个直接调用一个库函数就可以了
江科大STM32学习笔记(详细版)——2023持续更新_第154张图片

最后,调用TIM_Cmd,启动定时器,就完事了


模块篇

按键

江科大STM32学习笔记(详细版)——2023持续更新_第155张图片

传感器模块

江科大STM32学习笔记(详细版)——2023持续更新_第156张图片

江科大STM32学习笔记(详细版)——2023持续更新_第157张图片

对于光敏电阻传感器来说,这个N1就是光敏电阻;对于热敏电阻传感器来说,这个N1就是热敏电阻;对应这个红外传感器来说,这个N1就是一个红外接收管

左边这个C2是一个滤波电容,它是为了给中间的电压输出进行滤波的用来滤除一些干扰,保证输出电压波形的平滑一般我们在电路里遇到这种一端接在电路中,另一端接地的电容都可以考虑一下这个是不是滤波电容的作用,如果是滤波电容的作用,那这个电容就是用来保证电路稳定的。并不是电路的主要框架,这时候我们在分析电路的时候,就可以先把这个电容给抹掉,这样就可以使我们的电路分析更加简单。
那我们把这个电容抹掉,整个电路的主要框架就是定值电阻和传感器电阻的分压电路了。在这里可以用分压定理来分析一下传感器电阻的阻值变化对输出电压的影响,当然我们还可以用上下拉电阻的思维来分析,当这个N1阻值变小时,下拉作用就会增强,中间的AO端的电压就会拉低,极端情况下,N1阻值为0,AO输出被完全下拉,输出0V;当N1阻值变大,下拉作用就会减弱,中间的引脚由于R1的上拉作用,电压就会升高极端情况下,N1阻值无穷大,相当于断路,输出电压被R1拉高至VCC
江科大STM32学习笔记(详细版)——2023持续更新_第158张图片
这个LM393是一个电压比较器芯片,里面有两个独立的电压比较器电路,然后剩下的是VCC和GND供电。这个电压比较器其实就是一个运算放大器,当这个同相输入端的电压大于反相输入端的电压时,输出就会瞬间升高为最大值也就是输出接VCC,反之当同相输入端的电压小于反相输入端的电压时,输出就会瞬间降低为最小值也就是输出接GND,这样就可以对一个模拟电压进行二值化了,这里同相输入端IN+接到了AO这里,就是模拟电压端。
IN-呢,接了一个电位器,这个电位器的接法也是分压电阻的原理,拧动电位器,IN-就会生成一个可调的阈值电压,两个电压进行比较,最终输出结果就是DO,数字电压输出,DO最终就接到了引脚的输出端,这就是数字电路的由来,然后右边这里还有两个指示灯电路,左边的是电源指示灯,通电就亮;右边的是DO输出指示灯,它可以指示DO的输出电平,低电平点亮,高电平熄灭。那右边DO这里还多了个R5上拉电阻,这个是为了保证默认输出为高电平的。

旋转编码器

31:05开始介绍了编码器
江科大STM32学习笔记(详细版)——2023持续更新_第159张图片

江科大STM32学习笔记(详细版)——2023持续更新_第160张图片

舵机

江科大STM32学习笔记(详细版)——2023持续更新_第161张图片

江科大STM32学习笔记(详细版)——2023持续更新_第162张图片
如果单独供电的话,供电的负极要和STM32共地,然后正极接在5V供电引脚上不同的电源需要共地

可以看出,舵机其实并不是一种单独的电机,它的内部是由直流电机驱动的,它里面还有一个控制电路板,是一个电机的控制系统。大概的执行逻辑是:PWM信号输入到控制板,给控制板一个指定的目标角度,然后,这个电位器检测输出轴的当前角度。如果大于目标角度,电机就会反转;如果小于目标角度,电机就会正转,最终使输出轴固定在指定角度,这就是舵机的内部工作流程。
棕色是电源负,红色是电源正,橙色是信号线

直流电机

江科大STM32学习笔记(详细版)——2023持续更新_第163张图片
江科大STM32学习笔记(详细版)——2023持续更新_第164张图片


一些注意问题

参考资料:
电路分析基础(6)-总说电路的“地”

关于在外设接线中注意的问题——共地

这是因为,在实际中,各处的零电位实际上是不太相同的,将地线接在一起是为了统一零电位,以保证各处的电压,即电势差有统一的关系。
江科大STM32学习笔记(详细版)——2023持续更新_第165张图片

补充:C语言

你可能感兴趣的:(嵌入式,stm32,学习,笔记)