SylixOS的imx1050平臺PWM捕獲驅動
本文檔是對IMXRT1050平臺上的SylixOS PWM波的產生和捕獲功能的詳細分析。代碼在IMXRT1050的板級支持包的“bsp_rt1050/SylixOs/driver/pwm/”目錄下的pwm.c文件中,該文件會依賴於bsp_rt1050/SylixOs/driver/lib目錄,這個目錄是NXP官方提供的庫文件。
本文檔描述的應用場景是這樣的:PWM需要根據用戶自己設置的參數,達到控制輸出波形的頻率和精確的周期個數,以達到控制3D打印機的目的。
IMXRT1050包含4個PWM模塊,每個子模塊有3路,PWM_A/PWM_B/PWM_X, 如圖 2.1所示。暫時只關心PWM_A/PWM_B。
圖 2.1 PWM子模塊
圖 2.2 PWM波形產生
- 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輸入捕獲功能邏輯 -
代碼編寫
4.1 編碼思路
PWM_A作為輸出,PWM_B作為捕獲輸入。應用程序傳兩個參數,分別指定PWM_A輸出波形的頻率、PWM_B應該捕獲的周期數。當PWM_B捕獲到指定周期數,會觸發中斷處理函數,中斷處理函數關閉PWM_A的輸出波,這樣就達到了控制輸出周期的個數的目的。同時需要實現同步功能,即在中斷函數中需要post一個信號量,告知pend的一方已經產生了指定個數的周期,並關閉了PWM_A。
綜上所述,應該實現一下幾個函數:
? pwmConfig配置PWM輸出功能
? pwmCaptureInputConfig 配置捕獲功能
? pwmIsr 中斷處理函數
? 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捕獲驅動