RT_threadのUART设备学习笔记


前言

  UART(通用异步(Asynchronous)串口),我们做嵌入式开发都会使用到的基本外设,其工作原理就是将传输数据的每个字符一位一位的传输,只要2根传输线就可以实现双向通信,一根线发送数据的同时另一根线接收数据。本文记录学习下rt_thread系统中的UART设备。

1. UART设备驱动层

  UART串口通信有几个重要的参数:波特率、起始位、数据位、停止位和奇偶校验位,对于两个使用UART串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。Uart串口传输的数据格式如下:

起始位D0D1D2D3D4D5D6D7奇偶校验位停止位

  起始位为0,停止位为1,D0~D7为数据位,数据位可以是5,6,7,8,9,一般取值为8,即D0-D7,奇偶校验位用于接收方对接收到的数据进行校验,校验“1”的位数为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性,该位也可以不用。
  Uart设备驱动层主要写Uart的驱动配置、收发代码。这些在裸机下已经接触过。我们这里来学习rt-thread中Uart驱动的写法:
(1)首先定义了硬件uart相关结构体:

struct stm32_uart
{
    UART_HandleTypeDef UartHandle;
    IRQn_Type irq;

#ifdef BSP_UART_USING_DMA_RX
    IRQn_Type dma_irq;
    rt_size_t last_index;
    DMA_HandleTypeDef hdma_rx;
#endif

    char * uart_name;
    struct rt_serial_device serial;
};

  结构体中有uart_name用于给Uart对象起一个名字,还有struct rt_serial_device类型的成员变量serial,顾名思义,serial是一个串口设备,接下来看看结构体struct rt_serial_device的原型:

struct rt_serial_device
{
    struct rt_device          parent;//设备结构体

    const struct rt_uart_ops *ops;//针对串口独享的操作函数
    struct serial_configure   config;//串口的配置参数

    void *serial_rx;//私有的接收参数
    void *serial_tx;//私有的发送参数
};
typedef struct rt_serial_device rt_serial_t;

  其中,结构体struct rt_uart_ops的原型如下,它其实就是一个“容器”,串口的配置函数和控制函数和收发函数等可以包装在一起。

/**
 * uart operators
 */
struct rt_uart_ops
{
    rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
    rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);

    int (*putc)(struct rt_serial_device *serial, char c);
    int (*getc)(struct rt_serial_device *serial);

    rt_size_t (*dma_transmit)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction);
};

  串口的配置结构体定义如下:

struct serial_configure
{
    rt_uint32_t baud_rate;

    rt_uint32_t data_bits               :4;
    rt_uint32_t stop_bits               :2;
    rt_uint32_t parity                  :2;
    rt_uint32_t bit_order               :1;
    rt_uint32_t invert                  :1;
    rt_uint32_t bufsz                   :16;
    rt_uint32_t reserved                :6;
};

  在rt_thread中串口的默认配置参数是:

#define RT_SERIAL_CONFIG_DEFAULT           \
{                                          \
    BAUD_RATE_115200, /* 115200 bits/s */  \
    DATA_BITS_8,      /* 8 databits */     \
    STOP_BITS_1,      /* 1 stopbit */      \
    PARITY_NONE,      /* No parity  */     \
    BIT_ORDER_LSB,    /* LSB first sent */ \
    NRZ_NORMAL,       /* Normal mode */    \
    RT_SERIAL_RB_BUFSZ, /* Buffer size */  \
    0                                      \
}

(2)实例化一个串口
  关于struct rt_uart_ops的成员函数,我们可以使用RT_thread提供的函数,本人用的是STM32L4,这里只贴出实例stm32_uart_ops的初始化。

static const struct rt_uart_ops stm32_uart_ops =
{
    stm32_configure,
    stm32_control,
    stm32_putc,
    stm32_getc,
};

  函数stm32_configure负责完成Uart的初始化
  函数stm32_control负责完成Uart中断的打开和关闭
  函数stm32_putc负责完成Uart单字符发送
  函数stm32_getc负责完成Uart单字符接收
