rt-thread uart驱动

        uart驱动描述基于GD32F470芯片。

        rt-thread提供了一套I/O设备模型,如果想要使用操作系统的驱动去进行操作,就得将具体芯片的硬件驱动注册到设备驱动框架上。

        关于rt-thread的I/O设备模型相关内容可以参考

rt-thread I/O设备模型-CSDN博客文章浏览阅读554次,点赞21次,收藏5次。事实上即便有操作系统也是可以直接使用裸机操作方式去操作的,但是这样做有几个缺点,一是代码看起来比较不统一,显得很乱,二是软件没有分层,耦合严重,不利于维护,三是裸机操作属于不受操作系统管控的部分,在对硬件资源的占用上,裸机操作可能会与操作系统操作产生冲突导致异常。实际上具体设备的驱动和操作系统的驱动函数是有个对应的,而用来实现这种对应的就是rt_device_register()函数,通过这个函数就可以将种类繁多的驱动注册到设备驱动框架上,之后就可以使用统一的操作函数了。可以看到最终访问设备用的是。https://blog.csdn.net/u011436603/article/details/136492057?spm=1001.2014.3001.5501        本次主要描述如何实现GD32F470的uart设备驱动

        首先我们查看一下rt-thread的官方驱动文件serial.c,这里面有很多关于串口的函数,包括串口设备注册函数,串口设备操作函数,串口中断函数等。这个文件里的函数都是抽象的,并没有具体的硬件操作方法,我们要做的就是给它赋予实际的硬件操作能力。

        来看一下串口设备的结构,包含了通用设备类型,串口操作函数,串口配置以及两个空类型的指针函数。

        操作函数包含配置,控制,发送,接收,还有个dma传输。

        配置包含uart常见的配置参数,如波特率,数据位等。

struct rt_serial_device
{
    struct rt_device          parent;

    const struct rt_uart_ops *ops;
    struct serial_configure   config;

    void *serial_rx;
    void *serial_tx;
};

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;
};

        接下来我们先注册串口驱动,首先定义一个数据结构 gd32_uart,用于具体硬件初始化用,再定义一个串口设备变量。

struct gd32_uart
{             
    uint32_t uart_periph;           //Todo: 3bits
    IRQn_Type irqn;                 //Todo: 7bits    
    rcu_periph_enum per_clk;        //Todo: 5bits
    rcu_periph_enum tx_gpio_clk;    //Todo: 5bits
    rcu_periph_enum rx_gpio_clk;    //Todo: 5bits
    uint32_t tx_port;               //Todo: 4bits
    uint16_t tx_af;                 //Todo: 4bits
    uint16_t tx_pin;                //Todo: 4bits
    uint32_t rx_port;               //Todo: 4bits
    uint16_t rx_af;                 //Todo: 4bits
    uint16_t rx_pin;                //Todo: 4bits    
};

static struct gd32_uart uarts = 
{
    USART0,                                 // uart peripheral index
    USART0_IRQn,                            // uart iqrn
    RCU_USART0, RCU_GPIOA, RCU_GPIOA,       // periph clock, tx gpio clock, rt gpio clock
    GPIOA, GPIO_AF_7, GPIO_PIN_9,           // tx port, tx alternate, tx pin
    GPIOA, GPIO_AF_7, GPIO_PIN_10,          // rx port, rx alternate, rx pin
        
}

struct rt_serial_device serial0;

        接下来是实现操作系统设备驱动的各个函数,包括configure,control,getc,putc,至于dma_transmit我们暂时没用到,然后完成设备注册。

void gd32_uart_gpio_init(struct gd32_uart *uart)
{
    /* enable USART clock */
    rcu_periph_clock_enable(uart->tx_gpio_clk);
    rcu_periph_clock_enable(uart->rx_gpio_clk);
    rcu_periph_clock_enable(uart->per_clk);

    /* connect port to USARTx_Tx */
    gpio_af_set(uart->tx_port, uart->tx_af, uart->tx_pin);

    /* connect port to USARTx_Rx */
    gpio_af_set(uart->rx_port, uart->rx_af, uart->rx_pin);

    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(uart->tx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, uart->tx_pin);
    gpio_output_options_set(uart->tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, uart->tx_pin);

    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(uart->rx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, uart->rx_pin);
    gpio_output_options_set(uart->rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, uart->rx_pin);
    
    NVIC_SetPriority(uart->irqn, 0);
    NVIC_EnableIRQ(uart->irqn);
}

