ftdi_sio驱动学习笔记 6 - 增加MPSSE GPIO

目录

1. ftdi_private

2. 设置MPSSE模式

3. USB写函数

4. USB读操作

5. mpsse写gpio

6. mpsse读gpio

7. ftdi_gpio_init

8. ftdi_gpio_request

9. ftdi_gpio_free

10. ftdi_gpio_direction_get

11. ftdi_gpio_direction_input

12. ftdi_gpio_direction_output

13. ftdi_gpio_init_valid_mask

14. ftdi_gpio_get

15.  ftdi_gpio_set

15. ftdi_gpio_get_multiple

16. ftdi_gpio_set_multiple

17. 验证


之前GPIO是基于CBUS的模式,对于H系列芯片还支持MPSSE模式。注意CBUS模式和MPSSE模式不能同时使用。基本是根据CBUS模式修改。

1. ftdi_private

一路MPSSE最多支持16个GPIO,所以在ftdi_private里面增加3个u16的变量来表示这些gpio的属性。

#ifdef CONFIG_GPIOLIB
	struct gpio_chip gc;
	struct mutex gpio_lock;	/* protects GPIO state */
	bool gpio_registered;	/* is the gpiochip in kernel registered */
	bool gpio_used;		/* true if the user requested a gpio */
	u8 gpio_altfunc;	/* which pins are in gpio mode */
	u8 gpio_output;		/* pin directions cache */
	u8 gpio_value;		/* pin value for outputs */

	u16 mpsse_gpio_altfunc;
	u16 mpsse_gpio_output;
	u16 mpsse_gpio_value;
#endif

mpsse_gpio_altfunc这个变量每个位用于在系统上显示该GPIO是否被占用。 mpsse_gpio_output表示GPIO的方向,而mpsse_gpio_value表示GPIO的电平。

2. 设置MPSSE模式

首先在ftdi_sio.h里面添加MPSSE模式的参数值。

#define FTDI_SIO_BITMODE_MPSSE		0x02

然后是设置MPSSE模式的函数

static int ftdi_set_mpsse_mode(struct usb_serial_port *port)
{
	return ftdi_set_bitmode(port, FTDI_SIO_BITMODE_MPSSE);
}

在ftdi_private添加一个变量bitmode用来表示芯片当前处于什么模式。

u8 bitmode;

然后在ftdi_set_bitmode里面改变这个变量

priv->bitmode = mode;

因为mpsse的读写是需要通过串口的读写流程实现的,所以这里会根据进出mpsse模式打开或关闭串口,另外还需要设置latency以方便读写速度最快(H系列最小可以设置为0,其他则为4,这个值可能需要根据平台调试,如果设置为2,长时间测试可能会读数据错误)。

    if(mode == FTDI_SIO_BITMODE_MPSSE || 
		mode == FTDI_SIO_BITMODE_ASYNC_BITBANG || 
		mode == FTDI_SIO_BITMODE_SYNC_BITBANG) {
		struct tty_struct *tty = tty_port_tty_get(&port->port); 
		unsigned int latency = priv->latency;

		usb_serial_generic_open(tty, port);
		switch(priv->chip_type) {
			case FT232H:
			case FT232HP:
			case FT233HP:
			case FT2232H:
			case FT2232HP:
			case FT2233HP:
			case FT4232H:
			case FT4232HP:
			case FT4233HP:
			case FT4232HA:
				priv->latency = 0;	//For H serial, it can be set to 0.
				break;
			default:
				priv->latency = 4;
		}
		write_latency_timer(port);
		priv->latency = latency;
	} else {
		usb_serial_generic_close(port);
		write_latency_timer(port);
	}

3. USB写函数

需要添加写数据的api函数。这个函数的功能是把数据通过USB写给FTDI设备,串口默认那个写只是写缓冲,这里是直接通过usb发送。

static int ftdi_usb_write(struct usb_serial_port *port,
				  const unsigned char *data, int count, bool wait)
{
}

参数data是要写出去的数据,count是数据的长度,wait是表示是否等待写数据结束。

直接调用串口的写函数

result = usb_serial_generic_write(tty, port, data, count);
if(result < 0){
	dev_err(&port->dev, "Write failed\n");
	return result;
}

这个API函数会先把data写入到端口的FIFO中,然后找到空闲的写urb(FT232H有2个写urb),配置完urb后提交urb即可,并不会等待数据发送结束,而对于mpsse应用,写完后需要等待数据写完,这里是设置了以100ms为单位的超时错误。

