HAL+Cube MX 学习之PWM

记录HAL库+Cube MX的学习过程,不定期更新……

一、PWM’s introduction

PWM(Pulse width modulation脉冲宽度调制)是一种** 用数字信号控制模拟信号** 的一种技术,通过产生的高低电平(矩形波)来控制引脚的输出,从而可以控制lcd的亮与灭等,PWM是stm32定时器的一个重要应用,本文以产生一个频率为10kHz,占空比为20%为例说明。

二、Clock Configuration

首先进行时钟的配置,如图,在System view界面点击RCC进入时钟设置(与时钟有关的设置都在RCC中,不论是Cube MX或是库函数开发),HSE(外部高速时钟)选择Crystal/Ceramic Resonator(水晶/陶瓷 谐振器,可理解为时钟来源选择为外部晶振)(后续文章中可能会有关于晶振的学习记录,敬请期待!)
在这里插入图片描述
在这里插入图片描述
现在就可以将时钟频率设置为最大,stm32f103RC频率最大为72M。如果时钟来源不选择为外部晶振的话,频率最大只能选择为64M,可自行尝试。
设置时钟频率

三、Enable PWM

在左侧categories(分类)中的Times选项中选择TIM1(其它的也都可以),并在弹出的TIM1 Mode and Configuration中设置时钟来源为Internal Clock(内部时钟),Channel(通道)里选择PWM Generation CH1,至此所有的设置已经完成了。在设置Channel后Cube MX已经为产生PWM配置了对应的引脚 ,TIM通道以及Channel选择不同时引脚也会变化。
值得注意的是,PWM产生的操作不是对引脚GPIO的操作,所以不要在Pinout view中对芯片引脚进行操作。
在这里插入图片描述

四、PWM’s Configuration

要产生PWM有两个最重要的因素,一个是频率(即周期),一个是占空比(由于引脚产生的高电平时确定的,一般为3.3V,PWM的幅值也是确定的,在此不考虑)。我们要配置的主要是Counter Settings(计数设置)中的Prescaler(预分频)、Counter Period(计数周期),PWM Generation Channel1中的Pulse。
首先来理解PWM在单片机内部产生的机制,如图1,斜线表示定时器的计数,假设记到1000再从0开始重新计数,单片机的机制是设置一个比较值(Comparison value),其中一种方案是,当计数值小于这个值时,对相应引脚赋为低电平,计数值大于这个值时赋为高电平,于是引脚的输出就变成了图2,以此来实现PWM的输出,并通过改变比较值来调节PWM的占空比。

在这里插入图片描述
这里还有一个问题,就是前面说的计数值应该为多少合适,假设计数值只有10个,那么当比较值变化时,占空比就只能在在0%到100%之间以10%的幅度变化,如果,计数值为1000,那么当比较值变化时,占空比就能够在0%到100%之间以0.1%的幅度进行变化。计数值取得越大,PWM占空比精度越高,可以根据需要进行调节。

时钟原本的频率为72M,这里设置精度尽量高,Prescaler(预分频)设为1/8,Counter Period设为900 - 1,这样计数值为900,能保证精度。
在这里插入图片描述
下面设置比较值,900 x 20% = 180,所以这个值为180。这里要特别注意,因为前面在使能PWM时Channel1里设置的是PWM Generation CH1,意思是计数值小于比较值输出高电平,高于比较值输出低电平,而Channel1里的另一个选项PWM Generation CH1 N是取反的意思,勾选这个之后计数值小于比较值时输出低电平,高于计数值时输出高电平,与前文的讲述一致。(这里与我们的日常认知不同,重点

五、Generate Code

在对Project进行一些命名等设置之后就可以直接生成代码了
在这里插入图片描述

在这里插入图片描述

六、Coding in MDK

在keil中需要写的代码很简单,简单到只有一行:HAL_TIM_PWM_Start(&htimx,TIM_CHANNEL_y); // 使能timx的通道y
这里用的是TIM1的Channel1,所以只需要把HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); 插入到int main()中即可(注意要写到main中,否则会报错)
在这里插入图片描述

七、Final Effect

用示波器对PA0进行测量(TIM1的Channel1对应的引脚为PA0)
在这里插入图片描述
在这里插入图片描述

哈,成功产生一个频率为20%的pwm!

————————————————2019.08.27———————————————

Supplement

通过另一个HAL库函数可以直接修改比较值从而起到改变PWM占空比的作用

__HAL_TIM_SET_COMPARE(&htimx, TIM_CHANNEL_y, z); // 修改timx的通道y的pwm比较值为z

比如想要让PWM占空比变为50%,只需修改z的值为450即可。通过这个函数,还可以让PWM占空比实时变化,只需要让z的值不断变化即可。(身边没有示波器,可自行验证……)
在这里插入图片描述

  • 24
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
好的,以下是详细步骤: 1. 打开STM32CubeIDE,创建一个新工程,选择芯片型号为STM32F103RCT6。 2. 在工程中添加需要的库文件,包括HAL库和CMSIS库。 3. 打开STM32CubeMX,选择该芯片型号,进行图形化配置。 4. 在Pinout选项卡中,选择需要使用的引脚。本例中,我们需要使用PA0引脚输出PWM信号,使用PA1和PA2引脚控制L298N模块。 5. 在Configuration选项卡中,选择TIM2定时器,将其配置为PWM输出模式。 6. 在Code Generator选项卡中,选择HAL库,生成代码。 7. 打开生成的main.c文件,添加以下代码片段: ```c #include "main.h" #include "stm32f1xx_hal.h" TIM_HandleTypeDef htim2; UART_HandleTypeDef huart1; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM2_Init(void); static void MX_USART1_UART_Init(void); void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); MX_USART1_UART_Init(); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); uint8_t buffer[20]; while (1) { sprintf(buffer, "Speed: %d\r\n", __HAL_TIM_GET_COMPARE(&htim2, TIM_CHANNEL_1)); HAL_UART_Transmit(&huart1, buffer, strlen(buffer), HAL_MAX_DELAY); HAL_Delay(100); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 1000); HAL_Delay(1000); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 2000); HAL_Delay(1000); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } static void MX_TIM2_Init(void) { TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 71; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 39999; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } HAL_TIM_MspPostInit(&htim2); } static void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_TIM2_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim_pwm) { if (htim_pwm->Instance == TIM2) { __HAL_RCC_TIM2_CLK_ENABLE(); HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM2_IRQn); } } void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if (htim->Instance == TIM2) { __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } } void TIM2_IRQHandler(void) { HAL_TIM_IRQHandler(&htim2); } void Error_Handler(void) { __disable_irq(); while (1) { } } ``` 8. 连接L298N模块,将电机接到OUT1和OUT2端口。 9. 在main函数中,添加了一个死循环,每隔1秒改变PWM输出的占空比,将速度信息通过串口发送给电脑。 10. 通过USB线将STM32F103RCT6与电脑连接,使用串口调试工具可以读取速度信息。 以上就是使用STM32CubeIDE搭配CubeMXHAL库进行PWM输出驱动的步骤,希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值