static rt_err_t gd32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart =(struct gd32_uart *) serial->parent.user_data;
    
    gd32_uart_gpio_init(uart);
    
    usart_baudrate_set(uart->uart_periph, cfg->baud_rate);

    switch (cfg->data_bits)
    {
    case DATA_BITS_9:
        usart_word_length_set(uart->uart_periph, USART_WL_9BIT);
        break;

    default:
        usart_word_length_set(uart->uart_periph, USART_WL_8BIT);
        break;
    }

    switch (cfg->stop_bits)
    {
    case STOP_BITS_2:
        usart_stop_bit_set(uart->uart_periph, USART_STB_2BIT);
        break;
    default:
        usart_stop_bit_set(uart->uart_periph, USART_STB_1BIT);
        break;
    }

    switch (cfg->parity)
    {
    case PARITY_ODD:
        usart_parity_config(uart->uart_periph, USART_PM_ODD);
        break;
    case PARITY_EVEN:
        usart_parity_config(uart->uart_periph, USART_PM_EVEN);
        break;
    default:
        usart_parity_config(uart->uart_periph, USART_PM_NONE);
        break;
    }

    usart_receive_config(uart->uart_periph, USART_RECEIVE_ENABLE);
    usart_transmit_config(uart->uart_periph, USART_TRANSMIT_ENABLE);
    usart_enable(uart->uart_periph);

    return RT_EOK;
}

static rt_err_t gd32_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart =(struct gd32_uart *) serial->parent.user_data;

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable rx irq */
        NVIC_DisableIRQ(uart->irqn);
        /* disable interrupt */
        usart_interrupt_disable(uart->uart_periph, USART_INT_RBNE);

        break;
    case RT_DEVICE_CTRL_SET_INT:
        /* enable rx irq */
        NVIC_EnableIRQ(uart->irqn);
        /* enable interrupt */
        usart_interrupt_enable(uart->uart_periph, USART_INT_RBNE);
        break;
    }

    return RT_EOK;
}

static int gd32_putc(struct rt_serial_device *serial, char ch)
{
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart =(struct gd32_uart *) serial->parent.user_data;

    usart_data_transmit(uart->uart_periph, ch);
    while((usart_flag_get(uart->uart_periph, USART_FLAG_TC) == RESET));
    
    return 1;
}

static int gd32_getc(struct rt_serial_device *serial)
{
    int ch;
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart =(struct gd32_uart *) serial->parent.user_data;

    ch = -1;
    if (usart_flag_get(uart->uart_periph, USART_FLAG_RBNE) != RESET)
        ch = usart_data_receive(uart->uart_periph);
    return ch;
}

static const struct rt_uart_ops gd32_uart_ops =
{
    gd32_configure,
    gd32_control,
    gd32_putc,
    gd32_getc,
};

int gd32_hw_usart_init(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    
    serial0.ops    = &gd32_uart_ops;
    config.baud_rate=BAUD_RATE_9600;
    config.data_bits=DATA_BITS_8;
    config.stop_bits=STOP_BITS_1;
    config.parity=PARITY_NONE;
    serial0.config = config;
    rt_hw_serial_register(&serial0,
                          "uart0",
                          RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                          (void *)&uarts);
    

    return 0;
}
INIT_BOARD_EXPORT(gd32_hw_usart_init);

        完成上面这些基本就能够使用uart了,但是因为我们配置了串口中断,所以我们还需要去实现串口中断函数。

static void uart_isr(struct rt_serial_device *serial)
{
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart =(struct gd32_uart *) serial->parent.user_data;

    /* UART in mode Receiver -------------------------------------------------*/
    if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_RBNE) != RESET) &&
            (usart_flag_get(uart->uart_periph, USART_FLAG_RBNE) != RESET))
    {
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
        /* Clear RXNE interrupt flag */
        usart_flag_clear(uart->uart_periph, USART_FLAG_RBNE);
    }
}