if(wait) {
    result = wait_event_interruptible_timeout(priv->write_wait, !port->tx_bytes, HZ *     (count / priv->max_packet_size + 1) / 10);
    if (result == -ERESTARTSYS) {
	    // 信号中断睡眠
	    result = -EINTR;
    } else if (!result) {
	    // 超时
	    dev_err(&port->dev, "Write pending timeout\n");
	    result = -ETIMEDOUT;
    } else {
	    result = count;
    }
}

write_wait是一个等待队列头结构体变量,需要在结构体ftdi_private中添加(需要添加头文件<linux/wait.h>)

wait_queue_head_t write_wait;  

需要增加一个写数据结束的回调函数以唤醒等待队列。

static void ftdi_write_bulk_callback(struct urb *urb)
{
	struct usb_serial_port *port = urb->context;
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	usb_serial_generic_write_bulk_callback(urb);
	if(priv->bitmode == FTDI_SIO_BITMODE_MPSSE || 
		priv->bitmode == FTDI_SIO_BITMODE_SYNC_BITBANG) {
		if(urb->status == 0) {
			if(port->tx_bytes == 0) {
				wake_up_interruptible(&priv->write_wait); // 唤醒等待队列  
			}
		}
	}
}

这里的回调函数需要在ftdi_device里面添加

.write_bulk_callback =	ftdi_write_bulk_callback,

由于这一步会覆盖标准的串口写回调函数,所以在ftdi_write_bulk_callback里面会先调用标准的回调函数usb_serial_generic_write_bulk_callback。

4. USB读操作

读是不用设置的,但是当前读到数据后的回调函数是ftdi_process_read_urb。一旦打开设备,urb读请求就会设置好,然后读入数据后会继续设置urb读请求。而FTDI芯片会一直发送数据,如果没有实际发送的数据,也会发送2个字节的modem状态,而如果有实际数据,前面2个字节也是这个状态。

首先在ftdi_private里面添加新的变量来判断数据结束和读入的数据。

wait_queue_head_t read_wait; 
struct kfifo read_fifo; 
int read_len;

对于mpsse应用,在读之前会写入命令,所以在写之前需要初始化好read_fifo, read_len这2个变量。

在ftdi_process_read_urb结束前添加

    if((priv->bitmode == FTDI_SIO_BITMODE_MPSSE || 
			priv->bitmode == FTDI_SIO_BITMODE_SYNC_BITBANG) 
			&& count > 0) {
		int offset = 2;

		while(count > 0) {
			int ret = 0;
			count += 2;
			//printk("%s: count=%d,offset=%d,read_len=%d\n", __func__, count, offset, priv->read_len);
			if(count >= priv->max_packet_size) {
				if(kfifo_initialized(&priv->read_fifo)) {
					ret = kfifo_in(&priv->read_fifo, data + offset, priv->max_packet_size - 2);
				}
				priv->read_len -= priv->max_packet_size - 2;
				count -= priv->max_packet_size;
				offset += priv->max_packet_size;
			} else {
				if(kfifo_initialized(&priv->read_fifo)) {
					ret = kfifo_in(&priv->read_fifo, data + offset, count - 2);
				}
				priv->read_len -= count - 2;
                offset += count;
				count = 0;
			}
		}
		if(priv->read_len <= 0) {
			priv->read_len = 0;
			if(kfifo_initialized(&priv->read_fifo))
				wake_up_interruptible(&priv->read_wait); // 唤醒等待队列  
		}
	}

设备每max_packet_size的前面2个字节是modem status,所以数据是从data[2]开始拷贝的。如果read_len变为0或小于0,就唤醒等待队列。例如FT230X的芯片max_packet_size为64字节,假设接收到124个字节数据,那么实际接收到的数据是128字节。数据结构是:

0x30 0x60 + 62字节数据 + 0x30 0x60 + 62字节数据

所以循环64字节把数据去掉2个字节压入FIFO中。

5. mpsse写gpio

static int ftdi_set_mpsse_pins(struct usb_serial_port *port)

 把16个gpio的状态一次更新,不区分不同gpio。

