TIVA-TM4C123GH6PM的输入边沿计时模式的配置

正在学TIVA的TM4C123GH6PM板子,记录一下学习输入边沿计时模式的过程

2019年10月15日01:14:06更新

今天修复了Project里频率计的一个Bug。

原来的版本无法得到广谱上的正确频率,只能正确地得到几个分立的频率点。分析发现这些频率点其实都是时钟频率的约数,也就是说他们的整数倍正好可以得到时钟频率的数值。但是频率谱上抛开这几个点的其他频率,则会闪屏,也就是无法得到计算值在跳变。

我最开始以为,是因为在CCP在ReLoad的时候,结合图像来看就是那段斜率为90度的那段,在ReLoad的那个瞬间TIMER_TICK数组的两个值被更新导致差值正好是SysCtlClockGet() - (SysCtlClock() - freq), 导致计算得到错误的频率并且刷屏。

但其实不是,后来单步运行,发现是calc_freq()在执行的过程中,多次被打断而进入CCPIntHandler(),这改变了计算频率的参数TIMER_TICK数组的值。后来我尝试在调用calc_freq()时清除中断,却发现仍会进入CCPIntHandler()。

整个人都不好了

想了个笨方法,定义了个全局变量tmp来记录进入中断函数那个瞬间他们的差值,即使仍然会进入CCPIntHandler(),其差值也已经被记录了下来。

不过还有一个问题就是测量的带宽不够大,一旦频率变高了,比如说大于200kHz,就会因为进入中断函数太快而导致无法执行calc_freq(),它被忽略掉了…导致频率测不出来


一. 实验简介

  • 使用两个定时器
  • 一个用PWM模式产生PWM波形
  • 将此方波信号接入另一定时器的输入端,通过边沿计时模式来测量该信号的周期,并将其显示在液晶屏上

二. TM4C123输入边沿计时模式介绍

  • 输入边沿计时模式用于捕捉输入信号的边沿时间,当输入信号有一个待捕捉的边沿时(上升沿或下降沿),计数器会产生一个中断信号。
  • 对于上升沿检测,输入信号必须在上升沿之后保持高电平至少两个系统时钟周期,对于下降沿检测,输入信号必须在下降沿之后保持低电平至少两个系统时钟周期。根据这个标准,边沿检测的最大输入频率是系统频率的1/4。
  • 选择输入边沿计时模式,需要将寄存器GPTMTnMR中的TnCMR位置1,且TnMR位置0x3,以选择捕获模式。
  • 在输入边沿计时模式下,可以选择的边沿同样有上升沿,下降沿或双边沿,通过配置寄存器GPTMCTLTnEVENT位来选择。当定时器器独立工作时,可以使用预分频器将计数器的位数由16/32位扩展为24位/48位。
  • GPTMCTL中的TnEN被置位,计时开始。当有捕获事件(捕获到边沿信号)发生时,计数器的数值会被存储在寄存器GMTMTnRGPTMTnPS中,并且可以被微处理器读取,之后产生一个捕获事件中断用于处理捕获事件。也就是说,只要检测到了自己设定的边沿的到来,就会进入一次中断
  • 处理完成后计数继续进行,直到GPTMCTL中的TnEN位被置0。而GPTMTnV寄存器和GPTMTnPV寄存器会保存当前独立运行的计数值和预分频器的值。当计数从0达到了计数的初值(递增计数模式,由装载寄存器设置)或是由初值达到0(递减计数模式)时,计数器重新装载初始值继续计数。

下图示范了采用递减计数模式,只捕获上升沿,计数器预设值为GPTMTnILR=0Xffff,每当捕获一个上升沿时,计数器中的数值被写入GPTMTnRGPTMTnPS寄存器(当使用预分频器时才使用此寄存器)中,注意只要检测到了边沿的到来就会进入一次中断。(当初我这块没弄明白,卡顿了好久…)

