RT-Thread学习笔记【UART】

UART设备简介

UART:通用异步收发传输器,可以将传输数据的每个字符一位接一位地顺序传输

物理层

需要两个传输线,一根Rx接收数据,一根Tx发送数据

需要设置波特率、起始位、数据为、停止位、奇偶效验位,对于两个通过UART连接的端口,这些参数必须匹配,否则会造成传输出错

波特率:串口通信速率,用单位时间内传输的二进制代码的有效位表示,理论上可以是任意值,但常用4800、9600、38400、115200等特定值,数值越大数据传输越快。单位位每秒比特数bit/s(bps)

UART的起始位规定为逻辑电平0,停止位规定为逻辑电平1

数据位:表示传输几个比特位数据,可以设置为5、6、7、8、9,通常使用8bit,因为一个ASCII字符为8位

奇偶效验位:用于奇偶效验,不必须,使用时校验逻辑电平1的位数为偶数或奇数

RTT中使用UART

RTT提供了数个API供用户访问串口硬件,直接使用IO设备模型中的API配合串口设备名称即可使用UART

查找设备

使用rt_device_find()查找设备

rt_device_t rt_device_find(const char *name)
{
    struct rt_object *object;
    struct rt_list_node *node;
    struct rt_object_information *information;

    /* enter critical */
    if (rt_thread_self() != RT_NULL)
        rt_enter_critical();

    /* try to find device object */
    information = rt_object_get_information(RT_Object_Class_Device);
    RT_ASSERT(information != RT_NULL);
    for (node  = information->object_list.next;
         node != &(information->object_list);
         node  = node->next)
    {
        object = rt_list_entry(node, struct rt_object, list);
        if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0)
        {
            /* leave critical */
            if (rt_thread_self() != RT_NULL)
                rt_exit_critical();
            return (rt_device_t)object;
        }
    }
    /* leave critical */
    if (rt_thread_self() != RT_NULL)
        rt_exit_critical();
    /* not found */
    return RT_NULL;
}

本质上是遍历设备链表获取设备信息并返回设备句柄

打开/关闭串口

使用rt_device_open()打开串口

UART支持以下打开方式

/* 流模式 */
#define RT_DEVICE_FLAG_STREAM           0x040//流模式
/* 接收模式 */
#define RT_DEVICE_FLAG_INT_RX           0x100//中断接收模式
#define RT_DEVICE_FLAG_DMA_RX           0x200//DMA接收模式
/* 发送模式 */
#define RT_DEVICE_FLAG_INT_TX           0x400//中断发送模式
#define RT_DEVICE_FLAG_DMA_TX           0x800//DMA发送模式

使用时这发送模式、接收模式只能选择其一,打开参数默认为轮询模式,但可以使用|运算符与流模式共存使用

若使用流模式,当输出的字符是’\n’(十六进制值0x0A)时,自动在前面补一个’\r’(十六进制值0x0D)作为分行

使用rt_device_close()关闭串口

发送/接收数据

使用rt_device_write()和rt_device_read()发送、接收数据

注意:发送数据时数据长度应为sizeof(buffer)-1来去掉多余的’\0’

使用rt_device_set_tx_complete()设置回调函数来让底层硬件送数据发送完成后调用

rt_device_set_rx_indicate()的使用同理,在串口中有数据到达时通知上层应用程序处理

常见使用方法如下

#define UART_NAME "uart2"
static rt_device_t serial;
static struct rt_semaphore rx_sem;

/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev,rt_size_t size)
{
    rt_sem_release(&rx_sem);//串口接收数据产生中断,调用该函数发送接收信号量
    return RT_EOK;
}
/* UART开启函数 */
static int uart_open_sample(int argc,char* argv[])
{
    serial=rt_device_find(UART_NAME);
    rt_device_open(serial,RT_DEVICE_FLAG_INT_RX);//设置中断接收模式打开串口
    rt_sem_init(&rx_sem,"rx_sem",0,RT_IPC_FLAG_FIFO);//初始化信号量
    rt_device_set_rx_indicate(serial,uart_input);//设置接收数据回调函数
}
/* UART处理函数 */
static void serial_thread_entry(void* parameter)//单独开一个线程
{
    char ch;
    /* 接收到信号量后处理串口数据 */
    while(1)
    {
        while(rt_device_read(serial,-1,&ch,1)!=1)//阻塞等待接收信号量,等到信号量后读取1个字节的数据
            rt_sem_take(&rx_sem,RT_WAITING_FOREVER);
        ch++;
		rt_device_write(serial,0,&ch,1);//读取到的数据通过串口错位输出 
    }
}

上面的例子将一直开启串口

控制串口

使用rt_device_control()控制串口状态和配置

可用的设置如下所示

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                :4;//保留位
};

