<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    Kernel 中的 GPIO 定義和控制

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    最近要深一步用到GPIO口控制,寫個博客記錄下Kernel層的GPIO學習過程!

     

    一、概念

         General Purpose Input Output (通用輸入/輸出)簡稱為GPIO,或 總線擴展器。也就是芯片的引腳,當微控制器或芯片組沒有足夠的I/O端口,或當系統需要采用遠端串行通信或控制時,GPIO產品能夠提供額外的控制和監視功能。通常在ARM里,所有I/O都是通用的,

    每個GPIO端口包含8個管腳,如PA端口是PA0~PA7,而且GPIO口至少有兩個寄存器,即"通用IO控制寄存器"與"通用IO數據寄存器"。數據寄存器的各位都直接引到芯片外部,而對這種寄存器中每一位的作用,即每一位的信號流通方向,則可以通過控制寄存器中對應位獨立地加以設置。比如,可以設置某個引腳的屬性為輸入、輸出或其他特殊功能。

    常用的應該是:高阻輸入、推挽輸出、開漏輸出。

    通俗理解為 :

    高阻輸入—— 保持高阻抗狀態,徹底斷開輸出,避免干擾,對總線狀態不起作用,此時總線可由其他器件占用。

    推挽輸出——可以輸出高,低電平,連接數字器件。

    開漏輸出——輸出端相當于三極管的集電極. 要得到高電平狀態需要上拉電阻才行。

    軟件上就是通過設置IO口的模式,然后控制IO的上拉下拉,寫入對應寄存器,通過寄存器控制電路:

    上拉寄存器是控制對應端口上拉使能(DE)的。當對應位為0時,設置對應引腳上拉使能,為1時,禁止對應引腳上拉使能。如果上拉寄存器使能,無論引腳功能寄存器如何設置(輸入,輸出,數據,中斷等),對應引腳輸出高電平。

    上拉是一個電阻接到一個電壓,其實就是增強IO的驅動能力,IO口是高電平。
    下拉是一個電阻接到地,保證IO口是低電平。

     

     

    二、kernel層調用接口實現

    GPIO操作,在Kernel 2.6.32版本以上提供了gpio口管理的庫文件/kernel/drivers/gpio/gpiolib.c,里面就是我們需要的接口函數實現!

    幾個主要的方法:

    申請一個pin腳作為gpio口,命名為 * label,如果經過判斷空閑的 申請成功了做一些初始的bit位設置。

    int gpio_request(unsigned gpio, const char *label)
    {
    struct gpio_desc	*desc;
    struct gpio_chip	*chip;
    int	 status = -EINVAL;
    unsigned long	 flags;
    
    spin_lock_irqsave(&gpio_lock, flags);
    ...
    if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0)
    ...
    }

    釋放這個gpio口,還原bit。

     

    void gpio_free(unsigned gpio)
    {
    	unsigned long		flags;
    	struct gpio_desc	*desc;
    	struct gpio_chip	*chip;
    
    
    	might_sleep();
    ...
    clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
    clear_bit(FLAG_REQUESTED, &desc->flags);
    clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
    clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
    ...
    }

     

     

    設置gpio口為輸入模式:

    int gpio_direction_input(unsigned gpio)
    {
    	unsigned long		flags;
    	struct gpio_chip	*chip;
    	struct gpio_desc	*desc = &gpio_desc[gpio];
    	int			status = -EINVAL;
    
    
    	spin_lock_irqsave(&gpio_lock, flags);
    	if (!gpio_is_valid(gpio))
    		goto fail;
    	chip = desc->chip;
    ...
    	status = chip->direction_input(chip, gpio);
    ...
    }

    其中的chip->direction_input(chip, gpio)為實現!

     

    設置gpio口為輸出模式 value為初始值 0為高電平/1為低電平:

     

    int gpio_direction_output(unsigned gpio, int value)
    {
    	unsigned long		flags;
    	struct gpio_chip	*chip;
    	struct gpio_desc	*desc = &gpio_desc[gpio];
    	int			status = -EINVAL;
    
    
    	/* Open drain pin should not be driven to 1 */
    	if (value && test_bit(FLAG_OPEN_DRAIN,  &desc->flags))
    		return gpio_direction_input(gpio);
    ...
    	status = chip->direction_output(chip, gpio, value);
    ...
    }

     

    其中的chip->direction_output(chip, gpio, value)為實現!

     

     

    設置gpio口的值:

     

    void __gpio_set_value(unsigned gpio, int value)
    {
    	struct gpio_chip	*chip;
    
    	chip = gpio_to_chip(gpio);
    	WARN_ON(chip->can_sleep);
    	trace_gpio_value(gpio, 0, value);
    	if (test_bit(FLAG_OPEN_DRAIN,  &gpio_desc[gpio].flags))
    		_gpio_set_open_drain_value(gpio, chip, value);
    	else if (test_bit(FLAG_OPEN_SOURCE,  &gpio_desc[gpio].flags))
    		_gpio_set_open_source_value(gpio, chip, value);
    	else
    		chip->set(chip, gpio - chip->base, value);
    }

    獲取gpio口的值:

     

    int __gpio_get_value(unsigned gpio)
    {
    	struct gpio_chip	*chip;
    	int value;
    
    	chip = gpio_to_chip(gpio);
    	WARN_ON(chip->can_sleep);
    	value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
    	trace_gpio_value(gpio, 1, value);
    	return value;
    }

     

     對于有些掛載在I2C,SPI總線上的擴展GPIO,讀寫操作可能會導致睡眠,因此不能在中斷函數中
      使用。使用下面的函數以區別于正常的GPIO

     

      int gpio_get_value_cansleep(unsigned gpio);//讀GPIO
      void gpio_set_value_cansleep(unsigned gpio, int value);//寫GPIO

     

     

    三、gpiolib.c關聯芯片接口

     

    以上為gpiolib.c的基本方法都是向下調用到對應芯片的gpio實現!

    所以每個方法里面的實現都是通過 struct gpio_chip*chip 這個指針調用結構體中關聯的相關接口!

     

    下面是gplolib怎么關聯到特定的芯片(mach-at91)的幾個主要方法:

    mach-at91的/kernel/arch/arm/mach-at91/gpio.c中的

     

    static struct at91_gpio_chip gpio_chip[] = {
    	AT91_GPIO_CHIP("A", 0x00 + PIN_BASE, 32),
    	AT91_GPIO_CHIP("B", 0x20 + PIN_BASE, 32),
    	AT91_GPIO_CHIP("C", 0x40 + PIN_BASE, 32),
    	AT91_GPIO_CHIP("D", 0x60 + PIN_BASE, 32),
    	AT91_GPIO_CHIP("E", 0x80 + PIN_BASE, 32),
    };
    

     

    gpiolib.c中的接口與mach-at91函數接口對應關系如下:

     

    #define AT91_GPIO_CHIP(name, base_gpio, nr_gpio)			\
    	{								\
    		.chip = {						\
    			.label		  = name,			\
    			.direction_input  = at91_gpiolib_direction_input, \
    			.direction_output = at91_gpiolib_direction_output, \
    			.get		  = at91_gpiolib_get,		\
    			.set		  = at91_gpiolib_set,		\
    			.dbg_show	  = at91_gpiolib_dbg_show,	\
    			.base		  = base_gpio,			\
    			.ngpio		  = nr_gpio,			\
    		},							\
    	}

    從上面可以看到 在gpiolib.c中的方法調用芯片(mach-at91)的映射關系:

    比如:gpio_direction_output() 設置gpio口為輸出模式中最終的實現調用chip->direction_output(chip, gpio, value)。

    從上面可以看出來,也就是調用mach-at91的at91_gpiolib_direction_output()!

    其它的類似。

     

     

    gpiolib.c中的

     

    int gpiochip_add(struct gpio_chip *chip)
    {
    	unsigned long	flags;
    	int		status = 0;
    	unsigned	id;
    	int		base = chip->base;
    
    
    	if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
    			&& base >= 0) {
    		status = -EINVAL;
    		goto fail;
    	}
    
    
    	spin_lock_irqsave(&gpio_lock, flags);
    ....
    }

     

    添加到結構體:

     

    struct gpio_desc {
    	struct gpio_chip	*chip;
    	unsigned long		flags;
    /* flag symbols are bit numbers */
    #define FLAG_REQUESTED	0
    #define FLAG_IS_OUT	1
    #define FLAG_RESERVED	2
    ...
    }

    這就保存到了 chip 指針。

    這一部分在Kernel初始化的時候調用執行!

     

     

     

    四、芯片(mach-at91)gpio口的接口

    由gpiolib.c根據映射調用對應接口

     

    static int at91_gpiolib_direction_output(struct gpio_chip *chip,
    					 unsigned offset, int val)
    {
    	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
    	void __iomem *pio = at91_gpio->regbase;
    	unsigned mask = 1 << offset;
    
    	__raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR));
    	__raw_writel(mask, pio + PIO_OER);
    	return 0;
    }

    定義為輸出模式,初始設置val 上拉還是下拉 , 寫入數據寄存器。

     

    #define PIO_SODR	0x30	/* Set Output Data Register */
    #define PIO_CODR	0x34	/* Clear Output Data Register */
    #define PIO_ODR	 0x14	/* Output Disable Register */
    #define PIO_PDSR	 0x3c	/* Pin Data Status Register */

     

    同樣設置為輸入:

     

    static int at91_gpiolib_direction_input(struct gpio_chip *chip,
    					unsigned offset)
    {
    	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
    	void __iomem *pio = at91_gpio->regbase;
    	unsigned mask = 1 << offset;
    
    	__raw_writel(mask, pio + PIO_ODR);
    	return 0;
    }

     

     

    設置GPIO口的value 值 0或者1,已經設置為輸出模式下:

    static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val)
    {
    	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
    	void __iomem *pio = at91_gpio->regbase;
    	unsigned mask = 1 << offset;
    
    	__raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR));
    }

     

    讀寄存器獲取該GPIO口的值:

     

    static int at91_gpiolib_get(struct gpio_chip *chip, unsigned offset)
    {
    	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
    	void __iomem *pio = at91_gpio->regbase;
    	unsigned mask = 1 << offset;
    	u32 pdsr;
    
    	pdsr = __raw_readl(pio + PIO_PDSR);
    	return (pdsr & mask) != 0;
    }

     

    大體流程就這樣了。

    撰寫不易,轉載請注明出處http://blog.csdn.net/jscese/article/details/16823519

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全