天天看點

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

1 硬體結構

我使用的是風火輪出品的樹莓派A20闆子,同樣也買了一個DVK521擴充的底闆,可以看到有8個LED燈,關于GPIO,點亮LED是最好的驅動編寫例子。

先看DVK521闆子上的LED燈硬體原理:

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

LED燈原理圖

LED燈原理比較簡單,安裝該圖來看,需要将PE4~PE11置高即可,從這裡看來,A20的IO口輸出電流能力還是比較強。

2 驅動編寫

2.1 GPIO寄存器的相關知識

按照以前S3C2440,S3C6410等闆子,找到寄存器很簡單,隻需要翻遍手冊,然後使用核心提供的ioremap将硬體位址映射到核心的虛拟位址上,即可達到通過控制虛拟位址達到控制硬體位址的效果。

但是,在A20上,控制方法已經改成了script_parser_fetch()等等API操作方法,這表示描述方法有所不同,但是具體思路仍然相同。要注意的是,也可以使用以前的控制方法。

在《樹莓派3主要晶片手冊V1.0(A20datasheet).pdf》(點選連結)文檔的P19頁上,

A20将通用的GPIO當成了PIO,我截取出PIO的位址範圍,如下圖所示:

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

PIO位址範圍

再跳到P240,找到1.19 Port Controller這一章,我們隻需要關注的是Port E(PE):12 input/output port。

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

GPIO引腳

在這一章裡,說明了一些知識點:

1.上面列的GPIO引腳中,除了Port S做位DRAM來用,其他的沒有複用的功能的,都可以用作普通IO口。

2.A20上有32個外部PIO中斷。

由于是初次使用該款晶片,而GPIO是最基本的功能,是以,還是繼續貼出P247上的關于GPIO的配置寄存器内容,友善後面的查找:

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

GPIO配置寄存器

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

GPIO資料寄存器等等

2.2 配置寄存器

我在MarsBoard-A20-Linux-SDK-V1.2源碼目錄下,找到了sun4i-keypad.c檔案,在sw_keypad_init()中找到了API函數script_parser_fetch(),根據這個函數,我在gpio-sunxi.c中找到了操作gpio的許多API函數,例如sunxi_gpio_to_irq(),sunxi_gpio_set()等等。

最終,将所有的點,可以挂接到一個結構體上:struct gpio_chip。

到這裡,對于GPIO的内容,就到頭了,分析再多,那就變成了一篇驅動程式分析的文章了。

在gpio-sunxi.c的sunxi_gpio_probe()函數裡,通過注釋,知道該函數中有在找script.bin檔案。

2.2.1 找到fex2bin工具,sys_config.fex,script.bin的目錄

先找到fex2bin工具,sys_config.fex,script.bin在哪個目錄下:

[email protected]:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2$ ls tools/sunxi-tools/

adb-devprobe.sh fel-copy.c jtag-loop.c nand-part-main.c script_fex.c

bin fel-gpio jtag-loop.lds nand-part-main.o script_fex.h

bin2fex fel-pio.c jtag-loop.S phoenix_info.c script.h

boot_head.lds fel-pio.lds Makefile pio script+lcd.bin

boot_head.S fel-sdboot.c nand-common.h pio.c script_lcd.fex

bootinfo fel-sdboot.lds nand-part README script_uboot.c

bootinfo.c fex2bin nand-part-a10.h script.bin script_uboot.h

common.h fexc nand-part-a10.o script_bin.c sys_config.fex

COPYING fexc.c nand-part-a20.h script_bin.h usb-boot

fel fexc.h nand-part-a20.o script.c

fel.c include nand-part.c script.fex

[email protected]:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2$

2.2.2 找到開發闆上的script.bin檔案位置

在開發闆上使用如下指令可以找到script.bin檔案:

[email protected]:~# ls /mnt/

[email protected]:~# mount /dev/nanda /mnt/

[email protected]:~# ls /mnt/

boot.axf drv_de.drv font32.sft os_show sprite.axf

boot.ini drv_hdmi.drv linux prvt.axf uEnv.txt

boot_signature.axf font24.sft magic.bin script.bin uImage

[email protected]:~#

2.2.3 修改sys_config.fex檔案

從sys_config.fex檔案中,可以看到,裡面描述的都是io口的功能。

我把裡面最基本的功能描述,摘錄如下:

;A20 PAD application

;-------------------------------------------------------------------------------

; 說明:

; 1. 腳本中的字元串區分大小寫,使用者可以修改"="後面的數值,但是不要修改前面的字元串

; 2. 新增主鍵和子鍵的名稱必須控制在32個字元以内,不包括32個

; 3. 是以的注釋以“;”開始,單獨占據一行

; 4. 注釋不可和配置項同行,例如:主鍵和子健後面不能添加任何形式的注釋

;

; gpio的描述形式:Port:端口+組内序号

; 例如:port:PA0<0>

;-------------------------------------------------------------------------------

現在在硬體上,有PE4~PE11這些管腳需要設定。

用什麼參數怎麼去設定一個IO口?

要去尋求這個問題的答案,核心肯定會有相應的依據,我在script.h中找到了結構體:

struct sunxi_property_gpio_value。

摘錄如下:

struct sunxi_property_gpio_value {

u32 port;

u32 port_num;

s32 mul_sel;

s32 pull;

s32 drv_level;

s32 data;

};

每一項含義清楚了之後,我們就可以一個個的去設定了,那麼,還是要回到手冊:《樹莓派3主要晶片手冊V1.0(A20datasheet).pdf》上的P249。

