【Linux】基于美信串行解串器实现UART串口通信

article/2025/10/6 21:30:07

文章目录

  • 前言
  • 一、环境介绍
  • 二、硬件配置
    • 1. MAX96763
    • 2. MAX96752F
  • 三、串口通信协议
    • 1. 帧格式
    • 2. 同步帧
    • 3. 应答帧
    • 4. 包格式(包由帧组成)
  • 四、内核模块实现


前言

车载项目中串行/解串器是十分常见的外设,目前常用的有两种标准:GMSL(美信家的)、FPD-Link(TI家的)。本次基于美信的一对Ser/Deser实现:串行端将HDMI源视频信号转为GMSL2串行信号,解串端解串后转为LVDS格式连接显示屏,并在屏幕上显示。


一、环境介绍

SOC:Amlogic A311D2
SDK:Amlogic Android 11
Kernel:5.4
Serializer:MAX96763
Deserializer:MAX96752F

系统框图:
在这里插入图片描述

二、硬件配置

1. MAX96763

GPIO17/I2CSEL:low(Main Uart mode)
GPIO16/CXTP:low(TP双绞线)
ADD0~ADD2:000(Dev Addr=0x80)
确保:
HPD:high(即SOC的HDMI TX与MAX96763连接上)
PWDNB:high(MAX96763 ready)

2. MAX96752F

GPIO01/I2CSEL:low(Main Uart mode)
GPIO09/CXTP:low(TP双绞线)
ADD0~ADD2:010(Dev Addr=0x98)
确保:
PWDNB:high(MAX96752F ready)

三、串口通信协议

1. 帧格式

START(1bit) : 固定填0
DATA(8bit) : 8位数据
EVEN PARITY(1bit) :1位偶校验,1表示DATA的8位中1的个数为奇数;0表示DATA的8位中1的个数为偶数。
STOP(至少1bit,最多4bit): 固定填1

2. 同步帧

START  D0  D1  D2  D3  D4  D5  D6  D7  PARITY  STOP 0 	1 	0 	0 	1 	1 	1 	1 	0 		1	1				// 0x79

3. 应答帧

START  D0  D1  D2  D3  D4  D5  D6  D7  PARITY  STOP 0 	1 	1 	0 	0 	0 	0 	1 	1 		0	1				// 0xC3

4. 包格式(包由帧组成)

写操作(地址帧的bit0置0):

SYNC帧 | DEV ADDR帧(LSB=0) | REG ADDR(MSB) | REG ADDR(LSB) | BYTE COUNT | DATA1 | ········ | DATA N

读操作(地址帧的bit0置1):

SYNC帧 | DEV ADDR帧(LSB=1) | REG ADDR(MSB) | REG ADDR(LSB) | BYTE COUNT

示例:
读MAX96763的DEV ID,即读0x000D寄存器(应返回:C3 B2):

79 81 00 0D 01 

读MAX96752F的DEV ID,即读0x000D寄存器(应返回:C3 82):

79 99 00 0D 01 

四、内核模块实现

