天天看點

STM32F767的PWM驅動實作1 簡介2 接口設計3 驅動設計

1 簡介

1.1 PWM介紹

      脈沖寬度調制(PWM),是英文“Pulse Width Modulation” 的縮寫,簡稱脈寬調制。它是利用微處理器的數字輸出來對模拟電路進行控制的一種非常有效的技術,廣泛應用在從測量、通信到功率控制與變換的許多領域中。PWM本質就是對脈沖寬度的控制,其脈沖寬度在整個周期中所占的比例被稱為“占空比”。

1.2 STM32F767的PWM介紹

      STM32F767 的定時器除了 TIM6 和 7,其他的定時器都可以用來産生 PWM 輸出。其中進階定時器 TIM1 和 TIM8 可以同時産生多達 7 路的 PWM 輸出。主要寄存器包括控制寄存器(TIMxCR)、計數器(TIMxCNT)、預分頻器(TIMxPSC)、自動重載寄存器(TIMxARR)、比較寄存器(TIMxCCR)。PWM輸出頻率由PSC和ARR決定,占空比展現為CCR與ARR的比值。基本原理如圖 1.1所示。

STM32F767的PWM驅動實作1 簡介2 接口設計3 驅動設計

                                   圖 1.1 PWM原理示意圖

這四個定時器所接的時鐘總線并不一緻,其中TIM10、11接在APB2上,TIM13、14接在APB1上,如圖 1.2所示。

STM32F767的PWM驅動實作1 簡介2 接口設計3 驅動設計

                                                             圖 1.2 定時器時鐘框圖

本驅動主要實作TIM10、TIM11、TIM13、TIM14四路PWM波的初始化與參數配置接口。

2 接口設計

      TIM10、TIM11、TIM13、TIM14四路PWM對應四個裝置檔案/dev/pwm0、/dev/pwm1、/dev/pwm2、/dev/pwm3。驅動中将ARR固定配置為2500不可修改,通過ioctl配置PSC和CCR設定頻率與占空比。指令105配置參數PSC,指令106配置CCR,計算公式如下(APB1總線橋108000000Hz,APB2總線橋216000000Hz):

TIM10、TIM11

頻率(Hz) = 216000000  / 2500 / (PSC + 1)   (可配置範圍1.3~86400Hz)

占空比(%)= (CCR + 1)/ 2500               (可配置範圍0~100%)

TIM13、TIM14

頻率(Hz) = 108000000  / 2500 / (PSC + 1)   (可配置範圍0.66~43200Hz)

占空比(%)= (CCR + 1)/ 2500               (可配置範圍0~100%)

       應用程式例程如程式清單 2.1所示。

                                                                     程式清單 2.1 PWM應用例程

int  main (void)

{

    int fd, fd1;

    fd = open("/dev/pwm0", O_RDWR);

    if (fd < 0) {

        perror("open");

        exit(PX_ERROR);

    }

    ioctl(fd, 105, 287);

    ioctl(fd, 106, 1250);

    fd1 = open("/dev/pwm3", O_RDWR);

    if (fd < 0) {

        perror("open");

        exit(PX_ERROR);

    }

    ioctl(fd1, 105, 143);

    ioctl(fd, 106, 625);

    sleep(52);

    close(fd);

    close(fd1);

    return  (0);

}

3 驅動設計

      PWM驅動為字元裝置驅動,主要實作了open、close、ioctl三個接口。使用ST提供的Hal庫函數避免了直接操作具體的寄存器,目前使用1.2版本Hal庫。

3.1 open

      open中主要對指定的pwm定時器進行初始化操作,即調用__timPwmInit()。初始化内容包括引腳初始化、定時器初始化和通道初始化三個部分。預設配置占空比50%。__timPwmInit()具體實作如程式清單 3.1所示。

                                                            程式清單 3.1 pwm初始化操作

static void __timPwmInit (INT  uiIndex)

