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所示。

圖 1.1 PWM原理示意圖
這四個定時器所接的時鐘總線并不一緻,其中TIM10、11接在APB2上,TIM13、14接在APB1上,如圖 1.2所示。
圖 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); } |