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); } /* ** PWM0、PWM1頻率設置: ** 頻率(Hz)= 216000000 / 2500 / (uiPsc + 1) ** = 216000000 / 2500 / (287 + 1) ** = 300Hz */ ioctl(fd, 105, 287); /* * 佔空比50% */ ioctl(fd, 106, 1250); fd1 = open("/dev/pwm3", O_RDWR); if (fd < 0) { perror("open"); exit(PX_ERROR); } /* ** PWM2、PWM3頻率設置: ** 頻率(Hz)= 216000000 / 2 / 2500 / (uiPsc + 1) ** = 216000000 / 2 / 2500 / (143 + 1) ** = 300Hz */ ioctl(fd1, 105, 143); /* * 佔空比25% */ 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初始化操作
/********************************************************************************************************* ** 函數名稱: __timPwmInit ** 功能描述: pwm初始化 ** 輸 入 : uiIndex PWM 設備控制器序號 ** 輸 出 : NONE ** 返 回 : ERROR_CODE *********************************************************************************************************/ 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(); /* 使能定時器10 */ __HAL_RCC_GPIOF_CLK_ENABLE(); /* 開啓GPIOF時鐘 */ GPIO_Initure.Pin = GPIO_PIN_6; /* PF6 */ GPIO_Initure.Mode = GPIO_MODE_AF_PP; /* 複用推完輸出 */ GPIO_Initure.Pull = GPIO_PULLDOWN; /* 上拉 */ GPIO_Initure.Speed = GPIO_SPEED_HIGH; /* 高速 */ GPIO_Initure.Alternate = GPIO_AF3_TIM10; /* PF6 複用爲TIM10 */ HAL_GPIO_Init(GPIOF, &GPIO_Initure); _G_timHandler[uiIndex].Instance = TIM10; /* 定時器10 */ _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; /* 模式選擇PWM1 */ _G_timChannel[uiIndex].Pulse = PWM_ARR_DEFAULT / 2; /* 設置比較值,此值用來確定佔空比 */ /* 默認比較值爲自動重裝載值的一 */ /* 半,即佔空比爲50% */ _G_timChannel[uiIndex].OCPolarity = TIM_OCPOLARITY_HIGH; /* 輸出比較極性爲高 */ break; case HW_PWM1: __HAL_RCC_TIM11_CLK_ENABLE(); /* 使能定時器11 */ __HAL_RCC_GPIOF_CLK_ENABLE(); /* 開啓GPIOF時鐘 */ GPIO_Initure.Pin = GPIO_PIN_7; /* PF7 */ GPIO_Initure.Mode = GPIO_MODE_AF_PP; /* 複用推完輸出 */ GPIO_Initure.Pull = GPIO_PULLDOWN; /* 上拉 */ GPIO_Initure.Speed = GPIO_SPEED_HIGH; /* 高速 */ GPIO_Initure.Alternate = GPIO_AF3_TIM11; /* PF7 複用爲TIM11 */ HAL_GPIO_Init(GPIOF, &GPIO_Initure); _G_timHandler[uiIndex].Instance = TIM11; /* 定時器11 */ _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; /* 模式選擇PWM1 */ _G_timChannel[uiIndex].Pulse = PWM_ARR_DEFAULT / 2; /* 設置比較值,此值用來確定佔空比 */ /* 默認比較值爲自動重裝載值的一 */ /* 半,即佔空比爲50% */ _G_timChannel[uiIndex].OCPolarity = TIM_OCPOLARITY_HIGH; /* 輸出比較極性爲高 */ break; case HW_PWM2: __HAL_RCC_TIM13_CLK_ENABLE(); /* 使能定時器13 */ __HAL_RCC_GPIOF_CLK_ENABLE(); /* 開啓GPIOF時鐘 */ GPIO_Initure.Pin = GPIO_PIN_8; /* PF8 */ GPIO_Initure.Mode = GPIO_MODE_AF_PP; /* 複用推完輸出 */ GPIO_Initure.Pull = GPIO_PULLDOWN; /* 上拉 */ GPIO_Initure.Speed = GPIO_SPEED_HIGH; /* 高速 */ GPIO_Initure.Alternate = GPIO_AF9_TIM13; /* PF8 複用爲TIM13 */ HAL_GPIO_Init(GPIOF, &GPIO_Initure); _G_timHandler[uiIndex].Instance = TIM13; /* 定時器13 */ _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; /* 模式選擇PWM1 */ _G_timChannel[uiIndex].Pulse = PWM_ARR_DEFAULT / 2; /* 設置比較值,此值用來確定佔空比 */ /* 默認比較值爲自動重裝載值的一 */ /* 半,即佔空比爲50% */ _G_timChannel[uiIndex].OCPolarity = TIM_OCPOLARITY_HIGH; /* 輸出比較極性爲高 */ break; case HW_PWM3: __HAL_RCC_TIM14_CLK_ENABLE(); /* 使能定時器14 */ __HAL_RCC_GPIOF_CLK_ENABLE(); /* 開啓GPIOF時鐘 */ GPIO_Initure.Pin = GPIO_PIN_9; /* PF9 */ GPIO_Initure.Mode = GPIO_MODE_AF_PP; /* 複用推完輸出 */ GPIO_Initure.Pull = GPIO_PULLDOWN; /* 上拉 */ GPIO_Initure.Speed = GPIO_SPEED_HIGH; /* 高速 */ GPIO_Initure.Alternate = GPIO_AF9_TIM14; /* PF9 複用爲TIM14 */ HAL_GPIO_Init(GPIOF, &GPIO_Initure); _G_timHandler[uiIndex].Instance = TIM14; /* 定時器14 */ _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; /* 模式選擇PWM1 */ _G_timChannel[uiIndex].Pulse = PWM_ARR_DEFAULT / 2; /* 設置比較值,此值用來確定佔空比 */ /* 默認比較值爲自動重裝載值的一 */ /* 半,即佔空比爲50% */ _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]); /* 初始化PWM */ HAL_TIM_PWM_ConfigChannel(&_G_timHandler[uiIndex], &_G_timChannel[uiIndex], TIM_CHANNEL_1); /* 配置通道 */ HAL_TIM_PWM_Start(&_G_timHandler[uiIndex], TIM_CHANNEL_1); /* 開啓PWM通道 */ }
3.2 close
close操作主要就是關閉PWM波的輸出,如程序清單 3.2所示。
程序清單 3.2 close操作
/********************************************************************************************************* ** 函數名稱: __pwmClose ** 功能描述: 關閉 PWM 設備 ** 輸 入 : pFdEntry 文件結構 ** 輸 出 : NONE ** 返 回 : ERROR_CODE *********************************************************************************************************/ 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); /* 關閉PWM通道 */ LW_DEV_DEC_USE_COUNT(&pPwmDev->PWMC_devHdr); return (PX_ERROR); }
3.3 ioctl
ioctl操作主要通過接口__timPwmPrescaleSet()、__timPwmDutySet()配置PSC、CCR兩個寄存器,用於設置頻率與佔空比,如程序清單 3.3所示。
程序清單 3.3 頻率、佔空比配置
/********************************************************************************************************* ** 函數名稱: __timPwmPrescaleSet ** 功能描述: pwm預分頻設置 ** PWM0/1與PWM2/3頻率計算方式不一樣,這與技術手冊不一致,疑似硬件BUG ** PWM0、PWM1頻率設置: ** 頻率(Hz)= 216000000 / 2500 / (uiPsc + 1) ** PWM2、PWM3頻率設置: ** 頻率(Hz)= 216000000 / 2 / 2500 / (uiPsc + 1) ** 輸 入 : uiIndex PWM 設備控制器序號 ** 輸 出 : NONE ** 返 回 : ERROR_CODE *********************************************************************************************************/ 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; /* 頻率(Hz) = 216000000 */ /* / (PWM_ARR_DEFAULT + 1) */ /* / (uiPsc + 1) */ HAL_TIM_PWM_Init(&_G_timHandler[uiIndex]); /* 初始化PWM */ HAL_TIM_PWM_ConfigChannel(&_G_timHandler[uiIndex], &_G_timChannel[uiIndex], TIM_CHANNEL_1); /* 配置通道 */ HAL_TIM_PWM_Start(&_G_timHandler[uiIndex], TIM_CHANNEL_1); /* 開啓PWM通道 */ } /********************************************************************************************************* ** 函數名稱: __timPwmDutySet ** 功能描述: pwm佔空比設置 ** 佔空比(%)= uiDuty / (2500 - 1) ** 輸 入 : uiIndex PWM 設備控制器序號 ** 輸 出 : NONE ** 返 回 : ERROR_CODE *********************************************************************************************************/ 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; /* 佔空比(%) = uiDuty */ /* / PWM_ARR_DEFAULT */ HAL_TIM_PWM_Init(&_G_timHandler[uiIndex]); /* 初始化PWM */ HAL_TIM_PWM_ConfigChannel(&_G_timHandler[uiIndex], &_G_timChannel[uiIndex], TIM_CHANNEL_1); /* 配置通道 */ HAL_TIM_PWM_Start(&_G_timHandler[uiIndex], TIM_CHANNEL_1); /* 開啓PWM通道 */ }