// SPDX-License-Identifier: GPL-2.0
/* * Author: rentong rentong@skyworth.com* Description:This module configs the Maxin serializer and deserializer during kernel startup.* Version: 1.0 2022/3/26*/#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/clk.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/sysrq.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_bcm63xx.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/kthread.h>#define MAX96763_DEV_ADDR	0x80
#define MAX96752_DEV_ADDR	0x98
#define MAX96763_DEV_ID		0xB2
#define MAX96752_DEV_ID		0x82#define TTY_NAME  			"/dev/ttyS3"
#define TTY_SPEED			921600
#define TRY_TIMES			10
#define SCHE_TIMEOUT		msecs_to_jiffies(1000)
static struct task_struct 	*server_thread;static int max96763_main_uart_set_termios(struct file *filp)
{struct tty_file_private *priv = NULL;struct tty_struct *tty = NULL;priv = filp->private_data;if(IS_ERR(priv)) {pr_err("%s %d priv is NULL\n", __func__, __LINE__);return -1;}else {tty = priv->tty;}if(IS_ERR(tty)) {pr_err("%s %d tty is NULL\n", __func__, __LINE__);return -1;}else {struct ktermios ktermios = tty->termios;ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);ktermios.c_oflag &= ~OPOST;ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);ktermios.c_cflag |= CLOCAL;ktermios.c_cflag &= ~CBAUD;										/* clear baud */tty_termios_encode_baud_rate(&ktermios, TTY_SPEED, TTY_SPEED);	/* set baud rate */ktermios.c_cflag &= ~CSIZE;										/* clear bit size */ktermios.c_cflag |= CS8;										/* set bit size */ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);				/* clear parity enable and odd parity */ktermios.c_cflag |= PARENB;										/* enable even parity */ktermios.c_cflag &= ~CSTOPB;									/* set stop bit with one bit */ktermios.c_cflag &= ~CRTSCTS;									/* disable hardware flow control */tty_set_termios(tty, &ktermios);}pr_info("%s %d ok\n", __func__, __LINE__);return 0;
}static void max96763_main_uart_config_reg(struct file *filp)
{// for 96752unsigned char cmd_1[6]={ 0x79, MAX96752_DEV_ADDR, 0x00, 0x01, 0x01, 0x02 }; unsigned char cmd_2[6]={ 0x79, MAX96752_DEV_ADDR, 0x01, 0xCE, 0x01, 0x5E }; unsigned char cmd_3[6]={ 0x79, MAX96752_DEV_ADDR, 0x00, 0x02, 0x01, 0x43 }; unsigned char cmd_4[6]={ 0x79, MAX96752_DEV_ADDR, 0x02, 0x06, 0x01, 0x83 }; unsigned char cmd_5[6]={ 0x79, MAX96752_DEV_ADDR, 0x02, 0x07, 0x01, 0x27 }; // for 96763unsigned char cmd_6[6]={ 0x79, MAX96763_DEV_ADDR, 0x00, 0x01, 0x01, 0x88 };unsigned char cmd_206[6]={ 0x79, MAX96763_DEV_ADDR, 0x02, 0x06, 0x01, 0x84 };unsigned char cmd_207[6]={ 0x79, MAX96763_DEV_ADDR, 0x02, 0x07, 0x01, 0x20 };unsigned char cmd_208[6]={ 0x79, MAX96763_DEV_ADDR, 0x02, 0x08, 0x01, 0x87 };unsigned char cmd_7[6]={ 0x79, MAX96763_DEV_ADDR, 0x00, 0x10, 0x01, 0x31 };// read reg 0x000d to get dev idunsigned char cmd_a[6]={ 0x79, MAX96763_DEV_ADDR|0x01, 0x00, 0x0D, 0x01 }; unsigned char cmd_b[6]={ 0x79, MAX96752_DEV_ADDR|0x01, 0x00, 0x0D, 0x01 };// RFDM contorlunsigned char cmd_c[13]={ 0x79, 0x6c, 0x00, 0x02, 0x08, 0x09, 0x00, 0x01, 0x01, 0x53, 0xE1, 0x70, 0x43 }; // SOC ready//unsigned char cmd_d[13]={ 0x79, 0x6c, 0x00, 0x00, 0x08, 0x09, 0x00, 0x03, 0x03, 0xC2, 0x11, 0xD2, 0xE6 }; // 105° angleunsigned char cmd_e[13]={ 0x79, 0x6c, 0x00, 0x00, 0x08, 0x09, 0x00, 0x03, 0x01, 0x2C, 0x1F, 0xB3, 0xCA }; // 95° angleunsigned char cmd_f[13]={ 0x79, 0x6c, 0x00, 0x00, 0x08, 0x01, 0x00, 0x01, 0x00, 0xAC, 0x9A, 0xC9, 0x31 }; // power onloff_t pos = 0;unsigned int size = 0;mm_segment_t old_fs = get_fs();//char buf[2] = {0, 0};//unsigned int buf_len = 2;set_fs(KERNEL_DS);/**** check 96763's dev id ****/size = vfs_write(filp, cmd_a, sizeof(cmd_a), &pos);pr_info("[%s] write cmd_a %d bytes to file %s\n", __func__, size, TTY_NAME);#if 0size = vfs_read(filp, buf, buf_len, &pos);pr_info("[%s] read %d bytes from file %s, buf = %X %x\n", __func__, size, TTY_NAME, buf[0], buf[1]);if(buf[0]==0xC3 && buf[1]==MAX96763_DEV_ID){pr_info("[%s] MAX96763 found!\n", __func__);}else {pr_err("[%s] MAX96763 not found!\n", __func__);goto PROCESS_END;}memset(buf, 0, buf_len);#endif/**** check 96752's dev id ****/size = vfs_write(filp, cmd_b, sizeof(cmd_b), &pos);pr_info("[%s] write cmd_b %d bytes to file %s\n", __func__, size, TTY_NAME);#if 0size = vfs_read(filp, buf, buf_len, &pos);pr_info("[%s] read %d bytes from file %s, buf = %X %x\n", __func__, size, TTY_NAME, buf[0], buf[1]);if(buf[0]==0xC3 && buf[1]==MAX96752_DEV_ID){pr_info("[%s] MAX96752 found!\n", __func__);}else {pr_err("[%s] MAX96752 not found!\n", __func__);goto PROCESS_END;}#endif/**** config 96752 reg ****/size = vfs_write(filp, cmd_1, sizeof(cmd_1), &pos);pr_info("[%s] write cmd_1 %d bytes to file %s\n", __func__, size, TTY_NAME);size = vfs_write(filp, cmd_2, sizeof(cmd_2), &pos);pr_info("[%s] write cmd_2 %d bytes to file %s\n", __func__, size, TTY_NAME);size = vfs_write(filp, cmd_3, sizeof(cmd_3), &pos);pr_info("[%s] write cmd_3 %d bytes to file %s\n", __func__, size, TTY_NAME);size = vfs_write(filp, cmd_4, sizeof(cmd_4), &pos);pr_info("[%s] write cmd_4 %d bytes to file %s\n", __func__, size, TTY_NAME);size = vfs_write(filp, cmd_5, sizeof(cmd_5), &pos);pr_info("[%s] write cmd_5 %d bytes to file %s\n", __func__, size, TTY_NAME);/**** config 96763 reg ****/size = vfs_write(filp, cmd_6, sizeof(cmd_6), &pos);pr_info("[%s] write cmd_6 %d bytes to file %s\n", __func__, size, TTY_NAME);size = vfs_write(filp, cmd_206, sizeof(cmd_206), &pos);pr_info("[%s] write cmd_206 %d bytes to file %s\n", __func__, size, TTY_NAME);size = vfs_write(filp, cmd_207, sizeof(cmd_207), &pos);pr_info("[%s] write cmd_207 %d bytes to file %s\n", __func__, size, TTY_NAME);size = vfs_write(filp, cmd_208, sizeof(cmd_208), &pos);pr_info("[%s] write cmd_208 %d bytes to file %s\n", __func__, size, TTY_NAME);size = vfs_write(filp, cmd_7, sizeof(cmd_7), &pos);pr_info("[%s] write cmd_7 %d bytes to file %s\n", __func__, size, TTY_NAME);/**** control RFDM screen through main uart ****/msleep(10);size = vfs_write(filp, cmd_c, sizeof(cmd_c), &pos);pr_info("[%s] write cmd_c %d bytes to file %s\n", __func__, size, TTY_NAME);msleep(10);size = vfs_write(filp, cmd_e, sizeof(cmd_e), &pos);pr_info("[%s] write cmd_e %d bytes to file %s\n", __func__, size, TTY_NAME);msleep(10);size = vfs_write(filp, cmd_f, sizeof(cmd_f), &pos);pr_info("[%s] write cmd_f %d bytes to file %s\n", __func__, size, TTY_NAME);pr_info("[%s] -------------------- OK!\n", __func__);set_fs(old_fs);return;
}static int max96763_main_uart_config_thread(void *unused)
{struct file *filp  = NULL;int try = TRY_TIMES;while (!kthread_should_stop() && try--) {filp = filp_open(TTY_NAME, O_RDWR|O_NOCTTY, 0);if (IS_ERR(filp)) {pr_err("%s: cannot open %s\n", __func__, TTY_NAME);set_current_state(TASK_UNINTERRUPTIBLE);schedule_timeout(SCHE_TIMEOUT);}else {pr_info("%s: open %s success!\n", __func__, TTY_NAME);if(max96763_main_uart_set_termios(filp)==0) {max96763_main_uart_config_reg(filp);} else {pr_err("%s: set %s termios failed!\n", __func__, TTY_NAME);}filp_close(filp, NULL);break;}}pr_info("%s %d exit, try %d\n", __func__, __LINE__, try);return 0;
}static int __init max96763_main_uart_init(void)
{int rc;server_thread = kthread_run(max96763_main_uart_config_thread, NULL, "max96763_config");if (IS_ERR(server_thread)) {rc = PTR_ERR(server_thread);pr_err("%s %d kthread_run rc %d\n", __func__, __LINE__, rc);return -1;}pr_info("%s %d ok\n", __func__, __LINE__);return 0;
}static void __exit max96763_main_uart_exit(void)
{pr_info("%s %d\n", __func__, __LINE__);
}module_init(max96763_main_uart_init);
module_exit(max96763_main_uart_exit);MODULE_AUTHOR("rentong <rentong@skyworth.com>");
MODULE_DESCRIPTION("This module is used to config MAXIN chips through serial port");
MODULE_LICENSE("GPL");

