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

通讯协议如下:

根据上述协议,要使用 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;
}
}
}
}
运行结果:

本文来自博客园,作者:哈拎,转载请注明原文链接:https://www.cnblogs.com/halin/p/19897611
浙公网安备 33010602011771号