在使用串口的使用中,由于速率比较低,因此数据的收发都比较占用资源。尤其是数据的输入,因为在程序的执行过
程中无法预知到底何时才有数据过来,采用中断的方式去实现接收也有弊端,当需要解析帧协议时需要不断的去判断是
否有足够一帧的数据,而且在发送过程中也无法实现无阻塞。从而浪费了大量的 CPU 资源。这里采用一种
ringbuffer 的方式去实现无阻塞的收发,发送数据时只需要数据写入 buffer 即可,不需要等待完全发送完毕才
退出。接收数据时,可以先判断缓存中是否有足够的数据,再去取出缓存的数据。而且在没有数据的时候,即使调用
getchar,也不会阻塞。因此使用起来比较方便。代码的实现比较简单,很容易就能移植到其他的平台。
usart 初始化与接口函数
#include "includes.h"
static tRingBuffer RingBufferUART1TX;
static tRingBuffer RingBufferUART1RX;
unsigned int IrqCntUart1;
static int usart_getchar(void)
{
return RingBufferGet(&RingBufferUART1RX);
}
static void usart_putchar(uint8_t ch)
{
RingBufferPut(&RingBufferUART1TX, ch, 1);
}
static void usart_putstring(uint8_t *str)
{
while (*str != 0)
{
usart_putchar(*str);
str++;
}
}
/* usart 接收缓存中剩余数据 */
int usart_available(void)
{
return RingBufferFillLevel(&RingBufferUART1RX);
}
static int lastChar = -1;
int get_char(void) // 读取一个 Byte, 如果缓存为空, 返回 -1
{
if (lastChar < 0)
{
return usart_getchar();
}
else
{
int c = lastChar;
lastChar = -1;
return c;
}
}
void usart_flush(void)
{
while (RingBufferFillLevel(&RingBufferUART1TX) != 0);
}
void print(const char *fmt, ...)
{
char buf[128];
va_list vlist;
va_start(vlist, fmt);
vsnprintf(buf, sizeof(buf) - 1, fmt, vlist);
usart_putstring((unsigned char *)buf);
va_end(vlist);
}
static void usart_tx_Interrupt_en(void){ USART_ITConfig(USART1, USART_IT_TXE, ENABLE);}static void uart_buffer_init(void){ RingBufferInit(&RingBufferUART1TX, &usart_tx_Interrupt_en); RingBufferInit(&RingBufferUART1RX, 0L);}static void usart_nvic_init(void){ NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);} /* 串口初始化 */void debug_usart_init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); /* -------------------------- 端口配置 -------------------------- */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 256000; 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_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); /* 使能接收中断 */ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); uart_buffer_init(); usart_nvic_init();} /* USART 中断处理函数 */void USART1_IRQHandler(void) { int sr = USART1->SR; IrqCntUart1++; if (sr & USART_FLAG_TXE) { tRingBuffer *rb = &RingBufferUART1TX; if (rb->Read != rb->Write) { USART1->DR = rb->Buffer[rb->Read]; if (rb->Read + 1 == RingBufferSize(rb)) { rb->Read = 0; } else { rb->Read++; } } else { USART_ITConfig(USART1, USART_IT_TXE, DISABLE); __ASM volatile("nop"); __ASM volatile("nop"); } } if (sr & USART_FLAG_RXNE) { tRingBuffer *rb = &RingBufferUART1RX; unsigned char c = USART1->DR; if (RingBufferFillLevel(rb) + 1 == RingBufferSize(rb)) { rb->Overrun++; return; } rb->Buffer[rb->Write] = c; if (rb->Write + 1 == RingBufferSize(rb)) { rb->Write = 0; } else { rb->Write++; } }}
ringbuffer.h
#ifndef RINGBUFFER_H_
#define RINGBUFFER_H_
typedef struct
{
volatile int Read, Write, Overrun;
unsigned char Buffer[128];
void (*CallBack)(void);
}tRingBuffer;
void RingBufferInit(tRingBuffer *rb, void (*callback)(void));
int RingBufferSize(tRingBuffer *rb);
int RingBufferFillLevel(tRingBuffer *rb);
void RingBufferPut(tRingBuffer *rb, unsigned char c, int block);
void RingBufferPutBlock(tRingBuffer *rb, unsigned char *data, int dataLen, int block);
int RingBufferGet(tRingBuffer *rb);
int RingBufferPeek(tRingBuffer *rb);
#endif /* RINGBUFFER_H_ */
#include "includes.h"
void RingBufferInit(tRingBuffer *rb, void (*callback)(void))
{
rb->Read = 0;
rb->Write = 0;
rb->Overrun = 0;
rb->CallBack = callback;
}
int RingBufferSize(tRingBuffer *rb)
{
return sizeof(rb->Buffer);
}
int RingBufferFillLevel(tRingBuffer *rb)
{
return (rb->Write - rb->Read + RingBufferSize(rb)) % RingBufferSize(rb);
}
void RingBufferPut(tRingBuffer *rb, unsigned char c, int block)
{
if (block)
{
while (RingBufferFillLevel(rb) + 1 == RingBufferSize(rb));
}
else
{
if (RingBufferFillLevel(rb) + 1 == RingBufferSize(rb))
{
rb->Overrun++;
return;
}
}
rb->Buffer[rb->Write] = c;
if (rb->Write + 1 == RingBufferSize(rb))
{
rb->Write = 0;
}
else
{
rb->Write++;
}
if (rb->CallBack)
{
rb->CallBack();
}
}
void RingBufferPutBlock(tRingBuffer *rb, unsigned char *data, int dataLen, int block)
{
if (block)
{
while (RingBufferFillLevel(rb) + dataLen >= RingBufferSize(rb));
}
else
{
if (RingBufferFillLevel(rb) + dataLen >= RingBufferSize(rb))
{
rb->Overrun += dataLen;
if (rb->CallBack)
{
rb->CallBack();
}
return;
}
}
int free1 = RingBufferSize(rb) - rb->Write;
if (dataLen <= free1)
{
memcpy(rb->Buffer + rb->Write, data, dataLen);
if (rb->Write + dataLen == RingBufferSize(rb))
{
rb->Write = 0;
}
else
{
rb->Write += dataLen;
}
}
else
{
memcpy(rb->Buffer + rb->Write, data, free1);
int len2 = dataLen - free1;
memcpy(rb->Buffer, data + free1, len2);
rb->Write = len2;
}
if (rb->CallBack)
{
rb->CallBack();
}
}
int RingBufferGet(tRingBuffer *rb)
{
if (rb->Read == rb->Write)
{
return -1;
}
else
{
unsigned char c = rb->Buffer[rb->Read];
if (rb->Read + 1 == RingBufferSize(rb))
{
rb->Read = 0;
}
else
{
rb->Read++;
}
return c;
}
}
int RingBufferPeek(tRingBuffer *rb)
{
if (rb->Read == rb->Write)
{
return -1;
}
else
{
int c = rb->Buffer[rb->Read];
return c;
}
}
1、调用 debug_usart_init() 初始化串口和收发 ringbuffer
2、调用 get_char() 函数读取一个字节的数据, 该函数不会阻塞, 如果返回 -1 表示缓存中没有数据
3、调用 print() 发送数据, 该函数不会等到发送完成才返回, 只是将数据写入 buffer, 后面会自己去开启发
送中断函数完成发送。如果确实需要等到发送完成, 可以调用 usart_flush 函数。