[STM32WBA] 【NUCLEO-WBA65RI 测评】+串口控制PWM波输出

[复制链接]
228|0
LiuDW091 发表于 2025-12-12 11:49 | 显示全部楼层 |阅读模式
本次实验实现通过串口发送命令控制PWM波的占空比,并通过示波器测量


一、硬件
USART1:RX-----PA8;TX------PB12
TIM4的4通道-------PB9
52263693b85f2c6fd5.png 82391693b860fdf027.png
90888693b8625b1ba0.png
二、IDE软件设置
USART1:
49411693b86d25c686.png
TIM4:
19452693b86aa1b1d2.png
其他设置照旧,不在累赘了

三、代码
main函数

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

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_DUTY 1000  // 最大占空比值
#define RX_BUFFER_SIZE 128  // 增大缓冲区

uint16_t current_duty = 500;  // 当前占空比,初始50%
uint8_t rx_buffer[RX_BUFFER_SIZE];  // 用于存储接收的字符
uint16_t rx_index = 0;              // 缓冲区索引
uint8_t cmd_ready = 0;              // 命令就绪标志
uint8_t uart_rx_byte;               // 单字节接收缓冲区

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

void Set_PWM_DutyCycle(uint16_t duty);
void Set_PWM_DutyCycle_Percent(uint8_t percent);
void Increase_Duty(uint16_t step);
void Decrease_Duty(uint16_t step);
uint16_t Get_Current_Duty(void);
uint8_t Get_Current_Duty_Percent(void);
void PWM_Demo(void);
void Set_PWM_Mode(uint8_t mode);
void UART_ProcessCommand(void);
void UART_ShowHelp(void);
void UART_ShowStatus(void);

// 串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

// 重定向printf到串口
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

PUTCHAR_PROTOTYPE
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}


/* USER CODE END 0 */


/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM4_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);

  // 设置初始占空比
  Set_PWM_DutyCycle(current_duty);

  // 使能全局中断
   __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);

  // 清空串口缓冲区
  __HAL_UART_FLUSH_DRREGISTER(&huart1);

  // 启用串口接收中断
  HAL_UART_Receive_IT(&huart1, &uart_rx_byte, 1);

  // 显示欢迎信息
  printf("\r\n\r\n=== PWM Controller ===\r\n");
  printf("USART3: PB12(TX), PA8(RX)\r\n");
  printf("PWM: TIM4_CH4, Frequency: 2kHz\r\n");
  printf("Baud Rate: 9600 (Stable)\r\n");
  printf("Initial Duty Cycle: %d%%\r\n", Get_Current_Duty_Percent());
  printf("Type 'help' for command list\r\n");
  printf("> ");
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
            // 检查命令就绪标志
            if (cmd_ready) {
                cmd_ready = 0;
                printf("\r\nExecuting: '%s'\r\n", rx_buffer);
                UART_ProcessCommand();
                rx_index = 0;
                printf("> ");
            }
            HAL_Delay(1);


  }
  /* USER CODE END 3 */
}


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

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

  /** Configure the main internal regulator output voltage
  */
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEDiv = RCC_HSE_DIV1;
  RCC_OscInitStruct.PLL1.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_PCLK7|RCC_CLOCKTYPE_HCLK5;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB7CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.AHB5_PLL1_CLKDivider = RCC_SYSCLK_PLL1_DIV1;
  RCC_ClkInitStruct.AHB5_HSEHSI_CLKDivider = RCC_SYSCLK_HSEHSI_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}




/**
  * @brief 串口接收中断回调函数
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1) {
        // 立即回显字符
        HAL_UART_Transmit(&huart1, &uart_rx_byte, 1, HAL_MAX_DELAY);

        if (uart_rx_byte == '\r' || uart_rx_byte == '\n') {
            // 处理回车/换行
            if (rx_index > 0) {
                rx_buffer[rx_index] = '\0';
                cmd_ready = 1;  // 设置命令就绪标志
            }
        } else {
            // 存储普通字符
            if (rx_index < RX_BUFFER_SIZE - 1) {
                rx_buffer[rx_index++] = uart_rx_byte;
            } else {
                // 缓冲区满
                printf("\r\nError: Command too long!\r\n");
                rx_index = 0;
                printf("> ");
            }
        }

        // 重新启用接收中断
        HAL_UART_Receive_IT(&huart1, &uart_rx_byte, 1);
    }
}

/**
  * @brief 设置PWM占空比(直接值)
  * @param duty: 占空比值 (0-1000)
  */