根據手冊上的說明:

mul_sel:

0--輸入

1--輸出

2~7--複用功能

pull:

0--上拉/下拉禁止

1--上拉使能

2--下拉使能

drv_level:

0--level 0

1--level 1

2--level 2

3--level 3

data:

0--輸出電平為0

1--輸出電平為1

可以在這個網站上找到較長的描述:連結

現在,可以在sys_config.fex檔案中添加PE4~PE11的資訊:

[led_test_para]

led_test_enable = 1

led1 = port:PE4<1><1>

led2 = port:PE5<1><1>

led3 = port:PE6<1><1>

led4 = port:PE7<1><1>

led5 = port:PE8<1><1>

led6 = port:PE9<1><1>

led7 = port:PE10<1><1>

led8 = port:PE11<1><1>

修改完成後,需要将該檔案更新為script.bin,操作方法如下:

先看怎麼用fex2bin這個工具:

[email protected]:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2/tools/sunxi-tools$ ./fex2bin -v

./fex2bin: from fex: to bin:

之後,我們先備份源檔案,然後再生成自己的script.bin檔案。

[email protected]:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2/tools/sunxi-tools$ cp script.bin script_bak.bin

[email protected]:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2/tools/sunxi-tools$ ./fex2bin sys_config.fex script.bin

最後,将A20開發闆上的script.bin替換掉。

2.2.4 擷取script.bin的參數

找到\marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\leds\leds-sunxi.c檔案,這是一個很好的控制led燈的例子程式,可以參考它來做下面的工作。

我直接粘貼出來整個簡單的gpio.c程式檔案:

#include "linux/init.h"

#include

#include

#include

#include

script_gpio_set_t info;

unsigned gpio_handler;

static int __init led_init(void)

{

int err = 0;

int led_test_enabled = 0;

int ret = 0;

err = script_parser_fetch("led_test_para", "led_test_enable", &led_test_enabled,

sizeof(led_test_enabled)/sizeof(int));

if(!err){

printk("---script.bin led get ok,value:%d----\n",led_test_enabled);

}

else

{

printk("---script.bin led get false----\n");

return -1;

}

err = script_parser_fetch("led_test_para", "led1",

(int *)&info,

sizeof(script_gpio_set_t));

if (err) {

printk("----script.bin get io error----\r\n");

return -1;

}

gpio_handler = gpio_request_ex("led_test_para", "led1");

if (!gpio_handler) {

printk("----script.bin can't requst handler----\r\n");

return -1;

}

ret = gpio_set_one_pin_io_status(gpio_handler, 1,

"led1");

if (!ret)

ret = gpio_write_one_pin_value(gpio_handler,

1, "led1");

return 0;

}

static void __exit led_exit(void)

{

if (gpio_handler)

gpio_release(gpio_handler, 1);

printk("---dirver exit---\r\n");

}

module_init(led_init);

module_exit(led_exit);

MODULE_DESCRIPTION("Driver for empty");

MODULE_AUTHOR("wit_yuan");

MODULE_LICENSE("GPL");

Makefile檔案如下:

ifeq ($(KERNELRELEASE),)

KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi

PWD=$(shell pwd)

modules:

$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules

arm-linux-gnueabihf-gcc -o gpio gpio.c

modules_install:

$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install

clean:

rm -rf *.ko *.o .tmp_versions *.mod.c modules.order Module.symvers .*.cmd

else

obj-m:=gpio.o

endif

然後将gpio.ko檔案下載下傳到開發闆上,再開發闆上執行:

[email protected]:~# insmod gpio.ko

---script.bin led get ok,value:1----

[email protected]:~# rmmod gpio

---dirver exit---

[email protected]:~#

可以看到insmod程式的時候,L1燈亮起,而rmmod程式的時候,L1燈滅。

3 進階

3.1 添加應用層代碼

上面的驅動程式,隻是在核心驅動程式層面控制硬體。現在我們需要通過應用層程式來控制底層的硬體。

直接上代碼吧。

gpio.c代碼如下:

#include "linux/init.h"

#include

#include

#include

#include

#include

#include

#include

#include

#include

static script_gpio_set_t info;

static unsigned gpio_handler;

static struct class *leds_class;

static struct device *leds_device;

static unsigned int leds_major;

static int led_open(struct inode *inode, struct file *filp);

static ssize_t led_write (struct file *filp, const char __user *buf, size_t len, loff_t *off);

static int led_close(struct inode *inode, struct file *filp);

struct file_operations led_operations = {

.owner = THIS_MODULE,

.open = led_open,

.write = led_write,

.release = led_close,

};

static int led_open(struct inode *inode, struct file *filp)

{

int err = 0;

int led_test_enabled = 0;

int ret = 0;

err = script_parser_fetch("led_test_para", "led_test_enable", &led_test_enabled,

sizeof(led_test_enabled)/sizeof(int));

if(!err){

printk("---script.bin led get ok,value:%d----\n",led_test_enabled);

}

else

{

printk("---script.bin led get false----\n");

return -1;

}

err = script_parser_fetch("led_test_para", "led1",

(int *)&info,

sizeof(script_gpio_set_t));

if (err) {

printk("----script.bin get io error----\r\n");

return -1;

}

gpio_handler = gpio_request_ex("led_test_para", "led1");

if (!gpio_handler) {

printk("----script.bin can't requst handler----\r\n");

return -1;

}

return 0;

}

static ssize_t led_write (struct file *filp, const char __user *buf, size_t len, loff_t *off)

