在安卓/Linux主機(jī)上經(jīng)常會遇到CPU原生SPI/I2C/GPIO Master資源通道不夠、功能或性能不滿足實(shí)際產(chǎn)品需求的情況,基于USB2.0高速USB轉(zhuǎn)接芯片CH347,配合廠商提供的USB轉(zhuǎn)MPHSI(Multi Protocol High-Speed Serial Interface)Master總線驅(qū)動(dòng)(CH34X-MPHSI-Master)可輕松實(shí)現(xiàn)為系統(tǒng)擴(kuò)展SPI和I2C總線、GPIO Expander、中斷信號等。


該驅(qū)動(dòng)軟件正常工作后,會在系統(tǒng)下創(chuàng)建新的SPI和I2C Master,擁有獨(dú)立的bus num,原SPI和I2C器件的設(shè)備驅(qū)動(dòng)可直接掛載到該總線上,無需任何修改。驅(qū)動(dòng)會同時(shí)創(chuàng)建GPIO相關(guān)資源,各GPIO可通過sysfs文件系統(tǒng)或應(yīng)用層軟件直接訪問,也可以由其他設(shè)備驅(qū)動(dòng)申請?jiān)揋PIO的訪問權(quán)以及申請GPIO對應(yīng)中斷號并注冊中斷服務(wù)程序。


項(xiàng)目地址:GitHub - WCHSoftGroup/ch34x_mphsi_master_linux


應(yīng)用框圖

image.png

CH347是一款高速USB總線轉(zhuǎn)接芯片,通過USB總線提供異步串口、I2C同步串行接口、SPI同步串行接口和JTAG接口等。本方案僅使用到CH347的高速SPI、I2C串行總線,以及GPIO功能,使用串口功能需要單獨(dú)使用CH343SER串口驅(qū)動(dòng),使用JTAG功能或SPI和I2C的非總線模式應(yīng)用可使用CH341PAR多功能USB設(shè)備驅(qū)動(dòng)。


CH34X-MSPI-Master總線驅(qū)動(dòng)特點(diǎn)

1、支持CH347與CH341總線轉(zhuǎn)接芯片;

2、支持SPI、I2C、GPIO、IRQ等接口和功能擴(kuò)展;

3、支持SPI、I2C的bus總線號、GPIO編號、IRQ中斷號的動(dòng)態(tài)分配以及靜態(tài)指定;

4、支持自動(dòng)綁定spi通用設(shè)備驅(qū)動(dòng),創(chuàng)建/dev/spidev*;


驅(qū)動(dòng)概述


該Master驅(qū)動(dòng)支持在Linux/安卓主機(jī)上,使用USB轉(zhuǎn)串口/JTAG/SPI/I2C/GPIO轉(zhuǎn)換芯片CH347,和USB轉(zhuǎn)串口/SPI/I2C/GPIO轉(zhuǎn)換芯片CH341。

驅(qū)動(dòng)僅支持SPI/I2C/GPIO接口,該文檔主要介紹CH347芯片的相關(guān)特性。


CH347F SPI接口

image.png


CH347T SPI接口

image.png


SPI接口特性:

1、SPI模式0/1/2/3

2、SPI時(shí)鐘頻率60MHz~218.75KHz

3、MSB/LSB傳輸

4、8位/16位傳輸

5、2路片選

6、片選高/低有效


CH347F I2C接口

image.png

CH347T I2C接口

image.png

CH347F/T支持至少4種I2C時(shí)鐘:20kHz,100kHz,400kHz和750kHz。驅(qū)動(dòng)配置的默認(rèn)I2C時(shí)鐘為100KHz,當(dāng)前不支持動(dòng)態(tài)修改該時(shí)鐘頻率,若需要修改可以在ch34x_mphsi_i2c_init函數(shù)中修改。

在Linux上增加對器件的驅(qū)動(dòng)支持十分方便,只需要將該器件的設(shè)備驅(qū)動(dòng)綁定到此Master驅(qū)動(dòng)生成的總線下即可。舉例:


modprobe bmi160_i2c

echo "bmi160 0x68" > /sys/bus/i2c/devices/i2c-$DEV/new_device


modprobe tcs3472

echo "tcs3472 0x29" > /sys/bus/i2c/devices/i2c-$DEV/new_device


驅(qū)動(dòng)創(chuàng)建的I2C設(shè)備文件在/sys/bus/i2c/devices/i2c-$DEV/ 目錄下


CH347F GPIO接口

image.png


CH347T GPIO接口

image.png

CH347的硬件接口支持GPIO0~GPIO7,考慮到部分引腳被SPI和I2C的接口占用了,此驅(qū)動(dòng)僅開放支持了GPIO4,GPIO6和GPIO7。


驅(qū)動(dòng)操作說明

1、使用“make”或者其他方式編譯此驅(qū)動(dòng),如果動(dòng)態(tài)編譯成功會生成“ch34x_mphsi_master.ko”驅(qū)動(dòng)模塊