http://chatgpt.dhexx.cn/article/8ygJksu8.shtml

相关文章

毫米波雷达图解算法原理(基于TI雷达)

毫米波雷达数据处理原理 前言基础bin文件解读 以下我们取1帧进行操作&#xff1a;对数据矩阵进行操作前的转换——开始计算结果矩阵一维FFT&#xff08;距离&#xff09;二维FFT&#xff08;速度&#xff09;角度维FFT &#xff08;假设利用结果已经获取目标&#xff09;对目标…

【阵列信号处理】DOA估计算法

DOA估计中的ESPRIT算法 ESPRIT算法时一种利用子空间旋转法估计DOA参数的方法&#xff0c;其算法的基本思想是将阵列在结构上分成两个完全一致的子列&#xff0c;两个子列相应阵元偏移的距离相等&#xff0c;也就是说阵列的阵元被分成一对对的形式&#xff0c;而且每一对之间具…

Hector SLAM 原理详解、算法解析

目录 1.原理详解 2.算法解析 1.原理详解 Hector整体算法很直接&#xff0c;就是将激光点与已有的地图“对齐”&#xff0c;即扫描匹配。扫描匹配就是使用当前帧与已经有的地图数据构建误差函数&#xff0c;使用高斯牛顿法得到最优解和偏差量。其工作是实现激光点到栅格地图的转…