{

int val;

gpio_set_one_pin_io_status(gpio_handler, 1,

"led1");

copy_from_user(&val,buf,4);

printk("----led val:%d----\r\n",val);

switch(val){

case 0:

gpio_write_one_pin_value(gpio_handler,

0, "led1");

break;

case 1:

gpio_write_one_pin_value(gpio_handler,

1, "led1");

break;

default:

break;

}

return 0;

}

static int led_close(struct inode *inode, struct file *filp)

{

printk("----led close----\r\n");

return 0;

}

static int __init led_init(void)

{

leds_major = register_chrdev(0, "led_chrdev", &led_operations);

leds_class = class_create(THIS_MODULE, "leds_class");

if(!leds_class){

unregister_chrdev(leds_major, "led_chrdev");

printk("----leds_chrdev error----\r\n");

return -1;

}

leds_device = device_create(leds_class, NULL, MKDEV(leds_major,0),

NULL, "leds_device");

if(!leds_device){

class_destroy(leds_class);

unregister_chrdev(leds_major, "led_chrdev");

printk("----leds_device error----\r\n");

return -1;

}

printk("----leds init ok----\r\n");

return 0;

}

static void __exit led_exit(void)

{

if (gpio_handler)

gpio_release(gpio_handler, 1);

device_destroy(leds_class, MKDEV(leds_major, 0));

class_destroy(leds_class);

unregister_chrdev(leds_major, "led_chrdev");

printk("---driver exit---\r\n");

}

module_init(led_init);

module_exit(led_exit);

MODULE_DESCRIPTION("Driver for empty");

MODULE_AUTHOR("wit_yuan");

MODULE_LICENSE("GPL");

led_test.c代碼如下:

#include "stdio.h"

#include

#include

#include

int main(int argc,char *argv[])

{

int fd;

int val;

fd = open("/dev/leds_device",O_RDWR);

if(fd < 0){

printf("---open file error----\r\n");

return -1;

}

if(argc != 2){

printf("usage %s on|off\r\n",argv[0]);

return -1;

}

if(strncmp(argv[1],"on",2) == 0){

val = 1;

write(fd,&val,1);

}

else

{

val = 0;

write(fd,&val,1);

}

return 0;

}

Makefile代碼如下:

ifeq ($(KERNELRELEASE),)

KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi

PWD=$(shell pwd)

modules:

$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules

arm-linux-gnueabihf-gcc -o gpio gpio.c

modules_install:

$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install

clean:

rm -rf *.ko *.o .tmp_versions *.mod.c modules.order Module.symvers .*.cmd

else

obj-m:=gpio.o

endif

測試:

[email protected]:~# ./led_test on

---script.bin led get ok,value:1----

----led val:1----

---led close----

[email protected]:~# ./led_test off

---script.bin led get ok,value:1----

----led val:0----

----led close----

3.2 多LED燈控制

夜深了,睡覺,待續吧。。。

3.3 使用cdev注冊裝置

3.4 另外一種控制方式:

sys_config.fex下的GPIO内容資訊如下:

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

GPIO控制表

如果需要控制gpio_pin_2,則:

[email protected]:~# echo 2 > /sys/class/gpio/export

[email protected]:~#

[email protected]:~# ls /sys/class/gpio/

export gpio1_ph9 gpio2_pi15 gpiochip1 unexport

root@marsboard:~# echo out > /sys/class/gpio/gpio2_pi15/direction

[email protected]:~# echo 1 > /sys/class/gpio/gpio2_pi15/value

[email protected]:~# echo 0 > /sys/class/gpio/gpio2_pi15/value

通過這種方式,就可以控制LED燈的亮滅。

4 樹莓派A20的鍵盤複位驅動程式

該複位程式需要做到:複位引腳拉低至少20ms,再等400ms左右将引腳拉為高電平才可行。

4.1 添加配置資訊

在MarsBoard-A20-Linux-SDK-V1.1/tools/sunxi-tools下找到sys_config.fex檔案,然後添加内容:

[gpio_whb_para]

gpio_whb_used = 1

gpio_whb_num = 1

gpio_whb_pin_1 = port:PH6<1><1>

gpio_whb_name_1 = "keypad_reset"

如下圖所示:

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

添加配置資訊

接着:

$ ./fex2bin sys_config.fex script.bin

在A20上:

$ mount /dev/nanda /mnt

然後将script.bin檔案上傳到A20的/mnt目錄中。

4.2 修改Kconfig和Makefile内容

在linux-sunxi/drivers/misc/目錄下,修改Kconfig和Makefile内容。

先修改Kconfig檔案内容:

config GPOI_SUNXI_WHB

tristate "GPIO Support for sunxi platform (whb add)"

depends on (ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I)

help

This option enables support for gpio connected

lines on the Allwinner SOCs (sun4i/sun5i).

The gpios must be defined in [gpio_whb_para] section of sysconfig.fex

然後修改Makefile檔案内容:

obj-$(CONFIG_GPOI_SUNXI_WHB) += gpio_sunxi_whb.o

接着:

$ make menuconfig

可以看到如下圖所示内容:

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

選中後效果圖

驗證是否已經在核心中是選中狀态,可在linux-sunxi根目錄下檢視.config檔案,如下圖所示:

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

宏已經選中

4.3 編寫驅動程式

命名驅動程式名稱為:gpio_sunxi_whb.c,最終需要放到目錄:linux-sunxi/drivers/misc/下。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define GPIO_WHB_DEBUG

#ifdef GPIO_WHB_DEBUG

#define gpio_whb_debug(fmt, ...) printk(KERN_INFO "[GPIO_WHB][BUG][%d]"fmt, __LINE__, ##__VA_ARGS__)

