基于ringbuffer的串口接收demo

基于ringbuffer的串口接收demo

之前写了一篇文章,实现了一个环形缓冲区
一种环形缓冲区的分析和实现,比较详细的分析了环形缓冲区的原理和实现,本文将基于环形缓冲区实现一个串口接收的demo。

一、前提准备

  • GD32F103开发板(其他开发板也可,本人这里使用GD32F103)
  • 串口发送接收例程
  • 串口调试助手

二、实现思路

在串口发送接收例程的基础上,添加DMA接收串口数据,添加空闲中断,在中断里将DMA接收到的数据存入环形缓冲区,在主函数中读取环形缓冲区中的数据。

三、代码实现

1、准备好代码模板

GD32F103 模板

代码模板主要实现了毫秒、微秒延时,LED灯控制,printf重定向到串口,中断优先级分组,关闭半主机模式等功能。

2、添加ringbuffer库

将ringbuffer库添加到工程中,这里可以在RingBufferDemo工程中到User/App目录下找到,将array.harray.cring_buffer.hring_buffer.c复制到工程中。

在初始化串口前,需要先初始化ringbuffer,具体代码如下:

#define RX_FIFO_POOL_SIZE 1024

static uint8_t rx_fifo_pool[RX_FIFO_POOL_SIZE];
static Array rx_array = {0};
static RingBuffer rx_ring_buffer = {0};

void usart_config(void)
{
    ArrayError arr_err = array_init(&rx_array, rx_fifo_pool, RX_FIFO_POOL_SIZE, sizeof(uint8_t));
    if (ARRAY_OK != arr_err)
    {
        while (1)
            ;
    }

    RingBufferStatus ring_err = ring_buffer_init(&rx_ring_buffer, &rx_array, NULL, NULL);
    if (RB_OK != ring_err)
    {
        while (1)
            ;
    }
    // 串口初始化...
}

3、添加DMA接收串口数据

这里使用串口0进行发送和接收,DMA0的通道4用来接收串口0到数据,具体配置如下:

void dma_for_usart_config(void)
{
    dma_parameter_struct dma_init_struct;

    /* deinitialize DMA channel4 (USART0 rx) */
    dma_deinit(DMA0, DMA_CH4);
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY; // 方向是 外设到内存
    dma_init_struct.memory_addr = (uint32_t)rxbuffer; // DMA缓冲区到地址
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 内存地址自增
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT; // 内存数据宽度8位
    dma_init_struct.number = RX_BUFFER_SIZE; // DMA接收数据长度
    dma_init_struct.periph_addr = USART0_RDATA_ADDRESS; // 外设地址
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 外设地址不自增
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; // 外设数据宽度8位
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; // 优先级最高
    dma_init(DMA0, DMA_CH4, &dma_init_struct); // 初始化DMA通道4
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH4); // 环形模式关闭
    /* enable DMA channel4 */
    dma_channel_enable(DMA0, DMA_CH4); // 使能DMA通道4
}

其中rxbuffer是一个全局的数组,用来存储DMA从外设中接收到的数据。
USART0_RDATA_ADDRESS是一个宏定义,用于获取串口0接收数据的地址。

4、添加空闲中断

在初始化串口0到配置时,需要打开dma接收,然后使能空闲中断,并设置串口0的中断优先级.

static void params_usart_config(void)
{
    /* USART configure */
    usart_deinit(USART0);
    usart_baudrate_set(USART0, 115200);
    usart_word_length_set(USART0, USART_WL_8BIT);
    usart_stop_bit_set(USART0, USART_STB_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_dma_receive_config(USART0, USART_RECEIVE_DMA_ENABLE); // 使能DMA接收
    usart_enable(USART0);

    usart_interrupt_enable(USART0, USART_INT_IDLE); // 使能空闲中断
}

void nvic_for_usart_config(void)
{
    nvic_irq_enable(USART0_IRQn, 0, 0); // 配置串口0中断优先级
}

5、添加串口0中断处理函数

在串口0中断处理函数中,需要判断是否是空闲中断,如果是,则表示接收完成,此时需要将接收到的数据从DMA缓冲区中拷贝到ringbuffer中,最后还需要重新配置DMA接收,以便下一次接收数据。

void USART0_IRQHandler(void)
{
    uint8_t rx_count = 0;
    if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE))
    {
        /* clear IDLE flag */
        usart_data_receive(USART0);

        /* number of data received */
        rx_count = RX_BUFFER_SIZE - (dma_transfer_number_get(DMA0, DMA_CH4));

        ring_buffer_write(&rx_ring_buffer, rxbuffer, rx_count);

        /* disable DMA and reconfigure */
        dma_channel_disable(DMA0, DMA_CH4);
        dma_transfer_number_config(DMA0, DMA_CH4, RX_BUFFER_SIZE);
        dma_channel_enable(DMA0, DMA_CH4);
    }
}

