最近在学习嵌入式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