上面这四个函数不必我们自己写,完完全全可以使用RT_thread的。接下来看看串口1的实例化:

/* UART1 设备驱动结构体初始化 */
static struct stm32_uart uart1 =
{
    {USART1},               // 类型UART_HandleTypeDef;
    USART1_IRQn,            // 中断编号IRQn_Type;

#ifdef BSP_UART_USING_DMA_RX
    USART1_RX_DMA_IRQN,     // IRQn_Type dma_irq;
    0,                      // rt_size_t last_index;
    // DMA_HandleTypeDef hdma_rx;
    {USART1_RX_DMA_CHANNEL, {USART1_RX_DMA_REUQEST}},
#endif

    "uart1",                // char * uart_name;设备名字
    // struct rt_serial_device类型;
    {{0}, &stm32_uart_ops, RT_SERIAL_CONFIG_DEFAULT}
};

  上面的初始化并没有真正初始化串口的parent(类型struct rt_device),这个parent是在串口设备注册到IO设备管理器的时候更新,接下来就来看下串口的设备驱动框架层。

2.UART设备驱动框架层

  RT_thread提供了函数rt_hw_serial_register将串口设备注册到IO设备管理器中,函数原型如下:

rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
                               const char              *name,
                               rt_uint32_t              flag,
                               void                    *data)
{
    rt_err_t ret;
    struct rt_device *device;
    RT_ASSERT(serial != RT_NULL);//判断串口设备的有效性

    device = &(serial->parent);提取串口设备的父类
	//然后初始化parent
    device->type        = RT_Device_Class_Char;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;
    
    device->init        = rt_serial_init;
    device->open        = rt_serial_open;
    device->close       = rt_serial_close;
    device->read        = rt_serial_read;
    device->write       = rt_serial_write;
    device->control     = rt_serial_control;
    device->user_data   = data;//私有变量

    /* 注册 */
    ret = rt_device_register(device, name, flag);

    return ret;
}

  如果要注册上面的uart1,可以这么写:

result = rt_hw_serial_register(&uart1.serial,
                              uart1.uart_name,
#ifdef BSP_UART_USING_DMA_RX
                              RT_DEVICE_FLAG_DMA_RX |
#endif
                              RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                              &uart1);

注意到上面给串口设备的的父类parent封装进了几个函数:
  (1).rt_serial_init
  (2).rt_serial_open
  (3).rt_serial_close
  (4).rt_serial_read
  (5).rt_serial_write
  (6).rt_serial_control
函数rt_serial_init原型如下:

static rt_err_t rt_serial_init(struct rt_device *dev)
{
    rt_err_t result = RT_EOK;
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    serial = (struct rt_serial_device *)dev;//可以强转

    /* initialize rx/tx */
    serial->serial_rx = RT_NULL;
    serial->serial_tx = RT_NULL;

    /* 调用前面的函数stm32_configure完成串口的配置初始化 */
    if (serial->ops->configure)
        result = serial->ops->configure(serial, &serial->config);

    return result;
}

  当我们应用程序调用IO设备应用层函数接口rt_device_init或者rt_device_open,传进Uart设备句柄时,就会调用执行上面的函数。

函数rt_serial_open原型如下:

static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag)
{
    rt_uint16_t stream_flag = 0;
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    serial = (struct rt_serial_device *)dev;

    LOG_D("open serial device: 0x%08x with open flag: 0x%04x",
        dev, oflag);
    /* check device flag with the open flag */
    if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX))
        return -RT_EIO;
    if ((oflag & RT_DEVICE_FLAG_DMA_TX) && !(dev->flag & RT_DEVICE_FLAG_DMA_TX))
        return -RT_EIO;
    if ((oflag & RT_DEVICE_FLAG_INT_RX) && !(dev->flag & RT_DEVICE_FLAG_INT_RX))
        return -RT_EIO;
    if ((oflag & RT_DEVICE_FLAG_INT_TX) && !(dev->flag & RT_DEVICE_FLAG_INT_TX))
        return -RT_EIO;

    /* keep steam flag */
    if ((oflag & RT_DEVICE_FLAG_STREAM) || (dev->open_flag & RT_DEVICE_FLAG_STREAM))
        stream_flag = RT_DEVICE_FLAG_STREAM;

    /* get open flags */
    dev->open_flag = oflag & 0xff;

    /* initialize the Rx/Tx structure according to open flag */
    if (serial->serial_rx == RT_NULL)
    { 
        if (oflag & RT_DEVICE_FLAG_INT_RX)
        {
            struct rt_serial_rx_fifo* rx_fifo;

            rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) +
                serial->config.bufsz);
            RT_ASSERT(rx_fifo != RT_NULL);
            rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
            rt_memset(rx_fifo->buffer, 0, serial->config.bufsz);
            rx_fifo->put_index = 0;
            rx_fifo->get_index = 0;
            rx_fifo->is_full = RT_FALSE;

            serial->serial_rx = rx_fifo;
            dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
            /* configure low level device */
            serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX);
        }
#ifdef RT_SERIAL_USING_DMA        
        else if (oflag & RT_DEVICE_FLAG_DMA_RX)
        {
            if (serial->config.bufsz == 0) {
                struct rt_serial_rx_dma* rx_dma;

                rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma));
                RT_ASSERT(rx_dma != RT_NULL);
                rx_dma->activated = RT_FALSE;

                serial->serial_rx = rx_dma;
            } else {
                struct rt_serial_rx_fifo* rx_fifo;

                rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) +
                    serial->config.bufsz);
                RT_ASSERT(rx_fifo != RT_NULL);
                rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
                rt_memset(rx_fifo->buffer, 0, serial->config.bufsz);
                rx_fifo->put_index = 0;
                rx_fifo->get_index = 0;
                rx_fifo->is_full = RT_FALSE;
                serial->serial_rx = rx_fifo;
                /* configure fifo address and length to low level device */
                serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX);
            }
            dev->open_flag |= RT_DEVICE_FLAG_DMA_RX;
        }
#endif /* RT_SERIAL_USING_DMA */
        else
        {
            serial->serial_rx = RT_NULL;
        }
    }
    else
    {
        if (oflag & RT_DEVICE_FLAG_INT_RX)
            dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
#ifdef RT_SERIAL_USING_DMA
        else if (oflag & RT_DEVICE_FLAG_DMA_RX)
            dev->open_flag |= RT_DEVICE_FLAG_DMA_RX;
#endif /* RT_SERIAL_USING_DMA */  
    }

    if (serial->serial_tx == RT_NULL)
    {
        if (oflag & RT_DEVICE_FLAG_INT_TX)
        {
            struct rt_serial_tx_fifo *tx_fifo;

            tx_fifo = (struct rt_serial_tx_fifo*) rt_malloc(sizeof(struct rt_serial_tx_fifo));
            RT_ASSERT(tx_fifo != RT_NULL);

            rt_completion_init(&(tx_fifo->completion));
            serial->serial_tx = tx_fifo;

            dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
            /* configure low level device */
            serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX);
        }
#ifdef RT_SERIAL_USING_DMA
        else if (oflag & RT_DEVICE_FLAG_DMA_TX)
        {
            struct rt_serial_tx_dma* tx_dma;

            tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma));
            RT_ASSERT(tx_dma != RT_NULL);
            tx_dma->activated = RT_FALSE;

            rt_data_queue_init(&(tx_dma->data_queue), 8, 4, RT_NULL);
            serial->serial_tx = tx_dma;

            dev->open_flag |= RT_DEVICE_FLAG_DMA_TX;
            /* configure low level device */
            serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX);
        }