6、实现串口接收函数

为了方便使用,我们可以实现两个串口接收函数,一个用于接收一个字节,另一个用于接收指定长度的数据。
我这里使用了一个枚举类型给上层使用,具体实现可参考文末附到具体代码。

int32_t usart_rx_byte(USART_ID usart_id, uint8_t *data)
{
    return ring_buffer_read(usart_ring_buffer_handler[usart_id].ring_buffer, data, 1);
}

int32_t usart_rx(USART_ID usart_id, uint8_t *data, uint32_t len)
{
    return ring_buffer_read(usart_ring_buffer_handler[usart_id].ring_buffer, data, len);
}

7、主函数测试

在主函数中初始化各硬件,循环等待接收数据,如果没有接收到数据,打印提示空;如果环形缓冲出现错误,打印错误码;
如果接收到数据,打印接收到到数据。

int main(void)
{
    int32_t recv_num = 0;
    uint8_t recv_data[20] = {0};

    systick_config();

    gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);
    nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);

    usart_config();
    led_init(RESET);

    while (1)
    {
        recv_num = usart_rx(HARD_USART0, recv_data, 20);
        if (recv_num > 0)
        {
            printf("recv_size = %d, recv data: %s\r\n", recv_num, recv_data);
            led_ctrl(LED_R, LED_ON);
            delay_1ms(5000);
            led_ctrl(LED_R, LED_OFF);
        }
        else
        {
            if (recv_num == -RB_EMPTY)
            {
                printf("recv buffer empty\r\n");
            }
            else
            {
                printf("recv error. error code: %d\r\n", recv_num);
            }
        }
        delay_1ms(10);
    }
}

四、完整代码

完整代码

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中串口(SerialPort)接收数据的Demo示例如下: ```java import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.io.File; import java.io.IOException; import java.io.InputStream; import android_serialport_api.SerialPort; public class SerialPortActivity extends AppCompatActivity { private SerialPort mSerialPort; private InputStream mInputStream; private ReadThread mReadThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_serial_port); try { mSerialPort = new SerialPort(new File("/dev/ttyS1"), 9600, 0); mInputStream = mSerialPort.getInputStream(); } catch (SecurityException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mReadThread = new ReadThread(); mReadThread.start(); } private class ReadThread extends Thread { @Override public void run() { super.run(); while (!isInterrupted()) { try { if (mInputStream == null) { return; } byte[] buffer = new byte[1024]; int size = mInputStream.read(buffer); if (size > 0) { String data = new String(buffer, 0, size); Log.d("SerialPort", "Received data: " + data); // 处理接收到的数据... } } catch (IOException e) { e.printStackTrace(); } } } } @Override protected void onDestroy() { super.onDestroy(); if (mReadThread != null) { mReadThread.interrupt(); mReadThread = null; } if (mSerialPort != null) { mSerialPort.close(); mSerialPort = null; } } } ``` 以上是一个Android中通过串口接收数据的示例代码。在`onCreate`方法中,首先打开串口并获取输入流。然后创建一个`ReadThread`线程用于循环读取串口数据。在`ReadThread`线程的`run`方法中通过`InputStream`的`read`方法读取数据,并将读取到的数据进行处理(这里只是简单地打印出来)。在`onDestroy`方法中,关闭串口和销毁线程。 需要注意的是,这里使用到了一个`android_serialport_api`库,需要在项目的`build.gradle`文件中添加对应的依赖。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值