学习GD32C113 -- 使用 GD32C113 UART 中断方式接收数据(读取激光测距传感器 L10 数据)

L10 传感器介绍如下:

introduction

通讯协议如下:

protocol

根据上述协议,要使用 GD32C113 读取这个传感器,要做的事有:

1、实现串口接收数据

2、在接收到完整一包数据后,把数据从 ASCII 转换成 十六进制

3、对接收到数据进行 CRC16 校验

4、校验没问题,获取数据,有问题丢弃

第一步,先实现串口接收功能,这里使用 GD32C113 的 UART1,对应 PA2、PA3,采用中断接收方式,初始化如下:

void uart1_init(void)
{
    /* enable GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);

    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART1);

    /* connect port to USARTx_Tx */
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);

    /* connect port to USARTx_Rx */
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3);

    /* USART configure */
    usart_deinit(USART1);
    usart_baudrate_set(USART1, 115200U);
    usart_receive_config(USART1, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
    usart_enable(USART1);
	
	usart_interrupt_enable(USART1, USART_INT_RBNE);
	nvic_irq_enable(USART1_IRQn, 0, 0);	
}

接收数据,这里使用了一个 ring buffer (环形队列)库:https://github.com/MaJerle/lwrb,首先定义 lwrb_t 和缓存数组:

lwrb_t hgps_buff;
uint8_t hgps_buff_data[64];

然后初始化 ring buffer:

/* Create buffer for received data */
lwrb_init(&hgps_buff, hgps_buff_data, sizeof(hgps_buff_data));

然后再 uart1 中断里面写 ringbuffer :

/*!
    \brief      this function handles USART RBNE interrupt request and TBE interrupt request
    \param[in]  none
    \param[out] none
    \retval     none
*/
void USART1_IRQHandler(void)
{
	uint8_t c = 0;
    if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) {
        /* receive data */
		c = usart_data_receive(USART1);
		lwrb_write(&hgps_buff, &c, 1);
    }
}

然后现在 main 函数主循环 里面输出 ringbufer 里面的数据,来确认下接收到的数据对不对:

    while(1) {
			/* Read from buffer byte-by-byte and call processing function */
			if (lwrb_get_full(&hgps_buff)) {        /* Check if anything in buffer now */
					while (lwrb_read(&hgps_buff, &rx, 1) == 1) {
						printf("%c",rx);
					}
			}
    }

在确保能接收完整数据后,根据协议提取有效数据:

    if( rx == '~')
    {
        step = 1;
    }else if(step == 1 && rx != '\r' && rx != '\n')
    {
        rx_buf_raw[index++] = rx;
    }else if(step == 1 && rx == '\r')
    {
        step = 2;
    }else if(step == 2 && rx == '\n')
    {
        step = 0;
    	printf("Got all data:");
    	printf("%s\r\n",rx_buf_raw);
        index = 0;
    }

由于原始数据使用 ASCII 表示 16 进制,需要把数据从 ASCII 转换成二进制,转换以下:

/**
 * @brief  ASCII 十六进制字符串 转 二进制十六进制数据
 * @param  ascii_hex: ASCII 十六进制字符串 (如 "1A3F")
 * @param  out_data:  输出二进制数据缓冲区
 * @param  max_len:   输出缓冲区最大长度
 * @return 成功返回转换后的数据长度,失败返回 -1
 * @note   输入字符串必须是**偶数长度**,否则无法成对转换
 */
int ascii_hex_to_bin(const char *ascii_hex, uint8_t *out_data, int max_len)
{
    // 1. 检查输入合法性
    if (ascii_hex == NULL || out_data == NULL || max_len <= 0)
        return -1;

    int str_len = strlen(ascii_hex);
    // 必须是偶数个字符(两个ASCII字符=1字节数据)
    if (str_len % 2 != 0)
        return -1;

    // 计算需要的输出长度
    int data_len = str_len / 2;
    if (data_len > max_len)
        return -1;

    // 2. 逐字节转换
    for (int i = 0; i < data_len; i++)
    {
        char high = ascii_hex[i * 2];      // 高4位字符
        char low  = ascii_hex[i * 2 + 1];  // 低4位字符
        uint8_t h_val, l_val;

        // 转换高4位
        if (isdigit(high))
            h_val = high - '0';
        else if (high >= 'A' && high <= 'F')
            h_val = 10 + (high - 'A');
        else if (high >= 'a' && high <= 'f')
            h_val = 10 + (high - 'a');
        else
            return -1;  // 非法字符

        // 转换低4位
        if (isdigit(low))
            l_val = low - '0';
        else if (low >= 'A' && low <= 'F')
            l_val = 10 + (low - 'A');
        else if (low >= 'a' && low <= 'f')
            l_val = 10 + (low - 'a');
        else
            return -1;  // 非法字符

        // 组合成1字节数据
        out_data[i] = (h_val << 4) | l_val;
    }

    return data_len;
}

然后还需要 CRC16 校验函数:

/**
 * @brief  CRC16-MODBUS 实现 (多项式 0x8005, 初始值 0xFFFF, 结果异或 0x0000)
 * @param  data: 待校验数据首地址
 * @param  length: 数据长度
 * @return 16位校验值
 */
uint16_t crc16_modbus(const uint8_t *data, size_t length)
{
    uint16_t crc = 0xFFFF;  // 初始值

    for (size_t i = 0; i < length; i++)
    {
        crc ^= data[i];  // MODBUS 与数据低8位异或

        for (uint8_t j = 0; j < 8; j++)
        {
            if (crc & 0x0001)
            {
                crc = (crc >> 1) ^ 0xA001;  // 0xA001是0x8005的位反转
            }
            else
            {
                crc = crc >> 1;
            }
        }
    }
    return crc;
}

最后如下:

		    /* Create buffer for received data */
    lwrb_init(&hgps_buff, hgps_buff_data, sizeof(hgps_buff_data));
	uart1_init();
    while(1) {
			/* Read from buffer byte-by-byte and call processing function */
			if (lwrb_get_full(&hgps_buff)) {        /* Check if anything in buffer now */
                while (lwrb_read(&hgps_buff, &rx, 1) == 1) {
                    if( rx == '~')
                    {
                        step = 1;
                    }else if(step == 1 && rx != '\r' && rx != '\n')
                    {
                        rx_buf_raw[index++] = rx;
                    }else if(step == 1 && rx == '\r')
                    {
                        step = 2;
                    }else if(step == 2 && rx == '\n')
                    {
                        step = 0;
                        ascii_hex_to_bin((const char *)rx_buf_raw,rx_buf,8);
                        check_crc = crc16_modbus(rx_buf,6);
                        crc_l = check_crc & 0x00ff;
                        crc16_h = check_crc >> 8;
                        if( crc_l == rx_buf[6] && crc16_h == rx_buf[7] )
                        {
                            printf("dis:%d\r\n",(rx_buf[4] << 8) + rx_buf[5]);
                        }
                        index = 0;
                    }
                }
			}
    }

运行结果:

res

参考:https://mp.weixin.qq.com/s/I3SAhSCMcQVcvsr1Ba1Qzg

posted @ 2026-04-20 22:50  哈拎  阅读(11)  评论(0)    收藏  举报