本文主要介绍通过修改内核驱动的方法,使得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关掉系统的安全启动。