void Set_PWM_DutyCycle(uint16_t duty)
{
    if(duty > MAX_DUTY) duty = MAX_DUTY;
    current_duty = duty;
    __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, duty);
}

/**
  * @brief 设置PWM占空比(百分比)
  * @param percent: 占空比百分比 (0-100)
  */
void Set_PWM_DutyCycle_Percent(uint8_t percent)
{
    if(percent > 100) percent = 100;
    uint16_t duty = (percent * MAX_DUTY) / 100;
    Set_PWM_DutyCycle(duty);
}

/**
  * @brief 增加占空比
  * @param step: 增加的步长
  */
void Increase_Duty(uint16_t step)
{
    if(current_duty + step <= MAX_DUTY) {
        current_duty += step;
    } else {
        current_duty = MAX_DUTY;
    }
    Set_PWM_DutyCycle(current_duty);
}

/**
  * @brief 减少占空比
  * @param step: 减少的步长
  */
void Decrease_Duty(uint16_t step)
{
    if(current_duty >= step) {
        current_duty -= step;
    } else {
        current_duty = 0;
    }
    Set_PWM_DutyCycle(current_duty);
}

/**
  * @brief 获取当前占空比
  * @retval 当前占空比值
  */
uint16_t Get_Current_Duty(void)
{
    return current_duty;
}

/**
  * @brief 获取当前占空比百分比
  * @retval 当前占空比百分比
  */
uint8_t Get_Current_Duty_Percent(void)
{
    return (current_duty * 100) / MAX_DUTY;
}

/**
  * @brief PWM演示函数 - 呼吸灯效果
  */
void PWM_Demo(void)
{
    printf("Starting PWM Demo (Breathing Effect)...\r\n");

    // 逐渐增加亮度
    for(uint16_t i = 0; i <= MAX_DUTY; i += 10) {
        Set_PWM_DutyCycle(i);
        HAL_Delay(20);
    }

    // 逐渐减少亮度
    for(uint16_t i = MAX_DUTY; i > 0; i -= 10) {
        Set_PWM_DutyCycle(i);
        HAL_Delay(20);
    }

    printf("PWM Demo Finished.\r\n");
}

/**
  * @brief 设置特定占空比模式
  * @param mode: 模式选择
  *  0: 25%占空比
  *  1: 50%占空比
  *  2: 75%占空比
  *  3: 100%占空比
  */
void Set_PWM_Mode(uint8_t mode)
{
    switch(mode) {
        case 0: Set_PWM_DutyCycle_Percent(25); break;
        case 1: Set_PWM_DutyCycle_Percent(50); break;
        case 2: Set_PWM_DutyCycle_Percent(75); break;
        case 3: Set_PWM_DutyCycle_Percent(100); break;
        default: Set_PWM_DutyCycle_Percent(50); break;
    }
}

/**
  * @brief 显示帮助信息
  */
void UART_ShowHelp(void)
{
    printf("\r\n=== PWM Control Commands ===\r\n");
    printf("set <0-100>    - Set duty cycle percentage\r\n");
    printf("inc <value>    - Increase duty cycle\r\n");
    printf("dec <value>    - Decrease duty cycle\r\n");
    printf("demo           - Run PWM demo (breathing effect)\r\n");
    printf("mode <0-3>     - Set preset mode (0:25%%, 1:50%%, 2:75%%, 3:100%%)\r\n");
    printf("status         - Show current status\r\n");
    printf("help           - Show this help message\r\n");
    printf("==============================\r\n");
}

/**
  * @brief 显示当前状态
  */
