at32f403a的串口+dma+环形队列+校验 校验部分const uint8_t CRCTABH[256] { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 }; const uint8_t CRCTABL[256] { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40}; /* * 功能计算数组CRC16校验码 * 参数raw_data待校验数组 * len待校验数组长度 * 返回值uint16_t CRC16校验码 */ uint16_t crc16_arr(volatile uint8_t *raw_data, uint16_t len) { uint16_t index 0, i 0; uint8_t crch 0xff, crcl 0xff; for (i 0; i len; i) { index crch ^ raw_data[i]; crch crcl ^ CRCTABH[index]; crcl CRCTABL[index]; } return (crch 8) | crcl; }串口帧格式/* * function: 将数据包加上CRC校验码数据帧格式5a, a5, len, data, crc) * parametertx_data待发送数组(注意发送数组要预留三个字节的帧头两个字节的校验字节) * len待发送数组长度 * return处理完数据后的长度 */ uint16_t package_tx_crc(uint8_t *tx_data, uint16_t len) { for(uint8_t i 0; i len; i) { tx_data[len - i - 1 3] tx_data[len - i - 1]; } tx_data[0] 0x5a; tx_data[1] 0xa5; tx_data[2] len; uint16_t crc crc16_arr(tx_data, len 3); tx_data[len 3] crc 8; tx_data[len 4] crc 0xff; return len 2 3; } /* * function校验数据包CRC校验码 * parameterraw_data待校验数组 * len待校验数组长度 */ bool package_rx_crc(uint8_t* raw_data, uint16_t len) { uint16_t crc crc16_arr(raw_data, len - 2); if (crc ((raw_data[len - 2] 8) | raw_data[len - 1])) { return true; } else { return false; } }队列的实现// 循环队列结构体 typedef struct { uint8_t *buffer; // 缓冲区指针 uint16_t size; // 缓冲区大小 uint16_t head; // 写指针DMA中断写入 uint16_t tail; // 读指针main函数读取 uint16_t count; // 当前数据量 }queue_t; void queue_init(queue_t *q, uint8_t *buffer, uint16_t size); bool queue_is_full(queue_t *q); bool queue_is_empty(queue_t *q); bool queue_enqueue(queue_t *q_ptr, uint8_t data); bool queue_dequeue(queue_t *q_ptr, uint8_t *data); /* * function: queue_init * para: q_ptr: 指向队列结构体的指针 * buffer: 队列数据缓冲区 * size: 队列大小 * return: 无 */ void queue_init(queue_t *q_ptr, uint8_t *buffer, uint16_t size) { q_ptr-buffer buffer; q_ptr-size size; q_ptr-head 0; q_ptr-tail 0; q_ptr-count 0; } /* * function: queue_enqueue * para: q_ptr: 指向队列结构体的指针 * data: 要入队的元素 * return: 成功返回true失败返回false */ bool queue_enqueue(queue_t *q_ptr, uint8_t data) { if (queue_is_full(q_ptr)) return false; q_ptr-buffer[q_ptr-head] data; q_ptr-head (q_ptr-head 1) % q_ptr-size; q_ptr-count; return true; } /* * function: queue_dequeue * para: q_ptr: 指向队列结构体的指针 * data: 指向存储出队元素的指针 * return: 成功返回true失败返回false */ bool queue_dequeue(queue_t *q_ptr, uint8_t *data) { if (queue_is_empty(q_ptr)) return false; *data q_ptr-buffer[q_ptr-tail]; q_ptr-tail (q_ptr-tail 1) % q_ptr-size; q_ptr-count--; return true; } /* * function: queue_is_empty * para: q_ptr: 指向队列结构体的指针 * return: 队列为空返回true否则返回false */ bool queue_is_empty(queue_t *q_ptr) { return (q_ptr-count 0); } bool queue_is_full(queue_t *q_ptr) { return (q_ptr-count q_ptr-size); }串口的接收部分/* * function: usart1_init * para: 无 * return: 无 */ void usart1_init(void) { GPIO_InitType GPIO_InitStructure; USART_InitType USART_InitStructure; NVIC_InitType NVIC_InitStructure; DMA_InitType DMA_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_USART1, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOA | RCC_AHBPERIPH_DMA1, ENABLE); GPIO_PinAFConfig(GPIOA, GPIO_PinsSource9, GPIO_AF_1); GPIO_PinAFConfig(GPIOA, GPIO_PinsSource10, GPIO_AF_1); GPIO_InitStructure.GPIO_Pins GPIO_Pins_9 | GPIO_Pins_10; //TX GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_OutType GPIO_OutType_PP; GPIO_InitStructure.GPIO_Pull GPIO_Pull_PU; GPIO_InitStructure.GPIO_MaxSpeed GPIO_MaxSpeed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); USART_InitStructure.USART_BaudRate 9600; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, USART_InitStructure); USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); USART_INTConfig(USART1, USART_INT_IDLEF, ENABLE); //USART_INTConfig(USART1, USART_INT_RDNE, ENABLE); USART_Cmd(USART1, ENABLE); DMA_Reset(DMA1_Channel3); DMA_DefaultInitParaConfig(DMA_InitStructure); DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)USART1 -DT; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)usart1_rx_dma_buf; DMA_InitStructure.DMA_Direction DMA_DIR_PERIPHERALSRC; DMA_InitStructure.DMA_BufferSize USART1_RX_DMA_BUF_SIZE; DMA_InitStructure.DMA_PeripheralInc DMA_PERIPHERALINC_DISABLE; DMA_InitStructure.DMA_MemoryInc DMA_MEMORYINC_ENABLE; DMA_InitStructure.DMA_PeripheralDataWidth DMA_PERIPHERALDATAWIDTH_BYTE; DMA_InitStructure.DMA_MemoryDataWidth DMA_MEMORYDATAWIDTH_BYTE; DMA_InitStructure.DMA_Mode DMA_MODE_CIRCULAR; DMA_InitStructure.DMA_Priority DMA_PRIORITY_HIGH; DMA_InitStructure.DMA_MTOM DMA_MEMTOMEM_DISABLE; DMA_Init(DMA1_Channel3, DMA_InitStructure); DMA_ChannelEnable(DMA1_Channel3, ENABLE); NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn;//DMA1_Channel3_2_IRQn;//USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); } void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_INT_IDLEF) ! RESET) { USART_ClearITPendingBit(USART1, USART_INT_IDLEF); process_dma_buffer(); USART_ReceiveData(USART1); } if(USART_GetFlagStatus(USART1, USART_INT_ORERR) ! RESET) { USART_ReceiveData(USART1); USART_ClearFlag(USART1, USART_FLAG_ORERR); } } /* * function: process_dma_buffer * para: 无 * return: 无 */ void process_dma_buffer(void) { uint16_t current_index USART1_RX_DMA_BUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel3); uint16_t bytes_to_process 0; if (current_index dma_last_index) bytes_to_process current_index - dma_last_index; else bytes_to_process (USART1_RX_DMA_BUF_SIZE - dma_last_index) current_index; for (uint16_t i 0; i bytes_to_process; i) { uint16_t dma_index (dma_last_index i) % USART1_RX_DMA_BUF_SIZE; queue_enqueue(usart1_rx_queue, usart1_rx_dma_buf[dma_index]); } dma_last_index current_index; }当然也可以直接使用串口空闲中断进行接收volatile uint16_t old_queue_count 0; void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_INT_IDLEF) ! RESET) { USART_ClearITPendingBit(USART1, USART_INT_IDLEF); USART_ReceiveData(USART1); usart1_queue.head QUEUE_SIZE - DMA_GetCurrDataCounter(DMA1_Channel3); if(usart1_queue.head old_queue_count) usart1_queue.count usart1_queue.head - old_queue_count; else usart1_queue.count usart1_queue.head QUEUE_SIZE - old_queue_count; old_queue_count usart1_queue.head; } if(USART_GetITStatus(USART1, USART_INT_NERR) ! RESET) { USART_ClearITPendingBit(USART1, USART_INT_NERR); USART_ReceiveData(USART1); } if(USART_GetITStatus(USART1, USART_INT_ORERR) ! RESET) { USART_ClearITPendingBit(USART1, USART_INT_ORERR); USART_ReceiveData(USART1); } return; }串口发送部分/** * function: USART1 发送数据 * input: buf: 发送缓冲区地址 * len: 发送数据长度 * output: None */ void usart1_dma_send(uint8_t *buf, uint16_t len) { static uint8_t buf_usart1_dma[100] {0}; static uint16_t usart1_dma_len 0; if(buf NULL || len 0) return; if(usart1_tx_conplete_flag 1) { memcpy(buf_usart1_dma, buf, len); usart1_dma_len len; usart1_tx_conplete_flag 0; DMA_ChannelEnable(DMA1_Channel2, DISABLE); DMA_SetCurrDataCounter(DMA1_Channel2, usart1_dma_len); DMA1_Channel2-CMBA (uint32_t)buf_usart1_dma; DMA_ChannelEnable(DMA1_Channel2, ENABLE); } return; }解析数据帧部分/** * function: 从队列中提取完整的数据帧 * para: q_ptr: 指向队列结构体的指针 * data_buf: 存储提取出的原始数据 * buf_size: data_buf的缓冲区大小 * return: 成功返回提取的数据长度失败返回0 */ uint16_t usart_dequeue(volatile queue_t *q_ptr, uint8_t* data_buf, uint16_t buf_size) { // 最小帧长度帧头2 长度1 CRC2 5字节 if (q_ptr-count 5) return 0; // 不足一帧 uint16_t start_tail q_ptr-tail; // 记录开始位置 uint16_t bytes_processed 0; uint16_t frame_length 0; uint16_t data_length 0; uint16_t crc_received 0; uint16_t crc_calculated 0; // 查找帧头 uint8_t found_frame 0; // 最多查找队列中的字节数-1个位置因为需要连续两个字节 for (uint16_t i 0; i q_ptr-count - 1; i) { uint16_t peek_idx1 (q_ptr-tail i) (QUEUE_SIZE - 1); uint16_t peek_idx2 (q_ptr-tail i 1) (QUEUE_SIZE - 1); if (q_ptr-buffer[peek_idx1] 0x5a q_ptr-buffer[peek_idx2] 0xa5) { // 找到帧头计算当前位置 q_ptr-tail peek_idx1; // 将tail移动到帧头位置 bytes_processed i; // 记录已经跳过的字节数 found_frame 1; break; } } if (!found_frame) { // 没有找到有效帧头丢弃所有数据 q_ptr-tail (q_ptr-tail q_ptr-count) (QUEUE_SIZE - 1); q_ptr-count 0; return 0; } // 检查是否有足够的数据读取长度字节 if (q_ptr-count - bytes_processed 3) { // 数据不足恢复原状 q_ptr-tail start_tail; return 0; } // 读取数据长度第3个字节 uint16_t length_idx (q_ptr-tail 2) (QUEUE_SIZE - 1); data_length q_ptr-buffer[length_idx]; // 计算完整帧长度 frame_length data_length 5; // 数据长度 帧头2 长度1 CRC2 // 检查缓冲区大小是否足够 if (data_length buf_size) { // 目标缓冲区太小 q_ptr-tail start_tail; // 恢复原状 return 0; } // 检查队列中是否有完整帧 if (q_ptr-count - bytes_processed frame_length) { // 帧不完整恢复原状 q_ptr-tail start_tail; return 0; } // 提取数据部分到临时缓冲区用于CRC计算 uint8_t frame_data[150]; // 调整大小为最大可能的帧长度 if (frame_length sizeof(frame_data)) { // 帧太长 q_ptr-tail start_tail; return 0; } // 复制整个帧不包括CRC部分用于校验 for (uint16_t i 0; i frame_length - 2; i) { uint16_t idx (q_ptr-tail i) (QUEUE_SIZE - 1); frame_data[i] q_ptr-buffer[idx]; } // 提取CRC帧的最后两个字节 uint16_t crc_idx1 (q_ptr-tail frame_length - 2) (QUEUE_SIZE - 1); uint16_t crc_idx2 (q_ptr-tail frame_length - 1) (QUEUE_SIZE - 1); crc_received (q_ptr-buffer[crc_idx1] 8) | q_ptr-buffer[crc_idx2]; // 计算CRC帧头长度数据 crc_calculated crc16_arr(frame_data 3, frame_length - 5); // CRC校验 if (crc_received ! crc_calculated) { // CRC失败跳过这个帧头继续查找 q_ptr-tail (q_ptr-tail 1) (QUEUE_SIZE - 1); q_ptr-count - (bytes_processed 1); return 0; } // CRC校验通过提取数据部分 for (uint16_t i 0; i data_length; i) { uint16_t idx (q_ptr-tail 3 i) (QUEUE_SIZE - 1); data_buf[i] q_ptr-buffer[idx]; } // 移动tail指针移除已处理的帧 q_ptr-tail (q_ptr-tail frame_length) (QUEUE_SIZE - 1); q_ptr-count - (bytes_processed frame_length); return data_length; }注意事项1.使用dma进行发送的时候两帧之间需要有时间间隔因为在接收侧接收的时候需要根据串口的空闲中断来进行队列的更新如果是连续发送应用侧的队列的头与尾可能不会刷新导致队列内的数据进行不断的更新但是应用测检测不出来2.进行串口接收与发送数据的时候数据线上尽量不要过其他的大电流线不然会导致串口波形出现大量的畸变