#else

#define gpio_whb_debug(fmt, ...)

#endif

#define gpio_whb_error(fmt, ...) printk(KERN_INFO "[GPIO_WHB][ERR]"fmt"\n", ##__VA_ARGS__)

#define DRV_NAME "gpio_whb"

#define GPIO_WHB_IOCTL_GET 0

#define GPIO_WHB_IOCTL_SET 1

struct sunxi_gpio_whb_data {

unsigned gpio_handler;

script_gpio_set_t info;

char pin_name[16];

char gpio_name[64];

};

struct gpio_whb_ioctl_parameter {

int gpio;

int val;

};

static int gpio_whb_num = 0;

static struct sunxi_gpio_whb_data *psunxi_gpios_whb = NULL;

static struct miscdevice gpio_whb_misc = {0};

static int sunxi_gpio_get_value(unsigned gpio)

{

int ret;

user_gpio_set_t gpio_info[1];

if (gpio >= gpio_whb_num)

return -1;

ret = gpio_get_one_pin_status(psunxi_gpios_whb[gpio].gpio_handler,

gpio_info, psunxi_gpios_whb[gpio].pin_name, 1);

return gpio_info->data;

}

static void sunxi_gpio_set_value(unsigned gpio, int value)

{

int ret ;

if (gpio >= gpio_whb_num)

return;

gpio_whb_debug("SET:pin_name:%s, value:%d", psunxi_gpios_whb[gpio].pin_name, value);

ret = gpio_write_one_pin_value(psunxi_gpios_whb[gpio].gpio_handler,

value, psunxi_gpios_whb[gpio].pin_name);

return;

}

static int sunxi_direction_output(unsigned gpio, int value)

{

int ret;

if (gpio >= gpio_whb_num)

return -1;

ret = gpio_set_one_pin_io_status(psunxi_gpios_whb[gpio].gpio_handler, 1,

psunxi_gpios_whb[gpio].pin_name);

if (!ret)

ret = gpio_write_one_pin_value(psunxi_gpios_whb[gpio].gpio_handler,

value, psunxi_gpios_whb[gpio].pin_name);

return ret;

}

static int sunxi_gpio_is_valid(unsigned gpio)

{

if (gpio >= gpio_whb_num)

return -1;

if (psunxi_gpios_whb[gpio].gpio_handler)

return 0;

return -1;

}

static int gpio_whb_open(struct inode *inode, struct file *file)

{

int i = 0;

//所有IO設定為輸出并置1

for (i = 0; i < gpio_whb_num; ++i) {

gpio_whb_debug("Open: Set GPIO %d is 1\n", i);

sunxi_gpio_set_value(i, 1);

}

return 0;

}

static int gpio_whb_close(struct inode *inode, struct file *file)

{

int i = 0;

//所有IO置1(因為鍵盤複位時是低電平,是以關閉時需要置1)

for (i = 0; i < gpio_whb_num; ++i) {

gpio_whb_debug("Close: Set GPIO %d is 1\n", i);

sunxi_gpio_set_value(i, 1);

}

return 0;

}

static long gpio_whb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

struct gpio_whb_ioctl_parameter para = {0};

copy_from_user(&para, (void *)arg, sizeof(struct gpio_whb_ioctl_parameter));

gpio_whb_debug("Cmd: %d, GPIO: %d\n", cmd, para.gpio);

if (sunxi_gpio_is_valid(para.gpio) != 0) {

gpio_whb_error("gpio invalid");

return -1;

}

switch (cmd) {

case GPIO_WHB_IOCTL_GET:

para.val = sunxi_gpio_get_value(para.gpio);

copy_to_user((void *)arg, &para, sizeof(struct gpio_whb_ioctl_parameter));

break;

case GPIO_WHB_IOCTL_SET:

sunxi_gpio_set_value(para.gpio, para.val);

break;

default:

return -1;

}

return 0;

}

static const struct file_operations gpio_whb_fops = {

.owner = THIS_MODULE,

.open = gpio_whb_open,

.release = gpio_whb_close,

.unlocked_ioctl = gpio_whb_ioctl,

// .read = gpio_whb_read,

// .write = gpio_whb_write,

};

static int __init sunxi_gpio_whb_init(void)

