本帖最后由 admin3 于 2023-9-13 12:23 编辑
本帖最后由 admin3 于 2023-9-13 12:22 编辑
本帖最后由 admin3 于 2023-9-13 12:16 编辑
本帖最后由 admin3 于 2023-9-13 11:58 编辑
[md]定时器在嵌入式系统中有多种作用
- 计时和测量:定时器可以用于计时和测量特定的时间间隔。通过设置定时器的计数器和预设值,可以测量准确的时间间隔,用于实现精确的时间控制和时间基准。
- 生成脉冲和周期信号:定时器可以用于生成周期性的脉冲和信号。通过设置定时器的计数器和预设值,可以生成特定频率和占空比的方波、脉冲信号等,用于控制外部设备或执行特定操作。
- 定时中断:定时器可以用于生成定时中断。通过设置定时器的计数器和预设值,并配置中断使能,可以在特定时间间隔内生成中断请求,用于实现周期性的任务调度和时间触发的事件处理。
- 脉冲宽度调制(PWM):定时器可以用于实现脉冲宽度调制技术(PWM)。通过设置定时器的计数器和预设值,并配合输出比较功能,可以生成具有可调节占空比的脉冲信号。PWM常用于电机控制、LED亮度控制、音频合成等应用。
- 超时检测和超时保护:定时器可以用于超时检测和超时保护。通过设置定时器的计数器和预设值,并监测定时器溢出或比较事件,可以实现对特定操作的超时检测和保护,防止系统长时间无响应或操作超时。
CH32V307单片机有通用定时器模块包含一个16位可自动重装的定时器(TIM2、TIM3、TIM4 和 TIM5),用于测量脉冲宽度或者产生特定频率的脉冲、PWM 波等。可用于自动化控制、电源等领域。高级定时器模块包含一个功能强大的16位自动重装定时器(TIM1、TIM8、TIM9 和 TIM10),用于测量脉冲宽度或者产生特定频率的脉冲、PWM 波等。基本定时器模块包含一个16位可自动重装的定时器(TIM6 和 TIM7),用于计数和在更新新事件产生中断或 DMA 请求。
如图 15-1 所示,通用定时器的结构大致可以分为三部分,即输入时钟部分,核心计数器部分和比较捕获通道部分。通用定时器的时钟可以来自于AHB总线时钟(CK_INT),可以来自外部时钟输入引脚(TIMx_ETR),可以来自于其他具有时钟输出功能的定时器(ITRx),还可以来自于比较捕获通道的输入端(TIMx_CHx)。这些输入的时钟信号经过各种设定的滤波分频等操作后成为 CK_PSC 时钟,输出给核心计数器部分。另外,这些复杂的时钟来源还可以作为 TRGO 输出给其他的定时器、ADC 和 DAC 等外设。通用定时器的核心是一个 16 位计数器(CNT)。CK_PSC 经过预分频器(PSC)分频后,成为CK_CNT再最终输给CNT,CNT 支持增计数模式、减计数模式和增减计数模式,并有一个自动重装值寄存器(ATRLR)在每个计数周期结束后为 CNT 重装载初始化值。通用定时器拥有四组比较捕获通道,每组比较捕获通道都可以从专属的引脚上输入脉冲,也可以向引脚输出波形,即比较捕获通道支持输入和输出模式。比较捕获寄存器每个通道的输入都支持滤波、分频、边沿检测等操作,并支持通道间的互触发,还能为核心计数器 CNT 提供时钟。每个比较捕获通道都拥有一组比较捕获寄存器(CHxCVR),支持与主计数器(CNT)进行比较而输出脉冲。
这个例程利用了TIM2作为定时计数控制超声波测距功能,下面代码是超声波测距的主要代码
超声波引脚定义代码
#include "gpio.h"
#include "debug.h"
void ultrasonic_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);
}
void Start_Trig(void)
{
Trig_H;
Delay_Us(50);
Trig_L;
}
#ifndef USER_GPIO_H_
#define USER_GPIO_H_
#define Trig_H GPIO_SetBits(GPIOA,GPIO_Pin_0)
#define Trig_L GPIO_ResetBits(GPIOA,GPIO_Pin_0)
#define Echo_H GPIO_SetBits(GPIOA,GPIO_Pin_1)
#define Echo_L GPIO_ResetBits(GPIOA,GPIO_Pin_1)
void ultrasonic_GPIO_Init(void);
void Start_Trig(void);
#endif /* USER_GPIO_H_ */
#include "timer.h"
#include "gpio.h"
#include "debug.h"
void TIM2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
u16 count = 0;//计数
/*******************************************************************************
* Function Name : Input_Capture_Init
* Input : arr: the period value.
* psc: the prescaler value.
* Return : None
*******************************************************************************/
void Input_Capture_Init( u16 arr, u16 psc )
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//使能IM2时钟
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE);
ultrasonic_GPIO_Init(); //配置Trig和Echo两个引脚
TIM_DeInit(TIM2);
//定时器周期,实际就是设定自动重载寄存器 ARR 的值, ARR 为要装载到实际自动重载寄存器(即影子寄存器) 的值, 可设置范围为 0 至 65535。
TIM_TimeBaseInitStructure.TIM_Period = arr;
//定时器预分频器设置,时钟源经该预分频器才是定时器计数时钟CK_CNT,它设定 PSC 寄存器的值。
//计算公式为: 计数器时钟频率 (fCK_CNT) 等于fCK_PSC / (PSC[15:0] + 1),可实现 1 至 65536 分频。
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
//时钟分频,设置定时器时钟 CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择 1、 2、 4 分频。
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数模式,向上计数模式
//TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00; //设置重复计数器的值,0。重复计数器,只有 8 位,只存在于高级定时器。
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure); //初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM1捕获比较中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //设置响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能通道
NVIC_Init(&NVIC_InitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE ); //使能TIM2更新中断
TIM_Cmd( TIM2, DISABLE ); //定时器使能
}
void ENABLE_TIM(void)
{
//while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==RESET)
{
TIM_SetCounter(TIM2,0);
count = 0;
TIM_Cmd(TIM2,ENABLE);//回响信号到来,开启定时器计数
}
}
void DISABLE_TIM(void)
{
//while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==SET)
{
TIM_Cmd(TIM2,DISABLE);//回响信号到来,开启定时器计数
}
}
u32 GetCount(void)
{
u32 t = 0;
t = count*1000;
t += TIM_GetCounter(TIM2);
TIM_SetCounter(TIM2,0);
Delay_Ms(10);
return t;
}
//一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
//为了消除余震的影响,取五次数据的平均值进行加权滤波。
float Ultrasoniclength(void )
{
u32 t = 0;
int i = 0;
float length = 0 , sum = 0;
while(i!=5)
{
Start_Trig();
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==RESET); //此处一直等,等到为1,进行下一步
ENABLE_TIM();//回响信号到来,开启定时器计数
i = i + 1;
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==SET); //此处一直等,等到为0,进行下一步,这两段while之间的时间就是高电平时间,即发出到返回接收的时间
DISABLE_TIM();//回响信号到来,开启定时器计数
t = TIM_GetCounter(TIM2);
length=(t+count*1000)/75.0;//通过回响信号计算距离
sum = length + sum ;
//printf("sum is %.2f,length is %.2f,t is %d,count is %d\r\n",sum,length,t,count);
TIM_SetCounter(TIM2,0);
count = 0;
Delay_Ms(100);
}
length = sum/5.0;
return length;
}
/*******************************************************************************
* Function Name : TIM2_IRQHandler
*******************************************************************************/
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除中断标志
count++;
}
}
#ifndef USER_TIMER_H_
#define USER_TIMER_H_
#include "ch32v30x_conf.h"
void Input_Capture_Init( u16 arr, u16 psc );
void ENABLE_TIM(void);
void DISABLE_TIM(void);
u32 GetCount(void);
float Ultrasoniclength(void);
#endif /* USER_TIMER_H_ */
#include "debug.h"
#include "gpio.h"
#include "timer.h"
#include "IIC.h"
#include "ssd1306.h"
/*******************************************************************************
* Function Name : main
* Description : Main program.
* Input : None
* Return : None
*******************************************************************************/
u32 distance = 0;
void chaoshenbo(void)//超声波显示函数
{
OLED_ShowNum(1,6,distance/10000,1);//
OLED_ShowNum(8,6,distance%10000/1000,1);//
OLED_ShowNum(16,6,distance%1000/100,1);//
OLED_ShowNum(24,6,distance%100/10,1);//
OLED_ShowNum(32,6,distance%10,1);//
oled_show_char(40,6,'c',16);//
oled_show_char(48,6,'m',16);//
}
int main(void)
{
Delay_Init();
USART_Printf_Init(115200);//
Input_Capture_Init(1000-1,72-1);//这里是2KHz的频率
oled_init();//OLED初始化
oled_clear();//清屏
OLED_ShowStr(2,0,"CH32V307!",24);
OLED_ShowStr(28,3,"ELECFANS",16);
while(1)
{
distance = Ultrasoniclength(); //获取距离
printf("DISTANCE:%dcm\r\n",distance);//打印距离值
chaoshenbo();//超声波显示函数
Delay_Ms(1000);
}
}
以上代码就是超声波测距的代码,我用的超声波模块是SRF05,测试出来的数据可以显示在OLED屏和串口上,SRF05手册和代码工程已经上传
上面的图片是我测试出来的数据,用尺子量了一下距离,几乎没有误差,超声波采集回来显示在OLED屏和串口上的数据是正确的21CM。