首页 > 代码库 > pcDuino的linux移植四简单驱动开发

pcDuino的linux移植四简单驱动开发

开发环境
系统:ubuntu 10.04.4
单板:pcDuino(全志A10)
编译器:arm-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2

搭建开发环境详见ubuntu 10.04.4开发环境配置。

目标
1.配置 编译linux-3.0.62内核,串口正常输出
2.配置内核,支持nfs挂载,启动文件系统
3.最简单内核驱动开发

这个是在pcDuino linux移植一、二、三的基础上写的,重点介绍内核驱动开发,其它具体移植过程不再详述。最基础的裸板开发、u-boot移植以及linux移植,参考前面的文章,这里不再重复。

一、搭建驱动开发平台

这里用官方提供的源码kernel for pcduino,我试了哈,这里面有些常用的驱动,比如网卡驱动之类,在官方提供的kernel平台再添加自己的驱动。

1.准备工作

获取交叉编译器http://code.google.com/p/smp-on-qemu/downloads/list选择arm-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2并下载。然后在ubuntu下直接解压即可,配置过程还不清楚的看Ubuntu 10.04.4开发环境配。

获取linux源码 https://github.com/pcduino/kernel 点击linux-sunxi,跳转到https://github.com/linux-sunxi/linux-sunxi下载linux-sunxi源码

2.配置、编译内核

打开linux-sunxi-sunxi-3.0目录下的Makefile,修改195:

ARCH ?= ARM

CROSS_COMPILE ?=  your-path/arm-2009q3/bin/arm-none-linux-gnueabi-

其中your-path是gcc-linaro-arm-linux-gnueabihf-4.8-2013.04-20130417_linux.tar.bz2的解压路径

@change :~$" target="_blank" rel="nofollow">change@change :~$ cd /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/
@change :~/Si/A10/pcduino/linux-sunxi-sunxi-3.0$" target="_blank" rel="nofollow">change@change :~/Si/A10/pcduino/linux-sunxi-sunxi-3.0$ make menuconfig

记住选中wemac support(A1x),其它不变。开始也不知道选这个,后来分析官方提供的kernel才知道的。

编译过程超长,如果编译过程中遇到些问题,见pcduino linux 移植一、二、三

@change :~/Si/A10/pcduino/linux-sunxi-sunxi-3.0$" target="_blank" rel="nofollow">change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0$ ls arch/arm/boot
bootp  compressed  Image  install.sh  Makefile  uImage  zImage

3.测试

经过pcDuino linux移植一、二、三,我们已制作一张可启动的tf卡,现在用我们新的内核uImage,替换掉以前的进行测试。将上次制作的启动卡插到PC机,弹出窗口,直接拷贝上面编译生成的uimage到sdb1,替换掉以前的,取下tf卡插到pcduino启动如下:

U-Boot SPL 2012.10 (Apr 19 2013 – 18:46:44)
DRAM: 1024MB
SUNXI SD/MMC: 0
U-Boot 2012.10 (Apr 19 2013 – 18:46:44) Allwinner Technology

CPU:   SUNXI Family
Board: pcDuino
I2C:   ready
DRAM:  1 GiB
MMC:   SUNXI SD/MMC: 0
*** Warning – bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Hit any key to stop autoboot:  0
reading uEnv.txt

144 bytes read
Loaded environment from uEnv.txt
reading boot.scr

270 bytes read
Jumping to boot.scr
## Executing script at 44000000
reading script.bin

” from mmc 0:1 ** “script.bin
reading uImage

” from mmc 0:1 ** “uImage
bootm – boot application image from memory

Usage:
bootm [addr [arg ...]]
- boot application image stored in memory
passing arguments ‘arg …’; when booting a Linux kernel,
‘arg’ can be the address of an initrd image

Sub-commands to do part of the bootm sequence.  The sub-commands must be
issued in the order below (it’s ok to not issue all sub-commands):
start [addr [arg ...]]
loados  – load OS image
cmdline – OS specific command line processing/setup
bdt     – OS specific bd_t processing
prep    – OS specific prep before relocation or go
go      – start OS
reading script.bin

