搜索
bottom↓
回复: 0
打印 上一主题 下一主题

《ATK-DFPGL22G之FPGA开发指南_V1.0》 第十九章

[复制链接]

出0入234汤圆

跳转到指定楼层
1
发表于 2023-6-20 10:14:50 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
1)实验平台:正点原子 DFZU2EG_4EV MPSoC开发板
2)购买链接:https://item.taobao.com/item.htm?&id=692368045899
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-340252-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子FPGA交流群:994244016




第十九章 通用定时器输入捕获实验


本章将介绍使用APM32E103通用定时器的输入捕获功能。通过本章的学习,读者将学习到通用定时器输入捕获的使用。
本章分为如下几个小节:
19.1 硬件设计
19.2 程序设计
19.3 下载验证


19.1 硬件设计
19.1.1 例程功能

1. 捕获KEY_UP按键按下拉高PA0的脉冲时间,并通过串口输出
2. LED0闪烁,指示程序正在运行
19.1.2 硬件资源
1. LED
        LED0 - PB5
2. 按键
        KEY_UP - PA0
3. 定时器5
        通道1 - PA0
4. USART1(PA9、PA10连接至板载USB转串口芯片上)
19.1.3 原理图
本章实验使用的定时器5为APM32E103的片上资源,因此没有对应的连接原理图。
19.2 程序设计
19.2.1 Geehy标准库的TMR驱动

本章实验将使用TMR5的通道1捕获KEY_UP按键被按下的高电平脉冲宽度,因此除了像第十七章实验配置定时器的基本参数外,还需要配置通用定时器的输入捕获通道,具体的步骤如下:
①:配置TMR5的自动重装载值和预分频器数值等参数
②:配置输入捕获通道1
③:使能TMR5的捕获比较通道1中断和更新中断
④:使能TMR5中断,并配置其相关的中断优先级
⑤:使能TMR5
在Geehy标准库中对应的驱动函数如下:
①:配置TMR
请见第16.2.1小节中配置TMR的相关内容。
②:配置输入捕获通道
该函数用于配置TMR的任意输入捕获通道,其函数原型如下所示:
void TMR_ConfigIC(TMR_T* tmr, TMR_ICConfig_T* ICConfig);
该函数的形参描述,如下表所示:
表19.2.1.1 函数TMR_ConfigIC()形参描述

该函数的返回值描述,如下表所示:
表19.2.1.2 函数TMR_ConfigIC()返回值描述

该函数使用TMR_ICConfig_T类型的结构体变量传入TMR输入捕获通道的配置参数,该结构体的定义如下所示:
typedef enum
{
    TMR_CHANNEL_1                                        = 0x0000,        /* 定时器通道1*/
    TMR_CHANNEL_2                                        = 0x0004,        /* 定时器通道2*/
    TMR_CHANNEL_3                                        = 0x0008,        /* 定时器通道3*/
    TMR_CHANNEL_4                                        = 0x000C        /* 定时器通道4*/
} TMR_CHANNEL_T;

typedef enum
{
    TMR_IC_POLARITY_RISING                        = 0x00,                /* 捕获上升沿 */
    TMR_IC_POLARITY_FALLING                        = 0x02,                /* 捕获下降沿 */
    TMR_IC_POLARITY_BOTHEDGE                = 0x0A                /* 捕获双边沿 */
} TMR_IC_POLARITY_T;

typedef enum
{
    TMR_IC_SELECTION_DIRECT_TI                = 0x01,                /* 输入捕获映射在TI1上 */
    TMR_IC_SELECTION_INDIRECT_TI        = 0x02,                /* 输入捕获映射在TI2上 */
    TMR_IC_SELECTION_TRC                        = 0x03                /* 输入捕获映射在TRC上 */
} TMR_IC_SELECTION_T;

typedef enum
{
    TMR_IC_PSC_1,                                                                /* 不分频 */
    TMR_IC_PSC_2,                                                                /* 每2个事件触发1次捕获 */
    TMR_IC_PSC_4,                                                                /* 每4个事件触发1次捕获 */
    TMR_IC_PSC_8                                                                /* 每8个事件触发1次捕获 */
} TMR_IC_PSC_T;

