DJYOS驱动开发系列一:基于DJYOS的UART驱动编写指导手册

news2025/1/16 17:53:43

1.概述

DJYOS设计通用的串口驱动模型,在此模型的基础上,移植到不同硬件平台时,只需提供若干硬件操作函数,即可完成串口驱动开发,使开发工作变得简单而快速执行效率高。
DJYOS源代码都有特定的存放位置,
建议文件路径:在eclipse工程中的目录中为:
src->OS_code->bsp->cpudrv->src->cpu_peri_uart.c。
相应的头文件目录为:
src->OS_code->bsp->cpudrv-> src->cpu_peri_uart.h。
在文件系统(硬盘)中的目录结构是:
djysrc\bsp\cpudrv\cpu_name\src\cpu_peri_uart.c。
如果UART为CPU所在板件的板级外设,则
建议文件路径:在eclipse工程中的目录中为:
src->OS_code->bsp->boarddrv->src->chip_name.c。
相应的头文件目录为:
src->OS_code->bsp-> boarddrv -> src-> chip_name.h。
在文件系统(硬盘)中的目录结构是:
djysrc\bsp \chipdrv\chip_name\chip_name.c。

本指导手册简明扼要的介绍了都江堰操作系统的UART驱动模型,着重讲解了在此驱动架构下编写具体UART控制芯片的驱动编写方法。

2.驱动架构

UART驱动架构如图 21所示。该模型包括通用设备驱动driver.c及 UART通用层uart.c及底层硬件驱动部分cpu_peri_uart.c。它具有明显分层结构,顶层是应用程序,它通过访问设备驱动接口层访问串口通用接层;串口通用层是底层驱动与设备驱动之间桥梁,它调用了若干钩子函数,通过钩子函数间接访问实现串口收发。最下面一层是由BSP设计者提供的硬件驱动,BSP设计者只需要按照要求实现钩子函数。
BSP驱动程序人员的工作是编写图 21中底层驱动部分代码,它是根据具体UART控制器(即不同的CPU)实现与硬件相关的UART寄存器级别的操作。
驱动编写的主要工作,是:
1、初始化uart硬件。
2、实现3个回调函数。
3、若使用中断,则还要实现中断服务函数。
4、若printk使用uart(绝大多数情况是这样),则要实现PutChark、Putsk、GetChark、Getsk这几个函数。
在这里插入图片描述

图 2-1UART驱动架构图

3.UART驱动接口

3.1.初始化函数

3.1.1.Step1:初始化硬件

1、包括默认的参数配置,如IO、波特率、停止位、奇偶校验位等;
2、挂载UART中断到中断系统,并配置UART的中断类型,如配置为异步信号;

3.1.2.Step2:初始化参数结构体

UART初始化参数数据类型为truct tagUartParam,初始化该数据类型变量的成员,并调用函数UART_InstallPort(),注册UART为设备,并将它添加到资源列表。
对该函数的参数struct tagUartParam作如说明。
代码 3-1 UART参数初始化结构体

// 串口模块初始化结构体
struct tagUartParam
{
    const char *Name;					//UART名称,如UART0
    u32 TxRingBufLen;					//发送缓冲区配置字节数
    u32 RxRingBufLen;					//接收缓冲区配置字节数
    u32 Baud;							//默认的波特率
    ptu32_t UartPortTag;				//UART私有标签,如寄存器基址
    UartStartSend StartSend;			//启动发送回调函数指针
    UartDirectSend DirectlySend;		//直接轮询发送回调函数指针
    UartControl UartCtrl;				//控制函数回调函数指针
};

DJYOS通用驱动架构是先将用户发送数据保存到发送环形缓冲区,然后通过操作系统的通信方式(如信号量通信)通知驱动层将发送环形缓冲区中的数据写入UART控制器的发送寄存器;同理,驱动接收到的数据缓存到接收环形缓冲区,然后通知应用层读取接收环形缓冲区数据。
发送和接收缓冲区的初始化由UART_InstallPort()完成,BSP驱动编程人员只需配置struct tagUartParam中的参数TxRingBufLen和RxRingBufLen,它们分别是配置发送和接收缓冲区的字节长度。此外,UartPortTag为私有标签,对于UART,需赋值为UART控制器的寄存器基址。
UART三个回调函数参数的原型如所示,其中PrivateTag就是结构体中UART的私有标签,寄存器基址。
代码 3-2 UART回调函数类型定义

typedef u32 (* UartStartSend)(ptu32_t PrivateTag,u32 timeout);
typedef u32 (* UartDirectSend)(ptu32_t PrivateTag,u8 *send_buf,u32 len,u32 timeout);
typedef ptu32_t (*UartControl)(ptu32_t PrivateTag,u32 cmd, u32 data1,u32 data2);

3.1.3.Step3:挂载设备

挂载设备API函数UART_InstallPort(),该函数完成了如下功能:
1、初始化串口发送或接收所需的信号量或互斥量;
2、动态分配发送和接收数据的环形缓冲区空间,并初始化环形缓冲区;
3、动态分配UART的控制块UCB,并将UART添加为通用设备(即driver);
4、操作成功,则返回UCB指针,否则,返回NULL。
值得注意的是,UART驱动编程人员对添加串口设备的函数返回值的判断是极其重要的步骤,并将有效的返回值保存为静态变量。如所示,UART_Param是已经初始化好的参数结构类型,pUartCB是定义在C文件中类型为static struct tagUartCB *的静态变量数组,UART_InstallPort()返回值保存到该静态变量。
代码 3-3 添加串口设备

	pUartCB[serial_no] = UART_InstallPort(&UART_Param);
	if( pUartCB[serial_no] == NULL)
		return 0;
	else
		return 1;

3.2.回调函数

DJYOS的UART驱动架构采用注册回调函数的方式,能有效的将串口通用层和硬件驱动层分开,实现模块化编程。本章将对回调函数的编程要求进行详细的说明,它们是BSP驱动开发人员应该着重关心的部分。

3.2.1.启动发送

static u32 __UART_SendStart (tagUartReg *Reg,u32 Timeout)
参数:
Reg:UART控制寄存器基址。
Timeout:超时时间,单位us。
返回:发送字节数。
说明:该函数的关键在于启动发送功能,如触发发送中断,在中断实现发送数据的功能。比较通用的方法是填写FIFO深度的数据,触发发送中断,部分UART控制器能够实现不发送数据仍然能触发发送中断的功能,该函数将得到进一步的简化。
部分核心示例代码如代码 34所示,调用UART_PortRead读取发送环形缓冲区数据,数据大小为FIFO深度的字节数,并填写到FIFO,然后使能中断。注意,若启动发送时UART硬件缓冲区非空,则无须发送触发发送中断。
代码 3-4 启动发送

__UART_TxIntDisable(Reg);
if(__UART_TxTranEmpty(Reg))
{
    fifodep = UART_PortRead(pUartCB[port],ch,fifodep,0);//读FIFO大小数据
    for(num = 0; num < fifodep; num++)
    {
        Reg->D = ch[num];
    }
}
__UART_TxIntEnable(Reg);

3.2.2.直接发送

static u32 __UART_SendDirectly(tagUartReg *Reg,u8 *SendBuf,u32 Len,u32 Timeout)
参数:
Reg:UART控制寄存器基址。
SendBuf: 发送数据缓冲地址。
Len:发送字节数。
Timeout:超时时间,单位us。
返回:发送字节数。
说明:串口轮询发送的功能,主要实现操作系统未启动时的串口发送功能。由于轮询发送是比较耗时的发送方式,因此,有必要对发送过程进行超时处理,同时,发送过程必须关闭中断。
部分核心示例代码如代码 35所示,判断发送缓冲区为空后,往硬件发送寄存器中写数据,直到发送完成,若等待超时,则退出循环。发送过程必须关闭中断。
代码 3-5 轮询发送

__UART_TxIntDisable(Reg);
for(result=0; result < len; result ++)
{
	// 超时或者发送缓冲为空时退出
	while((false == __UART_TxTranEmpty(Reg))&& (timeout > 0))
	{
		timeout--;
		Djy_DelayUs(1);
	}
	if(timeout == 0)
		break;
	Reg->D = send_buf[result];
}
__UART_TxIntEnable(Reg);

3.2.3.控制函数

static ptu32_t __UART_Ctrl(tagUartReg *Reg,u32 Cmd, u32 Data1,u32 Data2)
参数:
Reg:UART控制寄存器基址。
Cmd:串口命令字,参见源码uart.h头文件定义。
Data1、Data2:含义依Cmd而定。
返回:0,执行成功,其他,自定义。
说明:底层驱动的控制函数主要完成与具体硬件相关的控制功能,如设置波特率、配置串口参数、暂停启动串口等。UART控制命令及参数说明如表 3-1所示,需要注意,并非所有的命令都需要用到,驱动编程人员须根据实际情况选择使用的命令。
表 3-1 UART控制命令
在这里插入图片描述

3.3.中断函数

因为采用中断的方式收发数据效率更高,对CPU的消耗低,所以建议驱动编写人员采用中断的方式实现UART的收发。
首先,必须将UART的中断号添加到critical文件夹下面的C文件critical.c中tg_IntUsed数组,使中断系统初始化时,将对应的UART中断号添加到中断系统中;其次,UART初始化时,配置UART中断类型和触发条件等。最后,完成中断服务函数UART_ISR。

3.3.1.中断实现过程

比较典型的UART中断函数实现过程如图 31所示。
在中断服务函数内部,通过读中断标志位判断中断类型,并进入不同的处理过程。接收中断时,将接收数据写入接收缓冲区;发送中断表示当前硬件FIFO中数据已经发送完成,则继续读发送缓冲区数据,直至发送结束;帧错误中断是指发生了校验、帧格式或起始结束位错误时的中断。无论是发送还是接收中断,都需要注意清中断标志位。

图 31 UART中断流程图

3.3.2.注意事项

中断服务函数的实现过程因UART控制器的不同,处理情况也有所差异,列出如下注意事项,以供参考。
1、中断服务函数中要注意通过判断中断标志来区分中断类型,并及时清中断标志;
2、中断类型为发送中断时,UART_PortRead()返回为0,则表示发送环形缓冲区中无数据,建议关闭发送中断;
3、帧错误处理方式由应用程序通过Ctrl函数注册,登记发生错误时弹出的事件。

4.移植建议

由于大部分的UART控制器的设计基本相似,因此,BSP程序人员可采取下面的步骤快速的完成DJYOS驱动架构下UART底层驱动的开发。
1、拷贝其他工程已测试通过的UART驱动文件cpu_peri_uart.c/cpu_peri_uart.h;
2、添加UART的中断号到critical.c文件下面tg_IntUsed数组;
3、修改cpu_peri_uart.c/cpu_peri_uart.h中与具体UART寄存器相关的部分;
4、实现中断收发数据,并测试通过。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DJYGUI系列文章五:GK显示器接口

1 GK显示器接口概述 显示器是图形显示的终端&#xff0c;图形的所有操作都会直接或间接的体现在显示器上面。DJYGUI支持多显示器、虚显示器和镜像显示器的功能。应用程序在调用API函数绘图前&#xff0c;需安装显示器&#xff0c;按照GK显示器标接口实现驱动函数。 GK的底层硬件…

DCS系统组态设计实验

太原理工大学控制仪表实验之DCS系统组态设计实验 DCS系统组态设计一.实验内容1.根据自己的理解&#xff0c;复述实验整体流程&#xff0c;并画出实验整体流程图。2.根据视频&#xff0c;写出DCS 信号通道接线关系表。即主控站DCS模块名称&#xff0c;模块型号&#xff0c;I/O模…

跟艾文学编程《Python基础》(5)Python的文件操作

作者&#xff1a; 艾文&#xff0c;计算机硕士学位&#xff0c;企业内训讲师和金牌面试官&#xff0c;公司资深算法专家&#xff0c;现就职BAT一线大厂。邮箱&#xff1a; 1121025745qq.com博客&#xff1a;https://wenjie.blog.csdn.net/内容&#xff1a;跟艾文学编程《Python…

linux网络编程epoll内核实现代码分析

1、linux内核epoll相关数据结构 1.1、epoll相关数据结构类图 1.2、关键数据结构说明 socket_wq结构体包含一个__wait_queue_head成员&#xff0c;__wait_queue_head用于连接wait_queue_t链表&#xff0c;对于epoll而言就是连接eppoll_entry&#xff1b; eppoll_entry包含一个e…

第七届信息类研究生学术论坛参赛有感

因为疫情不仅感叹时光飞逝&#xff0c;上了大半年的网课再次回到校园已经有师弟师妹了。今年的研究生学术论坛更卷了&#xff0c;入围了88项作品。这次科研作品征集研究生在学期间信息类相关研究成果&#xff0c;鼓励实物参展&#xff0c;包括软件系统、硬件系统等&#xff0c;…

Kubernetes(k8s)CNI(Calico)网络模型原理

文章目录一、概述二、Calico 架构和核心组件三、什么是BGP&#xff1f;三、Calico 两种网络模式1&#xff09;IPIP 模式2&#xff09;BGP 模式四、安装Calico插件1&#xff09;通过helm安装Calico2&#xff09;通过yaml文件安装3&#xff09;k8s flannel网络切换calico1、卸载f…

适配不同场景的RestTemplate

一个基本实现 如果项目里可能只是偶尔通过一个url&#xff0c;发起一个http请求&#xff0c;一个基本实现如下&#xff1a; Configuration public class RestTemplateConfiguration {Beanpublic RestTemplate restTemplate() {RestTemplate restTemplate new RestTemplate()…

项目常遇到的问题

这里写自定义目录标题1&#xff1a;uniapp生成二维码2&#xff1a;uniapp onShow接收参数3&#xff1a;javascript如何获取对象的key和value4&#xff1a;uni-app&#xff1a;页面直接传递复杂参数5&#xff1a;js对于数组元素相同的分类方法1&#xff1a;uniapp生成二维码 选择…

关联式容器(Associative Container)

1:什么是关联式容器&#xff1f; 关联式容器依照特定的排序准则 自动为元素排序 元素可以是任何类型的value 也可以是 key/value pair key可以是任何类型 映射至一个相关value 而value也可以是任意类型 通常是所有容器默认以<进行比较 也可以通过自己的比较函数 定义出不同的…

Dubbo基础

目录 什么是 RPC 那为什么要有 RPC&#xff0c;HTTP 不好么&#xff1f; RPC 的原理是什么? 如何设计一个 RPC 框架 从底向上的思路 服务消费者 服务提供者 注册中心 监控运维 小结一下 简单实现一个 RPC 框架 Dubbo 简介 Dubbo的历史 Dubbo的功能 为什么要用 …

Java语法之继承

上次给大家分享了Java的封装&#xff0c;今天小编给大家分享面向对象三大特性的第二大特性&#xff0c;也就是继承&#xff0c;fighting~~ 目录 &#x1f384;一.继承的概念 &#x1f384;1.1为什么需要继承 &#x1f384;1.2继承的概念 &#x1f384;1.3继承的语法 &#…

Android App开发中补间动画的讲解以及实现钟摆动画效果实战(附源码 简单易懂 可直接使用)

需要图片集和源码请点赞关注收藏后评论区留言~~~ 一、补间动画的种类 Android提供了补间动画&#xff0c;它允许开发者实现某个视图的动态变换&#xff0c;具体包括四种动画效果&#xff0c;分别是灰度动画&#xff0c;平移动画&#xff0c;缩放动画和旋转动画。因为开发者提供…

缓存穿透、缓存击穿、缓存雪崩区别和解决方案

生命无罪&#xff0c;健康万岁&#xff0c;我是laity。 我曾七次鄙视自己的灵魂&#xff1a; 第一次&#xff0c;当它本可进取时&#xff0c;却故作谦卑&#xff1b; 第二次&#xff0c;当它在空虚时&#xff0c;用爱欲来填充&#xff1b; 第三次&#xff0c;在困难和容易之…

【c++】类和对象

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 3.1类的两种定义方式&#xff1a; 4.类的访问限定符及封装 4.1访问限定符 5.类的作用域 6.类的实例化 7.类对象模型 7.1 如何计算类对象的大小 8.this指针 8.1 this指针的引出 8.2 this指针的特性 9.类的6个默认成员函…

深度学习简介及反向传播

Datawhale开源学习&#xff0c;机器学习课程&#xff0c;项目地址&#xff1a;https://github.com/datawhalechina/leeml-notes 之前学习机器学习的时候&#xff0c;总结了三步&#xff1a; define a set of functiongoodness of functionpick the best function 这三步所做的…

机器学习之决策树【西瓜书】

当一个有经验的老农看一个瓜是不是好瓜时&#xff0c;他可能会先看下瓜的颜色&#xff0c;一看是青绿的&#xff0c;心想有可能是好瓜&#xff1b;接着他又看了下根蒂&#xff0c;发现是蜷缩着的&#xff0c;老农微微点头&#xff0c;寻思着五成以上是好瓜&#xff1b;最后他又…

基于51单片机的客车辆超载报警Proteus仿真

资料编号&#xff1a;111 下面是相关功能视频演示&#xff1a; 111-基于51单片机的客车辆超载报警Proteus仿真&#xff08;源码仿真原理图全套资料&#xff09;功能如下&#xff1a; 利用单片机设计一个超载检测控制电路。 通过 2 组红外对管(类似电路模拟)检测上车人数和下车…

Flink-水位线的设置以及传递

6.2 水位线 6.2.1 概述 分类 有序流 无序流 判断的时间延迟 延迟时间判定 6.2.2 水位线的设置 分析 DataStream下的assignTimstampsAndWatermarks方法&#xff0c;返回SingleOutputStreamOperator本质还是个算子&#xff0c;传入的参数是WatermarkStrategy的生成策略 但…

C51 - 中断系统

Contents1> 定义2> 作用3> 组成3.1> 中断系统结构3.2> 8个中断源3.3> 中断向量3.4> 中断优先级4> 原理5> 应用1> 定义 中断&#xff08;interrupt&#xff09;是指&#xff1a; CPU执行某一程序过程中&#xff0c;由于系统内&#xff0c;或外部某…

【操作文件的系统调用】

目录文件操作系统调用的基本库函数打开文件读取文件写入文件关闭文件应用文件操作代码举例文件操作与进程复制的结合先打开文件再复制进程先进程复制&#xff0c;再进行打开文件缓冲区的知识回顾在上一篇讲述僵尸进程的文章中对文件的系统调用做了一点点的代码讲述&#xff0c;…