华大 MCU 串口 PWM 控制方案完整笔记
一、简介
基于华大(HDSC)MCU 的 USART1 串口通信,实现 PWM 频率、占空比、自动调频 三参数在线修改。
支持 115200 bps、中断接收、回显校验、字符串解析、范围检查与错误提示。
1.1、硬件资源
引脚 | 功能 | 说明 |
---|---|---|
PA13 | USART1_TX | 复用推挽输出 |
PA14 | USART1_RX | 复用推挽输出 |
1.2、软件流程
上电
├─ GPIO 初始化(复用功能)
├─ USART1 初始化(115200-8-N-1)
├─ NVIC 使能(RXNE 中断)
└─ 等待串口命令 → 中断接收 → 解析 → 更新 PWM
1.3、 协议格式(单行 ASCII)
字段 | 范围 | 示例 |
---|---|---|
PWMFrequency | 2200–4000 Hz | PWMFrequency=3200HZ |
PWMDutyCycle | 0–100 % | PWMDutyCycle=50% |
PWMAutoFrequency | 0 关闭 / 1 开启 | PWMAutoFrequency=0 |
关键字大小写敏感;多余字符自动忽略;以 \n 结束。
二、代码
2.1、GPIO 初始化(华大库)
/**
* @brief 调试串口 IO 初始化
* @retval 无
*/
static void debugUartGpioInit(void)
{std_gpio_init_t usart_gpio_init = {0};/* 开启 GPIOA 时钟 */std_rcc_gpio_clk_enable(RCC_PERIPH_CLK_GPIOA);/* PA13 -> TX , PA14 -> RX */usart_gpio_init.pin = GPIO_PIN_13 | GPIO_PIN_14;usart_gpio_init.mode = GPIO_MODE_ALTERNATE;usart_gpio_init.output_type = GPIO_OUTPUT_PUSHPULL;usart_gpio_init.pull = GPIO_PULLUP;usart_gpio_init.alternate = GPIO_AF1_USART1;std_gpio_init(GPIOA, &usart_gpio_init);
}
2.2、 USART1 初始化
/**
* @brief 调试 USART1 初始化
* @retval 无
*/
void debugUsart1Init(void)
{debugUartGpioInit(); /* 引脚配置 */System_Fun.DelayMs(2000); /* 延时防止烧录失效 *//* 开启 USART1 时钟 */std_rcc_apb2_clk_enable(RCC_PERIPH_CLK_USART1);std_usart_init_t usart_init = {0};usart_init.direction = USART_DIRECTION_SEND_RECEIVE;usart_init.baudrate = 115200;usart_init.wordlength = USART_WORDLENGTH_8BITS;usart_init.stopbits = USART_STOPBITS_1;usart_init.parity = USART_PARITY_NONE;usart_init.hardware_flow = USART_FLOWCONTROL_NONE;if (STD_OK != std_usart_init(USART1, &usart_init))while (1); /* 波特率错误死循环 */std_usart_enable(USART1);/* NVIC 配置 */NVIC_SetPriority(USART1_IRQn, NVIC_PRIO_1);NVIC_EnableIRQ(USART1_IRQn);/* 使能接收中断 */std_usart_cr1_interrupt_enable(USART1, USART_CR1_INTERRUPT_RXNE);
}
2.3、 printf 重定向(阻塞式)
int fputc(int ch, FILE *f)
{(void)f;uint32_t delay = 0;while (std_usart_get_flag(USART1, USART_FLAG_TC) == RESET) {__nop(); __nop(); __nop(); __nop();if (++delay >= 200000) break;}std_usart_tx_write_data(USART1, (char)ch);std_usart_clear_flag(USART1, USART_CLEAR_TC);return ch;
}
2.4、 字符串提取函数
// 从 str 中查找 key,把 key 后直到 , \n \r 的字符拷贝到 value_str
int extractValue(const char *str, const char *key,char *value_str, int value_str_size)
{char *token = strstr(str, key);if (token == NULL) return -1; /* 未找到键 */token += strlen(key);int i = 0;while (*token != ',' && *token != '\0' &&*token != '\n' && *token != '\r' &&i < value_str_size - 1) {value_str[i++] = *token++;}value_str[i] = '\0';return 0;
}
2.5、 命令解析与 PWM 更新
extern uint32_t TIM_PERIOD_VALUE; /* 定时器自动重载值 */
extern uint32_t TIM_PULSE0_VALUE; /* 占空比比较值 */
extern uint8_t AUTO_FRE_VALUE; /* 自动调频开关 */void debugParsePwmCommand(void)
{char *str = (char *)rx_buffer;char duty_str[10] = {0};char freq_str[10] = {0};char auto_str[10] = {0};int duty_value = 0, freq_value = 0, auto_value = 0;if (rx_done_flag == 1) {/* 频率 */if (extractValue(str, "PWMFrequency=", freq_str, sizeof(freq_str)) == 0) {freq_value = atoi(freq_str);printf("\r\n输出结果如下:\r\n");printf("\r\n\tPWM频率的值=%d\r\n", freq_value);if (freq_value >= 2200 && freq_value <= 4000) {TIM_PERIOD_VALUE = 1000000 / freq_value;printf("\tPWM频率的值设置成功\r\n");} else {printf("\tPWM频率的值超过范围\r\n");}} else {printf("\tPWM频率值未找到\r\n");}/* 占空比 */if (extractValue(str, "PWMDutyCycle=", duty_str, sizeof(duty_str)) == 0) {duty_value = atoi(duty_str);printf("\r\n\tPWM占空比的值=%d\r\n", duty_value);if (duty_value >= 0 && duty_value <= 100) {TIM_PULSE0_VALUE = TIM_PERIOD_VALUE * duty_value / 100;printf("\tPWM占空比的值设置成功\r\n");} else {printf("\tPWM占空比的值超过范围\r\n");}} else {printf("\tPWM占空比值未找到\r\n");}/* 自动调频 */if (extractValue(str, "PWMAutoFrequency=", auto_str, sizeof(auto_str)) == 0) {auto_value = atoi(auto_str);printf("\r\n\tPWM自动调频值=%d\r\n", auto_value);if (auto_value >= 0 && auto_value <= 1) {AUTO_FRE_VALUE = auto_value;printf("\tPWM自动调频值设置成功\r\n");} else {printf("\tPWM自动调频值超过范围\r\n");}} else {printf("\tPWM自动调频值未找到\r\n");}printf("\r\n/************************************************/\r\n");printf("\r\n输入提示,在输入框内输入数据(警告,固定频率,必须设置PWMAutoFrequency=0),示例如下:\r\n");printf("PWMFrequency=3200HZ,PWMDutyCycle=50%%,PWMAutoFrequency=0\r\n\r\n");printf("\r\n/************************************************/\r\n\r\n\r\n");rx_done_flag = 0;}
}
2.6、 中断接收(循环缓冲区)
警告:以下只是个示例,不要在中断中处理数据和使用printf。
#define RX_BUFFER_SIZE 256
uint8_t rx_buffer[RX_BUFFER_SIZE];
volatile uint16_t rx_index = 0;
volatile uint8_t rx_done_flag = 0;
uint8_t received_char;/**
* @brief USART1 中断服务函数
* @retval 无
*/
void USART1_IRQHandler(void)
{/* 清除错误标志 */if (std_usart_get_flag(USART1, USART_FLAG_PE))std_usart_clear_flag(USART1, USART_CLEAR_PE);if (std_usart_get_flag(USART1, USART_FLAG_FE))std_usart_clear_flag(USART1, USART_CLEAR_FE);if (std_usart_get_flag(USART1, USART_FLAG_ORE))std_usart_clear_flag(USART1, USART_CLEAR_ORE);/* 接收数据 */if (std_usart_get_cr1_interrupt_enable(USART1, USART_CR1_INTERRUPT_RXNE) &&std_usart_get_flag(USART1, USART_FLAG_RXNE)) {received_char = (uint8_t)std_usart_rx_read_data(USART1);std_usart_tx_write_data(USART1, received_char); /* 回显 */if (received_char == '\n') { /* 帧结束 */rx_buffer[rx_index] = '\0';rx_done_flag = 1;debugParsePwmCommand(); /* 立即解析 */rx_index = 0;} else if (received_char == '\r') {/* 忽略回车 */} else if (rx_index < RX_BUFFER_SIZE - 1) {rx_buffer[rx_index++] = received_char;}}
}