2、使用“sudo make load”或“sudo insmod ch34x_mphsi_master.ko”動(dòng)態(tài)加載驅(qū)動(dòng),使用此方式加載SPI總線號和GPIO起始序號會自動(dòng)分配,也可以通過增加參數(shù)進(jìn)行指定。

3、如:“sudo insmod ch34x_mphsi_master.ko spi_bus_num=3 gpio_base_num=60”

4、使用“sudo make unload”或“sudo rmmod ch34x_mphsi_master.ko”卸載驅(qū)動(dòng)

5、使用“sudo make install”將驅(qū)動(dòng)開機(jī)自動(dòng)工作

6、使用“sudo make uninstall”卸載該驅(qū)動(dòng)


使用此驅(qū)動(dòng),需要確認(rèn)CH347/CH341已經(jīng)插入主機(jī)并且工作正常,可以使用“l(fā)susb”或“dmesg”指令來確定,CH347/CH341的廠商VID是0x1A86。

如果芯片工作正常,可以使用“l(fā)s /sys/class/master”,“l(fā)s /sys/class/gpio”指令確認(rèn)設(shè)備節(jié)點(diǎn)路徑。


用戶空間訪問

使用SPI接口

一旦驅(qū)動(dòng)加載成功,默認(rèn)會提供2個(gè)關(guān)聯(lián)到這個(gè)新的SPI Bus的SPI Slave設(shè)備,以CH347為例:

/dev/spidev0.0
/dev/spidev0.1


根據(jù)設(shè)備名稱規(guī)則 /dev/spidev<bus>.<cs>,<bus> 是驅(qū)動(dòng)自動(dòng)選擇的總線號,<cs> 是芯片指定引腳的片選信號。

自linux內(nèi)核5.15開始綁定到spidev驅(qū)動(dòng)需要主動(dòng)bind使/dev目錄下設(shè)備可用,如bus 0下slave 1:


# echo spidev > /sys/class/spi_master/spi0/spi0.1/driver_override

# echo spi0.1 > /sys/bus/spi/drivers/spidev/bind


對所有ch34x_mphsi_master驅(qū)動(dòng)管理的設(shè)備:

# for i in /sys/bus/usb/drivers/mphsi-ch34x/*/spi_master/spi*/spi*.*; do echo spidev > $i/driver_override; echo $(basename $i) > /sys/bus/spi/drivers/spidev/bind; done


標(biāo)準(zhǔn)I/O函數(shù)如 open, ioctl 和close 可以直接和該spi slave進(jìn)行通訊,打開SPI設(shè)備:


int spi = open("/dev/spidev0.0", O_RDWR));

設(shè)備打開成功后,可以使用 ioctl函數(shù)修改SPI配置和傳輸數(shù)據(jù)等。


uint8_t mode = SPI_MODE_0;

uint8_t lsb = SPI_LSB_FIRST;

...

ioctl(spi, SPI_IOC_WR_MODE, &mode);

ioctl(spi, SPI_IOC_WR_LSB_FIRST, &lsb);


函數(shù) ioctl傳輸數(shù)據(jù)示例:


uint8_t *mosi; // output data

uint8_t *miso; // input data

...

// fill mosi with output data

...

struct spi_ioc_transfer spi_trans;

memset(&spi_trans, 0, sizeof(spi_trans));

spi_trans.tx_buf = (unsigned long) mosi;

spi_trans.rx_buf = (unsigned long) miso;

spi_trans.len = len;

int status = ioctl (spi, SPI_IOC_MESSAGE(1), &spi_trans);

// use input data in miso


掛載SPI NOR FLASH作為MTD存儲設(shè)備

舉例:flash器件掛載到bus 0 chip 0(spi0.0)


# echo spi0.0 > /sys/bus/spi/drivers/spidev/unbind

# echo spi-nor > /sys/bus/spi/devices/spi0.0/driver_override

# echo spi0.0 > /sys/bus/spi/drivers/spi-nor/bind


:為方便用戶使用,該驅(qū)動(dòng)默認(rèn)會創(chuàng)建spidev設(shè)備,用戶可以使用上面的命令主動(dòng)解綁與spidev的綁定,或者undefine在ch34x_mphsi_master_spi.c文件中的“SPIDEV”宏定義。


使用GPIO接口

用戶空間訪問GPIO,可以使用sysfs,對驅(qū)動(dòng)支持的GPIO,可在如下系統(tǒng)目錄下查看。

/sys/class/gpio/<gpio>/

is created by the system, where <gpio> is the name of the GPIO as defined in the driver variable ch347_board_config. These directories contain

<gpio> 是定義在驅(qū)動(dòng)變量 ch347_board_config中的GPIO名稱 ,目錄包含

1、value 文件用于配置或讀取GPIO電平

2、edge 文件用于配置GPIO中斷使能以及中斷類型

3、direction 文件用于改變支持雙向GPIO的引腳方向