{

    GPIO_InitTypeDef GPIO_Initure;

    if (uiIndex < 0 || uiIndex >= HW_PWM_MAX) {

        printk(KERN_ERR "%s: pwm index invalid!\n", __func__);

        return;

    }

    switch (uiIndex) {

    case HW_PWM0:

        __HAL_RCC_TIM10_CLK_ENABLE();                                                                    

        __HAL_RCC_GPIOF_CLK_ENABLE();                                                                   

        GPIO_Initure.Pin        = GPIO_PIN_6;                                                                      

        GPIO_Initure.Mode      = GPIO_MODE_AF_PP;                                                      

        GPIO_Initure.Pull        = GPIO_PULLDOWN;                                                           

        GPIO_Initure.Speed      = GPIO_SPEED_HIGH;                                                     

        GPIO_Initure.Alternate    = GPIO_AF3_TIM10;                                                       

        HAL_GPIO_Init(GPIOF, &GPIO_Initure);

        _G_timHandler[uiIndex].Instance          = TIM10;                                                     

        _G_timHandler[uiIndex].Init.Prescaler      = PWM_PSC_DEFAULT;                          

        _G_timHandler[uiIndex].Init.CounterMode  = TIM_COUNTERMODE_UP;                 

        _G_timHandler[uiIndex].Init.Period        = PWM_ARR_DEFAULT;                             

        _G_timHandler[uiIndex].Init.ClockDivision  = TIM_CLOCKDIVISION_DIV1;

        _G_timChannel[uiIndex].OCMode         = TIM_OCMODE_PWM1;                           

        _G_timChannel[uiIndex].Pulse            = PWM_ARR_DEFAULT / 2;

        _G_timChannel[uiIndex].OCPolarity       = TIM_OCPOLARITY_HIGH;

        break;

    case HW_PWM1:

        __HAL_RCC_TIM11_CLK_ENABLE();                                                                      

        __HAL_RCC_GPIOF_CLK_ENABLE();                                                                     

        GPIO_Initure.Pin        = GPIO_PIN_7;                                                                       

        GPIO_Initure.Mode      = GPIO_MODE_AF_PP;                                                       

        GPIO_Initure.Pull       = GPIO_PULLDOWN;                                                             

        GPIO_Initure.Speed      = GPIO_SPEED_HIGH;                                                       

        GPIO_Initure.Alternate   = GPIO_AF3_TIM11;                                                          

        HAL_GPIO_Init(GPIOF, &GPIO_Initure);

        _G_timHandler[uiIndex].Instance          = TIM11;                                                       

        _G_timHandler[uiIndex].Init.Prescaler      = PWM_PSC_DEFAULT;                           

        _G_timHandler[uiIndex].Init.CounterMode  = TIM_COUNTERMODE_UP;                 

        _G_timHandler[uiIndex].Init.Period        = PWM_ARR_DEFAULT;                             

        _G_timHandler[uiIndex].Init.ClockDivision  = TIM_CLOCKDIVISION_DIV1;

        _G_timChannel[uiIndex].OCMode         = TIM_OCMODE_PWM1;                            

        _G_timChannel[uiIndex].Pulse            = PWM_ARR_DEFAULT / 2;

        _G_timChannel[uiIndex].OCPolarity       = TIM_OCPOLARITY_HIGH;

        break;

    case HW_PWM2:

        __HAL_RCC_TIM13_CLK_ENABLE();                                                                     

        __HAL_RCC_GPIOF_CLK_ENABLE();                                                                    

        GPIO_Initure.Pin        = GPIO_PIN_8;                                                                      

        GPIO_Initure.Mode      = GPIO_MODE_AF_PP;                                                      

        GPIO_Initure.Pull       = GPIO_PULLDOWN;                                                            

        GPIO_Initure.Speed      = GPIO_SPEED_HIGH;                                                       

        GPIO_Initure.Alternate   = GPIO_AF9_TIM13;                                                         

        HAL_GPIO_Init(GPIOF, &GPIO_Initure);

        _G_timHandler[uiIndex].Instance          = TIM13;                                                     

        _G_timHandler[uiIndex].Init.Prescaler      = PWM_PSC_DEFAULT;                         

        _G_timHandler[uiIndex].Init.CounterMode  = TIM_COUNTERMODE_UP;               

        _G_timHandler[uiIndex].Init.Period        = PWM_ARR_DEFAULT;                            

        _G_timHandler[uiIndex].Init.ClockDivision  = TIM_CLOCKDIVISION_DIV1;

        _G_timChannel[uiIndex].OCMode         = TIM_OCMODE_PWM1;                          

        _G_timChannel[uiIndex].Pulse            = PWM_ARR_DEFAULT / 2;

        _G_timChannel[uiIndex].OCPolarity       = TIM_OCPOLARITY_HIGH;

        break;

    case HW_PWM3:

        __HAL_RCC_TIM14_CLK_ENABLE();                                                                  

        __HAL_RCC_GPIOF_CLK_ENABLE();                                                                 

        GPIO_Initure.Pin        = GPIO_PIN_9;                                                                    

        GPIO_Initure.Mode      = GPIO_MODE_AF_PP;                                                     

        GPIO_Initure.Pull       = GPIO_PULLDOWN;                                                            

        GPIO_Initure.Speed      = GPIO_SPEED_HIGH;                                                      

        GPIO_Initure.Alternate   = GPIO_AF9_TIM14;                                                        

        HAL_GPIO_Init(GPIOF, &GPIO_Initure);

        _G_timHandler[uiIndex].Instance          = TIM14;                                                    

        _G_timHandler[uiIndex].Init.Prescaler      = PWM_PSC_DEFAULT;                         

        _G_timHandler[uiIndex].Init.CounterMode  = TIM_COUNTERMODE_UP;               

        _G_timHandler[uiIndex].Init.Period        = PWM_ARR_DEFAULT;                            

        _G_timHandler[uiIndex].Init.ClockDivision  = TIM_CLOCKDIVISION_DIV1;

        _G_timChannel[uiIndex].OCMode         = TIM_OCMODE_PWM1;                          

        _G_timChannel[uiIndex].Pulse            = PWM_ARR_DEFAULT / 2;

        _G_timChannel[uiIndex].OCPolarity       = TIM_OCPOLARITY_HIGH;

        break;

    default:

        printk(KERN_ERR "__timPwmInit(): uiIndex Invalid!\n");

        return;

        break;

    }

    HAL_TIM_PWM_Init(&_G_timHandler[uiIndex]);                                                            

    HAL_TIM_PWM_ConfigChannel(&_G_timHandler[uiIndex],

                              &_G_timChannel[uiIndex],

                               TIM_CHANNEL_1);                                                                            

    HAL_TIM_PWM_Start(&_G_timHandler[uiIndex], TIM_CHANNEL_1);                           

}

