1. 程式人生 > >SylixOS的imx1050平臺PWM捕獲驅動

SylixOS的imx1050平臺PWM捕獲驅動

SylixOs

  • 概述
    本文檔是對IMXRT1050平臺上的SylixOS PWM波的產生和捕獲功能的詳細分析。代碼在IMXRT1050的板級支持包的“bsp_rt1050/SylixOs/driver/pwm/”目錄下的pwm.c文件中,該文件會依賴於bsp_rt1050/SylixOs/driver/lib目錄,這個目錄是NXP官方提供的庫文件。
    本文檔描述的應用場景是這樣的:PWM需要根據用戶自己設置的參數,達到控制輸出波形的頻率和精確的周期個數,以達到控制3D打印機的目的。
  • PWM波的輸出
    IMXRT1050包含4個PWM模塊,每個子模塊有3路,PWM_A/PWM_B/PWM_X, 如圖 2.1所示。暫時只關心PWM_A/PWM_B。技術分享圖片
  • 圖 2.1 PWM子模塊

    可以想象PWM只要確定一個上升沿和一個下降沿的時刻就能確定一個周期,從而可以確定一個頻率的波形。因此需要用到一個定時器和幾個比較器。如圖 2.2所示顯示的是中心對齊的波形產生示意圖。設置VAL2和VAL3寄存器,作為比較器的值,當計數器的值匹配VAL2,PWM_A會產生一個上升邊沿,當計數器的值匹配VAL3,PWM_A會產生一個下降沿,這樣循環下去,PWM_A就會產生一連串波形。PWM_B同樣如此。

    技術分享圖片
    圖 2.2 PWM波形產生

    1. PWM的捕獲
      PWM同樣可以設置為輸入捕獲功能。電路圖如圖 3.1所示。輸入有兩路,由INP_SEL控制。第一路直接輸入捕獲到電路,並可設置在輸入波形的上升沿/下降沿/雙邊沿觸發中斷,在中斷中可以關閉PWM用來實現控制輸出波形周期數,第二路輸入波形先經過一個8bit計數器,計數器記錄輸入波形雙邊沿個數,存儲在EDGCNT裏。可以設置期望比較值EDGCPM。比較器comparator比較EDGCNT和EDGCPM的值,當匹配時,reset邊沿計數器的值,同時進入捕獲電路。並可產生中斷。第二路將很好的減少中斷的次數,提高PWM的性能。捕獲電路Circuit 0 Capture和Circuit 1 Capture將會交替工作。使能Arming Logic 將使捕獲電路開始工作。
      技術分享圖片
      圖 3.1輸入捕獲功能邏輯
    2. 代碼編寫
      4.1 編碼思路
      PWM_A作為輸出,PWM_B作為捕獲輸入。應用程序傳兩個參數,分別指定PWM_A輸出波形的頻率、PWM_B應該捕獲的周期數。當PWM_B捕獲到指定周期數,會觸發中斷處理函數,中斷處理函數關閉PWM_A的輸出波,這樣就達到了控制輸出周期的個數的目的。同時需要實現同步功能,即在中斷函數中需要post一個信號量,告知pend的一方已經產生了指定個數的周期,並關閉了PWM_A。
      綜上所述,應該實現一下幾個函數:
      ? pwmConfig配置PWM輸出功能
      ?
      pwmCaptureInputConfig 配置捕獲功能
      ? pwmIsr 中斷處理函數

      ? PWM_StopTimer 停止PWM輸出功能
      ? PWM_StartTimer 打開PWM 輸出功能
      除了中斷處理函數,其他函數都可以掛在字符驅動函數
      pwmIoctl中。
      4.2 代碼分析
      4.2.1 初始化部分
      程序清單 4.1
      /
      為pwm0創建同步信號量
      /

      _G_pwm[kPWM_Module_0].PWMC_CapSem = API_SemaphoreBCreate("pwm_sync",LW_FALSE,LW_OPTION_OBJECT_GLOBAL,LW_NULL);

    if(LW_OBJECT_HANDLE_INVALID == _G_pwm[kPWM_Module_0].PWMC_CapSem)
    {
    PWM_DEBUG("Pwm API_SemaphoreBCreate Failed\n");
    return (PX_ERROR);
    }

    /*
        初始化PWM0中斷,用於pwm捕獲
    */

    PwmIRQ = PWM1_0_IRQn;
    API_InterVectorSetPriority(BSP_IRQ_TO_VECTOR(PwmIRQ), LW_INTER_PRIO_LOWEST);
    API_InterVectorConnect(BSP_IRQ_TO_VECTOR(PwmIRQ),(PINT_SVR_ROUTINE)__pwmIsr0,NULL,"pwm_isr0"); / 註冊中斷服務函數 /
    API_InterVectorEnable(BSP_IRQ_TO_VECTOR(PwmIRQ));
    4.2.2 __pwmIoctl

    程序清單 4.2
    static INT pwmIoctl (PLW_FD_ENTRY pFdEntry, INT iCmd, LONG lArg)
    {
    INT iRet;
    INT iIrq;
    PPWM_USER_CONFIG pPwmUserConfig;
    PPWM_DUTY_CONFIG pPwmDutyConfig;
    INT iModuleCtr;
    PPWM_CAPTURE_CONFIG pCapParaCfg;
    __PPWM_CONTROLER pPwmDev = (__PPWM_CONTROLER)pFdEntry->FDENTRY_pdevhdrHdr;

    switch (iCmd) {
    case PWM_MODE_SET:  /*  PWM 模式設置 */
        pPwmUserConfig = (__PPWM_USER_CONFIG)lArg;
    
        if(LW_NULL == pPwmUserConfig) 
        {
            return  (PX_ERROR);
        }
    
        iRet = __pwmConfig(pPwmDev->PWMC_pBase, pPwmUserConfig);
        break;
    case PWM_STOP_SET:    /*  停止 PWM  */
        iModuleCtr     = (INT)lArg;
        if (iModuleCtr > kPWM_Control_Module_3 ||
            iModuleCtr < kPWM_Control_Module_0) 
        {
            PWM_DEBUG("larg error\r\n");
            return PX_ERROR;
        }
    
        PWM_StopTimer(pPwmDev->PWMC_pBase, iModuleCtr);
        iRet = ERROR_NONE;
        break;
    
    case PWM_START_SET:     /*  打開 PWM  */                                      
        iModuleCtr     = (INT)lArg;
    
        if (iModuleCtr > kPWM_Control_Module_3 ||
            iModuleCtr < kPWM_Control_Module_0) 
        {
            PWM_DEBUG("larg error\r\n");
            return PX_ERROR;
        }
    
        PWM_StartTimer(pPwmDev->PWMC_pBase, iModuleCtr);
        iRet = ERROR_NONE;
        break;
    case PWM_INCAPT_SET:   /*  捕獲輸入 */
        pCapParaCfg = (__PPWM_CAPTURE_CONFIG)lArg;
        iRet = __pwmCaptureInputConfig(pPwmDev->PWMC_pBase,pCapParaCfg);
        break;
    case PWM_CAPT_WAITE: /*用於捕獲同步*/
        iRet = API_SemaphoreBPend(_G_pwm[kPWM_Module_0].PWMC_CapSem,LW_OPTION_WAIT_INFINITE);       
        break;
    default:
        return  (PX_ERROR);
    }
    
    return  (iRet);

    }
    4.3 捕獲配部分
    程序清單 4.3
    static INT __pwmCaptureInputConfig (PWM_Type *pBase, __PPWM_CAPTURE_CONFIG pPwmCaptureConfig)
    {
    uint32_t irq;
    uint32_t status = 0;

    PWM_SetupInputCapture(pBase, pPwmCaptureConfig->subModule, pPwmCaptureConfig->pwmChannel, pPwmCaptureConfig->inputCaptureParams);
    status = PWM_GetStatusFlags(pBase, pPwmCaptureConfig->subModule);

    /*
        先清中斷
    */
    if(status & (PWM_STS_CFB0_MASK)) 
    {
        //bspDebugMsg("PWM_ClearStatusFlags>>>>>>\n");
        PWM_ClearStatusFlags(pBase,pPwmCaptureConfig->subModule,status & (PWM_STS_CFB0_MASK));
    }
    
    /*
        中斷的開啟必須要在PWM_SetupInputCapture 配置之後,否則可能會出現配置沒有完成就進入中斷
    */
    irq = PWM_GetEnabledInterrupts(pBase, kPWM_Module_0);   
    PWM_EnableInterrupts(pBase, kPWM_Module_0, (uint32_t)(irq | PWM_INTEN_CB0IE(1) |PWM_INTEN_CB1IE(1)));
    /*
        使配置生效
    */
    PWM_SetPwmLdok(pBase, 1 << kPWM_Module_0, LW_TRUE);
    
    return (ERROR_NONE);

    }

    4.4 中斷部分
    程序清單 4.4
    static irqreturn_t __pwmIsr0(PVOID pvArg, ULONG ulVector)
    {
    uint32_t status = 0;

    status = PWM_GetStatusFlags(_G_pwm[kPWM_Module_0].PWMC_pBase, kPWM_Module_0);
    
    if(status & PWM_STS_CFB0_MASK) //clear int
    {
    PWM_ClearStatusFlags(_G_pwm[kPWM_Module_0].PWMC_pBase, kPWM_Module_0,status & PWM_STS_CFB0_MASK);
        PWM_StopTimer(_G_pwm[kPWM_Module_0].PWMC_pBase,1 << kPWM_Module_0);
    }
    
    if(status & PWM_STS_CFB1_MASK) //clear int
    {
    PWM_ClearStatusFlags(_G_pwm[kPWM_Module_0].PWMC_pBase, kPWM_Module_0,status & PWM_STS_CFB1_MASK);
        PWM_StopTimer(_G_pwm[kPWM_Module_0].PWMC_pBase,1 << kPWM_Module_0);
        //_DebugFormat(__PRINTMESSAGE_LEVEL, "2");
    }
    
    API_SemaphoreBPost(_G_pwm[kPWM_Module_0].PWMC_CapSem);
    
    return  (LW_IRQ_HANDLED);

    }

    SylixOS的imx1050平臺PWM捕獲驅動