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

文章介绍了如何使用CH347芯片通过USB转接实现对Linux/Android系统的SPI、I2C和GPIO接口扩展,提供高速接口和中断支持。驱动程序支持动态分配总线号和GPIO编号,并允许直接挂载原有SPI和I2C设备。此外,还提供了GPIO中断服务程序的示例代码。

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

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

项目地址:GitHub - WCHSoftGroup/ch34x_mphsi_master_linux

应用框图:

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

驱动概述

该Master驱动支持在Linux/安卓主机上,使用USB转串口/JTAG/SPI/I2C/GPIO转换芯片CH347,和USB转串口/SPI/I2C/GPIO转换芯片CH341。

驱动仅支持SPI/I2C/GPIO接口,该文档主要介绍CH347芯片的相关特性。

CH347F SPI接口

PIN脚SPI功能脚GPIO复用脚
13SCS0-
7SCS1-
14SCK-
16MOSI-
15MISO-

CH347T SPI接口

PIN脚SPI功能脚GPIO复用脚
5SCS0gpio2
9SCS1gpio5
6SCKgpio0
8MOSI-
7MISOgpio1

SPI接口特性:

  • SPI模式0/1/2/3

  • SPI时钟频率60MHz~218.75KHz

  • MSB/LSB传输

  • 8位/16位传输

  • 2路片选

  • 片选高/低有效

CH347F I2C接口

PIN脚I2C功能脚GPIO复用脚
11SCL-
12SDA-

CH347T I2C接口

PIN脚I2C功能脚GPIO复用脚
11SCLgpio3
12SDA-

CH347F/T支持至少4种I2C时钟:20kHz,100kHz,400kHz和750kHz。驱动配置的默认I2C时钟为100KHz,当前不支持动态修改该时钟频率,若需要修改可以在ch34x_mphsi_i2c_init函数中修改。

在Linux上增加对器件的驱动支持十分方便,只需要将该器件的设备驱动绑定到此Master驱动生成的总线下即可。举例:

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

驱动创建的I2C设备文件在/sys/bus/i2c/devices/i2c-$DEV/ 目录下

CH347F GPIO接口

PIN脚GPIO复用脚
17gpio0
18gpio1
10gpio2
9gpio3
23gpio4
24gpio5
25gpio6
26gpio7

CH347T GPIO接口

PIN脚GPIO复用脚
15gpio4
2gpio6
13gpio7

CH347的硬件接口支持GPIO0~GPIO7,考虑到部分引脚被SPI和I2C的接口占用了,此驱动仅开放支持了GPIO4,GPIO6和GPIO7。

驱动操作说明

  • 使用“make”或者其他方式编译此驱动,如果动态编译成功会生成“ch34x_mphsi_master.ko”驱动模块

  • 使用“sudo make load”或“sudo insmod ch34x_mphsi_master.ko”动态加载驱动,使用此方式加载SPI总线号和GPIO起始序号会自动分配,也可以通过增加参数进行指定。

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

  • 使用“sudo make unload”或“sudo rmmod ch34x_mphsi_master.ko”卸载驱动

  • 使用“sudo make install”将驱动开机自动工作

  • 使用“sudo make uninstall”卸载该驱动

使用此驱动,需要确认CH347/CH341已经插入主机并且工作正常,可以使用“lsusb”或“dmesg”指令来确定,CH347/CH341的厂商VID是0x1A86。

如果芯片工作正常,可以使用“ls /sys/class/master”,“ls /sys/class/gpio”指令确认设备节点路径。

用户空间访问

使用SPI接口

一旦驱动加载成功,默认会提供2个关联到这个新的SPI Bus的SPI Slave设备,以CH347为例:

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

根据设备名称规则 /dev/spidev<bus>.<cs><bus>是驱动自动选择的总线号,<cs> 是芯片指定引脚的片选信号。

自linux内核5.15开始绑定到spidev驱动需要主动bind使/dev目录下设备可用,如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驱动管理的设备:

# 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

标准I/O函数如 open, ioctlclose 可以直接和该spi slave进行通讯,打开SPI设备:

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

设备打开成功后,可以使用 ioctl函数修改SPI配置和传输数据等。

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

函数 ioctl传输数据示例:

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存储设备

举例: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

注:为方便用户使用,该驱动默认会创建spidev设备,用户可以使用上面的命令主动解绑与spidev的绑定,或者undefine在ch34x_mphsi_master_spi.c文件中的“SPIDEV”宏定义。

使用GPIO接口

用户空间访问GPIO,可以使用sysfs,对驱动支持的GPIO,可在如下系统目录下查看。

/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>是定义在驱动变量 ch347_board_config中的GPIO名称 ,目录包含

  • value 文件用于配置或读取GPIO电平

  • edge文件用于配置GPIO中断使能以及中断类型

  • direction文件用于改变支持双向GPIO的引脚方向