#endif /* RT_SERIAL_USING_DMA */
        else
        {
            serial->serial_tx = RT_NULL;
        }
    }
    else
    {
        if (oflag & RT_DEVICE_FLAG_INT_TX)
            dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
#ifdef RT_SERIAL_USING_DMA
        else if (oflag & RT_DEVICE_FLAG_DMA_TX)
            dev->open_flag |= RT_DEVICE_FLAG_DMA_TX;
#endif /* RT_SERIAL_USING_DMA */    
    }

    /* set stream flag */
    dev->open_flag |= stream_flag;

    return RT_EOK;
}

  上面的函数这句【serial->ops->control】其实就是调用stm32_control函数,这个函数用来配置串口的中断。
函数rt_serial_close原型如下:

static rt_err_t rt_serial_close(struct rt_device *dev)
{
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    serial = (struct rt_serial_device *)dev;

    /* this device has more reference count */
    if (dev->ref_count > 1) return RT_EOK;

    if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)
    {
        struct rt_serial_rx_fifo* rx_fifo;

        rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
        RT_ASSERT(rx_fifo != RT_NULL);

        rt_free(rx_fifo);
        serial->serial_rx = RT_NULL;
        dev->open_flag &= ~RT_DEVICE_FLAG_INT_RX;
        /* configure low level device */
        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void*)RT_DEVICE_FLAG_INT_RX);
    }
#ifdef RT_SERIAL_USING_DMA
    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)
    {
        if (serial->config.bufsz == 0) {
            struct rt_serial_rx_dma* rx_dma;

            rx_dma = (struct rt_serial_rx_dma*)serial->serial_rx;
            RT_ASSERT(rx_dma != RT_NULL);

            rt_free(rx_dma);
        } else {
            struct rt_serial_rx_fifo* rx_fifo;

            rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
            RT_ASSERT(rx_fifo != RT_NULL);

            rt_free(rx_fifo);
        }
        /* configure low level device */
        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_RX);
        serial->serial_rx = RT_NULL;
        dev->open_flag &= ~RT_DEVICE_FLAG_DMA_RX;
    }
#endif /* RT_SERIAL_USING_DMA */
    
    if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
    {
        struct rt_serial_tx_fifo* tx_fifo;

        tx_fifo = (struct rt_serial_tx_fifo*)serial->serial_tx;
        RT_ASSERT(tx_fifo != RT_NULL);

        rt_free(tx_fifo);
        serial->serial_tx = RT_NULL;
        dev->open_flag &= ~RT_DEVICE_FLAG_INT_TX;
        /* configure low level device */
        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void*)RT_DEVICE_FLAG_INT_TX);
    }
#ifdef RT_SERIAL_USING_DMA
    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
    {
        struct rt_serial_tx_dma* tx_dma;

        tx_dma = (struct rt_serial_tx_dma*)serial->serial_tx;
        RT_ASSERT(tx_dma != RT_NULL);

        rt_free(tx_dma);
        serial->serial_tx = RT_NULL;
        dev->open_flag &= ~RT_DEVICE_FLAG_DMA_TX;
    }
#endif /* RT_SERIAL_USING_DMA */
    return RT_EOK;
}

  如果设备的ref_count大于1,说明设备打开次数多与关闭次数,这个时候不会完全的关闭设备,完全关闭设备就是关闭串口设备的接收功能,如果使用了DMA发送数据,那么还关闭了串口的DMA发送功能。

函数rt_serial_read原型如下:

static rt_size_t rt_serial_read(struct rt_device *dev,
                                rt_off_t          pos,
                                void             *buffer,
                                rt_size_t         size)
{
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    if (size == 0) return 0;

    serial = (struct rt_serial_device *)dev;

    if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)
    {
        return _serial_int_rx(serial, buffer, size);
    }
#ifdef RT_SERIAL_USING_DMA
    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)
    {
        return _serial_dma_rx(serial, buffer, size);
    }
#endif /* RT_SERIAL_USING_DMA */    

    return _serial_poll_rx(serial, buffer, size);
}

  上面这个读函数,我觉得不怎么好用,根据判断使能接收中断或者DMA接收,又或者以上两者都不是来执行接收字符,读取到的数据不对或者数据丢失,如果是通信频繁的话,执行一些没必要的代码也起不到好处。最好的方法还是用传统裸机下串口接收中断处理数据。所以rt_serial_read不推荐使用。不过函数rt_serial_write还是可以使用的。
