基于CH347实现USB扩展SPI/I2C/GPIO Master主机应用方案

在安卓/Linux主机上经常会遇到CPU原生SPI/I2C/GPIO Master资源通道不够或者功性能不满足实际产品需求的情况,基于USB2.0高速USB转接芯片CH347,配合厂商提供的USB转MPSI(Multi Peripheral Serial Line)Master总线驱动(CH34X-MSPI-Master)可轻松实现为系统扩展SPI和I2C总线、GPIO Expander、中断信号等。

该驱动软件正常工作后,会在系统下创建新的SPI和I2C Master,拥有独立的bus num,原SPI和I2C器件的设备驱动可直接挂载到该总线上,无需任何修改。驱动会同时创建GPIO相关资源,各GPIO可通过sysfs文件系统或应用层软件直接访问,也可以由其他设备驱动申请该GPIO的访问权以及申请GPIO对应中断号并注册中断服务程序。

应用框图:

基于CH347实现USB扩展SPI/I2C/GPIO Master主机应用方案_第1张图片

 CH347是一款高速USB总线转接芯片,通过USB总线提供异步串口、I2C同步串行接口、SPI同步串行接口和JTAG接口等。本方案仅使用到CH347的高速SPI、I2C串行总线,以及GPIO功能,使用串口功能需要单独使用CH343SER串口驱动,使用JTAG功能或SPI和I2C的非总线模式应用可使用CH341PAR多功能USB设备驱动。

CH34X-MSPI-Master总线驱动特点:

1、支持CH347与CH341总线转接芯片;

2、支持SPI、I2C、GPIO、IRQ等接口和功能扩展;

3、支持SPI、I2C的bus总线号、GPIO编号、IRQ中断号的动态分配以及静态指定;

4、支持自动绑定spi通用设备驱动,创建/dev/spidev*;

Description

This driver can be used with ch347 usb to uart/jtag/spi/i2c/gpio adapter and ch341 usb to uart/spi/i2c/gpio adapter to connect spi/i2c/gpio slaves to a linux host.

This driver can only be used with spi/i2c/gpio interfaces. This document mainly introduces the relevant features of CH347.

CH347 SPI Interface

Pin SPI Function GPIO name
5 SCS0 gpio2
9 SCS1 gpio5
6 SCK gpio0
8 MOSI -
7 MISO gpio1

The SPI hardware interface supports:

  • SPI Mode 0/1/2/3

  • Clock frequency 60MHz/30MHz/15Mhz/7.5MHz/3.75MHz/1.875MHz/937.5KHz/468.75KHz

  • MSB/LSB transfer

  • 8/16 bits per word(16bits transfer to be continued...)

  • 2 slaves at maximum

  • Chip selection high/low active

CH347 I2C Interface

Pin I2C Function GPIO name
11 SCL gpio3
12 SDA -

The ch347 supports 4 different speeds: 20kHz, 100kHz, 400kHz and 750kHz. The driver inits i2c clock to 100kHz by default, and that currently cannot be dynamically changed. It is possible to change it with the ch34x_mpsi_i2c_init function.

Adding support for a device supported by Linux is easy. For instance:

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

or:

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

Files from these drivers will be created somewhere in /sys/bus/i2c/devices/i2c-$DEV/

CH347 GPIO Interface

Pin GPIO name
15 gpio4
2 gpio6
13 gpio7

The GPIO hardware interface supports GPIO0~GPIO7 actually, but this driver only supports GPIO4, GPIO6 and GPIO7 cause the other GPIOs are multiplexed.

Driver Operating Overview

  • Compile the driver using "make" or other method according to your environment, you will see the module "ch34x_mpsi_master.ko" if successful

  • Type "sudo make load" or "sudo insmod ch34x_mpsi_master.ko" to load the driver dynamically, in this way the spi bus number and gpio base number will be allocated dynamically, also they can be specified by parameters.

    exp: "sudo insmod ch34x_mpsi_master.ko spi_bus_num=3 gpio_base_num=60".

  • Type "sudo make unload" or "sudo rmmod ch34x_mpsi_master.ko" to unload the driver

  • Type "sudo make install" to make the driver work permanently

  • Type "sudo make uninstall" to remove the driver

Before the driver works, you should make sure that the ch341/ch347 device has been plugged in and is working properly, you can use shell command "lsusb" or "dmesg" to confirm that, VID of ch341/ch347 is [1A86].

If ch341/ch347 device works well, you can type "ls /sys/class/master" and "ls /sys/class/gpio" to confirm the master node.

Usage from user space

Using SPI slaves

Once the driver is loaded successfully, it provides up to 2 SPI slave devices on next available SPI bus, ch347 e.g.,

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

according to the naming scheme /dev/spidev.. is the bus number selected automatically by the driver and is the chip select signal of the according pin.

Since linux-5.15 binding to spidev driver is required to make slave devices available via /dev/, e.g. for slave 1 on bus 0:

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

For all devices handled by ch34x_mpsi_master driver:

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

Standard I/O functions like open, ioctl and close can be used to communicate with one of the slaves connected to the SPI.

To open an SPI device simply use:

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

Once the device is opened successfully, you can modify SPI configurations and transfer data using ioctl function.

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);

Function ioctl is also used to transfer data:

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

Attaching SPI NOR flash as MTD

E.g. flash IC is attached to 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

Please note: this driver will create spidev devices by default, you can unbind the device using the above command, or undefine the macro "SPIDEV" in file ch34x_mpsi_master_spi.c.

Using GPIOs

To access GPIOs from user space, sysfs can be used . For each configured GPIO, a directory

/sys/class/gpio//

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

  • the file value that can be used to read from and write to GPIOs

  • the file edge that can be used to control whether and what type of interrupt is enabled

  • the file direction that can be used to change the direction of the GPIO if possible

Please note: For read and write operations from and/or to these files, the user requires read and/or write permissions, respectively.

Possible interrupt types are

  • rising for interrupts on rising signal edges,

  • falling for interrupts on falling signal edges, and

  • both for interrupts on rising as well as falling signal edges.

Open a GPIO

Before a GPIO can be used, file value has to be opened

int  fd;

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

where is again the name of the GPIO.

Change the GPIO direction

To change the direction of a GPIO pin configured as input or output, simply write as root keyword in or keyword out to the file direction, e.g.

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

Write GPIO output

Once the file value is opened, you can use standard I/O functions to read and write. To write a GPIO value, simply use function write as following. The value is written to the GPIO out immediately.

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

Read GPIO input

To read values from GPIOs immediately, you can simply use function read as following:

char buf;

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

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

After each read operation, file position has to be rewound to first character before the next value can be read.

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

Reacting on GPIO input interrupt

Complete gpio driver example to use GPIO interrupt function.

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

#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");

Please note: this driver will create gpio devices by default, users should undefine the macro "SYSFS_GPIO" in file ch34x_mpsi_master_gpio.c before using gpio interruption in kernel.

Note

ch341 supports 2 modes:

mode0: [uart]

mode1: [spi + i2c + gpio]

ch347 supports 4 modes:

mode0: [uart * 2] in vcp/cdc driver mode

mode1: [spi + i2c + uart * 1] in vcp driver mode

mode2: [spi + i2c + uart * 1] in hid driver mode

mode3: [jtag + uart * 1] in vcp driver mode

This driver only can be used with ch341 mode1 or ch347 mode1.

Any question, you can send feedback to mail: [email protected]

你可能感兴趣的:(SPI主机扩展,I2C主机扩展,GPIO与中断扩展,USB转SPI主机,USB转多功能主机)