找回密码
立即注册
搜索
热搜: 活动 交友 discuz
发新帖

116

积分

0

好友

12

主题
发表于 2023-9-13 12:23:01 | 查看: 209| 回复: 0 IP:广东省东莞市 电信

本帖最后由 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]定时器在嵌入式系统中有多种作用

  1. 计时和测量:定时器可以用于计时和测量特定的时间间隔。通过设置定时器的计数器和预设值,可以测量准确的时间间隔,用于实现精确的时间控制和时间基准。
  2. 生成脉冲和周期信号:定时器可以用于生成周期性的脉冲和信号。通过设置定时器的计数器和预设值,可以生成特定频率和占空比的方波、脉冲信号等,用于控制外部设备或执行特定操作。
  3. 定时中断:定时器可以用于生成定时中断。通过设置定时器的计数器和预设值,并配置中断使能,可以在特定时间间隔内生成中断请求,用于实现周期性的任务调度和时间触发的事件处理。
  4. 脉冲宽度调制(PWM):定时器可以用于实现脉冲宽度调制技术(PWM)。通过设置定时器的计数器和预设值,并配合输出比较功能,可以生成具有可调节占空比的脉冲信号。PWM常用于电机控制、LED亮度控制、音频合成等应用。
  5. 超时检测和超时保护:定时器可以用于超时检测和超时保护。通过设置定时器的计数器和预设值,并监测定时器溢出或比较事件,可以实现对特定操作的超时检测和保护,防止系统长时间无响应或操作超时。

CH32V307单片机有通用定时器模块包含一个16位可自动重装的定时器(TIM2、TIM3、TIM4 和 TIM5),用于测量脉冲宽度或者产生特定频率的脉冲、PWM 波等。可用于自动化控制、电源等领域。高级定时器模块包含一个功能强大的16位自动重装定时器(TIM1、TIM8、TIM9 和 TIM10),用于测量脉冲宽度或者产生特定频率的脉冲、PWM 波等。基本定时器模块包含一个16位可自动重装的定时器(TIM6 和 TIM7),用于计数和在更新新事件产生中断或 DMA 请求。

微信图片_20230913000300.png

如图 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手册和代码工程已经上传

微信图片_20230913121142.png

微信图片_20230913121225.jpg

微信图片_20230913121225.jpg

上面的图片是我测试出来的数据,用尺子量了一下距离,几乎没有误差,超声波采集回来显示在OLED屏和串口上的数据是正确的21CM。

超声波.rar

20.23 MB, 下载次数: 1

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|国产电子社区 ( 沪ICP备2023018578号-1|

苏公网安备 32011102010465号


)|网站地图

GMT+8, 2024-4-19 20:33 , Processed in 0.061693 second(s), 23 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表