{

int i, err;

int gpio_whb_used = 0;

struct sunxi_gpio_whb_data *gpio_whb_i;

char key[20];

struct miscdevice *misc = NULL;

gpio_whb_debug("gpio_whb driver init\n");

err = script_parser_fetch("gpio_whb_para", "gpio_whb_used", &gpio_whb_used, sizeof(gpio_whb_used)/sizeof(int));

if (err) {

gpio_whb_error("sunxi gpio_whb can't find script data '[gpio_whb_para]' 'gpio_whb_used'\n");

return err;

}

if (!gpio_whb_used) {

gpio_whb_error("gpio_whb_used is false. Skip leds initialization\n");

err = 0;

return err;

}

err = script_parser_fetch("gpio_whb_para", "gpio_whb_num", &gpio_whb_num, sizeof(gpio_whb_num)/sizeof(int));

if (err) {

gpio_whb_error("script_parser_fetch '[gpio_whb_para]' 'gpio_whb_num' error\n");

return err;

}

if (!gpio_whb_num) {

gpio_whb_error("gpio_whb_num is none. Skip leds initialization\n");

err = 0;

return err;

}

gpio_whb_debug("gpio_whb_num = %d", gpio_whb_num);

psunxi_gpios_whb = kzalloc(sizeof(struct sunxi_gpio_whb_data) * gpio_whb_num, GFP_KERNEL);

if (!psunxi_gpios_whb) {

gpio_whb_error("%s kzalloc failed\n", __func__);

err = -ENOMEM;

goto exit;

}

gpio_whb_i = psunxi_gpios_whb;

for (i = 0; i < gpio_whb_num; i++) {

sprintf(gpio_whb_i->pin_name, "gpio_whb_pin_%d", i+1);

sprintf(key, "gpio_whb_name_%d", i + 1);

err = script_parser_fetch("gpio_whb_para", key,

(int *)gpio_whb_i->gpio_name,

sizeof(gpio_whb_i->gpio_name)/sizeof(int));

if (err) {

gpio_whb_error("script_parser_fetch '[gpio_whb_para]' '%s' error\n", key);

goto exit;

}

sprintf(key, "gpio_whb_pin_%d", i + 1);

err = script_parser_fetch("gpio_whb_para", key,

(int *)&gpio_whb_i->info,

sizeof(script_gpio_set_t));

if (err) {

gpio_whb_error("script_parser_fetch '[gpio_whb_para]' '%s' error\n", key);

break;

}

gpio_whb_debug("gpio_name:%s, port:%d,port_num:%d\n", gpio_whb_i->info.gpio_name, gpio_whb_i->info.port, gpio_whb_i->info.port_num);

gpio_whb_i->gpio_handler = gpio_request_ex("gpio_whb_para", key);

if (!gpio_whb_i->gpio_handler) {

gpio_whb_error("can't request '[gpio_whb_para]' '%s', already used ?", key);

break;

}

gpio_whb_i++;

}

misc = &gpio_whb_misc;

misc->minor = MISC_DYNAMIC_MINOR;

misc->name = DRV_NAME;

misc->fops = &gpio_whb_fops;

err = misc_register(misc);

if (err) {

gpio_whb_error("Unable to register a misc device\n");

goto exit;

}

gpio_whb_debug("Register a misc device Ok\n");

return 0;

exit:

if (err != -ENOMEM) {

for (i = 0; i < gpio_whb_num; i++) {

if (psunxi_gpios_whb[i].gpio_handler)

gpio_release(psunxi_gpios_whb[i].gpio_handler, 1);

}

kfree(psunxi_gpios_whb);

return err;

}

return err;

}

static void __exit sunxi_gpio_whb_exit(void)

{

int i = 0;

struct miscdevice *misc = NULL;

misc = &gpio_whb_misc;

misc_deregister(misc);

for (i = 0; i < gpio_whb_num; i++) {

if (psunxi_gpios_whb[i].gpio_handler)

gpio_release(psunxi_gpios_whb[i].gpio_handler, 1);

}

kfree(psunxi_gpios_whb);

return;

}

module_init(sunxi_gpio_whb_init);

module_exit(sunxi_gpio_whb_exit);

MODULE_ALIAS("platform:gpio_whb-sunxi");

MODULE_DESCRIPTION("sunxi gpio(whb) driver");

MODULE_AUTHOR("wang hb ");

MODULE_LICENSE("GPL");

4.4 驗證驅動加載是否成功

在樹莓派A20下,使用如下指令:

[email protected]:~# ll /dev/gpio_whb

crw------- 1 root root 10, 59 Jan 1 00:46 /dev/gpio_whb

[email protected]:~#

這說明驅動确實加載進去了。

5 SPI使用GPIO切換選擇

将代碼命名為spi-gpio.c,如果在編入核心,可以存放在如上所示,也就是linux-sunxi/drivers/misc/spi-gpio.c,如下所示:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define GPIO_DEBUG

#ifdef GPIO_DEBUG

#define gpio_debug(fmt, ...) printk(KERN_INFO "[%s][BUG][%d]"fmt, __FILE__,__LINE__, ##__VA_ARGS__)

#else

#define gpio_debug(fmt, ...)

#endif

#define gpio_error(fmt, ...) printk(KERN_INFO "[%s][ERR]"fmt"\n", __FILE__,##__VA_ARGS__)

#define DRV_NAME "A20_GPIO"

#define GPIO_IOCTL_GET 0

#define GPIO_IOCTL_SET 1

struct sunxi_gpio_data {

unsigned gpio_handler;

script_gpio_set_t info;

char pin_name[16];

char gpio_name[64];

};

struct gpio_ioctl_parameter {

int gpio;

int val;

};

static int gpio_num = 0;

static struct sunxi_gpio_data *psunxi_gpio = NULL;

static struct miscdevice gpio_misc = {0};

static void sunxi_gpio_set_value(unsigned gpio, int value)

{

int ret ;

if (gpio >= gpio_num)

return;

gpio_debug("SET:pin_name:%s, value:%d", psunxi_gpio[gpio].pin_name, value);

ret = gpio_write_one_pin_value(psunxi_gpio[gpio].gpio_handler,

value, psunxi_gpio[gpio].pin_name);

return;

}

static int sunxi_direction_output(unsigned gpio, int value)

{

int ret;

if (gpio >= gpio_num)

return -1;

ret = gpio_set_one_pin_io_status(psunxi_gpio[gpio].gpio_handler, 1,

psunxi_gpio[gpio].pin_name);

if (!ret)

ret = gpio_write_one_pin_value(psunxi_gpio[gpio].gpio_handler,

value, psunxi_gpio[gpio].pin_name);

return ret;

}

static int gpio_open(struct inode *inode, struct file *file)