44900 bytes read
reading uImage

4693140 bytes read
## Booting kernel from Legacy Image at 48000000 …
Image Name:   Linux-3.0.62
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    4693076 Bytes = 4.5 MiB
Load Address: 40008000
Entry Point:  40008000
Verifying Checksum … OK
Loading Kernel Image … OK
OK

Starting kernel …

<6>Initializing cgroup subsys cpuset
<5>Linux version 3.0.62 (change@change) (gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67) ) #2 PREEMPT Tue May 21 22:35:22 CST 2013
CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), cr=10c5387d
CPU: VIPT nonaliasing data cache, VIPT aliasing instruction cache
Machine: sun4i
<6>Memory cut off:
<6>     MALI : 0x5c000000 – 0x5fffffff  (  64 MB)
<4>Ignoring unrecognised tag 0×00000000
<6>Memory Reserved:
<6>     SYS  : 0×43000000 – 0x4300ffff  (  64 kB)
<6>     VE   : 0×44000000 – 0x48ffffff  (  80 MB)
<6>     G2D  : 0×49000000 – 0x49ffffff  (  16 MB)
<6>     LCD  : 0x5a000000 – 0x5bffffff  (  32 MB)
Memory policy: ECC disabled, Data cache writeback
<6>chip-id: A10 (AW1623 revision C)
<7>On node 0 totalpages: 245760
<7>free_area_init_node: node 0, pgdat c0887ea0, node_mem_map c094e000
<7>  Normal zone: 1280 pages used for memmap
<7>  Normal zone: 0 pages reserved
<7>  Normal zone: 146176 pages, LIFO batch:31
<7>  HighMem zone: 768 pages used for memmap
<7>  HighMem zone: 97536 pages, LIFO batch:31
<7>pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
<7>pcpu-alloc: [0] 0
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 243712
<5>Kernel command line: console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait loglevel=8 panic=10
<6>ID hash table entries: 4096 (order: 2, 16384 bytes)
<6>Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
<6>Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
<6>Memory: 448MB 512MB = 960MB total
<5>Memory: 833852k/833852k available, 149188k reserved, 393216K highmem

……….//省略若干,太长了

