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

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

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


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

更新部分可以参考我的GitHub

今天修复了Project里频率计的一个Bug。
原来的版本无法得到广谱上的正确频率,只能正确地得到几个分立的频率点。分析发现这些频率点其实都是时钟频率的约数,也就是说他们的整数倍正好可以得到时钟频率的数值。但是频率谱上抛开这几个点的其他频率,则会出现闪屏,也就是无法得到计算值在跳变。
我最开始以为,是因为在CCP在ReLoad的时候,结合图像来看就是那段斜率为90度的那段,在ReLoad的那个瞬间TIMER_TICK数组的两个值被更新导致差值正好是SysCtlClockGet() - (SysCtlClock() - freq), 导致计算得到错误的频率并且刷屏。

猜想错误原因

但其实不是,后来单步运行,发现是calc_freq()在执行的过程中,多次被打断而进入CCPIntHandler(),这改变了计算频率的参数TIMER_TICK数组的值。后来我尝试在调用calc_freq()时清除中断,却发现仍会进入CCPIntHandler()。
整个人都不好了
想了个笨方法,定义了个全局变量tmp来记录进入中断函数那个瞬间他们的差值,即使仍然会进入CCPIntHandler(),其差值也已经被记录了下来。
完美解决~
不过还有一个问题就是测量的带宽不够大,一旦频率变高了,比如说大于200kHz,就会因为进入中断函数太快而导致无法执行calc_freq(),它被忽略掉了…导致频率测不出来
TODO: To increase the band width of the frequency meter!

以下是原文

一. 实验简介

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

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

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

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

三. 库函数编程方法


  1. 首先使能对应的定时器模块,比如Timer0
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
//prototype:SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMERx);

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

  1. 将引脚设置为复用外设功能,如果要设置为定时器功能,调用以下函数
GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);
//prototype:GPIOPinTypeTimer(GPIO_PORTx_BASE, GPIO_PIN_x);

  1. 将GPTM模块的信号TnCCP0TnCCP1(或是WTnCCP0WTnCCP1)配置到具体的的GPIO引脚上去:配置寄存器GPIOPCTL。实际调用的是GPIOPinConfigure()函数
GPIOPinConfigure(GPIO_PB6_T0CCP0);
//prototype:GPIOPinConfigure(GPIO_Pxx_TxCCPx);

  1. 配置定时器模块为捕捉-边沿计时模式
    需要注意的是,在该模式下,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);

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

  1. 除了要作通用配置外,每种定时器根据不同模式还要作具体的初始化配置,一会儿在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;       //to 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);
    if(TIMER_TICK[0] < TIMER_TICK[1])
        tmp = TIMER_TICK[1] - TIMER_TICK[0];
    else
        tmp = TIMER_TICK[0] - TIMER_TICK[1];

    freq = SysCtlClockGet() / tmp;

}

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

五. 结果

别忘了把PB6和PF3跳线!因为它们是PWM接收和输出的端口~
我设置的PWM占空比为1%(设这么小主要是想看看极端条件下灵不灵敏),频率为50kHz
任意频率读取
屏幕内容
参考链接
[1]: Help with Frequency Meter(Google)
[2]: https://github.com/WadeGao/TIVA

发布了6 篇原创文章 · 获赞 3 · 访问量 356

猜你喜欢

转载自blog.csdn.net/weixin_44587168/article/details/102529152