常用命令:
1 cache
使用格式:cache [on off]
功能描述:cache命令用于管理微处理器的cache。在传输大容量的文件时,最好是把cache打开。
Redboot>cache //显示系统当前cache状态
Redboot>cache on //打开cache
Redboot>cache off //关闭cache
2 channel
使用格式:channel [-l channel number]
功能描述:如果不带任何参数,channel命令会显示当前的控制台通道号;如果参数为-1,则将控制台通道切换到默认的控制台通道;若参数为硬件平台所支持的其他控制台号,则channel命令就对控制台作相应的切换。
3 dump
使用格式:dump [-b location] [-l length] [-s] [-1 -2 -4]
功能描述: 显示参数指定区域的数据,显示方式由参数指定。
-b 存储器的起始位置
-l 显示的长度
-s 使用Motorala S-reconds格式显示数据
-1 按单字节显示数据
-2 按双字节显示数据
-4 按四字节显示数据
4 exec
使用格式:exec [-w timeout] [-r ramdisk_address] [-s ramdisk_length] [-b load_address] [-l load_length] [-c kernel_command_line] [entry_point]
功能描述:执行一个映象文件,如引导Linux内核
-w 执行映象文件之前的等待时间
-r 传递给内核的ramdisk_address起始地址
-s 传递给内核的ramdisk_address长度
-b 内核映象文件地址
-l 内核映象文件长度
-c 传递给内核的命令行
5 fis creat
使用格式:fis creat [-b data_address] [-l length] [-f flash_address] [-e entry] [-r relocation_address] [-s data_length] [-n] [name]
功能描述:在FIS(Flash Image System)目录中创建一个映象,将当前RAM中的数据写入FLASH存储器中。因此,在使用该命令之前,映象文件数据必须已经保存在RAM中。
-b 待写入flash数据的存放地址
-f flash地址
-e 可执行映象地址
-r 执行fis load命令时,可执行映象的重定位地址
-s 写入flash中的可执行映象的实际长度
-n 用于更新FIS目录
name 创建映象的名称
6 fis init
使用格式:fis init [-f]
功能描述:初始化FIS目录,-f表示将所有的flash空间初始化
7 fis list
使用格式:fis list [-c] [-d]
功能描述:显示FIS中当前的所有映象文件
-c 显示映象的校验和
-d 显示映象的长度
8 fis free
使用格式:fis free
功能描述:显示flash当前的空闲空间
9 fis delete
使用格式:fis delete [name]
功能描述:删除FIS目录中的映象。name为需要删除映象的名称。
举例:
Redboot>fis delete ramdisk.gz
10 fis lock
使用格式:fis lock [-f flash_address] [-l length]
功能描述:锁定flash空间
-f 锁定flash空间的起始地址
11 fis unlock
使用格式:fis unlock [-f flash_address] [-l length]
功能描述:解除flash空间的锁定
12 fis erase
使用格式:fis erase [-f flash_address] [-l length]
功能描述:擦除指定的flash空间
13 fis write
使用格式:fis write [-b mem_address] [-l length] [-f flash_address]
功能描述:将数据由RAM写入FLASH中
-b 待写数据在RAM中的起始地址
-f 写入Flash的起始地址
14 fconfig
使用格式:fconfig
功能描述:对已保存在flash中的配置选项进行管理和重配置。
15 go
使用格式:go [-w timeout] [start_address]
功能描述:执行放在某一位置的可执行代码
-w 执行代码前的等待时间
start_address 可执行代码的起始地址
16 ip_address
使用格式:ip_address [-l local_ip_address] [-h server_ip_address] [-d DNS_server_ip_address]
功能描述:设置或改变系统使用的IP地址
17 load
使用格式:load [-r] [-v] [-h host] [-m varies] [-c channel_number] [-b base_address] [file_name]
功能描述:下载数据到目标系统RAM中
-r 下载未处理的数据到RAM
-v 下载过程显示进度
-b 数据下载到RAM的地址
file_name 下载的文件名
18 mcmp
使用格式:mcmp [-s location] [-d location] [-l length] [-1 -2 -4]
功能描述:比较两个存储区域的内容
-s 源区域起始地址
-d 目的区域起始地址
-l 需要比较数据的长度
-1 单字节读取
-2 双字节读取
-4 四字节读取
19 mcopy
使用格式同mcmp,功能就是将数据从一个存储区域复制到另一个存储区域
20 mfill
使用格式:mfill [-b location] [-l length] [-p value] [-1 -2 -4]
功能描述:将给定的数值填充到指定的存储区域
21 reset
功能描述:复位系统
22 ping
使用格式:ping [-v] [-n count] [-l length] [-t timeout] [-r rate] [-i IP_addr] [-h IP_addr]
功能描述:向指定主机发送ICMP报文,用于检查网络是否正常。
-v 显示数据包信息
-n 发送数据包的数目
-l 发送报文的长度
-t 设置超时时间
-r 发送数据包的间隔时间
-i 本机IP地址
-h 远端主机IP地址
23 help
24 version
2009年4月14日星期二
2009年4月12日星期日
Intel CE2110上Redboot的使用
作者:Sam(甄峰) sam_code@hotmail.com
Sam很早之前使用过redboot。后来很快忘记如何使用了。 之后在Intel CE2110以及 CE3100上使用了redboot.结果现在有段时间不用,又忘记了。只好再看一遍。这次能记得住吗?光头葛说:我看(不)行!
Redboot简介:
Redboot是Redhat公司随eCos发布的一个BOOT方案,是一个开源项目。
Redboot支持的处理器构架有ARM,MIPS,MN10300,PowerPC, Renesas SHx,v850,x86等,是一个完善的嵌入式系统Boot Loader。
Redboot是在ECOS的基础上剥离出来的,继承了ECOS的简洁、轻巧、可灵活配置、稳定可靠等
品质优点。它可以使用X-modem或Y-modem协议经由串口下载,也可以经由以太网口通过
BOOTP/DHCP服务获得IP参数,使用TFTP方式下载程序映像文件,常用于调试支持和系统初始化。
RedBoot的基本用法:
1. 进入Redboot 设置界面:
在Intel-CE2110重新启动时,按下Ctrl+C。 则进入Redboot配置模式下。
出现以下提示符,表明进入RedBoot设置界面。
RedBoot>
在此界面下,可以使用RedBoot命令执行操作。
2. 修改RedBoot boot script。此script将被RedBoot在启动时使用。
首先进入RedBoot boot script edit界面。
RedBoot> fconfig
则出现:
>>
在这里输入: RedBoot boot script内容。
若发现fconfig后,出现莫名其妙的错误,可以清空boot script,方法:
RedBoot>fconfig -i
Intel CE2110上,按照kernel和rootfs放置的不同方式,有3种启动方式(与CE3100很类似):
1. kernel和rootfs全部放置在tftp server上。
2. kernel烧入到NOR flash中,rootfs则使用 nfs server中的。
3. kernel和rootfs全部烧入NOR flash中。
前2种方式在开发阶段非常有用。可以快速修改kernel和rootfs.
1. kernel和rootfs全部放置在tftp server上:
1.1: copy zImage(kernel)和initrd_media.gz(rootfs)到/tftpboot
1.2:
RedBoot> fconfig
>>load –v –r –m tftp –h 172.16.1.61 –b 0x200000 zImage_olo
>>load –v –r –m tftp –h 172.16.1.61 –b 0x1000000 initrd_media.gz
>>exec –r 0x1000000 –s 0x12B2BDD –l 0x300000 –c “console=ttyS0, 115200 mem=100M@0 root=/dev/ram0 init=/linuxrc” 0x200000
则可以使用了。(但Sam使用这个办法时总有问题。显示乱码。)
现讲解命令如下:
load: 下载数据到目标系统RAM中.
-v: 下载过程显示进度
-r: 下载未处理的数据到RAM
-b: 数据下载到RAM的地址
-m: 方式
-h: host
exec: 执行一个映象文件,如引导Linux内核
-r: 传递给内核的ramdisk_address起始地址
-s: 传递给内核的ramdisk_address长度
-b: 内核映象文件地址
-l: 内核映象文件长度
-c: 传递给内核的命令行
2. kernel烧入到NOR flash中,rootfs则使用 nfs server中的。 (Sam还是有问题)
2.1. kernel burn 到flash。
2.1.1 把新kernel--zImage放到/tftpboot
2.1.2 Ctrl-c 进入redboot模式。
2.1.3烧入:
RedBoot>fis init
RedBoot>load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot>fis unlock -f 0xc0040000 -l 0x00200000
RedBoot>fis create -b 0x00200000 -l 0x00200000 zImage
注解如下:
fis 是RedBoot FIS(Flash Image System)相关命令。
RedBoot>fis init
初始化FIS目录,-f表示将所有的flash空间初始化
RedBoot>load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
将zImage_olo这个文件从tftp上copy到RAM 0x00200000 处。
RedBoot>fis unlock -f 0xc0040000 -l 0x00200000
解除flash空间的锁定,-f flash_address,-l length
RedBoot>fis create -b 0x00200000 -l 0x00200000 zImage
在FIS(Flash Image System)目录中创建一个映象,将当前RAM中的数据写入FLASH存储器中。因此,在使用该命令之前,映象文件数据必须已经保存在RAM中。
-b:待写入flash数据的存放地址(RAM地址)
-l:长度
zImage:创建映象的名称
Sam想,为什么没有用 -f(flash地址),是因为上一句指令指定了unlock的flash.所以缺省放在那了。
2.2 准备rootfs在nfs中:
进入redboot config
RedBoot>fconfig
>>fis load zImage
>>exec -c "console=ttyS0,115200 mem=100M@0 root=/dev/nfs nfsroot=172.16.1.61:/home/sam/Intel, nolock ip=dhcp" 0x200000
3. kernel和rootfs全部烧入NOR flash中(重点)
3.1把Kernel(zImage)和rootfs(busybox_media.jffs2)放到/tftpboot中。
3.2ctrl-c进入redboot模式。
3.3烧入:
RedBoot〉fis init
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2
RedBoot〉fis unlock -f 0xc0040000 -l 0x00200000
RedBoot〉fis create -b 0x00200000 -l 0x00200000 zImage
RedBoot〉fis create -f 0xc0240000 -l 0x01d80000 -n root
RedBoot〉fis unlock -f 0xc0240000 -l 0x01d80000
RedBoot〉fis erase -f 0xc0240000 -l 0x01d80000
RedBoot〉fis write -f 0xc0240000 -l xxxxxx -b 0x01000000
注意:xxxxxx是rootfs的长度,需要经过计算得到
也就是在load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2之后,
看到其输出:Raw file loaded 0x01000000-0x01e5ffff
则长度为:0x01e5ffff-0x01000000+1=0xE60000
RedBoot〉fis write -f 0xc0240000 -l 0xE60000 -b 0x01000000
讲解如下:
RedBoot〉fis init
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2
初始化FIS目录。并把zImage_olo和rootfs分别放到RAM 0x200000和0x1000000位置。
fis unlock -f 0xc0040000 -l 0x00200000
解除flash空间的锁定
RedBoot〉fis create -b 0x00200000 -l 0x00200000 zImage
RedBoot〉fis create -f 0xc0240000 -l 0x01d80000 -n root
将RAM中0x00200000处的东西(zImage)放到Flash xC0040000处. 并创建名为zImage的分区。
在Flash起始地址0xc0240000 创建为root分区。
RedBoot〉fis unlock -f 0xc0240000 -l 0x01d80000
RedBoot〉fis erase -f 0xc0240000 -l 0x01d80000
RedBoot〉fis write -f 0xc0240000 -l xxxxxx -b 0x01000000
把root分区所在flash解压缩。
把root分区所在Flash Erase.
把RAM 0x01000000的东西(rootfs)放到0xc0240000的FLASH中。
至此:把kernel和rootfs放到flash各自区域中了。
3.4进入redboot config
RedBoot〉fconfig
>>fis load zImage
>> exec -c "console=ttyS0,115200 mem=100M@0 rootfstype=jffs2 rootflags=noatime root=/dev/mtdblock2 rw" 0x200000
一切正常。
Sam很早之前使用过redboot。后来很快忘记如何使用了。 之后在Intel CE2110以及 CE3100上使用了redboot.结果现在有段时间不用,又忘记了。只好再看一遍。这次能记得住吗?光头葛说:我看(不)行!
Redboot简介:
Redboot是Redhat公司随eCos发布的一个BOOT方案,是一个开源项目。
Redboot支持的处理器构架有ARM,MIPS,MN10300,PowerPC, Renesas SHx,v850,x86等,是一个完善的嵌入式系统Boot Loader。
Redboot是在ECOS的基础上剥离出来的,继承了ECOS的简洁、轻巧、可灵活配置、稳定可靠等
品质优点。它可以使用X-modem或Y-modem协议经由串口下载,也可以经由以太网口通过
BOOTP/DHCP服务获得IP参数,使用TFTP方式下载程序映像文件,常用于调试支持和系统初始化。
RedBoot的基本用法:
1. 进入Redboot 设置界面:
在Intel-CE2110重新启动时,按下Ctrl+C。 则进入Redboot配置模式下。
出现以下提示符,表明进入RedBoot设置界面。
RedBoot>
在此界面下,可以使用RedBoot命令执行操作。
2. 修改RedBoot boot script。此script将被RedBoot在启动时使用。
首先进入RedBoot boot script edit界面。
RedBoot> fconfig
则出现:
>>
在这里输入: RedBoot boot script内容。
若发现fconfig后,出现莫名其妙的错误,可以清空boot script,方法:
RedBoot>fconfig -i
Intel CE2110上,按照kernel和rootfs放置的不同方式,有3种启动方式(与CE3100很类似):
1. kernel和rootfs全部放置在tftp server上。
2. kernel烧入到NOR flash中,rootfs则使用 nfs server中的。
3. kernel和rootfs全部烧入NOR flash中。
前2种方式在开发阶段非常有用。可以快速修改kernel和rootfs.
1. kernel和rootfs全部放置在tftp server上:
1.1: copy zImage(kernel)和initrd_media.gz(rootfs)到/tftpboot
1.2:
RedBoot> fconfig
>>load –v –r –m tftp –h 172.16.1.61 –b 0x200000 zImage_olo
>>load –v –r –m tftp –h 172.16.1.61 –b 0x1000000 initrd_media.gz
>>exec –r 0x1000000 –s 0x12B2BDD –l 0x300000 –c “console=ttyS0, 115200 mem=100M@0 root=/dev/ram0 init=/linuxrc” 0x200000
则可以使用了。(但Sam使用这个办法时总有问题。显示乱码。)
现讲解命令如下:
load: 下载数据到目标系统RAM中.
-v: 下载过程显示进度
-r: 下载未处理的数据到RAM
-b: 数据下载到RAM的地址
-m: 方式
-h: host
exec: 执行一个映象文件,如引导Linux内核
-r: 传递给内核的ramdisk_address起始地址
-s: 传递给内核的ramdisk_address长度
-b: 内核映象文件地址
-l: 内核映象文件长度
-c: 传递给内核的命令行
2. kernel烧入到NOR flash中,rootfs则使用 nfs server中的。 (Sam还是有问题)
2.1. kernel burn 到flash。
2.1.1 把新kernel--zImage放到/tftpboot
2.1.2 Ctrl-c 进入redboot模式。
2.1.3烧入:
RedBoot>fis init
RedBoot>load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot>fis unlock -f 0xc0040000 -l 0x00200000
RedBoot>fis create -b 0x00200000 -l 0x00200000 zImage
注解如下:
fis 是RedBoot FIS(Flash Image System)相关命令。
RedBoot>fis init
初始化FIS目录,-f表示将所有的flash空间初始化
RedBoot>load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
将zImage_olo这个文件从tftp上copy到RAM 0x00200000 处。
RedBoot>fis unlock -f 0xc0040000 -l 0x00200000
解除flash空间的锁定,-f flash_address,-l length
RedBoot>fis create -b 0x00200000 -l 0x00200000 zImage
在FIS(Flash Image System)目录中创建一个映象,将当前RAM中的数据写入FLASH存储器中。因此,在使用该命令之前,映象文件数据必须已经保存在RAM中。
-b:待写入flash数据的存放地址(RAM地址)
-l:长度
zImage:创建映象的名称
Sam想,为什么没有用 -f(flash地址),是因为上一句指令指定了unlock的flash.所以缺省放在那了。
2.2 准备rootfs在nfs中:
进入redboot config
RedBoot>fconfig
>>fis load zImage
>>exec -c "console=ttyS0,115200 mem=100M@0 root=/dev/nfs nfsroot=172.16.1.61:/home/sam/Intel, nolock ip=dhcp" 0x200000
3. kernel和rootfs全部烧入NOR flash中(重点)
3.1把Kernel(zImage)和rootfs(busybox_media.jffs2)放到/tftpboot中。
3.2ctrl-c进入redboot模式。
3.3烧入:
RedBoot〉fis init
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2
RedBoot〉fis unlock -f 0xc0040000 -l 0x00200000
RedBoot〉fis create -b 0x00200000 -l 0x00200000 zImage
RedBoot〉fis create -f 0xc0240000 -l 0x01d80000 -n root
RedBoot〉fis unlock -f 0xc0240000 -l 0x01d80000
RedBoot〉fis erase -f 0xc0240000 -l 0x01d80000
RedBoot〉fis write -f 0xc0240000 -l xxxxxx -b 0x01000000
注意:xxxxxx是rootfs的长度,需要经过计算得到
也就是在load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2之后,
看到其输出:Raw file loaded 0x01000000-0x01e5ffff
则长度为:0x01e5ffff-0x01000000+1=0xE60000
RedBoot〉fis write -f 0xc0240000 -l 0xE60000 -b 0x01000000
讲解如下:
RedBoot〉fis init
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2
初始化FIS目录。并把zImage_olo和rootfs分别放到RAM 0x200000和0x1000000位置。
fis unlock -f 0xc0040000 -l 0x00200000
解除flash空间的锁定
RedBoot〉fis create -b 0x00200000 -l 0x00200000 zImage
RedBoot〉fis create -f 0xc0240000 -l 0x01d80000 -n root
将RAM中0x00200000处的东西(zImage)放到Flash xC0040000处. 并创建名为zImage的分区。
在Flash起始地址0xc0240000 创建为root分区。
RedBoot〉fis unlock -f 0xc0240000 -l 0x01d80000
RedBoot〉fis erase -f 0xc0240000 -l 0x01d80000
RedBoot〉fis write -f 0xc0240000 -l xxxxxx -b 0x01000000
把root分区所在flash解压缩。
把root分区所在Flash Erase.
把RAM 0x01000000的东西(rootfs)放到0xc0240000的FLASH中。
至此:把kernel和rootfs放到flash各自区域中了。
3.4进入redboot config
RedBoot〉fconfig
>>fis load zImage
>> exec -c "console=ttyS0,115200 mem=100M@0 rootfstype=jffs2 rootflags=noatime root=/dev/mtdblock2 rw" 0x200000
一切正常。
2009年4月10日星期五
Intel CE2110 (Olo) PDK Install and system build
作者:Sam(甄峰) sam_code@hotmail.com
Sam之前在Intel CE2110(Olo)上作程序。后来转到Intel CE3100(Canmore) 上。可天有不测风云,今天又需要回到Intel CE2110上编译一个程序。却发现连编译环境都忘记了。只好老老实实再作一次。
1. 安装PDK
如果为ISO格式文件:
#mount -t iso9660 -o loop Intel_CE_2110-2-1[1].358.iso mount_dir
#sh installer.sh /home/sam/work/current/Intel_CE_2110/Intel_CE_2110_Dev/
于是PDK被安装到指定目录。
2. 安装Toolchain。
toolchain为:gcc-3.4.5-glibc-2.3.6.tar.gz。
把它解压后放到/usr/local/下即可。
3. 整体编译:
#make all
4. 编译我们所需要的driver, protocol module.
#cd ~/Intel_CE_2110-1.x.xxx/linux-2.6.16.16
#make menuconfig
network->bluetooth中选中Modules
5. 编译Bluetooth 库文件:
Sam暂时使用老的bluetooth lib库。编译出 bluetooth.so
6. 编译我们的程序:
修改Makefile。很容易就可以编译了。
7. 架设Olo板:
Sam之前在Intel CE2110(Olo)上作程序。后来转到Intel CE3100(Canmore) 上。可天有不测风云,今天又需要回到Intel CE2110上编译一个程序。却发现连编译环境都忘记了。只好老老实实再作一次。
1. 安装PDK
如果为ISO格式文件:
#mount -t iso9660 -o loop Intel_CE_2110-2-1[1].358.iso mount_dir
#sh installer.sh /home/sam/work/current/Intel_CE_2110/Intel_CE_2110_Dev/
于是PDK被安装到指定目录。
2. 安装Toolchain。
toolchain为:gcc-3.4.5-glibc-2.3.6.tar.gz。
把它解压后放到/usr/local/下即可。
3. 整体编译:
#make all
4. 编译我们所需要的driver, protocol module.
#cd ~/Intel_CE_2110-1.x.xxx/linux-2.6.16.16
#make menuconfig
network->bluetooth中选中Modules
5. 编译Bluetooth 库文件:
Sam暂时使用老的bluetooth lib库。编译出 bluetooth.so
6. 编译我们的程序:
修改Makefile。很容易就可以编译了。
7. 架设Olo板:
2009年4月1日星期三
MTK平台程序开发-2
作者:Sam(甄峰) sam_code@hotmail.com
MTK程序将数据分为动态和静态数据。
动态数据指程序运行时才知道,由程序动态生成的。
静态数据指固定不变的数据,可以在程序编译时转换为二进制数据存入烧录文件中。
静态数据被称为资源。常见的资源有以下几种:字串,图像,菜单,字库,主题,声音等。
添加一个资源需要3个步骤:原料,ID,装载。
原料其实就是数据源,如图片,字串等。
ID:资源的别名,程序只能通过ID得到资源。
装载:将原料转换为二进制数据,并与ID联系起来。
资源装载预编译程序是plutommi\Customer\ResGenerator\mtk_resgenerator.exe,这个程序在每次编译目标烧入文件之前临时编译生成。
添加资源步骤1:添加增加资源入口:
在plutommi\Customer\CustResource\PLUTO_MMI\Res_MMI添加一个Res_BluetoothUI.c 。
这个程序提供一个如何添加以及添加何种资源的function.
void PopulateBluetoothUIRes(void)
{
}
注意,这个C文件是预编译时提供给预编译程序。用来指定怎么添加以及添加何种资源的。
添加资源步骤2:修改Makefile
在plutommi\Customer\ResGenerator\Makefile中。
-I "plutommi/mmi/MainMenu/MainMenuInc" \
-I "plutommi\mmi\BluetoothUI\BluetoothUIInc" \
注意:此Makefile是资源预编译程序mtk_resgenerator.exe的Makefile
添加资源步骤3:修改PopulateRes.c
把PopulateBluetoothUIRes() 添加到PopulateResDate()中。当预编译程序mtk_resgenerator.exe运行时,会按照我们提供的接口PopulateBluetoothUIRes()中的内容把资源添加上去。
添加资源步骤4:添加资源ID:
因为每个类型资源的ID都在同一个取值空间。所以不能重复。
在plutommi\mmi\Inc\MMIDataType.h中
它实际是是提供了每个程序的BASE-ID.
现在以字串资源为例:
1. 在BluetoothUIDefs.h中添加字串ID:
STR_BLUETOOTHUI_STRING = BLUETOOTHUI_BASE+1,
注意:BLUETOOTHUI_BASE是在上面plutommi\mmi\Inc\MMIDataType.h中添加的。
2. 将ID与资源内容对应起来:
plutommi\Customer\CustResource\PLUTO_MMI\ref_list.txt中添加:
STR_BLUETOOTHUI_STRING 3Dijoy 鼎亿科技
表示:STR_BLUETOOTHUI_STRING这个ID与3Dijoy(英文) ,鼎亿科技(中文对应)
3. 字串装载:
在plutommi\Customer\CustResource\PLUTO_MMI\Res_MMI\Res_BluetoothUI.c
的
void PopulateBluetoothUIRes(void)
{
ADD_APPLICATION_STRING2(STR_BLUETOOTHUI_STRING,"3DiJoy", "");
}
第一个参数为:ID
第二个参数为:缺省资源(当ref_list.txt中没有此ID对应的资源时,使用这个)
第三个参数:字串描述,可以忽略
4. 字串读取:
GetString()可以从ID中读取数据。
gui_print_text((UI_string_type)GetString(STR_BLUETOOTHUI_STRING));
以下添加菜单:
MTK程序将数据分为动态和静态数据。
动态数据指程序运行时才知道,由程序动态生成的。
静态数据指固定不变的数据,可以在程序编译时转换为二进制数据存入烧录文件中。
静态数据被称为资源。常见的资源有以下几种:字串,图像,菜单,字库,主题,声音等。
添加一个资源需要3个步骤:原料,ID,装载。
原料其实就是数据源,如图片,字串等。
ID:资源的别名,程序只能通过ID得到资源。
装载:将原料转换为二进制数据,并与ID联系起来。
资源装载预编译程序是plutommi\Customer\ResGenerator\mtk_resgenerator.exe,这个程序在每次编译目标烧入文件之前临时编译生成。
添加资源步骤1:添加增加资源入口:
在plutommi\Customer\CustResource\PLUTO_MMI\Res_MMI添加一个Res_BluetoothUI.c 。
这个程序提供一个如何添加以及添加何种资源的function.
void PopulateBluetoothUIRes(void)
{
}
注意,这个C文件是预编译时提供给预编译程序。用来指定怎么添加以及添加何种资源的。
添加资源步骤2:修改Makefile
在plutommi\Customer\ResGenerator\Makefile中。
-I "plutommi/mmi/MainMenu/MainMenuInc" \
-I "plutommi\mmi\BluetoothUI\BluetoothUIInc" \
注意:此Makefile是资源预编译程序mtk_resgenerator.exe的Makefile
添加资源步骤3:修改PopulateRes.c
把PopulateBluetoothUIRes() 添加到PopulateResDate()中。当预编译程序mtk_resgenerator.exe运行时,会按照我们提供的接口PopulateBluetoothUIRes()中的内容把资源添加上去。
添加资源步骤4:添加资源ID:
因为每个类型资源的ID都在同一个取值空间。所以不能重复。
在plutommi\mmi\Inc\MMIDataType.h中
它实际是是提供了每个程序的BASE-ID.
现在以字串资源为例:
1. 在BluetoothUIDefs.h中添加字串ID:
STR_BLUETOOTHUI_STRING = BLUETOOTHUI_BASE+1,
注意:BLUETOOTHUI_BASE是在上面plutommi\mmi\Inc\MMIDataType.h中添加的。
2. 将ID与资源内容对应起来:
plutommi\Customer\CustResource\PLUTO_MMI\ref_list.txt中添加:
STR_BLUETOOTHUI_STRING 3Dijoy 鼎亿科技
表示:STR_BLUETOOTHUI_STRING这个ID与3Dijoy(英文) ,鼎亿科技(中文对应)
3. 字串装载:
在plutommi\Customer\CustResource\PLUTO_MMI\Res_MMI\Res_BluetoothUI.c
的
void PopulateBluetoothUIRes(void)
{
ADD_APPLICATION_STRING2(STR_BLUETOOTHUI_STRING,"3DiJoy", "");
}
第一个参数为:ID
第二个参数为:缺省资源(当ref_list.txt中没有此ID对应的资源时,使用这个)
第三个参数:字串描述,可以忽略
4. 字串读取:
GetString()可以从ID中读取数据。
gui_print_text((UI_string_type)GetString(STR_BLUETOOTHUI_STRING));
以下添加菜单:
2009年3月30日星期一
MTK平台程序开发-1
作者:Sam(甄峰) sam_code@hotmail.com
Sam以MediaTek MT62xx为平台开发。首先介绍MediaTek开发平台。
MediaTek提供标准软件包,它包含完整的一体化软件,由GSM/GPRS L1和协议栈,device driver,应用程序和MMI组成。通常情况下,采用MTK解决方案的用户,只需要修改一小部分代码来配合他们的硬件修改和定制。目录位于:.../mcu/custom.
程序安装:
1.ADS安装。
2.copy ADS_1.2 Others\ADS_1.2\ADS Patch\armAdsBuild842\bin中内容到 C:\Program Files\ARM\ADSv1_2\Bin
3.copy ADS_1.2 Others\ADS_1.2\ADS Patch\armlib_build_837\Adsv1_2\Lib\armlib内容到 C:\Program Files\ARM\ADSv1_2\Lib\armlib
就是修改编译器和lib.
4. ActivePerl安装:
ActivePerl-5.8.4.810-MSWin32-x86
驱动安装:
程序编译:
打开cmd,进入字符界面。进入source code 目录。
1>. make ivy grps r
然后开始编译。编译完成后,会生成目录 ~\build\IVY\IVY_PCB01_gprs_MT6235B_S01_IVY_SW_0.9.bin, image文件就放在其中。
编译命令的解释:
make xxx gprs new
make xxx gprs update
make xxx gprs remake
make xxx gprs clean
编译成功后,在build目录下生成所有的.obj,编译信息log文件、下载.bin文件等;编译失败时,到build目录下查看相应的log文件,可看到出错信息。
几个关键字解释如下:
1). xxx,Project名,对应make目录下的xxx_GPRS.mak文件;
2) 最后一个关键字:
a. new:不管资源、代码是否有改变,全部重编;
b. update:扫描资源、代码的改变,有改变的重编,无改变的不编;
c. remake:不扫描资源,只扫描代码的改变,有改变的重编,资源和无改变的代码不编;
d. clean:清除build目录下的所有内容,保留目录结构。
程序烧入:
将手机和PC用烧入线连接起来。
运行FlashTool_v3.0836.00\Flash_tool.exe
选择Scatter-loading, 选择~build\IVY\IVY_PCB01_gprs_MT6235B_S01_IVY_SW_0.9.bin\scatIVY.txt文件
Option-〉选择对应的serial port.
关机
按下download
开机,之后开始download
Sam的工作从MMI开始。
MMI:Man Machine Interface。即人机界面。
首先加入一个UI,上面写上:Hello World!
建议加入的代码放到新目录内:
一:目录结构的创建:
首先在plutommi\mmi目录中创建BluetoothUI目录,并在其中创建BluetoothUIInc目录和BluetoothUISrc。顾名思义,里面分别放头文件和源文件。
Sam在plutommi\mmi\BluetoothUI\BluetoothUISrc中创建了一个源文件:BluetoothUISrc.c
又在plutommi\mmi\BluetoothUI\BluetoothUIInc中创建了:
BluetoothUIProt.h :本程序所有函数声明,但只被本程序include
BluetoothUIDefs.h: 本程序的资源ID.
BluetoothUITypes.h:放置本程序使用的所有类型,结构,常量。
BluetoothUIGprot.h:需要被别的程序使用的函数声明,被别的程序include。
二:程序的编写:
void mmi_BluetoothUI_entry(void)
{
//从前一个Screen中退出,进入当前Screen,如果不这样,那前一个Screen还在响应一些消息。
EntryNewScreen(SCR_3DIJOY_SCREEN, NULL, mmi_BluetoothUI_entry, NULL);
//清屏
clear_screen();
//指针位置
gui_move_text_cursor(2, 100);
//字符颜色
gui_set_text_color(UI_COLOR_GREEN);
//写字符入屏幕
gui_print_text(L"Bluetooth Game Controller");
gui_move_text_cursor(60, 200);
gui_set_text_color(UI_COLOR_BLACK);
gui_print_text(L"3DiJoy Inc.");
//刷新,否则字符没有显示
gui_BLT_double_buffer(0, 0, UI_device_width - 1, UI_device_height - 1);
return;
}
并且:SCR_3DIJOY_SCREEN需要被加入到ConnectivityResDef.h中。Screen ID
三:程序加入主程序:
MTK程序与PC或其它Linux嵌入式程序不同,它的程序与整个系统是固定在一起的。Nuclues类似于UC/OS-II。所以Sam只提供了一个 mmi_BluetoothUI_entry()供别人调用。为了测试这个函数,Sam将它加入到:
plutommi\mtkapp\AudioPlayer\AudioPlayerSrc\AudioPlayerSrc.c中的mmi_audply_app_pre_entry()中。也就是“多媒体-〉音乐播放器”的入口程序中。
如同Makefile中需要指定某个obj需要被编译出一样。我们也需要指出BluetoothUISrc.c需要被编译。
Sam先查看AudioPlayerSrc.c在make目录中是被加入到哪里的。发现是:
make\plutommi\media_app\media_app.lis中。于是需要将BluetoothUISrc.c也加入到这个文件中以指出这个.c需要被编译。
并在make\plutommi\media_app\media_app.pth中加入源码路径:
plutommi\mmi\BluetoothUI\BluetoothUISrc,
编译时会报一些东西未定义,只需要加入相应头文件即可。
注[1]
编译成功,烧入手机,进入多媒体-〉音乐播放器,则显示自己的UI。
注1:之前Sam严格按照目录创建了头文件,然后在AudioPlayerSrc.c中include。但在make\plutommi\media_app\media_app.inc指定头文件目录后,还是无法找到头文件,估计是需要 make ivy gprs n
但我们并没有所有的源码,所以无法成功编译。
Sam以MediaTek MT62xx为平台开发。首先介绍MediaTek开发平台。
MediaTek提供标准软件包,它包含完整的一体化软件,由GSM/GPRS L1和协议栈,device driver,应用程序和MMI组成。通常情况下,采用MTK解决方案的用户,只需要修改一小部分代码来配合他们的硬件修改和定制。目录位于:.../mcu/custom.
程序安装:
1.ADS安装。
2.copy ADS_1.2 Others\ADS_1.2\ADS Patch\armAdsBuild842\bin中内容到 C:\Program Files\ARM\ADSv1_2\Bin
3.copy ADS_1.2 Others\ADS_1.2\ADS Patch\armlib_build_837\Adsv1_2\Lib\armlib内容到 C:\Program Files\ARM\ADSv1_2\Lib\armlib
就是修改编译器和lib.
4. ActivePerl安装:
ActivePerl-5.8.4.810-MSWin32-x86
驱动安装:
程序编译:
打开cmd,进入字符界面。进入source code 目录。
1>. make ivy grps r
然后开始编译。编译完成后,会生成目录 ~\build\IVY\IVY_PCB01_gprs_MT6235B_S01_IVY_SW_0.9.bin, image文件就放在其中。
编译命令的解释:
make xxx gprs new
make xxx gprs update
make xxx gprs remake
make xxx gprs clean
编译成功后,在build目录下生成所有的.obj,编译信息log文件、下载.bin文件等;编译失败时,到build目录下查看相应的log文件,可看到出错信息。
几个关键字解释如下:
1). xxx,Project名,对应make目录下的xxx_GPRS.mak文件;
2) 最后一个关键字:
a. new:不管资源、代码是否有改变,全部重编;
b. update:扫描资源、代码的改变,有改变的重编,无改变的不编;
c. remake:不扫描资源,只扫描代码的改变,有改变的重编,资源和无改变的代码不编;
d. clean:清除build目录下的所有内容,保留目录结构。
程序烧入:
将手机和PC用烧入线连接起来。
运行FlashTool_v3.0836.00\Flash_tool.exe
选择Scatter-loading, 选择~build\IVY\IVY_PCB01_gprs_MT6235B_S01_IVY_SW_0.9.bin\scatIVY.txt文件
Option-〉选择对应的serial port.
关机
按下download
开机,之后开始download
Sam的工作从MMI开始。
MMI:Man Machine Interface。即人机界面。
首先加入一个UI,上面写上:Hello World!
建议加入的代码放到新目录内:
一:目录结构的创建:
首先在plutommi\mmi目录中创建BluetoothUI目录,并在其中创建BluetoothUIInc目录和BluetoothUISrc。顾名思义,里面分别放头文件和源文件。
Sam在plutommi\mmi\BluetoothUI\BluetoothUISrc中创建了一个源文件:BluetoothUISrc.c
又在plutommi\mmi\BluetoothUI\BluetoothUIInc中创建了:
BluetoothUIProt.h :本程序所有函数声明,但只被本程序include
BluetoothUIDefs.h: 本程序的资源ID.
BluetoothUITypes.h:放置本程序使用的所有类型,结构,常量。
BluetoothUIGprot.h:需要被别的程序使用的函数声明,被别的程序include。
二:程序的编写:
void mmi_BluetoothUI_entry(void)
{
//从前一个Screen中退出,进入当前Screen,如果不这样,那前一个Screen还在响应一些消息。
EntryNewScreen(SCR_3DIJOY_SCREEN, NULL, mmi_BluetoothUI_entry, NULL);
//清屏
clear_screen();
//指针位置
gui_move_text_cursor(2, 100);
//字符颜色
gui_set_text_color(UI_COLOR_GREEN);
//写字符入屏幕
gui_print_text(L"Bluetooth Game Controller");
gui_move_text_cursor(60, 200);
gui_set_text_color(UI_COLOR_BLACK);
gui_print_text(L"3DiJoy Inc.");
//刷新,否则字符没有显示
gui_BLT_double_buffer(0, 0, UI_device_width - 1, UI_device_height - 1);
return;
}
并且:SCR_3DIJOY_SCREEN需要被加入到ConnectivityResDef.h中。Screen ID
三:程序加入主程序:
MTK程序与PC或其它Linux嵌入式程序不同,它的程序与整个系统是固定在一起的。Nuclues类似于UC/OS-II。所以Sam只提供了一个 mmi_BluetoothUI_entry()供别人调用。为了测试这个函数,Sam将它加入到:
plutommi\mtkapp\AudioPlayer\AudioPlayerSrc\AudioPlayerSrc.c中的mmi_audply_app_pre_entry()中。也就是“多媒体-〉音乐播放器”的入口程序中。
如同Makefile中需要指定某个obj需要被编译出一样。我们也需要指出BluetoothUISrc.c需要被编译。
Sam先查看AudioPlayerSrc.c在make目录中是被加入到哪里的。发现是:
make\plutommi\media_app\media_app.lis中。于是需要将BluetoothUISrc.c也加入到这个文件中以指出这个.c需要被编译。
并在make\plutommi\media_app\media_app.pth中加入源码路径:
plutommi\mmi\BluetoothUI\BluetoothUISrc,
编译时会报一些东西未定义,只需要加入相应头文件即可。
注[1]
编译成功,烧入手机,进入多媒体-〉音乐播放器,则显示自己的UI。
注1:之前Sam严格按照目录创建了头文件,然后在AudioPlayerSrc.c中include。但在make\plutommi\media_app\media_app.inc指定头文件目录后,还是无法找到头文件,估计是需要 make ivy gprs n
但我们并没有所有的源码,所以无法成功编译。
2009年3月25日星期三
SDL学习
作者:Sam(甄峰) sam_code@hotmail.com
Sam在移植SDL时遇到一些问题,虽然解决了。但还是有点知其然,不知其所以然的感觉。主要原因是对SDL不熟悉造成的。所以抽空利用SDL的test程序学习一下SDL编程。
先看几个常用的structure:
1. SDL_Surface: Graphical Surface Structure
这个结构体实际上是给出了一个可以显示的Surface。其中包含了surface大小,bit_per_pixel,以及pixel data.
它通常是由SDL_SetVideoMode()创建。
以testsprite为例:
1.初始化Video
SDL_Init(SDL_INIT_VIDEO);
2.使用SDL_SetVideoMode()创建一个定制长宽,以及bit-per-pixel的surface。
screen = SDL_SetVideoMode(width, height, video_bpp, videoflags);
创建了一个Surface_A(背景 surface).
3. 使用SDL_LoadBMP()load 图像,并把数据存到一个SDL_Surface ---Surface_B(笑脸surface)中。
4. 使用SDL_DisplayFormat copy Surface_B到一个可以被快速显示的Surface_C(将要显示的笑脸surface)中。
5. 使用SDL_MapRGB()将Surface_A(背景 surface)的 format设置。
6. 使用SDL_BlitSurface()将Surface_C(将要显示的笑脸surface) 快速copy到Surface_A(背景 surface)的0,0位置,大小与Surface_C大小一致。
7. 使用SDL_FillRect()将Surface_A(背景 surface)刚才被copy的位置给盖起来了。Sam:等于没copy.
8. 在loop中:
8.1 首先使用SDL_FillRect()将Surface_A(背景 surface)重新用背景色填充。Sam:Erase
8.2 使用SDL_BlitSurface()将Surface_C(将要显示的笑脸surface) 快速copy到Surface_A(背景 surface)的某个位置(Rand)。
8.3 调用SDL_Flip()或者SDL_UpdateRects()重新画。
Sam在移植SDL时遇到一些问题,虽然解决了。但还是有点知其然,不知其所以然的感觉。主要原因是对SDL不熟悉造成的。所以抽空利用SDL的test程序学习一下SDL编程。
先看几个常用的structure:
1. SDL_Surface: Graphical Surface Structure
这个结构体实际上是给出了一个可以显示的Surface。其中包含了surface大小,bit_per_pixel,以及pixel data.
它通常是由SDL_SetVideoMode()创建。
以testsprite为例:
1.初始化Video
SDL_Init(SDL_INIT_VIDEO);
2.使用SDL_SetVideoMode()创建一个定制长宽,以及bit-per-pixel的surface。
screen = SDL_SetVideoMode(width, height, video_bpp, videoflags);
创建了一个Surface_A(背景 surface).
3. 使用SDL_LoadBMP()load 图像,并把数据存到一个SDL_Surface ---Surface_B(笑脸surface)中。
4. 使用SDL_DisplayFormat copy Surface_B到一个可以被快速显示的Surface_C(将要显示的笑脸surface)中。
5. 使用SDL_MapRGB()将Surface_A(背景 surface)的 format设置。
6. 使用SDL_BlitSurface()将Surface_C(将要显示的笑脸surface) 快速copy到Surface_A(背景 surface)的0,0位置,大小与Surface_C大小一致。
7. 使用SDL_FillRect()将Surface_A(背景 surface)刚才被copy的位置给盖起来了。Sam:等于没copy.
8. 在loop中:
8.1 首先使用SDL_FillRect()将Surface_A(背景 surface)重新用背景色填充。Sam:Erase
8.2 使用SDL_BlitSurface()将Surface_C(将要显示的笑脸surface) 快速copy到Surface_A(背景 surface)的某个位置(Rand)。
8.3 调用SDL_Flip()或者SDL_UpdateRects()重新画。
GCC,glibc, uclibc,stdc的一些基本概念
作者:Sam(甄峰) sam_code@hotmail.com
Sam有一个C++程序,其中用到:
std::wstring mbstowcs(std::string str);
使用X5平台的编译器编译时,遇到如下问题:
error: expected constructor, destructor, or type conversion before "mbstowcs"
error: expected `,' or `;' before "mbstowcs"
Sam就开始寻找原因。顺便把gcc,glibc,uclibc,stdc++等东西的概念性东西放在这里。
GCC:gcc(gnu collect compiler)是一组编译工具的总称。它主要完成的工作任务是“预处理”和“编译”,以及提供了与编译器紧密相关的运行库的支持,如libgcc_s.so、libstdc++.so等。
glibc:glibc是gnu发布的libc库,也即c运行库。glibc是linux系统中最底层的api,几乎其它任何的运行库都会倚赖于glibc。glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现.
uclibc:uclibc是另一c运行库,与glibc对应。它比glibc小。虽然uClibc和Glibc在已有的接口上是兼容的,但有些接口并没有实现。
libstdc++:libstdc++ 是GNU C++ standard Library .
Sam觉得既然编译就通不过,那说明X5的编译器本身就不认识,有可能是X5平台的交叉编译器在创建时没有添加stdc支持?后来觉得不是这样,因为STL完全是以头文件形式提供的。
1.所以只需要指定头文件路径,就应该可以编译。于是Sam添加了:
-I/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/c++/3.4.3/
(Sam觉得海思的交叉编译器做得不是特别规范,为什么呢。因为它放头文件,库文件的地点多变)
添加这个之后,理论上应该是可以编译了。
可发现还是不认识wstring.
2.于是查看/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/c++/3.4.3/bits/stringfwd.h。
发现要定义_GLIBCXX_USE_WCHAR_T才会有wstring.
3.于是在编译程序时添加了 -D_GLIBCXX_USE_WCHAR_T
还是通不过,说没有wint_t。
4.于是又查STL. 添加了 -D__WINT_TYPE__
又通不过, 说没有 btowc,Sam查遍了toolchain. 也没找到这个类型的定义。感觉很奇怪,就去查/usr/include. 发现这个类型有定义。但toolchain中没有对应的头文件。
所以,Sam觉得很多做嵌入式程序的工程师都不愿使用STL,喜欢用标准C,是有原因的,因为toolchain限制太大。很多提供toolchain的公司对类似STL的提供都不是很全。
Sam有一个C++程序,其中用到:
std::wstring mbstowcs(std::string str);
使用X5平台的编译器编译时,遇到如下问题:
error: expected constructor, destructor, or type conversion before "mbstowcs"
error: expected `,' or `;' before "mbstowcs"
Sam就开始寻找原因。顺便把gcc,glibc,uclibc,stdc++等东西的概念性东西放在这里。
GCC:gcc(gnu collect compiler)是一组编译工具的总称。它主要完成的工作任务是“预处理”和“编译”,以及提供了与编译器紧密相关的运行库的支持,如libgcc_s.so、libstdc++.so等。
glibc:glibc是gnu发布的libc库,也即c运行库。glibc是linux系统中最底层的api,几乎其它任何的运行库都会倚赖于glibc。glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现.
uclibc:uclibc是另一c运行库,与glibc对应。它比glibc小。虽然uClibc和Glibc在已有的接口上是兼容的,但有些接口并没有实现。
libstdc++:libstdc++ 是GNU C++ standard Library .
Sam觉得既然编译就通不过,那说明X5的编译器本身就不认识,有可能是X5平台的交叉编译器在创建时没有添加stdc支持?后来觉得不是这样,因为STL完全是以头文件形式提供的。
1.所以只需要指定头文件路径,就应该可以编译。于是Sam添加了:
-I/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/c++/3.4.3/
(Sam觉得海思的交叉编译器做得不是特别规范,为什么呢。因为它放头文件,库文件的地点多变)
添加这个之后,理论上应该是可以编译了。
可发现还是不认识wstring.
2.于是查看/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/c++/3.4.3/bits/stringfwd.h。
发现要定义_GLIBCXX_USE_WCHAR_T才会有wstring.
3.于是在编译程序时添加了 -D_GLIBCXX_USE_WCHAR_T
还是通不过,说没有wint_t。
4.于是又查STL. 添加了 -D__WINT_TYPE__
又通不过, 说没有 btowc,Sam查遍了toolchain. 也没找到这个类型的定义。感觉很奇怪,就去查/usr/include. 发现这个类型有定义。但toolchain中没有对应的头文件。
所以,Sam觉得很多做嵌入式程序的工程师都不愿使用STL,喜欢用标准C,是有原因的,因为toolchain限制太大。很多提供toolchain的公司对类似STL的提供都不是很全。
2009年3月19日星期四
scons 学习
作者:Sam(甄峰) sam_code@hotmail.com
http://www.scons.org/
Sam有个好朋友是做游戏的,整天嘲笑做嵌入式的用的工具多土多原始。gdb不是图形化,DDD又没VC调试好用,只会写Makefile等等。
刚好最近有需要使用scons。所以Sam准备学习之以回击这种挑衅。呵呵。
scons简介:
scons是一个Python写的自动化构建工具,从构建这个角度说,它跟GNU make是同一类的工具。
scons与其它工具最显著的差别就是:scons配置文件是Python script.
它的思想是跟GNU make完全不同的。GNU make的核心是“依赖关系”,我要做的事情,就是告诉系统,一个目标依赖什么东西,并且,当被依赖的东西发生变化时,我要做什么。
例如:
all: main.c
$(CC) $(CFLAGS) $(LFLAGS) main.c -o BT_remote -lbluetooth -lBluetooth_remote -lm -lpthread
意思是:all依赖于main.c。 如果main.c有了变化,作下面这些事。
这样做可以解决相当多的问题,但是也带来了一个最大的问题:我如何判别这个目标依赖什么?
对于一个两个,甚至十几个文件,我当然还比较容易搞清楚,谁依赖谁。但是当文件有成百上千个时,要分清楚谁依赖谁可就没这么容易了。尤其是C/C++头文件的依赖,如果手工分析的话,工程量可是不小。为了解决这个问题,GNU又提供了另外一套工具:Automake,使用程序来分析依赖性,然后辅助你产生makefile。
于是乎,就有人想了,既然如此,我干吗费那劲,用程序分析依赖性,然后生成一个文件,再交给另外一个程序去处理呢?既然依赖性需要用程序来分析,那么就直接交给构建工具本身去做不就好了吗?对的,这是一个非常自然的思路,于是,Java世界有了Ant,而Python世界有了scons。
scons就是这样一个构建工具:你告诉它要做的任务,以及完成这个任务需要的输入,以及这个任务产生的输出,怎么做这个任务(当然其中就包括依赖性分析),就交给工具本身完成。
例1:
只有一个 test.c文件,需要编译成可执行文件。
创建SConstruct,内容为:
Program('test.c')
这句话透露2层意思:
1.编译方法:想要编译成什么类型。(Program:可执行文件)
2.哪个东西想编译(test.c)
#scons
% scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cc -o test.o -c test.c
cc -o test test.o
scons: done building targets.
clean:
#scons -c
则把编译出的东西又删除了,类似make clean
例2:
只有一个文件,想编译成object文件。
Object('test.c')
1.编译方法:想要编译成什么类型。(Object: .o文件)
2.哪个东西想编译(test.c)
#scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cc -o hello.o -c hello.c
scons: done building targets.
清空:
#scons -c
则把编译出的东西又删除了,类似make clean
例3:
有多个.c文件,并想指定生成的程序名:
Program('program', ['prog.c', 'file1.c', 'file2.c'])
生成可执行文件 program, 由prog.c file1.c file2.c 生成。
#scons -Q
cc -o file1.o -c file1.c
cc -o file2.o -c file2.c
cc -o prog.o -c prog.c
cc -o program prog.o file1.o file2.o
甚至可以这样写:
common_sources = ['file1.c', 'file2.c']
Program('program2', common_sources + ['program2.c'])
例4:
有很多个.c文件,不想一个一个指定,类似Makefile中 *.o: *.c
Program('test', Glob('*.c'))
不管使用common_sources = ['file1.c', 'file2.c'] 还是Program('test1.c'....'testn.c')
每个文件名都需要被单或者双引号包起来。这太不方便了。
SCons 提供了Split函数
Program('program', Split('main.c file1.c file2.c'))
或:
src_files = Split('main.c file1.c file2.c')
Program('program', src_files)
例5:
SCons还对output file和source file有关键字,可以用来指定
src_files = Split('main.c file1.c file2.c')
Program(target = 'program', source = src_files)
因为有关键字指定,多以可以换前后:
Program(source = src_files, target = 'program')
例6:
创建库:
Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o'])
#scons -Q
cc -o f1.o -c f1.c
cc -o f3.o -c f3.c
ar rc libfoo.a f1.o f2.o f3.o f4.o
ranlib libfoo.a
例7:建立静态和动态库:
静态库:其实使用Library也是建立静态库。
StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
动态库:
SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
% scons -Q
cc -o f1.os -c f1.c
cc -o f2.os -c f2.c
cc -o f3.os -c f3.c
cc -o libfoo.so -shared f1.os f2.os f3.os
例8:使用静态和动态库:
使用$LIBS 来指定库,使用$LIBPATH 指定库位置。
Library('foo', ['f1.c', 'f2.c', 'f3.c'])
Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.')
% scons -Q
cc -o f1.o -c f1.c
cc -o f2.o -c f2.c
cc -o f3.o -c f3.c
ar rc libfoo.a f1.o f2.o f3.o
ranlib libfoo.a
cc -o prog.o -c prog.c
cc -o prog prog.o -L. -lfoo -lbar
Program('prog.c', LIBS = ['m'], LIBPATH = ['/usr/lib', '/usr/local/lib'])
% scons -Q
cc -o prog.o -c prog.c
cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm
以BTX为例使用scons:
Sam创建了一个SConstruct,(类似于GNU make的Makefile文件,是scons的默认文件名).
Sam首先想生成BTX library.
然后想将library 和 main.c和成一个可执行文件:BTX_Test
内容如下:
#指定生成文件名为:libBTX.a. 由BTX.c生成的。-I'/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include -I../include
StaticLibrary('BTX', ['BTX.c'], CPPPATH=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include', '../include'])
Program('BTX_Test', ['main.c'], CPPPATH=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include', '../include'], LIBS=['BTX','blu\etooth','pthread'], LIBPATH=['.', '/usr/lib'])
#scons
提示之不到bluetooth库,Sam在/usr/lib中将 libbluetooth.so 软连接到libbluetooth.so.2。
于是编译成功,所以,Sam觉得Scons和Makefile很类似,只需要编译时指定-I ,-L, -l就好了。对应关系如下:
-I:CPPPATH=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include', '../include']
-L:LIBPATH=['.', '/usr/lib']
-l: LIBS=['BTX','bluetooth','pthread']
CPPPATH,LIBPATH,LIBS,CC等从:
http://www.scons.org/doc/1.2.0/HTML/scons-user/a4774.html#cv-CPPPATH 中找到
同时请注意,最好在每个关键字赋值后面都跟 [],里面有多个时用逗号分开。
X5 Version:
StaticLibrary('BTX', ['BTX.c'], CC=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc'], CPPPATH=['/opt/his\ilicon/toolchains/arm-uclibc-linux-soft/include', '../include'])
Program('BTX_Test', ['main.c'], CC=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc'], CPPPATH=['/opt/his\ilicon/toolchains/arm-uclibc-linux-soft/include', '../include'], LIBS=['BTX','bluetooth','pthread'], LIBPATH=['./', '../resource/']\)
scons只需要描述任务,并不需要指定依赖关系,甚至我们都没有指出头文件,但是你修改BTX.h的时候仍然会触发构建。这是因为scons内部有个scanner,可以帮助扫描包含文件的关系。
我们还发现,这里我们根本没有指定编译器,也没有指定编译选项,但scons仍然很聪明的选择了gcc. 这是因为scons内置提供了很多编译器及其对应选项的选择,然后对于不同的平台,会有一个默认项。
Environments:
Scons有3种环境变量:
1. External Environment
2. Construction Environment
3. Execution Environment
当想要使用External Environment,必须通过os.environ。这意味着首先要加入:
import os
之后就可以在SConscript文件中使用os.environ来初始化construction environments 为用户环境变量了。
1.创建Construction Environment
env = Environment()
例如:
import os
env = Environment(CC = 'gcc', CCFLAGS = '-O2')
env.Program('foo.c')
最重要的环境变量是:Construction Environment
它在复杂的工程中非常重要,例如,不同的源文件需要不同的编译选项。或者不同的可执行程序需要链接不同的库。scons提供Construction Environment。让我们可以创建不同的Construction Environment来创建不同的编译选项。
1. 创建Construction Environment ,使用Environment function。
env = Environment() 所有的Construction Environment 项目值全用scons自己找到的。
env = Environment(CC = 'gcc', CCFLAGS = '-O2') ,其他的与上一个相同,但只有CC和CCFLAGS 自定义。
2. 察看Construction Environment 每个项目值:注[1]
需要使用Dictionary() function
env = Environment()
dict = env.Dictionary()
keys = dict.keys()
keys.sort()
for key in keys:
print "construction variable = '%s', value = '%s'" % (key, dict[key])
打印所有Construction Environment项目。
scons中Python模块的导入:
1. import os
os模块包含普遍的操作系统功能。如果你希望你的程序能够与平台无关的话,这个模块是尤为重要的。即它允许一个程序在编写后不需要任何改动,也不会发生任何问题,就可以在Linux和Windows下运行。一个例子就是使用os.sep可以取代操作系统特定的路径分割符。
os.name字符串指示你正在使用的平台。比如对于Windows,它是'nt',而对于Linux/Unix用户,它是'posix'。
os.getcwd()函数得到当前工作目录,即当前Python脚本工作的目录路径。
os.getenv()和os.putenv()函数分别用来读取和设置环境变量。
os.listdir()返回指定目录下的所有文件和目录名。
os.remove()函数用来删除一个文件。
os.system()函数用来运行shell命令。
os.linesep字符串给出当前平台使用的行终止符。例如,Windows使用'\r\n',Linux使用'\n'而Mac使用'\r'。
os.path.split()函数返回一个路径的目录名和文件名。
os.environ Shell variables
这里重点说os.environ:
它应该是Linux环境变量。
例1:
想要得到当前Linux环境变量PATH,LD_LIBRARY_PATH,HOME等。(这与前面讲的环境变量对应起来了)
#导入OS模块
import os
#创建一个Construction Environment
env=Environment()
print "HOME: ", os.environ["HOME"]
则打印结果为HOME:/home/sam。这样就得到了Linux环境变量。
例2:
想要用当前Linux环境变量ARMGCC来设置toolchain.
首先:在Linux下:
export ARMGCC=arm-linux-gcc
之后,修改config
#导入OS模块
import os
#创建一个Construction Environment
env=Environment()
if os.environ.has_key('ARMGCC'):
env.Replace(CC = os.environ['ARMGCC'])
则使用arm-linux-gcc取代了Construction Environment中的CC。
2. import sys
sys模块包含系统对应的功能.sys模块包含了与Python解释器和它的环境有关的函数.
import sys
if sys.byteorder == 'little' or env['PLATFORM'] == 'win32':
env.Append(CPPDEFINES = ['LSB_FIRST'])
3. import platform
platform得到编译信息:
platform.processor() ---i686
platform.python_build()---('r251:54863', 'Jun 4 2007 15:03:52')
platform.python_compiler()----'GCC 3.4.4 20050721 (Red Hat 3.4.4-2)'
platform.python_version()----'2.5.1'
platform.release()----'2.6.9-22.ELsmp'
platform.system()----'Linux'
platform.version()----'#1 SMP Mon Sep 19 18:32:14 EDT 2005'
platform.uname()----('Linux', 'nec', '2.6.9-22.ELsmp', '#1 SMP Mon Sep 19 18:32:14 EDT 2005', 'i686' , 'i686')
platform.machine()----'i686'
platform.libc_ver()----('glibc', '2.0')
注[1]:
所有Construction Variables可以在下面找到:
Construction Variables可用在:
1. CPPPATH,LIBS,LIBPATH。(在BTX例子中可见)
2. Construction Environment(env=Environment)中的项目。
Appendix A. Construction Variables
http://www.scons.org/
Sam有个好朋友是做游戏的,整天嘲笑做嵌入式的用的工具多土多原始。gdb不是图形化,DDD又没VC调试好用,只会写Makefile等等。
刚好最近有需要使用scons。所以Sam准备学习之以回击这种挑衅。呵呵。
scons简介:
scons是一个Python写的自动化构建工具,从构建这个角度说,它跟GNU make是同一类的工具。
scons与其它工具最显著的差别就是:scons配置文件是Python script.
它的思想是跟GNU make完全不同的。GNU make的核心是“依赖关系”,我要做的事情,就是告诉系统,一个目标依赖什么东西,并且,当被依赖的东西发生变化时,我要做什么。
例如:
all: main.c
$(CC) $(CFLAGS) $(LFLAGS) main.c -o BT_remote -lbluetooth -lBluetooth_remote -lm -lpthread
意思是:all依赖于main.c。 如果main.c有了变化,作下面这些事。
这样做可以解决相当多的问题,但是也带来了一个最大的问题:我如何判别这个目标依赖什么?
对于一个两个,甚至十几个文件,我当然还比较容易搞清楚,谁依赖谁。但是当文件有成百上千个时,要分清楚谁依赖谁可就没这么容易了。尤其是C/C++头文件的依赖,如果手工分析的话,工程量可是不小。为了解决这个问题,GNU又提供了另外一套工具:Automake,使用程序来分析依赖性,然后辅助你产生makefile。
于是乎,就有人想了,既然如此,我干吗费那劲,用程序分析依赖性,然后生成一个文件,再交给另外一个程序去处理呢?既然依赖性需要用程序来分析,那么就直接交给构建工具本身去做不就好了吗?对的,这是一个非常自然的思路,于是,Java世界有了Ant,而Python世界有了scons。
scons就是这样一个构建工具:你告诉它要做的任务,以及完成这个任务需要的输入,以及这个任务产生的输出,怎么做这个任务(当然其中就包括依赖性分析),就交给工具本身完成。
例1:
只有一个 test.c文件,需要编译成可执行文件。
创建SConstruct,内容为:
Program('test.c')
这句话透露2层意思:
1.编译方法:想要编译成什么类型。(Program:可执行文件)
2.哪个东西想编译(test.c)
#scons
% scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cc -o test.o -c test.c
cc -o test test.o
scons: done building targets.
clean:
#scons -c
则把编译出的东西又删除了,类似make clean
例2:
只有一个文件,想编译成object文件。
Object('test.c')
1.编译方法:想要编译成什么类型。(Object: .o文件)
2.哪个东西想编译(test.c)
#scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cc -o hello.o -c hello.c
scons: done building targets.
清空:
#scons -c
则把编译出的东西又删除了,类似make clean
例3:
有多个.c文件,并想指定生成的程序名:
Program('program', ['prog.c', 'file1.c', 'file2.c'])
生成可执行文件 program, 由prog.c file1.c file2.c 生成。
#scons -Q
cc -o file1.o -c file1.c
cc -o file2.o -c file2.c
cc -o prog.o -c prog.c
cc -o program prog.o file1.o file2.o
甚至可以这样写:
common_sources = ['file1.c', 'file2.c']
Program('program2', common_sources + ['program2.c'])
例4:
有很多个.c文件,不想一个一个指定,类似Makefile中 *.o: *.c
Program('test', Glob('*.c'))
不管使用common_sources = ['file1.c', 'file2.c'] 还是Program('test1.c'....'testn.c')
每个文件名都需要被单或者双引号包起来。这太不方便了。
SCons 提供了Split函数
Program('program', Split('main.c file1.c file2.c'))
或:
src_files = Split('main.c file1.c file2.c')
Program('program', src_files)
例5:
SCons还对output file和source file有关键字,可以用来指定
src_files = Split('main.c file1.c file2.c')
Program(target = 'program', source = src_files)
因为有关键字指定,多以可以换前后:
Program(source = src_files, target = 'program')
例6:
创建库:
Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o'])
#scons -Q
cc -o f1.o -c f1.c
cc -o f3.o -c f3.c
ar rc libfoo.a f1.o f2.o f3.o f4.o
ranlib libfoo.a
例7:建立静态和动态库:
静态库:其实使用Library也是建立静态库。
StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
动态库:
SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
% scons -Q
cc -o f1.os -c f1.c
cc -o f2.os -c f2.c
cc -o f3.os -c f3.c
cc -o libfoo.so -shared f1.os f2.os f3.os
例8:使用静态和动态库:
使用$LIBS 来指定库,使用$LIBPATH 指定库位置。
Library('foo', ['f1.c', 'f2.c', 'f3.c'])
Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.')
% scons -Q
cc -o f1.o -c f1.c
cc -o f2.o -c f2.c
cc -o f3.o -c f3.c
ar rc libfoo.a f1.o f2.o f3.o
ranlib libfoo.a
cc -o prog.o -c prog.c
cc -o prog prog.o -L. -lfoo -lbar
Program('prog.c', LIBS = ['m'], LIBPATH = ['/usr/lib', '/usr/local/lib'])
% scons -Q
cc -o prog.o -c prog.c
cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm
以BTX为例使用scons:
Sam创建了一个SConstruct,(类似于GNU make的Makefile文件,是scons的默认文件名).
Sam首先想生成BTX library.
然后想将library 和 main.c和成一个可执行文件:BTX_Test
内容如下:
#指定生成文件名为:libBTX.a. 由BTX.c生成的。-I'/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include -I../include
StaticLibrary('BTX', ['BTX.c'], CPPPATH=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include', '../include'])
Program('BTX_Test', ['main.c'], CPPPATH=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include', '../include'], LIBS=['BTX','blu\etooth','pthread'], LIBPATH=['.', '/usr/lib'])
#scons
提示之不到bluetooth库,Sam在/usr/lib中将 libbluetooth.so 软连接到libbluetooth.so.2。
于是编译成功,所以,Sam觉得Scons和Makefile很类似,只需要编译时指定-I ,-L, -l就好了。对应关系如下:
-I:CPPPATH=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include', '../include']
-L:LIBPATH=['.', '/usr/lib']
-l: LIBS=['BTX','bluetooth','pthread']
CPPPATH,LIBPATH,LIBS,CC等从:
http://www.scons.org/doc/1.2.0/HTML/scons-user/a4774.html#cv-CPPPATH 中找到
同时请注意,最好在每个关键字赋值后面都跟 [],里面有多个时用逗号分开。
X5 Version:
StaticLibrary('BTX', ['BTX.c'], CC=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc'], CPPPATH=['/opt/his\ilicon/toolchains/arm-uclibc-linux-soft/include', '../include'])
Program('BTX_Test', ['main.c'], CC=['/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc'], CPPPATH=['/opt/his\ilicon/toolchains/arm-uclibc-linux-soft/include', '../include'], LIBS=['BTX','bluetooth','pthread'], LIBPATH=['./', '../resource/']\)
scons只需要描述任务,并不需要指定依赖关系,甚至我们都没有指出头文件,但是你修改BTX.h的时候仍然会触发构建。这是因为scons内部有个scanner,可以帮助扫描包含文件的关系。
我们还发现,这里我们根本没有指定编译器,也没有指定编译选项,但scons仍然很聪明的选择了gcc. 这是因为scons内置提供了很多编译器及其对应选项的选择,然后对于不同的平台,会有一个默认项。
Environments:
Scons有3种环境变量:
1. External Environment
2. Construction Environment
3. Execution Environment
当想要使用External Environment,必须通过os.environ。这意味着首先要加入:
import os
之后就可以在SConscript文件中使用os.environ来初始化construction environments 为用户环境变量了。
1.创建Construction Environment
env = Environment()
例如:
import os
env = Environment(CC = 'gcc', CCFLAGS = '-O2')
env.Program('foo.c')
最重要的环境变量是:Construction Environment
它在复杂的工程中非常重要,例如,不同的源文件需要不同的编译选项。或者不同的可执行程序需要链接不同的库。scons提供Construction Environment。让我们可以创建不同的Construction Environment来创建不同的编译选项。
1. 创建Construction Environment ,使用Environment function。
env = Environment() 所有的Construction Environment 项目值全用scons自己找到的。
env = Environment(CC = 'gcc', CCFLAGS = '-O2') ,其他的与上一个相同,但只有CC和CCFLAGS 自定义。
2. 察看Construction Environment 每个项目值:注[1]
需要使用Dictionary() function
env = Environment()
dict = env.Dictionary()
keys = dict.keys()
keys.sort()
for key in keys:
print "construction variable = '%s', value = '%s'" % (key, dict[key])
打印所有Construction Environment项目。
scons中Python模块的导入:
1. import os
os模块包含普遍的操作系统功能。如果你希望你的程序能够与平台无关的话,这个模块是尤为重要的。即它允许一个程序在编写后不需要任何改动,也不会发生任何问题,就可以在Linux和Windows下运行。一个例子就是使用os.sep可以取代操作系统特定的路径分割符。
os.name字符串指示你正在使用的平台。比如对于Windows,它是'nt',而对于Linux/Unix用户,它是'posix'。
os.getcwd()函数得到当前工作目录,即当前Python脚本工作的目录路径。
os.getenv()和os.putenv()函数分别用来读取和设置环境变量。
os.listdir()返回指定目录下的所有文件和目录名。
os.remove()函数用来删除一个文件。
os.system()函数用来运行shell命令。
os.linesep字符串给出当前平台使用的行终止符。例如,Windows使用'\r\n',Linux使用'\n'而Mac使用'\r'。
os.path.split()函数返回一个路径的目录名和文件名。
os.environ Shell variables
这里重点说os.environ:
它应该是Linux环境变量。
例1:
想要得到当前Linux环境变量PATH,LD_LIBRARY_PATH,HOME等。(这与前面讲的环境变量对应起来了)
#导入OS模块
import os
#创建一个Construction Environment
env=Environment()
print "HOME: ", os.environ["HOME"]
则打印结果为HOME:/home/sam。这样就得到了Linux环境变量。
例2:
想要用当前Linux环境变量ARMGCC来设置toolchain.
首先:在Linux下:
export ARMGCC=arm-linux-gcc
之后,修改config
#导入OS模块
import os
#创建一个Construction Environment
env=Environment()
if os.environ.has_key('ARMGCC'):
env.Replace(CC = os.environ['ARMGCC'])
则使用arm-linux-gcc取代了Construction Environment中的CC。
2. import sys
sys模块包含系统对应的功能.sys模块包含了与Python解释器和它的环境有关的函数.
import sys
if sys.byteorder == 'little' or env['PLATFORM'] == 'win32':
env.Append(CPPDEFINES = ['LSB_FIRST'])
3. import platform
platform得到编译信息:
platform.processor() ---i686
platform.python_build()---('r251:54863', 'Jun 4 2007 15:03:52')
platform.python_compiler()----'GCC 3.4.4 20050721 (Red Hat 3.4.4-2)'
platform.python_version()----'2.5.1'
platform.release()----'2.6.9-22.ELsmp'
platform.system()----'Linux'
platform.version()----'#1 SMP Mon Sep 19 18:32:14 EDT 2005'
platform.uname()----('Linux', 'nec', '2.6.9-22.ELsmp', '#1 SMP Mon Sep 19 18:32:14 EDT 2005', 'i686' , 'i686')
platform.machine()----'i686'
platform.libc_ver()----('glibc', '2.0')
注[1]:
所有Construction Variables可以在下面找到:
Construction Variables可用在:
1. CPPPATH,LIBS,LIBPATH。(在BTX例子中可见)
2. Construction Environment(env=Environment)中的项目。
Appendix A. Construction Variables
2009年3月11日星期三
Linux下Framebuffer学习

作者:Sam(甄峰) sam_code@hotmail.com
Sam之前写过vfb(虚拟framebuffer) driver. 但现在什么都不记得了。 现在需要在X5平台上使用framebuufer. 只好再学习一次。
零:Framebuffer 简介:
Frame Buffer设备为图形硬件提供抽象。它表示一些视频硬件的Frame Buffer,允许应用软件通过定义好的接口访问图形硬件,所以这些软件不必知道任何关于低层(硬件寄存器)的知识。
使用frame buffer设备的应用程序(例如 X server)默认使用/dev/fb0(老一点的软件使用 /dev/fb0current)。你可以通过设定环境变量$FRAMEBUFFER等于frame buffer设备的路径名来改变frame buffer设备,例如(针对 sh/bash用户):export FRAMEBUFFER=/dev/fb1或者(针对 csh 用户):setenv FRAMEBUFFER /dev/fb1之后,X server将使用第二个frame buffer。
大家都已经知道,frame buffer设备是一个内存设备,就像/dev/mem, 所以他拥有相同的特征。你可以读他,写他,在他里面定位和mmap他(主要用途)。
/dev/fb*也允许在他之上做一些ioctls操作,通过这种方法硬件的许多信息可以获取和设置。颜色映射处理(the color map handling)也通过ioctls工作。
- 你可以取得硬件的不可更改信息,例如名字,屏幕内存组织方式(位面,象素格式,……)和地址和屏幕内存的长度。
- 你可以获取和修改硬件的可变信息,例如可见,虚拟几何,深度,颜色映射格式,定时,等等。如果你试图修改这些信息,驱动可能规整一些值以适应硬件的能力(或者返回EINVAL 如果这个操作不可能) (译注:the driver maybe will round up some values to meet the hardware's capabilities)
- 你可以取得和设置部分颜色映射。通信使用16位每一颜色(红,绿,蓝,透明)来支持所有存在的硬件。驱动处理所有应用于硬件的计算(截取他们到较少的位,可能会舍弃透明)。
视频模式计时:(非常重要)
显示器使用电子束(彩色模式使用3个电子束,单色模式使用1个电子束)绘制屏幕上的图像。屏幕的前面被颜色荧光(象素)覆盖.如果荧光被电子击中,它就会发出光子从而可见.
电子束从左至右绘制水平行,从上到下绘制整个屏幕.通过改变电子束的亮度,便可以显示各种颜色和亮度的像素
每条扫描线完成后电子束必须重新移回到下一行的左端:这称做水平回扫.等到完成整个屏幕(帧)的绘制,电子束会移回到左上方点:这被称做垂直回扫.在水平和垂直回扫期间,电子束被关闭(空).
电子束绘制像素的速度由图形板的时钟决定(译注:dotclock).
例如:时钟是28.37516MHZ(每秒百万周期)。也就是说每秒可以绘制28.37516M像素。
每像素需35242 ps(微秒):1/(28.37516E6 HZ) = 35.242E-9 s
如果屏幕的分辨率是640x480, 将需要640*35.242E-9 s = 22.555E-6 s来绘制一条扫描线上640(xres)个像素.但是水平回扫也需要时间(例如 272 像素), 所以一条完整的水平扫描线需要:(640+272)*35.242E-9 s = 32.141E-6 s
我们称水平刷新率约是31 kHz:1/(32.141E-6 s) = 31.113E3 Hz
整个屏幕有480(yres)条扫描线,但是我们必须还要考虑垂直回扫(例如 49 条扫描线). 所以完整的一屏需要:(480+49)*32.141E-6 s = 17.002E-3 s垂直刷新率约是 59 Hz:1/(17.002E-3 s) = 48.815 Hz这意味着屏幕上的数据每秒钟刷新约59。
从最上面的图可以看出:
水平回扫时间是左边缘(left_margin),右边缘(right_margin)和 hsync 长度(hsync_len)的总和. 而垂直回扫时间是上边缘(upper_margin),下边缘(lower_margin)和 vsync长度(vsync_len)的总和。这些值与fb_var_screeninfo结构体中的项目对应。
frame buffer设备期待所有的水平计时以时钟(以picoseconds, 1E-12 s)为单位,垂直计时则以扫描线数目为单位.
pixclock(以ps为单位的像素时钟)
- pixclock: 以ps为单位的像素时钟(pico seconds)
- left_margin: 从sync到画面的时间
- right_margin: 从画面到sync的时间
- upper_margin: 从sync到画面的时间
-lower_margin: 从画面到sync的时间
- hsync_len: 水平sync的长度
- vsync_len: 垂直sync的长度
一. 基本使用方法:
(之后再补上)
二. 最重要的结构体:
fb_var_screeninfo
简单的项如下:
__u32 xres; /* visible resolution */
__u32 yres; //可见部分的长宽
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual; //虚拟部分的长宽
__u32 xoffset; /* offset from virtual to visible */ //可见和虚拟部分的offset
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; //每个象素占多少位
__u32 grayscale; //不等于0的话,灰度
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
fb_bitfield:结构很重要:
struct fb_bitfield
{
__u32 offset; /* beginning of bitfield */
__u32 length; /* length of bitfield */
__u32 msb_right; /* != 0 : Most significant bit is */
};
像素详解:
以上的结构其实与像素有关,这个结构是说:这种色值(RGB或者transp=alpha)在一个像素点结构中的offset,length 。
---------------- 一个像素点
R G B a
这样,上面这个结构就明白了吧。
其它机构的含义,请看上面的视频模式部分。呵呵
2009年2月26日星期四
Nucleus学习
作者:Sam (甄峰) sam_code@hotmail.com
公司想MTK解决方案中添加手柄概念进去。MTK使用Nucleus 操作系统,它是由ATI开发的RTOS.
MTK使用了nucleus实时操作系统,在其上做了个内核抽象层的封装(Kal) ,以适应多种实时操作系统,如oscar、ThreadX、nucleus。整个软件系统包括nucleus操作系统、平台设备驱动、协议栈、文件系统、WGUI、MMI、J2ME等。在这里MMI部分几乎包括了操作系统内核、协议栈、文件系统之上的所有部分,其中WGUI也在其中。
Nucleus是单一地址空间操作系统的一种,作为商业化的嵌入式操作系统产品,曾被广泛使用。在MIPS架构中,其操作系统和用户程序完全工作在内核模式,且只占用和访问0x80000000以上的线性地址空间。因此,在Nucleus中,操作系统和用户程序工作在线性地址空间中,且用户程序与内核服务之间没有明显的区分,进入内核服务更像是调用API(Application Program Interface)而不需要上下文切换。其优点是限制少,编程方便,但系统健壮性差。
MTK手机软件系统的构建使用了GNU的make,使得整个工程的构建可以自动进行,且可以灵活控制。整个构建过程由Make.bat、make2.pl、MoDIS.dsw、Gsm2.mak、Option.mak、_.mak等文件控制。
在MTK手机软件系统的根目录下有一个批处理文件Make.bat,这个批处理文件启动了整个工程的构建过程。在windows的命令行下,在该系统的根目录下输入命令make和相应的参数即可开始工程的构建,该批处理文件的使用方法如下所示。
Usage: make ["customer""mt62xx"] "project" "action" ["modules"]
增加模块的配置实例
若我们需要加入zlib这么一个模块(zlib包含了很多程序都用到的压缩和解压函数),我们可以按如下步骤进行。
1. 把zlib的源代码包拷贝到你的MTK软件系统根目录下,这样所有的zlib代码都在zlib目录或zlib-1.2.3目录下(用哪个目录名由你的喜好来确定),在此为简单起见就放在zlib目录。
2. 在make目录下新增一个zlib的目录(最好和根目录下的目录名一样),增加四个文件,分别是zlib.def、zlib.inc、zlib.lis、zlib.pth。zlib.def文件只要加上APCS_INTWORK就可以了,其他三个文件中加上源文件及其目录、头文件目录即可。
3. 在REL_CR_MMI_.mak这个文件的合适位置加上如下语句。CUS_REL_SRC_COMP += zlib
4. 把这个工程remake一下,若没有错误,zlib模块就成功加上去了。其他模块就可以调用zlib提供的压缩和解压缩函数了。
Nucleus内核的主要目的是管理实时任务的调度运行,共享处理器资源,为应用提供各种便利,快速响应外部事件,实现实时性。利用Nucleus开发平台,用户只需要编写任务代码(Task)和中断服务程序代码(ISR),二者利用系统调用实现与Nucleus的交互,由Nucleus内核来调度多个任务并行执行,实现处理器共享。
公司想MTK解决方案中添加手柄概念进去。MTK使用Nucleus 操作系统,它是由ATI开发的RTOS.
MTK使用了nucleus实时操作系统,在其上做了个内核抽象层的封装(Kal) ,以适应多种实时操作系统,如oscar、ThreadX、nucleus。整个软件系统包括nucleus操作系统、平台设备驱动、协议栈、文件系统、WGUI、MMI、J2ME等。在这里MMI部分几乎包括了操作系统内核、协议栈、文件系统之上的所有部分,其中WGUI也在其中。
Nucleus是单一地址空间操作系统的一种,作为商业化的嵌入式操作系统产品,曾被广泛使用。在MIPS架构中,其操作系统和用户程序完全工作在内核模式,且只占用和访问0x80000000以上的线性地址空间。因此,在Nucleus中,操作系统和用户程序工作在线性地址空间中,且用户程序与内核服务之间没有明显的区分,进入内核服务更像是调用API(Application Program Interface)而不需要上下文切换。其优点是限制少,编程方便,但系统健壮性差。
MTK手机软件系统的构建使用了GNU的make,使得整个工程的构建可以自动进行,且可以灵活控制。整个构建过程由Make.bat、make2.pl、MoDIS.dsw、Gsm2.mak、Option.mak、
在MTK手机软件系统的根目录下有一个批处理文件Make.bat,这个批处理文件启动了整个工程的构建过程。在windows的命令行下,在该系统的根目录下输入命令make和相应的参数即可开始工程的构建,该批处理文件的使用方法如下所示。
Usage: make ["customer""mt62xx"] "project" "action" ["modules"]
增加模块的配置实例
若我们需要加入zlib这么一个模块(zlib包含了很多程序都用到的压缩和解压函数),我们可以按如下步骤进行。
1. 把zlib的源代码包拷贝到你的MTK软件系统根目录下,这样所有的zlib代码都在zlib目录或zlib-1.2.3目录下(用哪个目录名由你的喜好来确定),在此为简单起见就放在zlib目录。
2. 在make目录下新增一个zlib的目录(最好和根目录下的目录名一样),增加四个文件,分别是zlib.def、zlib.inc、zlib.lis、zlib.pth。zlib.def文件只要加上APCS_INTWORK就可以了,其他三个文件中加上源文件及其目录、头文件目录即可。
3. 在REL_CR_MMI_
4. 把这个工程remake一下,若没有错误,zlib模块就成功加上去了。其他模块就可以调用zlib提供的压缩和解压缩函数了。
Nucleus内核的主要目的是管理实时任务的调度运行,共享处理器资源,为应用提供各种便利,快速响应外部事件,实现实时性。利用Nucleus开发平台,用户只需要编写任务代码(Task)和中断服务程序代码(ISR),二者利用系统调用实现与Nucleus的交互,由Nucleus内核来调度多个任务并行执行,实现处理器共享。
2009年2月23日星期一
嵌入式系统中的usleep延迟
作者:Sam(甄峰) sam_code@hotmail.com
Sam在作嵌入式系统中,发现类似usleep这样的函数在不同平台上延迟并不相同。所以如果想要延迟一段时间,尤其是比较精确的延迟,应慎重使用类似函数。
举例而言:
例1:
usleep(5000); //延迟5ms.
在PC-linux中,延迟时间大约在5020-5030微秒之间。
在Canmore(800M CPU)中,延迟则会大很多, 在8010微秒左右。
在X5平台(300M CPU)中, 延迟竟然大到20000微秒左右。
当然,这不排除Sam使用gettimeofday() 函数所用时间,但可以肯定的是,在不同CPU速度下,usleep() 延迟造成的效果应该差别很大。
例2:
usleep(50); //延迟50微秒
在PC-linux中,延迟时间大约在59-60微秒之间。
在Canmore(800M CPU)中,延迟则会大很多, 在4000微秒左右。
在X5平台(300M CPU)中, 延迟竟然大到20000微秒左右。
例3:
usleep(50000); //延迟50000微秒
在PC-linux中,延迟时间大约在50076微秒左右。
在Canmore(800M CPU)中,延迟则会大很多, 在56047微秒左右。
在X5平台(300M CPU)中, 延迟到60000微秒左右。
从上面的数据看,在嵌入式系统中,最好不用时用usleep这类函数,因为他们特别不准确。当延迟比较大时,稍微准确一些,但延迟小到一定程度时,延迟会非常不准确。好像有一个延迟最低点。低于这个数据后,不管你想要延迟多少,系统都会延迟这么久。例如X5平台的20000微秒。
Sam在作嵌入式系统中,发现类似usleep这样的函数在不同平台上延迟并不相同。所以如果想要延迟一段时间,尤其是比较精确的延迟,应慎重使用类似函数。
举例而言:
例1:
usleep(5000); //延迟5ms.
在PC-linux中,延迟时间大约在5020-5030微秒之间。
在Canmore(800M CPU)中,延迟则会大很多, 在8010微秒左右。
在X5平台(300M CPU)中, 延迟竟然大到20000微秒左右。
当然,这不排除Sam使用gettimeofday() 函数所用时间,但可以肯定的是,在不同CPU速度下,usleep() 延迟造成的效果应该差别很大。
例2:
usleep(50); //延迟50微秒
在PC-linux中,延迟时间大约在59-60微秒之间。
在Canmore(800M CPU)中,延迟则会大很多, 在4000微秒左右。
在X5平台(300M CPU)中, 延迟竟然大到20000微秒左右。
例3:
usleep(50000); //延迟50000微秒
在PC-linux中,延迟时间大约在50076微秒左右。
在Canmore(800M CPU)中,延迟则会大很多, 在56047微秒左右。
在X5平台(300M CPU)中, 延迟到60000微秒左右。
从上面的数据看,在嵌入式系统中,最好不用时用usleep这类函数,因为他们特别不准确。当延迟比较大时,稍微准确一些,但延迟小到一定程度时,延迟会非常不准确。好像有一个延迟最低点。低于这个数据后,不管你想要延迟多少,系统都会延迟这么久。例如X5平台的20000微秒。
2009年2月19日星期四
高速数据交换时的lock问题
作者:Sam(甄峰) sam_code@hotmail.com
Sam在作Bluetooth Remote Control程序时,遇到一个问题。
手柄每5ms发送一个数据包。
而Sam程序是这样设计的:
在手柄连接时,BTRC创建Read thread. 在Read thread中会读取手柄数据并解析包。解析结果放到一个buffer(结构体)中。
另外一边,应用程序会使用BTRC的read接口去读取这个buffer的内容。这个应用程序应该会用一个thread去不断的调用BTRC的read接口去读取。
方案一:
Sam在解析包部分向buffer中写东西时会加锁,写完会解锁。BTRC的read接口读取buffer时加锁,读完会解锁。
但这样有个后果:
1. 如果 双方都是全速运行,也就是说:如果双方都不添加sleep.则会出现:程序先进入BTRC的read接口读取buffer很多次(20次)左右。这个时候,BTRC的使用BTX去读取并解析然后写入buffer的程序的lock无法进入。
然后,在某一时刻,BTRC使用BTX区读取并解析然后写入buffer的程序又集中运行很多次(15-20次)。
这就造成上层程序使用BTRC的读取接口读到的解析后的数据完全不正确。
2. 如果上层程序调用BTRC的读取接口时作一个延迟,比如4ms.
这样的话,因为程序使用BTRC 的读取接口有一个延迟,所以BTRC是用BTX去读取并解析然后写入buffer的程序有更大机会去写入。但会造成写入多个才能够读取一次的情况。例如:A包过来了,BTRC使用BTX去读取,解析后写入buffer. A+1包又过来了,然后BTRC使用BTX读取时,BTRC的读接口来读buffer,但buffer已经被lock了。所以只能等下一次去读取了。这造成只能读取第二个包。所以还是会造成丢包。
3. 如果去掉lock,并全速运行:
现象与情况一相同。
方案二:
将BTRC的读接口改造,每次不是再去读当前的一份数据,而是如下去做:
用返回值表示从上次读取到此次读取这段时间内,buffer中新的数据包个数。参数为指针的指针。指向数据头。
注[1]:Sam有个感觉,在不同速度的CPU上,尤其是一些嵌入式平台中,thread的schedule速度好像比较慢,所以在不同thread之前的切换速度比较慢。像在X5平台上,BTRC的Read接口去read buffer, 竟然只能读到30%-40%的包。Canmore可以读到70%的包。而PC上则可以读到100%的包,还有重复读到的。
Sam在作Bluetooth Remote Control程序时,遇到一个问题。
手柄每5ms发送一个数据包。
而Sam程序是这样设计的:
在手柄连接时,BTRC创建Read thread. 在Read thread中会读取手柄数据并解析包。解析结果放到一个buffer(结构体)中。
另外一边,应用程序会使用BTRC的read接口去读取这个buffer的内容。这个应用程序应该会用一个thread去不断的调用BTRC的read接口去读取。
方案一:
Sam在解析包部分向buffer中写东西时会加锁,写完会解锁。BTRC的read接口读取buffer时加锁,读完会解锁。
但这样有个后果:
1. 如果 双方都是全速运行,也就是说:如果双方都不添加sleep.则会出现:程序先进入BTRC的read接口读取buffer很多次(20次)左右。这个时候,BTRC的使用BTX去读取并解析然后写入buffer的程序的lock无法进入。
然后,在某一时刻,BTRC使用BTX区读取并解析然后写入buffer的程序又集中运行很多次(15-20次)。
这就造成上层程序使用BTRC的读取接口读到的解析后的数据完全不正确。
2. 如果上层程序调用BTRC的读取接口时作一个延迟,比如4ms.
这样的话,因为程序使用BTRC 的读取接口有一个延迟,所以BTRC是用BTX去读取并解析然后写入buffer的程序有更大机会去写入。但会造成写入多个才能够读取一次的情况。例如:A包过来了,BTRC使用BTX去读取,解析后写入buffer. A+1包又过来了,然后BTRC使用BTX读取时,BTRC的读接口来读buffer,但buffer已经被lock了。所以只能等下一次去读取了。这造成只能读取第二个包。所以还是会造成丢包。
3. 如果去掉lock,并全速运行:
现象与情况一相同。
方案二:
将BTRC的读接口改造,每次不是再去读当前的一份数据,而是如下去做:
用返回值表示从上次读取到此次读取这段时间内,buffer中新的数据包个数。参数为指针的指针。指向数据头。
注[1]:Sam有个感觉,在不同速度的CPU上,尤其是一些嵌入式平台中,thread的schedule速度好像比较慢,所以在不同thread之前的切换速度比较慢。像在X5平台上,BTRC的Read接口去read buffer, 竟然只能读到30%-40%的包。Canmore可以读到70%的包。而PC上则可以读到100%的包,还有重复读到的。
2009年2月18日星期三
Hi3110 EV 100平台上Bluetooth的移植
作者:Sam(甄峰) sam_code@hotmail.com
Sam想把手柄库移植到海思 Hi3100 EV 100平台上去。先进行评估如下:
1. Hi3100 EV 100为ARM平台。
2. Hi3100 EV 100使用uclibc。
这两个差异决定了需要从bluetooth 底层库开始移植。
一:
Sam从linux kernel中编译出了bluetooth.ko, hci_usb.ko, l2cap.ko, hidp.ko。
二:
下载新版bluez.
http://www.bluez.org/
三:
交叉编译bluez:
交叉编译时发现,新版bluez要基于D-BUS等库。所以Sam暂时使用bluez 1.0版本。
#cd bluez-libs-2.12
# CC=/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc ./configure --host=arm-linux
#make clean;make
#cd bluez-libs-2.12/src/.libs
看到libbluetooth.so就在这里。
四:
交叉编译BTX:
在BTX Makefile中,
CC := /opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc
CFLAGS := -D_SHOW -D_DEBUGTIMEOUT -Wall -I../include -I/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/
LFLAGS := -L./ -L../resource -L/opt/hisilicon/toolchains/sysroot-uclibc-soft/usr/lib
编译libBTX.so和BTX_Test成功。
其中libBTX.so编译选项如下:
$(CC) $(CFLAGS) $(LFLAGS) -fPIC -shared BTX.o -o libBTX.so -lbluetooth
但奇怪的是,在Hi3110 EV100平台上,运行BTX_Test会出现如下信息:
Can't modify .//libBTX.so's text section. Use GCC option -fPIC for shared objects, please.
Sam只好把BTX并入程序中。
五:
编译Bluetooth_remote:
CC := /opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc CFLAGS := -D_SHOW -D_DEBUGTIMEOUT -Wall -I../include -I/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/ LFLAGS := -L./ -L../resource -L/opt/hisilicon/toolchains/sysroot-uclibc-soft/usr/lib
并将BTX.c编译进去。
编译成功。
但运行时会有问题,反应比较慢。Sam还在查。
Sam想把手柄库移植到海思 Hi3100 EV 100平台上去。先进行评估如下:
1. Hi3100 EV 100为ARM平台。
2. Hi3100 EV 100使用uclibc。
这两个差异决定了需要从bluetooth 底层库开始移植。
一:
Sam从linux kernel中编译出了bluetooth.ko, hci_usb.ko, l2cap.ko, hidp.ko。
二:
下载新版bluez.
http://www.bluez.org/
三:
交叉编译bluez:
交叉编译时发现,新版bluez要基于D-BUS等库。所以Sam暂时使用bluez 1.0版本。
#cd bluez-libs-2.12
# CC=/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc ./configure --host=arm-linux
#make clean;make
#cd bluez-libs-2.12/src/.libs
看到libbluetooth.so就在这里。
四:
交叉编译BTX:
在BTX Makefile中,
CC := /opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc
CFLAGS := -D_SHOW -D_DEBUGTIMEOUT -Wall -I../include -I/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/
LFLAGS := -L./ -L../resource -L/opt/hisilicon/toolchains/sysroot-uclibc-soft/usr/lib
编译libBTX.so和BTX_Test成功。
其中libBTX.so编译选项如下:
$(CC) $(CFLAGS) $(LFLAGS) -fPIC -shared BTX.o -o libBTX.so -lbluetooth
但奇怪的是,在Hi3110 EV100平台上,运行BTX_Test会出现如下信息:
Can't modify .//libBTX.so's text section. Use GCC option -fPIC for shared objects, please.
Sam只好把BTX并入程序中。
五:
编译Bluetooth_remote:
CC := /opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc CFLAGS := -D_SHOW -D_DEBUGTIMEOUT -Wall -I../include -I/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/ LFLAGS := -L./ -L../resource -L/opt/hisilicon/toolchains/sysroot-uclibc-soft/usr/lib
并将BTX.c编译进去。
编译成功。
但运行时会有问题,反应比较慢。Sam还在查。
2009年2月15日星期日
FCEU 分析
作者: Sam(甄峰) sam_code@hotmail.com
Sam下载了fceu_0.96.tar.gz.
首先察看 Makefile.
这个Makefile包含makefile.base。将所有东西编译进去。因为是Linux版本,所以底层使用SDL.
所以Makefile将drivers/cli 中的sdl有关的内容全部编译进去。
主程序从 drivers/cli/sdl.c中开始。
大致看来,FCEU底层显示和声音基本都是使用SDL实现的。
Makefile中,有一项-DUNIXDSP。如果使用这一项,则直接使用OSS driver建立的device--/dev/dsp来播放声音。如果不使用这一项,则通过SDL播放。 SDL底层则可以适配到不同的音频设备上,如OSS, ALSA等。
代码解析:
在Fceu代码目录中,drivers/cli为Linux下实现Fceu的代码段。因为Fceu基于SDL实现,所以底层和SDL有很大关系。
代码起点放在: drivers/cli/sdl.c中。
SetDefaults(); 用来初始化一些设置,这些设置放在全局变量Settings中。
然后进入Fceu的主体:CLImain()
1.FCEUI_Initialize()
NES(FC)的分辨率为:256x224(NTSC) 或 256x239(PAL)
1.1 分配video buffer.
每个scanline有256+15 byte.
一共有最多240个scanline.
另外还有8 byte的alignment。 8byte的catch overflow
所以分配了 (256+16)x 240 + 8 + 8 字节的buffer,并设为128。
1.2 建立目录。
在 "HOME"目录下创建.fceultra 目录,并在其中创建5个目录。
例如: /home/sam/.fceultra/
1.3 load config 或建立config file.
查/home/sam/.fceultra/fceu.cfg文件,如果有,则提取其config,如果没有,则建立。
Sam下载了fceu_0.96.tar.gz.
首先察看 Makefile.
这个Makefile包含makefile.base。将所有东西编译进去。因为是Linux版本,所以底层使用SDL.
所以Makefile将drivers/cli 中的sdl有关的内容全部编译进去。
主程序从 drivers/cli/sdl.c中开始。
大致看来,FCEU底层显示和声音基本都是使用SDL实现的。
Makefile中,有一项-DUNIXDSP。如果使用这一项,则直接使用OSS driver建立的device--/dev/dsp来播放声音。如果不使用这一项,则通过SDL播放。 SDL底层则可以适配到不同的音频设备上,如OSS, ALSA等。
代码解析:
在Fceu代码目录中,drivers/cli为Linux下实现Fceu的代码段。因为Fceu基于SDL实现,所以底层和SDL有很大关系。
代码起点放在: drivers/cli/sdl.c中。
SetDefaults(); 用来初始化一些设置,这些设置放在全局变量Settings中。
然后进入Fceu的主体:CLImain()
1.FCEUI_Initialize()
NES(FC)的分辨率为:256x224(NTSC) 或 256x239(PAL)
1.1 分配video buffer.
每个scanline有256+15 byte.
一共有最多240个scanline.
另外还有8 byte的alignment。 8byte的catch overflow
所以分配了 (256+16)x 240 + 8 + 8 字节的buffer,并设为128。
1.2 建立目录。
在 "HOME"目录下创建.fceultra 目录,并在其中创建5个目录。
例如: /home/sam/.fceultra/
1.3 load config 或建立config file.
查/home/sam/.fceultra/fceu.cfg文件,如果有,则提取其config,如果没有,则建立。
2009年2月5日星期四
SDL的移植
作者:Sam(甄峰) sam_code@hotmail.com
Sam有一个计划,将SDL video driver底层适配到dfb和framebuffer上。之前做过一些工作,虽然编译通过了,但SDL例子程序跑起来却会有问题。这次决定重新作一次。
SDL简介:
SDL(Simple DirectMedia Layer),一个简单的游戏开发库。SDL是一个跨平台的多媒体函数库,被用来设计成在低层访问音频,键盘,鼠标,游戏杆,基于OPENGL(一个3D图象开发函数库)的3D 硬件,和2D图象帧缓冲区。Sam:这个图像帧缓冲区是否就是framebuffer??
1.下载SDL:
http://www.libsdl.org/download-1.2.php
2. SDL代码结构粗浅理解:
Sam看过SDL/src目录后,对代码结构有个粗浅的理解。
以下以audio和video目录为例谈一谈。
2.1 audio目录:
audio目录中包括一些.c文件和一些目录,如alsa,dsp,dummy等。
Sam的理解是:audio目录中的.c文件是上层程序使用audio的统一接口。而这些目录如alsa,dsp,dummy等中的代码则是SDL对具体使用何种audio device的适配。alsa就是audio统一接口对alsa的适配,dsp则为对OSS的适配等等。
2.2 video目录:
同上video目录下的.c文件为上层程序显示的统一接口。目录内的程序则为各种显示方法的适配,
如fbcon为framebuffer. directfb为dfb。
3.编译尝试:
3.1: SDL提供了一个最小化的Makefile:Makefile.minimal,所有的实现都是dummy,就是一个空的实现,编译能通过,但运行时什么都不能做.
我们先研究一下这个Makefile:
INCLUDE = -I./include
CFLAGS = -g -O2 $(INCLUDE)
AR = ar
RANLIB = ranlib
CONFIG_H = include/SDL_config.h
TARGET = libSDL.a
SOURCES = \
src/*.c \
src/audio/*.c \
src/cdrom/*.c \
src/cpuinfo/*.c \
src/events/*.c \
src/file/*.c \
src/joystick/*.c \
src/stdlib/*.c \
src/thread/*.c \
src/timer/*.c \
src/video/*.c \
src/audio/dummy/*.c \
src/video/dummy/*.c \
src/joystick/dummy/*.c \
src/cdrom/dummy/*.c \
src/thread/generic/*.c \
src/timer/dummy/*.c \
src/loadso/dummy/*.c \
OBJECTS = $(shell echo $(SOURCES) sed -e 's,\.c,\.o,g')
all: $(TARGET)
$(TARGET): $(CONFIG_H) $(OBJECTS)
$(AR) crv $@ $^ $(RANLIB) $@
$(CONFIG_H):
cp $(CONFIG_H).default $(CONFIG_H)
clean:
rm -f $(TARGET) $(OBJECTS)
这个Makefile很简单:
#make all 会生成libSDL.a
libSDL.a是所有.o ar成的一个库。
注意:OBJECTS= SOURCES的.c 变.o
#make clean 会删除libSDL.a和所有.o
3.2 正式编译
那怎样把想要的实现加上去呢?
比如:
3.2.1
Sam想在Video上选择framebuffer,在audio上选择alsa. 该怎么做呢?
则可以在Makefile中添加:
src/video/fbcon/*.c
src/audio/alsa/*.c
直接编译,发现编译成功。生成了libSDL.a
3.2.2
Sam想要把底层适配为如下模式:
Video: directfb
audio: alsa
则Sam如下添加:
src/video/directfb/*.c
src/audio/alsa/*.c
编译后会报错。显示缺乏directfb头文件。
于是Sam在Makefile中添加了:
-I/home/sam/work/current/Intel_CE_3110/Intel_CE3110_Dev/Canmore-1.1078/i686-linux-elf/usr/local/include/directfb
但发现为跨平台准备的类型定义重定义了。
这其实是因为SDL和Directfb都为跨平台考虑,tyedef了类型。
但directfb中是:typedef unsigned long uintptr_t;
SDL中为:typedef unsigned int uintptr_t;
所以Sam修改了SDL中的typedef
则可以编译成功。
注意:虽然编译成功,但其实bootstrap中并未加入fbcon或者directfb.
所以Sam又在include/SDL_config_minimal.h中添加了:
/* Sam add it for fb*/
#define SDL_VIDEO_DRIVER_FBCON 1
则fb被加入了。
SDL Video架构:
基于SDL的程序初始化时,会调用 SDL_Init()。
SDL_Init(flags)通过调用SDL_InitSubSystem(flags)来初始化各个子系统。具体哪个子系统,通过flags指定。
如果flags指定为video. 则调用SDL_VideoInit()
在SDL_VideoInit() 中,通过bootstrap去分别指向video底层的各个实现。
SDL设计的很精巧,bootstrap中只有2个接口:available,create。
其中available用来查询设备是否可用。 create用来对上层提供所有接口。
所以,如果有人想要在嵌入式系统中移植SDL,则要么支持fb或dfb.要么提供一套新的接口。并利用available和create将他们提供给上层。
SDL中有个结构:
SDL_PixelFormat:
这个结构是用来表示每个Pixel的格式的。因为SDL适配于多种显示设备上。所以用这个结构体来抽象每个Pixel。
这个结构中类似BitsPerPixel这样的含义很简单。
还有一下这些项目:
//每个色彩的精确度损失
Uint8 Rloss;
Uint8 Gloss;
Uint8 Bloss;
Uint8 Aloss;
//每个piexl中各个颜色的偏移
Uint8 Rshift;
Uint8 Gshift;
Uint8 Bshift;
Uint8 Ashift;
//每个piexl中对应色彩的Mask。
Uint32 Rmask;
Uint32 Gmask;
Uint32 Bmask;
Uint32 Amask;
这些数值可以和framebuffer中的fb_bitfield 结构对应起来看。
例如:
从Framebuffer中,读到的fb_var_screeninfo 中:
red:offset=10,length=5
Blue: offset=5, length=5
green:offset=0, length=5
transp:offset=15,length=1
这说明一个pixel结构如下:
GBRA
5551 =16bit
那么:
Rshift=Red.offset=10
Bshift = Blue.offset=5
Gshift = Green。offset=0
Ashift=transp.offset=15
Rmask=111110000000000B
Bmask=000001111100000B
Gmask=000000000111110B
Amask=00000000000001B
Sam有一个计划,将SDL video driver底层适配到dfb和framebuffer上。之前做过一些工作,虽然编译通过了,但SDL例子程序跑起来却会有问题。这次决定重新作一次。
SDL简介:
SDL(Simple DirectMedia Layer),一个简单的游戏开发库。SDL是一个跨平台的多媒体函数库,被用来设计成在低层访问音频,键盘,鼠标,游戏杆,基于OPENGL(一个3D图象开发函数库)的3D 硬件,和2D图象帧缓冲区。Sam:这个图像帧缓冲区是否就是framebuffer??
1.下载SDL:
http://www.libsdl.org/download-1.2.php
2. SDL代码结构粗浅理解:
Sam看过SDL/src目录后,对代码结构有个粗浅的理解。
以下以audio和video目录为例谈一谈。
2.1 audio目录:
audio目录中包括一些.c文件和一些目录,如alsa,dsp,dummy等。
Sam的理解是:audio目录中的.c文件是上层程序使用audio的统一接口。而这些目录如alsa,dsp,dummy等中的代码则是SDL对具体使用何种audio device的适配。alsa就是audio统一接口对alsa的适配,dsp则为对OSS的适配等等。
2.2 video目录:
同上video目录下的.c文件为上层程序显示的统一接口。目录内的程序则为各种显示方法的适配,
如fbcon为framebuffer. directfb为dfb。
3.编译尝试:
3.1: SDL提供了一个最小化的Makefile:Makefile.minimal,所有的实现都是dummy,就是一个空的实现,编译能通过,但运行时什么都不能做.
我们先研究一下这个Makefile:
INCLUDE = -I./include
CFLAGS = -g -O2 $(INCLUDE)
AR = ar
RANLIB = ranlib
CONFIG_H = include/SDL_config.h
TARGET = libSDL.a
SOURCES = \
src/*.c \
src/audio/*.c \
src/cdrom/*.c \
src/cpuinfo/*.c \
src/events/*.c \
src/file/*.c \
src/joystick/*.c \
src/stdlib/*.c \
src/thread/*.c \
src/timer/*.c \
src/video/*.c \
src/audio/dummy/*.c \
src/video/dummy/*.c \
src/joystick/dummy/*.c \
src/cdrom/dummy/*.c \
src/thread/generic/*.c \
src/timer/dummy/*.c \
src/loadso/dummy/*.c \
OBJECTS = $(shell echo $(SOURCES) sed -e 's,\.c,\.o,g')
all: $(TARGET)
$(TARGET): $(CONFIG_H) $(OBJECTS)
$(AR) crv $@ $^ $(RANLIB) $@
$(CONFIG_H):
cp $(CONFIG_H).default $(CONFIG_H)
clean:
rm -f $(TARGET) $(OBJECTS)
这个Makefile很简单:
#make all 会生成libSDL.a
libSDL.a是所有.o ar成的一个库。
注意:OBJECTS= SOURCES的.c 变.o
#make clean 会删除libSDL.a和所有.o
3.2 正式编译
那怎样把想要的实现加上去呢?
比如:
3.2.1
Sam想在Video上选择framebuffer,在audio上选择alsa. 该怎么做呢?
则可以在Makefile中添加:
src/video/fbcon/*.c
src/audio/alsa/*.c
直接编译,发现编译成功。生成了libSDL.a
3.2.2
Sam想要把底层适配为如下模式:
Video: directfb
audio: alsa
则Sam如下添加:
src/video/directfb/*.c
src/audio/alsa/*.c
编译后会报错。显示缺乏directfb头文件。
于是Sam在Makefile中添加了:
-I/home/sam/work/current/Intel_CE_3110/Intel_CE3110_Dev/Canmore-1.1078/i686-linux-elf/usr/local/include/directfb
但发现为跨平台准备的类型定义重定义了。
这其实是因为SDL和Directfb都为跨平台考虑,tyedef了类型。
但directfb中是:typedef unsigned long uintptr_t;
SDL中为:typedef unsigned int uintptr_t;
所以Sam修改了SDL中的typedef
则可以编译成功。
注意:虽然编译成功,但其实bootstrap中并未加入fbcon或者directfb.
所以Sam又在include/SDL_config_minimal.h中添加了:
/* Sam add it for fb*/
#define SDL_VIDEO_DRIVER_FBCON 1
则fb被加入了。
SDL Video架构:
基于SDL的程序初始化时,会调用 SDL_Init()。
SDL_Init(flags)通过调用SDL_InitSubSystem(flags)来初始化各个子系统。具体哪个子系统,通过flags指定。
如果flags指定为video. 则调用SDL_VideoInit()
在SDL_VideoInit() 中,通过bootstrap去分别指向video底层的各个实现。
SDL设计的很精巧,bootstrap中只有2个接口:available,create。
其中available用来查询设备是否可用。 create用来对上层提供所有接口。
所以,如果有人想要在嵌入式系统中移植SDL,则要么支持fb或dfb.要么提供一套新的接口。并利用available和create将他们提供给上层。
SDL中有个结构:
SDL_PixelFormat:
这个结构是用来表示每个Pixel的格式的。因为SDL适配于多种显示设备上。所以用这个结构体来抽象每个Pixel。
这个结构中类似BitsPerPixel这样的含义很简单。
还有一下这些项目:
//每个色彩的精确度损失
Uint8 Rloss;
Uint8 Gloss;
Uint8 Bloss;
Uint8 Aloss;
//每个piexl中各个颜色的偏移
Uint8 Rshift;
Uint8 Gshift;
Uint8 Bshift;
Uint8 Ashift;
//每个piexl中对应色彩的Mask。
Uint32 Rmask;
Uint32 Gmask;
Uint32 Bmask;
Uint32 Amask;
这些数值可以和framebuffer中的fb_bitfield 结构对应起来看。
例如:
从Framebuffer中,读到的fb_var_screeninfo 中:
red:offset=10,length=5
Blue: offset=5, length=5
green:offset=0, length=5
transp:offset=15,length=1
这说明一个pixel结构如下:
GBRA
5551 =16bit
那么:
Rshift=Red.offset=10
Bshift = Blue.offset=5
Gshift = Green。offset=0
Ashift=transp.offset=15
Rmask=111110000000000B
Bmask=000001111100000B
Gmask=000000000111110B
Amask=00000000000001B
2009年2月4日星期三
GDB学习及嵌入式下如何使用Debug
作者: Sam(甄峰) sam_code@hotmail.com
前段时间和朋友聊天,谈到工作中使用什么调试工具,Sam很不好意思地讲,我只用printf打印信息调试,被狠狠地嘲笑了一通。Sam一直作嵌入式程序或kernel层程序,在这个领域内,用调试工具的人好像还真不算多。但Sam很认同朋友的话,调试工具能够极大的提高效率。所以决定借当前的移植工作把GDB用起来。
GDB(GNU symbolic debugger)
GDB可以让你调试一个程序,包括让程序在你希望的地方停下,此时你可以查看变量,寄存器,内存及堆栈。更进一步你可以修改变量及内存值。
Sam首先用BT_remote来做GDB试验。
1. 编译时需要加入选项 -g
2. 使用GDB 启动程序。
#gdb BT_remote
GNU gdb Red Hat Linux (6.6-35.fc8rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) l //相当于list,列出代码
166 int main(int argc, char** argv)
167 {
168 BOOL bDongle_Ready = FALSE;
169 BTRC_Rel BTRC_Rel_all;
170 int iRet_pthread_create = 0;
171 pthread_t ntid;
172 UINT8 ID;
173 UINT16 SetAudioFile_rel;
174 int i = 0;
175 BT_CONNECTION_STATUS status;
176
177
178
179 BTRC_Rel_all = BluetoothDongleInit();
180 if(BTRC_Rel_all != BTRC_SUCCESS)
181 {
182 printf("\nBluetooth Dongle Init fail. Error Code:[%d]\n", BTRC_Rel_all);
183 return -1;
184 }
(gdb) break 169 //在168行设置断点
(gdb) break BluetoothDongleInit //在函数BluetoothDongleInit处加断点
(gdb) info break //察看断点
Num Type Disp Enb Address What
1 breakpoint keep yBluetoothDongleInit
2 breakpoint keep y 0x0804925c in main at main.c:169
(gdb)clear169 //清除Line 169断点
(gdb)watch iBluetooth_Rel_all //将变量设为观察点,当此变量有变化时,程序立刻停住
(gdb)info watchpoints //显示观察点
(gdb) r //run,开始run程序
到了第一个断点,则停下来,可以用 l 命令看其前后代码,也可以用p 来看某个变量的值
(gdb) p bDongle_Ready
$1 = FALSE
(gdb) n //next
(gdb) c //继续执行
主题二:
嵌入式下如何使用GDB:
1. 下载GDB.
http://ftp.gnu.org/gnu/gdb/
2. 编译:
(注意:因为此gdbserver是为Canmore使用的,所以不需要类似:CC=arm-linux-gcc ./configure --host=.... --target==... 这样的东西,如果是其它ARM平台,则需要这样做)
2.1 编译GDB
#./configure
#make clean;make
2.2 编译gdbserver
#cd gdb/gdbserver
#./configure
#make clean;make
将生成的gdbserver放到嵌入式板子上去。
3. copy 所需库:
将libthread_db copy到嵌入式平台。
4. 使用
4.1在嵌入式平台:
#gdbserver 172.16.1.61:1234 BT_remote
Process BT_remote created; pid = 716
Listening on port 1234
这个意思是:debug BT_remote这个程序。对方是172.16.1.61. 端口为:1234。
4.2在Host 端:
#gdb BT_remote
GNU gdb Red Hat Linux (6.6-35.fc8rh)Copyright (C) 2006 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) target remote 172.16.1.31:1234
Undefined target command: "172.16.1.31:1234 ". Try "help target".(gdb) target remote 172.16.1.31:1234`system-supplied DSO at 0x110000' has disappeared; keeping its symbols.Remote debugging using 172.16.1.31:1234[New Thread 716]
warning: Missing the separate debug info file: /usr/lib/debug/.build-id/ac/2eeb206486bb7315d6ac4cd64de0cb50838ff6.debug0xb7f17810 in _start () from /lib/ld-linux.so.2
此时发现嵌入式板子上有反应:
Remote debugging from host 172.16.1.61
则表明嵌入式板子和Host Debug链接成功,则可以在Host上调试嵌入式板子上的程序了。
但请注意,在Host上,不能使用 r 来运行,而应该使用 c来运行,因为这个程序已经在运行了。
则可以正常使用了。
嵌入式平台上GDB调试实例:
Sam想在X5平台上使用GDB来调试程序。
分析:
1. 需要gdbserver
2. 客户端需要gdb程序。
3. 需要重新编译,加入 -g.
1.编译gdbserver:
#cd gdb-6.8/gdb/gdbserver
#CC=/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc ./configure --host=arm-linux
#make clean;make all
则gdbserver生成成功。Sam把它copy到板子的/bin目录中
2. 客户端gdb程序。
直接使用交叉编译器中的arm-uclibc-linux-gdb
3.重新编译程序。
Sam是想调试SDL例子程序,所以在SDL库生成过程和test生成过程中均加入 -g.
实际操作:
1. 嵌入式板子中:
#gdbserver 172.16.1.61:1234 testsprite
2. Host
2.1
#arm-uclibc-linux-gdb testsprite
2.2
(gdb) target remote 172.16.1.45:1234
2.3
(gdb) c
之后和本地调试类似。
前段时间和朋友聊天,谈到工作中使用什么调试工具,Sam很不好意思地讲,我只用printf打印信息调试,被狠狠地嘲笑了一通。Sam一直作嵌入式程序或kernel层程序,在这个领域内,用调试工具的人好像还真不算多。但Sam很认同朋友的话,调试工具能够极大的提高效率。所以决定借当前的移植工作把GDB用起来。
GDB(GNU symbolic debugger)
GDB可以让你调试一个程序,包括让程序在你希望的地方停下,此时你可以查看变量,寄存器,内存及堆栈。更进一步你可以修改变量及内存值。
Sam首先用BT_remote来做GDB试验。
1. 编译时需要加入选项 -g
2. 使用GDB 启动程序。
#gdb BT_remote
GNU gdb Red Hat Linux (6.6-35.fc8rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) l //相当于list,列出代码
166 int main(int argc, char** argv)
167 {
168 BOOL bDongle_Ready = FALSE;
169 BTRC_Rel BTRC_Rel_all;
170 int iRet_pthread_create = 0;
171 pthread_t ntid;
172 UINT8 ID;
173 UINT16 SetAudioFile_rel;
174 int i = 0;
175 BT_CONNECTION_STATUS status;
176
177
178
179 BTRC_Rel_all = BluetoothDongleInit();
180 if(BTRC_Rel_all != BTRC_SUCCESS)
181 {
182 printf("\nBluetooth Dongle Init fail. Error Code:[%d]\n", BTRC_Rel_all);
183 return -1;
184 }
(gdb) break 169 //在168行设置断点
(gdb) break BluetoothDongleInit //在函数BluetoothDongleInit处加断点
(gdb) info break //察看断点
Num Type Disp Enb Address What
1 breakpoint keep y
2 breakpoint keep y 0x0804925c in main at main.c:169
(gdb)clear169 //清除Line 169断点
(gdb)watch iBluetooth_Rel_all //将变量设为观察点,当此变量有变化时,程序立刻停住
(gdb)info watchpoints //显示观察点
(gdb) r //run,开始run程序
到了第一个断点,则停下来,可以用 l 命令看其前后代码,也可以用p 来看某个变量的值
(gdb) p bDongle_Ready
$1 = FALSE
(gdb) n //next
(gdb) c //继续执行
主题二:
嵌入式下如何使用GDB:
1. 下载GDB.
http://ftp.gnu.org/gnu/gdb/
2. 编译:
(注意:因为此gdbserver是为Canmore使用的,所以不需要类似:CC=arm-linux-gcc ./configure --host=.... --target==... 这样的东西,如果是其它ARM平台,则需要这样做)
2.1 编译GDB
#./configure
#make clean;make
2.2 编译gdbserver
#cd gdb/gdbserver
#./configure
#make clean;make
将生成的gdbserver放到嵌入式板子上去。
3. copy 所需库:
将libthread_db copy到嵌入式平台。
4. 使用
4.1在嵌入式平台:
#gdbserver 172.16.1.61:1234 BT_remote
Process BT_remote created; pid = 716
Listening on port 1234
这个意思是:debug BT_remote这个程序。对方是172.16.1.61. 端口为:1234。
4.2在Host 端:
#gdb BT_remote
GNU gdb Red Hat Linux (6.6-35.fc8rh)Copyright (C) 2006 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) target remote 172.16.1.31:1234
Undefined target command: "172.16.1.31:1234 ". Try "help target".(gdb) target remote 172.16.1.31:1234`system-supplied DSO at 0x110000' has disappeared; keeping its symbols.Remote debugging using 172.16.1.31:1234[New Thread 716]
warning: Missing the separate debug info file: /usr/lib/debug/.build-id/ac/2eeb206486bb7315d6ac4cd64de0cb50838ff6.debug0xb7f17810 in _start () from /lib/ld-linux.so.2
此时发现嵌入式板子上有反应:
Remote debugging from host 172.16.1.61
则表明嵌入式板子和Host Debug链接成功,则可以在Host上调试嵌入式板子上的程序了。
但请注意,在Host上,不能使用 r 来运行,而应该使用 c来运行,因为这个程序已经在运行了。
则可以正常使用了。
嵌入式平台上GDB调试实例:
Sam想在X5平台上使用GDB来调试程序。
分析:
1. 需要gdbserver
2. 客户端需要gdb程序。
3. 需要重新编译,加入 -g.
1.编译gdbserver:
#cd gdb-6.8/gdb/gdbserver
#CC=/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc ./configure --host=arm-linux
#make clean;make all
则gdbserver生成成功。Sam把它copy到板子的/bin目录中
2. 客户端gdb程序。
直接使用交叉编译器中的arm-uclibc-linux-gdb
3.重新编译程序。
Sam是想调试SDL例子程序,所以在SDL库生成过程和test生成过程中均加入 -g.
实际操作:
1. 嵌入式板子中:
#gdbserver 172.16.1.61:1234 testsprite
2. Host
2.1
#arm-uclibc-linux-gdb testsprite
2.2
(gdb) target remote 172.16.1.45:1234
2.3
(gdb) c
之后和本地调试类似。
2009年1月5日星期一
LPC系列学习之一(GPIO)
作者:Sam(甄峰) sam_code@hotmail.com
程序背景:
RFID项目中,Larry设计了一个Keyboard.他是这样设想的:因为我们的手持机只需要18个key.
所以做了一个4x5的线路交叉列表。每个键就是一个开关,当按下时则可以通过线路电平变化得到按下哪个键。
4x5的线路交叉列表,则至少需要9(4+5)个引脚---S0,S1,S2,S3,K0,K1,K2,K3,K4. 这9个引脚则连接到LPC2138(共64个引脚)的引脚上。
具体是这样的:
S0:引脚17.(P0.31)
S1: 引脚39 (P0.13)
S2: 引脚38 (P0.12)
S3: 引脚36 (P1.23 //)
K0: 引脚31 (P0.9//)
K1: 引脚28 (P1.25//)
K2: 引脚22 (P0.2/SCL0/CAP0.0)
K3: 引脚20(P1.31/TRST)
K4: 引脚37 (P0.11//)
GPIO详解:
LPC2138上的GPIO特征:
1. 单独位的方向控制,即每一个I/O口线可单独设置为输入/输出模式。
2. 单独控制I/O口输出的置位或清零.
3. 所有I/O口在复位后默认为输入。
LPC2138上的GPIO用途:
1. 通用I/O 口。
2. 驱动LED或者其它指示器。
3. 控制片外器件。
4. 检测数字输入,如键盘或开关信号。
引脚描述:
P0和P1端口的GPIO引脚。
P0: P0.0-P0.31
P1:P1.16-P1.31
通用I/O口。
LPC2138拥有2个32位的通用I/O口---P0,P1.
P0口使用了30个引脚(P0.0-P0.25, P0.27-P0.30)
P1 口有16个引脚 (P1.16-p1.31)可以用作GPIO功能。
GPIO控制寄存器:
P0口和P1口由2组(每组4个)寄存器控制。
IOPIN:引脚值寄存器(IO0PIN-0xE0028000,IO1PIN-0xE0028010)
它提供GPIO引脚的值。读取可以读出相应GPIO引脚的值,写入无意义。
其中:IO0PIN的0位对应P0.0。位31对应P0.31
IOSET:输出置位寄存器(IO0SET-0xE0028004, IO1SET-oxE0028014)
当引脚配置为GPIO输出模式时,可使用该寄存器从引脚输出高电平。对应位写入1,则对应Px.x输出高电平。则此时IOPIN对应位读取时显示为1。写入0无效。当引脚被配置为输入或者其他功能,写IOSET 无效。
IOCLR:输出清零寄存器(IO0CLR-0xE002800C, IO1CLR-0xE002801C)
当引脚配置为GPIO输出模式时,可使用该寄存器从引脚输出低电平。对应位写入1,则对应Px.x输出低电平。则此时IOPIN对应位读取时显示为0。写入0无效。引脚被配置为输入或者其他功能,写IOCLR 无效。
IODIR:方向寄存器(IO0DIR-0xE0028008, IO1DIR-0xE0028018)
当引脚为GPIO模式时,可以使用该寄存器控制引脚的方向。(0=输入,1=输出)
现在讲解如何得到某个键被按下:
1.1:将S0,S1,S2,S3全部设为高电品。
1.1.1. IO0SET=0x80000000 将P0.31(S0)引脚设为高电平。
1.1.2. IO0SET=(1<<13) 将P0.13(S1)引脚设为高电平。
1.1.3. IO0SET=(1<<12) 将P0.12(S2)引脚设为高电平。
1.1.4 IO1SET=(1<<(23))将P1.23(S3)引脚设为高电平。
1.2:依次将S0,S1,S2,S3设为低电平,然后检查K0,K1,K2,K3,K4. 看谁为低电平。就证明Sx Kx处按键被按下(例如当S0为低电平,S1,S2,S3均为高电平时,监测到K1为低电平,则证明S0 K1处开关被连通,也就是说此按键被按下)。然后再恢复S位为高电平。
例1:
IO0CLR=0x80000000 //S0低电平
if((IO0PIN&(1<return 1;
IO0SET=0x80000000 //S0变为高电平
程序背景:
RFID项目中,Larry设计了一个Keyboard.他是这样设想的:因为我们的手持机只需要18个key.
所以做了一个4x5的线路交叉列表。每个键就是一个开关,当按下时则可以通过线路电平变化得到按下哪个键。
4x5的线路交叉列表,则至少需要9(4+5)个引脚---S0,S1,S2,S3,K0,K1,K2,K3,K4. 这9个引脚则连接到LPC2138(共64个引脚)的引脚上。
具体是这样的:
S0:引脚17.(P0.31)
S1: 引脚39 (P0.13)
S2: 引脚38 (P0.12)
S3: 引脚36 (P1.23 //)
K0: 引脚31 (P0.9//)
K1: 引脚28 (P1.25//)
K2: 引脚22 (P0.2/SCL0/CAP0.0)
K3: 引脚20(P1.31/TRST)
K4: 引脚37 (P0.11//)
GPIO详解:
LPC2138上的GPIO特征:
1. 单独位的方向控制,即每一个I/O口线可单独设置为输入/输出模式。
2. 单独控制I/O口输出的置位或清零.
3. 所有I/O口在复位后默认为输入。
LPC2138上的GPIO用途:
1. 通用I/O 口。
2. 驱动LED或者其它指示器。
3. 控制片外器件。
4. 检测数字输入,如键盘或开关信号。
引脚描述:
P0和P1端口的GPIO引脚。
P0: P0.0-P0.31
P1:P1.16-P1.31
通用I/O口。
LPC2138拥有2个32位的通用I/O口---P0,P1.
P0口使用了30个引脚(P0.0-P0.25, P0.27-P0.30)
P1 口有16个引脚 (P1.16-p1.31)可以用作GPIO功能。
GPIO控制寄存器:
P0口和P1口由2组(每组4个)寄存器控制。
IOPIN:引脚值寄存器(IO0PIN-0xE0028000,IO1PIN-0xE0028010)
它提供GPIO引脚的值。读取可以读出相应GPIO引脚的值,写入无意义。
其中:IO0PIN的0位对应P0.0。位31对应P0.31
IOSET:输出置位寄存器(IO0SET-0xE0028004, IO1SET-oxE0028014)
当引脚配置为GPIO输出模式时,可使用该寄存器从引脚输出高电平。对应位写入1,则对应Px.x输出高电平。则此时IOPIN对应位读取时显示为1。写入0无效。当引脚被配置为输入或者其他功能,写IOSET 无效。
IOCLR:输出清零寄存器(IO0CLR-0xE002800C, IO1CLR-0xE002801C)
当引脚配置为GPIO输出模式时,可使用该寄存器从引脚输出低电平。对应位写入1,则对应Px.x输出低电平。则此时IOPIN对应位读取时显示为0。写入0无效。引脚被配置为输入或者其他功能,写IOCLR 无效。
IODIR:方向寄存器(IO0DIR-0xE0028008, IO1DIR-0xE0028018)
当引脚为GPIO模式时,可以使用该寄存器控制引脚的方向。(0=输入,1=输出)
现在讲解如何得到某个键被按下:
1.1:将S0,S1,S2,S3全部设为高电品。
1.1.1. IO0SET=0x80000000 将P0.31(S0)引脚设为高电平。
1.1.2. IO0SET=(1<<13) 将P0.13(S1)引脚设为高电平。
1.1.3. IO0SET=(1<<12) 将P0.12(S2)引脚设为高电平。
1.1.4 IO1SET=(1<<(23))将P1.23(S3)引脚设为高电平。
1.2:依次将S0,S1,S2,S3设为低电平,然后检查K0,K1,K2,K3,K4. 看谁为低电平。就证明Sx Kx处按键被按下(例如当S0为低电平,S1,S2,S3均为高电平时,监测到K1为低电平,则证明S0 K1处开关被连通,也就是说此按键被按下)。然后再恢复S位为高电平。
例1:
IO0CLR=0x80000000 //S0低电平
if((IO0PIN&(1<
IO0SET=0x80000000 //S0变为高电平
2009年1月3日星期六
Linux下显卡驱动的安装
作者:Sam(甄峰) sam_code@hotmail.com
Sam拿到一台MinPC.
Intel 965系列芯片组,INTEL965是其为Conroe处理器量身定做的芯片组。Intel 965系列家族的研发代号为Broadwater,主要包括了以下四款系列芯片,它们分别是P965、G965、Q963和Q965。其中P965和G965面向主流消费者;Q963和Q965则主要面向企业用户,用来构建Averill平台。
主板是Intel集成芯片组G965,集成的显卡就是GMA X3000或X3100。
现在Sam想在Fedora8上安装此显卡驱动。
Sam在网上查到此驱动包含在xf86-video-intel中,于是在http://lists.freedesktop.org/archives/xorg/2008-April/034611.html 中下载了此包。
但Sam在./configure的时候,pkg-cofig提示找不到xorg-server. Sam也在/usr/lib/pkgconfig/中确实没找到xorg-server.pc 。
之后运行 #yum install xorg-x11-server-sdk .然后发现xorg-server.pc被建立。
Sam拿到一台MinPC.
Intel 965系列芯片组,INTEL965是其为Conroe处理器量身定做的芯片组。Intel 965系列家族的研发代号为Broadwater,主要包括了以下四款系列芯片,它们分别是P965、G965、Q963和Q965。其中P965和G965面向主流消费者;Q963和Q965则主要面向企业用户,用来构建Averill平台。
主板是Intel集成芯片组G965,集成的显卡就是GMA X3000或X3100。
现在Sam想在Fedora8上安装此显卡驱动。
Sam在网上查到此驱动包含在xf86-video-intel中,于是在http://lists.freedesktop.org/archives/xorg/2008-April/034611.html 中下载了此包。
但Sam在./configure的时候,pkg-cofig提示找不到xorg-server. Sam也在/usr/lib/pkgconfig/中确实没找到xorg-server.pc 。
之后运行 #yum install xorg-x11-server-sdk .然后发现xorg-server.pc被建立。
2008年12月29日星期一
Linux下如何监控程序具体使用内存情况
作者: Sam(甄峰) sam_code@hotmail.com
公司在Canmore下作游戏时,因为Canmore可用Memory不大,所以必须常常查看游戏具体使用多少内存。
Sam发现有以下几种方式可以查看当前程序内存使用量:
1. #ps
然后查看VSZ 项目。但这一项只是任务虚拟地址空间大小。并不代表真的使用如此多内存。
ps并没得出进程真实用了多少内存。它计算的是假设系统上只存在这唯一运行的进程时,此进程所占用的内存。事实是,几乎每时每刻系统上有一大堆进程在运行,也就是说ps报告的数据毫无疑问是“错”的。
Linux上大多数程序使用了共享库。例如KEdit使用了好几个KDE的,好几个Xlib的,还有好几个系统库。这些库,其中最常用的恐怕是libc库,同时被Linux系统中要多程序使用着。经过这种共享,Linux可以耍这么一个花招:可以把共享库拷入内存中然后到处引用给每一个程序。
很多工具程序没有考虑这一点。它们只是简单计算一下,而不考虑共享库起的作用。两个程序的共享库部分因此可能被统计了两次内存占用,这样多计算了一次可能导致很多误会。
实际上真实的内存占用是很难获得的。你不仅仅要知道系统如何工作,而且你必须在一些很难判断的问题上作出决定。如,被进程唯一使用的共享库是否应该被统计在内?被多个进程使用的共享库是否应该被统计多次,或者忽略不计?这里并没有一个严格的规则。在不同的情况下,你应该需要不同的选择。可想而知为什么ps在解决这个问题的时候,给出的答案比较含糊。
2. #top -d 1
然后查看VSZ 项目,也只是任务虚拟地址空间大小。并不代表真的使用如此多内存。
所以Sam常常使用如下方法:
在运行前后将比较used以及free和cached的用量。则可推断出实际使用量。
Mem: 33400K used, 160300K free, 0K shrd, 0K buff, 14056K cached
VSZ也会遇到与#ps出来的VSZ同样的问题。
注意:#top 时,会有一个cached值用来表示交换区域,这个区域大小总变化,很不利于我们通过两次的差值看大小。可以这样处理。
#echo 3 > /proc/sys/vm/drop_caches
#sync
然后 cached的值就会被固定。注意,echo 后面的值可以为:1,2,3
3. #free
然后查看2次运行的差值,这表明此程序内存使用量。
4. cat /proc/uid/statm
999 184 146 7 0 577 0
分别表示:
Size (total pages) 任务虚拟地址空间的大小 VmSize/4
Resident(pages) 应用程序正在使用的物理内存的大小 VmRSS/4
Shared(pages) 共享页数 0
Trs(pages) 程序所拥有的可执行虚拟内存的大小 VmExe/4
具体到一个程序中:内存使用量如何呢:
Sam试验表明:
如果某个程序什么都不作。
只是while(1)
则使用cat /proc/uid/statm。发现也会使用256K程序。这可能是分配给它的栈以及glibc库所占位置。
如果Sam这个程序代码什么都不作,但编译时添加 -lBTX, -lbluetooth, -lpthread.
则 #cat /proc/uid/statm发现多用100K物理内存。这表明Linux在计算内存使用量时,会把动态库所占内存包含进去。
公司在Canmore下作游戏时,因为Canmore可用Memory不大,所以必须常常查看游戏具体使用多少内存。
Sam发现有以下几种方式可以查看当前程序内存使用量:
1. #ps
然后查看VSZ 项目。但这一项只是任务虚拟地址空间大小。并不代表真的使用如此多内存。
ps并没得出进程真实用了多少内存。它计算的是假设系统上只存在这唯一运行的进程时,此进程所占用的内存。事实是,几乎每时每刻系统上有一大堆进程在运行,也就是说ps报告的数据毫无疑问是“错”的。
Linux上大多数程序使用了共享库。例如KEdit使用了好几个KDE的,好几个Xlib的,还有好几个系统库。这些库,其中最常用的恐怕是libc库,同时被Linux系统中要多程序使用着。经过这种共享,Linux可以耍这么一个花招:可以把共享库拷入内存中然后到处引用给每一个程序。
很多工具程序没有考虑这一点。它们只是简单计算一下,而不考虑共享库起的作用。两个程序的共享库部分因此可能被统计了两次内存占用,这样多计算了一次可能导致很多误会。
实际上真实的内存占用是很难获得的。你不仅仅要知道系统如何工作,而且你必须在一些很难判断的问题上作出决定。如,被进程唯一使用的共享库是否应该被统计在内?被多个进程使用的共享库是否应该被统计多次,或者忽略不计?这里并没有一个严格的规则。在不同的情况下,你应该需要不同的选择。可想而知为什么ps在解决这个问题的时候,给出的答案比较含糊。
2. #top -d 1
然后查看VSZ 项目,也只是任务虚拟地址空间大小。并不代表真的使用如此多内存。
所以Sam常常使用如下方法:
在运行前后将比较used以及free和cached的用量。则可推断出实际使用量。
Mem: 33400K used, 160300K free, 0K shrd, 0K buff, 14056K cached
VSZ也会遇到与#ps出来的VSZ同样的问题。
注意:#top 时,会有一个cached值用来表示交换区域,这个区域大小总变化,很不利于我们通过两次的差值看大小。可以这样处理。
#echo 3 > /proc/sys/vm/drop_caches
#sync
然后 cached的值就会被固定。注意,echo 后面的值可以为:1,2,3
3. #free
然后查看2次运行的差值,这表明此程序内存使用量。
4. cat /proc/uid/statm
999 184 146 7 0 577 0
分别表示:
Size (total pages) 任务虚拟地址空间的大小 VmSize/4
Resident(pages) 应用程序正在使用的物理内存的大小 VmRSS/4
Shared(pages) 共享页数 0
Trs(pages) 程序所拥有的可执行虚拟内存的大小 VmExe/4
具体到一个程序中:内存使用量如何呢:
Sam试验表明:
如果某个程序什么都不作。
只是while(1)
则使用cat /proc/uid/statm。发现也会使用256K程序。这可能是分配给它的栈以及glibc库所占位置。
如果Sam这个程序代码什么都不作,但编译时添加 -lBTX, -lbluetooth, -lpthread.
则 #cat /proc/uid/statm发现多用100K物理内存。这表明Linux在计算内存使用量时,会把动态库所占内存包含进去。
订阅:
博文 (Atom)