<6>mmc0: new high speed SD card at address 0260
[    4.070000] mmc0: new high speed SD card at address 0260
<6>mmcblk0: mmc0:0260 SD    948 MiB
[    4.090000] mmcblk0: mmc0:0260 SD    948 MiB
<6> mmcblk0: p1 p2
[    4.100000]  mmcblk0: p1 p2
<6>Console: switching to colour frame buffer device 160×45
[    4.230000] Console: switching to colour frame buffer device 160×45
<4>regulator_init_complete: axp20_buck3: incomplete constraints, leaving on
[    4.270000] regulator_init_complete: axp20_buck3: incomplete constraints, leaving on
<4>regulator_init_complete: axp20_buck2: incomplete constraints, leaving on
[    4.290000] regulator_init_complete: axp20_buck2: incomplete constraints, leaving on
<4>regulator_init_complete: axp20_ldo4: incomplete constraints, leaving on
[    4.300000] regulator_init_complete: axp20_ldo4: incomplete constraints, leaving on
<4>regulator_init_complete: axp20_ldo3: incomplete constraints, leaving on
[    4.320000] regulator_init_complete: axp20_ldo3: incomplete constraints, leaving on
<4>regulator_init_complete: axp20_ldo2: incomplete constraints, leaving on
[    4.330000] regulator_init_complete: axp20_ldo2: incomplete constraints, leaving on
<4>regulator_init_complete: axp20_ldo1: incomplete constraints, leaving on
[    4.350000] regulator_init_complete: axp20_ldo1: incomplete constraints, leaving on
<6>sunxi-rtc sunxi-rtc: sunxi_rtc_gettime
[    4.360000] sunxi-rtc sunxi-rtc: sunxi_rtc_gettime
<6>sunxi-rtc sunxi-rtc: read time 2010-1-1 0:0:9
[    4.370000] sunxi-rtc sunxi-rtc: read time 2010-1-1 0:0:9
<6>sunxi-rtc sunxi-rtc: setting system clock to 2010-01-01 00:00:09 UTC (1262304009)
[    4.380000] sunxi-rtc sunxi-rtc: setting system clock to 2010-01-01 00:00:09 UTC (1262304009)
<3>EXT3-fs (mmcblk0p2): error: couldn’t mount because of unsupported optional features (240)
[    4.400000] EXT3-fs (mmcblk0p2): error: couldn’t mount because of unsupported optional features (240)
<3>EXT2-fs (mmcblk0p2): error: couldn’t mount because of unsupported optional features (244)
[    4.420000] EXT2-fs (mmcblk0p2): error: couldn’t mount because of unsupported optional features (244)
<6>EXT4-fs (mmcblk0p2): recovery complete
[    4.740000] EXT4-fs (mmcblk0p2): recovery complete
<6>EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
[    4.750000] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
<6>VFS: Mounted root (ext4 filesystem) on device 179:2.
[    4.770000] VFS: Mounted root (ext4 filesystem) on device 179:2.
<6>devtmpfs: mounted
[    4.780000] devtmpfs: mounted
<6>Freeing init memory: 164K
[    4.790000] Freeing init memory: 164K
mount: mounting devpts on /dev/pts failed: No such file or directory
mount: mounting /dev/mmcblk0p1 on /boot failed: No such file or directory

Please press Enter to activate this console.

Processing /etc/profile… Done

/ # ls
bin      etc      lib      mnt      run      sys      usr
dev      home     linuxrc  proc     sbin     tmp      var
/ # ifconfig eth0 172.16.1.111
<4>wemac wemac.0: WARNING: no IRQ resource flags set.
[  126.910000] wemac wemac.0: WARNING: no IRQ resource flags set.
<6>wemac wemac.0: eth0: link up, 100Mbps, full-duplex, lpa 0x45E1
[  127.060000] wemac wemac.0: eth0: link up, 100Mbps, full-duplex, lpa 0x45E1
/ # <7>eth0: no IPv6 routers present
[  137.860000] eth0: no IPv6 routers present

/ # ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 8A:CA:EF:97:41:11
inet addr:172.16.1.111  Bcast:172.16.255.255  Mask:255.255.0.0
inet6 addr: fe80::88ca:efff:fe97:4111/64 Scopeink
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:20 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:6831 (6.6 KiB)  TX bytes:468 (468.0 B)
Interrupt:55 Base address:0×8000

/ # ping 172.16.1.134
PING 172.16.1.134 (172.16.1.134): 56 data bytes
64 bytes from 172.16.1.134: seq=0 ttl=64 time=11.119 ms
64 bytes from 172.16.1.134: seq=1 ttl=64 time=1.628 ms
64 bytes from 172.16.1.134: seq=2 ttl=64 time=0.866 ms
^C
— 172.16.1.134 ping statistics —
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.866/4.537/11.119 ms
/ #

看到系统基本正常启动了,网卡工作也正常能ping通。先写到这里,下一步做简单驱动开发,待续。。。并且已支持挂载,以后把自己编译好的驱动挂载到单板pcduino,在手动加载到单板,然后写个简单应用程序,测试驱动。最后再编译进内核。我是想搭建这么个驱动开发平台。

二、最简单内核驱动开发

1.编写代码

写一个最简单的驱动程序,介绍驱动开发流程。先写字符一个简单的字符驱动代码:first_drv.c