//波特率配置
#define BAUD_RATE_2400                  2400
#define BAUD_RATE_4800                  4800
#define BAUD_RATE_9600                  9600
#define BAUD_RATE_19200                 19200
#define BAUD_RATE_38400                 38400
#define BAUD_RATE_57600                 57600
#define BAUD_RATE_115200                115200
#define BAUD_RATE_230400                230400
#define BAUD_RATE_460800                460800
#define BAUD_RATE_921600                921600
#define BAUD_RATE_2000000               2000000
#define BAUD_RATE_3000000               3000000

//数据位
#define DATA_BITS_5                     5
#define DATA_BITS_6                     6
#define DATA_BITS_7                     7
#define DATA_BITS_8                     8
#define DATA_BITS_9                     9

//停止位
#define STOP_BITS_1                     0
#define STOP_BITS_2                     1
#define STOP_BITS_3                     2
#define STOP_BITS_4                     3

//奇偶效验位
#define PARITY_NONE                     0
#define PARITY_ODD                      1
#define PARITY_EVEN                     2

//字节序
#define BIT_ORDER_LSB                   0
#define BIT_ORDER_MSB                   1

//模式
#define NRZ_NORMAL                      0       /* Non Return to Zero : normal mode */
#define NRZ_INVERTED                    1       /* Non Return to Zero : inverted mode */

//缓冲区默认大小
#define RT_SERIAL_RB_BUFSZ              64

默认配置如下

#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                                      \
}

使用示例

中断接收、轮询发送的方式如上面例子一样配置,这里特别强调DMA接收的配置方法

DMA接收

DMA自动接收UART输入的数据,自动将其放入FIFO后触发中断,中断处理函数调将消息挂至消息队列;应用程序收到消息后触发读取函数,从缓冲区读取字符

#include <rtthread.h>
#define UART_NAME "uart3"

struct rx_msg//串口接收消息结构体
{
    rt_device_t dev;
    rt_size_t size;
}

static rt_device_t serial;//串口设备句柄
static struct rt_messagequeue rx_mq;//消息队列控制块

static rt_err_t uart_input(rt_device_t dev,rt_size_t size)//接收数据回调(消息队列处理)函数
{
    struct rx_msg msg;
    rt_err_t result;
    msg.dev=dev;
    msg.size=size;
    
    result=rt_mq_send(&rx_mq,&msg,sizeof(msg));//发送收取消息到消息队列
    if(result=-RT_EFULL)
        rt_kprintf("message queue full!\n");
    return result;
}

static void serial_thread_entry(void* parameter)//串口处理线程函数
{
    struct rx_msg msg;
    rt_err_t result;
    rt_uint32_t rx_length;
    static char rx_buffer[RT_SERIAL_RB_BUFSZ+1];
    
    while(1)
    {
        rt_memset(&msg,0,sizeof(msg));//清空之前的消息
        result=rt_mq_recv(&rx_mq,&msg,sizeof(msg),RT_WAITING_FOREVER);//从消息队列中轮询消息
        if(result==RT_EOK)//如果读取到消息
        {
            rx_length=rt_device_read(msg.dev,0,rx_buffer,msg.size);
            rt_buffer[rx_length]='\0';//从串口(DMA FIFO)读取数据
            
            rt_device_write(serial,0,rx_buffer,rx_length);
            rt_kprintf("%s\n",rx_buffer);//打印数据
        }
    }
}

static int uart_dma(int argc,char* argv[])//主函数
{
    rt_err_t ret=RT_EOK;
    char uart_name[RT_NAME_MAX];
    static char msg_poll[256];
    char str[]="helloworld!\r\n";
    
    if(argc==2)
        rt_strcpy(uart_name,argv[1],RTNAME_MAX);
    else
        rt_strcpy(uart_name,UART_NAME,RT_NAME_MAX);
    
    serial=rt_device_find(uart_name);//查找串口设备
    if(!serial)
    {
        rt_kprintf("find %s failed!\n",uart_name);
        return RT_ERROR;
    }
    
    /* 消息队列初始化 */
    rt_mq_init(&rx_mq,"rx_mq",
               msg_pool,//缓存区
               sizeof(struct rx_msg),//一条消息的最大长度
               sizeof(msg_pool),//缓存区大小
               RT_IPC_FLAG_FIFP);//按FIFO分配多个线程获得的消息
    
    rt_device_open(serial,RT_DEVICE_FLAG_DMA_RX);//以DMA接收及轮询方式打开串口
	rt_device_set_rx_indicate(serial,uart_input);//设置接收回调(消息队列处理)函数
    rt_device_write(serial,0,str,(sizeof(str)-1));//发送字符串
    rt_thread_t thread=rt_thread_create("serial",serial_thread_entry,RT_NULL,1024,25,10);//创建串口处理线程
    if(thread!=RT_NULL)
        rt_thread_startup(thread);
    else
        ret=RT_ERROR;
    return ret;//返回结果
}

MSH_CMD_EXPORT(uart_dma,uart device dma test);//调试指令
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值