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\n",
__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";
再查看系统uClinux\linux\arch\m68knommu\kernel/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