{

int i = 0;

for (i = 0; i < gpio_num; ++i) {

gpio_debug("Open: Set GPIO %d is 1\n", i);

sunxi_gpio_set_value(i, 1);

}

return 0;

}

static ssize_t gpio_write (struct file *filep, const char __user *buf, size_t len, loff_t *off)

{

unsigned char value;

int ret;

if(len != 1){

printk("---len error-------\r\n");

return -1;

}

ret = copy_from_user(&value,buf,len);

switch(value){

case 0:

sunxi_direction_output(0,0);

sunxi_direction_output(1,0);

sunxi_direction_output(2,0);

break;

case 1:

sunxi_direction_output(0,1);

sunxi_direction_output(1,0);

sunxi_direction_output(2,0);

break;

case 2:

sunxi_direction_output(0,0);

sunxi_direction_output(1,1);

sunxi_direction_output(2,0);

break;

case 3:

sunxi_direction_output(0,1);

sunxi_direction_output(1,1);

sunxi_direction_output(2,0);

break;

case 4:

sunxi_direction_output(0,0);

sunxi_direction_output(1,0);

sunxi_direction_output(2,1);

break;

case 5:

sunxi_direction_output(0,1);

sunxi_direction_output(1,0);

sunxi_direction_output(2,1);

break;

case 6:

sunxi_direction_output(0,0);

sunxi_direction_output(1,1);

sunxi_direction_output(2,1);

break;

case 7:

sunxi_direction_output(0,1);

sunxi_direction_output(1,1);

sunxi_direction_output(2,1);

break;

default:

printk("%s,value error\r\n",__FILE__);

break;

}

return len;

}

static int gpio_close(struct inode *inode, struct file *file)

{

int i = 0;

for (i = 0; i < gpio_num; ++i) {

gpio_debug("Close: Set GPIO %d is 1\n", i);

sunxi_gpio_set_value(i, 1);

}

return 0;

}

static const struct file_operations A20_gpio_fops = {

.owner = THIS_MODULE,

.open = gpio_open,

.release = gpio_close,

.write = gpio_write,

};

static int __init sunxi_gpio_init(void)

{

int i, err;

int gpio_used = 0;

struct sunxi_gpio_data *gpio_i;

struct miscdevice *misc = NULL;

gpio_debug("gpio driver init\n");

err = script_parser_fetch("A20_SPI_GPIO_para", "gpio_used", &gpio_used, sizeof(gpio_used)/sizeof(int));

if (err) {

gpio_error("sunxi gpio can't find script data '[gpio_para]' 'gpio_used'\n");

return err;

}

if (!gpio_used) {

gpio_error("gpio_used is false. Skip leds initialization\n");

err = 0;

return err;

}

err = script_parser_fetch("A20_SPI_GPIO_para", "gpio_num", &gpio_num, sizeof(gpio_num)/sizeof(int));

if (err) {

gpio_error("script_parser_fetch '[gpio_whb_para]' 'gpio_whb_num' error\n");

return err;

}

if (3!=gpio_num) {

gpio_error("gpio_num is not 3. Skip gpio initialization\n");

err = 0;

return err;

}

gpio_debug("gpio_num = %d", gpio_num);

psunxi_gpio = kzalloc(sizeof(struct sunxi_gpio_data) * gpio_num, GFP_KERNEL);

if (!psunxi_gpio) {

gpio_error("%s kzalloc failed\n", __func__);

err = -ENOMEM;

goto exit;

}

gpio_i = psunxi_gpio;

for (i = 0; i < gpio_num; i++) {

sprintf(gpio_i->pin_name, "A20_GPIO%d", i+1);

err = script_parser_fetch("A20_SPI_GPIO_para", gpio_i->pin_name,

(int *)gpio_i->gpio_name,

sizeof(gpio_i->gpio_name)/sizeof(int));

if (err) {

gpio_error("script_parser_fetch '[A20_SPI_GPIO_para]' '%s' error\n", gpio_i->pin_name);

goto exit;

}

gpio_debug("gpio_name:%s, port:%d,port_num:%d\n", gpio_i->info.gpio_name, gpio_i->info.port, gpio_i->info.port_num);

gpio_i->gpio_handler = gpio_request_ex("A20_SPI_GPIO_para", gpio_i->pin_name);

if (!gpio_i->gpio_handler) {

gpio_error("can't request '[gpio_whb_para]' '%s', already used", gpio_i->pin_name);

break;

}

gpio_i++;

}

for (i = 0; i < gpio_num; ++i) {

sunxi_gpio_set_value(i, 1);

}

misc = &gpio_misc;

misc->minor = MISC_DYNAMIC_MINOR;

misc->name = DRV_NAME;

misc->fops = &A20_gpio_fops;

err = misc_register(misc);

if (err) {

gpio_error("Unable to register a misc device\n");

goto exit;

}

gpio_debug("Register a misc device Ok\n");

return 0;

exit:

if (err != -ENOMEM) {

for (i = 0; i < gpio_num; i++) {

if (psunxi_gpio[i].gpio_handler)

gpio_release(psunxi_gpio[i].gpio_handler, 1);

}

kfree(psunxi_gpio);

return err;

}

return err;

}

static void __exit sunxi_gpio_exit(void)

{

int i = 0;

struct miscdevice *misc = NULL;

misc = &gpio_misc;

misc_deregister(misc);

for (i = 0; i < gpio_num; i++) {

if (psunxi_gpio[i].gpio_handler)

gpio_release(psunxi_gpio[i].gpio_handler, 1);

}

kfree(psunxi_gpio);

return;

}

module_init(sunxi_gpio_init);