3.2 close

      close操作主要就是關閉PWM波的輸出,如程式清單 3.2所示。

                                                                  程式清單 3.2 close操作

static INT  __pwmClose (PLW_FD_ENTRY   pFdEntry)

{

    __PPWM_CONTROLER  pPwmDev = (__PPWM_CONTROLER)pFdEntry->FDENTRY_pdevhdrHdr;

    UINT              uiIndex = pPwmDev->PWMC_uiIndex;

    HAL_TIM_PWM_Stop(&_G_timHandler[uiIndex],TIM_CHANNEL_1);           

    LW_DEV_DEC_USE_COUNT(&pPwmDev->PWMC_devHdr);

    return (PX_ERROR);

}

3.3 ioctl

      ioctl操作主要通過接口__timPwmPrescaleSet()、__timPwmDutySet()配置PSC、CCR兩個寄存器,用于設定頻率與占空比,如程式清單 3.3所示。

                                                         程式清單 3.3 頻率、占空比配置

static void __timPwmPrescaleSet (INT  uiIndex, UINT16  uiPsc)

{

    if (uiIndex < 0 || uiIndex >= HW_PWM_MAX) {

        printk(KERN_ERR "%s: pwm index invalid!\n", __func__);

        return;

    }

    _G_timHandler[uiIndex].Init.Prescaler      = uiPsc;                                             

    HAL_TIM_PWM_Init(&_G_timHandler[uiIndex]);                                               

    HAL_TIM_PWM_ConfigChannel(&_G_timHandler[uiIndex],

                              &_G_timChannel[uiIndex],

                               TIM_CHANNEL_1);                                                               

    HAL_TIM_PWM_Start(&_G_timHandler[uiIndex], TIM_CHANNEL_1);              

}

static void __timPwmDutySet (INT  uiIndex, UINT16  uiDuty)

{

    if (uiIndex < 0 || uiIndex >= HW_PWM_MAX) {

        printk(KERN_ERR "%s: pwm index invalid!\n", __func__);

        return;

    }

    if (uiDuty > PWM_ARR_DEFAULT) {

        printk(KERN_ERR "%s: pwm Duty invalid!\n", __func__);

        return;

    }

    _G_timChannel[uiIndex].Pulse  = uiDuty;                                                  

    HAL_TIM_PWM_Init(&_G_timHandler[uiIndex]);                                        

    HAL_TIM_PWM_ConfigChannel(&_G_timHandler[uiIndex],

                              &_G_timChannel[uiIndex],

                               TIM_CHANNEL_1);                                                         

    HAL_TIM_PWM_Start(&_G_timHandler[uiIndex], TIM_CHANNEL_1);        

}

繼續閱讀