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通道 */ }