UART编程框架详解

1. UART介绍

UART:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),简称串口。

  • 调试:移植u-boot、内核时,主要使用串口查看打印信息

  • 外接各种模块

 1.1 硬件知识_UART硬件介绍

UART的全称是Universal Asynchronous Receiver and Transmitter,即异步发送和接收。 串口在嵌入式中用途非常的广泛,主要的用途有:

  • 打印调试信息;

  • 外接各种模块:GPS、蓝牙;

串口因为结构简单、稳定可靠,广受欢迎。

通过三根线即可,发送、接收、地线。

TxD线把PC机要发送的信息发送给ARM开发板。 最下面的地线统一参考地。

1.2 串口的参数

  • 波特率:一般选波特率都会有9600,19200,115200等选项。其实意思就是每秒传输这么多个比特位数(bit)。

  • 起始位:先发出一个逻辑”0”的信号,表示传输数据的开始。

  • 数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。小端传输。

  • 校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。

  • 停止位:它是一个字符数据的结束标志。

怎么发送一字节数据,比如‘A‘? ‘A’的ASCII值是0x41,二进制就是01000001,怎样把这8位数据发送给PC机呢?

  • 双方约定好波特率(每一位占据的时间);

  • 规定传输协议

    • 原来是高电平,ARM拉低电平,保持1bit时间;

    • PC在低电平开始处计时;

    • ARM根据数据依次驱动TxD的电平,同时PC依次读取RxD引脚电平,获得数据;

 前面图中提及到了逻辑电平,也就是说代表信号1的引脚电平是人为规定的。 如图是TTL/CMOS逻辑电平下,传输‘A’时的波形:

在xV至5V之间,就认为是逻辑1,在0V至yV之间就为逻辑0。

如图是RS-232逻辑电平下,传输‘A’时的波形:

在-12V至-3V之间,就认为是逻辑1,在+3V至+12V之间就为逻辑0。

RS-232的电平比TTL/CMOS高,能传输更远的距离,在工业上用得比较多。

市面上大多数ARM芯片都不止一个串口,一般使用串口0来调试,其它串口来外接模块。

1.3 串口电平

ARM芯片上得串口都是TTL电平的,通过板子上或者外接的电平转换芯片,转成RS232接口,连接到电脑的RS232串口上,实现两者的数据传输。

现在的电脑越来越少有RS232串口的接口,当USB是几乎都有的。因此使用USB串口芯片将ARM芯片上的TTL电平转换成USB串口协议,即可通过USB与电脑数据传输。

1.4 串口内部结构

ARM芯片是如何发送/接收数据? 如图所示串口结构图:

要发送数据时,CPU控制内存要发送的数据通过FIFO传给UART单位,UART里面的移位器,依次将数据发送出去,在发送完成后产生中断提醒CPU传输完成。 接收数据时,获取接收引脚的电平,逐位放进接收移位器,再放入FIFO,写入内存。在接收完成后产生中断提醒CPU传输完成。

图片中描述的是串行通信中的一个典型的串行接口(Peripheral Bus)结构,特别是关于数据发送和接收的组件。以下是对图片内容的解释:

  1. 发送器(发送器):

    • 负责将数据从串行接口发送出去。
  2. 发送FIFO寄存器:

    • 在FIFO(先进先出)模式下,发送FIFO寄存器用于暂存待发送的数据。
  3. 发送缓冲寄存器:

    • 这是一个64字节的缓冲区,用于存储即将通过发送器发送的数据。
  4. 发送保持寄存器:

    • 在非FIFO模式下,发送保持寄存器用于存储下一个要发送的字节。
  5. 发送移位器:

    • 负责将数据按位(bit)顺序移出,以串行方式发送。
  6. TXDn:

    • 表示发送数据线,数据通过这条线发送到外部设备。
  7. 控制:

    • 涉及串行通信的控制机制,如波特率、时钟源等。
  8. 波特率:

    • 串行通信中数据传输的速率,以比特每秒(bps)计量。
  9. 时钟源:

    • 为串行通信提供时钟信号的源,可以是PCLK(外设时钟)、FCLK(功能时钟)或UEXTCLK(外部时钟)。
  10. 单元产生器:

    • 可能是指控制单元,用于生成控制信号以管理数据传输。
  11. 接收器:

    • 负责接收来自外部设备的数据。
  12. 接收移位器:

    • 负责将接收到的串行数据按位顺序移入。
  13. RXDn:

    • 表示接收数据线,数据通过这条线接收到设备中。
  14. 接收保持寄存器:

    • 在非FIFO模式下,接收保持寄存器用于存储刚刚接收到的字节。
  15. 接收缓冲寄存器:

    • 这是一个64字节的缓冲区,用于存储接收到的数据。
  16. 接收FIFO寄存器:

    • 在FIFO模式下,接收FIFO寄存器用于暂存接收到的数据。
  17. FIFO模式与非FIFO模式:

    • FIFO模式允许使用整个64字节的缓冲寄存器作为FIFO,以暂存大量数据。
    • 非FIFO模式下,只使用缓冲寄存器中的1字节作为保持寄存器,用于存储单个数据字节。

在串行通信中,数据通常通过发送器和接收器在设备之间传输。FIFO模式和非FIFO模式决定了数据如何被存储和检索。FIFO模式适用于数据传输速率较高且需要缓冲大量数据的情况,而非FIFO模式则适用于数据传输速率较低或不需要大量缓冲的情况。

2.  Linux串口应用编程

在Linux系统中,操作设备的统一接口就是:open/ioctl/read/write。

对于UART,又在ioctl之上封装了很多函数,主要是用来设置行规程。

所以对于UART,编程的套路就是:

  • open

  • 设置行规程,比如波特率、数据位、停止位、检验位、RAW模式、一有数据就返回

  • read/write

编写驱动程序的套路:

  • 确定主设备号,也可以让内核分配

  • 定义自己的file_operations结构体

  • 实现对应的drv_open/drv_read/drv_write等函数,填入file_operations结构体

  • 把file_operations结构体告诉内核:register_chrdev

  • 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数

  • 有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用unregister_chrdev

  • 其他完善:提供设备信息,自动创建设备节点:class_create, device_create

 3. UART驱动框架分析

3.1 UART驱动情景分析_注册

 

3.2 UART驱动情景分析_open

它要做的事情:

  • 找到tty_driver

    • 分配/设置tty_struct

    • 行规程相关的初始化

3.3 UART驱动情景分析_read

串行通信中的"行规程":

在串行通信中,"行规程"特别指的是与串行数据传输相关的一系列设置,这些设置定义了数据如何被发送和接收。这包括:

  • 波特率:数据传输的速率,以比特每秒(bps)计量。
  • 数据位:每个字符的数据位长度,通常为7、8或9位。
  • 停止位:数据字符之间的可选额外位,用于标识字符边界,可以是1或2位。
  • 校验位:用于错误检测的位,可以是无校验、奇校验或偶校验。
  • 流控制:如XON/XOFF或RTS/CTS,用于控制数据流。

在Linux和其他操作系统中,行规程可以通过设置串行端口的属性来配置,这些属性通常通过termios结构体进行控制。

read过程分析:

流程为:

  • APP读

    • 使用行规程来读

    • 无数据则休眠

  • UART接收到数据,产生中断

    • 中断程序从硬件上读入数据

  • 发给行规程

    • 行规程处理后存入buffer

    • 行规程唤醒APP

  • APP被唤醒后,从行规程buffer中读入数据,返回

3.4 UART驱动情景分析_write

流程为:

  • APP写

    • 使用行规程来写

    • 数据最终存入uart_state->xmit的buffer里

  • 硬件发送:怎么发送数据?

    • 使用硬件驱动中uart_ops->start_tx开始发送

    • 具体的发送方法有2种:通过DMA,或通过中断

  • 中断方式

    • 方法1:直接使能 tx empty中断,一开始tx buffer为空,在中断里填入数据

    • 方法2:写部分数据到tx fifo,使能中断,剩下的数据再中断里继续发送

4.  编写虚拟UART驱动程序_框架

4.1  编写UART驱动要做的事

  • 注册一个uart_driver:它里面有名字、主次设备号等

  • 对于每一个port,调用uart_add_one_port,里面的核心是uart_ops,提供了硬件操作函数

    • uart_add_one_port由platform_driver的probe函数调用

    • 所以:

      • 编写设备树节点

      • 注册platform_driver

4.2 源码分析

#include <linux/module.h>       // 模块化编程支持
#include <linux/ioport.h>       // I/O端口支持
#include <linux/init.h>        // 模块初始化和清理宏
#include <linux/console.h>     // 控制台支持
#include <linux/sysrq.h>       // 系统请求键支持
#include <linux/platform_device.h> // 平台设备支持
#include <linux/tty.h>         // TTY支持
#include <linux/tty_flip.h>    // TTY翻转缓冲区支持
#include <linux/serial_core.h> // 串行核心支持
#include <linux/serial.h>      // 串行硬件支持
#include <linux/clk.h>         // 时钟支持
#include <linux/delay.h>       // 延时支持
#include <linux/rational.h>    // 有理数支持
#include <linux/reset.h>       // 重置支持
#include <linux/slab.h>        // 内存分配
#include <linux/of.h>         // 设备树支持
#include <linux/of_device.h>   // 设备树设备支持
#include <linux/io.h>          // IO操作
#include <linux/dma-mapping.h> // DMA内存映射
#include <linux/proc_fs.h>     // 进程文件系统#include <asm/irq.h>           // 特定体系结构的中断支持#define BUF_LEN  1024          // 定义缓冲区长度为1024字节
#define NEXT_PLACE(i) ((i+1)&0x3FF) // 循环缓冲区的索引计算宏// 定义全局变量
static struct uart_port	*virt_port; // 虚拟UART端口
static unsigned char txbuf[BUF_LEN]; // 发送缓冲区
static int tx_buf_r = 0;            // 发送缓冲区读索引
static int tx_buf_w = 0;            // 发送缓冲区写索引
static unsigned char rxbuf[BUF_LEN]; // 接收缓冲区
static int rx_buf_w = 0;            // 接收缓冲区写索引
static struct proc_dir_entry *uart_proc_file; // 串行控制台的proc文件// 虚拟UART驱动结构体
static struct uart_driver virt_uart_drv = {.owner          = THIS_MODULE, // 驱动的模块所有者.driver_name    = "VIRT_UART", // 驱动名称.dev_name       = "ttyVIRT",   // 设备名称.major          = 0,          // 主设备号.minor          = 0,          // 次设备号.nr             = 1,          // 设备数量
};// 循环缓冲区操作函数
static int is_txbuf_empty(void) // 检查发送缓冲区是否为空
{return tx_buf_r == tx_buf_w;
}static int is_txbuf_full(void) // 检查发送缓冲区是否已满
{return NEXT_PLACE(tx_buf_w) == tx_buf_r;
}static int txbuf_put(unsigned char val) // 向发送缓冲区添加数据
{if (is_txbuf_full())return -1;txbuf[tx_buf_w] = val;tx_buf_w = NEXT_PLACE(tx_buf_w);return 0;
}static int txbuf_get(unsigned char *pval) // 从发送缓冲区读取数据
{if (is_txbuf_empty())return -1;*pval = txbuf[tx_buf_r];tx_buf_r = NEXT_PLACE(tx_buf_r);return 0;
}static int txbuf_count(void) // 计算发送缓冲区中的数据量
{if (tx_buf_w >= tx_buf_r)return tx_buf_w - tx_buf_r;elsereturn BUF_LEN + tx_buf_w - tx_buf_r;
}// 虚拟UART的文件操作函数
ssize_t virt_uart_buf_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{// 实现从虚拟UART读取数据到用户空间的函数// ...
}static ssize_t virt_uart_buf_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{// 实现从用户空间写入数据到虚拟UART的函数// ...
}static const struct file_operations virt_uart_buf_fops = {.read = virt_uart_buf_read, // 读取操作.write = virt_uart_buf_write, // 写入操作
};// 虚拟UART的中断处理函数和相关操作
static unsigned int virt_tx_empty(struct uart_port *port)
{// 检查发送缓冲区是否为空// ...
}static void virt_start_tx(struct uart_port *port)
{// 开始发送数据// ...
}// 其他虚拟UART操作函数
static void virt_set_termios(struct uart_port *port, struct ktermios *termios,struct ktermios *old)
{// 设置终端参数// ...
}static int virt_startup(struct uart_port *port)
{// 启动虚拟UART// ...
}// 虚拟UART驱动的probe和remove函数
static int virtual_uart_probe(struct platform_device *pdev)
{// 虚拟UART设备探测函数// ...
}static int virtual_uart_remove(struct platform_device *pdev)
{// 虚拟UART设备移除函数// ...
}// 虚拟UART驱动的入口和出口函数
static int __init virtual_uart_init(void)
{// 虚拟UART驱动的初始化函数// ...
}static void __exit virtual_uart_exit(void)
{// 虚拟UART驱动的退出函数// ...
}module_init(virtual_uart_init); // 注册初始化函数
module_exit(virtual_uart_exit); // 注册退出函数MODULE_LICENSE("GPL"); // 模块许可证

代码解释:

  • 头文件包含:代码包括了处理模块化编程、I/O端口、初始化、控制台、系统请求键、平台设备、TTY、串行通信、时钟、延时、有理数、重置、内存分配、设备树、IO操作、DMA内存映射和进程文件系统等所需的头文件。
  • 宏定义:定义了缓冲区长度和循环缓冲区索引计算的宏。
  • 全局变量:定义了虚拟UART端口、发送和接收缓冲区、proc文件系统条目等全局变量。
  • 虚拟UART驱动结构体:定义了一个uart_driver结构体,包含了驱动的名称、设备名称、设备号和设备数量。
  • 循环缓冲区操作函数:实现了一组函数,用于管理发送缓冲区的状态,包括检查缓冲区是否为空或满,以及添加和读取数据。
  • 文件操作函数:实现了虚拟UART的读取和写入操作,用于与用户空间交换数据。
  • 虚拟中断处理和UART操作:实现了虚拟UART的中断处理函数和一系列UART操作函数,包括发送数据、接收数据、设置终端参数等。
  • 平台设备探测和移除函数:实现了平台设备的探测和移除函数,用于初始化和清理虚拟UART设备。
  • 模块初始化和退出函数:实现了模块的初始化和退出函数,用于注册和注销虚拟UART驱动。

这个模块实现了一个虚拟的UART驱动程序,它可以模拟UART硬件的行为,对于嵌入式系统开发中的串行通信测试和调试非常有用。通过这种方式,开发者可以在没有实际硬件的情况下开发和测试UART相关的软件。

 

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

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

相关文章

新160个crackme - 011-wocy.1

运行分析 显示Unregister&#xff0c;点击注册无反应&#xff0c;猜测要先注册 PE分析 C 程序&#xff0c;32位&#xff0c;无壳 静态分析 ida发现关键字符串&#xff0c;进入关键函数 动态调试 设置断点动态调试&#xff0c;CWnd::UpdateData(true) &#xff1a;用于将屏幕上控…

MATLAB基础:数据和变量

今天我们开始学习MATLAB基础知识 1、常用非运算符及其作用 1、“,” 作为程序运行的分隔符&#xff0c;起到分隔语句的作用 2、“;” 同样作为分隔符&#xff0c;与“,”不同的是“;”会在程序运行时隐藏该行语句 如下图&#xff1a; 3、“...” 三个英文句点表示续行符…

W30-python03-pytest+selenium+allure访问百度网站实例

此篇文章为总结性&#xff0c;将pystest、selenium、allure结合起来 功能如下&#xff0c;web自动化&#xff0c;输入baidu网站&#xff0c;搜索“雷军”、打开网页中第一条内容 pytestsel.py如下&#xff1a; import time import re import allure import pytest from tools…

提升ROI:利用高级爬虫技术优化营销策略

如何通过高级爬虫技术高效提升营销ROI&#xff1f; 摘要&#xff1a; 在当今数据驱动的营销环境中&#xff0c;提升投资回报率&#xff08;ROI&#xff09;的关键在于精准洞察市场与用户行为。本文将探讨如何运用高级爬虫技术来优化营销策略&#xff0c;从海量互联网数据中挖掘…

【数据分享】2008-2022年我国省市县三级的逐日NO2数据(excel\shp格式)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000-2022年的省市县三级的逐日PM2.5数据、2013-2022年的省市县三级的逐日CO数据和2013-2022年的省市县三级的逐日SO2数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff01; 本次…

jmeter实战(1)- Mac环境安装

一、安装 JDK 这个就不介绍了&#xff0c;本地自行安装 JDK 并且配置好环境变量 二、安装 Jmeter 1. 下载地址 —> 下载链接点击这里 2. 选择合适的版本下载 3. 解压到本地目录 解压后&#xff0c;会得到下面的目录文件&#xff1a; 输入cd bin&#xff0c;进入到bin…

[STM32]HAL库实现自己的BootLoader-BootLoader与OTA-STM32CUBEMX

目录 一、前言 二、BootLoader 三、BootLoader的实现 四、APP程序 五、效果展示 六、拓展 一、前言 听到BootLoader大家一定很熟悉&#xff0c;在很多常见的系统中都会存在BootLoader。本文将介绍BootLoader的含义和简易实现&#xff0c;建议大家学习前掌握些原理基础。 …

【Android】Activity与Fragment的数据传递

上一篇文章学到了碎片的创建与生命周期&#xff0c;接下来学习碎片的常用操作&#xff0c;其中会用到上一篇文章的三个碎片&#xff0c;就做一个简单的说明吧&#xff1a;LeftFragment&#xff08;包含一个按钮&#xff09;、RightFragment4&#xff08;以粉色为背景的文本&…

408专业课130|零基础五个月速成攻略

计算机考研&#xff0c;有两个选择&#xff0c;一个是自命题&#xff0c;一个是408。如果你只是考一个普通院校&#xff0c;可以选择考自命题院校&#xff0c;容易上岸&#xff0c;但是如果考985/211/这类院校&#xff0c;最好还是选择408&#xff0c;因为408的考风险能力很强&…

Apollo部署与简易架构梳理

文章目录 apollo 安装apollo的基本架构组件机制component编译与加载 节点通讯数据的传输消息读写的实现消息的写端消息读端 常用术语ComponentChannelTaskNodeReader/WriterService/ClientParameter服务发现CRoutineSchedulerMessageDag文件Launch文件Record文件Mainboard Moni…

在图神经网络(GNN)上进行关系推理的新架构

开发能够学习推理的模型是一个众所周知的具有挑战性的问题&#xff0c;在这个领域中&#xff0c;使用图神经网络&#xff08;GNNs&#xff09;似乎是一个自然的选择。然而&#xff0c;以往关于使用GNNs进行推理的工作表明&#xff0c;当这些模型面对需要比训练时更长推理链的测…

(leetcode学习)236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的祖…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十一章 添加设备树节点

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

linux禁用root

linux禁用root 1. 禁止普通用户切换到root1.1 sudo -i和sudo -s的区别1.2 sudo -i和直接登录root账号的区别1.3 禁止sudo -i切换root1.4 禁止su - root切换root 2. 禁止root远程登录2.1 ssh禁止root登录2.2 禁止远程桌面登录 本文主要介绍&#xff1a; 如何禁止普通用户切换到r…

Java---后端事务管理

代码世界聚眸光&#xff0c;昼夜敲盘思绪长。 算法心间精构建&#xff0c;编程路上细思量。 屏前架构乾坤定&#xff0c;键上飞驰智慧扬。 默默耕耘成果现&#xff0c;创新科技铸辉煌。 目录 一&#xff0c;概念 二&#xff0c;Spring事务管理 三&#xff0c;rollbackFor事务回…

运维锅总浅析Kubernetes之Ceph

Ceph 的核心组件有哪些&#xff1f;Ceph读写数据流程及故障自愈是怎样的&#xff1f;如何对Ceph部署架构进行调优&#xff1f;如何用Ceph集成到kubernetes&#xff1f;希望本文能帮您解答这些疑惑&#xff01; 一、Ceph简介 Ceph 是一个开源的分布式存储系统&#xff0c;旨在…

PySide(PyQt)使用QPropertyAnimation制作动态界面

主脚本&#xff1a; # encoding: utf-8 import os import sysfrom PySide6.QtCore import QPropertyAnimation, QEasingCurvefrom UIS import *# 主画面类 class MainWindow(QMainWindow, animationButton_ui.Ui_MainWindow):def __init__(self):super().__init__()self.setup…

韩顺平0基础学Java——第37天

p736-758 MySQL三层结构 1.所谓安装Mysql数据库&#xff0c;就是在主机安装一个数据库管理系统(DBMS)&#xff0c;这个管理程序可以管理多个数据库。DBMS(database manage system) 2.一个数据库中可以创建多个表,以保存数据(信息)。 3.数据库管理系统(DBMS)、数据库和表的关系…

Android 性能之刷新率设置和管理

目录 1. 刷新率和帧率 2. 多种刷新率 3. 基本原理 3.1 屏幕 & 显示控制器 3.2 Composer Service 4. Framework 策略 4.1基本架构 4.2 刷新率设置项的定义 4.2.1 最低刷新率 4.2.2 默认刷新率 & 默认的用户设置刷新率 4.2.2.1 设置入口 4.2.2.2 设置场景 4…

从零开始:在linux系统安装MongoDB数据完整指南 新手常用命令

1 前言 MongoDB 是为快速开发互联网应用而设计的数据库系统。MongoDB 的设计目标是极简、灵活、作为 Web 应用栈的一部分。MongoDB 的数据模型是面向文档的&#xff0c;所谓文档是一种类似于json的结构。 官网教程&#xff1a;https://www.mongodb.com/docs/manual/ 2 安装部…