void UART_ShowStatus(void)
{
    printf("\r\n=== PWM Status ===\r\n");
    printf("Current Duty: %d/%d (%d%%)\r\n",
           Get_Current_Duty(), MAX_DUTY, Get_Current_Duty_Percent());
    printf("PWM Frequency: 2kHz\r\n");
    printf("UART Baud Rate: 9600\r\n");
    printf("===================\r\n");
}

/**
  * @brief 处理串口命令
  */
void UART_ProcessCommand(void)
{
    char *token;
    char *cmd;
    int value;

    // 分割命令和参数
    token = strtok((char *)rx_buffer, " ");
    cmd = token;

    if (cmd == NULL) {
        printf("Error: Empty command\r\n");
        return;
    }

    if (strcmp(cmd, "help") == 0) {
        UART_ShowHelp();
    }
    else if (strcmp(cmd, "status") == 0) {
        UART_ShowStatus();
    }
    else if (strcmp(cmd, "demo") == 0) {
        PWM_Demo();
    }
    else if (strcmp(cmd, "set") == 0) {
        token = strtok(NULL, " ");
        if (token != NULL) {
            value = atoi(token);
            if (value >= 0 && value <= 100) {
                Set_PWM_DutyCycle_Percent(value);
                printf("Duty cycle set to %d%%\r\n", value);
            } else {
                printf("Error: Value must be between 0-100\r\n");
            }
        } else {
            printf("Error: Missing value. Usage: set <0-100>\r\n");
        }
    }
    else if (strcmp(cmd, "inc") == 0) {
        token = strtok(NULL, " ");
        if (token != NULL) {
            value = atoi(token);
            if (value > 0) {
                Increase_Duty(value);
                printf("Duty cycle increased by %d, now %d%%\r\n", value, Get_Current_Duty_Percent());
            } else {
                printf("Error: Value must be positive\r\n");
            }
        } else {
            printf("Error: Missing value. Usage: inc <value>\r\n");
        }
    }
    else if (strcmp(cmd, "dec") == 0) {
        token = strtok(NULL, " ");
        if (token != NULL) {
            value = atoi(token);
            if (value > 0) {
                Decrease_Duty(value);
                printf("Duty cycle decreased by %d, now %d%%\r\n", value, Get_Current_Duty_Percent());
            } else {
                printf("Error: Value must be positive\r\n");
            }
        } else {
            printf("Error: Missing value. Usage: dec <value>\r\n");
        }
    }
    else if (strcmp(cmd, "mode") == 0) {
        token = strtok(NULL, " ");
        if (token != NULL) {
            value = atoi(token);
            if (value >= 0 && value <= 3) {
                Set_PWM_Mode(value);
                const char *modes[] = {"25%", "50%", "75%", "100%"};
                printf("Mode set to %s\r\n", modes[value]);
            } else {
                printf("Error: Mode must be 0-3\r\n");
            }
        } else {
            printf("Error: Missing mode. Usage: mode <0-3>\r\n");
        }
    }
    else {
        printf("Error: Unknown command '%s'\r\n", cmd);
        printf("Type 'help' for available commands\r\n");
    }
}



/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */





四、实验效果
视频:
https://www.bilibili.com/video/BV1unm7BSEUN/
输入help,串口显示:

help
>
Executing: 'help'

=== PWM Control Commands ===
set <0-100>    - Set duty cycle percentage
inc <value>    - Increase duty cycle
dec <value>    - Decrease duty cycle
demo           - Run PWM demo (breathing effect)
mode <0-3>     - Set preset mode (0:25%, 1:50%, 2:75%, 3:100%)
status         - Show current status
help           - Show this help message
==============================


输入相应命令,PWM波占空比将进行相应变化。

set <0-100>    - 直接设置占空比
inc <value>    - 增加占空比
dec <value>    - 较少占空比
demo           - 运行demo里波形(占空比变化的PWM波)
mode <0-3>     - 设置模式(0:25%, 1:50%, 2:75%, 3:100%)
status         - 显示波形信息
help           - Show this help message

您需要登录后才可以回帖 登录 | 注册

本版积分规则

179

主题

713

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部
0