目录
EXTI外部中断
NVIC嵌套中断向量控制器
内核外设(叫号系统)
统一分配中断优先级和管理中断
NVIC优先级分组(给不同的中断(病人)分配优先级)
抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队
NVIC的中断优先级由优先级寄存器的4位(0~15)决定(16个优先级,值越小,优先级越高)
合为4
两块,先分组,再配置
EXTI外部中断
检测GPIO电平变化,申请中断
第一个病人
支持的触发方式:上升沿(低电平变高电平时触发)/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
AFIO
AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
或门,多个输入全为0,输出才是0,有一个高电平1,则1
与门:多个输入全为1,输出才是1,有一个高电平0,则0
多个输入,选择一个输出
信号,外部驱动的,突发的,被动读取,转瞬即逝,不会等你
旋转编码器
用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
带正交波形信号输出的编码器,是可以用来测方向的(正交输出)
PB14号引脚的电平信号就可以顺利通过AFIO,进入到后级EXTI电路了
A,B相都触发中断
只有在B相下降沿和A相低电平时,才判断为正转
在A相下降沿和B相低电平时,才判断为反转
PB14-----EXTI15_10 (EXTI14)
PB1-----EXTI1
PB0-----EXTI0
定时器TIM
TIM定时中断
定时触发中断,就是计数器
当这个计数器的输入是一个准确可靠的基准时钟的时候,那它在对这个基淮时钟进行计数的过程,实际上就是计时的过程(有着配备基淮时钟的一个计数器,计数也是计时了)
定时器的基淮时钟一般都是主频72MHz
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
TIM输出比较(OC)
定时器的输出比较模块输出一定频率和占空比的PWM波形
CNT:时基单元里的计数器
CCR:捕获/比较寄存器(输入捕获,输出比较共用)
CNT计数自增,CCR是我们给定的一个值,两者比较大小,输出置1置0,输出电平不断跳变的PWM波形
pwm波形,等效的实现模拟信号的输出(0和1间快速转换,足够快时相当于达到了一个中间的状态),具有惯性的系统中,对脉冲宽度调制,等效获得所需模拟参量(数字系统等效输出模拟量)
引脚重映射
这个TM2的CH1可以从PAO挪到PA15引脚上
舵机
舵机要求的周期时20ms
CCR 500-2500(运行过程中设置)
直流电机
电能转换为机械能,有两个电极
当电极正接时,电机正转,当电极反接时,电机反转
直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向
TIM输入捕获(IC)
对于一个定时器,输入捕获,输出比较只能使用一个不能同时使用,共用寄存器,共用引脚
输出比较模式下,CCR是只写的,要用SetCompare写入
输入捕获模式下,CCR是只读的,要用GetCapture读出
输入引脚电平跳变的瞬间,把CNT的值锁存到CCR中
可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道,基本定时器没有输入捕获功能
TIM编码器接口
TIM的编码器接口接收正交信号(编码器产生的),自动控制CNT自增或自减
这个编码器接口,其实就相当于是一个带有方向控制的外部时钟,它同时控制着CNT的计数时钟和计数方向
相当于带有方向控制的外部时钟,同时控制CNT的计数时钟计数方向,CNT的值表示编码器的位置,隔一段时间取一次,再清零,取出来的值表示编码器的速度(测频法测正交脉冲的频率)
CNT的值表示了编码器的位置
两个输入引脚借用了输入捕获的通道1(CH1)和通道2(CH2)
CH3,CH4不能接编码器
附:公式相关
定时1s == 定时频率为1Hz
PSC 与 ARR取值0-65535
预分频器PSC
自动重装器ARR
ARR,PSC,CCR共同决定输出PWM的周期和占空比
代码
EXTI外部中断
对射式红外传感器计次
流程总结
(这种需要实时处理的就是中断信号,让系统承认他,给他配置了中断信号的待遇,中断程序中实现了计次操作)
1.初始化
RCC (GPIOB , AFIO , EXTI(无需) , NVIC(内核的外设无需))
GPIO (端口为输入模式(参考手册,浮空上拉或下拉))
AFIO(选择GPIOx , 连接到EXTI)
(PB14号引脚的电平信号就可以顺利通过AFIO , 进入到后级EXTI电路了)
EXTI(选择边沿触发方式--上升沿\\\\\\\\选择触发响应方式--中断响应)
NVIC(给中断选一个合适的优先级)
(进入CPU,CPU收到中断信号,跳转到中断函数执行中断程序)
2.写中断函数(EXTI15_10_IRQHandler)
中断函数名字固定,每个中断通道(EXTI15_10_IRQn)对应一个中断函数
中断标志位判断是否为1(EXTI10-EXTI15都可进来,但当前是EXTI14)
执行中断程序
清除中断标志位
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
OLED_Init();
CountSensor_Init();
OLED_ShowString(1, 1, "Count:");//1行1列显示字符串Count:
while (1)//实时获取刷新那个值,这个速度比你中断程序的外界的那个速度快
{
OLED_ShowNum(1, 7, CountSensor_Get(), 5);//OLED不断刷新显示CountSensor_Get的返回值
}
}
CountSensor.c
#include "stm32f10x.h" // Device header
//一个数字统计中断触发的次数
uint16_t CountSensor_Count;
void CountSensor_Init(void)//外部中断(所经所有模块)配置一下(初始化)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//GPIO
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 外部中断 引脚选择
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
//EXTI
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = 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
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//抢占响应优先级配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//选择配置NVIC的EXTI15_10线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级配置给1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级配置给1
NVIC_Init(&NVIC_InitStructure);
}
//返回那个值
uint16_t CountSensor_Get(void)
{
return CountSensor_Count;
}
//中断函数
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14)==SET)//读取EXTI14的标志位是否为1
{//执行中断程序
CountSensor_Count++;
EXTI_ClearITPendingBit(EXTI_Line14);//清除中断标志位
}
}
旋转编码器计次
//main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t Num; //定义待被旋转编码器调节的变量
int main(void)
{
OLED_Init();
Encoder_Init();
OLED_ShowString(1, 1, "Num:");
while (1)
{
Num += Encoder_Get(); //获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上
OLED_ShowSignedNum(1, 5, Num, 5);
}
}
///Encoder.c
//旋转编码器
#include "stm32f10x.h" // Device header
int16_t Encoder_Count;//带着符号的(正反转)
void Encoder_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//AFIO 外部中断 引脚选择
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
//EXTI
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
//NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//抢占响应优先级配置
//+要对两个通道分别设置优先级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//选择配置NVIC的EXTI15_10线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级配置给1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级配置给1
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//选择配置NVIC的EXTI15_10线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级配置给1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应优先级配置给1
NVIC_Init(&NVIC_InitStructure);
}
int16_t Encoder_Get(void)//返回值
{
//返回Encoder_Count后清零,所以返回的是每次的增量
int16_t Temp;
Temp = Encoder_Count;
Encoder_Count = 0;
return Temp;
}
//第一个中断函数
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET)//判断是否是外部中断0号线触发的中断
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)//PB0的下降沿触发中断,检测另一相PB1的电平,判断旋转方向
{
Encoder_Count --;//反转,计数变量自减
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
//第二个中断函数
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)//PB1的下降沿触发中断,检测另一相PB0的电平
{
Encoder_Count ++;//正转
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
TIM定时器
定时器定时中断
流程总结
1.初始化
RCC开启时钟(通用定时器TIM2(APB1))
选择时基单元时钟(选择定时器的时钟源):RCC内部时钟
配置时基单元
使能 更新中断(中断输出配置):(UI,与更新事件对应的那个)
NVIC(打开定时器中断的通道,并分配一个优先级)(两块,分组,配置)
启动(使能)定时器(运行控制)
2.中断函数
检查中断标志位
清除标志位
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num; //定义在定时器中断里自增的变量
int main(void)
{
OLED_Init();
Timer_Init(); //定时中断初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
while (1)
{
OLED_ShowNum(1, 5, Num, 5); //不断刷新显示Num变量
}
}
//定时器中断函数
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//查看标志位
{
Num++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清楚标志位
}
}
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//选择定时器的时钟源
TIM_InternalClockConfig(TIM2);//设置TIM2定时器的时基单元的时钟源是内部时钟
//配置(初始化)时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//和滤波器有关的一个参数
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器的计数模式向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000-1;//ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1;//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值(高级定时器才有的)(这里无需)
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);//避免刚初始化完就进中断的问题,让定时器从0开始 //清除定时器更新标志位
//使能更新中断(开启更新中断到NVIC的通路)
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//抢占响应优先级配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//选择配置NVIC的EXTI15_10线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级配置给1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级配置给1
NVIC_Init(&NVIC_InitStructure);
//启动定时器,定时器使能
TIM_Cmd(TIM2, ENABLE);
}
/*
//定时器中断函数
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//查看标志位
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清楚标志位
}
}
*/
定时器外部时钟
对射式红外传感器作为一个外部时钟
1.通过ETR引脚的外部时钟模式2(内部时钟改为外部时钟)
2.增加GPIO时钟及初始化
3.预分频自动重装值改小一点
/*//选择定时器的时钟源
TIM_InternalClockConfig(TIM2);//设置TIM2定时器的时基单元的时钟源是内部时钟
*/
//参:外部触发预分频器。外部触发极性。滤波器相关
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);
PWM驱动LED呼吸灯
流程总结
(GPIO的PA0引脚输出PWM波驱动LED)(LED变成了高电平点亮(更直观一点)(占空比越大,LED越亮,占空比越小。LED就越暗))
运行过程中不断更改CCR的值,以更改占空比,占空比越大,LED越亮
1.初始化
RCC开启时钟(TIM外设和GPIO外设)
初始化时基单元
初始化输出比较单元(里面包括这个CCR的值、输出比较模式(PWM1)、极性选择、输出使能这些参数)
(PA0口对应第一个输出比较通道OC1)
初始化GPIO(把PWM对应的GPIO口,初始化为复用推挽输出的配置)
运行控制:启动计数器
2.更改CCR的值
/main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i; //定义for循环的变量
int main(void)
{
OLED_Init(); //OLED初始化
PWM_Init(); //PWM初始化
while (1)
{
for (i = 0; i <= 100; i++)//逐渐变亮
{
PWM_SetCompare1(i);
Delay_ms(10);//非常关键!!!!!
}
for (i = 0; i <= 100; i++)//逐渐变暗
{
PWM_SetCompare1(100 - i);
Delay_ms(10);
}
}
}
PWM.C
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
//GPIO
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);
TIM_InternalClockConfig(TIM2);//选择TIM2定时器的时钟为内部时钟
//时基单元初始化
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 //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//PSC //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//初始化输出比较通道
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 = 0;//CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//启动定时器
TIM_Cmd(TIM2, ENABLE);
}
//根据传入的参数更改CCR的值
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);//用于单独更改通道1的CCR值
}
PWM驱动舵机
PA1口 通道2
(同一个定时器的不同通道,公用计数器,频率一样,占空比由CCR决定,可各自设置,由于计数器更新。所有PWM同时跳变,相位同步)
运行过程中设置CCR
//main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum; //按键键码
float Angle; //定义角度变量
int main(void)
{
//模块初始化
OLED_Init(); //OLED初始化
Servo_Init(); //舵机初始化
Key_Init(); //按键初始化
//显示静态字符串
OLED_ShowString(1, 1, "Angle:"); //1行1列显示字符串Angle:
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1) //按键按下
{
Angle += 30; //角度变量自增30
if (Angle > 180) //角度变量超过180后
{
Angle = 0; //角度变量归零
}
}
Servo_SetAngle(Angle); //设置舵机的角度为角度变量
OLED_ShowNum(1, 7, Angle, 3);
}
}
/Servo.c
#include "stm32f10x.h" // Device header
void Servo_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
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 = 0;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
}
//根据传入的参数更改CCR的值
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare);//用于单独更改通道1的CCR值
}
void Servo_SetAngle(float Angle)//舵机设置角度
{
PWM_SetCompare2(Angle / 180 * 2000 + 500); //设置占空比
//将角度线性变换,对应到舵机要求的占空比范围上
}
PWM驱动直流电机
TB6612电机驱动模块
PIN2,通道3
输入捕获模式测 频率
(只能测高低电平数字信号)(测周法) 测量信号从PA6输入 待测PWM信号自己生成
通过ARR调节频率,会影响占空比,所以通过调节PSC
ARR定-
在PWM呼吸灯程序上,加一个模块单独修改PSC
流程总结
1.PA0口输出频率为1kHz占空比50%的待测信号
2.输入捕获初始化
RCC开启时钟(TIM和GPIO的)(TIM2输出PWM,所以用TIM3输入捕获,TIM3--CH1--PA6)
GPIO初始化,输入模式(上拉或浮空)
配置时基单元,让CNT计数器在内部时钟的驱动下自增运行(自动重装值要设置大点防止计数溢出)
配置输入捕获单元(滤波器,极性(上升沿触发),直连或交叉通道,分频器(不分频每次触发都有效))(结构体)(TIM3通道一)
主从模式的相关配置
选择从模式(TRGI)的触发源(TI1FP1)
选择触发后执行的操作(从模式为Reset)
开启定时器(TIM_Cmd)
3.测周法公式配置f_x=f_c / N
(f_c当前是1000000Hz(f_c=72M /(PSC+1)),N为CCR的值)
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate); //设置PSC的值
}
main.c
//-----------------------输入捕获测频率-----------------------------
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{ OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1, 1, "Freq:00000Hz");
//PWM模块提供输入捕获的测试信号
PWM_SetPrescaler(720 - 1); //PWM频率Freq = 72M / (PSC + 1) / 100
PWM_SetCompare1(50); //PWM占空比Duty = CCR / 100
while (1)
{ OLED_ShowNum(1, 6, IC_GetFreq(), 5); //不断刷新显示输入捕获测得的频率
}
}
///IC.c
#include "stm32f10x.h" // Device header
void IC_Init(void)
{ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
//GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM3);//选择TIM2定时器的时钟为内部时钟
//时基单元初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//ARR //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
//初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//选择配置定时器通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF;//输入滤波器参数,可以过滤信号抖动
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//上升沿触发
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//不分频
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//直连通道
TIM_ICInit(TIM3, &TIM_ICInitStructure);
//触发源选择
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
//选择从模式
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
//启动定时器
TIM_Cmd(TIM3, ENABLE);
}
//测周法公式配置f_x=f_c / N
uint32_t IC_GetFreq(void)
{ return 1000000 / (TIM_GetCapture1(TIM3) + 1);//不执行+1的操作也可
}
PWMI模式测 频率 占空比
输入捕获初始化的部分升级,配置成两个通道同时捕获同一个引脚的模式
/*PWMI模式初始化*/
TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择配置定时器通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,选择为上升沿触发捕获
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //输入信号交叉,选择直通,不交叉
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
/
//获取占空比
uint32_t IC_GetDuty(void)
{
return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1); //占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可
}
在函数里,会自动把剩下的一个通道初始化成相反的配置
比如我这里传入通道1,直连,上升沿
那函数里面就会顺带配置通道2,交叉,下降沿
编码器接口测速
(定时器定时中断)
1.初始化
RCC开启时钟(GPIO,TIM)
配置GPIO,时基单元
配置输入捕获单元(滤波器,极性选择)
启动定时器
(测编码器位置,直接读CNT值,测编码器速度和方向,设置匝门时间,取出CNT并清零)
/*定时器编码器接口配置*/
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
常用代码
AFIO
//复位,清除所有操作
void GPIO_AFIODeInit(void);
//配置AFIO时间输出功能
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);
//(外部中断)配置AFIO数据选择器,选择中断引脚
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
EXTI
void EXTI_DeInit(void);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
//软件触发外部中断(外部引脚触发中断时不需要)
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
//读取标志位
//主程序
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);//获取指定的标志位是否被置1
void EXTI_ClearFlag(uint32_t EXTI_Line);//清除置1的标志位
//中断函数
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);获取中断标志位是否被置1
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除中断挂起标志位
NVIC
(misc.h)
//中断分组
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
//设置中断向量表
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
//系统低功耗配置
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
TIM
void TIM_DeInit(TIM_TypeDef* TIMx);//恢复默认配置
//时基单元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//同before
//使能计数器(运行控制)
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
//使能中断输出信号(中断输出控制)
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
//(时基单元的时钟选择部分)
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择ITRx
其他定时器的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);//选择TIx
捕获通道的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式1输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式2输入的时钟
//单独用来配置ETR引脚的预分频器、极性、滤波器这些参数的
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
//单独写预分频值的
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
//改变计数器的计数模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
//自动重装器预装功能配置
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
//给计数器写入一个值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
//给自动重装器写一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
//获取当前计数器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
//获取当前预分频器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
//获取标志位,清除标志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
//配置输出比较模块
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
//给输出比较结构体赋默认值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
//配置强制输出模式
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
//单独更改CCR寄存器值
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
//仅高级定时器,输出PWM时需调用
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
//给结构体赋初始值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
//结构体配置初始化输入捕获单元(四个通道共用了一个函数(可能有交叉通道的配置))(一次配置一个通道)
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
//初始化输入捕获单元(两个通道)
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_Ivoid TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
//3主从触发
//选择输入触发源TRGI
CStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
//选择输出触发源TRGO
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
//选择从模式
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
//配置通道里的分频器
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
//读取四个通道的CCR
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
接线
对射式红外传感器计次
旋转编码器计次
定时器定时中断
定时器外部时钟
PWM驱动LED呼吸灯
PWM驱动舵机
PWM驱动直流电机
输入捕获模式测频率 PWMI模式测 频率 占空比
编码器接口测速