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輸出、編碼器計數、還有多路的脈衝採集真是想了好半天……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章