static int ftdi_set_mpsse_pins(struct usb_serial_port *port)
{
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	int result;
	u8 cmd[6];
    if(priv->mpsse_enable == false)
		return 0;
	cmd[0] = 0x80;
    cmd[1] = (u8)(priv->mpsse_gpio_value & 0xff);
    cmd[2] = (u8)(priv->mpsse_gpio_output & 0xff);
    cmd[3] = 0x82;
    cmd[4] = (u8)((priv->mpsse_gpio_value >> 8) & 0xff);
    cmd[5] = (u8)((priv->mpsse_gpio_output >> 8) & 0xff);
	result = ftdi_usb_write(port, cmd, true);
	return result;
}

6. mpsse读gpio

static int ftdi_get_mpsse_pins(struct usb_serial_port *port)

这个的返回值就是16个gpio的电平值。

mpsse读GPIO的命令是0x81和0x83,分别读入低字节和高字节GPIO状态。写完这2个命令字后读入2个字节数据。

首先初始化fifo

priv->read_len = 2;
if(kfifo_alloc(&priv->read_fifo, priv->read_len * 2, GFP_KERNEL)) {
	dev_info(&port->dev, "mpsse get pins read buffer malloc fail\n");
	return -ENOMEM;
}

然后发送2个字节的读GPIO命令

cmd[0] = 0x81;		//Read AD0-AD7
cmd[1] = 0x83;		//Read AC0-AC7
result = ftdi_usb_write(port, cmd, 2, false);
if(result < 0) {
	dev_info(&port->dev, "mpsse get pins fail(send command) %d\n", result);
	goto cleanup;
}

等待读入的数据,即2个字节数据

    result = wait_event_interruptible_timeout(priv->read_wait, !priv->read_len, HZ / 10);
    if (result == -ERESTARTSYS) {
        result = -EINTR;// 信号中断睡眠
        goto cleanup;
    } else if (result < 0) {  // 超时
        dev_err(&port->dev, "Read pending timeout\n");
        priv->read_len = 0; // 防止死循环
        result = -ETIMEDOUT;
        goto cleanup;
    }

最后把数据从fifo取出来,然后返回

result = kfifo_out(&priv->read_fifo, cmd, 2);
	result = (cmd[1] << 8) | cmd[0];
	
cleanup:
	kfifo_free(&priv->read_fifo);
	return result;

7. ftdi_gpio_init

增加ftdi_mpsse_gpio_init_ft232h,由于FT232H是特例,既支持CBUS又支持MPSSE,所以把GPIO分为20个pin,前面4个为CBUS,后面16个是MPSSE。

static void ftdi_mpsse_gpio_init_ft232h(struct usb_serial_port *port)
{
    struct ftdi_private *priv = usb_get_serial_port_data(port);
    priv->gc.ngpio += 16;
	priv->mpsse_gpio_altfunc = 0x0000;
}

然后在ftdi_gpio_init_ft232h后面添加

case FT232H:
case FT232HP:
case FT233HP:
	result = ftdi_gpio_init_ft232h(port);
	ftdi_mpsse_gpio_init_ft232h(port);
	init_waitqueue_head(&priv->read_wait);
	init_waitqueue_head(&priv->write_wait);
	break;

同理,添加FT2232H和FT4232H的情况。

static void ftdi_mpsse_gpio_init_ft2232h(struct usb_serial_port *port)
{
	struct ftdi_private *priv = usb_get_serial_port_data(port);
    priv->gc.ngpio = 16;
	priv->mpsse_gpio_altfunc = 0x0000;
}

static int ftdi_mpsse_gpio_init_ft4232h(struct usb_serial_port *port)
{
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	if(priv->channel == CHANNEL_A || priv->channel == CHANNEL_B)
    	priv->gc.ngpio = 8;
	else
	{
		priv->gc.ngpio = 0;
		return -EIO;
	}
	priv->mpsse_gpio_altfunc = 0x0000;
	return 0;
}

	case FT2232H:
	case FT2232HP:
    case FT2233HP:
		ftdi_mpsse_gpio_init_ft2232h(port);
        init_waitqueue_head(&priv->read_wait);
		init_waitqueue_head(&priv->write_wait);
        result = 0;
		break;

	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		result = ftdi_mpsse_gpio_init_ft4232h(port);
        init_waitqueue_head(&priv->read_wait);
		init_waitqueue_head(&priv->write_wait);
		break;

注意FT4232H有点特殊,通道C和通道D不支持MPSSE。 

编译安装驱动后可以看到gpio有20个了。