module_exit(sunxi_gpio_exit);

MODULE_ALIAS("platform:spi_fpga_dsp_gpio_wityuan-sunxi");

MODULE_DESCRIPTION("sunxi spi_fpga_gpio(wityuan) driver");

MODULE_AUTHOR("wityuan ");

MODULE_LICENSE("GPL");

Makefile檔案為:

ifeq ($(KERNELRELEASE),)

KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi

PWD=$(shell pwd)

modules:

$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules

arm-linux-gnueabihf-gcc -o spi-gpio spi-gpio.c

modules_install:

$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install

clean:

rm -rf *.ko *.o .tmp_versions *.mod.c modules.order Module.symvers .*.cmd

else

obj-m:=spi-gpio.o

endif

測試程式命名為spi-gpio_test.c,内容如下:

#include "stdio.h"

#include

#include

#include

int main(int argc,char *argv[])

{

int fd;

int val;

unsigned char write_val;

fd = open("/dev/A20_GPIO",O_RDWR);

if(fd < 0){

printf("---open file error----\r\n");

return -1;

}

if(argc != 2){

printf("usage %s (0|1|2|3|4|5|6|7|)\r\n",argv[0]);

return -1;

}

write_val = (unsigned char)atoi(argv[1]);

if((write_val>7) | (write_val<0)){

printf("---value is valid---\r\n");

return -1;

}

write(fd,&write_val,1);

while(1);

return 0;

}

即可通過如下方式進行測試:

$ ./spi-gpio_test 0

$ ./spi-gpio_test 1

$ ./spi-gpio_test 2

$ ./spi-gpio_test 3

$ ./spi-gpio_test 4

$ ./spi-gpio_test 5

$ ./spi-gpio_test 6

$ ./spi-gpio_test 7

最終,我們往核心添加時,要将spi-gpio.c放到linux-sunxi/drivers/misc下。

并且在Kconfig和Makefile中添加内容:

Kconfig:

config SPI_GPIO_YUAN

tristate "(wit_yuan add)SPI GPIO SELECT Support for sunxi platform"

depends on (ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I)

help

This option enables support for gpio connected

lines on the Allwinner SOCs (sun4i/sun5i).

The gpios must be defined in [A20_SPI_GPIO_para] section of sysconfig.fex

Makefile:

obj-$(CONFIG_SPI_GPIO_YUAN) += spi-gpio.o

sys_config.fex配置為:

[A20_SPI_GPIO_para]

gpio_used = 1

gpio_num = 3

A20_GPIO1 = port:PH0<1><1>

A20_GPIO2 = port:PH1<1><1>

A20_GPIO3 = port:PH1<1><1>

最終,查找是否有裝置:

$ ls /dev/A20_GPIO

6 AD,RCA,MIC等使用IO脈沖切換

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

AD引腳配置

[AD_Switch_para]

AD_Switch_used = 1

gpio_num = 2

sw1 = port:PH0<1><1>

sw2 = port:PH1<1><1>

接着,在linux-sunxi/drivers/misc/目錄下,修改Kconfig和Makefile内容。

先修改Kconfig檔案内容:

config A20_sw

tristate "A20_sw for sunxi platform (wityuan add)"

depends on (ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I)

help

This option enables support for gpio connected

lines on the Allwinner SOCs (sun4i/sun5i).

The gpios must be defined in section of sysconfig.fex

然後修改Makefile檔案内容:

obj-$(CONFIG_A20_sw) += A20_sw.o

接着:

$ make menuconfig

找到該選項并且選中:

a10 android應用控制gpio控制led燈開發執行個體1014,三 . 樹莓派A20 GPIO驅動程式編寫

選中A20_sw

最後,我們需要将驅動程式,名稱為:gpio_sunxi_whb.c,放到目錄:linux-sunxi/drivers/misc/下。

源碼如下:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define GPIO_DEBUG

#ifdef GPIO_DEBUG

#define gpio_debug(fmt, ...) printk(KERN_INFO "[%s][BUG][%d]"fmt, __FILE__,__LINE__, ##__VA_ARGS__)

#else

#define gpio_debug(fmt, ...)

#endif

#define gpio_error(fmt, ...) printk(KERN_INFO "[%s][ERR]"fmt"\n", __FILE__,##__VA_ARGS__)

#define DRV_NAME "A20_SW"

struct sunxi_gpio_data {

unsigned gpio_handler;

script_gpio_set_t info;

char pin_name[16];

char gpio_name[64];

};

typedef struct{

int id;

int value;

}SW_T;

static int gpio_num = 0;

static struct sunxi_gpio_data *psunxi_gpio = NULL;

static struct miscdevice gpio_misc = {0};

static void sunxi_gpio_set_value(unsigned gpio, int value)

{

int ret ;

if (gpio >= gpio_num)

return;

gpio_debug("SET:pin_name:%s, value:%d", psunxi_gpio[gpio].pin_name, value);

ret = gpio_write_one_pin_value(psunxi_gpio[gpio].gpio_handler,

value, psunxi_gpio[gpio].pin_name);

return;

}

static int gpio_open(struct inode *inode, struct file *file)

{

int i = 0;

for (i = 0; i < gpio_num; ++i) {

gpio_debug("Open: Set GPIO %d is 1\n", i);

sunxi_gpio_set_value(i, 0);

}

return 0;

}

static ssize_t gpio_write (struct file *filep, const char __user *buf, size_t len, loff_t *off)

