STM32H743通过PWM实现电机调速与呼吸灯

PWM

首先,让我们先了解一下PWM的概念与基本原理。
PWM(Pulse width modulation),即脉冲宽度调制,其基本原理是控制方式对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替正弦波或所需要的波形。即在输出波形的半个周期中产生多个脉冲,使各脉冲的等值电压为正弦波形,所获得的输出平滑且低次谐波少。按一定的规则对各脉冲的宽度进行调制,既可改变逆变电路输出电压的大小,也可改变输出频率。
例如,把正弦半波波形分成N等份,就可把正弦半波看成由N个彼此相连的脉冲所组成的波形。这些脉冲宽度相等,都等于 π/n ,但幅值不等,且脉冲顶部不是水平直线,而是曲线,各脉冲的幅值按正弦规律变化。如果把上述脉冲序列用同样数量的等幅而不等宽的矩形脉冲序列代替,使矩形脉冲的中点和相应正弦等分的中点重合,且使矩形脉冲和相应正弦部分面积(即冲量)相等,就得到一组脉冲序列,这就是PWM波形。可以看出,各脉冲宽度是按正弦规律变化的。根据冲量相等效果相同的原理,PWM波形和正弦半波是等效的。对于正弦的负半周,也可以用同样的方法得到PWM波形。
在PWM波形中,各脉冲的幅值是相等的,要改变等效输出正弦波的幅值时,只要按同一比例系数改变各脉冲的宽度即可,因此在交-直-交变频器中,PWM逆变电路输出的脉冲电压就是直流侧电压的幅值。

PWM中重要的参数是周期与占空比。
占空比 = 高电平持续时间 / PWM时钟周期。

电机驱动模块L298N

L298N是双H桥电机驱动芯片,可以驱动两个直流电机或一个步进电机,能实现电机的正反转以及调速。
L298N
OUT1、OUT2可接直流电机,OUT3、OUT4也可接直流电机。
使用+12V电源输入来驱动模块,GND接电源负极,注意此GND要与单片机的GND相连共地,+5V输入至单片机。
ENA、ENB为使能端口,其中ENA使能左边电机,ENB使能右边电机,(默认高电平使能)IN1~IN4为控制端口,IN1、IN2控制左边电机,IN3、IN4控制右边电机。
当IN1 = 0(低电平),IN2 = 1(高电平)时,直流电机正转,反之则反转;IN3、IN4同理。
我们可使用单片机输入一个高电平给控制端口以控制电机转向,原本使能端口为高电平时,电机可一直均速转动,我们要拆掉使能端口的跳帽,用单片机对应引脚接使能端口,从而可以使用PWM调节电机转速。

基本原理

定时器的应用场景为定时器中断、PWM输出、输入捕获。
STM32分为三种定时器:①高级定时器(TIM1、TIM8):用于PWM电机控制;②通用定时器(TIM2~TIM5、TIM12~TIM17):用于定时计数、PWM输出、输入捕获以及输出比较;③基本定时器(TIM6、TIM7):用于驱动DAC。
本次实验采用的是通用定时器TIM4。
每个通用定时器完全独立,各拥有4个独立通道(TIMx_CH1~4)。
使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
定时器结构分为时基单元、计数器模式、时钟选择与捕获/比较通道。
定时器相关寄存器为 当前值寄存器CNT、预分频寄存器TIMx_PSC、自动重装载寄存器TIMx_ARR、控制寄存器与DMA中断使能寄存器TIMx_DIER等。
下图为通用定时器框图。通用定时器框图
PWM波的占空比由CCRx决定,周期由ARR决定。在这里插入图片描述
在向上计数模式下,使用PWM2模式,则CNT > CCRx时为有效电平,极性设置为HIGH,则有效电平为高电平。当CCRx由大变小时,CNT > CCRx的部分逐渐变大,得到的输出脉冲宽度也逐渐变宽,由此可得,输出电压也逐渐变大,LED灯由暗逐渐变亮,呼吸灯由此而来!电机转速也会由慢逐渐变快,如果直接设定CCRx为固定值,那么电机的转速也会固定,其大小可由占空比计算得出,只需改变捕获比较值CCRx就可以实现电机的调速啦!

PWM输出配置步骤

① 使能定时器4和相关IO口时钟。
② 初始化IO口为复用功能输出。
③ 把PB7用作定时器的PWM输出引脚,要重映射配置,所以需要开启AFIO时钟,同时设置重映射。
④ 初始化定时器:ARR,PSC等。
⑤ 初始化输出比较参数。
⑥ 使能预装载寄存器。
⑦ 使能定时器。
⑧ 不断改变比较值CCRx,达到不同的占空比效果。

硬件连接