void USART0_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    uart_isr(&serial0);

    /* leave interrupt */
    rt_interrupt_leave();
}

        现在,uart驱动已经注册完毕,剩余的就是具体的应用功能了。这里为了减少篇幅,只列举了单串口,例程里提供了7个串口的驱动,有兴趣可以看看https://download.csdn.net/download/u011436603/88914288?spm=1001.2014.3001.5503

        下面简单介绍一种应用的方式

        首先通过rt_device_find去找到uart设备,使用rt_device_open去打开设备。注意,在rt_device_open打开设备时,如果首次打开,会调用device_init进行初始化,此时才会执行gd32_configure进行串口的初始化。

        之后可以创建两个线程,一个发送一个接收,通过操作系统提供的事件来进行同步。发送线程时刻等待发送事件,一旦接收到发送事件则开始发送数据。接收线程时刻等待接收事件,一旦接收到接收事件则从串口中读数据填入缓存,以备后续处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/434877.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot的母婴商城

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本母婴商城系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息&am…

js五星评价的制作方法

方法有两种&#xff0c;1、jquer插件&#xff1b;2、图片循环&#xff1b; 第一种、效果图 代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"…

Uniapp壁纸小程序源码/双端微信抖音小程序源码

Uniapp壁纸小程序源码&#xff0c;双端微信抖音小程序源码。WordPress后台的小程序。为一些做壁纸类自媒体的朋友解决变现难的问题&#xff0c;抖音壁纸常见的图片取号码&#xff0c;微信的壁纸公众号小程序均可使用该小程序进行变现&#xff0c;这款小程序后端为WordPress&…

44、网络编程/数据库相关操作练习20240306

一、代码实现数据库的创建&#xff08;员工信息表&#xff09;&#xff0c;并存储员工信息&#xff08;工号、姓名、薪资&#xff09;&#xff0c;能实现增加人员信息、删除人员信息、修改人员薪资操作。 代码&#xff1a; #include<myhead.h>int do_update(sqlite3 *p…

深色系可视化界面看腻了,来点浅色系?安排,20页来了。

只要不放在大屏上展示&#xff0c;贝格前端工场还是非常推崇浅色系的可视化界面&#xff0c;把它作为配色的首选 。浅色系可视化界面具有以下几个优势&#xff1a; 清晰明了 浅色系界面通常使用明亮的颜色&#xff0c;如白色、浅灰色等&#xff0c;使界面元素更加清晰可见。这…

微前端之什么是微前端

什么是微前端 微前端分类 基于路由的微前端&#xff1a;组件化微前端&#xff1a;iframe嵌入式微前端&#xff1a; 优点缺点 动态加载/懒加载微前端&#xff1a;微应用容器化方案&#xff1a; 微前端解决方案 single-spa阿里巴巴 Cloud Alfaiframe 方案Web ComponentsModule Fe…

【工作记录】Threejs学习笔记-引入OrbitControls

前言 前一篇文章我们介绍了three.js中的基础概念&#xff0c;并给出了展示整体流程的一个简单示例&#xff0c; 本文我们继续研究。 问题 我们在很多3d效果图上都能看到鼠标移动或者缩进实现旋转或者放大缩小的效果&#xff0c;这个在three.js中是通过OrbitControls这个组件…

计算机系统缺少cv100.dll文件解决方法(最新)

cv100.dll 是一个Windows操作系统中的动态链接库&#xff08;DLL&#xff09;文件。DLL文件是包含可由多个程序共享的代码和数据的模块&#xff0c;以减少磁盘空间占用并提高系统性能。根据收集到的信息&#xff0c;cv100.dll 文件可能与图像处理、计算机视觉相关的功能有关。 …

springcloud:3.8测试限流

服务提供者【test-provider8001】 Openfeign远程调用服务提供者搭建 文章地址http://t.csdnimg.cn/06iz8 相关接口 测试远程调用&#xff1a;http://localhost:8001/payment/index 服务消费者【test-consumer-resilience4j8004】 Openfeign远程调用消费者搭建 文章地址http://t…

功能安全概念梳理二

什么是SEooC&#xff1f;SEooC和element有什么不一样&#xff1f; 参考链接&#xff1a;车规级 | ISO26262中对独立安全要素&#xff08;SEooC&#xff09;的开发要求 汽车功能安全(ISO 26262)系列: 到底什么是SEooC开发 安全措施(Safety measure)和安全机制(Safety mechanis…

