以下是基于GD32F4XX串口,使用sscanf
函数、strstr
和strcmp
,并结合环形缓冲区在中断中写数据,主函数处理发送接收的代码示例:
串口初始化与环形缓冲区定义
#include <gd32f4xx.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE 256 // 环形缓冲区大小
// 环形缓冲区结构体
typedef struct {
uint8_t buffer[BUF_SIZE];
uint16_t write_idx;
uint16_t read_idx;
} RingBuffer;
RingBuffer rx_ring_buf;
// 初始化串口
void usart_init(uint32_t baudrate) {
rcu_periph_clock_enable(RCU_USART0);
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
usart_deinit(USART0);
usart_baudrate_config(USART0, baudrate);
usart_word_length_config(USART0, USART_WL_8BIT);
usart_stop_bit_config(USART0, USART_SBT_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
// 开启中断
usart_interrupt_enable(USART0, USART_INT_RBNE);
nvic_priority_group_config(NVIC_PRIORITY_GROUP_2);
nvic_irq_enable(USART0_IRQn, 0, 0);
}
// 初始化环形缓冲区
void ring_buffer_init(RingBuffer *ring_buf) {
ring_buf->write_idx = 0;
ring_buf->read_idx = 0;
}
// 写入环形缓冲区
void ring_buffer_write(RingBuffer *ring_buf, uint8_t data) {
ring_buf->buffer[ring_buf->write_idx] = data;
ring_buf->write_idx = (ring_buf->write_idx + 1) % BUF_SIZE;
}
// 从环形缓冲区读取数据
uint8_t ring_buffer_read(RingBuffer *ring_buf) {
uint8_t data = ring_buf->buffer[ring_buf->read_idx];
ring_buf->read_idx = (ring_buf->read_idx + 1) % BUF_SIZE;
return data;
}
// 环形缓冲区是否为空
uint8_t ring_buffer_is_empty(RingBuffer *ring_buf) {
return (ring_buf->read_idx == ring_buf->write_idx);
}
串口接收中断处理
// 串口中断服务函数
void USART0_IRQHandler(void) {
if (usart_flag_get(USART0, USART_FLAG_RBNE) != RESET) {
uint8_t received_data = usart_data_receive(USART0);
ring_buffer_write(&rx_ring_buf, received_data);
}
}
北斗协议解析与发送
// 北斗协议解析函数
void parse_bds_sentence() {
static char sentence_buf[256];
static uint8_t sentence_idx = 0;
char *token;
while (!ring_buffer_is_empty(&rx_ring_buf)) {
uint8_t data = ring_buffer_read(&rx_ring_buf);
sentence_buf[sentence_idx++] = data;
if (data == '\n' || sentence_idx >= sizeof(sentence_buf)) {
sentence_buf[sentence_idx] = '\0';
sentence_idx = 0;
// 找到开头的$符号
char *start_pos = strstr(sentence_buf, "$");
if (start_pos) {
// 提取指令关键字
char command[16];
sscanf(start_pos, "$%[^,*]", command);
// 判断指令类型并进行解析
if (strcmp(command, "BDICI") == 0) {
parse_bds_card(start_pos);
} else if (strcmp(command, "BDBSI") == 0) {
parse_bds_signal(start_pos);
} else if (strcmp(command, "BDDWR") == 0) {
parse_bds_position(start_pos);
} else if (strcmp(command, "BDTXR") == 0) {
parse_bds_message(start_pos);
}
}
}
}
}
// 解析北斗卡号
void parse_bds_card(char *sentence) {
char card_number[16];
sscanf(sentence, "$BDICI,%[^,]", card_number);
printf("Beidou Card Number: %s\n", card_number);
}
// 解析信号状态
void parse_bds_signal(char *sentence) {
char signal_values[50];
sscanf(sentence, "$BDBSI,%4s,%4s,%[^,*]", NULL, NULL, signal_values);
printf("Signal Values: %s\n", signal_values);
}
// 解析定位信息
void parse_bds_position(char *sentence) {
char time[16], latitude[16], longitude[16], altitude[16];
sscanf(sentence, "$BDDWR,%*[^\r\n],%[^,],%[^,],%[^,],%[^,],%[^,],", time, latitude, longitude, altitude);
printf("Position - Time: %s, Latitude: %s, Longitude: %s, Altitude: %s\n", time, latitude, longitude, altitude);
}
// 解析接收到的消息
void parse_bds_message(char *sentence) {
char message_content[100];
sscanf(sentence, "$BDTXR,%*[^,],%*[^,],%[^,]", message_content);
printf("Received Message: %s\n", message_content);
}
主函数与发送功能
//送 发北斗指令
void send_bds_sentence(const char *sentence) {
uint8_t len = strlen(sentence);
for (uint8_t i = 0; i < len; i++) {
usart_data_transmit(USART0, sentence[i]);
while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET);
}
}
// 主函数
int main(void) {
rcu_clock_prescale_config(RCU_APB2_RTCPrescale, 1);
rcu_clock_configuration();
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_USART0);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
// 初始化串口
usart_init(9600);
// 初始化环形缓冲区
ring_buffer_init(&rx_ring_buf);
while (1) {
// 解析接收到的数据
parse_bds_sentence();
// 发送北斗指令示例
static uint8_t send_flag = 0;
send_flag++;
if (send_flag > 100) { // 模拟发送频率控制
send_flag = 0;
// 发送读取卡号指令
send_bds_sentence("$CCICA,0,00*7B\r\n");
delay_1ms(100); // 简单延时函数
}
}
}
异或校验字节计算
// 计算异或校验字节
char calculate_checksum(const char *sentence) {
char *start = strstr(sentence, "$") + 1;
char *end = strstr(start, "*");
if (!end) return 0x00;
uint8_t checksum = 0;
while (start < end) {
checksum ^= *start++;
}
return checksum;
}
说明
- 本代码示例适用于GD32F4XX系列MCU,需要根据具体型号调整引脚和时钟配置。
- 环形缓冲区大小可以根据实际应用需求调整。
sscanf
函数用于解析北斗协议中的字段,strstr
和strcmp
用于指令识别。- 在实际应用中,需要添加有效的延时函数
delay_1ms
和错误处理机制。 - 异或校验字节在发送指令时需要正确计算,可调用
calculate_checksum
函数。 - 根据需求,可以扩展解析和发送更多类型的北斗短报文协议指令。