[攻城狮计划]RT-Thread—详解UART设备(基于RA2E1)

文章目录

  • [攻城狮计划]|RT-Thread—详解UART设备(基于RA2E1)
    • UART简介
    • 串口设备管理
    • 创建和注册串口设备
      • 创建
    • 访问串口设备
    • 示例代码

[攻城狮计划]|RT-Thread—详解UART设备(基于RA2E1)

开启攻城狮的成长之旅!这是我参与的由 CSDN博客专家 架构师李肯和 瑞萨MCU 联合发起的「 致敬未来的攻城狮计划 」的第4天,点击查看活动计划详情

首先非常感谢李老师能给我参加这个计划的机会,让我有机会接触到许多的开发板,同时也感谢瑞萨官方 为我们提供的开发板。在参加活动的期间,结识了许多的大佬,让我收获了许多的知识,我感觉短短的几天,收获的知识与开发经验比自己独自学习几个月的收获还要多。所以,希望各位大佬们也能加入李老师的这个活动,而且,李老师水平真的很高,而且非常耐心,谁来谁知道!!!!

好了,接下来我们开始进入今天的主题,如何在RA2E1上基于RT-Thread优雅地使用UART设备!

本文将会详细介绍有关RT-Thread的UART设备,但是考虑到可能有部分和我一样的初学者,所以有些具体细节并未介绍。其实本文中的内容并不仅仅适用于一款开发板,只要运行了RT-Thread操作系统的开发板,都是一样的。


UART简介

接下来,我们来详细介绍一下UART设备(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器),我们一般也叫其为串口。

UART 是异步串口通信协议的一种,工作原理就是是将传输数据的每个字符一位接一位地传输。它的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线(TX)发送数据的同时用另一根线(RX)接收数据。UART 串口通信有几个重要的参数:

  • 起始位:表示数据传输的开始,电平逻辑为 “0” 。
  • **数据位:**可能值有 5、6、7、8、9,表示传输这几个 bit 位数据。一般取值为 8,因为一个 ASCII 字符值为 8 位。
  • 停止位: 表示一帧数据的结束。电平逻辑为 “1”。
  • **奇偶校验位:**用于接收方对接收到的数据进行校验,校验 “1” 的位数为偶数 (偶校验) 或奇数(奇校验),以此来校验数据传送的正确性,使用时不需要此位也可以。
  • **波特率:**串口通信时的速率,它用单位时间内传输的二进制代码的有效位 (bit) 数来表示,其单位为每秒比特数 bit/s(bps)。常见的波特率值有 4800、9600、14400、38400、115200 等,数值越大数据传输的越快,波特率为 115200 表示每秒钟传输 115200 位数据。

对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则将无法通信。UART 串口传输的数据格式如下图所示:

[攻城狮计划]RT-Thread—详解UART设备(基于RA2E1)_第1张图片


串口设备管理

在RT-Thrread中,应用程序可通过通用I/O设备管理接口来访向串口硬件,可以按照轮询、中断或DMA等方式进行串口数据收发,也可以设置串口的波特率、数据位等。

串口设备驱动框架中定义了串口的设备模型,代码如下所示:

struct rt_serial_device
{
    struct rt_device          parent;		/* 继承 rt_device */
    const struct rt_uart_ops *ops;			/* 串口设备的操作方法 */
    struct serial_configure   config;		/* 串口设备的配置参数 */
    void *serial_rx;
    void *serial_tx;
    struct rt_device_notify rx_notify;		/* 通知结构 */
};

注:串口设备驱动程序根据串口设备模型定义,创建出具备硬件访问能力的串口设备实例,将该设备通过rt_ hw_serial_register()接口注册到串口设备驱动框架中。

我们主要的就是需要去实现这些操作方法,我们在下面会介绍。


创建和注册串口设备


创建

接下来,我们需要来创建一个串口设备,而主要需要做的就是实现串口设备的操作方法,也就是将其实例化。

/**
 * 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 (*transmit)(struct rt_serial_device       *serial,
                                 rt_uint8_t             *buf,
                                 rt_size_t               size,
                                 rt_uint32_t             tx_flag);
};
方法名称 方法描述
configure 串口配置操作函数
control 串口控制函数
putc 发送一个字符数据
getc 接收一个字符数据
transmit 多字节数据的的发送

这部分的实现在drv_usart_v2.c文件里面能看到。

串口设备被创建后,使用如下接口注册到串口设备驱动框架中:

rt_err_t rt_hw_serial_register	(	struct rt_serial_device * 	serial,
const char * 	name,
rt_uint32_t 	flag,
void * 	data 
)	
参数 描述
serial 串口设备句柄
name 串口设备名称
flag 串口设备模式标志
data 串口设备私有数据
返回 描述
RT_EOK 成功
-RT_ERROR 注册失败,已有其他驱动使用该name注册

访问串口设备

应用程序通过 RT-Thread提供的 I/O 设备管理接口来访问串口硬件,相关接口如下所示:

函数 描述
rt_device_open() 打开设备
rt_device_read() 读取数据
rt_device_write() 写入数据
rt_device_control() 控制设备
rt_device_close() 关闭设备

示例代码

#include 

#define SAMPLE_UART_NAME       "uart9"      /* 串口设备名称 */

/* 用于接收消息的信号量 */
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);

你可能感兴趣的:(rt-thread,c语言,单片机,物联网,硬件工程)