华大 MCU 串口 PWM 控制方案完整笔记

发布于:2025-09-09 ⋅ 阅读:(21) ⋅ 点赞:(0)

一、简介

  基于华大(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;
        }
    }
}

三、运行示例

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到