注:对文件的读写操作,用户需要指定的读写权限。

当前支持的中断类型包括:

  • rising 上升沿中断

  • falling 下降沿中断

  • both 双边沿中断

打开GPIO

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

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

<gpio>是GPIO的名称

设置GPIO方向

配置GPIO方向为input或output,可在root权限下简单地写入inout字符串到direction文件。

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

设置GPIO输出

文件value打开后,可使用标准I/O函数进行读写,配置GPIO输出电平,可简单使用write函数,写入后GPIO会立刻输出指定电平。

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

读取GPIO电平

读取GPIO电平,可简单使用read函数:

char buf;
​
if (read(fd, &buf, 1) == -1) 
{
    perror("read");
    return -1;
}
​
value = (buf == '0') ? 0 : 1;

每一次读操作后,需要将文件位置指针需要重新定位到首字节。

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

使用GPIO中断

完整的使用GPIO中断功能的驱动例程:

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

注:该驱动默认会创建gpio设备,若需要在内核中使用中断功能,需要undefine在ch34x_mphsi_master_gpio.c中定义的“SYSFS_GPIO”宏。

CH341支持3种工作模式

模式0: [串口]

模式1: [SPI+ I2C + GPIO]

模式2: [打印口]

CH347F 支持1种模式

模式0: [串口* 2 + SPI + I2C + JTAG + SWD] VCP/CDC/HID 驱动模式

CH347T 支持4种模式

模式0: [串口* 2] VCP/CDC 驱动模式

模式1: [SPI + I2C + 串口* 1] VCP 驱动模式

模式2: [SPI + I2C + 串口* 1] HID 驱动模式

模式3: [JTAG + 串口* 1] VCP 驱动模式

该驱动支持CH347FCH341 模式1CH347 模式1

有技术问题,可以发邮件至技术邮箱: tech@wch.cn