typedef struct
{
    TMR_CHANNEL_T                channel;                        /* 定时器通道 */
    TMR_IC_POLARITY_T        polarity;                                /* 输入捕获极性 */
    TMR_IC_SELECTION_T        selection;                                /* 输入捕获映射 */
    TMR_IC_PSC_T                        prescaler;                        /* 输入捕获通道预分频因子 */
    uint16_t                                filter;                                /* 输入捕获通道滤波器 */
} TMR_ICConfig_T;

该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_tmr.h"

void example_fun(void)
{
    TMR_ICConfig_T tmr_ic_init_struct;

    /* 配置TMR5输入捕获通道1 */
    tmr_ic_init_struct.channel                = TMR_CHANNEL_1;
    tmr_ic_init_struct.polarity                = TMR_IC_POLARITY_RISING;
    tmr_ic_init_struct.selection        = TMR_IC_SELECTION_DIRECT_TI;
    tmr_ic_init_struct.prescaler        = TMR_IC_PSC_1;
    tmr_ic_init_struct.filter                = 0;
    TMR_ConfigIC(GTMR_TMRX_CAP, &tmr_ic_init_struct);
}
③:使能TMR指定中断
请见第16.2.1小节中使能TMR指定中断的相关内容。
④:配置TMR中断
请见第12.2.3小节中配置中断的相关内容。
⑤:使能TMR
请见第16.2.1小节中使能TMR的相关内容。
19.2.2 通用定时器驱动
本章实验的通用定时器驱动主要负责向应用层提供通用定时器的初始化函数,并实现通用定时器的中断回调函数。本章实验中,通用定时器驱动的代码包括gtmr.c和gtmr.h两个文件。
通用定时器驱动中,对TMR、GPIO的相关宏定义,如下所示:
/* 通用定时器引脚定义 */
#define GTMR_TMRX_CAP_CHY_GPIO_PORT         GPIOA
#define GTMR_TMRX_CAP_CHY_GPIO_PIN          GPIO_PIN_0
#define GTMR_TMRX_CAP_CHY_GPIO_CLK_ENABLE() do{ RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA); }while(0)

