最近在学习嵌入式Linux驱动编程,平台是友善之臂的mini2440开发板,堪称经典古董级别的,不过在互联网上它的资料和视频教程非常多,适合初学者入门。在学习驱动的道路上走了很多弯路,十分坎坷,尤其在nfs挂载时涉及到windows, ubuntu, 开发板三者网络配置,需要设置bootloader启动参数,安装和配置 nfs。这篇文章基于以图片为主导,一步一步地介绍环境搭建过程,后面有的地方也是粗略介绍,仅作为参考;希望可以帮助一些驱动爱好者。
本篇主要介绍开发环境搭建,基于AMD A8或者intel i5和Windows10 企业版LTS。主要以下几点:
1.Windows版本的 VMWare workstation pro
2.Linux发行版 ubuntu 1604(友善之臂官方使用fedora 9)
3.编辑器vim
4.Linux版本的dnw
5.交叉编译工具arm-linux-gcc
6.编译和烧录bootloader
7.编译和烧录内核
8.制作根文件系统
9.NFS服务
10.通过NFS挂载根文件系统
11.Windows版本Jflash烧录软件
12.编写一个驱动demo
在环境搭建中,可能会遇到各种奇葩的问题,不要灰心,大部分百度哥都可以解决。
一、VMWare workstation pro
VMWare主要虚拟一台电脑硬件,可以安装Windows系列、Linux发行版操作系统,采用VMWare workstation pro 14版本,以下为安装过程:
1>打开安装向导界面,点击下一步
2>用户许可协议,接受许可协议中的条款,点击下一步
3>自定义安装,选择后点击下一步
4>用户体验设置,选择后点击下一步
5>快捷选择,根据喜好选择,点击下一步
6>安装过程
7>完成安装,点击许可证
8>输入许可证秘钥,填写后点击输入
9>点击完成
10>VMware Workstation 14 Pro 界面
在菜单栏->编辑->虚拟网络编辑器里进行网卡配置;
在菜单栏->虚拟机->设置里进行硬件、网络、共享文件夹设置;
二、ubuntu 1604
它是Linux发行版之一,属于ubuntu系列,目前最新LTS版本是1804。由于本人对ubuntu 1604比较熟悉,以此为例进行安装,之后工具安装全部基于此版本。
1>点击菜单栏->选项卡->主页,打开以下界面,点击创建新的虚拟机
2>虚拟机向导,选择自定义,点击下一步
3>兼容性,默认即可,点击下一步
4>选择稍后安装操作系统,点击下一步
5>选择Linux,ubuntu,点击下一步
6>选择安装位置,点击下一步
7>根据计算机配置,选择处理器核心,点击下一步
8>配置虚拟机内存,点击下一步
9>选择桥接网络,点击下一步
10>默认即可,点击下一步
11>默认即可,选择下一步
12>默认即可,点击下一步
13>默认即可,选择下一步
14>选择磁盘文件位置,点击下一步
15>点击自定义硬件
16> 选择新CD/DVD,使用ISO影响文件,确认文件位置,点击关闭
17>返回15,点击完成,如图界面,点击开启此虚拟机
18>选择安装时使用中文,点击安装Ubuntu
19>点击继续
20>点击现在安装
21>点击继续
22>选择时区,点击继续
23>点击继续
24>设置账户密码,点击继续
25 >正在安装
26>安装结束,点击现在重启
27>输入用户密码
28>ubuntu1604用户界面
29>Ctrl+Alt+t 打开一个终端
30>点击VMware菜单栏->虚拟机->安装VMware Tools,然后在ubuntu终端拷贝文件到家目录,然后进入VMware Tools目录解压文件
31>进入vmware-tools-distrib目录,执行脚本,输入y,一直回车点击
32>安装VMware Tools成功信息,然后可以自行在VMware菜单栏->虚拟机->设置->选项里指定共享文件夹,用于ubuntu和windows互传文件,这个文件夹会挂在ubuntu /mnt/htfs/目录下
三、vim
它是一个功能丰富的编辑器,提供了查找、替换、格式整理等功能。
打开一个终端,使用sudo apt install vim 命令安装,因为每个人使用vim习惯不同,所以自行配置。
四、dnw
它和bootloader结合使用,用于操作NandFlash,例如更新NandFlash上的bootloader、kernel、rootfs等映像。在互联网上下载dnw源码包,里面包含驱动模块源码,编译装载就可以了,具体使用的命令有:
sudo insmod *.ko
sudo rmmod *.ko
sudo ./dnw *.bin
五、arm-linux-gcc
交叉编译工具,用于编译源程序。
1>将共享文件夹中的压缩包拷贝到/tmp目录下,进行解压安装
2>将路径添加到环境变量中,在/etc/profile文件中加入
export PATH=$PATH: /opt/FriendlyARM/toolschain/4.4.3/bin
然后查看执行arm-linux-gcc -v 获得版本信息则安装成功
六、bootloader
引导加载程序,友善之臂supervivi是闭源的,因此无法获取源码。有兴趣的可以移植uboot,它功能十分强大,毫不逊色于supervivi。
七、kernel
1>安装字符终端处理库
2>将内核源码拷贝到/tmp目录下,进行解压
3>进入内核目录,选型后进行默认配置
4>内核图形化配置界面,使用默认配置,退出
5>修改kernel/timeconst.pl第373行
6>切换为root用户,生成内核镜像文件,时间比较长
7>进入arch/arm/boot目录,生成内核镜像zImage
八、rootfs
不具体介绍根文件系统制作方法,使用友善之臂发布的根文件系统即可
九、NFS
1->安装nfs服务器
2>执行sudo gedit /etc/exports,加入最后一行,设置访问目录和权限
3>配置windows, ubuntu,开发板网络,让其处于同一个网段,如下为ubuntu网络配置
十、mount
设置开发板启动参数,启动后把服务器:192.168.1.111:/home/hhw/rootfs_qtopia_qt4 目录挂在自己根目录上
十一、JFlash
结合JLink用于更新NorFlash上的bootloader
1>Jflash用户界面
2>在options->Projects settings设置接口,选择处理器型号等
3>点击Target->connect连接芯片,点击file->open data file选择镜像文件,然后点击target->manual->Programming进行烧录
十二、driver demo
驱动例程包含driver.c、app.c和Makefile,如下图。
driver.c
#include #include #include #include #include #include unsigned int *gpiob_base = NULL; #define GPBCON (*(volatile unsigned int *)(gpiob_base)) #define GPBDAT (*(volatile unsigned int *)(gpiob_base + 1)) static int chardev_open(struct inode *node, struct file *fp) { printk("%s is calledn", __FUNCTION__); GPBCON &= ~(0x03 << 2*5); GPBCON |= 0x01 << 2*5; GPBCON &= ~(0x03 << 2*6); GPBCON |= 0x01 << 2*6; GPBCON &= ~(0x03 << 2*7); GPBCON |= 0x01 << 2*7; GPBCON &= ~(0x03 << 2*8); GPBCON |= 0x01 << 2*8; GPBDAT &= ~(0x0f << 5); return 0; } static int chardev_release(struct inode *node, struct file *fp) { printk("%s is calledn", __FUNCTION__); GPBCON &= ~(0x03 << 2*5); GPBCON &= ~(0x03 << 2*6); GPBCON &= ~(0x03 << 2*7); GPBCON &= ~(0x03 << 2*8); GPBDAT &= ~(0x0f << 5); return 0; } static ssize_t chardev_read(struct file *fp, char __user *str, size_t size, loff_t *offset) { unsigned char buffer; printk("%s is calledn", __FUNCTION__); if(copy_to_user((void __user*)str, (void *)&buffer, 1) < 0) printk("copy_to_user errorn"); return 0; } static ssize_t chardev_write(struct file *fp, const char __user *str, size_t size, loff_t *offset) { int i; unsigned char buffer; printk("%s is calledn", __FUNCTION__); if(copy_from_user(&buffer, str, 1) < 0) printk("copy_to_user errorn"); for(i = 0; i < 4; i++) { if((buffer >> i)&0x01) GPBDAT &= ~(0x20 << i); else GPBDAT |= 0x20 << i; } return 0; } static long chardev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int i; printk("%s is calledn", __FUNCTION__); switch(_IOC_DIR(cmd)) { case 0x00: { for(i = 0; i < 4; i++) { if((_IOC_TYPE(cmd) >> i)&0x01) { if((_IOC_NR(cmd) >> i)&0x01) GPBDAT &= ~(0x20 << i); else GPBDAT |= 0x20 << i; } } } break; case 0x01: break; case 0x10: break; case 0x11: break; default : break; } return 1234; } static struct file_operations chardev_fops = { .owner = THIS_MODULE, .open = chardev_open, .release = chardev_release, .read = chardev_read, .write = chardev_write, .unlocked_ioctl = chardev_ioctl, }; #define CHARDEV_NAME "leds_dri" static struct miscdevice chardev = { .minor = 255, .name = CHARDEV_NAME, .fops = &chardev_fops, }; static int __init chardev_init(void) { printk("%s is calledn", __FUNCTION__); gpiob_base = (unsigned int *)ioremap(0x56000010, 8); if(misc_register(&chardev) != 0) { printk("misc_register errorn"); return -EBUSY; } printk("misc_register successn"); return 0; } static void __exit chardev_exit(void) { printk("%s is calledn", __FUNCTION__); iounmap(gpiob_base); if(misc_deregister(&chardev) != 0) { printk("misc_deregister errorn"); } printk("misc_deregister successn"); } module_init(chardev_init); module_exit(chardev_exit); MODULE_LICENSE("GPL"); app.c #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int i; unsigned int cmd; unsigned char iodata = 0x01; int fd = open(argv[1], O_RDWR); if(fd == -1) { perror("open"); return -1; } printf("open successn"); cmd = _IO(0x0f, 0x00); ioctl(fd, cmd); for(i = 0; i < 16; i++) { cmd = _IO(iodata, iodata); ioctl(fd, cmd); sleep(1); cmd = _IO(iodata, 0); ioctl(fd, cmd); iodata <<= 1; if(iodata == 0x10) iodata = 0x01; } close(fd); return 0; } Makefile KDIR:=/opt/FriendlyARM/mini2440/linux-2.6.32.2 NAME=driver obj-m += $(NAME).o all: make -C $(KDIR) M=$(PWD) modules arm-linux-gcc app.c -o app.out clean: rm -f *.o *.mod.o *.mod.c *.symvers *.markers *.order