注:對文件的讀寫操作,用戶需要指定的讀寫權(quán)限。

當(dāng)前支持的中斷類型包括:

1、rising 上升沿中斷

2、falling 下降沿中斷

3、both 雙邊沿中斷


打開GPIO

使用GPIO前,需要先打開value文件

int??fd;

if?((fd?=?open("/sys/class/gpio/<gpio>/value",?O_RDWR))?==?-1)?
{
????perror("open");
????return?-1;
}


<gpio> 是GPIO的名稱


設(shè)置GPIO方向


配置GPIO方向?yàn)閕nput或output,可在root權(quán)限下簡單地寫入in或out字符串到direction文件。

echo out > /sys/class/gpio/gpio4/direction


設(shè)置GPIO輸出


文件value打開后,可使用標(biāo)準(zhǔn)I/O函數(shù)進(jìn)行讀寫,配置GPIO輸出電平,可簡單使用write函數(shù),寫入后GPIO會立刻輸出指定電平。


if?(write(fd,?value???"1"?:?"0",?1)?==?-1)?
{
????perror?("write");
????return?-1;
}

讀取GPIO電平


讀取GPIO電平,可簡單使用read函數(shù):


char?buf;

if?(read(fd,?&buf,?1)?==?-1)?
{
????perror("read");
????return?-1;
}

value?=?(buf?==?'0')???0?:?1;

每一次讀操作后,需要將文件位置指針需要重新定位到首字節(jié)。


if?(lseek(fd,?0,?SEEK_SET)?==?-1)?{
????perror("lseek");
????return?-1;
}

使用GPIO中斷


完整的使用GPIO中斷功能的驅(qū)動(dòng)例程:

#include?<linux/bitops.h>
#include?<linux/clk.h>
#include?<linux/delay.h>
#include?<linux/device.h>
#include?<linux/gpio.h>
#include?<linux/of_gpio.h>
#include?<linux/module.h>
#include?<linux/of.h>
#include?<linux/of_device.h>
#include?<linux/uaccess.h>
#include?<linux/irq.h>
#include?<linux/timer.h>
#include?<linux/interrupt.h>

#define?GPIO_NUMBER?509?/*?modify?with?actual?gpio?number?*/

static?irqreturn_t?gpio_interrupt(int?irq,?void?*dev_id)
{
????printk("gpio_interrupt?callback.\n");
????return?IRQ_HANDLED;
}

static?int?__init?ch34x_gpio_init(void)
{
????unsigned?long?flags?=?IRQF_TRIGGER_FALLING;
????int?ret;
????int?irq;
????irq?=?gpio_to_irq(GPIO_NUMBER);
????printk("irq:?%d\n",?irq);
????ret?=?gpio_request(GPIO_NUMBER,?"gpioint");
????if?(ret)?{
????????printk("gpio_request?failed.\n");
????????goto?exit;
????}
????ret?=?gpio_direction_input(GPIO_NUMBER);
????if?(ret)?{
????????printk("gpio_direction_input?failed.\n");
????????gpio_free(GPIO_NUMBER);
????????goto?exit;
????}
????irq_set_irq_type(irq,?flags);
????ret?=?request_irq(irq,?gpio_interrupt,?0,?"gpio_handler",?NULL);
????printk("%s?-?request_irq?=?%d?result?=?%d\n",?__func__,?irq,?ret);
exit:
????return?ret;
}

static?void?__exit?ch34x_gpio_exit(void)
{
????int?irq;
????irq?=?gpio_to_irq(GPIO_NUMBER);
????free_irq(irq,?NULL);
????gpio_free(GPIO_NUMBER);
????printk("gpio?driver?exit.\n");
}

module_init(ch34x_gpio_init);
module_exit(ch34x_gpio_exit);
MODULE_LICENSE("GPL");


:該驅(qū)動(dòng)默認(rèn)會創(chuàng)建gpio設(shè)備,若需要在內(nèi)核中使用中斷功能,需要undefine在ch34x_mphsi_master_gpio.c中定義的“SYSFS_GPIO”宏。


CH341支持3種工作模式

模式0: [串口]

模式1: [SPI+ I2C + GPIO]

模式2: [打印口]


CH347F支持1種模式(優(yōu)選&主推型號)

模式0: [串口* 2 + SPI + I2C + JTAG + SWD]? VCP/CDC/HID 驅(qū)動(dòng)模式


CH347T 支持4種模式

模式0: [串口* 2] VCP/CDC 驅(qū)動(dòng)模式

模式1: [SPI + I2C + 串口* 1] VCP 驅(qū)動(dòng)模式

模式2: [SPI + I2C + 串口* 1] HID 驅(qū)動(dòng)模式

模式3: [JTAG + 串口* 1] VCP 驅(qū)動(dòng)模式


該驅(qū)動(dòng)支持CH347F、CH341的模式1、CH347的模式1


有技術(shù)問題,可以發(fā)郵件至技術(shù)郵箱: tech@wch.cn