本次实验实现通过串口发送命令控制PWM波的占空比,并通过示波器测量
一、硬件
USART1:RX-----PA8;TX------PB12
TIM4的4通道-------PB9
二、IDE软件设置
USART1:
TIM4:
其他设置照旧,不在累赘了
三、代码
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
|