[Redis]——缓存击穿和缓存穿透及解决方案(图解+代码+解释)

目录 一、缓存击穿&#xff08;热点Key问题&#xff09; 1.1 问题描述 1.2 解决方案及逻辑图 1.2.1 互斥锁 1.2.2 逻辑过期 二、缓存穿透 2.1 问题描述 2.2 解决方案逻辑图 2.2.1 缓存空对象 2.2.2 布隆过滤器 一、缓存击穿&#xff08;热点Key问题&#xff09; 个人理…

鸿蒙岗位大反攻:几十家头部应用加入鸿蒙后,鸿蒙工程师薪酬水涨船高

华为原生鸿蒙生态开始百花齐放&#xff1a;已经有几十家国内应用软件&#xff0c;宣布适配华为原生鸿蒙APP&#xff0c;其中就包括支付宝、美团、新浪微博、小红书、B站、高德地图、58同城等多家头部应用。 因为华为已经公开宣布&#xff1a;2024年的鸿蒙5.0系统将不再兼容安卓…

Redis(5.0)

1、什么是Redis Redis是一种开源的、基于内存、支持持久化的高性能Key-Value的NoSQL数据库&#xff0c;它同时也提供了多种数据结构来满足不同场景下的数据存储需求。 2、安装Redis&#xff08;Linux&#xff09; 2.1、去官网&#xff08;http://www.redis.cn/&#xff09;下…

Hololens 2应用开发系列(3)——MRTK基础知识及配置文件配置(中)

Hololens 2应用开发系列&#xff08;3&#xff09;——MRTK基础知识及配置文件配置&#xff08;中&#xff09; 一、前言二、输入系统2.1 MRTK输入系统介绍2.2 输入数据提供者&#xff08;Input Data Providers&#xff09;2.3 输入动作&#xff08;Input Actions&#xff09;2…

云服务器迁移--天翼云篇(最省钱解决官方未解问题)

0x00 背景 距离上次从华为云迁移到天翼云过了一年之久&#xff0c;之前买的一年期天翼云服务器快到期了&#xff0c;这次需要把这个池子(内蒙)的天翼云服务器迁移到新买的另一个池子的天翼云里。上一篇文章华为云迁移到天翼云教程在 云服务器迁移 (全网最省钱最详细攻略)_服务…

二分以及练习题目

二分模板 判断是否可以二分 &#xff08;1&#xff09;单调性 备选答案集是有序的 &#xff08;2&#xff09;二段性 在检查了mid是否符合要求之和&#xff0c;我可以舍弃左右某一边的答案 两个模板 关键词&#xff1a;满足条件的最小值&#xff0c;最大值最小&#xff0…

Django学习记录08——图表及文件上传案例

1.图表Echarts的应用 Apache ECharts 1.1 使用方法 引用echarts.js即可到官方文档中查询使用 1.2 常用图标的使用 图表展示页面的部署&#xff08;主要展示折线图、柱状图、饼图&#xff09; {% block content %}<div class"container"><div class&qu…

李沐动手学习深度学习——4.1练习

1. 计算pReLU激活函数的导数。 pReLU激活函数公式根据课本有如下&#xff1a; p R e L U ( x ) max ⁡ ( 0 , x ) α min ⁡ ( 0 , x ) \mathrm{pReLU}(x) \max(0, x)\alpha \min(0,x) pReLU(x)max(0,x)αmin(0,x) 对应的函数图像为: 对应的导数计算为&#xff1a; d p R…

github双因子认证

最近换了个安卓手机&#xff0c;打算让之前的苹果手机退役了&#xff0c;所以需要重新搞GitHub的Two-factor authentication 步骤如下&#xff1a; 1. 访问安全中心 https://github.com/settings/security 2. 点击Authenticator app右侧按钮 3. 下载腾讯身份验证器&#xff…

解密程序员的“藏宝图”:我的祖传代码大公开

程序员是如何看待“祖传代码”的&#xff1f; 大家好&#xff0c;我是小明&#xff0c;一位充满好奇心和分享热情的程序员。今天&#xff0c;我要为大家揭开我心中的“藏宝图”——那些我认为值得传世的祖传代码。让我们一同踏上这场奇妙的代码冒险之旅吧&#xff01; 宝物一…