在这里插入图片描述
在这里插入图片描述
用电烙铁焊接电线与直流减速电机的引脚,从而引出两端引脚,接入L298N电机驱动模块的OUT3、OUT4;将12V的电池电源正负极分别连接至L298N的+12V与GND端口;用跳线连接单片机的GND与L298N的GND,实现共地;用跳线连接单片机的+5V与L298N的+5V,向单片机供电,TTL高电平5V。
通用定时器TIM4的通道TIM4_CH1、TIM4_CH2可以分别作为PB6、PB7的引脚复用,而PB6、PB7已与LED1、LED2用跳线连接。用跳线连接单片机的PB7至L298N模块的使能端口ENB(因为我使用的是右边的电机,故需用到ENB、IN3 / IN4);由单片机输入一个高电平至L298N的IN3或IN4,我点亮了LED3,而LED3对应的是GPIO的PH4,故我将PH4与IN3或IN4连接,即可供给5V的TTL高电平给控制端口。

MDK调试——代码及注释说明

“main.c”

#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

char i;
void SystemClock_Config(void);

int main(void)
{
    uint16_t i;  //定义无符号的16位整数,占2字节
    HAL_Init();  //重置所有外围设备,初始化闪存接口和Systick
    SystemClock_Config();  //配置系统时钟
/*初始化所有配置的外围设备*/
    MX_GPIO_Init(); 
    MX_USART1_UART_Init();
    MX_TIM4_Init();  //初始化通用定时器4

    printf("Waveshare STM32H7 PWM test\r\n");
    HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);  //使能定时器4通道1
    HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);  //使能定时器4通道2

    while (1)
    {
    HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); //LED3 ON
    i=i+3;
    if(i>1000){
        i = 0;
    }
    TIM4->CCR1 = i;
    TIM4->CCR2 = 1000-i;
    HAL_Delay(5);
    }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0); //Supply configuration update enable
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); //Configure the main internal regulator output voltage
  
  while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY) 
  {
    
  }

/* 初始化CPU、AHB和APB总线时钟 */

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 400;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  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_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART1;
  PeriphClkInitStruct.Usart16ClockSelection = RCC_USART16CLKSOURCE_D2PCLK2;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{

}

“tim.c” 定时器4初始化函数

void MX_TIM4_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 200-1;  //预分频装载值
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;  //向上计数
  htim4.Init.Period = 1000;  //周期,即ARR的值
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM2;  //PWM2模式,CNT > CCRx为有效电平
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;  //极性,有效电平为高电平
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim4);

}

“gpio.c” GPIO初始化函数

void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
	
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

	/*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
	
	  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED3_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED3_GPIO_Port, &GPIO_InitStruct);
}

“usart.c” 串口初始化函数

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;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.Init.Prescaler = UART_PRESCALER_DIV1;
  huart1.Init.FIFOMode = UART_FIFOMODE_DISABLE;
  huart1.Init.TXFIFOThreshold = UART_TXFIFO_THRESHOLD_1_8;
  huart1.Init.RXFIFOThreshold = UART_RXFIFO_THRESHOLD_1_8;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }

}

注意在相关头文件中定义相关函数或结构体。
#define xxx

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Stm32h743是一款高性能的微控制器,支持多种外设和通信接口,其中包括pwm输出。FreeRTOS是一种流行的实时操作系统,可以帮助你更好地管理任务和资源。 要在Stm32h743实现PWM,你需要配置定时器和GPIO口。具体步骤如下: 1. 配置定时器作为PWM生成器,选择适当的预分频系数和计数器自动重载值以产生所需的PWM频率和分辨率。 2. 配置GPIO口为定时器的输出通道,选择适当的GPIO口和复用功能以实现PWM输出。 3. 在FreeRTOS中编写任务来控制PWM输出,可以使用定时器中断或者定时器DMA方式。 以下是一个简单的示例代码: ```c #include "stm32h7xx_hal.h" #include "FreeRTOS.h" #include "task.h" #define PWM_FREQ 10000 // PWM频率 #define PWM_RES 256 // PWM分辨率 TIM_HandleTypeDef htim; uint16_t pwm_duty = 0; void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) { GPIO_InitTypeDef GPIO_InitStruct; if(htim->Instance==TIM1) { __HAL_RCC_TIM1_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF1_TIM1; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); } } void pwm_init(void) { TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; htim.Instance = TIM1; htim.Init.Prescaler = (HAL_RCC_GetHCLKFreq() / PWM_FREQ / PWM_RES) - 1; htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = PWM_RES - 1; htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim); sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig); sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = pwm_duty; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_MspInit(&htim); HAL_TIM_PWM_Start(&htim, TIM_CHANNEL_1); } void pwm_task(void *pvParameters) { while(1) { for(uint16_t i=0; i<PWM_RES; i++) { pwm_duty = i; __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_1, pwm_duty); vTaskDelay(pdMS_TO_TICKS(10)); } } } int main(void) { HAL_Init(); pwm_init(); xTaskCreate(pwm_task, "PWM Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); vTaskStartScheduler(); while(1); } ``` 以上代码使用TIM1作为PWM生成器,PE9口作为PWM输出口,通过一个任务不断改变占空比实现PWM输出。你可以根据具体需求调整代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值