linux下让cp2102支持高于921600bps波特率的方法

本文主要介绍通过修改内核驱动的方法,使得cp2102支持高于921600bps波特率的方法。

1、windows下使用cp2102 usb转ttl模块调试rk3588板卡(默认波特率为1500000),putty能正常输入输出。

2、linux下使用cp2102 usb转ttl模块调试rk3588板卡(默认波特率为1500000)的时候,putty和minicom输出均为乱码。通过minicom修改波特率为1500000bps,2000000bps等高于1000000bps时,通过示波器测得的波形频率均为925.9kHz,没有变化,基本怀疑是linux驱动的限制。于是通过阅读产品手册和Linux驱动代码来解释该现象,并通过修改驱动的方法,让cp2102支持更高的波特率。

官方手册给出cp2102支持的波特率如下表所示:

 官方手册给出cp2104的波特率如下表所示:

并且相关波特率的计算方法如下图所示:

cp210x linux下的驱动文件为(drivers/usb/serial/cp210x.c),根据内核版本下载对应的文件到本地,内核通过下列代码实现波特率修改。

/*
 * CP2101 supports the following baud rates:
 *
 *	300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800,
 *	38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600
 *
 * CP2102 and CP2103 support the following additional rates:
 *
 *	4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000,
 *	576000
 *
 * The device will map a requested rate to a supported one, but the result
 * of requests for rates greater than 1053257 is undefined (see AN205).
 *
 * CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud,
 * respectively, with an error less than 1%. The actual rates are determined
 * by
 *
 *	div = round(freq / (2 x prescale x request))
 *	actual = freq / (2 x prescale x div)
 *
 * For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps
 * or 1 otherwise.
 * For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1
 * otherwise.
 */
static void cp210x_change_speed(struct tty_struct *tty,
		struct usb_serial_port *port, struct ktermios *old_termios)
{
	struct usb_serial *serial = port->serial;
	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
	u32 baud;

	/*
	 * This maps the requested rate to the actual rate, a valid rate on
	 * cp2102 or cp2103, or to an arbitrary rate in [1M, max_speed].
	 *
	 * NOTE: B0 is not implemented.
	 */
	baud = clamp(tty->termios.c_ospeed, priv->min_speed, priv->max_speed);

	if (priv->use_actual_rate)
		baud = cp210x_get_actual_rate(baud);
	else if (baud < 1000000)
		baud = cp210x_get_an205_rate(baud);

	dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud);
	if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) {
		dev_warn(&port->dev, "failed to set baud rate to %u\n", baud);
		if (old_termios)
			baud = old_termios->c_ospeed;
		else
			baud = 9600;
	}

	tty_encode_baud_rate(tty, baud, baud);
}

在use_actual_rate为true的情况下通过cp210x_get_rate_rate获取,否则通过cp210x_get_an205_rate来获取。

static const struct cp210x_rate cp210x_an205_table1[] = {
	{ 300, 300 },
	{ 600, 600 },
	{ 1200, 1200 },
	{ 1800, 1800 },
	{ 2400, 2400 },
	{ 4000, 4000 },
	{ 4800, 4803 },
	{ 7200, 7207 },
	{ 9600, 9612 },
	{ 14400, 14428 },
	{ 16000, 16062 },
	{ 19200, 19250 },
	{ 28800, 28912 },
	{ 38400, 38601 },
	{ 51200, 51558 },
	{ 56000, 56280 },
	{ 57600, 58053 },
	{ 64000, 64111 },
	{ 76800, 77608 },
	{ 115200, 117028 },
	{ 128000, 129347 },
	{ 153600, 156868 },
	{ 230400, 237832 },
	{ 250000, 254234 },
	{ 256000, 273066 },
	{ 460800, 491520 },
	{ 500000, 567138 },
	{ 576000, 670254 },
	{ 921600, UINT_MAX }
};

/*
 * Quantises the baud rate as per AN205 Table 1
 */
static speed_t cp210x_get_an205_rate(speed_t baud)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(cp210x_an205_table1); ++i) {
		if (baud <= cp210x_an205_table1[i].high)
			break;
	}

	return cp210x_an205_table1[i].rate;
}

static speed_t cp210x_get_actual_rate(speed_t baud)
{
	unsigned int prescale = 1;
	unsigned int div;

	if (baud <= 365)
		prescale = 4;

	div = DIV_ROUND_CLOSEST(48000000, 2 * prescale * baud);
	baud = 48000000 / (2 * prescale * div);

	return baud;
}

cp210x_get_an205_rate获取表格指定的波特率,cp210x_get_rate_rate则是用cp2104给定的baud rate generation公式计算波特率和时钟分频比。

对于cp2102,波特率最大为921600bps,use_actual_rate为false;对于cp2104,波特率最大为2000000bps,use_actual_rate为true。

为了让cp2102像cp2104一样支持任意的波特率,对驱动稍作修改(cp2102和cp2014设置同样的参数)就可以达到,修改后的代码如下:

static void cp210x_init_max_speed(struct usb_serial *serial)
{
	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
	bool use_actual_rate = false;
	speed_t min = 300;
	speed_t max;

	switch (priv->partnum) {
	case CP210X_PARTNUM_CP2101:
		max = 921600;
		break;  
	case CP210X_PARTNUM_CP2103:
		max = 1000000;
		break;
	case CP210X_PARTNUM_CP2102:
	case CP210X_PARTNUM_CP2104:
		use_actual_rate = true;
		max = 2000000;
		break;
	case CP210X_PARTNUM_CP2108:
		max = 2000000;
		break;
	case CP210X_PARTNUM_CP2105:
		if (cp210x_interface_num(serial) == 0) {
			use_actual_rate = true;
			max = 2000000;	/* ECI */
		} else {
			min = 2400;
			max = 921600;	/* SCI */
		}
		break;
	case CP210X_PARTNUM_CP2102N_QFN28:
	case CP210X_PARTNUM_CP2102N_QFN24:
	case CP210X_PARTNUM_CP2102N_QFN20:
		use_actual_rate = true;
		max = 3000000;
		break;
	default:
		max = 2000000;
		break;
	}

	priv->min_speed = min;
	priv->max_speed = max;
	priv->use_actual_rate = use_actual_rate;
}

编译和安装新修改的驱动:

1、建立Makefile文件,编译生成cp210x.ko


obj-m := cp210x.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
    $(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
    rm -rf *.o *.ko

2、安装

卸载已经加载的驱动

sudo rmmod cp210x

将系统原有的驱动改名

sudo mv /lib/modules/$(shell uname -r)/kernel/drivers/usb/serial/cp210x.ko /lib/modules/$(shell uname -r)/kernel/drivers/usb/serial/cp210x_old.ko

将新编译驱动copy到制定目录

将新编译的驱动拷贝到ib/modules/$(shell uname -r)/kernel/drivers/usb/serial/cp210x.ko 

安装驱动(重启系统会自动安装)

sudo insmod cp210x.ko

如果提示驱动未签名,可以通过bios关掉系统的安全启动。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值