MPU 6050姿态角度融合算法

1、介绍 1.1 姿态角&#xff08;Euler角&#xff09;pitch yaw roll介绍 飞行器的姿态角并不是指哪个角度&#xff0c;是三个角度的统称。它们是&#xff1a;俯仰、滚转、偏航。你可以想象是飞机围绕XYZ三个轴分别转动形成的夹角。 地面坐标系&#xff08;earth-surface inert…

linemod算法过程理解

一、提取模板 1、预处理 使用高斯模糊预处理将要作为模板的RGB图 2、模板梯度计算 分别计算RGB三个通道中每个像素点x和y方向的梯度&#xff08;sobel算子&#xff09;&#xff0c;取幅值最大的作为该像素的梯度&#xff0c;若梯度幅度值小于阈值&#xff0c;则被舍弃 3、梯度离…

MATLAB函数angle、unwrap

一、angle 相位角 语法 P angle&#xff08;Z&#xff09;描述 P angle&#xff08;Z&#xff09;返回复数数组Z的每个元素的相角&#xff08;以弧度为单位&#xff09;。角度介于π之间。对于复数Z&#xff0c;幅值R和相角theta由下式给出 R 绝对值&#xff08;Z&#xff0…

fbp算法matlab实现,matlab实现fbp算法

matlab提供大量函数,可以方便的完成fbp算法 1)fbp算法原理: 中心切片定理 (CST) : 原数据投影的一维傅立叶变换等于原数据的二维傅立叶变换 投影 --> 一维傅立叶变换 --> 滤波 --> 二维傅立叶反变换 经过上述过程应该得到原始数据 2)投影相关知识 2.1)正投影:对…

一种简单的图形旋转算法

图形旋转好玩又有实用性, 这里介绍一种简单的图形旋转算法. 具体步骤如下: 1. 首先将原图和旋转图的坐标原点都变换到图形的中心位置处. 2. 历遍旋转图形中的每一个pixel, 将pixel的坐标(j,i)反向旋转映射到原图, 得到原图对应的坐标值(Xr,Yr). 3. 考虑到旋转图的尺寸可能大于…

多目标跟踪之数据关联算法——匈牙利算法

零、Track和Detection的cost matrix,distance metric。距离计算的方式有如下几种: 距离cost distance metric,track和detection的距离矩阵。 外观距离appearance distance,来自检测切片ROI的网络特征提取;——余弦距离 运动模型距离 马氏距离,来自检测-跟踪的kalman校正…

