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

文章目录

  • 前言
  • 一、环境介绍
  • 二、硬件配置
    • 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

系统框图:
【Linux】基于美信串行解串器实现UART串口通信_第1张图片

二、硬件配置

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 [email protected]
 * Description:This module configs the Maxin serializer and deserializer during kernel startup.
 * Version: 1.0 2022/3/26
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 96752
	unsigned 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 96763
	unsigned 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 id
	unsigned 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 contorl
	unsigned 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° angle
	unsigned char cmd_e[13]={ 0x79, 0x6c, 0x00, 0x00, 0x08, 0x09, 0x00, 0x03, 0x01, 0x2C, 0x1F, 0xB3, 0xCA }; // 95° angle
	unsigned char cmd_f[13]={ 0x79, 0x6c, 0x00, 0x00, 0x08, 0x01, 0x00, 0x01, 0x00, 0xAC, 0x9A, 0xC9, 0x31 }; // power on

	loff_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 0
	size = 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 0
	size = 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 ");
MODULE_DESCRIPTION("This module is used to config MAXIN chips through serial port");
MODULE_LICENSE("GPL");


你可能感兴趣的:(linux,android,C,android,linux,iot)