:~/Project/ftdi_sio$ cd /sys/class
:/sys/class$ cd gpio/gpiochip512
:/sys/class/gpio/gpiochip512$ ls
base  device  label  ngpio  power  subsystem  uevent
:/sys/class/gpio/gpiochip512$ cat base
512
:/sys/class/gpio/gpiochip512$ cat ngpio
20

8. ftdi_gpio_request

这一步是将GPIO暴露出来时执行的,是将芯片的模式切换到CBUS或MPSSE模式,而且只需要在申请第一个GPIO时处理设置模式。

首先把ftdi_private里面的gpio_used改一下,由原来的bool型改为int型,这样记录每个gpio是否使用。

int gpio_used;		/* true if the user requested a gpio */

这个函数里面设置这个变量为true的地方移动到返回前(移动到if外),并且判断这个gpio是不是已经request了,如果是就返回EBUSY错误。

if(priv->gpio_used & (1 << offset))
	result = -EBUSY;
else
	priv->gpio_used = (1 << offset);

在ftdi_gpio_request里面修改,要分3种情况,

switch(priv->chip_type) {

}

如果是FT232H/FT232HP/FT233HP,要区分CBUS和MPSSE模式。

case FT232H:
case FT232HP:
case FT233HP:
	if(offset < 4) {
		result = ftdi_set_cbus_pins(port);
		if (result) {
			goto fail_setmode;
		}
	} else {
		result = ftdi_set_mpsse_mode(port);
		if (result) {
			dev_info(&port->dev, "set mpsse mode fail %d\n", result);
			goto fail_setmode;
		}
		ftdi_set_mpsse_pins(port);
	}
	break;

就是将芯片切换到CBUS模式或者MPSSE模式。

如果是其他H系列的芯片则只要设置MPSSE模式。

case FT2232H:
case FT2232HP:
case FT2233HP:
case FT4232H:
case FT4232HP:
case FT4233HP:
case FT4232HA:
	result = ftdi_set_mpsse_mode(port);
	if (result) {
		goto fail_setmode;
	}	
	ftdi_set_mpsse_pins(port);
	break;

其他芯片按照之前的方式

default:
	result = ftdi_set_cbus_pins(port);
	if (result) {
		goto fail_setmode;
	}
	break;

这里去掉下面那部分代码是因为使用gpiodev这类库时,程序每次控制gpio都会request或free,不去掉会每次控制其他GPIO。

priv->gpio_output = 0x00;
priv->gpio_value = 0x00;

9. ftdi_gpio_free

这个是新增加的,为了处理unexport所有gpio后,需要做一些处理。在ftdi_gpio_init中添加这个函数的赋值

priv->gc.free = ftdi_gpio_free;

该函数将unexport的gpio对应的位清0

static void ftdi_gpio_free(struct gpio_chip *gc, unsigned int offset)
{
	struct usb_serial_port *port = gpiochip_get_data(gc);
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	priv->gpio_used &= ~(BIT(offset));
}

10. ftdi_gpio_direction_get

这个函数是读取到当前gpio的方向,通过私有数据gpio_output(CBUS模式)或mpsse_gpio_output(MPSSE模式)返回对应的bit就可。

同样区分三种方式,后面所有的gpio控制函数都是这样的结构。

switch(priv->chip_type) {
	case FT232H:
	case FT232HP:
	case FT233HP:
		if(gpio < 4)
			return !(priv->gpio_output & BIT(gpio));
		else
			return !(priv->mpsse_gpio_output & BIT(gpio - 4));
	case FT2232H:
	case FT2232HP:
	case FT2233HP:
	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		return !(priv->mpsse_gpio_output & BIT(gpio));
	default:
		return !(priv->gpio_output & BIT(gpio));
}

11. ftdi_gpio_direction_input

这个函数是设置gpio的方向为输入,输入的设定值是0,同样,私有数据gpio_output(CBUS模式)或mpsse_gpio_output(MPSSE模式)设置对应的位即可。

switch(priv->chip_type) {
	case FT232H:
	case FT232HP:
	case FT233HP:
	    if(gpio < 4) {
			priv->gpio_output &= ~BIT(gpio);
			result = ftdi_set_cbus_pins(port);
		} else {
			priv->mpsse_gpio_output &= ~BIT(gpio - 4);
			result = ftdi_set_mpsse_pins(port);
			if(result >= 0)
				result = 0;
		}
		break;	
	case FT2232H:
	case FT2232HP:
	case FT2233HP:
	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		priv->mpsse_gpio_output &= ~BIT(gpio);
		result = ftdi_set_mpsse_pins(port);
		if(result >= 0)
			result = 0;
		break;
	default:
		priv->gpio_output &= ~BIT(gpio);
		result = ftdi_set_cbus_pins(port);
		break;
}

