酝酿许久的手记终于面世了,希望高手多多指点。从7月到现在,我参与的产品的研发,也算告一段落,明天就是成都的医博会了,这几个月慢慢啃出来的东西,要好好的总结一下,大学的时候,玩的是单片机,arm裸机,对于系统有所了解,做过一些小实验,但是没有系统的学习,如今做的事情,让我对嵌入式linux的认识也更正了许多。在此做个手记。
以前一直认为,要做linux,就得安装真机的linux系统,就像“不下水的人,永远也不能真正学会游泳”一样。但是虚拟机很方便,安装好系统后,做个备份,在安装其他东西,系统崩溃了
删除就是了。
现在重头开始慢慢理一下思路,以前学习用的zlg2410的资料我还有,但是那是试验教材用的资料,在具体应用中就很麻烦。万事开头难,公司用了一个10.4寸的lcd屏,是vga接口的郁闷死我的事情是:买这个屏的时候,对方提供了一个驱动原文件以及全英文的使用文档,这个屏是 TouchScreen: egalax TouchScreen (USB触摸驱动)。怎么也得先触摸显示了,才有其他程序开发的事撒,于是,我就开始看文档,编译他的usb触摸屏驱动,我看文档的时候,感觉到这个触摸屏在我的开发板上用有问题,但是如果我连驱动文件.ko都编译不出来,我想还是值得花点时间研究这个usb的触摸屏,其实这里我出了有一个基本问题的错误,文档里面已经说明了是基于X11的,而qte显示是基于framebuffer的,我花了一周多的时间看usb的驱动的编译以及移植,期望他能在我的目标板上能够触摸,这个屏的驱动文件,需要在2.6的linux内核下编译,菜鸟真的很苦的啊。。我又开始开始琢磨起2.6内核和2.4的内核的驱动开发的问题。首先想到的是usb驱动设备的开发环境以及移植,参看Macheal 分享的一篇《构建嵌入式驱动开发编译环境》的文字和loveuzz的《嵌入式平台USB驱动程序移植(zc0301p 摄像头)》,对内核一些列配置选择以后,在内核源码的目录下执行:
# make
# make bzImage
其中,第一个make也可以不执行,直接make bzImage。这个过程可能要持续一个小时左右,因此是对整个内核重新编译了。执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux, 其属性为-rwxr-xr-x。 然后执行:
# touch * // 时间或时区设置,源代码的时间戳比本机的时间更新,否则产生:make[2]: 警告:检测到时钟错误。您的创建可能是不完整的。
# make modules 对内核的所有模块进行编译
# make modules_install 对内核的所有模块进行和安装
执行结束之后,会在/lib/modules下生成新的目录/lib/modules/2.6.14.7 在随后的编译模块文件时,要用到这个路径下的build目录。至此,内核编译完成。
目录“/usr/src/2.6.14.7”中就是所谓的“内核代码树”但是“/lib/modules/2.6.14.7/build”是个符号链接,也指向这个目录,所以这里也可以叫做“内核代码树”,我是在redhat9.0下做的,所以在make modules_install的时候报错,郁闷,一怒之下,别开所悟,继续下面的操作。在源码树中编译模块
官方内核模块的源代码都是按模块(驱动)类型组织的, 我们到内核源码树的drivers目录可以看到char, usb, block之类的子目录. 那么我们在内核源码树中添加文件时, 最好也遵循这些分类. 分类的规则自己灵活把握.
下面以前面的"hello, world"这个简单的模块为例, 来看看如何在内核源码树中编译模块.
1, 不新建子目录
(1) 先在内核源码树中的drivers目录编辑一个c源程序, 名为hello.c.
(2) 修改drivers目录的Makefile文件, 添加: obj-m += hello.o
(3) 重新编译内核(回到源码树根目录, 运行 $ make).
这样, 在drivers目录多出了这样几个文件: hello.mod.c, hello.mod.o, hello.o, hello.ko. hello.ko就是编译出来的模块了.
2, 新建子目录
如果源文件比较多, 可以在drivers目录中新建子目录. 还是以hello, world为例:
(1) 在内核源码树的drivers目录中新建一个hello子目录, 并将hello.c放在hello目录中.
(2) 修改drivers目录的Makefile文件, 添加: obj-m += hello/
(3) 在hello目录中新建一个Makefile文件, 内容为: obj-m += hello.o
(4) 重新编译内核(回到源码树根目录, 运行 $ make).
这样, 新生成的模块文件就位于hello目录中.
按照方法2,我做了测试,结果在2.6.12的内核目录下生成了需要的egalax.ko的usb驱动模块文件(此时,我电脑上安装了2.6内核的linux系统),但是下载到目标板,insmod egalax.ko 提示信息加载成功,但是触摸还是没有反应(~!~应该没有反应的,o(∩_∩)o...),参看《嵌入式平台USB驱动程序移植(zc0301p 摄像头)》按照自己的需要,定义CONFIG_USB_EGALAX,修改drivers/ usb/ Makefile文件,添加:
obj-$(CONFIG_USB_EGALAX) += TouchKit/
2、修改drivers/ usb/ Kconfig文件,在最后添加:
config CONFIG_USB_EGALAX
tristate "USB egalax TouchScreen "
depends on USB
成功在执行make menuconfig之后,就可以在usb的driver下选择,把生成的的zImage下载到目标板,可以看到usb1.1设备显示eGalax加载成功的信息。
网上也有一段把驱动编译进内核的方法的描述,在下愚昧没有成功,但是载录如下:
驱动程序的使用可以按照两种方式编译,一种是静态编译进内核,另一种是编译成模块以供动态加载。由于uClinux不支持模块动态加载,
而且嵌入式LINUX不能够象桌面LINUX那样灵活的使用insmod/rmmod加载卸载设备驱动程序,因而这里只介绍将设备驱动程序静态编译进uClinux内核的方法。
下面以UCLINUX为例,介绍在一个以模块方式出现的驱动程序test.c基础之上,将其编译进内核的一系列步骤:
(1) 改动test.c源带代码
第一步,将原来的:
#include
#include
char kernel_version[]=UTS_RELEASE;
改动为:
#ifdef MODULE
#include
#include
char kernel_version[]=UTS_RELEASE;
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif
第二步,新建函数int init_test(void)
将设备注册写在此处:
result=register_chrdev(254,"test",&test_fops);
(2)将test.c复制到/uclinux/linux/drivers/char目录下,并且在/uclinux/linux/drivers/char目录下mem.c中,int chr_dev_init( )函数中增加如下代码:
#ifdef CONFIG_TESTDRIVE
init_test();
#endif
(3) 在/uclinux/linux/drivers/char目录下Makefile中增加如下代码:
ifeq($(CONFIG_TESTDRIVE),y)
L_OBJS+=test.o
Endif
(4)在/uclinux/linux/arch/m68knommu目录下config.in中字符设备段里增加如下代码:
bool 'support for testdrive' CONFIG_TESTDRIVE y
(5) 运行make menuconfig(在menuconfig的字符设备选项里你可以看见我们刚刚添加的'support for testdrive'选项,并且已经被选中);
make dep;make linux;make linux.text;make linux.data;cat linux.text linux.data > linux.bin。
(6) 在 /uclinux/romdisk/romdisk/dev/目录下创建设备:
mknod test c 254 0
并且在/uclinux/appsrc/下运行make,生成新的Romdisk.s19文件。
到这里,在UCLINUX中增加设备驱动程序的工作可以说是完成了,只要将新的linux.bin与Romdisk.s19烧入目标板中,你就可以使用自己的新设备test了。
至此,关于驱动的编译有了一个大概的了解,在后面,书写io口控制的驱动,以及修改rs232驱动为485驱动的时候,有很大的帮助至于书写有一定规模的驱动,使用skype做的东东,慢慢学习一下。
公司买的第一块工控开发板是龙人计算机的,他们给的文件相当的杂乱。所谓的使用文档和说明和实际也有很大的出入,所以在编译他们给的交叉编译环境qtopia2.2.0的时候,也是遇到一些麻烦,最让我恼火的是,在我搞不清楚触摸该怎么弄的时候,我咨询能不能把触摸屏上的4线电阻直接接到2440的触摸屏的控制引脚上,他给我乱扯些修改usb驱动他们不做这个支持,我们提供的系统是那样的,你怎么可以修改成那样,那样做了肯定是不行的。。。郁闷。。什么人嘛,自己没有实践,就把别人说得一塌糊涂,虽然我是菜鸟,那也不是我的错啊,我只是没经验没钱。。没法,菜鸟总是会忘记一些东西,以后会记住了,路是坎坷了些,正如一个朋友说的:人要一边拉车,一边抬头看路,驱动是在内核文件里面提供了的,那把4线电阻接到2440的触摸引脚,应该没有问题,试验证明我是对的。总算是可以触摸了。。我的时间被我给蹉跎得。。。谁晓得这又不是一件好事呢?毕业快半年了,现在总算静下心来,弄自己的东东。感谢所有的麻烦,我才会慢慢做好~!·
以前使用zlg的qt做过点小东西。他的环境是:
readhat9.0
交叉编译工具是2.95.3(其中已经编译好了jpeg,uic等库)
qte-2.3.7
qtopia1.7.0
qt-x11-2.3.2
tmake1.11
交叉编译工具,解压到/ usr/ local/ arm下
tar -jxvf cross_2.95.3-zhiyuan.tar.bz2
执行完以后,export PATH=/ usr/ local/ arm/ 2.95.3/ bin:$ PATH,然后修改/ etc/ profile文件,在#Path manipulation 段 添加编译器路径 pathmunge / usr/ local/ 2.95.3/ bin 。
编译很顺利,我想每一个开发者都不希望把时间花在环境编译配置上,这些原理晓得,操作过一次就够了。再加上编译环境确实是一件麻烦的事情。幸好zlg里面提供了build文件,
先提供给有兴趣的朋友如下:
#!/ bin/ bash
echo remove old dirs
rm -rf tmake
rm -rf qt
rm -rf qt-x11
rm -rf qtopia
echo Unpack all packages
tar xzvf tmake-1.11.tar.gz
tar xzvf qt-embedded-2.3.7.tar.gz
tar xzvf qt-x11-2.3.2.tar.gz
tar xzvf qtopia-free-1.7.0.tar.gz
echo rename
mv tmake-1.11 tmake
mv qtopia-free-1.7.0 qtopia
mv qt-2.3.7 qt
echo Start Install Qt-X11
cd qt-2.3.2
export QTDIR=$ PWD
echo yes | ./ configure -static -no-xft -no-opengl -no-sm
make -C src/moc
cp src/ moc/ moc bin
make -C src
make -C tools/ designer
make -C tools/ qvfb
cp tools/ qvfb/ qvfb bin
strip bin/ uic bin/ moc bin/ designer bin/ qvfb
#cp bin/ uic $ QTEDIR/bin
cp bin/ ?* ../ qt/bin
cd ..
#cp $ QTDIR/bin/ ?* qt/ bin
mv qt-2.3.2 qt-x11
echo Start Setting env-var
export QTDIR=$ PWD/ qt
export QTEDIR=$ QTDIR
export QPEDIR=$ PWD/ qtopia
export TMAKEDIR=$ PWD/ tmake
export TMAKEPATH=$ TMAKEDIR/ lib/ qws/ linux-arm-g++
export PATH=$ QTDIR/ bin:$ QPEDIR/ bin:$ TMAKEDIR/ bin:$ PATH
export LD_LIBRARY_PATH=$ QTDIR/ lib:$ LD_LIBRARY_PATH
echo Start build Qt/ Embedded
cd qt
make clean
cp $QPEDIR/ src/ qt/ qconfig-qpe.h src/ tools
(echo yes; echo no )| ./ configure -platform linux-arm-g++ -qconfig qpe -qvfb -depths 16,24,32
make -C src
cd ..
echo Start Build Qtopia
cd qtopia/ src
./ configure -platform linux-arm-g++
make
echo OK
编译完以后,每次使用的时候,需要source 一下环境就可以了。制作成文件 set-env
export QTDIR=$PWD/ qt
export QPEDIR=$PWD/ qtopia
export TMAKEDIR=$PWD / tmake
export TMAKEPATH=$TMAKEDIR/ lib/ qws/ linux-arm-g++
export PATH=$QTDIR/ bin:$QPEDIR/ bin:$TMAKEDIR/ bin:$PATH
在应用中需要用到多线程,作为菜鸟的我,很是苦恼了几天。关于多线程,网上收到的资料提到了两个东西 phtread 和thread。大部分资料指出,qt2对多线程的支持不是很好,程序容易崩溃。后来,Google了pthread和thread的说明,可能是大家多觉得比较简单,所以,我看到的合适的东西比较少,最后找到一个论坛上看到(没记下是那个论坛,sorry),对多线程的支持不佳,指的是qt2编译出的thread,既带-mt的库。pthread是支持pthread的linux系统提供的。我试着在zlg提供的文件上修改build文件,添加-thread,但是编译qt-x11和qte的时候,都报错。Google。。。。网上关于交叉编译的文字很多,我拿过来试着编译,还是错误,(有可能我写的,你拿去也会出错,不过我这个菜鸟写的东西,诸位大虾又怎么会参看。~!~是我自己觉得需要,才写写罢了),最终成功编译的build修改如下:
cd qt-2.3.2
export QTDIR=$PWD
echo yes|./configure -static -no-xft -no-opengl -no-sm -system-jpeg -thread
make
# -C src/moc
cp src/moc/moc bin
#make -C src
make -C tools/designer
cd qt
make clean
cp $QPEDIR/src/qt/qconfig-qpe.h src/tools
(echo yes; echo no)|./configure -xplatform linux-arm-g++ -thread -qconfig qpe -qvfb -depths 16,24,32
make
# -C src
cd ..
环境好了,先来个Dialog,下载到目标便看看效果。ok~!~
编译了以下,板商提供 qtopia2.2.0,没有成功,其中笔误很多,很多。。。最后在网上下了一个lib的库,和头文件的包,才勉强编译移植成功。
接下来便是linux下的485通信了,485与232相比,对于编程来说,不同的就是485比232多了一个读写控制线,他是半双工的,当该控制线为低的时候,该模块处于接收数据状态,总线上只能同时只有一个处于发送状态,用两组485可以组成全双工。一直使用的是让人痛苦的龙人的板子,他们在2440的串口2上接了个485,我先是用串口1的232做好了通信,再移植到串口2的,硬件应该是好的,他们提供的通信程序可以测试发送,在linux的dev下由一个串口设备描述文件,搞怪的是,我在这个设备上写数据,串口2居然没有波形产生,这个设备文件就是假的,郁闷得我啊。。。(作为菜鸟,在老大没有下决心动他的板子以前,我还是老老实实的弄我的通信程序,查串口资料)。最后,把板子上的串口2的485断掉,串口1的232断掉,在串口1上接485,ok了。(我对串口这样的驱动,还不熟悉,动他不得,其他简单点的驱动还是可以动的。)我咨询他们,如果我在串口的驱动里面,在发送的的时候,判断该串口是否是串口1,他们也久久没有答复,如下是我对内核文件中的s3c2410.c的修改,比较差劲,但是先用着了。
static void
s3c24xx_serial_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
if(port->line==1){
s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG11, 0);
}
if (tx_enabled(port)) {
disable_irq(TX_IRQ(port));
tx_enabled(port) = 0;
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_enable(port);
}
}
static void
s3c24xx_serial_start_tx(struct uart_port *port, unsigned int tty_start)
{
if(port->line==1){
s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG11, 1);
}
if (!tx_enabled(port)) {
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
enable_irq(TX_IRQ(port));
tx_enabled(port) = 1;
}
}
static int s3c24xx_serial_startup(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
。。。。。。。。
/* the port reset code should have done the correct
* register setup for the port controls */
if(port->line==1){
s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG11, 0);
}
local_irq_restore(flags);
return ret;
err:
s3c24xx_serial_shutdown(port);
local_irq_restore(flags);
return ret;
}
还有个问题,我还没搞定。我在通过串口1,收发字符,都可以正常接收,但是接收数值型数据时,每个字节的高位都被屏蔽了一样,比如pc机发送16进制数据0xAA,目标板接收到的却是0x2A。我怀疑是驱动的问题,希望有高手看到了,帮我这个菜鸟一下。以下是我以二进制方式读串口的代码段:(今天没有带回来,过两天有时间了来补上)
至10月28号,前阶段算是ok了,在此阶段,系统移植,文件,内核的裁剪还有驱动动有了一些实际的操作经验,找时间在学学做做驱动。在linux下不把驱动搞定就拉到了。特别是中断部分。
加油。。。~!~
附: