Linux内核早期打印机制与RS485通信技术

往期内容

本专栏往期内容:Uart子系统

  1. UART串口硬件介绍
  2. 深入理解TTY体系:设备节点与驱动程序框架详解
  3. Linux串口应用编程:从UART到GPS模块及字符设备驱动
  4. 解UART 子系统:Linux Kernel 4.9.88 中的核心结构体与设计详解
  5. IMX 平台UART驱动情景分析:注册篇
  6. IMX 平台UART驱动情景分析:open篇
  7. IMX 平台UART驱动情景分析:read篇--从硬件驱动到行规程的全链路剖析
  8. IMX 平台UART驱动情景分析:write篇--从 TTY 层到硬件驱动的写操作流程解析
  9. 深入浅出UART驱动开发与调试:从基础调试到虚拟驱动实现
    10.Linux 内核日志系统---printk的机制与应用

interrupt子系统专栏:

  1. 专栏地址:interrupt子系统
  2. Linux 链式与层级中断控制器讲解:原理与驱动开发
    -- 末片,有专栏内容观看顺序

pinctrl和gpio子系统专栏:

  1. 专栏地址:pinctrl和gpio子系统

  2. 编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用

    -- 末片,有专栏内容观看顺序

input子系统专栏:

  1. 专栏地址:input子系统
  2. input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    -- 末片,有专栏内容观看顺序

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    -- 末篇,有专栏内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    -- 末篇,有专栏内容观看顺序

目录

  • 往期内容
  • 前言
  • 1.early_printk和earlycon
    • [1.1 内核信息的早期打印](#1.1 内核信息的早期打印)
    • [1.2 early_printk](#1.2 early_printk)
    • [1.3 earlycon](#1.3 earlycon)
  • 2.RS485简单讲解
    • [2.1 RS485线路图](#2.1 RS485线路图)
    • [2.2 RS485应用编程](#2.2 RS485应用编程)
      • [2.2.1 标准用法](#2.2.1 标准用法)
      • [2.2.2 自己控制引脚](#2.2.2 自己控制引脚)
    • [2.3 驱动速览](#2.3 驱动速览)
      • [2.3.1 使用GPIO的RS485驱动](#2.3.1 使用GPIO的RS485驱动)

前言

本文围绕Linux内核早期打印机制和RS485通信技术展开,详细解析了early_printk和earlycon的实现与使用场景,以及RS485的基础原理和应用实践。在早期打印部分,阐明了early_printk和earlycon的区别:前者通过手动实现打印函数实现早期日志输出,后者则结合设备树实现灵活硬件映射,适用于现代系统。在RS485部分,介绍了其差分信号传输、抗干扰能力强等技术特点,并结合SP3485芯片说明了发送与接收的硬件原理,同时提供了Linux环境下的标准驱动编程示例,涵盖RTS控制模式与GPIO实现方式。

1.early_printk和earlycon

linux 4.9.88

1.1 内核信息的早期打印

console驱动,它属于uart_driver的一部分。

注册了uart_driver、并调用uart_add_one_port后,它里面才注册console,在这之后才能使用printk。

如果想更早地使用printk函数,比如在安装UART驱动之前就使用printk,这时就需要自己去注册console。

更早地、单独地注册console,有两种方法:

  • early_printk:自己实现write函数,不涉及设备树,简单明了
  • earlycon:通过设备树传入硬件信息,跟内核中驱动程序匹配

earlycon是新的、推荐的方法,在内核已经有驱动的前提下,通过设备树或cmdline指定寄存器地址即可。

1.2 early_printk

源码为:arch\arm\kernel\early_printk.c,要使用它,必须实现这几点:

  • 配置内核,选择:CONFIG_EARLY_PRINTK
  • 内核中实现:printch函数
  • cmdline中添加:earlyprintk

1.3 earlycon

arch\arm\kernel\early_printk.c📎early_printk.c

drivers\tty\serial\earlycon.c📎earlycon.c

提供硬件信息的两种方法:

earlycon就是early console的意思,实现的功能跟earlyprintk是一样的,只是更灵活。

我们知道,对于console,最主要的是里面的write函数:它不使用中断,相对简单。

所以很多串口console的write函数,只要确定寄存器的地址就很容易实现了。

假设芯片的串口驱动程序,已经在内核里实现了,我们需要根据板子的配置给它提供寄存器地址。

怎么提供?

  • 设备树
  • cmdline参数
c 复制代码
early_param("earlycon", param_setup_earlycon);
//如上图所示,当在cmdline参数中有erlycon这个属性的时候,就会导致param_setup_earlycon被调用

/* early_param wrapper for setup_earlycon() */
static int __init param_setup_earlycon(char *buf)
{
	int err;

	/*
	 * Just 'earlycon' is a valid param for devicetree earlycons;
	 * don't generate a warning from parse_early_params() in that case
	 */
	if (!buf || !buf[0]) {
		if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
			earlycon_init_is_deferred = true;
			return 0;
		} else {
			return early_init_dt_scan_chosen_stdout(); //1. earlycon不带参数,参数采用设备节点表示
		}
	}

	err = setup_earlycon(buf);  //2. earlycon = XX,带参数
	if (err == -ENOENT || err == -EALREADY)
		return 0;
	return err;
}
  • 如果cmdline中只有"earlycon",不带更多参数:对应early_init_dt_scan_chosen_stdout函数

    • 使用"/chosen"下的"stdout-path"找到节点

    • 或使用"/chosen"下的"linux,stdout-path"找到节点

    • 节点里有"compatible"和"reg"属性

      • 根据"compatible"找到OF_EARLYCON_DECLARE,里面有setup函数,它会提供write函数
      • write函数写什么寄存器?在"reg"属性里确定
  • 如果cmdline中"earlycon=xxx",带有更多参数:对应setup_earlycon函数

    • earlycon=xxx格式为:
shell 复制代码
<name>,io|mmio|mmio32|mmio32be,<addr>,<options>
<name>,0x<addr>,<options>
<name>,<options>
<name>
    • 根据"name"找到OF_EARLYCON_DECLARE,里面有setup函数,它会提供write函数
    • write函数写什么寄存器?在"addr"参数里确定

2.RS485简单讲解

Documentation\serial\serial-rs485.txt📎serial-rs485.txt

2.1 RS485线路图

RS485通信----基本原理+电路图-CSDN博客

RS485 是美国电子工业协会(Electronic Industries Association,EIA)于1983年发布的串行通信接口标准,经通讯工业协会(TIA)修订后命名为 TIA/EIA-485-A。

RS485 是一种工业控制环境中常用的通讯协议,其中RS 是 Recommended Standard 的缩写。

RS485 是 半双工异步 串行通信。

特点

  1. 支持多节点:一般最大支持 32 个节点。
  2. 传输距离远:最远通讯距离可达1200米。
  3. 抗干扰能力强:差分信号传输。
  4. 连接简单:只需要两根信号线(A+和B-)就可以进行正常的通信。

差分信号传输

RS485 通信采用差分信号传输,通常情况下只需要两根信号线就可以进行正常的通信。

在差分信号中,逻辑0和逻辑1是用两根信号线(A+和B-)的电压差来表示。

  • 逻辑 1:两根信号线(A+和B-)的电压差在 +2V~+6V 之间。
  • 逻辑 0:两根信号线(A+和B-)的电压差在 -2V~-6V 之间。

SP3485 芯片****是一款非常经典的+3.3V低功耗半双工RS485收发器

RS485使用A、B两条差分线传输数据:

  • 要发送数据时

    • 把SP3485的DE(Driver output Enable)引脚设置为高
    • 通过TxD引脚发送1给SP3485,SP3485会驱动A、B引脚电压差为+2~+6V
    • 通过TxD引脚发送0给SP3485,SP3485会驱动A、B引脚电压差为-6~-2V
    • SP3485自动把TxD信号转换为AB差分信号
    • 对于软件来说,通过RS485发送数据时,跟一般的串口没区别,只是多了DE引脚的设置
  • 要读取数据时

    • 把SP3485的nRE(Receiver Output Enable)引脚设置为低
    • SP3485会根据AB引脚的电压差驱动RO为1或0
    • RO的数据传入UART的RxD引脚
    • 对于软件来说,通过RS485读取数据时,跟一般的串口没区别,只是多了nRE引脚的设置
  • nRE和DE使用同一个引脚时,可以简化成这样:

    • 发送:RTS输出高 --- 导致DE为高,使能发送,nRE为低,接收禁止
    • 接收:RTS输出低 ---- 导致nRE为低,使能接收,DE为低,发送禁止
引脚 名称 功能
1 RO 接收器输出----接RX
2 RE 接收器输出使能(低电平-接收使能)
3 DE 驱动器输出使能(高电平-发送使能)
4 DI 驱动器输入----接TX
5 GND 接地
6 A 驱动器输出/接收器输入(同相)
7 B 驱动器输出/接收器输入(反相)
8 VCC 芯片供电+3.3V

2.2 RS485应用编程

2.2.1 标准用法

在Linux的串口驱动中,它已经支持RS485,可以使用RTS引脚控制RS485芯片的DE引脚,分两种情况

  • 有些UART驱动:使用UART的RTS引脚
  • 有些UART驱动:使用GPIO作为RTS引脚,可以通过设备树指定这个GPIO

在使用RS485发送数据前,把RTS设置为高电平就可以。

通过serial_rs485结构体控制RTS,示例代码如下:

c 复制代码
#include <linux/serial.h>

/* 用到这2个ioctl: TIOCGRS485, TIOCSRS485 */
#include <sys/ioctl.h>

struct serial_rs485 rs485conf;

/* 打开串口设备 */
int fd = open ("/dev/mydevice", O_RDWR);
if (fd < 0) {
    /* 失败则返回 */
    return -1;
}

/* 读取rs485conf */
if (ioctl (fd, TIOCGRS485, &rs485conf) < 0) {
    /* 处理错误 */
}

/* 使能RS485模式 */
rs485conf.flags |= SER_RS485_ENABLED;

/* 当发送数据时, RTS为1 */
rs485conf.flags |= SER_RS485_RTS_ON_SEND;

/* 或者: 当发送数据时, RTS为0 */
rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);

/* 当发送完数据后, RTS为1 */
rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;

/* 或者: 当发送完数据后, RTS为0 */
rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);

/* 还可以设置: 
 * 发送数据之前先设置RTS信号, 等待一会再发送数据
 * 等多久? delay_rts_before_send(单位ms)
 */
rs485conf.delay_rts_before_send = ...;

/* 还可以设置: 
 * 发送数据之后, 等待一会再清除RTS信号
 * 等多久? delay_rts_after_send(单位ms)
 */
rs485conf.delay_rts_after_send = ...;

/* 如果想在发送RS485数据的同时也接收数据, 还可以这样设置 */
rs485conf.flags |= SER_RS485_RX_DURING_TX;

if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
    /* 处理错误 */
}

/* 使用read()和write()就可以读、写数据了 */

/* 关闭设备 */
if (close (fd) < 0) {
    /* 处理错误 */
}

2.2.2 自己控制引脚

发送之前,自己设置GPIO控制DE引脚。

2.3 驱动速览

源码为:Linux-4.9.88\drivers\tty\serial\imx.c📎imx.c

2.3.1 使用GPIO的RS485驱动

这里只是举个例子。

源码为:Linux-4.9.88\drivers\tty\serial\omap-serial.c📎omap-serial.c

1. 从设备树获得用作RTS的GPIO

2.发送数据前设置GPIO

相关推荐
坏柠14 分钟前
深入浅出SPI通信协议与STM32实战应用(W25Q128驱动)(理论部分)
stm32·单片机·嵌入式硬件
塞尔维亚大汉25 分钟前
OpenHarmony之分布式软总线json.payload.c(三)
分布式·嵌入式·harmonyos
别说我什么都不会26 分钟前
OpenHarmony解读之设备认证:sts协议-客户端接收end响应
物联网·嵌入式·harmonyos
Invinciblenuonuo2 小时前
STM32八股【5】----- TIM定时器
stm32·单片机·嵌入式硬件
selenia88602 小时前
如何成功点亮LED灯并实现闪烁效果
单片机·嵌入式硬件
猫猫的小茶馆5 小时前
【PCB工艺】软件是如何控制硬件的发展过程
开发语言·stm32·单片机·嵌入式硬件·mcu·51单片机·pcb工艺
柒月玖.8 小时前
基于AT89C52单片机的轮胎压力监测系统
单片机·嵌入式硬件·mongodb
多多*9 小时前
Java设计模式 简单工厂模式 工厂方法模式 抽象工厂模式 模版工厂模式 模式对比
java·linux·运维·服务器·stm32·单片机·嵌入式硬件
小麦嵌入式16 小时前
Linux驱动开发实战(九):Linux内核pinctrl_map详解与优势分析
linux·c语言·汇编·驱动开发·stm32·嵌入式硬件·硬件工程
磨十三19 小时前
初始ARM
arm开发