<think>好的,用户现在想了解如何在NuttX中使用GPIOI2CSPI和CAN等协议的示例、配置和文档。首先,我需要回忆一下之前提供的NuttX相关信息,以及用户提到的引用内容。用户之前的问题涉及NuttX的环境搭建、性能调试,现在向具体外设的使用。 用户提供的引用内容中提到了AWorks平台的外设接口,比如GPIO的aw_gpio_get、aw_gpio_set等函数,SPI的aw_spi_mkdev、aw_spi_setup等接口,还有配置引脚和适配过程。但要注意,NuttX和AWorks是不同的RTOS,所以需要区分开,用户可能混淆了。因此,在回答时需要明确指出NuttX自身的配置方法,而不是AWorks的。 用户的需求是具体的协议使用示例和配置步骤,需要分每个协议来讲解。首先,GPIO在NuttX中的配置通常通过Kconfig工具,并在代码中使用相应的API。比如,在NuttX中,GPIO的配置可能涉及在board层的引脚映射,以及使用nxgpio_ops结构体中的函数进行操作。 接下来是I2C协议。根据用户引用中的内容,他们提到查看外设文件确定引脚,比如imx1050的I2C引脚。在NuttX中,启用I2C需要在配置中打开CONFIG_IMXRT_LPI2C,并在board初始化中配置引脚。然后使用标准的open、ioctl等接口进行操作,可能需要参考具体的驱动实现。 对于SPI,用户引用中提到了SPI主机接口,但属于AWorks。NuttX中的SPI配置需要启用对应的驱动,比如CONFIG_SPI,并在board中设置SPI总线。使用SPI时,可能需要通过ioctl命令设置模式、频率等参数,然后进行数据传输。 CAN协议方面,用户没有直接提到引用,但需要说明NuttX中的CAN配置步骤,比如启用CONFIG_CAN和相关驱动,配置引脚,然后使用socketcan接口或特定的CAN API进行操作。 需要注意的是,用户可能希望看到具体的代码示例和配置步骤,所以每个部分都应包含配置方法和代码片段。同时,用户之前的问题中提到了Kconfig工具的使用,因此在配置步骤中要指导用户如何通过menuconfig启用相关驱动和外设。 另外,用户可能对文档资源有需求,应提供NuttX官方文档链接或相关示例路径,帮助他们进一步学习。同时,考虑到用户可能在实际操作中遇到问题,可以建议参考board目录下的现有配置,或者社区资源。 最后,生成相关问题时要围绕NuttX的外设使用、配置调试、常见问题等,帮助用户深入理解和解决问题。</think>### NuttX 外设协议配置使用指南(GPIO/I2C/SPI/CAN) 在 NuttX 中使用外设协议需结合 **硬件引脚配置** 和 **驱动框架调用**,以下是具体操作流程示例代码。 --- #### 一、GPIO 配置使用 1. **配置引脚功能** 通过 `menuconfig` 启用 GPIO 驱动: ```bash # 在 NuttX 根目录执行 make menuconfig ``` 路径: **Device Drivers > GPIO Driver > Enable GPIO Driver**(勾选) 2. **代码示例** ```c #include <nuttx/gpio/gpio.h> // 初始化 GPIO(假设 GPIO1_17 对应 LED) #define LED_PIN (GPIO_PORT1 | GPIO_PIN17) // 配置为输出模式 nxgpio_configure(LED_PIN, GPIO_OUTPUT | GPIO_PULLUP); // 控制电平 nxgpio_write(LED_PIN, 1); // 高电平 nxgpio_toggle(LED_PIN); // 翻电平 ``` --- #### 二、I2C 协议配置 1. **硬件引脚映射** 在板级配置文件中定义 I2C 引脚(如 `board/your_board/src/your_board.h`): ```c #define GPIO_I2C1_SCL (GPIO_PORT1 | GPIO_PIN16) // SCL 引脚 #define GPIO_I2C1_SDA (GPIO_PORT1 | GPIO_PIN17) // SDA 引脚 ``` 2. **驱动配置** 启用 I2C 驱动: ```bash make menuconfig ``` 路径: **Device Drivers > I2C Driver > Enable I2C Driver**(勾选) **System Type > Your MCU > I2C1 Support**(勾选) 3. **代码示例** ```c #include <nuttx/i2c/i2c_master.h> // 初始化 I2C 控制器 struct i2c_master_s *i2c = i2c_init(I2C_PORT1); // 发送数据到从机地址 0x50 uint8_t buffer[2] = {0x01, 0x02}; i2c_transfer(i2c, 0x50, buffer, 2, NULL, 0); ``` --- #### 三、SPI 协议配置 1. **启用 SPI 驱动** 在 `menuconfig` 中勾选: **Device Drivers > SPI Driver > Enable SPI Driver** 2. **引脚定义** 在板级文件中配置 SPI 引脚(如 SPI1): ```c #define GPIO_SPI1_SCK (GPIO_PORT2 | GPIO_PIN0) #define GPIO_SPI1_MISO (GPIO_PORT2 | GPIO_PIN1) #define GPIO_SPI1_MOSI (GPIO_PORT2 | GPIO_PIN2) ``` 3. **代码示例** ```c #include <nuttx/spi/spi.h> struct spi_dev_s *spi = spi_init(SPI_PORT1); spi_setmode(spi, SPIDEV_MODE0); // 设置模式 spi_setbits(spi, 8); // 数据位宽 spi_send(spi, 0xAA); // 发送单字节 ``` --- #### 四、CAN 协议配置 1. **驱动配置** 启用 CAN 驱动: ```bash make menuconfig ``` 路径: **Device Drivers > CAN Driver > Enable CAN Driver** 2. **代码示例** ```c #include <nuttx/can.h> // 初始化 CAN 控制器 struct can_dev_s *can = can_init(CAN_PORT1); // 发送 CAN 帧 struct can_msg_s msg; msg.cm_hdr.ch_id = 0x123; // CAN ID msg.cm_hdr.ch_dlc = 8; // 数据长度 memcpy(msg.cm_data, "HelloCAN", 8); can_write(can, &msg); ``` --- #### 五、关键配置文件参考 1. **引脚定义** 通常位于 `boards/<架构>/<芯片型号>/<板级名称>/include/board.h`,例如: ```c // 定义 SPI 引脚 #define GPIO_SPI_CS (GPIO_OUTPUT | GPIO_PORT3 | GPIO_PIN4) ``` 2. **Kconfig 选项** 外设的编译开关通过 `arch/<架构>/src/Kconfig` 或 `drivers/Kconfig` 管理[^4]。 --- #### 六、文档资源 - **官方示例**:`apps/examples/*` 目录包含 GPIOSPI 等示例代码。 - **开发者手册**:[NuttX RTOS 文档](https://nuttx.apache.org/docs/latest/) - **调试工具**:使用 `ftrace` 监控外设中断频率(需启用 `CONFIG_DEBUG_FTRACE`)[^4]。 --- ### 相关问题 1. 如何通过 NuttX 的 `ioctl` 接口动态修改 SPI 时钟频率? 2. NuttX 中 CAN 总线错误帧的检测处理方法? 3. 如何为自定义硬件板添加 GPIO 引脚映射? 4. NuttX 是否支持多线程并发访问同一 I2C 总线? 如需进一步调试,可参考具体芯片的 `arch/<架构>/src/<芯片>/` 目录下的驱动实现[^1][^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PC技术小能手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值