函数rt_serial_write原型如下:

static rt_size_t rt_serial_write(struct rt_device *dev,
                                 rt_off_t          pos,
                                 const void       *buffer,
                                 rt_size_t         size)
{
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    if (size == 0) return 0;

    serial = (struct rt_serial_device *)dev;

    if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
    {
        return _serial_int_tx(serial, buffer, size);
    }
#ifdef RT_SERIAL_USING_DMA    
    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
    {
        return _serial_dma_tx(serial, buffer, size);
    }
#endif /* RT_SERIAL_USING_DMA */
    else
    {
        return _serial_poll_tx(serial, buffer, size);
    }
}

  一般地,我们不开串口发送中断RT_DEVICE_FLAG_INT_TXRT_DEVICE_FLAG_DMA_TX,这样我们最终使用的_serial_poll_tx函数完成字符的发送。

函数rt_serial_control原型如下:

static rt_err_t rt_serial_control(struct rt_device *dev,
                                  int              cmd,
                                  void             *args)
{
    rt_err_t ret = RT_EOK;
    struct rt_serial_device *serial;

    RT_ASSERT(dev != RT_NULL);
    serial = (struct rt_serial_device *)dev;

    switch (cmd)
    {
        case RT_DEVICE_CTRL_SUSPEND:
            /* suspend device */
            dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
            break;

        case RT_DEVICE_CTRL_RESUME:
            /* resume device */
            dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
            break;

        case RT_DEVICE_CTRL_CONFIG:
            if (args)
            {
                struct serial_configure *pconfig = (struct serial_configure *) args;
                if (pconfig->bufsz != serial->config.bufsz && serial->parent.ref_count)
                {
                    /*can not change buffer size*/
                    return RT_EBUSY;
                }
                /* set serial configure */
                serial->config = *pconfig;
                if (serial->parent.ref_count)
                {
                    /* serial device has been opened, to configure it */
                    serial->ops->configure(serial, (struct serial_configure *) args);
                }
            }

            break;
            default :
            /* control device */
            ret = serial->ops->control(serial, cmd, args);
            break;
    }

    return ret;
}

通过命令控制字,应用程序对串口设备进行配置,形参cmd是命令控制字,可取值:RT_DEVICE_CTRL_CONFIG;形参arg是控制参数,可取类型:struct serial_configure

3.UART的I/O设备管理层函数接口

UART的I/O设备管理层函数接口是供给应用程序使用的,这一层UART真正封装成一个设备来使用了,RT_thread提供下面的I/O设备管理接口来访问串口:

函数描述
rt_device_find根据串口设备名称查找设备获取设备句柄
rt_device_open打开设备
rt_device_read读取数据
rt_device_write写入数据
rt_device_control控制设备
rt_device_set_rx_indicate设置接收回调函数
rt_device_set_tx_complete设置发送完成回调函数
rt_device_close关闭设备

  看上面几个函数好像是在学习IO设备模型的时候接触过,确实是接触过,针对UART,访问它们实际上是访问设备驱动框架层的函数:

函数描述函数
rt_device_find等价于访问rt_device_find
rt_device_open等价于访问rt_serial_open
rt_device_read等价于访问rt_serial_read
rt_device_write等价于访问rt_serial_write
rt_device_control等价于访问rt_serial_control
rt_device_close等价于访问rt_serial_close

  rt_device_set_tx_complete函数是设置发送完成回调函数,在应用程序调用rt_device_write()写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件数据发送完成后(例如DMA传送完成或FIFO已经写入完毕产生完成中断时)调用。函数声明如下:

rt_err_t rt_device_set_tx_complete(rt_device_t dev, 
rt_err_t (*tx_done)(rt_device_tdev,void *buffer));
参数描述
dev设备句柄
tx_done回调函数指针
返回--------
RT_EOK设置成功