三. 库函数编程方法

  • 首先使能对应的定时器模块,比如Timer0

    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    //prototype:SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMERx);
    
  • 找到该GPTM模块中TnCCP0TnCCP1(或是WTnCCP0WTnCCP1)所对应的GPIO引脚,使能这些引脚所属的GPIO模块的时钟信号

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    //prototype:SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOBx);
    
  • 将引脚设置为复用外设功能,如果要设置为定时器功能,调用以下函数

    GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);
    //prototype:GPIOPinTypeTimer(GPIO_PORTx_BASE, GPIO_PIN_x);
    
  • 将GPTM模块的信号TnCCP0TnCCP1(或是WTnCCP0WTnCCP1)配置到具体的的GPIO引脚上去:配置寄存器GPIOPCTL。实际调用的是GPIOPinConfigure()函数

    GPIOPinConfigure(GPIO_PB6_T0CCP0);
    //prototype:GPIOPinConfigure(GPIO_Pxx_TxCCPx);
    
  • 配置定时器模块为捕捉-边沿计时模式

    • 需要注意的是,在该模式下,TimerConfigure第二个参数是TIMER_CFG_SPLIT_PAIR和一下之一相或:

      • TIMER_CFG_A_CAP_TIME模块A捕捉-边沿减计时模式
      • TIMER_CFG_A_CAP_TIME _UP 模块A捕捉-边沿加计时模式
      • TIMER_CFG_B_CAP_ TIME 模块B捕捉-边沿减计时模式
      • TIMER_CFG_B_CAP_ TIME _UP 模块B捕捉-边沿加计时模式
    • 例如

    TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP);
    
  • 设置计时范围,TimerLoadSet无论加计时还是减计时,都用这个函数设置Preload值,这个值是指经过了多少个检测到的脉冲边沿后将重新装载(而不是检测到了多少个边沿才发生一次中断

  • 除了要作通用配置外,每种定时器根据不同模式还要作具体的初始化配置,一会儿在src里体现

四. 示例代码

实现了输入任意频率PWM波形的频率检测,可以充当频率计使用,PB6接收PWM脉冲边沿,PF3输出PWM波形

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_timer.h"
#include "inc/hw_ints.h"
#include "inc/hw_gpio.h"
#include "inc/hw_i2c.h"
#include "inc/hw_sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/ssi.h"
#include "driverlib/i2c.h"
#include "driverlib/udma.h"
#include "driverlib/fpu.h"
#include "driverlib/rom.h"
#include "uc1701.h"

//*****************************************************************************
//Attention
//TM4C123 NMI unlock - To those who want to use PF0 and PD7, be reminded that these pins defaults as NMI ! ! !
//
//*****************************************************************************

// The error routine that is called if the driver library encounters an error.
#ifdef DEBUG
void __error__(char *pcFilename, unsigned long ulLine)
{
   
}
#endif
//*****************************************************************************

//*************************Global variables*************************//
unsigned char pulse;
unsigned long freq;
unsigned long TIMER_TICK[2];
unsigned short i = 0;
unsigned long CCPLoadSet;
unsigned long PWM_Frep;
//******************************************************************//

//*************************Function dedclarations*************************//
void calc_freq(void);
void PWM_1B_IntHandler(void);
void CCP_Init(void);
void PWM_Set_PWM_Frep(unsigned long freq);//modified parts
void CCP_Set_Load(unsigned long load);//modified parts
void PWM_Init(unsigned long Freq_Hz, unsigned char duty);
void CCPIntHandler(void);
//*************************Function dedclarations*************************//
void main(void)
{
   
    SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    PWM_Set_PWM_Frep(131e3);         //modified parts
    PWM_Init(PWM_Frep, 1);
    InitUC1701();
    UC1701CharDispaly(0, 4, "Freq Meter");
    UC1701CharDispaly(1, 9, "Hz");
    UC1701CharDispaly(2, 9, "kHz");
    UC1701CharDispaly(3, 9, "%");
    CCP_Set_Load(SysCtlClockGet() - 1);     //modified parts
    CCP_Init();
    IntMasterEnable();
    while(1){
   
        calc_freq();
        UC1701DisplayN(1, 2, freq);
        DisPlayDataFloat_lower_than_100(2, 2, freq / 1000.00);
        UC1701DisplayN(3, 4, 100 - pulse);
    }
}
//*************************Initialization modules*************************//
void PWM_Init(unsigned long Freq_Hz, unsigned char duty){
   
    pulse = 100 - duty;
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    GPIOPinConfigure(GPIO_PF3_T1CCP1);
    GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_3);
    TimerConfigure(TIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM);
    TimerIntRegister(TIMER1_BASE, TIMER_B, PWM_1B_IntHandler);
    IntEnable(INT_TIMER1B);  //Enables a timer interrupt
    TimerIntEnable(TIMER1_BASE, TIMER_CAPB_EVENT);   //Enables individual timer interrupt sources
    IntMasterEnable();  //Enables the processor interrupt
    TimerLoadSet(TIMER1_BASE, TIMER_B, SysCtlClockGet() / Freq_Hz - 1);
    TimerMatchSet(TIMER1_BASE, TIMER_B, (SysCtlClockGet() / Freq_Hz - 1) * (pulse / 100.0));
    TimerEnable(TIMER1_BASE, TIMER_B);
}
void CCP_Init(void){
   
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIOPinConfigure(GPIO_PB6_T0CCP0);
    GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);
    GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_6, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
    TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP);
    TimerControlEvent(TIMER0_BASE, TIMER_A, TIMER_EVENT_NEG_EDGE);
    TimerLoadSet(TIMER0_BASE, TIMER_A, CCPLoadSet);
    TimerIntRegister(TIMER0_BASE, TIMER_A, CCPIntHandler);
    IntMasterEnable();
    TimerIntEnable(TIMER0_BASE, TIMER_CAPA_EVENT);
    IntEnable(INT_TIMER0A);
    TimerEnable(TIMER0_BASE, TIMER_A);
}
//*************************Initialization modules*************************//

