华恒ColdFire系列嵌入式Linux开发板常见问题解答
——基于公用核心板HHCF5249/5272
(华恒科技版权所有)
1、关于宿主机Linux的安装
建议使用REDHAT系列:建议选择完全安装,即选择Custom,然后在Package中选择everything。
【以下配置详见最新PDF手册下载(例如:HHCO5272-R1的手册)】
宿主机的网络配置
主要是要安装好以太网卡,对于一般常见的RTL8139网卡,REDHAT7.2可以自动识别并自动安装好,完全不要用户参与,因此建议使用该网卡。然后配置宿主机IP:
ifconfig eth0 192.168.2.32
【注意】
对于REDHAT7.2,它默认的是打开了防火墙,因此对于外来的IP访问它全部拒绝,这样其它网络设备根本无法访问它,即无法用NFS mount它,无法通过TFTP从它下载,无法telnet,ftp它等。因此网络安装完毕后,应立即关闭防火墙。操作如下:
运行setup,选择Firewall configuration,选中No firewall。然后到上一层菜单选择System services,去掉ipchains和iptables两项服务。最后退出setup。
其实,在安装REDHAT7.2/3时,就要求选择Custom定制安装,其中由一项就是要求选择No Firewall,这样的话,启动后,就不需要执行setup来设置防火墙了。还有,REDHAT在这里有个BUG,即无论上次你进入Firewall configuration选择什么,每次再次进入时它都显示High,这是REDHAT显示的BUG,其实防火墙已经关闭了。
配置NFS
运行linuxconf,在config选项下选Server tasks,选中Exported File systems(NFS),然后选择Add Directory,加入根目录/,然后Accept。系统就会输出根目录允许NFS mount。
下一步再选择Control项下面Control panel下的Control Service activity,然后选择nfs enabled,然后start。配置好后的界面显示其中nfs必须为: Automatic Running。
【注意】
这里建议把ipchains和iptables都取消其自动启动的状态。
最后,在Control项下面Control panel下选择Activate configuration,则弹出界面,提示系统配置的改动,选择"Do it",最后退出时则完成NFS配置。
配置完成后,可用如下办法简单测试一下NFS是否配置好了:
在宿主机上自己mount自己,看是否成功就可以判断NFS是否配好了。例如在宿主机/目录下执行:
mount 192.168.2.32:/ /mnt
然后到/mnt/目录下看是否可以列出/目录下的所有文件和目录,可以则说明mount成功,NFS配置成功。
配置TFTP服务器
参见下面第二点介绍。
【注意】
安装完华恒uClinux软件光盘后,不要make xconfig,直接make即可。加入驱动等修改内核的操作都不必make xconfig,除非要更改处理器平台,例如移植到MCF5407等才需要make xconfig。其实这个操作不过就是修改linux/.config和linux/include/linux/autoconfig.h中的宏设置。完全可以由手工完成。
2、关于gdbtftpflash烧写
【注意】
烧写必须接百兆以太网接口,对于多以太网板的10M口是不能用来烧写的。
在一个Linux TTY终端执行
./flash
然后立刻切换到另一个TTY终端启动的minicom下去查看信息
Reading image.bin from 192.168.2.46 to 0x00100000
TFTP download successful
或者信息如下:
ICMP: Port unreachable
ICMP: Port unreachable
ICMP: Port unreachable
TFTP could not make connection to server
Errors in TFTP download
Read 1004740 bytes (1963 blocks)【这才是关键所在,前面的信息都无用,只要这里读到的字节数和宿主机上/tftpboot/目录下的image.bin文件的大小一致就表明TFTP下载成功了!】
>>>>>>>>>> Init mflash
>>>>>>>>>> Init mflash Successfully
********** File size : 0xF54C4 bytes
********** Address base : 0x0
********** Manufacturer ID 1
********** Device ID 2249
********** Sector 0 [FFC00000] (0-unprotect, 1 protect):0
********** Sector 1 [FFC04000] (0-unprotect, 1 protect):0
********** Sector 2 [FFC06000] (0-unprotect, 1 protect):0
********** Sector 3 [FFC08000] (0-unprotect, 1 protect):0
********** Sector 4 [FFC10000] (0-unprotect, 1 protect):0
********** Sector 5 [FFC20000] (0-unprotect, 1 protect):0
********** Sector 6 [FFC30000] (0-unprotect, 1 protect):0
********** Sector 7 [FFC40000] (0-unprotect, 1 protect):0
********** Sector 8 [FFC50000] (0-unprotect, 1 protect):0
********** Sector 9 [FFC60000] (0-unprotect, 1 protect):0
********** Sector 10 [FFC70000] (0-unprotect, 1 protect):0
********** Sector 11 [FFC80000] (0-unprotect, 1 protect):0
********** Sector 12 [FFC90000] (0-unprotect, 1 protect):0
********** Sector 13 [FFCA0000] (0-unprotect, 1 protect):0
********** Sector 14 [FFCB0000] (0-unprotect, 1 protect):0
********** Sector 15 [FFCC0000] (0-unprotect, 1 protect):0
********** Sector 16 [FFCD0000] (0-unprotect, 1 protect):0
********** Sector 17 [FFCE0000] (0-unprotect, 1 protect):0
********** Sector 18 [FFCF0000] (0-unprotect, 1 protect):0
********** Sector 19 [FFD00000] (0-unprotect, 1 protect):0
********** Sector 20 [FFD10000] (0-unprotect, 1 protect):0
********** Sector 21 [FFD20000] (0-unprotect, 1 protect):0
********** Sector 22 [FFD30000] (0-unprotect, 1 protect):0
********** Sector 23 [FFD40000] (0-unprotect, 1 protect):0
********** Sector 24 [FFD50000] (0-unprotect, 1 protect):0
********** Sector 25 [FFD60000] (0-unprotect, 1 protect):0
********** Sector 26 [FFD70000] (0-unprotect, 1 protect):0
********** Sector 27 [FFD80000] (0-unprotect, 1 protect):0
********** Sector 28 [FFD90000] (0-unprotect, 1 protect):0
********** Sector 29 [FFDA0000] (0-unprotect, 1 protect):0
********** Sector 30 [FFDB0000] (0-unprotect, 1 protect):0
********** Sector 31 [FFDC0000] (0-unprotect, 1 protect):0
********** Sector 32 [FFDD0000] (0-unprotect, 1 protect):0
********** Sector 33 [FFDE0000] (0-unprotect, 1 protect):0
********** Sector 34 [FFDF0000] (0-unprotect, 1 protect):0
xxxxxxxxxx Program sector 0 : Read Fill Erase Program End
xxxxxxxxxx Program sector 1 : Read Fill Erase Program End
xxxxxxxxxx Program sector 2 : Read Fill Erase Program End
xxxxxxxxxx Program sector 3 : Read Fill Erase Program End
xxxxxxxxxx Program sector 4 : Read Fill Erase Program End
xxxxxxxxxx Program sector 5 : Read Fill Erase Program End
xxxxxxxxxx Program sector 6 : Read Fill Erase Program End
xxxxxxxxxx Program sector 7 : Read Fill Erase Program End
xxxxxxxxxx Program sector 8 : Read Fill Erase Program End
xxxxxxxxxx Program sector 9 : Read Fill Erase Program End
xxxxxxxxxx Program sector 10 : Read Fill Erase Program End
xxxxxxxxxx Program sector 11 : Read Fill Erase Program End
xxxxxxxxxx Program sector 12 : Read Fill Erase Program End
xxxxxxxxxx Program sector 13 : Read Fill Erase Program End
xxxxxxxxxx Program sector 14 : Read Fill Erase Program End
xxxxxxxxxx Program sector 15 : Read Fill Erase Program End
xxxxxxxxxx Program sector 16 : Read Fill Erase Program End
xxxxxxxxxx Program sector 17 : Read Fill Erase Program End
xxxxxxxxxx Program sector 18 : Read Fill Erase Program End
********** Verify Complete
下面板子开始重启。
【注意】
一定要看到这些Read Fill Erase Program End信息才表示烧写进去了,前面
********** Sector 0 [FFC00000] (0-unprotect, 1 protect):0等只是显示FLASH的扇区分布,
并没有进行烧写,只有看到Read Fill Erase Program End信息才表示在进行烧写。
在minicom中用ctrl+a,然后按b,接着用上下箭头,可以翻看前面的信息,从而查看出了什么错误。例如,TFTP连接失败的信息就要前翻来查看,信息如下:
Unable to locate 192.168.1.46
Errors in TFTP download.
Read 0 bytes (0 blocks)
这就要查看宿主机(即TFTP服务器配置是否成功)。
工作机制:
./flash调用hhco.gdb脚本对CPU进行初始化,(sys-init)
然后指定
target bdm /dev/bdmcf0
load 即下载一个小软件(tftp.elf,约80K)到板子的RAM中,并用c(continue)命令让它跑起来,这时CPU完全由这个小软件接管。这个小软件跑起来后初始化以太网及TFTP协议栈,然后它运行TFTP客户端,从内存中0x00200004(2M+4处)读取gdb脚本放在此处的TFTP服务器IP地址,并从该IP的TFTP服务器PC下载image.bin到板子内存1M地址处,再将其烧写到FLASH上,重启板子,这时引导起来的就是LINUX了。
问:烧写完毕后最后会报如下错误:
Program received signal SIGBUS, Bus error.
0x488 in asm_exception_handler ()
1: x/i $pc 0x488
: orib #84,%d0
注意上述信息并非错误,烧写完退出时都要显示这个信息,并不能表示烧写是否成功。烧写成功与否完全要看minicom端的显示信息来判断。
问:运行./flash时报错,内容如下:
./gdb:error while loading shared libraries:libncurses.so.4:cannot load shared object file:No such file or directory.
这是怎么回事呢?
答:这是由于宿主机REDHAT安装时不完全,不支持DEVELOPMENT工具,如gcc,gdb等工具,导致系统没有安装运行这些工具所需的共享库。建议重新完全安装系统。
3、 关于TFTP服务器设置
其实TFTP服务器可以是和板子相连的局域网内任意一台开通了TFTP服务的LINUX PC机,即板子可以从任意一台TFTP服务器下载IMAGE文件并进行烧写,当然首先要检查IP是否匹配及连线是否正确。但若将LINUX宿主机(即用串口线连接的那一台LINUX PC)同时开通TFTP服务,这样就不必占用多台机器。默认完全安装后的REDHAT的TFTP服务是没有开通的,要自己手工开通。
开通宿主机上的TFTP服务,对于REDHAT6.x,可以在宿主机上:
vim /etc/inetd.conf
查找tftp,若发现前面有#就表示这一行被注释掉了,即服务没有打开,去掉#就打开了TFTP服务,然后重启宿主机即可。
对于REDHAT7.2,则在宿主机上执行setup,选择System services,将其中的tftp一项选中(出现 [*]表示选中),并去掉ipchains和iptables两项服务(即去掉它们前面的*号)。然后还要选择Firewall configuration,选中No firewall。最后,退出setup,执行如下命令以启动TFTP服务:
service xinetd restart
配置完成后,建议简单测试一下TFTP服务器是否可用,即自己tftp自己,例如在宿主机上执行:
cd /
cp /HHCO5272-R1/image/image.bin /tftpboot/
tftp 192.168.2.32
tftp>get image.bin
若出现如下信息:
Received 634732 bytes in 0.7 seconds
就表示TFTP服务器配置成功了。若弹出信息说:Timed out,则表明未成功,需要按照上述步骤重新检查一遍。
或者用如下命令查看tftp服务是否开通:
netstat -a|grep tftp
完成上述配置工作后,就可以开始使用该以太网烧写工具了。
4、BDM与重启问题
安装bdm驱动模块,以HHCO5272-R1为例:
1. cd HHCO5272-R1/bdm
2. insmod linux-bdm.o 【可将此句写入/etc/rc.d/rc.local文件中,如:
/sbin/insmod /HHCO5272-R1/bdm/linux-bdm.o,这样每次PC启动都会自动执行,否则每次启动宿主机都要执行这一句】
3. ./MAKEDEV 【在/dev/下创建设备bdmcf0,只要执行一次】
【注意】
在insmod时有可能出现.o文件与内核版本不匹配现象,这时只要重编该模块驱动即可。具体请参阅下面第31条。
还有,BDM线的长度不能加长,否则使用时会报"bdm not open"。
问:板子刚拿到,一加电就不启动,运行指示灯不闪,minicom什么信息也没有,怎么回事?
答:这是因为插了BDM卡,若不插BDM卡,板子就可以正常启动了。
从软件使用者的角度来说,插了BDM后,核心板的reset键就失效,所以板子加电后没有收到reset信号,所以板子不能启动。这时要板子让板子上的软件系统启动运行,可以先执行一下chk,然后立刻退出则板子就会自动重启。
cd chk
./chk
>>>x
当板子启动后,可以通过minicom接收命令,则要重启就直接在minicom下键入reboot即可重启,这时按reset键无效。若不插BDM则板子的reset按键是有效的。
有时,,执行chk时报错:
device busy表示有其它程序在使用BDM
用ps -A
看看是否有其它程序在使用BDM,例如gdb(即gdbtftpflash下的./flash)或者chk,
若提示device not found,则表明没有安装驱动linux-bdm.o
5、板子死了?
判断板子是否死机的标准不是minicom是否可以输入等,最可信的标准就是看核心板上的运行指示灯是否还正常的闪烁,若不再闪了,必死无疑。有时串口可以打印输出信息,但用户却无法输入命令,这经常是由于操作者手上静电太高导致串口工作异常。这时建议给板子断电,把核心板和底板以及串口线等都重新拔插一下再加电重启试试;或者换一台PC,或者用telnet使用网络终端。
6、TFTP下载与RAM版本问题
用bootloader下载RAM内核,总是报错:
KERNEL: Bad trap from supervisor state, vector=4
CURRENT PROCESS:
COMM=swapper PID=1
TEXT=00100000-00156e6c DATA=00000000-0016685c BSS=0016685c-0017d7f0
USER-STACK=00000000 KERNEL-STACK=0021d000
......
答:
因为TFTP采用的是udp传输的,
而我们的板子是通过一个小HUB跟局域网联在一起的,
可能在传输中有些包丢失了,或者是在传输中有些
数据出现错误,因此出现RAM版的不能用。后来单独把板子跟一台计算机的网卡相连,
或者都接到同一个HUB上,就不会有问题了。
有时烧写完后板子启动时回出现如下错误:
Unable to open an initial console.
这种现象有多种原因可能导致出现,其中就有可能是这里所提的烧写错误所致。
7、BUSS ERROR的问题
记住出错时打印出的PC指针值,将linux/linux
用m68k-elf-objdump -D linux >tt
vim tt
反汇编后查看该PC地址处在什么函数调用内,
从而作进一步定位。
用chk单步跟踪,(fp命令)
建议阅读chk源代码,可以根据自己的需要定制自己的调试命令。
所有可用的BDM操作都在bdm/下的一个bdm.h文件中。
8、硬件扩展
问:CN5的复用脚p19,p23,p25是系统宽度总线控制信号,留给 用户,系统没有用吗?那么复位期间如何确定总线宽度?
回答:上电的时候作为系统宽度总线控制信号;但是上电结束开始运行之后,就变成SPI信号线了,您可以使用。问:普通usb口只用d+ d_两条信号线,而5272增加了转出去的信号线:usb-rp,usb-tp....(pa{6:0}),用户能否用其第一功能?
回答:5272本身带有usb收发器,5272可以让您选择是否选择外部usb收发器(通过配置内部寄存器来选择)。建议选用外部USB收发器。注意!是收发器,不是控制器!USB控制器是在5272里面的。
【注】cn5,cn6只是做简单的扩展,而你可以通过自己设计底板(华恒底板都已经在手册里面有原理图了!),从而在cn1,,cn2,cn3使用信号。cn1,cn2,cn3上的管脚对应的信号原理图上都有。其中地址线在cn1的1~45(奇数脚),数据线在cn2的2~64(偶数脚)。ddat[3:0]和DTEA都在cn3上,是用于接BDM口的。具体使用请参考5272处理器手册。
USB[3:1]管脚是从一个外接的USB收发芯片pdiusb11上接出的。而TX_P~RX_N是以太网隔离变压器的输出线。cpuclk的频率为66Mhz,无驱动。
关于系统I/O:
MCF5272共提供48个通用IO引脚,它们可以分为3个16位PORT,即PORTA、PORTB、PORTC。其中PORTA,PORTB对应的控制寄存器来实现与其它信号引脚的复用。Port C没有PCCNT寄存器,它只能在工作为外总线16位模式时作为16位的I/O口线使用,即它与D[0~15]复用,只有16位外总线模式时,因为只用高16位D[16~31],这时D[0~15]就为PC[0~15]。另外的一个端口Port D不能被用做I/O线。
系统剩余GPIO资源(以HHCO5272-R1为例):
PA7~PA14(8根引脚);PB4~PB7(4根引脚);
若去掉USB接口,则PA[6:0]可用(7根引脚)。
所以一般的去掉USB,通用IO引脚可用的有8+4+7=19根。
串口0所占用的PB[3:0](4根引脚),其中PB0/1为URT0_TxD/URT0RxD,PB2/3为CTS/RTS。
若去掉串口0的CTS/RTS,又多2根,即可19+2=21根。
若整个去掉串口0,又多两根,即最多21+2=23根。
9、串口2检测
将板子的串口2和宿主机的串口1连接起来,在宿主机的一个TTY启动minicom,另一个TTY执行chk
./chk
>>>fr colilo2.bin e00000 【下载到RAM中0x00e00000地址处】
>>>fg xx e00400 【从0x00e00400地址处开始执行,加上0x400是要跳过前面1024个字节的ramvect】
代码下到板子RAM中,然后用fg将其激活跑起来后,CPU就由那个代码控制了,这时就不要在chk中输入任何命令了。不要在chk中执行退出(x),否则板子立即reset,下载到RAM中的内容就丢了。【注意】没有fg之前,核心板上运行指示灯(LEDK1)是不亮不闪的。一旦fg xx e00400后就应该亮闪起来。这是判断下载软件是否跑起来的唯一标准,与串口是否打印完全无关。若minicom没有打印信息,就只能说明串口2是坏的。
10、开发/使用注意
◇无论编译内核还是自己的某一个应用程序都必须在uClinux目录下进行编译make,在其它目录下编译都会报错。◇当板子插了网线时,核心板上的百兆以太网连接和碰撞指示灯(LEDL2)就会亮起来,若这时板子ping一台PC机或者一台PC机ping板子的话,LEDL2灯就会闪烁起来。若LEDL2不亮,可能是以太网插口接触不好所致,用力插紧即可。
◇HHCO5272-R1提供了串口2可用于作MODEM拨号,需要把uClinux/user/pppd/、chat/和diald/三个目录加入编译,并编写正确的拨号脚本和配置文件。华恒提供完备的PPP拨号软件包。
◇HHCO5272-R1板上采用的是ROMFS的文件系统,它是只读的。可以扩展支持JFFS/JFFS2文件系统,它可读可写。华恒提供完备的JFFS/JFFS2支持软件包。
◇在用ddd-5272调试应用程序时,若看不到代码,是因为没有加-g的编译参数;若代码能看到,但是是在run的时候提示BDM not Open! 这是run命令的问题,运行时不要用run,而用continue命令,即c。
◇MCF5272不支持外设的DMA,它只有内部DMA,为FEC所用。
◇要在系统启动时自动执行用户自己的应用程序,就必须修改uClinux/romfs/etc/rc文件,这是个启动的脚本文件,它的作用就相当于WINDOWS98下的autoexec.bat,这个脚本文件中的每一句都是在shell下可直接执行的命令。例如用户移植了WEB SERVER boa,要启动就执行boa,就在这个rc文件的最后加一句boa&,然后到uClinux下执行make,然后./flash烧写板子即可。
◇板子IP的修改:
可以用ifconfig/route(2.4内核不需要route命令)命令来修改板子启动后的IP,但这个修改在重启后无法保存。因为板子IP的指定是在其ROMFS文件系统的/etc/rc文件中指定的,如下:
ifconfig eth0 192.168.2.112
route add -net 192.168.2.0
要永久的改变板子的IP,就要在宿主机上修改uClinux/romfs/etc/rc文件中对应的IP设置内容,然后在uClinux下重新编译,然后重新烧写。但这样做的问题在于无法做到动态修改IP并保存。要实现这种功能,就必须在板子上实现文件存储功能。实现的机制有许多,如flatfsd、JFFS/JFFS2文件系统等。对于板子上的存储机制,华恒提供完整的JFFS/JFFS2软件包销售,可大大加快用户的开发进度及产品上市时间。
11、启动失败
客户问:HHCF5272-LCD-IDE-R1套件中:把image.bin写到flash后,应该可以从板子启动进入uClinux中,但是它会出现一个错误是kernel panic attempted to kill init!
答复:都是usb_init惹的祸
这样修改:
vim uClinux/linux-2.4.x/drivers/char/mem.c
去掉usb_init一行,然后重新编译烧写就没有问题了。
12、串口2问题
◇客户问:
一个简单测试程序,向/dev/ttyS0发送数据,波特率设为38400,在PC机下顺利接收到。利用5272开发板上的串口1发送,另一端在minicom下,设为相同的波特率,能接收到数据,但是相同的程序,只修改为/dev/ttyS1确收不到任何数据,
答复:经次测试,发现是缺少了对串口2占用的PD口的初始化。
对于HHCF5272-R1,更新的文件sysinit.c,
将它覆盖到uClinux/linux/arch/m68knommu/platform/5272/目录下,
重新编译烧写即可。
对于HHCF5272-LCD-IDE-R1 ,将附件colilo.bin覆盖到uClinux/colilo/目录下,重新编译烧写即可。
13、扩大RAM盘的方法
有客户问到增大HHCF5272-R1板子上的/var/目录大小的问题,
这个目录是RAM盘,可读可写。
它的大小是在uClinux/romfs.mk中指定的:
例如要指定为2M大小的RAM盘:
则加入如下一句:
RAMFSy = $(USER)/ramimage/ramfs2048.img
即可。
【注意】
RAM盘太大好像会引起系统死机。
14、关于编译重烧启动时出现“unable to open an initial console”问题
解决办法:
修改uClinux/vendor/HHtech/M5272/Makefile,
将其中的genromfs改为/usr/local/bin/genromfs即可。
15、关于BDM驱动“ kernel-module version mismatch”问题
现象:在bdm/driver/linux下执行make后,insmod linux-bdm.o时出现如下错误:
linux-bdm.o: kernel-module version mismatch
linux-bdm.o was compiled for kernel version 2.4.17
while this kernel is version 2.4.5.
解决办法:
1、要注释MODVERSIONS=-DMODVERSIONS
即:#MODVERSIONS=-DMODVERSIONS
2、修改bdm/driver/linux/Makefile中的CFLAGS一行为如下:
CFLAGS = -Wall -g -O -pipe -I/usr/src/linux-2.4/include -I.. -DMODULE -D__KERNEL__ $(MODVERSIONS)
这样编译就不会有内核版本不匹配的问题了
16、关于系统启动时数据总线宽度的问题
5272复用脚L5 QSPICLK/BUSW1 和M5 SPI_CS0/BUSW0
在HHCO5272-R1中用作 QSPICLK 和 SPI_CS0 功能(CN1
pin93 ,CN1 pin95) , 而只有在CPU复位时它们才作为BUSW1:BUSW0组合决定CS0内存数据宽度。这个是不需要配置的。
具体参见MCF5272手册:P14-2页,即P14.3.1节介绍。
华恒板子的BUSW1:BUSW0组合为[1:0],即16位模式,因此刚加电时接CS0的FLASH0的数据总线宽度为16位模式。
同理,N4 QSPI_DOUT/WSEL 在HHCO5272-R1中用作
QSPI_DOUT 功能(CN1 pin89 ),
WSEL也只是CPU复位时生效,华恒板子此脚接下拉电阻,表示为0,因此为32位模式。此后都作为QSPI_DOUT信号使用,不需配置。
17、关于REDHAT7.3/8.0/9.0等高版本宿主机上NFS配置的问题
目前主要的问题在于完全安装的这些REDHAT版本没有提供linuxconf工具软件,因此无法配置NFS服务器。不过这个问题很容易解决,这需要到http://ftp.boe.tcc.edu.tw/tnc/firewall/下载一个名为:linuxconf-1.25r7-3.i386.rpm的RPM包,然后在REDHAT LINUX机器上执行:
rpm -i linuxconf-1.25r7-3.i386.rpm (6,995KB)
这个安装比较耗时,安装完毕后,立即就可以使用linuxconf工具了
18、关于定时器使用的问题
对于秒级以上的应用程序:
alarm(sec)
在sec秒以后就会产生一个sigalrm信号。
注册一个自己的处理函数来处理这个信号就可以实现。
对于ms级的定时,usleep为us级的延时,但无法提供中断。
MCF5272提供四个定时器,uClinux只使用了其中一个:
uClinux/linux/arch/m68knommu/platform/5272/config.c中:
void coldfire_timer_init(void (*handler)(int, void *, struct pt_regs *))
{
volatile unsigned short *timerp;
volatile unsigned long *icrp;
/* Set up TIMER 1 as poll clock */
timerp = (volatile unsigned short *) (MCF_MBAR + MCFTIMER_BASE1);
timerp[MCFTIMER_TMR] = MCFTIMER_TMR_DISABLE;
timerp[MCFTIMER_TRR] = (unsigned short) ((MCF_CLK / 16) / HZ);
timerp[MCFTIMER_TMR] = MCFTIMER_TMR_ENORI | MCFTIMER_TMR_CLK16 |
MCFTIMER_TMR_RESTART | MCFTIMER_TMR_ENABLE;
icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
*icrp = 0x0000d000; /* TMR1 with priority 5 */
request_irq(69, handler, SA_INTERRUPT, "ColdFire Timer", NULL);
}
void config_BSP(char *commandp, int size)
{
memset(commandp, 0, size);
mach_sched_init = coldfire_timer_init;
mach_tick = coldfire_tick;
mach_trap_init = coldfire_trap_init;
}
uClinux/linux/arch/m68knommu/kernel/time.c:
void time_init(void)
{
unsigned int year, mon, day, hour, min, sec;
extern void arch_gettod(int *year, int *mon, int *day, int *hour,
int *min, int *sec);
arch_gettod (&year, &mon, &day, &hour, &min, &sec);
if ((year += 1900) < 1970)
year += 100;
xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
xtime.tv_usec = 0;
if (mach_sched_init)
mach_sched_init(timer_interrupt);
}
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
void timer_interrupt(int irq, void *dummy, struct pt_regs * regs)
{
/* last time the cmos clock got updated */
static long last_rtc_update=0;
/* may need to kick the hardware timer */
if (mach_tick)
mach_tick();
do_timer(regs);
/*
* If we have an externally synchronized Linux clock, then update
* CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
* called as close as possible to 500 ms before the new second starts.
*/
if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
xtime.tv_usec > 500000 - (tick >> 1) &&
xtime.tv_usec < 500000 + (tick >> 1)) {
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
}
}
add_timer函数在uClinux/linux/kernel/sched.c中定义
void add_timer(struct timer_list *timer)
{
unsigned long flags;
save_flags(flags);
cli();
#if SLOW_BUT_DEBUGGING_TIMERS
if (timer->next || timer->prev) {
printk("add_timer() called with non-zero list from %p
",
__builtin_return_address(0));
goto out;
}
#endif
internal_add_timer(timer);
#if SLOW_BUT_DEBUGGING_TIMERS
out:
#endif
restore_flags(flags);
}
static inline void internal_add_timer(struct timer_list *timer)
{
/*
* must be cli-ed when calling this
*/
unsigned long expires = timer->expires;
unsigned long idx = expires - timer_jiffies;
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
insert_timer(timer, tv1.vec, i);
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
insert_timer(timer, tv2.vec, i);
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
insert_timer(timer, tv3.vec, i);
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
insert_timer(timer, tv4.vec, i);
} else if (expires < timer_jiffies) {
/* can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
*/
insert_timer(timer, tv1.vec, tv1.index);
} else if (idx < 0xffffffffUL) {
int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
insert_timer(timer, tv5.vec, i);
} else {
/* Can only get here on architectures with 64-bit jiffies */
timer->next = timer->prev = timer;
}
}
在uClinux/linux/arch/m68knommu/platform/5272/config.c中的coldfire_timer_init中增加对TMR2初始化设置和中断申请。然后自己写TMR2的定时器中断服务程序,
这个代码可放在config.c中,也可加到OS代码中:
uClinux/linux/arch/m68knommu/kernel/time.c:
这个文件中有LINUX使用TMR1的定时器中断服务程序代码:
void timer_interrupt(int irq, void *dummy, struct pt_regs * regs)
它是用来给LINUX OS提供时钟的,自己的定时中断就不必做这个工作了,让它10ms进一次中断,在自己的TMR2中断程序中做自己的工作就可以了。当然这里的处理一定要快。
19、关于C++支持
华恒HHCF系列开发套件光盘提供的软件系统支持C++代码,下面给出一个编译参数的例子:
m68k-elf-g++ -m5307 -msep-data -Wl,-elf2flt -o test test.cpp -lstdc++ -lc -lgcc
【注意】
这里没有加入库的路径和头文件的路径:-L和-I,请用户在编译时自己指定绝对路径。
下面介绍一下将C++代码统一到user目录下像普通应用程序一样参与编译:
C++程序在5272上的编译,首先要修改uclinux目录下的config.arch文件,将其中的
CXX = $(CROSS_COMPILE)g++
改为:
CXX = $(CROSS_COMPILE)g++ $(CPUFLAGS) -DCONFIG_COLDFIRE
再将其中的
CXXLIBS = $(LDPATH) $(LIBSTDCPP) $(LIBIOSTREAM) $(LIBIO) $(LIBIBERTY)
$(LIBC) $(LIBGCC)
改为:
CXXLIBS = $(LDPATH) $(LIBSTDCPP) $(LIBIBERTY)
$(LIBC) $(LIBGCC)
然后模仿以下的Makefile书写:
EXEC = test
OBJS = test.o
all: $(EXEC)
$(EXEC): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(CXXLIBS)
romfs:
$(ROMFSINST) /bin/$(EXEC)
clean:
-rm -f $(EXEC) *.elf *.gdb *.o
即可。
说明:uclinux(uclibc)现在对iostream的支持还不够,不能使用iostream库。
【注】
华恒客户可发信到support@hhcn.com索取测试代码。
20. 关于进程间通信IPC的问题
uClinux下system V的进程间通信IPC机制不可用!
(现在已经可以用了,在make menuconfig中内核配置菜单中选择General setup --->,选中:
[*] System V IPC)
1、信号:但要求知道另一个进程的pid号:
进程间用信号通信时,用kill发送信号,但需要指定进程号,
可以这样作:
接收信号的进程把自己的进程号写到RAM盘(如/tmp目录)下的一个文件中,发送信号的进程从这个文件中读取这个进程号即可成功的实现信号发送。
这里是直接指定内存地址(当然做好是在靠后的未用地址),然后用一个进程启动另一个进程,这两个进程之间的进程号恰好相差1,这样就可以在两个进程之间使用信号了。
2、共享文件:两个进程访问同一个文件,用文件锁实现互斥访问。
要保护/tmp/a文件,
在一个进程中每次要fopen前,先判断是否有a.lock文件锁,
若有就等待。
若没有,则创建该文件锁用open(O_CREAT|0_EXECL,
S_IRUSR|S_IWUSR)创建。
另一方法:在RAM中建立共享区(相当于文件)
在一个进程里面分配一个内存快,然后将内存快的地址传给
由vfork和exclp创建的另一个进程,这样这两个进程就可以对这块内存进行读写了,对共享内存的互锁是通过发信号实现的。
一共有4个进程,分别一个启动一个,这样他们的进称号恰好是连续的,一就是说一个进程知道任意一个它想发信号的进程号。这样就可以在所有进程间发信号了,共享内存是先分配内存块,然后传递已经有操作系统分配好的共享内存地址,管道使用的pipe创建,然后使用vfork创建另一个进程,并传递管道的读、写fd,就可以读写管道了。
3、消息队列
含消息队列的程序编译程序时会提示没有定义符号--"msgctl,msgsnd,msgget,msgrcv";
在/uClinux/linux/ipc/msg.c里有系统调用的源代码
-"sys_msgget,sys_msgctl,sys_msgsnd,sys_msgrcv";
再查看系统uClinuxlinuxarchm68knommukernel/sys_m68k.c里ipc系统调用里调用--"sys_msgget,sys_msgctl,sys_msgsnd,sys_msgrcv"内核函数;
所以推断是档案libc.a里没包含"msgctl,msgsnd,msgget,msgrcv"原形函数调用;
做法:
1)make xconfig;选取SYSTEM V IPC
2)在/uClinux/lib/libc/生成目录./msg
3)添加文件msgctl.c,msgsnd.c,msgget.c,msgrcv.c,写相应的makefile;
4)修改上一级目录里makefile,添加msg目录;->加函数到libc.a里;
5)make dep;
6)make;
注意:在系统机上发消息队列的长度和接收消息队列的长度可以不等长;华恒内核中长度好象必须相等,(最大不要超过BUFSIZ=1024)。
消息队列可以对其进行编址,使得各个进程各取所需,并且可以使消息队列的个数变少。但是如果在消息队列被删除后还有N个要发送或检索的进程,会发生问题
信号量的做法一样!
21、关于应用程序中使用GPIO的问题
关于GPIO,要设置三个寄存器,分别是:以PB口为例
1)PBCNT,绝对各个引脚的复用信号是否为IO
2)方向寄存器,每个引脚是输入还是输出。
3)数据寄存器,进行IO的输入输出。
给个例子代码:(注意:这是在用户应用程序中的代码,不是内核态代码)
*(volatile unsigned long *)0x10000080 &=0xff003fff; // PACNT 14-23 bit is cleared,use PA7-11
*((volatile unsigned short *)0x10000084 |= 0x0f80; // PADDR 7-11 bit is setted,use as output
*(volatile unsigned short *)0x10000086 = 0x0080; // PA7为0,PA8为0
*(volatile unsigned short *)0x10000086 = 0x0180; // PA7为0,PA8为1
22、关于管道的使用
下面是一个例子,在一个进程中通过管道启动另一个进程test。
#include
#include
int main(void)
{
FILE *fd;
int waitstat;
char buf[20];
if((fd=popen("/bin/test","r"))==NULL)
printf("popen error!
");
wait(&waitstat);
//pclose(fd);
if(fgets(buf,15,fd)==NULL)
printf("fgets error!
");
printf("%s",buf);
return 0;
}
代码移植到uC下时应在popen后加入,pclose(fd)或wait(&waitstat)。因为uClibc中popen的实现与X86下的不同,它使用了vfork(PC上是用fork)必须等子进程可靠结束关闭文件后才执行对文件的下一步读写。
23. 关于ColdFire系列开发板的uClinux内核版本
华恒ColdFire系列开发板提供的嵌入式uClinux内核版本目前主要有两种:2.0.38和2.4.x,
其中只有HHCF5272-R1还保留了2.0.38的内核版本,其它套件全部为2.4内核。目前HHCF5272-R1也可以提供2.4的内核。
华恒版本的uClinux和从网上uClinux.org down下来的版本的区别在于:
1)网上开源的版本只提供了RAM版内核,不提供ROM版本,呵呵,这样做我想可能就是为了限制商业化用户吧,因为RAM版本还需要bootloader来引导(但行业内,bootloader基本上大家都不提供源代码),不能直接启动,根本无法作为商业产品使用,因此也就只能给爱好者折腾着玩玩而已。华恒替客户完成了这部分ROM化的工作。
2)华恒以本地化器件提供的硬件板卡,因此要针对自己的硬件修改系统启动代码、硬件设备驱动BSP,并相应的提供下载、烧写等工具软件,这些都要自己移植,修改或者完全自己定制。没有这些辅助工具,空有uClinux的tar包,是根本无法进行开发调试的。
3)本地化全中文技术手册及相关的技术支持(论坛)。
24. 关于不同内核版本间应用程序移植的问题
应用程序的移植一般的和内核版本是没有任何关系的,当然有的应用程序是需要内核支持的,例如pppd等。在2.0.38内核和2.4内核之间互移应用程序,唯一要注意的就是Makefile的写法:
其实区别就一句话,就是2.4下面,它把elf2flt作为gcc的一个参数一步完成了,而2.0.38还要分为两步完成,体现在Makefile上就如下:
对于2.0.38:
$(LD) $(LDFLAGS) -o $@.elf $(OBJS) $(LDLIBS)
$(CONVERT)
对于2.4.x:
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
25、关于外设中断的问题
MCF5272处理器提供了6路外部中断以供外设控制芯片使用。在MCF5272的启动代码中(sysinit.c代码中)屏蔽了所有的内部和外部中断,因为用户在扩展使用自己的中断源设备时,在该设备的驱动初始化代码(如open函数中)中就必须手工打开允许这个外部中断源。屏蔽和允许一个中断源都是通过设置ICRn寄存器来实现的。
在对应的PI位写1,而后面的三位IPL位都为0就表示屏蔽该中断源。
在对应的PI位写1,后面的三位IPL位写001~111就表明打开运行该中断源,并设置其中断优先级为1到7级,级别越高,优先级越高。
switch(irq)
{
case 1:
*(volatile unsigned long *)(0x10000020) |= 0xb0000000;
break;
case 3:
*(volatile unsigned long *)(0x10000020) |= 0x00b00000;
break;
case 4:
*(volatile unsigned long *)(0x10000020) |= 0x000b0000;
break;
case 5:
*(volatile unsigned long *)(0x1000002c) |= 0x0b000000;
break;
}
26、关于中断的问题
在每个使用中断的设备的驱动初始化代码中,都首先要打开中断,因为启动代码把所有中断都禁止(mask)了,根据CPU手册,在ICRn对应的PI位写1,而后面的三位IPL位都为0就表示屏蔽该中断源。即将对应ICR中对应的字节设置为8。例如启动时,ICR1就被设置为:0x88888888。
打开中断时,则在ICRn对应的PI位写1,后面的三位IPL位写001~111就表明打开运行该中断源,并设置其中断优先级为1到7级,级别越高,优先级越高。例如MCF5272的几个内部模块所使用的内部中断:
1)TIMER:在ICR1对应位设置0xd,即priority == 5(2.0.38用的是TMR1,2.4内核用的是TMR4)
2)FEC:在ICR3对应位设置0xd,即priority == 5
3)FEC要工作的同时还要使用MII(占用INT2),在ICR1对应位设置0xd,即priority == 5
4)UART:在ICR2对应位设置0xe,即priority == 6
样例代码如下:
FEC:fec.c中int __init fec_enet_init(struct net_device *dev)
volatile unsigned long *icrp;
icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR3);//FEC
*icrp = 0x00000ddd;
icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);//MII
*icrp = (*icrp & 0x70777777) | 0x0d000000;
TMR4:在uClinux/linux-2.4.x/arch/m68knommu/platform/5272/config.c中对TIMER的初始化如下:
void coldfire_timer_init(void (*handler)(int, void *, struct pt_regs *))函数中有如下代码:
icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
*icrp = 0x0000000d; /* TMR4 with priority 5 */
request_irq(72, handler, SA_INTERRUPT, "ColdFire Timer", NULL);
UART1/UART2:在uClinux/linux-2.4.x/drivers/char/mcfserial.c中,有如下代码对UART进行中断初始化:
static void mcfrs_irqinit(struct mcf_serial *info)
volatile unsigned long *icrp;
volatile unsigned long *portp;
icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR2);
switch (info->line) {
case 0:
*icrp = 0xe0000000;
break;
case 1:
*icrp = 0x0e000000;
break;
此外,MCF5272相应外部中断后,并不会自动的清楚该中断的pending位,这样用户就必须在自己的中断处理函数的末尾自己来替处理器完成这一工作。例如:对于外部中断3
*(volatile unsigned long*)(MCF_MBAR+0x20) |= 0x00800000;
27、应用程序中如何定时产生信号?
这个问题就相当于实现WINDOWS下的WM_TIMER功能,定时给应用程序发送消息
例如:要求每一秒产生一个信号,去打印一句test。本来signal/alarm只能一次性工作,现在要让它循环起来,每一秒都产生信号,就要再其信号处理函数中对其进行重置。
void test()
{
signal(SIGALRM,test);//重置
alarm(1);
printf("test
");
}
main()
{
signal(SIGALRM,test);
alarm(1);
for(;;)
{/*主函数处理部分*/
}
}
28、应用程序的编译参数观察
用户在编译自己的应用程序时,也是要在uClinux下执行make,屏幕一滚就过去了,SHIFT+PAGEUP也翻不了那末多,其实自己的应用怎么编译的也不清楚。
在uClinux下执行make >&t,则自动生成临时文件t,它记录了整个编译过程及每步编译的详细参数,对于理解编译工具的使用及其参数非常有帮助。(整个的编译过程详细分析请参见手册2.1.3节),举例而言,以应用程序uClinux/user/inetd和ping为例:
对于uClinux-2.0.38下的写法如下:
/HHCF5272-R1/uClinux/tools/m68k-elf-gcc -m5200 -Wa,-m5200 -DCONFIG_COLDFIRE -Dl
inux -D__linux__ -Dunix -DEMBED -O2 -msoft-float
-I/HHCF5272-R1/uClinux/tools/gcc-include -I/HHCF5272-R1/uClinux/lib/libc/include
-I/HHCF5272-R1/uClinux/lib/libm
-I/HHCF5272-R1/uClinux/vendors/include -fno-builtin -DSERVICES="/etc/services
" -DINETD_CONF="/etc/inetd.conf" -c -o inetd.o inetd.c
对于uClinux-2.4.17下的编译全部以gcc为前台,连ld也被隐藏到后台了:
m68k-elf-gcc -m5307 -DCONFIG_COLDFIRE -Os -g -fomit-frame-pointer -Dlinux
-D__linux__ -Dunix -D__uClinux__ -DEMBED
-I/HHCF5272-R1/uClinux/lib/libc/include -I/HHCF5272-R1/uClinux/lib/libm
-I/HHCF5272-R1/uClinux -I/HHCF5272-R1/uClinux/linux-2.4.x/include
-fno-builtin -msep-data -c -o ping.o ping.c
m68k-elf-gcc -m5307 -DCONFIG_COLDFIRE -Os -g -fomit-frame-pointer
-Dlinux -D__linux__ -Dunix -D__uClinux__ -DEMBED
-I/HHCF5272-R1/uClinux/lib/libc/include -I/HHCF5272-R1/uClinux/lib/libm
-I/HHCF5272-R1/uClinux -I/HHCF5272-R1/uClinux/linux-2.4.x/include
-fno-builtin -msep-data -Wl,-elf2flt -o ping ping.o
-L/HHCF5272-R1/uClinux/lib/libc/. -L/HHCF5272-R1/uClinux/lib/libc/lib
-L/HHCF5272-R1/uClinux/lib/libm -L/HHCF5272-R1/uClinux/lib/libnet
-L/HHCF5272-R1/uClinux/lib/libdes -L/HHCF5272-R1/uClinux/lib/libpcap
-L/HHCF5272-R1/uClinux/lib/libssl -lc
-L表明后面为所要链接的libc库的路径。库路径可以有多个(对于一个应用程序而言,很多库并没有用),因此可以看到有多个-L参数;
-I参数表明后面所跟的为C语言INCLUDE头文件的路径,这个路径可以有多个,因此可以看到有多个-I参数;
-m5200(-m5307)为处理器相关编译参数,2.4下采用了-m5307,这其实对于应用程序而言没有任何区别;
-c后面为所要编译的C文件;
-o后面为这行编译操作的目的,即这行编译完毕后要生成的文件;
-Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce:这些都是编译参数;
29、FLASH上数据保存
鉴于目前发行的开发板中的uClinux均采用ROMFS作为其根文件系统,因此其目录大多是不可写的。只有/var,/tmp是RAM盘可写,但板子一掉电里面的内容就丢失了,因此只能作临时文件保存,无法永久的保存数据,例如配置文件等。下面大概介绍一下几种FLASH上保存配置的方法:
1、对于简单的数据,小的配置文件等,而且不是非常频繁的(例如一分钟写10次)写入的,可以直接自己在FLASH的空余处(例如第二片FLASH上)划出一块区域,以自己定义的方式写入,并在板子启动时自动读出。华恒提供了这样的样例代码,即user/memtools/,其详细介绍在手册第二章“FLASH扇区保存IP地址”一节。这样作最大的好处是用户的控制程度最大,形式最灵活,保存的可以是自定义的数据,可以不是文件的形式。而下面的几种方式都是要求文件的形式保存,无法处理自定义的数据保存。
2、对于比较多的配置文件,一般的都先写在RAM盘中,然后选择保存,一次性写入FLASH的某几个扇区。这时就可使用flatfsd软件,它的使用需要内核的配合支持,即要在blkmem.c中为其指定保存数据的几个扇区的起始/结束地址。这中方式也不能适应非常频繁的写入。
使用说明:将要保存的文件放在板子的RAM盘目录/etc/config目录下,因为uClinux的根目录/是建于FLASH上的romfs文件系统,是不可写的,只有几个RAM盘才可写,可用mount命令查看:
/etc> mount
rootfs on / type rootfs (rw)
/dev/rom0 on / type romfs (ro)
/proc on /proc type proc (rw)
/dev/ram0 on /var type ext2 (rw)
/dev/ram1 on /etc/config type ext2 (rw)
/dev/mtdblock0 on /sbin type jffs2 (rw)
要保存的时候,就在minicom里面执行
killall -10 flatfsd
或者在C程序代码里面用vfork+execlp执行这句命令行即可.
这样就会自动将RAM盘中/etc/config目录里面的文件全部保存写入到板子FLASH的扇区里面(具体哪几个扇区是由linux-2.4.x/drivers/block/blkmem.c中指定的,可在板子启动信息里面看到:)
Blkmem copyright 1998 Kenneth Albanowski
Blkmem 2 disk images:
0: FFC77362-FFCCE761 [VIRTUAL FFC77362-FFCCE761] (RO) /*romfs*/
1: FFFC0000-FFFFFFFF [VIRTUAL FFFC0000-FFFFFFFF] (RW) /*flatfsd*/
能够保存的机制是由如下文件的一行保证的:
/etc> cat inittab
inet:unknown:/bin/inetd
flt:unknown:/bin/flatfsd
这样就保证系统启动后系统进程里面有flatfsd进程永远在运行,才能保证接收兵处理用户发来的数值为10的信号SIGNAL:
/etc> ps
PID PORT STAT SIZE SHARED %CPU COMMAND
1 S 37K 0K 0.0 init
2 S 0K 0K 0.0 keventd
3 R 0K 0K 0.2 ksoftirqd_CPU0
4 R 0K 0K 22.7 kswapd
5 S 0K 0K 0.0 bdflush
6 S 0K 0K 0.0 kupdated
7 S 0K 0K 0.0 mtdblockd
16 S 0K 0K 0.0 jffs2_gcd_mtd0
30 S0 S 85K 0K 0.0 /bin/sh
31 S 38K 0K 0.0 /bin/inetd
32 S 53K 0K 0.0 /bin/flatfsd
33 S 190K 0K 2.2 /bin/diald -f /etc/config/diald.pppoe1
34 S 207K 0K 0.0 /bin/dhcpd -d -cf /etc/config/dhcpd.conf eth0
1220 S 149K 0K 0.0 /bin/pppd -detach mtu 1454 mru 1454 file /etc/config/pppoe1
1221 S 69K 0K 7.4 /bin/pppoe -I eth0 -T 3000 -m 1412 -p /var/log/pppoe.pid
1241 S 49K 0K 0.0 /bin/telnetd
1242 p2 R 88K 0K 0.0 sh
而板子启动时通过执行/etc/rc里面的flatfsd –r就可以将FLASH上的保存的文件恢复到RAM盘目录/etc/config/下。
/> cd /etc/
/etc> cat rc
hostname mp3server
/bin/expand /etc/ramfs.img /dev/ram0
/bin/expand /etc/ramfs.img /dev/ram1
mount -t proc proc /proc
mount -t ext2 /dev/ram0 /var
mount -t ext2 /dev/ram1 /etc/config
mount -t jffs2 /dev/mtdblock0 /sbin
mkdir /var/tmp
mkdir /var/log
mkdir /var/run
mkdir /var/lock
/bin/flatfsd -r
touch /etc/config/dhcpd.leases
ifconfig lo 127.0.0.1
ifconfig eth0 192.168.2.254 netmask 255.255.255.0
ifconfig eth0:1 192.168.1.2 netmask 255.255.255.0
/bin/ena -e
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
注意:若想修改板子启动后/etc/config里面的文件的内容,例如 板子启动后的IP地址设置文件/etc/config/start文件,需要如下操作:
修改uClinux源代码uClinux-dist/romfs/etc/default/start文件内容,然后重新编译生成image.bin,烧写板子,板子启动后,在minicom里面执行
cp /etc/default/start /etc/config
killall -10 flatfsd
这样下次启动就会自动生效。
3、对于比较频繁的数据保存,就要在板子上建立额外的日志型文件系统JFFS/JFFS2,或者干脆就用JFFS/JFFS2取代ROMFS作根文件系统。这样板子的目录就是可写的,就像硬盘一样,不需要额外的工具来负责将数据写入FLASH。JFFS为2.0.38内核所支持,它不支持JFFS2,JFFS2到2.4内核才被支持,它采用了成熟稳定的MTD技术,因此要比JFFS稳定。这两种文件系统要在uClinux上实现支持并不复杂,但它的实用还需要一些额外的工作,例如烧写工具的配合,新型image.bin编译生成,因为真正产品化的软件是不能允许每次启动后都还要进行许多的手工操作,例如加载文件系统等,板子出厂烧写也要一次完成,而不能还要分多次烧写等等,这些工作都是比较繁杂的,而且没有烧写工具的源代码是无法完成的。华恒提供JFFS/JFFS2整套软件技术。
30、关于2.4内核版本下的RAM版内核的编译
对于2.0.38内核,华恒提供了完备的ram.ld和crt0_ram.S,它们和华恒提供的bootloader可配合使用。 对于2.4.17的内核,从网上直接DOWN的uClinux-Coldfire版本(例如最新版本:uClinux-dist-20020701.tar.gz)是可以直接在华恒bootloader提示符HHCN>下执行readàgo 100000跑起来的。而若客户要从华恒2.4系列开发套件软件系统的基础上改动跑RAM版则需要做一些改动才可以的,例如HHCF5272-LCD-IDE-R1、HHCF5272-2ETH-R2等都是采用的2.4内核。因为华恒板子烧在FLASH上的就是一个RAM版本的内核,经由华恒的bootloader解压复制到RAM中才激活执行的,它的执行方式和readàgo的方式是完全不同的,虽然都是RAM版。要做到这一点,华恒是做了一些改动的,而现在实际上就是要恢复成从网上直接DOWN下来的状态。
1、首先是crt0_ram.S和直接下载的uClinux-Coldfire版本的crt0_ram.S有区别,是经过华恒修改的代码,若直接使用必然无法通过bootloaderàreadàgo 100000而跑起来。因此这时就必须使用网上下载的原始未经改动的crt0_ram.S,其实两个文件的区别很小,仅在于华恒的版本注释了复制romfs的部分代码:
只要把uClinux/linux-2.4.x/arch/m68knommu/platform/5272/HHTECH/crt0_ram.S中的一个#if 0 改为#if 1
下面贴出华恒代码:
_start:
nop /* Filler */
move.w #0x2700, %sr /* No interrupts */
/*
* Setup VBR here, otherwise buserror remap will not work.
* if dBug was active before (on my SBC with dBug 1.1 of Dec 16 1996)
*
* bkr@cut.de 19990306
*
* Note: this is because dBUG points VBR to ROM, making vectors read
* only, so the bus trap can't be changed. (RS)
*/
move.l #VBR_BASE, %a7 /* Note VBR can't be read */
movec %a7, %VBR
move.l %a7, _ramvec /* Set up vector addr */
move.l %a7, _rambase /* Set up base RAM addr */
/*
* Set to 4 meg for the Cadre III board (m5206e).
*/
move.l #MEM_SIZE, %a0
move.l %a0, %d0 /* Mem end addr is in a0 */
move.l %d0, %sp /* Set up initial stack ptr */
move.l %d0, _ramend /* Set end ram addr */
#if 1
/*
* Enable CPU internal cache.
*/
move.l #0x01000000, %d0 /* Invalidate cache cmd */
movec %d0, %CACR /* Invalidate cache */
move.l #0x80000100, %d0 /* Setup cache mask */
movec %d0, %CACR /* Enable cache */
#endif
/*
* Move ROM filesystem above bss :-)
*/
lea.l _ebss, %a1 /* Set up destination */
move.l %a1, _ramstart /* Set start of ram */
#if 1【这里原来是#if 0,即在华恒版本中被注释掉了】
lea.l _sbss, %a0 /* Get start of bss */
lea.l _ebss, %a1 /* Set up destination */
move.l %a0, %a2 /* Copy of bss start */
move.l 8(%a0), %d0 /* Get size of ROMFS */
addq.l #8, %d0 /* Allow for rounding */
and.l #0xfffffffc, %d0 /* Whole words */
add.l %d0, %a0 /* Copy from end */
add.l %d0, %a1 /* Copy from end */
move.l %a1, _ramstart /* Set start of ram */
_copy_romfs:
move.l -(%a0), %d0 /* Copy dword */
move.l %d0, -(%a1)
cmp.l %a0, %a2 /* Check if at end */
bne _copy_romfs
#endif
/*
* Zero out the bss region.
*/
lea.l _sbss, %a0 /* Get start of bss */
lea.l _ebss, %a1 /* Get end of bss */
clr.l %d0 /* Set value */
_clear_bss:
move.l %d0, (%a0)+ /* Clear each word */
cmp.l %a0, %a1 /* Check if at end */
bne _clear_bss
/*
* load the current task pointer and stack
*/
lea init_task_union, %a0
movel %a0, _current_task
lea 0x2000(%a0), %sp
/*
* Assember start up done, start code proper.
*/
jsr start_kernel /* Start Linux kernel */
_exit:
jmp _exit /* Should never get here */
2、ram.ld也要有修改,
MEMORY {
ram : ORIGIN = 0x100000, LENGTH = 0x6e0000
}
从网上直接DOWN下来默认的ORIGIN = 0x20000,这样用我们的bootloader下载下去当然是跑不起来的。以下的SECTIONS 节不需要任何改动。
3、修改vendors/HHTECH/M5272/Makefile
image:
[ -d $(IMAGEDIR) ] || mkdir -p $(IMAGEDIR)
/usr/local/bin/genromfs -v -V "ROMdisk" -f $(ROMFSIMG) -d $(ROMFSDIR)
m68k-elf-objcopy -O binary $(ROOTDIR)/$(LINUXDIR)/linux
$(IMAGEDIR)/linux.bin
#gzip -f $(IMAGEDIR)/linux.bin 【注释掉】
#cat $(IMAGEDIR)/linux.bin.gz $(ROMFSIMG) > $(IMAGE) 【注释掉】
cat $(IMAGEDIR)/linux.bin $(ROMFSIMG) > $(IMAGE)
$(ROOTDIR)/tools/cksum -b -o 2 $(IMAGE) >> $(IMAGE)
[ -n "$(NO_BUILD_INTO_TFTPBOOT)" ] || cp $(IMAGE) /tftpboot
#$(MAKE) -C ../../../colilo 【注释掉】
BSS=`m68k-elf-objdump --headers $(ROOTDIR)/$(LINUXDIR)/linux |
grep .bss` ;
ADDR=`set -- $${BSS} ; echo 0x$${4}` ;
m68k-elf-objcopy --add-section=.romfs=$(ROMFSIMG)
--adjust-section-vma=.romfs=$${ADDR} --no-adjust-warnings
--set-section-flags=.romfs=alloc,load,data
$(ROOTDIR)/$(LINUXDIR)/linux $(ELFIMAGE) 2> /dev/null
4、修改linux-2.4.x/drivers/block/blkmem.c的第125行开始的几句,注释掉defined(CONFIG_HHTECH),修改后代码如下:
#ifdef CONFIG_COLDFIRE
#ifdef CONFIG_TELOS
#undef CONFIG_CARDE3
#define CAT_ROMARRAY
......
其实关键就是去掉CAT_ROMARRAY的定义。
31、2.4 Linux内核大小
uClinux/linux-2.4.x/目录下编译出来的linux文件有5M多,这是ELF格式的文件,它转换成image.bin时还要用m68k-elf-objcopy工具转换成binary格式的linux.bin,这时它就只有800k了,再压缩一下就变成300k了。这个工作具体可参见uClinux/vendors/HHTECH/M5272/Makefile。
32、从SDRAM中划出一块不让Linux访问,供自己使用,可作共享内存用
对于2.4内核修改uClinux/linux-2.4.x/arch/m68knommu/platform/5272/HHTECH/crt0_ram.S(对于2.0ROM版,为uClinux/linux/arch/m68knommu/platform/5272/MOTOROLA/crt0_rom.S),只要修改一处:即文件最前面定义的MEM_SIZE宏,华恒提供的为0x01000000,即16M。用户若想划出8M为自己管理使用,只要将这个值改为0x00800000即可。这样作就是不让LINUX知道还有这8M地址,这样OS在分配内存的时候就不会用到这些地址空间,相当于从OS那里偷内存给自己使用。例如可用于内核驱动和应用程序的通信用共享内存等。
33、关于通过NFS mount宿主机硬盘调试应用程序的问题
嵌入式LINUX调试应用程序最主要的方式就是通过NFS mount宿主机硬盘上的应用程序来执行,通过观察其在串口终端打印的信息来达到调试的目的。
在这个过程中,存在一个权限的问题,即板子mount宿主机硬盘后,这个NFS mount的操作默认的不是以root的权限执行的,因此一般的板子没有权限执行宿主机硬盘上的程序,这时就要在宿主机上执行chmod 777 app,其中app为应用程序可执行文件的名字。其实这种现象还是比较容易为开发人员解决的,因为当执行应用时,minicom就会报错,permision denied,或者unknown error 4。但对于有的情况就不一定这么容易看出是权限的问题:
例如:调试WEB管理软件cgi代码时,我们把宿主机上/cgi-bin/通过NFS mount到板子的CGI工作目录/home/httpd/cgi-bin/上,这时通过浏览器IE执行CGI操作时,就会报错403,这里也是一个权限的问题,即CGI要求其工作目录可写,这时就必须在宿主机上执行:chmod 777 /cgi-bin,这时浏览器里立刻就可以工作了。
34、关于uClinux下线程的使用
使用线程是要求uClibc里加入pThread库编译。
这样就要求make menuconfig时选择uClibc,而不是华恒默认选择的uC-libc。
在uClibc中选择pThread库,可以按照以下操作:
1.cd uClibc
2.make menuconfig CROSS=m68k-elf-
3.选上posix thread support选项
4.cd ..
5.make clean
6.make
库选择好之后,先不必自己编制应用程序,uClinux提供了完备的测试程序:即uClinux/user/threaddemos
你make menuconfig在应用程序中选择这个程序加入编译,看是否可以编译通过,通过后执行是否正确即可。
若编译有错误,可以直接到user/threaddemos目录下,执行:
m68k-elf-gcc -m5307 -msep-data -Wl,-elf2flt -o bcdm bcdm.c -lpthread -lc
可直接生成可执行文件bcdm,可直接mount到板子上执行。
【注意】
这里要求使用20030314的elf-tools 。
华恒客户可发信到support@hhcn.com索取相关测试代码。
35、关于支持模块动态加载(Loadable Module)的问题
在make menuconfig中配置内核时选择:Loadable module support --->
[*] Enable loadable module support
[] Set version information on all module symbols (NEW)
[*] Kernel module loader (NEW)
并在应用程序配置中选择busybox下的insmod/rmmod/lsmod,
并选中: [*] Post 2.1 kernel modules。
关于驱动模块的.c(例如mydriver.c)可这样编译,不需要单独写Makefile,就直接放到uClinux/linux2.4.x里面参与内核编译即可:
cp mydriver.c uClinux-dist/linux-2.4.x/drivers/char
然后修改uClinux-dist/linux-2.4.x/drivers/char/Makefile,
obj-y += mem.o tty_io.o n_tty.o tty_ioctl.o raw.o pty.o misc.o random.o mydriver.o
在最后加入mydriver.o
这样内核就会用正确的参数编译出mydriver.o。
然后把这个mydriver.o单独复制出来到uClinux-dist/romfs/usr下面,然后再把uClinux-dist/linux-2.4.x/drivers/char/Makefile恢复回去,然后再重编内核,烧写,启动后执行insmod /usr/mydriver.o即可成功。再lsmod看看。
36、关于支持应用程序动态链接uClibc的问题
早期的uClinux下的应用程序都是采用静态链接的方式链接uClibc/uC-libc,现在uClinux早就已经可以支持动态链接uClibc库了。动态链接的好处是可以实现多个应用程序实现代码共享,从而可以节省内存消耗,就相当于WINDOWS下的DLL所引入的共享优势。
动态链接在NOMMU处理器上的实现采用了XIP(execute in place)代码。
详细分析参见:
http://mailman.uclinux.org/pipermail/uclinux-dev/2002-April/007786.html
37、关于最新的20030314 elf toolschain
参见:http://www.uclinux.org/pub/uClinux/m68k-elf-tools/
下载m68k-elf-tools-20030314.sh到LINUX PC机上,chmod 777 m68k-elf-tools-20030314.sh,然后./m68k-elf-tools-20030314.sh则直接将最新的m68k-elf-xxx工具集合安装到/usr/local/下面,可以直接使用。
38、执行我的应用程序时报错:BINFMT_FLAT: bad magic/rev (0x1010100, need 0x4)
例如:mount宿主机上的nmbd,在minicom下执行/mnt/nmbd&,出现如下错误:
BINFMT_FLAT: bad magic/rev (0x1010100, need 0x4)
BINFMT_FLAT: bad magic/rev (0x1010100, need 0x4)
/mnt/nmbd: Exec format error
这是因为这个应用程序没有使用交叉编译工具编译,而是用了PC Linux下的gcc编译出来的。造成这种问题的经常是由于客户不是在uClinux目录下执行make统一进行编译,而是直接到该应用程序目录下执行make所致。这是由于uClinux/user/下每个应用程序的Makefile中使用了许多宏,列入CC等,这些宏都是统一在uClinux下的一个目录下的文件中定义的,若用户直接到user/下的应用程序目录下执行make,则这些宏就无法获取uClinux为其设定的值,而是自动采用PC Linux系统默认的,例如CC若用户不指定,默认的就是gcc,所以应用程序就没有用m68k-elf-gcc编译,而是用PC
Linux下的gcc编译的,这样编出来的可执行文件在板子上当然是无法执行的了。
39、关于关闭shell