这个回调函数的存在意义主要是降低上层程序与底层驱动程序的耦合度,只要发送完成后,将数据包信息给回调函数就可以了。
  rt_device_set_rx_indicate函数常用于发送信号量或者事件通知串口数据处理线程有数据到达。函数声明如下:

rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t
dev,rt_size_t size));
参数描述
dev设备句柄
rx_ind回调函数指针
dev设备句柄
size缓冲区数据大小(回调函数参数)
返回-------
RT_EOK设置成功

4.实践测试

开一个线程,使用串口2,返回接收到的数据消息。
(1)实例化串口2

struct stm32_uart uart2 =
{
    {USART2},               // UART_HandleTypeDef UartHandle;
    USART2_IRQn,            // IRQn_Type irq;

#ifdef BSP_UART_USING_DMA_RX
    USART2_RX_DMA_IRQN,     // IRQn_Type dma_irq;
    0,                      // rt_size_t last_index;
    // DMA_HandleTypeDef hdma_rx;
    {USART2_RX_DMA_CHANNEL, {USART2_RX_DMA_REUQEST}},
#endif

    "uart2",                // char * uart_name;
    // struct rt_serial_device serial;
    {{0}, &stm32_uart_ops, RT_SERIAL_CONFIG_DEFAULT}
};

(2)编写串口2的中断函数

//uint8_t uart2_recv_buff[128];
//uint8_t uart2_buff_index = 0;
//uint8_t uart2_message_len =0;
//uint8_t recv_done = 0;
void USART2_IRQHandler(void)
{
		uint8_t recv =0;
    /* enter interrupt */
    rt_interrupt_enter();
		if(__HAL_UART_GET_FLAG(&uart2.UartHandle, UART_FLAG_RXNE) != RESET)
		{
			HAL_UART_Receive(&uart2.UartHandle, &recv, 1, 10000);
			uart2_recv_buff[uart2_buff_index] = recv;
			
			if((uart2_recv_buff[uart2_buff_index-1] == 0x0D) && (uart2_recv_buff[uart2_buff_index] == 0x0A))
			{
				recv_done = 1;
				//记录数据包长度
				uart2_message_len = uart2_buff_index+1-2;
				uart2_buff_index = 0;
			}
			else{
				uart2_buff_index++;
				if(uart2_buff_index == 128) uart2_buff_index = 0;
			}
		}
		HAL_UART_IRQHandler(&uart2.UartHandle);
    /* leave interrupt */
    rt_interrupt_leave();
}

(3)注册到IO设备管理器中

int stm32_hw_usart_init(void)
{
    struct stm32_uart* uarts[] =
    {
#ifdef BSP_USING_UART1
        &uart1,
#endif

#ifdef BSP_USING_UART2
        &uart2,
#endif
/****/
    int i;

    for (i = 0; i < sizeof(uarts) / sizeof(uarts[0]); i++)
    {
        struct stm32_uart *uart = uarts[i];
        rt_err_t result;

        /* register UART device */
        result = rt_hw_serial_register(&uart->serial,
                              uart->uart_name,
#ifdef BSP_UART_USING_DMA_RX
                              RT_DEVICE_FLAG_DMA_RX |
#endif
                              RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                              uart);
        RT_ASSERT(result == RT_EOK);
        (void)result;
    }

    return 0;
}

(4)线程

extern uint8_t uart2_message_len,recv_done,uart2_recv_buff[128];
void Printf_task(void *parameter)
{
	const rt_uint8_t buff[] = {"Uart device test\r\n"};
	rt_uint8_t recv_buff[50];
	rt_device_t uart2_device = rt_device_find("uart2");
	if(RT_NULL != uart2_device)
	{
		rt_device_open(uart2_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM);
		uart2_device->control(uart2_device, RT_DEVICE_CTRL_SET_INT, RT_NULL);
	}
	while(1)
	{
		//rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
		if(recv_done == 1)
		{
			recv_done = 0;
			uart2_device->write(uart2_device, 0, uart2_recv_buff, uart2_message_len);
		}
		//rt_mutex_release(dynamic_mutex);
		rt_thread_mdelay(10);
	}
}
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值