• RT-Thread UART设备


    UART

    UART(Universal Asynchronous Receiver/Trasmitter)通用异步收发器,UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。是在应用程序开发过程中使用频率最高的数据总线。

    UART串口的特点是将数据一位一位地顺序传送,只要2根传输线就可以实现双向通信,一根数据线发送数据的同时用另一根线接收数据。

    UART串口通信有几个重要参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用UART串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。

    在这里插入图片描述
    起始位:表示数据传输的开始,电平逻辑为 “0” 。

    数据位:可能值有 5、6、7、8、9,表示传输这几个 bit 位数据。一般取值为 8,因为一个 ASCII 字符值为 8 位。

    奇偶校验位:用于接收方对接收到的数据进行校验,校验 “1” 的位数为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性,使用时不需要此位也可以。

    停止位: 表示一帧数据的结束。电平逻辑为 “1”。

    波特率:串口通信时的速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数 bit/s(bps)。常见的波特率值有 4800、9600、14400、38400、115200等,数值越大数据传输的越快,波特率为 115200 表示每秒钟传输 115200 位数据。

    串口数据接收和发送数据的模式分为3种:中断模式、轮询模式、DMA模式。在使用的时候,这3种模式只能选其一,若串口的打开参数oflags没有指定使用中断模式或者DMA模式,则默认使用轮询模式。

    DMA(Direct Memroy Access)即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有像中断处理方式那样保留现场和恢复现场的过程,通过DMA控制器为RAM与I/O设备开辟一条直接传送数据的通路,这就节省了CPU的资源来做其它操作。使用DMA传输可以连续获取或发送一段信息而不占用中断或延时,在通信频繁或有大段信息要传输时非常有用。

    注:* RT_DEVICE_FLAG_STREAM:流模式用于向串口终端输出字符串:当输出的字符是 “\n” (对应 16 进制值为 0x0A)时,自动在前面输出一个 “\r”(对应 16 进制值为 0x0D) 做分行。

    注:默认串口配置接收数据缓冲区大小为 RT_SERIAL_RB_BUFSZ,即 64 字节。若一次性数据接收字节数很多,没有及时读取数据,那么缓冲区的数据将会被新接收到的数据覆盖,造成数据丢失,建议调大缓冲区,即通过 control 接口修改。在修改缓冲区大小时请注意,缓冲区大小无法动态改变,只有在 open 设备之前可以配置。open 设备之后,缓冲区大小不可再进行更改。但除缓冲区之外的其他参数,在 open 设备前 / 后,均可进行更改。

    若实际使用串口的配置参数与默认配置参数不符,则用户可以通过应用代码进行修改。修改串口配置参数,如波特率、数据位、校验位、缓冲区接收 buffsize、停止位等。

    接收数据

    rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
    
    • 1
    • dev:设备句柄。
    • pos:读取数据偏移量,此参数串口设备未使用。
    • buffer:缓冲区指针,读取的数据会被保存在缓冲区中。
    • size:读取数据的大小

    串口使用中断接收模式并配合接收回调函数

    static rt_device_t serial;
    static struct re_semaphore rx_sem;
    
    //接收数据线程
    static void serial_thread_entry(void *parameter)
    {
    	char ch;
    	while(1)
    	{
    		while(rt_device_read(serial, -1, &ch, 1) !=1)
    		{;
    			rt_sem_take(&rx_sem,RT_WAITING_FOREVER)
    		}
    		ch = ch + 1;
    		rt_device_write(serial, 0, &ch, 1);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    中断接收及轮询发送

    1. 首先查找串口设备获取设备句柄。
    2. 初始化回调函数发送使用的信号量,然后以读写及中断接收方式打开串口设备。
    3. 设置串口设备的接收回调函数,之后发送字符串,并创建读取线程。

    读取线程会尝试读取一个字符数据,如果没有数据则会挂起并等待信号量,当串口设备接收到一个数据时会触发中断并调用接收回调函数,此函数会发送信号量唤醒线程,此时线程会马上读取接收到的线程。

    此示例代码不局限于特定的 BSP,根据 BSP 注册的串口设备,修改示例代码宏定义 SAMPLE_UART_NAME 对应的串口设备名称即可运行。

    /*
     * 程序清单:这是一个 串口 设备使用例程
     * 例程导出了 uart_sample 命令到控制终端
     * 命令调用格式:uart_sample uart2
     * 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
     * 程序功能:通过串口输出字符串"hello RT-Thread!",然后错位输出输入的字符
    */
    
    #include 
    
    #define SAMPLE_UART_NAME       "uart2"
    
    /* 用于接收消息的信号量 */
    static struct rt_semaphore rx_sem;
    static rt_device_t serial;
    
    /* 接收数据回调函数 */
    static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
    {
        /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
        rt_sem_release(&rx_sem);
    
        return RT_EOK;
    }
    
    static void serial_thread_entry(void *parameter)
    {
        char ch;
    
        while (1)
        {
            /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
            while (rt_device_read(serial, -1, &ch, 1) != 1)
            {
                /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
                rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
            }
            /* 读取到的数据通过串口错位输出 */
            ch = ch + 1;
            rt_device_write(serial, 0, &ch, 1);
        }
    }
    
    static int uart_sample(int argc, char *argv[])
    {
        rt_err_t ret = RT_EOK;
        char uart_name[RT_NAME_MAX];
        char str[] = "hello RT-Thread!\r\n";
    
        if (argc == 2)
        {
            rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
        }
        else
        {
            rt_strncpy(uart_name, SAMPLE_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_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
        /* 以中断接收及轮询发送模式打开串口设备 */
        rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
        /* 设置接收回调函数 */
        rt_device_set_rx_indicate(serial, uart_input);
        /* 发送字符串 */
        rt_device_write(serial, 0, str, (sizeof(str) - 1));
    
        /* 创建 serial 线程 */
        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 命令列表中 */
    MSH_CMD_EXPORT(uart_sample, uart device sample);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    DMA接收及轮询发送

    当串口接收到一批数据后会调用接收回调函数,接收回调函数会把此时缓冲区的数据大小通过消息队列发送给等待的数据处理线程
    线程获取到消息后被激活,并读取数据。
    一般情况下,DMA接收模式会结合DMA接收完成中断串口空闲中断完成数据接收。

    在这里插入图片描述

    串口接收不定长数据

    串口接收不定长数据需要用户在应用层进行处理,一般会有特定的协议,比如一帧数据可能会有起始标记位、数据长度位、数据、终止标记位等,发送数据帧时按照约定的协议进行发送,接收数据时按照协议进行解析。

    以下是一个简单的串口接收不定长数据示例代码,仅做了数据的结束标志位DATA_CMD_END,如果遇到结束标志,表示一帧数据结束。

    1. 首先查找串口设备获取设备句柄。
    2. 初始化回调函数发送使用的信号量,然后以读写以及中断接收方式打开串口设备。
    3. 设置串口设备的接收回调函数,之后发送字符串,并创建解析数据线程。

    解析数据线程会尝试读取一个字符数据,如果没有数据则会挂起并等待信号量,当串口设备接收到一个数据时会触发中断并调用接收回调函数,此函数会发送信号量唤醒线程,此时线程会马上读取接收到的数据。在解析数据时,判断结束符,如果结束,则打印数据。

    此示例代码不局限于特定的BSP,根据BSP注册的串口设备,修改示例代码宏定义SAMPLE_UART_NAME 对应的串口设备名称即可。

  • 相关阅读:
    Java项目:基于JavaWeb的教务管理系统的设计与实现
    HZOJ-251:士兵
    (206)Verilog HDL:脉冲边沿检测2
    Flink Hudi DataStream API代码示例
    Java 8实战(四)- Lambda类型推断与方法引用
    机器学习--支持向量机(sklearn)
    设计模式-访问者模式-笔记
    5.使用日志+自定义全局异常过滤器
    FFplay文档解读-21-音频过滤器六
    Nginx 相关介绍(Nginx是什么?能干嘛?)
  • 原文地址:https://blog.csdn.net/Caramel_biscuit/article/details/133121812