调试完pcDuino的led裸板程序,再调pcDuino基于linux内核的gpio驱动开发,以前的问题迎刃而解。本文是在pcDuino的linux移植四简单驱动开发的基础上,做GPIO驱动开发。然后写一个应用测试程序,通过敲命令控制pcduino的TX led亮、灭。同时如果你GPIO4、GPIO5接个LED,也会跟着亮、灭。
开发环境:
系统:ubuntu 10.04.4
单板:pcDuino
编译器:arm-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
目标:敲命令控制pcDuino上的TX_LED亮、灭
一、硬件介绍
仔细看pcDuino上的原理图和pcDuino的手册,发现二者不是完全对应的,还是以原理图为准。根据原理图知道TX_LED是接到PH15上,可以当做普通IO口用,不需要连跳线
二、编写GPIO驱动代码
主要包括2部分,驱动代码first_drv.c和应用测试程序firstdrvtest.c以及Makefile。
驱动代码first_drv.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <mach/gpio.h> #include <mach/hardware.h> #include <linux/device.h> static struct class *firstdrv_class; static struct class_device *firstdrv_class_dev; volatile unsigned long *gphcfg1;//0x100 ph9 [6:4]001 volatile unsigned long *gphdat; //0x10c static int first_drv_open(struct inode *inode, struct file *file) { printk("first_drv_open\n"); /* 配置GPh9为输出引脚 gpio4*/ *gphcfg1 |= ((0x1<<4)|(0x1<<8)|(0X1<<28)); return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) { int val; printk("first_drv_write\n"); copy_from_user(&val, buf, count); // copy_to_user(); if (val == 1) { // 点灯 *gphdat &= ~((0X1<<9)|(0X1<<10)|(1<<15)); } else { // 灭灯 *gphdat |= ((0X1<<9)|(0X1<<10)|(1<<15)); } return 0; } static struct file_operations first_drv_fops ={ .owner = THIS_MODULE, .open = first_drv_open, .write = first_drv_write, }; int major; static int first_drv_init(void) { major = register_chrdev(0, "first_drv", &first_drv_fops); firstdrv_class = class_create(THIS_MODULE, "firstdrv"); firstdrv_class_dev = device_create(firstdrv_class,NULL,MKDEV(major, 0), NULL, "xyz"); gphcfg1 = (volatile unsigned long *)ioremap(0x01c20900, 16); gphdat = (volatile unsigned long *)ioremap(0x01c2090c, 16);//gphcfg1 + 3; return 0; } static void first_drv_exit(void) { unregister_chrdev(major,"first_drv"); device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); iounmap(gphcfg1);
iounmap(gphdat); } module_init(first_drv_init); module_exit(first_drv_exit); MODULE_LICENSE("GPL");
文件Makefile:
KERN_DIR = /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0 all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += first_drv.o
应用测试程序firstdrvtest.c:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* firstdrvtest on * firstdrvtest off */ int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/xyz", O_RDWR); if (fd < 0) { printf("can't open!\n"); } if (argc != 2) { printf("Usage :\n"); printf("%s <on|off>\n", argv[0]); return 0; } if (strcmp(argv[1], "on") == 0) { val = 1; } else { val = 0; } write(fd, &val, 4); return 0; }
三、编译测试
1.编译驱动first_drv.c
change@change:~/Si/A10/2_led$ cd ../pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv$ ls
first_drv.c first_drv.mod.c first_drv.o firstdrvtest.c modules.order
first_drv.ko first_drv.mod.o firstdrvtest Makefile Module.symvers
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv$ make clean
make -C /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0 M=`pwd` modules clean
make[1]: Entering directory `/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0'
LD /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/built-in.o
CC [M] /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.o
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c: In function 'first_drv_write':
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c:30: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c: In function 'first_drv_init':
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c:57: warning: assignment from incompatible pointer type
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c: In function 'first_drv_exit':
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c:66: warning: passing argument 1 of 'device_unregister' from incompatible pointer type
include/linux/device.h:692: note: expected 'struct device *' but argument is of type 'struct class_device *'
Building modules, stage 2.
MODPOST 1 modules
CC /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.mod.o
LD [M] /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.ko
CLEAN /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/.tmp_versions
CLEAN /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/Module.symvers
make[1]: Leaving directory `/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0'
rm -rf modules.order
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv$ make
make -C /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0 M=`pwd` modules
make[1]: Entering directory `/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0'
CC [M] /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.o
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c: In function 'first_drv_write':
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c:30: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c: In function 'first_drv_init':
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c:57: warning: assignment from incompatible pointer type
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c: In function 'first_drv_exit':
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.c:66: warning: passing argument 1 of 'device_unregister' from incompatible pointer type
include/linux/device.h:692: note: expected 'struct device *' but argument is of type 'struct class_device *'
Building modules, stage 2.
MODPOST 1 modules
CC /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.mod.o
LD [M] /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv/first_drv.ko
make[1]: Leaving directory `/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0'
2.编译应用测试程序firstdrvtest.c
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv$ /home/change/tools/arm-2009q3/bin/arm-none-linux-gnueabi-gcc -o firstdrvtest firstdrvtest.c
其中/home/change/tools/arm-2009q3/bin/arm-none-linux-gnueabi-gcc是我的交叉编译路径,根据自己情况修改。
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv$ lsfirst_drv.c first_drv.mod.c first_drv.o firstdrvtest.c modules.order
first_drv.ko first_drv.mod.o firstdrvtest Makefile Module.symvers
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv$ cp first_drv.ko firstdrvtest /home/change/work/rootfs_dir/fs_mini/home/linux-3.0.62/pcduino/
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/led_drv$
其中/home/change/work/rootfs_dir/fs_mini是我的NFS共享目录,详细配置见我的ubuntu 开发环境配置。程序比较简单,就不分析了,有问题直接留言吧,开始测试吧。
3.测试
启动前面pcDuino的linux移植一、二、三、四搭建的驱动开发平台,上电进入pcDuino启动控制台,串口输出如下
/ # ifconfig eth0 172.16.1.111
<4>wemac wemac.0: WARNING: no IRQ resource flags set.
[ 18.250000] wemac wemac.0: WARNING: no IRQ resource flags set.
<6>wemac wemac.0: eth0: link up, 100Mbps, full-duplex, lpa 0x45E1
[ 18.400000] wemac wemac.0: eth0: link up, 100Mbps, full-duplex, lpa 0x45E1
/ # ping 172.16.1<7>eth0: no IPv6 routers present
[ 28.860000] eth0: no IPv6 routers present
PING 172.16.1 (172.16.0.1): 56 data bytes
^C
--- 172.16.1 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
/ # ping 172.16.1.137
PING 172.16.1.137 (172.16.1.137): 56 data bytes
64 bytes from 172.16.1.137: seq=0 ttl=64 time=10.015 ms
64 bytes from 172.16.1.137: seq=1 ttl=64 time=1.013 ms
64 bytes from 172.16.1.137: seq=2 ttl=64 time=1.735 ms
64 bytes from 172.16.1.137: seq=3 ttl=64 time=0.814 ms
^C
--- 172.16.1.137 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.814/3.394/10.015 ms
/ # ls
bin etc lib mnt run sys usr
dev home linuxrc proc sbin tmp var
/ # mount -t nfs -o nolock 172.16.1.137:/home/change/work/rootfs_dir/fs_mini /mn
t/
/ # ls /mnt/
bin etc lib mnt root sys usr
dev home linuxrc proc sbin tmp
/ # cd /mnt/home/linux-3.0.62/pcduino/
/mnt/home/linux-3.0.62/pcduino # ls
first_drv.ko firstdrvtest gpio_drv.ko gpiodrvtest
/mnt/home/linux-3.0.62/pcduino # insmod first_drv.ko
/mnt/home/linux-3.0.62/pcduino # lsmod
first_drv 1768 0 - Live 0xbf000000
/mnt/home/linux-3.0.62/pcduino # ./firstdrvtest off
first_drv_open
[ 303.610000] first_drv_open
first_drv_write
[ 303.610000] first_drv_write
/mnt/home/linux-3.0.62/pcduino # ./firstdrvtest on
first_drv_open
[ 309.510000] first_drv_open
first_drv_write
[ 309.510000] first_drv_write
/mnt/home/linux-3.0.62/pcduino #
可以看到执行./firstdrvtest off ,pcDuino上的TX LED就灭,执行./firstdrvtest on,pcDuino上的TX LED就亮。测试基本正常,卸载刚刚加载的驱动如下;
/mnt/home/linux-3.0.62/pcduino # rmmod first_drv
/mnt/home/linux-3.0.62/pcduino # lsmod
/mnt/home/linux-3.0.62/pcduino #
基本OK了,下一步继续完善驱动。