[plain] view plaincopy



  • #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;
  • static int first_drv_open(struct inode *inode, struct file *file)
  • {
  •     printk(“first_drv_open\n”);
  •     return 0;
  • }
  • static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
  • {
  •     printk(“first_drv_write\n”);
  •     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”);
  •     return 0;
  • }
  • static void first_drv_exit(void)
  • {
  •     unregister_chrdev(major,”first_drv”);
  •     device_unregister(firstdrv_class_dev);
  •     class_destroy(firstdrv_class);
  • }
  • module_init(first_drv_init);
  • module_exit(first_drv_exit);
  • MODULE_LICENSE(“GPL”);


编写Makefile:

[plain] view plaincopy



  • 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


其中KERN_DIR是你的内核源码存放路径,要根据自己情况修改。

2.编译驱动源码

change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv$ 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/first_drv.o
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/first_drv.c: In function ‘first_drv_init’:
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/first_drv.c:38: warning: assignment from incompatible pointer type
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/first_drv.c: In function ‘first_drv_exit’:
/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/first_drv.c:45: 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/first_drv.mod.o
LD [M]  /home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv/first_drv.ko
make[1]: Leaving directory `/home/change/Si/A10/pcduino/linux-sunxi-sunxi-3.0′
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv$ ls
firstdrivetest.c  first_drv.ko     first_drv.mod.o  Makefile       Module.symvers
first_drv.c       first_drv.mod.c  first_drv.o      modules.order
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv$

可以看到生成了驱动加载模块first_drv.ko,下面还要写个简单的驱动测试代码firstdrivetest.c:

[plain] view plaincopy



  • #include <sys/types.h>
  • #include <sys/stat.h>
  • #include <fcntl.h>
  • #include <stdio.h>
  • 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”);
  •     }
  •     write(fd, &val, 4);
  •     return 0;
  • }


接着编译驱动测试代码

change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv$ /home/change/tools/arm-2009q3/bin/arm-none-linux-gnueabi-gcc -o firstdrivetest firstdrivetest.c
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv$ ls
firstdrivetest    first_drv.c   first_drv.mod.c  first_drv.o  modules.order
firstdrivetest.c  first_drv.ko  first_drv.mod.o  Makefile     Module.symvers
change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv$

先拷到我的主机nfs挂载目录,不知道怎么配置看ubuntu 10.04.4开发环境配置

change@change:~/Si/A10/pcduino/linux-sunxi-sunxi-3.0/drivers/mydrv$ cp first_drv.ko firstdrivetest /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$

记住编译驱动、根文件系统、内核的交叉编译器最好用同一套编译器,不然会出现问题,我又遇到过。看到测试驱动的firstdrivetest也生成了。下面将其挂载到到你的单板pcduino进行测试。现在切换到单板pcduino启动的控制台进行挂载

/ # mount -t nfs -o nolock 172.16.1.134:/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    firstdrivetest
/mnt/home/linux-3.0.62/pcduino #

看到挂载成功了,刚刚我们编译生成的first_drv.ko    firstdrivetest也挂载到单板了。测试先加载驱动

/mnt/home/linux-3.0.62/pcduino # ls /dev/xyz
ls: /dev/xyz: No such file or directory
/mnt/home/linux-3.0.62/pcduino # insmod first_drv.ko
/mnt/home/linux-3.0.62/pcduino # ls /dev/xyz -l
crw——-    1 0        0         245,   0 Jan  1 00:52 /dev/xyz
/mnt/home/linux-3.0.62/pcduino #

可以看到加载前是不存在这个xyz设备的,成功加载后出现了。再运行测试程序,测试程序很简单就是驱动加载成功则输出first_drv_open、first_drv_write

/mnt/home/linux-3.0.62/pcduino # ./firstdrivetest
first_drv_open
[ 3297.090000] first_drv_open
first_drv_write
[ 3297.090000] first_drv_write
/mnt/home/linux-3.0.62/pcduino #

运行firstdrivetest ,与预期效果一致,基本正常。但是后来发现还有问题,如下不能卸载驱动

/mnt/home/linux-3.0.62/pcduino # lsmod
first_drv 1473 0 – Live 0xbf000000
/mnt/home/linux-3.0.62/pcduino # rmmod first_drv
rmmod: can’t change directory to ‘/lib/modules’: No such file or directory
/mnt/home/linux-3.0.62/pcduino # rmmod first_drv.ko
rmmod: can’t change directory to ‘/lib/modules’: No such file or directory
/mnt/home/linux-3.0.62/pcduino #

遇到问题,跟着提示做,没有’/lib/modules’那新建一个

/mnt/home/linux-3.0.62/pcduino # mkdir /lib/modules
/mnt/home/linux-3.0.62/pcduino # rmmod first_drv
rmmod: can’t change directory to ’3.0.62′: No such file or directory

又出现没有’3.0.62′,再创建一个

/mnt/home/linux-3.0.62/pcduino # mkdir /lib/modules/3.0.62
/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这下解决了,我以前又遇到过,发现这么解决最简单,再完整测试一遍

/mnt/home/linux-3.0.62/pcduino # ./firstdrivetest
first_drv_open
[ 3841.960000] first_drv_open
first_drv_write
[ 3841.970000] first_drv_write
/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 #

问题基本解决了,我们的驱动开发平台就搭建好了,以后抓紧写其它驱动代码。还试了一下rmmod first_drv.ko 卸载不了,还是要用rmmod first_drv

/mnt/home/linux-3.0.62/pcduino # insmod first_drv.ko
/mnt/home/linux-3.0.62/pcduino # lsmod
first_drv 1473 0 – Live 0xbf008000
/mnt/home/linux-3.0.62/pcduino # rmmod first_drv.ko
/mnt/home/linux-3.0.62/pcduino # lsmod
first_drv 1473 0 – Live 0xbf008000
/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 #

测试完使用halt停机

/mnt/home/linux-3.0.62/pcduino # halt
The system is going down NOW!
Sent SIGTERM to all processes
Terminated
Sent SIGKILL to all processeso #
Requesting system halt<6>[sw-ohci2]: shutdown start
[ 4290.610000] [sw-ohci2]: shutdown start
[sw-ohci2]: close clock
[ 4290.620000] [sw-ohci2]: close clock
<6>[sw-ohci2]: shutdown end
[ 4290.620000] [sw-ohci2]: shutdown end
<6>[sw-ehci2]: shutdown start
[ 4290.630000] [sw-ehci2]: shutdown start
[sw-ehci2]: Set USB Power OFF
[ 4290.640000] [sw-ehci2]: Set USB Power OFF
[sw-ehci2]: close clock
[ 4290.640000] [sw-ehci2]: close clock
<6>[sw-ehci2]: shutdown end
[ 4290.650000] [sw-ehci2]: shutdown end
<6>[sw-ohci1]: shutdown start
[ 4290.660000] [sw-ohci1]: shutdown start
[sw-ohci1]: close clock
[ 4290.660000] [sw-ohci1]: close clock
<6>[sw-ohci1]: shutdown end
[ 4290.670000] [sw-ohci1]: shutdown end
<6>[sw-ehci1]: shutdown start
[ 4290.680000] [sw-ehci1]: shutdown start
[sw-ehci1]: Set USB Power OFF
[ 4290.690000] [sw-ehci1]: Set USB Power OFF
[sw-ehci1]: close clock
[ 4290.690000] [sw-ehci1]: close clock
<6>[sw-ehci1]: shutdown end
[ 4290.700000] [sw-ehci1]: shutdown end
[NAND]shutdown
[ 4290.710000] [NAND]shutdown
nand try to shutdown 0 time
[ 4290.710000] nand try to shutdown 0 time
Nand flash shutdown ok!
[ 4290.720000] Nand flash shutdown ok!
<0>System halted.
[ 4290.720000] System halted.

一切正常,暂时还没发现其他问题,可以休息了。发现问题请提出,一起解决。

转至:http://blog.csdn.net/u010216127/article/details/8947183

pcDuino的linux移植四简单驱动开发