k60-脉冲采集

K60-脉冲计数采集

之前在做智能车的时候需要对LC振荡电路进行频率采集(其实如果频率变化率比较大,直接选择去掉整流电路采集电压的变化也可行),研究了一段时间的K60脉冲计数。K60本身的配置和功能就不多说了,就直接切入正题吧。(以下的所有代码都是基于LPLD库实现的,野火或者山外的就自己去论坛找找,有相应的实现代码)

FTM脉冲计数

FTM模块是一个多功能定时器模块,主要功能有PWM输出、输入捕捉、输出比较、定时中断、脉冲加减计数、脉冲周期脉宽测量。K60中包含有3个互相独立的FTM模块:FTM0、FTM1、FTM2,其中MK60F系列还含有额外的FTM3模块。FTM模块最常用的功能是实现PWM输出,但是只有FTM1、FTM2模块具备正交解码功能,而正交解码功能正是我们实现脉冲计数采集所需要的。

PS:实际上FTM的输入捕获也是可以实现脉冲计数的,但是一方面考虑到是通过软件中断,本身程序中已经有设置了一个中断进行K60控制,担心会影响主要程序的运行,另一方面,输入捕捉在频率较高的情况下采集精度不高,不适合对脉冲数进行处理。

正交解码听起来挺厉害的,但是实际上实现起来恨简单(反正都是调用库函数),正交解码最常用的是用来采集编码器的正反信号,那个就是采集的脉冲信号,如果我们只考虑编码器的正转的话就完全可以等效为脉冲计数采集,同时及时在较高频率的情况下也能保证精度。

下面就给出具体的代码:

//初始化FTM的正交解码模块,其中通道0为需要采集的脉冲端口,通道1接地
void Qd_Init(void)
{
  FTM_InitTypeDef ftm_init_struct;
  ftm_init_struct.FTM_Ftmx = FTM2;                      //只有FTM1和FTM2有正交解码功能
  ftm_init_struct.FTM_Mode = FTM_MODE_QD;               //正交解码功能
  ftm_init_struct.FTM_QdMode = QD_MODE_CNTDIR;          //计数和方向解码模式 
  ftm_init_struct.FTM_ClkDiv = FTM_CLK_DIV64;           //128分频

  LPLD_FTM_Init(ftm_init_struct);                       //初始化FTM
}

//获取脉冲数
short int pulseCount = LPLD_FTM_GetCounter(FTM2);       //获取计数器中采集到脉冲数
LPLD_FTM_ClearCounter(FTM2);                            //每次采集之后都要对FTM计数器清零

PS:如果出现采集的数据一直是65535之类的,很有可能是数据类型出错了,尝试一下其他的整数类型

LPTMR脉冲计数

LPTMR就没什么好说的了,在配置的时候选择脉冲累计模式就行了。

下面是代码:

//LPTMR初始化
void LPTMR_Init()
{
  LPTMR_InitTypeDef lptmr_init_struct;              
  lptmr_init_struct.LPTMR_Mode = LPTMR_MODE_PLACC;      //脉冲计数模式
  lptmr_init_struct.LPTMR_PluseAccInput = LPTMR_ALT2;   //选择ALT2通道,PTC5,具体的通道在对应头文件中查看
  lptmr_init_struct.LPTMR_Isr = NULL;
  LPLD_LPTMR_Init(lptmr_init_struct);                   //初始化LPTMR    
}

//获取脉冲数
short int pulseCount = LPLD_LPTMR_GetPulseAcc();        //获取脉冲数
LPLD_LPTMR_ResetCounter();                              //清空LPTMR

LPTMR在采集脉冲的时候相对于FTM、DMA来说是准确度最高的,但是只能LPTMR模块只能实现单路脉冲采集。

DMA脉冲计数

DMA使得外围设备可以通过DMA控制器直接访问内存,与此同时,CPU可以继续执行程序,相对于普通的IO中断时会向CPU发送中断请求显然节约了CPU的处理时间,也就是硬件中断,比软件中断和轮询都要好很多。而且K60有5组IO口,每组IO口都可以选择一个IO口作为DMA脉冲计数的IO口,大大的扩展了可计数的路数,但是实际上DMA采集到的脉冲数会时不时的有很大的跳变,虽然在一定程序上可以通过软件限幅解决,但是很影响采集精度,而且程序的可靠性也会受影响。(DMA其实也有出现在K60的中断表中,不知道是不是因为这个原因影响到了采集脉冲的总中断?)

下面给出来的代码是从野火那里移植过来的,LPLD本身并没有能够实现DMA脉冲计数的函数,使用野火的直接去找源码吧:

/*************************************************************************
*                             野火嵌入式开发工作室
*
*  函数名称:DMA_count_Init
*  功能说明:DMA累加计数初始化
*  参数说明:DMA_CHn              通道号(DMA_CH0 ~ DMA_CH15)
*            PTxn                 触发端口
*            count                累加计数中断值
*            DMA_Count_cfg        DMA传输配置
*  函数返回:无
*  修改时间:2012-1-20
*  备    注:
*************************************************************************/
void DMA_count_Init(uint8 CHn, PortPinsEnum_Type ptxn,uint32 count)
{
    uint8 byten = DMA_BYTE1;
    uint8 BYTEs=(byten==DMA_BYTE1 ? 1:(byten==DMA_BYTE2 ? 2:(byten==DMA_BYTE4 ? 4:16 ) ) );    //计算传输字节数
    if(count > 0x7FFF )count = 0x7FFF;
    count_init[CHn]=count;

    /* 开启时钟 */
    SIM->SCGC7|=SIM_SCGC7_DMA_MASK;                          //打开DMA模块时钟
    SIM->SCGC6|=SIM_SCGC6_DMAMUX_MASK;                       //打开DMA多路复用器时钟

    /* 配置 DMA 通道 的 传输控制块 TCD ( Transfer Control Descriptor ) */
    DMA0->TCD[CHn].SADDR = DMA_SADDR_SADDR(COUNTSADDR); // 设置源地址
    DMA0->TCD[CHn].DADDR = DMA_DADDR_DADDR(COUNTDADDR); // 设置目的地址       

    DMA0->TCD[CHn].SOFF  =    0;                                  // 设置源地址不变
    DMA0->TCD[CHn].DOFF  =    0;                                  // 每次传输后,目的地址不变

    DMA0->TCD[CHn].ATTR  =    (0
                        | DMA_ATTR_SMOD(0x0)                // 源地址模数禁止  Source address modulo feature is disabled
                        | DMA_ATTR_SSIZE(byten)             // 源数据位宽 :DMA_BYTEn  。    SSIZE = 0 -> 8-bit ,SSIZE = 1 -> 16-bit ,SSIZE = 2 -> 32-bit ,SSIZE = 4 -> 16-byte
                        | DMA_ATTR_DMOD(0x0)                // 目标地址模数禁止
                        | DMA_ATTR_DSIZE(byten)             // 目标数据位宽 :DMA_BYTEn  。  设置参考  SSIZE
                        );

    DMA0->TCD[CHn].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(count); //当前主循环次数
    DMA0->TCD[CHn].BITER_ELINKNO = DMA_CITER_ELINKNO_CITER(count);//起始主循环次数

    DMA0->CR &=~0x80u;                             // CR[EMLM] = 0

    DMA0->TCD[CHn].NBYTES_MLNO=   DMA_NBYTES_MLNO_NBYTES(BYTEs);  // 通道每次传输字节数,这里设置为BYTEs个字节。注:值为0表示传输4GB */

    /* 配置 DMA 传输结束后的操作 */
    DMA0->TCD[CHn].SLAST      =   -count;                              //调整  源地址的附加值,主循环结束后恢复  源地址
    DMA0->TCD[CHn].DLAST_SGA  =   0;                                  //调整目的地址的附加值,主循环结束后恢复目的地址或者保持地址
    DMA0->TCD[CHn].CSR        =   (0
                             | DMA_CSR_DREQ_MASK            //主循环结束后停止硬件请求
                             | DMA_CSR_INTMAJOR_MASK        //主循环结束后产生中断
                             );

    /* 配置 DMA 触发源 */
    DMAMUX->CHCFG[CHn] = (0
                         | DMAMUX_CHCFG_ENBL_MASK                        /* Enable routing of DMA request */
                         | DMAMUX_CHCFG_SOURCE((ptxn>>5)+PORTA_DMAREQ)     /* 通道触发传输源:     */
                         );

    SIM->SCGC5 |= (SIM_SCGC5_PORTA_MASK<<(ptxn>>5));                                                                 //开启PORTx端口

    /* 开启中断 */
    LPLD_DMA_EnableReq(CHn);                                    //使能通道CHn 硬件请求
    enable_irq((IRQn_Type)(CHn + DMA0_IRQn));                             //允许DMA通道传输
}
/*************************************************************************
*                             野火嵌入式开发工作室
*
*  函数名称:DMA_count_get
*  功能说明:返回累加计数值
*  参数说明:DMA_CHn              通道号(DMA_CH0 ~ DMA_CH15)
*  函数返回:累加计数值
*  修改时间:2012-3-320
*  备    注:
*************************************************************************/
uint32 DMA_count_get(uint8 CHn)
{
    uint32 temp =  count_init[CHn] - DMA0->TCD[CHn].CITER_ELINKNO  ;
    return temp;
}


/*************************************************************************
*                             野火嵌入式开发工作室
*
*  函数名称:DMA_count_reset
*  功能说明:重新加载累加计数值
*  参数说明:DMA_CHn              通道号(DMA_CH0 ~ DMA_CH15)
*  函数返回:累加计数值
*  修改时间:2012-3-320
*  备    注:
*************************************************************************/
void DMA_count_reset(uint8 CHn)
{

        //设置主循环计数器 current major loop count
    DMA0->TCD[CHn].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(count_init[CHn]);

}

//初始化DMA采集
void Count_Init(void)
{
  GPIO_InitTypeDef DMA_GPIO;

  DMA_GPIO.GPIO_PTx = PTC;                                  //选择PORT
  DMA_GPIO.GPIO_Pins = GPIO_Pin5;                           //选择要初始化的引脚
  DMA_GPIO.GPIO_Dir = DIR_INPUT;                            //选择输入模式
  DMA_GPIO.GPIO_PinControl = INPUT_PULL_UP|IRQC_DMAFA;      //选择输入上拉、下降沿产生DMA请求 
  LPLD_GPIO_Init(DMA_GPIO);                                 //初始化对应GPIO口
  DMA_count_Init(DMA_CH0, PTC5,0x7FFF);                     //DMA脉冲计数初始化
}

//获取脉冲计数
int32 pulseCount = DMA_count_get(DMA_CH0);                  //采集脉冲数
DMA_count_reset(DMA_CH0);                                   //清空

花了点时间整理了一下思路,当时既要考虑PWM输出、编码器计数、还有多路的脉冲采集真是想了好半天……

你可能感兴趣的:(电子设计)