目录
一、什么是通用定时器
1、通用定时器的分布和各自特点
TIM2到TIM5
特点:
①有四个通道
②通用定时器包含一个 16 位(TIM3 和 TIM4)或 32 位(TIM2 和 TIM5)递增、递减和递增/递减自动重载计数器。
③16 位可编程预分频器
④输入捕获和输出比较
⑤编码器使用
TIM9和TIM12
特点:
①有两个通道
②通用定时器包含一个 16位自动重载计数器递增计数器。
③16 位可编程预分频器
④输入捕获和输出比较
TIM10,TIM11,TIM13,TIM14
特点:
①有一个通道
②通用定时器包含一个 16 自动重载计数器递增计数器。
③16 位可编程预分频器
④输入捕获和输出比较
2、通用定时器使用情景
1、延时和定时中断
2、输出比较(驱动IO口输出PWM )
3、输入捕获(定时器计算IO口高低电平的时间)
此篇文章重点学习输出比较与输入捕获........
二、如何配置通用定时器
1、通用定时器框图
2、通用定时器框图结构
2.1 时钟源选择部分
TIM2~5:
①内部时钟作为时钟源
配置此寄存器后,便可使用内部时钟作为时钟源
总结:
信号源来自于哪里
APBX总线
如何选择本时钟源
SMCR寄存器的SMS 000
②外部时钟源模式2作为时钟源
经过从左往右:
配置完以上寄存器,便可使用外部时钟源模式2作为时钟源
信号源来自于哪里
片外接入(时钟脉冲设备)
如何选择本时钟源
SMCR寄存器的 ECE位 写入1
经过:
变沿选择 分频 滤波
③外部时钟源模式1作为时钟源
总结:
信号源来自于哪里
ITRX(定时器级联)
级联的作用
如何配置级联(级联表)
TI1F_ED(输入捕获的通道1接口进入不经过滤波)
TI1FP1(输入捕获的通道1接口进入经过滤波)
TI2FP2(输入捕获的通道2接口进入经过滤波)
ETRF(外部时钟模式2的接口进入)
说明:如何选择以上某一个信号源 SMCR寄存器的TS位
如何选择本时钟源
SMCR寄存器的SMS 111
选择ITRX(定时器级联)的配置:
TIM9和TIM12:
①内部时钟作为时钟源
总结:
信号源来自于哪里
APBX总线
如何选择本时钟源
SMCR寄存器的SMS 000
②外部时钟源模式1作为时钟源
总结:
信号源来自于哪里
ITRX(定时器级联)
级联的作用
如何配置级联(级联表)
TI1F_ED(输入捕获的通道1接口进入不经过滤波)
TI1FP1(输入捕获的通道1接口进入经过滤波)
TI2FP2(输入捕获的通道2接口进入经过滤波)
ETRF(外部时钟模式2的接口进入)
说明:如何选择以上某一个信号源 SMCR寄存器的TS位
如何选择本时钟源
SMCR寄存器的SMS 111
TIM10、11、13、14:
内部时钟作为时钟源
总结:
信号源来自于哪里
APBX总线
如何选择本时钟源
SMCR寄存器的SMS 000
特别提示:通常情况建议用内部时钟,不用外部时钟
TIM9 / TIM12
时钟源:
同TIM2 ~ TIM5 但是没有外部时钟模式2
TIM10 TIM11 TIM13 TIM14
时钟源:
只能用内部时钟源(APBx)
2.2 基本定时器部分
完全符合基本定时规则
2.3 输出比较部分
PWM:波形的周期不变,占空比可以改变
占空比:高电平的时间与周期的比(高低电平的时间可以改变)
周期:一个脉冲的时间
配置定时器寄存器输出PWM
CR1:
7 位 重装载值寄存器影子寄存器使能位 //1
6:5 位 对齐方式 //00 边沿对齐方式
4 位 计数方向 //0 递增计数
3 位 计数模式 //0 连续计数模式
2: 1 位 允许更新事件,UG位触发 //00
0 位 使能计数器 //1
SMCR
2:0 位 时钟源选择 //000 内部时钟源
CCMRx
6:4 位 输出形式 //110 PWM波模式1
3 位 比较寄存器的影子寄存器使能 //1 使用影子寄存器
1:0 位 通道模式配置 //00 选择通道的输出模式
CCER
3 位 输出比较模式必须为0 //0
1 位 选择有效电平 //根据自己需求
0 位 1 号通道使能 //1 通道使能
PSC
内部时钟分频 //直接写
ARR
计数总数 一个脉冲的周期 //直接写入
CCRx
设置比较值(占空比) //直接写入
EGR
0 位 人为产生更新事件 //1 UG位人为产生更新事件
注意:
此时影子寄存器全部使能
自动重装载值影子寄存器保证一个周期的完整性
比较寄存器影子保证一个周期内占空比不变
PWM原理:PWM可以驱动IO的电压,改变占空比从而改变电压
设计程序
①总体思路
{
/*IO控制器配置*/
/*定时器控制器配置*/
}
②详细思路
{
/*IO控制器配置*/
//端口时钟使能
//端口模式配置-------复用模式
//输出类型
//输出速度
//上下拉
//复用功能寄存器
/*定时器控制器配置*/
//使能定时器时钟
//CR1
//SMCR 时钟源选择
//CCMRx 比较模式寄存器:配置通道模式
//CCER 比较使能寄存器:使能通道
//PSC
//ARR
//CCRx
//EGD
//使能定时器
}
2.4 输入捕获部分
输入捕获:监控外部输入信号的脉冲宽度,一个脉冲的有效时间
捕获一个脉冲的原理:
外部管脚输入进来脉冲信号,
经过滤波器(滤掉高频杂波),
经过边沿检测器,
如果边沿检测器检测到需要捕获的边沿,就会将此时计数器的值加载到捕获寄存器同时可以触发捕获中断
捕获过程:
通道设置为输入----管脚
信号进入需要滤波(去除加杂在有效波形中的高频杂波)
信号输入进来后检测到一个边沿后将计数器中的值加载到捕获寄存器中
切换另外一个边沿
第二个边沿触发后,再次捕获计数器的计数,两次的差就是脉冲的宽度。
注意:
此时可能出现计数器计数完成,脉冲还没到另外一个边沿。如按键按下不抬起
记录溢出次数。(通过更新中断就可以记录溢出次数)
脉冲宽度计数 = 65535*n - test1 + test2
n溢出的次数(更新中断的触发次数)
捕获过程中需要哪些中断?
更新中断//记录溢出的次数
捕获中断//记录捕获到计数器的值(捕获寄存器)到变量
输入滤波
也就是采样次数为N次才是一次有效边沿;
边沿检测
信号选择
CC:Capture/Compare
预分频
ICxPS:Input capture x prescaler
程序设计
总体思路
定时器捕获初始化
{
//IO控制器配置
//定时器控制器配置
//NVIC控制器配置
}
中断服务函数
{
//判断是否为更新中断
{
}
//判断是否为捕获中断
{
}
}
详细思路
定时器捕获初始化
{
/*IO控制器配置*/
//端口时钟使能
//端口模式选择----复用
//端口上下拉
//复用功能配置
/*定时器控制器配置*/
//定时器时钟使能
//CR1
//SMCR
//DIER
//CCMRx
//CCER
//PSC
//ARR
//EGD
//人为清除更新中断标志位
/*NVIC控制器配置*/
//设置优先级分组
//计算优先级编码值,设置抢占和响应的级别值
//设置具体某个中断源的优先级
//中断信号响应通道使能
}
中断服务函数
{
//判断是否为更新中断
{
//清除更新标志位
//计数
}
//判断是否为捕获中断
{
//清除捕获中断标志位
//判断是否为上升沿
{
//有效次数
//记录捕获到计数器的值(捕获寄存器)到变量1
//切换为下降沿
}
//判断是否为下升沿
{
//记录捕获到计数器的值(捕获寄存器)到变量2
//计算脉宽
//切换为上升沿----为了下一次计算
}
}
}
三、具体使用通用定时器
1、输出比较部分
需求1:按键控制LED3灯的亮度
KEY1 调亮
KEY2 调暗
分析:
LED3------PC6--------TIM3_CH1
低电平为有效电平
通过按键控制占空比,也就是控制比较寄存器的值
TIM3-----APB1------84M
分频:84分频 1M 1/us 1000/ms
周期:1ms 1000个数
CCR1 0~1000
思路:
定时器3的通道1输出PWM波形的初始化函数
在while 中 通过按键调节比较寄存器的值
限制住比较值,不能数据溢出
定时器3的通道1输出PWM波形的初始化函数:
/***************************************
*函数名 :timer3_ch1_pwm_LED3
*函数功能 :定时器3通道1输出PWM控制LED3亮度
*函数参数 :无
*函数返回值 :无
*函数描述 :
PC6 --- LED3
Tim3_CH1 --- PC6
84MHZ 84分频 1000/ms
****************************************/
void timer3_ch1_pwm_LED3(void)
{
/*IO口控制器配置*/
//端口C使能
RCC->AHB1ENR |= 1 << 2;
//模式选择---复用
GPIOC->MODER &= ~(3 << 12);
GPIOC->MODER |= 2 << 12;
//类型选择---推挽输出
GPIOC->OTYPER &= ~(1 << 6);
//输出速度
GPIOC->OSPEEDR &= ~(3 << 12);
//上下拉
GPIOC->PUPDR &= ~(3 << 12);
//配置复用功能
GPIOC->AFR[0] &= ~(0xf << 24);
GPIOC->AFR[0] |= 2 << 24;
/*定时器控制器配置*/
//使能定时器
RCC->APB1ENR |= 1 << 1;
//CR1
TIM3->CR1 |= 1 << 7;//使能影子寄存器
TIM3->CR1 &= ~(3 << 5);//边沿对齐模式
TIM3->CR1 &= ~(1 << 4);//递增
TIM3->CR1 &= ~(1 << 3);//连续脉冲
TIM3->CR1 &= ~(1 << 1);//允许产生更新事件的条件
//SMCR
TIM3->SMCR &= ~(7 << 0);//时钟源选择
//CCMR1
TIM3->CCMR1 &= ~(7 << 4);
TIM3->CCMR1 |= 6 << 4;//PWM模式
TIM3->CCMR1 |= 1 << 3;//比较影子寄存器使能
TIM3->CCMR1 &= ~(3 << 0);//通道1配置输出
//CCER
TIM3->CCER |= 1 << 1;//低电平有效
TIM3->CCER |= 1 << 0;//通道1使能
//PSC
TIM3->PSC = 84 - 1;
//ARR
TIM3->ARR = 1000 - 1;
//CCR1
TIM3->CCR1 = 500;
//EGD
TIM3->EGR |= 1 << 0;//人为产生更新事件
//使能定时器
TIM3->CR1 |= 1 << 0;
}
while(1)
{
keynum = Key_scan();
if(keynum == 1)
{
TIM3->CCR1 += 100;
if(TIM3->CCR1 >= 1000)
{
TIM3->CCR1 = 100;
}
}
if(keynum == 2)
{
if(TIM3->CCR1 >= 100)
{
TIM3->CCR1 -= 100;
}
}
}
需求:2:LED3的呼吸灯
分析:
利用定时中断来控制比较值,控制LED3的电压从而控制亮度
注意产品品思想,尽量让每个功能之间不互相影响
定时器3的通道1输出PWM波形的初始化函数:同上
//缺点:while(1)走完一圈至少4s,影响到下面功能的执行
// for(TIM3->CCR1 = 1;TIM3->CCR1 <= 1000;TIM3->CCR1++)
// {
// timer11_delay_ms(2);
// if(TIM3->CCR1 >= 1000)
// {
// break;
// }
// }
// for(TIM3->CCR1 = 1000 ;TIM3->CCR1 <= 1000;TIM3->CCR1--)
// {
// if(TIM3->CCR1 != 0)
// {
// TIM3->CCR1 --;
// timer11_delay_ms(2);
// }
// else
// TIM3->CCR1 = 0;
// }
/*
函数名:TIM7_IRQHandler
函数功能:基本定时器中断服务函数
返回值:void
形参:void
函数说明:
*/
u16 Tim7_cnt[5];
u8 led3_flag = 1;
void TIM7_IRQHandler(void)
{
//清除中断标志位
TIM7->SR &= ~(1<<0);
//紧急事件
Tim7_cnt[1]++;
Tim7_cnt[2]++;
Tim7_cnt[3]++;
Tim7_cnt[4]++;
//第一件事
if(Tim7_cnt[1] >= 2)
{
if(led3_flag == 1)
{
TIM3->CCR1 ++;
if(TIM3->CCR1 >= 1000)
{
led3_flag = 0;
}
}
else if(led3_flag == 0)
{
TIM3->CCR1 --;
if(TIM3->CCR1 <= 0)
{
led3_flag = 1;
}
}
Tim7_cnt[1] = 0;
}
}
需求3:按键控制彩灯
按键1:红 按键2:绿 按键3:蓝 按键4:随机颜色
RGB_R-------PA1-------TIM5_CH2
RGB_G-------PA2-------TIM5_CH3
RGB_B-------PA3-------TIM5_CH4
定时器5的通道2、3、4输出PWM波形的初始化函数:
/***************************************
*函数名 : timer5_ch234_pwm_RGB()
*函数功能 :定时器5通道234输出PWM控制彩灯
*函数参数 :无
*函数返回值 :无
*函数描述 :
R--------PA1-------TIM5_CH2
G--------PA2-------TIM5_CH3
B--------PA3-------TIM5_CH4
84MHZ 84分频 1000/ms
****************************************/
void timer5_ch234_pwm_RGB(void)
{
/*IO口控制器配置*/
//IO口时钟使能
RCC->AHB1ENR |= (1<<0);
//端口模式配置
GPIOA->MODER &= ~((3<<2)|(3<<4)|(3<<6));
GPIOA->MODER |= ((2<<2)|(2<<4)|(2<<6)); //复用功能
//输出类型配置
GPIOA->OTYPER &= ~((1<<1)|(1<<2)|(1<<3));
//输出速度配置
GPIOA->OSPEEDR &= ~((3<<2)|(3<<4)|(3<<6));
//上下拉配置
GPIOA->PUPDR &= ~((3<<2)|(3<<4)|(3<<6));
//复用关系配置 AF2
GPIOA->AFR[0] &= ~((0xf<<4)|(0xf<<8)|(0xf<<12));
GPIOA->AFR[0] |= ((2<<4)|(2<<8)|(2<<12));
/*定时器控制器配置*/
RCC->APB1ENR |= (1<<3);
//CR1
TIM5->CR1 |= (1<<7); //重载影子寄存器
TIM5->CR1 &= ~(3<<5); //边沿对齐模式
TIM5->CR1 &= ~(1<<4); //递增计数方式
TIM5->CR1 &= ~(1<<3); //循环计数模式
TIM5->CR1 &= ~(1<<1); //使能更新事件
//SMCR
TIM5->SMCR &= ~(7<<0); //选择内部时钟源
//CCMRx
//OC2
TIM5->CCMR1 &= ~(3<<8); //配置2为输出
TIM5->CCMR1 |= (1<<11); //比较寄存器的影子寄存器
TIM5->CCMR1 &= ~(7<<12);
TIM5->CCMR1 |= (6<<12); //PWM1模式
//OC3
TIM5->CCMR2 &= ~(3<<0); //配置3为输出
TIM5->CCMR2 |= (1<<3); //比较寄存器的影子寄存器
TIM5->CCMR2 &= ~(7<<4);
TIM5->CCMR2 |= (6<<4); //PWM1模式
//OC4
TIM5->CCMR2 &= ~(3<<8); //配置4为输出
TIM5->CCMR2 |= (1<<11); //比较寄存器的影子寄存器
TIM5->CCMR2 &= ~(7<<12);
TIM5->CCMR2 |= (6<<12); //PWM1模式
//CCER
//OC2
TIM5->CCER |= (1<<4); //通道2输出使能
TIM5->CCER |= (1<<5); //低电平有效电平
//OC3
TIM5->CCER |= (1<<8); //通道3输出使能
TIM5->CCER |= (1<<9); //低电平有效电平
//OC4
TIM5->CCER |= (1<<12); //通道4输出使能
TIM5->CCER |= (1<<13); //低电平有效电平
//PSC
TIM5->PSC = 84 - 1;
//ARR
TIM5->ARR = 1000 - 1;
//CCxR
TIM5->CCR2 = 0;
TIM5->CCR3 = 0;
TIM5->CCR4 = 0;
//EGR
TIM5->EGR |= (1<<0); //手动产生更新事件
//开启定时器
TIM5->CR1 |= (1<<0);
}
定时中断控制任意灯:
u16 Tim7_cnt[5];
u8 led3_flag = 1;
u8 RGB_flag = 0;
void TIM7_IRQHandler(void)
{
//清除中断标志位
TIM7->SR &= ~(1<<0);
//紧急事件
Tim7_cnt[1]++;
Tim7_cnt[2]++;
Tim7_cnt[3]++;
Tim7_cnt[4]++;
//第一件事
if(Tim7_cnt[1] >= 2)
{
if(led3_flag == 1)
{
TIM3->CCR1 ++;
if(TIM3->CCR1 >= 1000)
{
led3_flag = 0;
}
}
else if(led3_flag == 0)
{
TIM3->CCR1 --;
if(TIM3->CCR1 <= 0)
{
led3_flag = 1;
}
}
Tim7_cnt[1] = 0;
}
//第二件事 RGB彩灯
if(Tim7_cnt[2] >= 300)
{
if(RGB_flag == 1)
{
RGB_R += 25;
if(RGB_R >= 1000)
{
RGB_R = 0;
}
RGB_G += 99;
if(RGB_G >= 1000)
{
RGB_G = 0;
}
RGB_B += 155;
if(RGB_B >= 1000)
{
RGB_B = 0;
}
}
Tim7_cnt[2] = 0;
}
}
主函数按键控制:
while(1)
{
keynum = Key_scan();
switch(keynum)
{
case 1:RGB_flag = 0;RGB_R = 255;RGB_G = 0; RGB_B = 0; break;
case 2:RGB_flag = 0;RGB_R = 0; RGB_G = 255;RGB_B = 0; break;
case 3:RGB_flag = 0;RGB_R = 0; RGB_G = 0; RGB_B = 255;break;
case 4:RGB_flag = 1;break;
}
}
需求4:电机控制
按键1:开机 按键2:关机 按键3:加速 按键4:减速
PA6-----TIM13_CH1
定时器13通道1输出PWM控制电机初始化配置:
/***************************************
*函数名 : timer13_ch1_pwm_motor()
*函数功能 :定时器13通道1输出PWM控制电机
*函数参数 :无
*函数返回值 :无
*函数描述 :
PA6-----TIM13_CH1
84MHZ 84分频 1000/ms
****************************************/
void timer13_ch1_pwm_motor(void)
{
/*IO口控制器配置*/
//IO口时钟使能
RCC->AHB1ENR |= (1<<0);
//端口模式配置
GPIOA->MODER &= ~(3 << 12);
GPIOA->MODER |= 2 << 12;
//输出类型配置
GPIOA->OTYPER &= ~(1 << 6);
//输出速度配置
GPIOA->OSPEEDR &= ~(3 << 12);
//上下拉配置
GPIOA->OSPEEDR &= ~(3 << 12);
//复用关系配置 AF9 1001
GPIOA->AFR[0] &= ~(0xf << 24);
GPIOA->AFR[0] |= 9 << 24;
/*定时器控制器配置*/
//使能定时器
RCC->APB1ENR |= 1 << 7;
//CR1
TIM13->CR1 |= 1 << 7;//使能重载影子寄存器
TIM13->CR1 &= ~(1 << 1);//允许产生更新事件的条件
//CCMR1
TIM13->CCMR1 &= ~(7 << 4);
TIM13->CCMR1 |= 6 << 4;//PWM模式
TIM13->CCMR1 |= 1 << 3;//比较影子寄存器使能
TIM13->CCMR1 &= ~(3 << 0);//通道1配置输出
//CCER
TIM13->CCER &= ~(1 << 1);//高电平有效
TIM13->CCER |= 1 << 0;//通道1使能
//PSC
TIM13->PSC = 84 - 1;
//ARR
TIM13->ARR = 1000 - 1;
//CCR1
TIM13->CCR1 = 0;
//EGD
TIM13->EGR |= 1 << 0;//人为产生更新事件
//使能定时器
TIM13->CR1 |= 1 << 0;
}
主函数按键控制电机:
while(1)
{
keynum = Key_scan();
if(keynum == 1)//开机
{
MOTOR = motor_num;
}
if(keynum == 2 && MOTOR)//关机
{
motor_num = MOTOR;
MOTOR = 0;
}
if(keynum == 3 && MOTOR)//加速
{
MOTOR += 100;
motor_num = MOTOR;
if(MOTOR >= 1000)
{
MOTOR = 1000;
}
}
if(keynum == 4 && MOTOR)//减速
{
MOTOR -= 100;
motor_num = MOTOR;
if(MOTOR <= 100)
MOTOR = 100;
}
}
优化:
//1、关机后,再次开机,转速还是关机前的转速--》在关机前记录一下转速
//2、关机后调速失效--》标志位
//3、调速后防止按开机键又重新赋值--》在调速的时候记录一下转速
需求5:舵机控制
舵机控制原理:
舵机的控制一般需要一个 20ms 左右的时基脉冲, 该脉冲的高电平部分一般为 0.5ms~2.5ms 范围内的角度控制脉冲部分。 以 180 度角度伺服为例, 那么对应的控制关系是这样的:
0.5ms------------0 度;
1.0ms------------45 度;
1.5ms------------90 度;
2.0ms-----------135 度;
2.5ms-----------180 度;
具体描述如下图:
需求:
定时器14通道1输出PWM控制舵机初始化配置:
/***************************************
*函数名 : timer14_ch1_pwm_steering_engine
*函数功能 :定时器14通道1输出PWM控制舵机
*函数参数 :无
*函数返回值 :无
*函数描述 :
PA7-------TIM14_CH1
84MHZ 84分频 1000/ms
****************************************/
void timer14_ch1_pwm_steering_engine(void)
{
/*IO口控制器配置*/
//IO口时钟使能
RCC->AHB1ENR |= (1<<0);
//端口模式配置
GPIOA->MODER &= ~(3 << 14);
GPIOA->MODER |= 2 << 14;
//输出类型配置
GPIOA->OTYPER &= ~(1 << 7);
//输出速度配置
GPIOA->OSPEEDR &= ~(3 << 14);
//上下拉配置
GPIOA->OSPEEDR &= ~(3 << 14);
//复用关系配置 AF9 1001
GPIOA->AFR[0] &= ~(0xf << 28);
GPIOA->AFR[0] |= 9 << 28;
/*定时器控制器配置*/
//使能定时器
RCC->APB1ENR |= 1 << 8;
//CR1
TIM14->CR1 |= 1 << 7;//使能重载影子寄存器
TIM14->CR1 &= ~(1 << 1);//允许产生更新事件的条件
//CCMR1
TIM14->CCMR1 &= ~(7 << 4);
TIM14->CCMR1 |= 6 << 4;//PWM模式
TIM14->CCMR1 |= 1 << 3;//比较影子寄存器使能
TIM14->CCMR1 &= ~(3 << 0);//通道1配置输出
//CCER
TIM14->CCER &= ~(1 << 1);//高电平有效
TIM14->CCER |= 1 << 0;//通道1使能
//PSC
TIM14->PSC = 84 - 1;
//ARR
TIM14->ARR = 20000 - 1;
//CCR1
TIM14->CCR1 = 500;
//EGD
TIM14->EGR |= 1 << 0;//人为产生更新事件
//使能定时器
TIM14->CR1 |= 1 << 0;
}
主函数按键控制舵机:
while(1)
{
keynum = Key_scan();
if(keynum == 1)//0°
{
STEER = steer_val;
}
if(keynum == 2)//180°
{
STEER = 2500;
}
if(keynum == 3 )//逆时针45°
{
STEER -= steer_val;
if(STEER <= 500)
{
STEER = steer_val;
}
}
if(keynum == 4)//顺时针45°
{
STEER += steer_val;
if(STEER >= 2500)
{
STEER = 2500;
}
}
}
2、输入捕获部分
需求1:检测按键按下的时间
分析:
KEY1-------PA0--------TIM5_CH1 输入捕获
先上升沿捕获,再切换下降沿
PSC: 84
ARR:65535
脉冲宽度计数 = 65535*n - test1 + test2
定时器5通道1捕获按键按下时间初始化配置:
/***************************************
*函数名 :timer5_ch1_in_key1
*函数功能 :定时器5通道1捕获按键按下时间
*函数参数 :无
*函数返回值 :无
*函数描述 :PA0----KEY1
TIM5——CH1
84M ---- 84分频 1/us
周期65535
****************************************/
void timer5_ch1_in_key1(void)
{
/*IO口控制器配置*/
//端口时钟使能
RCC->AHB1ENR |= (1<<0);
//端口模式配置------复用
GPIOA->MODER &= ~(3<<0);
GPIOA->MODER |= (2<<0);
//端口上下拉
GPIOA->PUPDR &= ~(3<<0);
//复用功能配置
GPIOA->AFR[0] &= ~(0xf<<0);
GPIOA->AFR[0] |= (2<<0);
/*定时器控制器配置*/
//定时器时钟使能
RCC->APB1ENR |= (1<<3);
//CR1
TIM5->CR1 &= ~(3<<8); //滤波所用的频率不分频
TIM5->CR1 |= (1<<7); //影子寄存器
TIM5->CR1 &= ~(3<<5); //边沿对齐模式
TIM5->CR1 &= ~(1<<4); //递增计数
TIM5->CR1 &= ~(1<<3); //循环计数
TIM5->CR1 &= ~(1<<2); //允许产生中断
TIM5->CR1 &= ~(1<<1); //允许产生更新事件
//SMCR
TIM5->SMCR &= ~(7<<0); //内部时钟
//DIER
TIM5->DIER |= (1<<1); //通道1捕获中断使能
TIM5->DIER |= (1<<0); //更新中断使能
//CCMRx
TIM5->CCMR1 &= ~(3<<0);
TIM5->CCMR1 |= (1<<0); //通道1捕获TI1信号
TIM5->CCMR1 &= ~(3<<2); //通道1无分频
TIM5->CCMR1 &= ~(0xf<<4);
TIM5->CCMR1 |= (0xf<<4); //通道1滤波采样
//CCER
TIM5->CCER |= (1<<0); //使能通道1捕获
TIM5->CCER &= ~(1<<3);
TIM5->CCER &= ~(1<<1); //上升沿捕获
//PSC
TIM5->PSC = 84-1;
//ARR
TIM5->ARR = 65535;
//EGR
TIM5->EGR |= (1<<0);
//人为更新清除标志位
TIM5->SR &= ~(1<<0);
/*NVIC控制器配置*/
//优先级分组 -----在主函数
//计算优先级编码值
u32 pri = NVIC_EncodePriority (5, 0,1);
//设置具体中断源
NVIC_SetPriority(TIM5_IRQn,pri);
//使能NVIC响应通道
NVIC_EnableIRQ(TIM5_IRQn);
//使能计数器
TIM5->CR1 |= (1<<0);
}
中断服务函数:
/*
函数名:TIM5_IRQHandler
函数功能:通用定时器中断服务函数
返回值:void
形参:void
函数说明:
*/
void TIM5_IRQHandler(void)
{
static u8 TIM5_cnt = 0;
static u16 temp1;
u16 temp2;
u32 temp;
//判断是否为更新中断
if(TIM5->SR & (1 << 0))
{
//清除更新中断标志位
TIM5->SR &= ~(1 << 0);
//记录溢出次数
TIM5_cnt ++;
}
//判断是否为捕获中断
if(TIM5->SR & (1 << 1))
{
//清除捕获中断标志位
TIM5->SR &= ~(1 << 1);
//判断是否为上升沿
if(!(TIM5->CCER & (1 << 1)))
{
//有效次数
TIM5_cnt = 0;
//记录捕获寄存器的值到变量
temp1 = TIM5->CCR1 ;
//切换为下降沿
TIM5->CCER |= (1 << 1);
}
//判断是否为下降沿
else if((TIM5->CCER & (1 << 1)))
{
//记录捕获寄存器的值到变量
temp2 = TIM5->CCR1 ;
//计算脉宽
temp = TIM5_cnt * 65535 - temp1 + temp2;
printf("temp:%.2fms\r\n",temp/1000.0);
//切换为上升沿
TIM5->CCER &= ~(1 << 1);
}
}
}
注意:
由于判断完是否为上升沿后切换成下降沿,所以不能又立即判断是否为下降沿
需求2:超声波测距
Echo-------PB6--------TIM4_CH1
Trig---------PB8
每隔100ms自动测距
小于30cm蜂鸣器报警
超声波测距原理:
MCU输出至少10us的高电平信号通过TRIG给到超声波模块,模块自动发送8个40khz的方波,自动检测是否有信号返回;有信号返回,通过IO口ECH0输出一个高电平,然后定时器输入捕获,获取到高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;
定时器4通道1捕获超声波测距初始化配置:
/***************************************
*函数名 :timer4_ch1_HC-SR04
*函数功能 :定时器4通道1捕获超声波测距
*函数参数 :无
*函数返回值 :无
*函数描述 :Echo-------PB6--------TIM4_CH1
84M ---- 84分频 1/us
周期65535
****************************************/
void timer4_ch1_HC_SR04(void)
{
/*IO口控制器配置*/
//端口时钟使能
RCC->AHB1ENR |= (1 << 1);
//端口模式选择---复用
GPIOB->MODER &= ~(3 << 12);
GPIOB->MODER |= (2 << 12);
//端口上下拉
GPIOB->PUPDR &= ~(3 << 12);
//复用功能 AF2
GPIOB->AFR[0] &= ~(0xf << 24);
GPIOB->AFR[0] |= (2 << 24);
/*定时器控制器配置*/
RCC->APB1ENR |= (1 << 2);
//CR1
TIM4->CR1 &= ~(3 << 8); //滤波所用的频率不分频
TIM4->CR1 |= (1 << 7); //影子寄存器
TIM4->CR1 &= ~(3 << 5); //边沿对齐模式
TIM4->CR1 &= ~(1 << 4); //递增计数
TIM4->CR1 &= ~(1 << 3); //循环计数
TIM4->CR1 &= ~(1 << 2); //允许产生中断
TIM4->CR1 &= ~(1 << 1); //允许产生更新事件
//SMCR
TIM4->SMCR &= ~(7 << 0); //内部时钟
//DIER
TIM4->DIER |= (1<<1); //通道1捕获中断使能
TIM4->DIER |= (1<<0); //更新中断使能
//CCMRx
TIM4->CCMR1 &= ~(3<<0);
TIM4->CCMR1 |= (1<<0); //通道1捕获TI1信号
TIM4->CCMR1 &= ~(3<<2); //通道1无分频
TIM4->CCMR1 &= ~(0xf<<4);
TIM4->CCMR1 |= (0xf<<4); //通道1滤波采样
//CCER
TIM4->CCER |= (1<<0); //使能通道1捕获
TIM4->CCER &= ~(1<<3);
TIM4->CCER &= ~(1<<1); //上升沿捕获
//PSC
TIM4->PSC = 84-1;
//ARR
TIM4->ARR = 65535;
//EGR
TIM4->EGR |= (1<<0);
//人为更新清除标志位
TIM4->SR &= ~(1<<0);
/*NVIC控制器配置*/
//优先级分组 -----在主函数
//计算优先级编码值
u32 pri = NVIC_EncodePriority (5, 2,1);
//设置具体中断源
NVIC_SetPriority(TIM4_IRQn,pri);
//使能NVIC响应通道
NVIC_EnableIRQ(TIM4_IRQn);
//使能计数器
TIM4->CR1 |= (1<<0);
}
触发超声波开始测距:
/*
函数名:HC_RS04_Start
函数功能:触发超声波开始测距
返回值:void
形参:void
函数说明:
TRIG检测到至少有10us的高电平就会触发模块自动发送8个40KHZ的方波
*/
void HC_RS04_Start(void)
{
GPIOB->ODR |= (1 << 8);
timer6_delay_us(10);
GPIOB->ODR &= ~(1 << 8);
}
中断服务函数:
/*
函数名:TIM4_IRQHandler
函数功能:通用定时器中断服务函数
返回值:void
形参:void
函数说明:
*/
void TIM4_IRQHandler(void)
{
static u8 TIM4_cnt = 0;
static u16 temp1;
u16 temp2;
u32 temp;
float HC_RS04;
//判断是否为更新中断
if(TIM4->SR & (1 << 0))
{
//清除更新中断标志位
TIM4->SR &= ~(1 << 0);
//记录溢出次数
TIM4_cnt ++;
}
//判断是否为捕获中断
if(TIM4->SR & (1 << 1))
{
//清除捕获中断标志位
TIM4->SR &= ~(1 << 1);
//判断是否为上升沿
if(!(TIM4->CCER & (1 << 1)))
{
//有效次数
TIM4_cnt = 0;
//记录捕获寄存器的值到变量
temp1 = TIM4->CCR1 ;
//切换为下降沿
TIM4->CCER |= (1 << 1);
}
//判断是否为下降沿
else if((TIM4->CCER & (1 << 1)))
{
//记录捕获寄存器的值到变量
temp2 = TIM4->CCR1 ;
//计算脉宽
temp = TIM4_cnt * 65535 - temp1 + temp2;
HC_RS04 = (temp / 1000.0) * 34 / 2;
printf("检测的距离:%.2fcm\r\n",HC_RS04);
if(HC_RS04 < 30)
{
ALL_LED_ON();
}
else
{
ALL_LED_OFF();
}
//切换为上升沿
TIM4->CCER &= ~(1 << 1);
}
}
}