一、简介
基于华大(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;
}
}
}