//*************************Modified parts*************************//
void PWM_Set_PWM_Frep(unsigned long freq){
   
    PWM_Frep = freq;
}
void CCP_Set_Load(unsigned long load){
   
    CCPLoadSet = load;
}
//*************************Modified parts*************************//

//*************************InterruptHandlers modules*************************//
void PWM_1B_IntHandler(void){
   
    TimerIntClear(TIMER1_BASE, TIMER_CAPB_EVENT);
}
void CCPIntHandler(void){
   
    TimerIntClear(TIMER0_BASE, TIMER_CAPA_EVENT);
    if(i == 2)
        i = 0;
    TIMER_TICK[i++] = TimerValueGet(TIMER0_BASE,TIMER_A);
}
//*************************InterruptHandlers modules*************************//

//*************************Calculate frequence*************************//
void calc_freq(void)
{
   
    static unsigned long tmp;
    //ignore the re-execution of CCPIntHandler
    //save the current TIMER_TICK[0] - TIMER_TICK[1]
    //although TIMER_TICK is still varying
    TimerIntClear(TIMER0_BASE, TIMER_A);
    tmp = abs(TIMER_TICK[1] - TIMER_TICK[0])
    freq = SysCtlClockGet() / tmp;
}

这个链接里可以找到完整项目代码,zip里有一些头文件和库
提取码: snih

五. 结果

别忘了把PB6和PF3跳线!因为它们是PWM接收和输出的端口~
我设置的PWM占空比为1%(设这么小主要是想看看极端条件下灵不灵敏),频率为50kHz


参考链接
[1]: Help with Frequency Meter(Google)
[2]: https://github.com/WadeGao/TIVA

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务