12. ftdi_gpio_direction_output

这个函数是设置GPIO的方向为输出且输出电平。

switch(priv->chip_type) {
	case FT232H:
	case FT232HP:
	case FT233HP:
		if(gpio < 4) {
			priv->gpio_output &= ~BIT(gpio);
			priv->gpio_output |= BIT(gpio);
			if (value)
			    priv->gpio_value |= BIT(gpio);
			else
				priv->gpio_value &= ~BIT(gpio);

			result = ftdi_set_cbus_pins(port);
		} else {
			priv->mpsse_gpio_output &= ~BIT(gpio - 4);
			priv->mpsse_gpio_output |= BIT(gpio - 4);
			if (value)
				priv->mpsse_gpio_value |= BIT(gpio - 4);
			else
				priv->mpsse_gpio_value &= ~BIT(gpio - 4);
			result = ftdi_set_mpsse_pins(port);
            if(result >= 0)
				result = 0;
		}
		break;	
	case FT2232H:
	case FT2232HP:
	case FT2233HP:
	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		priv->mpsse_gpio_output &= ~BIT(gpio);
		priv->mpsse_gpio_output |= BIT(gpio);
		if (value)
			priv->mpsse_gpio_value |= BIT(gpio);
		else
			priv->mpsse_gpio_value &= ~BIT(gpio);
		result = ftdi_set_mpsse_pins(port);
        if(result >= 0)
			result = 0;
		break;
	default:
		priv->gpio_output |= BIT(gpio);
		if (value)
			priv->gpio_value |= BIT(gpio);
		else
			priv->gpio_value &= ~BIT(gpio);

		result = ftdi_set_cbus_pins(port);
		break;
}

13. ftdi_gpio_init_valid_mask

这个函数用于初始化芯片的合法掩码。

unsigned long map = priv->gpio_altfunc;
switch(priv->chip_type) {
	case FT232H:
	case FT232HP:
	case FT233HP:
		map = (priv->gpio_altfunc & 0x0f) | (priv->mpsse_gpio_altfunc << 4);
		break;	
	case FT2232H:
	case FT2232HP:
	case FT2233HP:
	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		map = priv->mpsse_gpio_altfunc;
		break;
	default:
		break;
}

如果altfunc对应的位为1,在系统里面这个GPIO就是被占用的。 

14. ftdi_gpio_get

这个函数是读取对应gpio的电平值,即sysfs中value文件的值。同样,分3种情况读取gpio即可。

switch(priv->chip_type) {
	case FT232H:
	case FT232HP:
	case FT233HP:
		if(gpio < 4){
			result = ftdi_read_cbus_pins(port);
		} else {
			mutex_lock(&priv->gpio_lock);
			result = ftdi_get_mpsse_pins(port);
			mutex_unlock(&priv->gpio_lock);
			result <<= 4;
		}
		break;
	case FT2232H:
	case FT2232HP:
	case FT2233HP:
	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		mutex_lock(&priv->gpio_lock);
		result = ftdi_get_mpsse_pins(port);
		mutex_unlock(&priv->gpio_lock);
		break;
	default:
		result = ftdi_read_cbus_pins(port);
		break;
}

15.  ftdi_gpio_set

对应ftdi_gpio_get,设置某个gpio的电平。

switch(priv->chip_type) {
	case FT232H:
	case FT232HP:
	case FT233HP:
		if(gpio < 4) {
			if (value)
				priv->gpio_value |= BIT(gpio);
			else
				priv->gpio_value &= ~BIT(gpio);

			result = ftdi_set_cbus_pins(port);
		} else {
			if (value)
				priv->mpsse_gpio_value |= BIT(gpio - 4);
			else
				priv->mpsse_gpio_value &= ~BIT(gpio - 4);
			result = ftdi_set_mpsse_pins(port);
		}
		break;	
	case FT2232H:
	case FT2232HP:
	case FT2233HP:
	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		if (value)
			priv->mpsse_gpio_value |= BIT(gpio);
		else
			priv->mpsse_gpio_value &= ~BIT(gpio);
		result = ftdi_set_mpsse_pins(port);
		break;
	default:
		if (value)
			priv->gpio_value |= BIT(gpio);
		else
			priv->gpio_value &= ~BIT(gpio);

		result = ftdi_set_cbus_pins(port);
		break;
}