/* 通用定时器定义 */
#define GTMR_TMRX_CAP                       TMR5
#define GTMR_TMRX_CAP_IRQn                  TMR5_IRQn
#define GTMR_TMRX_CAP_IRQHandler            TMR5_IRQHandler
#define GTMR_TMRX_CAP_CHY                   TMR_CHANNEL_1
#define GTMR_TMRX_CAP_CHY_CLK_ENABLE()      do{ RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR5); }while(0)
通用定时器驱动中TMR5的初始化函数,如下所示:
/**
* @brief       初始化通用定时器通道捕获
* @note
*              通用定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
*              通用定时器的时钟为APB1时钟的2倍, 而APB1为60M,所以定时器时钟 = 120Mhz
*              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
*              Ft=定时器工作频率,单位:Mhz
*
* @param       arr: 自动重装值。
* @param       psc: 时钟预分频数。
* @retval      无
*/
void gtmr_tmrx_cap_chy_init(uint16_t arr, uint16_t psc)
{
    GPIO_Config_T gpio_init_struct;
    TMR_BaseConfig_T tmr_init_struct;
    TMR_ICConfig_T tmr_ic_init_struct;
   
    /* 使能时钟 */
    GTMR_TMRX_CAP_CHY_GPIO_CLK_ENABLE();               /* 使能GPIOB时钟 */
    GTMR_TMRX_CAP_CHY_CLK_ENABLE();                    /* 使能通用定时器时钟 */
TMR_ClearIntFlag(GTMR_TMRX_CAP,
TMR_INT_UPDATE | TMR_INT_CC1);    /* 清除中断标志位 */
   
    /* 配置PWM输入引脚 */
    gpio_init_struct.pin = GTMR_TMRX_CAP_CHY_GPIO_PIN; /* 初始化IO口 */
    gpio_init_struct.mode = GPIO_MODE_IN_FLOATING;     /* 浮空输入 */
GPIO_Config(GTMR_TMRX_CAP_CHY_GPIO_PORT,
&gpio_init_struct);                     /* 初始化KEY_UP引脚 */
   
    /* 配置通用定时器 */
    tmr_init_struct.countMode = TMR_COUNTER_MODE_UP;   /* 递增计数模式 */
    tmr_init_struct.clockDivision = TMR_CLOCK_DIV_1;   /* 时钟分频系数 */
    tmr_init_struct.period = arr;                      /* 自动装载值 */
    tmr_init_struct.division = psc;                    /* 设置预分频器 */
    TMR_ConfigTimeBase(GTMR_TMRX_CAP,&tmr_init_struct);/* 初始化通用定时器 */
   
    /* 配置PWM输入捕获 */
tmr_ic_init_struct.channel = TMR_CHANNEL_1;        /* 通道选择CHANNEL_1 */
/* 上升沿捕获 */
tmr_ic_init_struct.polarity = TMR_IC_POLARITY_RISING;
/* 映射到TI1上 */
    tmr_ic_init_struct.selection = TMR_IC_SELECTION_DIRECT_TI;
    tmr_ic_init_struct.prescaler = TMR_IC_PSC_1;       /* 配置输入分频,不分频 */
    tmr_ic_init_struct.filter = 0;                     /* 配置输入滤波器,不滤波 */
    TMR_ConfigIC(GTMR_TMRX_CAP, &tmr_ic_init_struct);  /* 配置通用定时器通道 */
   
    /* 使能通用定时器及其相关中断 */
    NVIC_EnableIRQRequest(GTMR_TMRX_CAP_IRQn, 1, 0);   /* 使能中断*/
    TMR_ConfigInternalClock(GTMR_TMRX_CAP);            /* 配置通用定时器内部时钟 */
    TMR_EnableInterrupt(GTMR_TMRX_CAP, TMR_INT_UPDATE);/* 使能更新中断 */
    TMR_EnableInterrupt(GTMR_TMRX_CAP, TMR_INT_CC1);   /* 使能定时器中断 */
    TMR_EnableCCxChannel(GTMR_TMRX_CAP, TMR_CHANNEL_1);/* 开启通道 */
    TMR_EnableCCPreload(GTMR_TMRX_CAP);                /* 使能捕获比较加载控制器 */
    TMR_Enable(GTMR_TMRX_CAP);                         /* 使能通用定时器 */
}
从TMR5的初始化代码中可以看到,不仅配置了TMR5的自动重装载值和预分频系数等基本参数,还配置了TMR5的输入捕获通道1,并开启了TMR5的输入捕获通道1中断和TMR5更新中断,由于需要使用GPIO引脚来获取外部信号,因此对应的GPIO引脚也配置了复用功能。
通用定时器驱动中TMR5的中断回调函数,如下所示:
/**
* @brief       通用定时器中断服务函数
* @param       无
* @retval      无
*/
void GTMR_TMRX_CAP_IRQHandler(void)
{
    if ((g_tmrxchy_cap_sta & 0x80) == 0)
{
/* 检查通用定时器更新中断是否发生 */
        if (TMR_ReadIntFlag(GTMR_TMRX_CAP, TMR_INT_UPDATE) != RESET)
        {
            /* 清除中断标志位 */
            TMR_ClearIntFlag(GTMR_TMRX_CAP, TMR_INT_UPDATE | TMR_INT_CC1);  
            
            if (g_tmrxchy_cap_sta & 0x40)                 /* 捕获到一个下降沿 */
            {
                if ((g_tmrxchy_cap_sta & 0x3F) == 0x3F)   /* 高电平 */
                {
                    g_tmrxchy_cap_sta |= 0x80;            /* 标记成功捕获一次 */
                    g_tmrxchy_cap_val = 0xFFFF;
                }
                else                                      /* 累计定时器溢出次数 */
                {
                    g_tmrxchy_cap_sta++;
                }
            }
        }
        
        if (TMR_ReadIntFlag(GTMR_TMRX_CAP, TMR_INT_CC1) != 0)/* 发生捕获事件 */
        {
            /* 捕获到一个下降沿 */
            if (g_tmrxchy_cap_sta & 0x40)
            {   /* 标记成功捕获一次高电平脉宽 */
                g_tmrxchy_cap_sta |= 0x80;
                /* 获取当前捕获值 */
                g_tmrxchy_cap_val = TMR_ReadCaputer1(GTMR_TMRX_CAP);
                /* 配置通用定时器通道的上升沿捕获 */
                TMR_ConfigOC1Polarity(GTMR_TMRX_CAP, TMR_OC_POLARITY_HIGH);
            }
            /* 还没开始,第一次捕获上升沿 */
            else
            {
                g_tmrxchy_cap_sta = 0;                       /* 清空状态 */
                g_tmrxchy_cap_val = 0;
                TMR_ConfigCounter(GTMR_TMRX_CAP, 0);
/* 标记捕获到了上升沿 */
                g_tmrxchy_cap_sta |= 0x40;
/* 通用定时器的通道设置为下降沿捕获 */
                TMR_ConfigOC1Polarity(GTMR_TMRX_CAP, TMR_OC_POLARITY_LOW);
            }
        }
    }
}
从上面的代码中可以看出,在TMR5        的中断回调函数中会依次捕获输入信号的上升沿和下降沿,并在第一次捕获到信号上升沿的时候清空TMR5的计数值。随后在捕获到信号下降沿的时候读取TMR5的计数值,该值就是该输入信号高电平脉宽对应的计数值,只要根据TMR5的计数频率,就能够计算出输入信号高电平脉宽的时间。TMR5的更新中断是用于处理计数溢出的。
19.2.3 实验应用代码
本实验的应用代码,如下所示:
int main(void)
{
    uint8_t t = 0;
    uint32_t temp = 0;
   
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);  /* 设置中断优先级分组为组4 */
    sys_apm32_clock_init(15);                         /* 配置系统时钟 */
    delay_init(120);                                  /* 初始化延时功能 */
    usart_init(115200);                               /* 初始化串口 */
    led_init();                                       /* 初始化LED */
    gtmr_tmrx_cap_chy_init(0xFFFF, 60 - 1);           /* 初始化通用定时器通道捕获 */
   
    while (1)
    {
        if (g_tmrxchy_cap_sta & 0x80)                 /* 成功捕获到了一次高电平 */
        {
            temp = g_tmrxchy_cap_sta & 0x3F;
            temp *= 65536;                            /* 溢出时间总和 */
            temp += g_tmrxchy_cap_val;                /* 得到总的高电平时间 */
            printf("HIGH:%d us\r\n", temp);           /* 打印总的高点平时间 */
            g_tmrxchy_cap_sta = 0;                    /* 开启下一次捕获 */
        }
        t++;
        
        if (t > 20)                                   /* 200ms进入一次 */
        {
            t = 0;
            LED0_TOGGLE();                            /* LED0闪烁 ,提示程序运行 */
        }
        delay_ms(10);
    }
}
从上面的代码中可以看到,TMR5的预分频系数被配置为(60 - 1),同时因为TMR5的时钟频率为60MHz,因此TMR5的计数频率为1MHz,因此在TMR5成功捕获到外部输入信号的高电平后,可以直接计算出捕获到高电平的脉宽时间。
19.3 下载验证
在完成编译和烧录后,短暂按下并抬起KEY_UP按键,可以通过串口调试助手观察到捕获到的KEY_UP按键被按下的时间。

阿莫论坛20周年了!感谢大家的支持与爱护!!

盗墓祖师爷留下的规矩:父子两人盗墓结束后,必定是父亲先出墓穴,儿子跟在后面。
如果儿子先上来,说不定想要独吞财物,可能转身一脚就将跟在后面的父亲封闭在墓穴里面。
而父亲永远不会杀死自己的儿子。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-2-28 07:26

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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