24、USART 使用 ringbuffer 实现无阻塞的读写


       在使用串口的使用中,由于速率比较低,因此数据的收发都比较占用资源。尤其是数据的输入,因为在程序的执行过

程中无法预知到底何时才有数据过来,采用中断的方式去实现接收也有弊端,当需要解析帧协议时需要不断的去判断是

否有足够一帧的数据,而且在发送过程中也无法实现无阻塞。从而浪费了大量的 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 文件

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_ */


ringbuffer.c 

#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 函数。





你可能感兴趣的:(STM32F1,开发)