EAST算法简单解析

前言 最近写了很多算法代码的解析&#xff0c;但是却很少写原理的解析&#xff0c;这段时间学得快忘得也快&#xff0c;所以寻思这几天写几篇学过算法的原理&#xff0c;可能不是很详细但是一定很简单&#xff0c;利于理解。 算法介绍 EAST: An Efficient and Accurate Scen…

定位算法初探

定位算法初探 一、指纹定位算法介绍 指纹定位(finger-printing localization)算法&#xff0c;是基于室内环境复杂&#xff0c;信号反射折射所形成的在不同位置形成的不同的信号强度信息而提出的一套算法。 指纹算法能很好的利用了反射折射所形成的信号信息&#xff0c;离线首…

使用python模拟实现PID控制算法

使用python模拟实现PID控制算法 PID控制算法是工业应用中最广泛算法之一&#xff0c;在闭环系统的控制中&#xff0c;可自动对控制系统进行准确且迅速的校正。 P、I、D分别是“比例&#xff08;proportional&#xff09;、积分&#xff08;integral&#xff09;、微分&#xff…

TCP Nagle算法简述

TCP/IP协议中&#xff0c;无论发送多少数据&#xff0c;总是要在数据前面加上协议头&#xff0c;同时&#xff0c;对方接收到数据&#xff0c;也需要发送ACK表示确认。为了尽可能的利用网络带宽&#xff0c;TCP总是希望尽可能的发送足够大的数据。 &#xff08;一个连接会设置M…

倒角算法推导

推导原理基本很简单&#xff1a; 已知AB&#xff0c; BC两条线段&#xff0c;且交于B点&#xff0c;求倒角半径为 L&#xff0c;AB&#xff0c;BC的倒角 以最短边&#xff08;假定为AB&#xff09;长 LAB&#xff0c; 在BC中&#xff0c;以B为起点&#xff0c;找出与LAB同长度…

[控制算法]

[常用控制算法] 0.博览众长 0.1 视频 1. DR_CAN b站 0.2 文章 1.控制算法整理 0.3 传统 VS 现代控制算法 1. 传统 传统控制算法&#xff1a;PID&#xff0c;模糊&#xff0c;神经网络控制算法。 2. 现代 现代控制算法有比例&#xff0c;LQR算法(用于线性系统)&#x…

求树的直径证明

树的直径&#xff08;最长路&#xff09; 的详细证明 主要是利用了反证法&#xff1a; 假设 s-t这条路径为树的直径&#xff0c;或者称为树上的最长路 现有结论&#xff0c;从任意一点u出发搜到的最远的点一定是s、t中的一点&#xff0c;然后在从这个最远点开始搜&#xff0c;就…

树的直径和树的重心

1.树包括有根树和无根树&#xff0c;有根树是有向图的子图&#xff0c;无根树是无向图的子图&#xff0c;都满足边数等于节点数减一。根是入度为零或没有父亲的节点 2.树的直径&#xff1a;树上最长的简单路径&#xff08;不重复经过点的路径&#xff09; 3.求解算法&#xf…

树的直径总结

树的直径 一、定义 在一棵树中&#xff0c;最远的两个子节点之间的距离被称为树的直径&#xff1b; 链接这两个点的路径被称为树的最长链&#xff1b; 有两种求法&#xff0c;时间复杂度均为 O ( n ) O(n) O(n) &#xff1b; 二、树形DP 1. 状态 由于一个点的最长路通过…

基础算法 - 树的直径

题目地址&#xff1a;https://leetcode-cn.com/problems/tree-diameter/ 1245. 树的直径 难度中等48收藏分享切换为英文接收动态反馈 给你这棵「无向树」&#xff0c;请你测算并返回它的「直径」&#xff1a;这棵树上最长简单路径的 边数。 我们用一个由所有「边」组成的数…

树的直径-c++

题目 实验室里原先有一台电脑(编号为1)&#xff0c;最近氪金带师咕咕东又为实验室购置了N-1台电脑&#xff0c;编号为2到N。每台电脑都用网线连接到一台先前安装的电脑上。但是咕咕东担心网速太慢&#xff0c;他希望知道第i台电脑到其他电脑的最大网线长度&#xff0c;但是可怜…