15. ftdi_gpio_get_multiple

功能类似ftdi_gpio_set,只是获取多个gpio。

switch(priv->chip_type) {
	case FT232H:
	case FT232HP:
	case FT233HP:
		int mpsse = 0;
		result = 0;
		if(priv->bitmode != FTDI_SIO_BITMODE_MPSSE) {
			result = ftdi_read_cbus_pins(port);
		}
		else {
			mutex_lock(&priv->gpio_lock);
			mpsse = ftdi_get_mpsse_pins(port);
			mutex_unlock(&priv->gpio_lock);
		}
		result |= mpsse << 4;
		break;
	case FT2232H:
	case FT2232HP:
	case FT2233HP:
	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		mutex_lock(&priv->gpio_lock);
		result = ftdi_get_mpsse_pins(port);
		mutex_unlock(&priv->gpio_lock);
		break;
	default:
		result = ftdi_read_cbus_pins(port);
		break;
}

16. ftdi_gpio_set_multiple

功能类似ftdi_gpio_set,只是设置多个gpio。

switch(priv->chip_type) {
	case FT232H:
	case FT232HP:
	case FT233HP:
		unsigned long mask_mpsse = ((*mask) >> 4) & 0xffff;
		unsigned long bits_mpsse = ((*bits) >> 4) & 0xffff;
		if(priv->bitmode == FTDI_SIO_BITMODE_MPSSE) {
			priv->mpsse_gpio_value &= ~mask_mpsse;
			priv->mpsse_gpio_value |= bits_mpsse & mask_mpsse;
			ftdi_set_mpsse_pins(port);
		} else {
			priv->gpio_value &= ~mask_cbus;
			priv->gpio_value |= bits_cbus & mask_cbus;
			ftdi_set_cbus_pins(port);
		}
		break;	
	case FT2232H:
	case FT2232HP:
	case FT2233HP:
	case FT4232H:
	case FT4232HP:
	case FT4233HP:
	case FT4232HA:
		priv->mpsse_gpio_value &= ~(*mask);
		priv->mpsse_gpio_value |= *bits & *mask;
		ftdi_set_mpsse_pins(port);
		break;
	default:
		priv->gpio_value &= ~(*mask);
		priv->gpio_value |= *bits & *mask;
		ftdi_set_cbus_pins(port);
		break;
}

17. 验证

假设控制FT232H的ACBUS7,编号为base + 11,即前面4个为CBUS,所以MPSSE的AC7(第8个gpio)对应11。因为base = 512,所以该GPIO的编号为523。注意,在Ubuntu中要想操作gpio,需要root下执行命令。

:/sys/class/gpio# echo 523 > /sys/class/gpio/export
:/sys/class/gpio# ls
export  gpio523  gpiochip512  unexport
:/sys/class/gpio# echo "out" >/sys/class/gpio/gpio523/direction
:/sys/class/gpio# echo 1 > gpio523/value
:/sys/class/gpio# echo 0 > gpio523/value
:/sys/class/gpio# cat gpio523/value
0
:/sys/class/gpio# gpioinfo 0
gpiochip0 - 20 lines:
	line   0:      unnamed       kernel   input  active-high [used]
	line   1:      unnamed       kernel   input  active-high [used]
	line   2:      unnamed       kernel   input  active-high [used]
	line   3:      unnamed       kernel   input  active-high [used]
	line   4:      unnamed       unused   input  active-high 
	line   5:      unnamed       unused   input  active-high 
    line   6:      unnamed       unused   input  active-high 
	line   7:      unnamed       unused   input  active-high 
	line   8:      unnamed       unused   input  active-high 
	line   9:      unnamed       unused   input  active-high 
	line  10:      unnamed       unused   input  active-high 
	line  11:      unnamed       unused   input  active-high 
	line  12:      unnamed       unused   input  active-high 
	line  13:      unnamed       unused   input  active-high 
	line  14:      unnamed       unused   input  active-high 
	line  15:      unnamed       unused   input  active-high 
	line  16:      unnamed       unused   input  active-high 
	line  17:      unnamed       unused   input  active-high 
	line  18:      unnamed       unused   input  active-high 
	line  19:      unnamed       unused   input  active-high

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值