{

SW_T sw_t;

int ret;

if(len != (sizeof(sw_t))){

printk("---len error-------\r\n");

return -1;

}

ret = copy_from_user(&sw_t,buf,len);

printk("---sw_t.id:%d, sw_t.value:%d-------\r\n",sw_t.id,sw_t.value);

if(sw_t.id == 0){

sunxi_gpio_set_value(0, sw_t.value);

sunxi_gpio_set_value(1, 0);

}

else {

sunxi_gpio_set_value(0, 0);

sunxi_gpio_set_value(1, sw_t.value);

}

return len;

}

static int gpio_close(struct inode *inode, struct file *file)

{

#if 0

int i = 0;

for (i = 0; i < gpio_num; ++i) {

gpio_debug("Close: Set GPIO %d is 1\n", i);

sunxi_gpio_set_value(i, 0);

}

#endif

return 0;

}

static const struct file_operations A20_gpio_fops = {

.owner = THIS_MODULE,

.open = gpio_open,

.release = gpio_close,

.write = gpio_write,

};

static int __init sunxi_gpio_init(void)

{

int i, err;

int gpio_used = 0;

struct sunxi_gpio_data *gpio_i;

struct miscdevice *misc = NULL;

gpio_debug("gpio driver init\n");

err = script_parser_fetch("AD_Switch_para", "AD_Switch_used", &gpio_used, sizeof(gpio_used)/sizeof(int));

if (err) {

gpio_error("sunxi gpio can't find script data '[gpio_para]' 'gpio_used'\n");

return err;

}

if (!gpio_used) {

gpio_error("gpio_used is false. Skip leds initialization\n");

err = 0;

return err;

}

err = script_parser_fetch("AD_Switch_para", "gpio_num", &gpio_num, sizeof(gpio_num)/sizeof(int));

if (err) {

gpio_error("script_parser_fetch '[AD_Switch_para]' error\n");

return err;

}

if (2!=gpio_num) {

gpio_error("gpio_num is not 2. Skip gpio initialization\n");

err = 0;

return err;

}

gpio_debug("gpio_num = %d", gpio_num);

psunxi_gpio = kzalloc(sizeof(struct sunxi_gpio_data) * gpio_num, GFP_KERNEL);

if (!psunxi_gpio) {

gpio_error("%s kzalloc failed\n", __func__);

err = -ENOMEM;

goto exit;

}

gpio_i = psunxi_gpio;

for (i = 0; i < gpio_num; i++) {

sprintf(gpio_i->pin_name, "sw%d", i+1);

err = script_parser_fetch("AD_Switch_para", gpio_i->pin_name,

(int *)gpio_i->gpio_name,

sizeof(gpio_i->gpio_name)/sizeof(int));

if (err) {

gpio_error("script_parser_fetch '[AD_Switch_para]' '%s' error\n", gpio_i->pin_name);

goto exit;

}

gpio_debug("gpio_name:%s, port:%d,port_num:%d\n", gpio_i->info.gpio_name, gpio_i->info.port, gpio_i->info.port_num);

gpio_i->gpio_handler = gpio_request_ex("AD_Switch_para", gpio_i->pin_name);

if (!gpio_i->gpio_handler) {

gpio_error("can't request '[AD_Switch_para]' '%s', already used", gpio_i->pin_name);

break;

}

gpio_i++;

}

for (i = 0; i < gpio_num; ++i) {

sunxi_gpio_set_value(i, 0);

}

misc = &gpio_misc;

misc->minor = MISC_DYNAMIC_MINOR;

misc->name = DRV_NAME;

misc->fops = &A20_gpio_fops;

err = misc_register(misc);

if (err) {

gpio_error("Unable to register a misc device\n");

goto exit;

}

gpio_debug("Register a misc device Ok\n");

return 0;

exit:

if (err != -ENOMEM) {

for (i = 0; i < gpio_num; i++) {

if (psunxi_gpio[i].gpio_handler)

gpio_release(psunxi_gpio[i].gpio_handler, 1);

}

kfree(psunxi_gpio);

return err;

}

return err;

}

static void __exit sunxi_gpio_exit(void)

{

int i = 0;

struct miscdevice *misc = NULL;

misc = &gpio_misc;

misc_deregister(misc);

for (i = 0; i < gpio_num; i++) {

if (psunxi_gpio[i].gpio_handler)

gpio_release(psunxi_gpio[i].gpio_handler, 1);

}

kfree(psunxi_gpio);

return;

}

module_init(sunxi_gpio_init);

module_exit(sunxi_gpio_exit);

MODULE_ALIAS("platform:sw0,sw1 select");

MODULE_DESCRIPTION("sw0,sw1(wityuan) driver");

MODULE_AUTHOR("wityuan ");

MODULE_LICENSE("GPL");

測試程式sw_test.c為:

#include "stdio.h"

#include

#include

#include

typedef struct{

int id;

int value;

}SW_T;

int main(int argc,char *argv[])

{

int fd;

int val;

SW_T sw_t;

fd = open("/dev/A20_SW",O_RDWR);

if(fd < 0){

printf("---open file error----\r\n");

return -1;

}

if(argc != 2){

printf("usage %s on|off\r\n",argv[0]);

return -1;

}

val = atoi(argv[1]);

sw_t.id = val;

sw_t.value = 1;

write(fd,&sw_t,sizeof(SW_T));

//sleep(1);

usleep(200000);

sw_t.id = val;

sw_t.value = 0;

write(fd,&sw_t,sizeof(SW_T));

usleep(200000);

while(1);

return 0;

}

7 參考檔案

1.\marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\leds\leds-sunxi.c

2.\marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\gpio\gpio-sunxi.c