2008年11月30日星期日

BTX初建

作者: Sam(甄峰) sam_code@hotmail.com

1. 因为BTX是Sam设计的整个架构的基础,需要非常稳定。所以Sam决定每个接口都经过大量测试。
1.1. 单独的接口测试,包括[in]参数的边界测试,超出范围测试,[out]参数的参数为空,buffer不足够测试等。接口多次调用测试。
1.2. 多个接口组合测试,各个接口的各种常规和非常规组合测试。有意使用错误组合和参数测试。


2. BTX的具体实现:
2.1 BTX_Rel BTX_Init(uint16_t dev_id)
这个function 将 USB Bluetooth Dongle up起来。类似#hciconfig hci0 up
需要测试:
2.1.1. Bluetooth driver, protocol没有insmod时的反应。
2.1.2. Bluetooth USB Dongle没有插入时的反应。
2.1.3. 参数dev_id=0 (第一个USB Dongle) 和 dev_id = 1(第二个USB Dongle)时的反应。
2.1.4. 各种状况下 /proc/uid/fd/下察看终端。看是否有该释放的handle而没有释放的情况发生。注[1]。
常见的问题:打开hci socket后,遇到error,没有关闭socket就推出function.这样就会遗留一个socket.

2.2 BTX_Rel BTX_GetLocalDeviceInfo(uint16_t dev_id, bdaddr_t* pBdaddr, char* pBdName)
这个function的到本地Bluetooth 信息,包括 bdaddr, device name等。如果返回值不为BTX_SUCCESS。则说明Bluetooth USB Dongle没有Ready.
需要测试:
2.2.1:参数dev_id=0和dev_id=1时。
2.2.2:参数pBdaddr,pBdName没有分配实际的内存时,会出现Segmentation fault
Sam虽然在function 实现中添加了if(pBdaddr == NULL pBdName == NULL)...
但如果使用时不讲pBdaddr初始化为NULL。则它还是有一个值的。还是会造成Segmentation fault.这个问题等待未来解决。
2.2.3: 在调用此函数之前没有调用 BTX_Init():会返回BTX_DONGLE_NO_READY。但用perror()输出protocol未insmod.

2.3 BTX_Rel BTX_Scan_Device(uint16_t dev_id, unsigned int* pNum_rsp, bdaddr_t** ppBdaddr, uint16_t time);




注[1]:
在所有涉及到打开句柄或者socket的程序中,都应该察看 /proc/uid/fd中的信息。察看是否正常建立和销毁了句柄。

2008年11月29日星期六

ADS1.2下使用ARMulator软件调试以及ASM学习

作者: Sam (甄峰) sam_code@hotmail.com

使用ADS1.2 IDE--Codewarrior创建一个ARM Executable Image工程。
并在工程中建立一个ASM文件如下:
汇编程序1:
AREA Example1, CODE, READONLY ;声明代码段Example1
ENTRY ; 表明程序入口
CODE32 ;声明32位ARM指令
START MOV R0, #15 ;R0寄存器赋值为15
MOV R1, #8 ;R1寄存器为8
ADDS R0,R0,R1 ;R0=R0+R1
B START ;跳到START去
END
这个ASM程序非常简单。但我们可以从这个最简单的例子学会ARM Linker设置以及ARMulator软件仿真。

1.ARM Linker设置。
DebugRel Setting->ARM Linker页面中。选中Output分页。
Linktype的意思是:以何种方式Link。有3个选项:
Partia:
Simple: 使用简单的指定
Scattered:使用分散加载文件指定Image放置地点,栈,堆位置,程序从何处开始执行等

咱们首先讲Simple:
Simple Image中,
RO Base:表示程序放置的位置(或者是Read Only?)
RW Base:表示
Options: Image Entry Point:表示程序开始执行的位置。注意,这个位置必须在RO范围内,并且4字节对奇的头位置。
例1:
RO Base:0x40000000
RW Base:0x40003000
Image Entry Point: 0x40000000
则表示,程序放在0x40000000,且从0x40000000处开始执行。
使用F5进入AXD。则可以看到PC寄存器指向0x40000000(START处).可以一步步执行下去。
并看到Memory Start address: 0x40000000处有代码。
例2:
RO Base:0x40000000
RW Base:0x40003000
Image Entry Point: 0x40000004
则表示,程序放在0x40000000,且从0x40000004处开始执行。
进入AXD,则PC寄存器指向0x40000004(MOV R1, #8)处。
并看到Memory Start address: 0x40000000处有代码。


汇编程序2:
目的1:掌握LDR/STR读取和存储数据到Memory上。
目的2:继续深入掌握AXD.
COUNT EQU 0x40003100 ;定义一个变量,地址为0x40003100
AREA Example2,CODE,READONLY
ENTRY ;程序入口
CODE32 ;表明32位ARM指令
START LDR R1, =COUNT ;LDR从COUNT处读取数据到R1
MOV R0, #0; R0设为0
STR R0,[R1] ;R0的内容放置到R1所带表的地址中去

LOOP LDR R1, =COUNT ;R1 读取COUNT中内容
LDR R0,[R1] ;R0 读取R1所表示的内存区内容
ADD R0,R0,#1 R0=R0+1
CMP R0,#10 ;比较R0 和10
MOVHS R0,#0 如果R0>10 则执行这指令:将R0设为0
STR R0,[R1] 将R0内容写入R1代表的内存中去。
B LOOP
END

可以在AXD下面看Memory变化。R0,R1的Register变化等。

2008年11月27日星期四

Linux 下Bluetooth开发包--BTX的开发的背景介绍

作者:Sam(甄峰) sam_code@hotmail.com

BTX背景介绍:
Sam所在公司需要Linux下与Bluetooth Game Control连接,Linux为操作系统的嵌入式设备使用USB Bluetooth Dongle与Bluetooth Game Control连接,并收发数据。Sam评估后决定用以下结构:

1. HID,L2cap, hci 这三层protocol使用Linux下官方Bluetooth 协议栈--BlueZ. Bluez详细介绍见:[注1]

2. Bluetooth USB Dongle的驱动使用BlueZ提供的hci_usb 驱动。

3. 使用BlueZ提供的函数库和应用程序接口,创建一个针对手柄的连接库。即为BTX。这个手柄连接库提供的接口如下:
1>.Bluetooth USB Dongle初始化
2>.得到Bluetooth USB Dongle信息。
3>.查找周边Bluetooth Device设备。
4>.连接指定手柄。
5>.断开指定手柄。
6>.读取数据。
7>.写数据。
其中连接手柄是建立2个l2cap连接。PSM=0x11. 0x13.
这一层是提供给公司与所有手柄连接的库。任何一代手柄都将使用这个库作基本连接。

4. 使用BTX库和接口,开发一套针对某一代手柄所使用的库。BTRC--Bluetooth Remote Control.
因为每一代手柄连接过程中都有自己独特的加密与验证过程。并且数据格式也各不相同。所以使用这一层将这些不同与上层程序隔绝开来。

5.使用BTRC库,开发一套包含不同动作分析 的库供更上层程序使用。



注[1]:
BlueZ介绍:
Linux OS开放的bluetooth 协议栈主要包括IBM公司的BlueDrekar, Nokia公司的Affix, Axis公司的OpenBT和官方的协议栈BlueZ.
Bluetooth协议栈BlueZ分为两部分:内核代码和用户态程序及工具集。
其中内核代码由BlueZ核心协议驱动程序组成;Bluetooth协议实现在内核源代码 /net/bluetooth中。包括hci,l2cap,hid,rfcomm,sco,SDP,BNEP等协议的实现。驱动程序放在 /driver/bluetooth中,包含Linux kernel对各种接口的Bluetooth device的驱动。例如:USB接口,串口等。
用户态程序及工具集包括应用程序接口和BlueZ工具集。BlueZ提供函数库以及应用程序接口,便于程序员开发bluetooth应用程序。BlueZ utils是主要工具集,实现对bluetooth设备的初始化和控制。

ADS1.2上的软件仿真ARMulator

转载:

ARMulator 简介
ARMulator 是一个在 ARM 公司推出的集成开发环境 ADS (ARM Developer Suite)中提供的指令集模拟器。它与运行在通用计算机(通常是x86体系结构)上的调试器相连接,模拟 ARM 微处理器体系结构和指令集,提供了开发和调试 ARM 程序的软件仿真环境。ARMulator 不仅可以仿真 ARM 处理器的体系结构和指令集,还可以仿真存储器和处理器外围设备,例如中断控制器和定时器等,这样就模拟了一个进行嵌入式开发的最小子系统,另外使用者还可以扩展添加自己的外设。ARMulator 同时支持全部的标准C库函数,这样所有的C程序都可以在仿真器上运行。ARMulator 通过 Semihosting 方式来实现程序中输入/输出功能。Semihosting是指在 ARM 程序开发调试的过程中,目标系统还不具备输入/输出的功能,通过调试代理向调试器请求 I/O。ARMulator 就是这里所说的调试代理之一,其他还有 Angel、Multi-ICE也通过类似的方式来实现 I/O 功能。

ARMulator 在 Windows 平台上实现为动态链接库文件(.dll),在 Linux 或者 Solaris 系统上实现为共享库文件(.so)。前者的使用一般是通过 ARM 公司提供的图形界面调试器 AXD 中,选择目标系统为 ARMulate.dll,这样就设置了 ARMulator 作为调试代理。用户在进行调试开发时,对于底层使用的调试代理是透明的。


ARMulator 组成
ARMulator 本身是由一系列的模块(module)组成的,其中最主要的模块包括: ARM 处理器核心模块,用来仿真处理器的体系结构。 处理器使用的存储器模块,用来仿真存储系统。缺省的内存模型是 flatmem ,内存的大小不限制,理论上 4G 的地址空间都可以使用。同时,ARMulator 也包含了几种外设模块,用来仿真如下的外设模型: Interrupt controller(中断控制器)用于中断控制,包含状态寄存器、使能寄存器、状态位清除寄存器等。寄存器基址:Base=0x0a000000 Timer(定时器)ARMulator 提供了两个定时器。包含控制寄存器、计数值寄存器、中断清除寄存器等。定时器支持 free-running 和 periodic 两种工作方式,前者计数器从0xFFFF递减,到“0”后计数器溢出发生中断。后者计数器从计数值寄存器中读取计数值,然后递减至“0”后溢出发生中断。寄存器基址:Base=0x0a800000 Watchdog(看门狗)看门狗是为了防止用户程序出错导致系统死锁而采用的一种保护手段。一旦到了预定时间没有设置看门狗,看门狗就会停止ARMulator的运行,返回调试器中。看门狗使用2个定时器工作,第一个定时器在复位时启动,或者通过程序写入KeyValue 寄存器预先设定好的值来启动,当过了一定时钟周期(WatchPeriod)后,第一个定时器产生 IRQ 中断,并启动第二个定时器;如果在规定时间(IRQPeriod)内程序没有写KeyValue寄存器,则看门狗将停止 ARMulator ,返回调试器中。寄存器基址:Base=0xb0000000 KeyValue=0x12345678WatchPeriod=0x80000 IRQPeriod=3000 IntNumber=16 Stack tracker(栈跟踪器)栈跟踪器在每条指令执行后,检查栈指针(r13)寄存器的值。它记录下栈顶的最小值,从而可以计算出所用堆栈的最大长度。栈底地址:StackBase=0x80000000 栈空间限制:StackLimit=0x70000000 Tube(显示管)Tube 实际上是一个寄存器,它用来显示输出。一旦写入这个寄存器某个可以显示的字符,则调试器将会把它显示在控制台 Console 下。这样用来显示对某个指定地址的写操作记录。寄存器基址:Base=0x0d800020使用者也可以根据一定的接口规范编写自己的外设模块,或者通过修改已有模块的方式来适合自己的需求。比如定制自己的存储系统等。3.使用 ARMulator从调试器中可以配置 ARMulator 的一些属性,在调试器启动的时候,会将当前配置的部分信息输出显示。ADS 提供了6个 .ami 配置文件来定义当前 ARMulator 的工作行为。当 ARMulator 被调试器启动时,就会搜索环境变量 armconf 中的路径,读取所有的 .ami 配置文件来对 ARMulator 进行设置。ARMulator 还提供了跟踪(Tracer)功能,用来记录程序中指令和内存被访问的情况,也可以记录下各种事件的发生,例如 ARM 处理器事件、MMU 和 cache 事件等。另外,使用 ARMulator 的 profiling 记录,可以统计特定函数在执行过程中被调用的次数,包括子函数的调用在总的执行时间中所占的比例。使用 ARMulator 只要在调试器中设置了 target 就可以了,通过图形用户界面的调试器AXD ,可以查看 ARM 寄存器(包括通用寄存器和程序状态寄存器等),也可以查看任意内存单元的内容,还可以进行异常的处理,例如中断和程序中止等。通过对上面提到的定时器和中断控制器的设置,在 ARMulator 上甚至可以移植嵌入式实时操作系统 uC/OS-II。uC/OS-II 是一个实时操作系统内核,包含了任务调度、任务管理、时间管理、内存管理和任务间的通信与同步等基本功能。通过在 ARMulator 上移植和调试 uC/OS-II,以后就可以更快将其移植到自己的嵌入式硬件平台上。

2008年11月26日星期三

EasyARM2200开发板上使用LPC2138(使用Flash Magic烧入程序)

作者:Sam(甄峰) sam_code@hotmail.com

1. 使用Flash Magic进行ISP连接:
1.1. EasyARM2200开发板上有ISP使能跳线JP1。短接JP1进入ISP模式。
1.2. 使用串口线将PC与EasyARM2200 UART0 连接。
1.3. 打开Flash Magic. ISP->Read Device Signation....
则可以读取到 Device ID和Bootloader Version。
说明ISP连接成功。


2. 使用Flash Magic烧入程序:
2.1. EasyARM2200开发板上有ISP使能跳线JP1。短接JP1进入ISP模式。
22. 使用串口线将PC与EasyARM2200 UART0 连接。
2.3. 打开Flash Magic. ISP->Read Device Signation....
则可以读取到 Device ID和Bootloader Version。
说明ISP连接成功。
2.4. 设置Flash Magic:
2.4.1:COM:你所连接的COM口。
2.4.2:Baud rate: 波特率 19200
2.4.4:Device: LPC2138
2.4.5:Interface:None(ISP)
2.4.6:Oscillator Freq(MHz):12.000000
2.4.7:选中:Erase blocks used by Hex file(将烧入文件对应的区域Erase掉)
2.5:选中烧入目标文件:
xxx_Data\DebugInFLASH\xxx.i32文件
烧入时遇到问题。Sam先选择Erase all Flash+Code Rd Port,Erase所有的Flash。之后再回到Erase blocks used by Hex file。然后一切正常。


3. 使用ASD Codewarrior IDE打开之前的工程并编译:
3.1 进入Source code目录中xxx_data\DebugInFLASH目录。(因为Sam只编译DebugInFlash目标)
删除目录中所有文件。

3.2 打开mcp文件。选择DebugInFLASH。因为xxx_data\DebugInFLASH目录中所有东西都被删除了。所以显示所有文件都未被编译。F7 make. 会在xxx_data\DebugInFLASH目录声称xxx.i32文件。
Sam:也可以使用Codewarrior IDE 菜单中,project->Remote Object code...删除所有obj文件。就可以强行重新编译所有文件了。

3.3 可以改变声称的目标文件为hex文件。选择Setting中ARM FromELF。在Output file name中写入xxx.hex。则生成文件变为hex文件。

2008年11月19日星期三

ADS集成开发环境

ADS是ARM公司推出的ARM核微控制器集成开发环境。ARM Developer Suite.

1.ADS组成:
1.1代码生成工具,也就是编译器,链接器。
1.2集成开发环境:Codewarrior IDE.
1.3调试器:AXD
1.4指令模拟器:ARMulator,用来仿真调试。
1.5ARM开发包:底层例程,例如fromELF.
1.6ARM应用库。C,C++库等。

2. 关于ARM工程模板。
周立功网站提供了基于ADS和Keil的针对各种LPC芯片的工程模板。
例如:LPC2200系列的ARM7有6个工程模板。
工程模板有什么用呢?
因为ARM下程序与PC程序有所不同,也许需要将程序放到某个位置,这个位置是CPU启动后跳转的地点。所以ARM程序要做很多事,比如要设置中断向量表的位置。这块程序肯定是要ASM实现的。则工程模板会帮客户实现这些ASM程序。并且会提供分散加载文件(mem_a.scf,mem_b.scf, mem_c.scf).这些文件会指定程序会加载在何处。


3.LPC2200工程模板分散加载描述文件
当使用ARM Executable Image for lpc2200模板建立工程。则工程有4个生成目标(target system): DebugIExram, DebugInChipFlash, RelInChip,RelOutChip.

3.1 DebugInExram:片外RAM调试模式。程序放在片外RAM中。并且开发板设为片外RAM使用Bank0 。如果用户定制的设备不是按照RAM使用Bank0。则不能使用这个模式。
入口点地址:0x80000000
对应分散加载描述文件: mem_b.scf.
这个模式有个前提:片外RAM使用Bank0(即起始地址为0x80000000). 这一条不可修改。


3.2 DebugInChipFlash:片内Flash调试模式,程序放在片内Flash中。
入口点地址:0x00000000
对应分散加载描述文件:mem_c.scf

3.3 RelInChip: 片内Flash工作模式,程序放在片内Flash中。程序写入Flash之后即被加密。
入口点地址:0x00000000
对应分散加载描述文件:mem_c.scf

3.4 RelOutChip:片外Flash工作模式。程序放在片外Flash中。
入口点地址:0x80000000
对应分散加载描述文件:mem_a.scf

如何确定哪个模式对应的分散加载描述文件
在Codewarrior IDE中。
Setting中,Linker ---〉ARM Linker
其中Scatter:中指定了使用哪个分散加载描述文件。


分散加载描述文件解析:
ROM_EXEC:入口点地址
IRAM:片内RAM地址
STACKS:?
ERAM:片外RAM地址
HEAP:?

RFID开发软件安装以及LPC基础知识

作者:Sam(甄峰) sam_code@hotmail.com

Sam在做RFID项目时,使用的是ADS1.2开发环境。

1.安装ADS1.2。
1.1 RFID\tools\ADS_1.2\ads1.2\setup.exe
1.2.安装完毕后,选择"Install License"。选中tools\ADS_1.2\ads1.2\CRACK\LICENSE.DAT.则CRACK完成。ADS可以正常使用。

2.安装Flash Magic.
RFID\tools\FlashMagic下直接安装即可。

下面介绍LPC基础知识:
1.Flash,SRAM知识:
1.1 SRAM,DRAM知识简单介绍:
SRAM是英文Static RAM的缩写,它是一种具有静止存取功能的内存,不需要刷新电路即能保存它内部存储的数据。不像DRAM内存那样需要刷新电路,每隔一段时间,固定要对DRAM刷新充电一次,否则内部的数据即会消失,因此SRAM具有较高的性能,但是SRAM也有它的缺点,即它的集成度较低,相同容量的DRAM内存可以设计为较小的体积,但是SRAM却需要很大的体积。因为其存取速度快,所以常被用作CPU高速缓存(Cache Memory )。

1.2Non Flash,Nand Flash。
Non Flash存取速度慢,单元价格高。但可以直接执行。
Nand Flash存取速度快,价格低,但不能直接执行。要以块为单元读写。

1.3
LPC21xx, LPC22xx,LPC23xx系列芯片,大都带有片内Flash.片内SRAM。例如:lpc2138有512K片内高速Flash,32K片内RAM。

通常,可以按照需要添加片外Flash,片外SRAM.例如:EasyARM2200开发平台就有512K片外SRAM. 2M片外Flash。

1.4 片外Flash, 片外SRAM的地址空间分配:
EasyARM2200使用LPC芯片外部存储器接口的Bank0,Bank1扩展Flash(SST39VF160)和SRAM(IS61LV25616).他们各使用一个Bank. 并可以使用JP6设置Flash,RAM谁用哪个Bank.
当程序调适时,通常将Bank0分给SRAM。当程序固化与Flansh中时,则将Bank0分给Flash。因为Bank0可以用来引导程序运行。

1.5 片内Flash, 片外Flash谁来引导。
如果想要片外Flash引导程序执行:
1.必须将Bank0分给片外Flash。
2.必须设置从片外而不是片内Flash启动。
如果想要从片内Flash引导,则设置从片内Flash启动。

1.6 片内Flash, RAM 地址范围:
LPC 片内Flash通常从0x00000000开始。片内RAM从0x40000000开始。
也就是说如果选择从片内启动。则一定是从Flash启动。

2008年11月17日星期一

Intel CE3100(Canmore)多种方式启动

作者: Sam(甄峰) sam_code@hotmail.com

Intel Canmore嵌入式平台可以基于多种方式启动。

先介绍背景知识。
Intel CE3110 启动时,首先去执行Non flash上的一个称为CEFDK的程序。CEFDK做最初的初始化动作。然后将执行权交给redboot. redboot则将kernel和rootfs准备好并将执行权交给redboot.

CEFDK, redboot, kernel, rootfs放在不同的位置上,所以Canmore可以有多种启动方式。

方式1:
CEFDK, redboot,kernel,rootfs全部放入Non flash.
这个方式有个前提, 因为Non flash非常小。 所以 kernel,rootfs必须非常基础,非常小的。其实做不了什么事。基本很少使用这种方式。

方式2:
CEFDK,redboot放在Non flash. Kernel放在tftp根目录。rootfs放在nfs目录。
这种方式常用来在开发阶段使用。因为kernel,rootfs可以很容易的修改。

方式3:
CEFDK,redboot放在Non flash中,Kernel, rootfs放在SATA harddisk中。
这种方式是STB最终产品模式。

现在具体介绍不同方式的做法:

CEFDK在Canmore出厂时,就已经烧入到Non Flash中。所以需要做的就是把redboot, kernel,rootfs放到相应的位置。

方式2:
CEFDK,redboot放在Non flash. Kernel放在tftp根目录。rootfs放在nfs目录。
1.首先烧入redboot.
重新启动Canmore时,按住空格键,则进入CEFDK界面:
shell>
shell> ymodem 0x200000 0
在Transfer->send File 中选择ymodem. 并把redboot_flashboot.bin选中。
传输完成后,只是将image放到内存.还需要烧到Non flash中去。
shell>expflash burnFlash 0x200000 0x100000 0x20000
注:redboot_flashboot.bin表示是:这个版本的redboot是放在Non flash中的。
重新启动Canmore,按ctrl+c.则进入redboot界面:
RedBoot>

2.将kernel放到tftpboot中,将rootfs放到NFS目录中。
将kernel文件,bzImage放到server的/tftpboot中。(tftp要安装好)
将rootfs目录--fsroot放到NFS允许使用的目录中。(nfs服务要安装好 /etc/exports要允许Canmore IP地址使用这个目录)

3.在redboot中,设置kernel,rootfs的放置地点。
RedBoot> fconfig

>> load -v -r -m tftp -h 172.16.1.61 -b 0x200000 bzImage
>> exec -b 0x200000 -l 0x300000 -c "console=ttyS0,115200 root=/dev/nfs rw nfsroot=172.16.1.61:/home/sam/Intel/Canmore/fsroot_canmore_1073_new ip=dhcp mem=exactmap memmap=640k@0 memmap=95M@1M"
>>
Boot script timeout (1000ms resolution): 2Use BOOTP for network configuration: trueDefault server IP address: 172.16.1.70Console baud rate: 115200GDB connection port: 9000Force console for special debug messages: falseNetwork debug at boot time: falseUpdate RedBoot non-volatile configuration - continue (y/n)? y

之后重新启动,则redboot从172.16.1.61的tftpboot中拿kernel.
把172.16.1.61:/home/sam/Intel/Canmore/fsroot_canmore_1073_new 当作rootfs.



方式3: CEFDK,redboot放在Non flash中,Kernel, rootfs放在SATA harddisk中。
3.1.准备SATA harddisk.
3.1.1分区:
将SATA硬盘连接到开发机Linux上 ,而不是Canmore Linux上。
#fdisk -l

注意:/dev/sdb是4G固态硬盘。/dev/sda是Sam插入的40G SATA硬盘。
Sam想把SATA硬盘分2个区。第一个区用来放置redboot.第二个分区用来放kernel+rootfs.
注意:
#fdisk /dev/sda

Command (m for help): n
Command action e extended p primary partition (1-4)p
Partition number (1-4): 1
First cylinder (1-1022, default 1): Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-1022, default 1022): 1

Command (m for help): n
Command action e extended p primary partition (1-4)p
Partition number (1-4): 2
First cylinder (2-1022, default 2): Using default value 2
Last cylinder or +size or +sizeM or +sizeK (2-1022, default 1022): +1024M

Command (m for help): a
Partition number (1-4): 1
Command (m for help): w
The partition table has been altered!

n:新建partition.
p:逻辑分区
1,2:分区号。
First cylinder :第一个扇区号
Last cylinder or +size or +sizeM or +sizeK :最后一个扇区号.或者用+xxxM.

a: 制定boot flag.
w:执行以上动作。

3.1.2格式化/dev/sda2
#mkfs.ext3 /dev/sda2


3.2:放置redboot.bin到第一个分区。
首先使用tftpboot+nfs方式启动。
# dd conv=sync if=redboot.bin of=/dev/sda1 [注1]
224+1 records in
225+0 records out

3.3:放置kernel+rootfs到第二个分区。
# mkdir disk
# mount /dev/sda2 /disk/
#cp bzImage /disk
#cd fsroot
# cp * -rf /disk/

3.4:设置redboot:(Sam认为应该是Non Flash中的redboot)
先查看当前Redboot设置。
Redboot> fconfig -l

写Redboot:
Redboot> fconfig
>> load -v -r -m disk -b 0x200000 hda2:bzImage
>> exec -b 0x200000 -l 0x300000 -c "console=ttyS0,115200 root=/dev/sda2 rw mem=exactmap memmap=640K@0 memmap=95M@1M"
>>

重新启动。OK



大Memory设置:
在PR7之后的版本中,Canmore可以通过一系列设置,将用户空间大小提升到194M。
可以如下操作:
1.修改config文件:
#emacs /etc/platform_config/platform_config.hcfg
修改以下句子
media_base_address = 0x0C300000 /*0x10000000 sam Modify it*/
2. boot时修改redboot传入的参数:
load -v -r -m tftp -h 172.16.1.61 -b 0x200000 bzImage_canmore_1087exec -b 0x200000 -l 0x300000 -c "console=ttyS0,115200 root=/dev/nfs rw nfsroot=172.16.1.61:/home/sam/Intel/Canmore/TEST_TEAM_FSROOT/image4/fsroot_canmore_1087 ip=172.16.1.41 mem=exactmap memmap=640k@0 memmap=194M@1M"
之前是:memmap=95M@1M 表明从1M处到95M处。新写法表明从1M处到194M处。

注1:
Linux命令dd:
dd: convert and copy a file.转换和copy 文件。
conv= : convert the file as per the comma separated symbol list. 将文件转化为指定的格式
其中:conv = sync:把每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。
if=:read from FILE instead of stdin。输入
of=:write to FILE instead of stdout。输出
# dd conv=sync if=redboot.bin of=/dev/sda1
所以,这句话:将redboot.bin放入/dev/sda1

2008年11月16日星期日

Intel CE3100(Canmore) PDK Install

作者:Sam(甄峰) sam_code@hotmail.com

2008/11/17. Intel 发布了PR-6-1073. Sam得到的PDK包为:ACC-1.0.0.1073 BASE-1.0.0.1073 GPL MPEG-1.0.0.1073。
按照经验,Sam首先安装BASE-1.0.0.1073.
BASE中的目录为:Canmore-BASE-1[1].0.0.1073.iso Canmore-BASE-1[1].0.0.1073.iso.md5 CE3100_SDK_ReleaseNotes_PR6.pdf readme.txt。

1.将ISO mount到目录中。
#mount -t iso9660 -o loop,ro Canmore-BASE-1\[1\].0.0.1073.iso ../mount_dir/

2.安装BASE.
#sh installer /home/intel/Intel_CE3110/Intel_CE3110_Dev_1073
显示PGL后正常安装。
之后发现Sam最关心的kernel竟然没有。于是察看readme.txt后。应该在GPL中。呵呵。

完全安装后一切正常。

#make all

2008年11月11日星期二

基于bluez程序编译时注意事项

作者:Sam(甄峰) sam_code@hotmail.com

Sam制作了一个基于bluez的,类似于BTW的库---BTX.在编写和使用这个库时,发现头文件包含容易出现的问题。

第一:不要用到哪个Bluez头文件中定义的内容就只去包含这个头文件。而应该将bluetooth/bluetooth.h最先加入。类似:
//==================================
//bluetooth header file
#include //BTPROTO_HCI
#include //struct hci_dev_info
#include //hci_devid()
#include //l2cap
#include //hidp


第二:因为Linux 下Bluetooth实现是通过socket来完成的。sa_family_t会被使用。
bluetooth/hci.h中就用到了。所以必须在include bluetooth header file之前,include socket header file.
#include //socket()
#include
否则就会出现类似以下问题:
bluetooth/hci.h:1091: error: expected specifier-qualifier-list before 'sa_family_t'

gcc与g++的区别(转贴)

Sam在上一篇帖子中提到:当使用xxx.c.并使用gcc编译时,会有重定义无法检测出来的现象。今天学习一下gcc与g++的区别。

Sam之前在leadtek作嵌入式时,与Milong谈过gcc与g++的区别,曾认为gcc就是C方式编译,g++就是C++方式编译。现在才知道是不对的。

误区一:gcc只能编译c代码,g++只能编译c++代码
实际上,gcc与g++都是既可以编译C程序,也可以编译C++程序
但需要注意的是:
1.后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;后缀为.cpp的,两者都会认为是c++程序。
2.编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++编译似的。


误区二:gcc不会定义__cplusplus宏,而g++会
这个宏只是标志着编译器将会把代码按C还是C++语法来解释,如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义。


误区三:编译只能用gcc,链接只能用g++
严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用g++或者gcc -lstdc++。因为gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。但在编译阶段,g++会自动调用gcc,二者等价。


误区四:extern "C"与gcc/g++有关系
实际上并无关系,无论是gcc还是g++,用extern "c"时,都是以C的命名方式来为symbol命名,否则,都以c++方式命名。
Sam认为这句话值得商榷:也许对于.c文件,gcc会以C的命名方式为symbol命名。对于.cpp文件,gcc和g++都用C++方式命名。如果有了extern "C".则全部使用C方式命名。

另:gcc -S 可以生成.s文件,就可以看到symbol了。

2008年11月9日星期日

extern "C" 详解

Sam之前在提供BTX库时,需要提供一个 BTX.h文件。按照标准头文件的写法:
#ifndef _BTX_H
#define _BTX_H
#ifdef __cplusplus
extern "C" {
#endif

//xxxxxx
extern functionA();

#ifdef __cplusplus
}
#endif

#endif
也就是说:将所有function 声明和 struct定义全放在里面了。但corner建议只将function声明放在其中。并且,不需要再次声明:extern functionA();
所以研究一下#extern "C"

众所周知:
#ifndef _BTX_H
#define _BTX_H
//xxxx
#endif
编译宏用来防止重复引用。

下面将仔细研究#extern.(这部分引用CSDN:keen资源,觉得他讲的足够清楚了)


extern 作用1:声明外部变量
现代编译器一般采用按文件编译的方式,因此在编译时,各个文件中定义的全局变量是
互相透明的,也就是说,在编译时,全局变量的可见域限制在文件内部。

例1:
创建一个工程,里面含有A.cpp和B.cpp两个简单的C++源文件:
//A.cpp:
int iRI;
int main()
{
//.....
}

//B.cpp
int iRI;

gcc A.cpp -c
gcc B.cpp -c
编译出A.o, B.o都没有问题。
但当gcc A.o B.o -o test时,
main.o:(.bss+0x0): multiple definition of `iRI'
b.o:(.bss+0x0): first defined here
报错:重定义。
(但Sam有个非常意外的发现:当同样的代码,使用A.c B.c.并使用gcc编译时,竟然不会报重定义的错误,非常不明白是怎么回事。)
这就是说,在编译阶段,各个文件中定义的全局变量相互是透明的,编译A时觉察不到B中也定义了i,同样,编译B时觉察不到A中也定义了i。
但是到了链接阶段,要将各个文件的内容“合为一体”,因此,如果某些文件中定义的全局变量名相同的话,在这个时候就会出现错误,也就是上面提示的重复定义的错误。因此,各个文件中定义的全局变量名不可相同。


但如果用下列方式:在B.cpp中定义iRI;在A.cpp中直接使用。则编译A.cpp时就无法通过。
//A.cpp
int main()
{
iRI=64;
}

//B.cpp
int iRI;

gcc A.cpp -c
was not declared in this scope.
因为编译器按照文件方式编译,所以编译A.cpp时,并不知道B.cpp中定义了iRI。
也就是说:文件中定义的全局变量的可见性扩展到整个程序是在链接完成之后,而在编译阶段,他们的可见性仍局限于各自的文件。
解决方案如下:
编译器的目光不够长远,编译器没有能够意识到,某个变量符号虽然不是本文件定义的,但是它可能是在其它的文件中定义的。
虽然编译器不够远见,但是我们可以给它提示,帮助它来解决上面出现的问题。这就是extern的作用了。
extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”
//A.cpp:
extern int iRI;
int main()
{
iRI = 64;
//.....
}

//B.cpp
int iRI;
这样编译就能够通过。
extern int iRI; //并未分配空间,只是通知编译器,在其它文件定义过iRI。


extern 作用2:在C++文件中调用C方式编译的函数
C方式编译和C++方式编译
相对于C,C++中新增了诸如重载等新特性。所以全局变量和函数名编译后的命名方式有很大区别。

int a;
int functionA();
对于C方式编译:
int a;=> _a
int functionA(); => _functionA
对于C++方式编译:
int a; =>xx@xxx@a
int functionA(); => xx@xx@functionA
可以看出,因为要支持重载,所以C++方式编译下,生成的全局变量名和函数名复杂很多。与C方式编译的加一个下划线不同。
于是就有下面几种情况:
例2:C++调用C++定义的全局变量
//A.cpp:
extern int iRI;
int main()
{
iRI = 64;
//.....
}
//B.cpp
int iRI;
gcc A.cpp -c
gcc B.cpp -c
gcc A.o B.o -o test
那么在编译链接时都没问题。

例3:C++调用C定义的全局变量
//A.cpp:
extern int iRI;
int main()
{
iRI = 64;
//.....
}
//B.c
int iRI;
编译时没有问题,
gcc A.cpp -c
gcc B.c -c
但链接时,gcc B.o A.o -o test
则会报iRI没有定义。为什么呢?
因为gcc看到A.cpp,就使用C++方式编译,看到B.c,就使用C方式编译。
所以在A.cpp中的iRI=>XXX@XXX_iRI;
而B.c中iRI=〉_iRI;
所以在链接时,A.cpp想找到XXX@XXX_iRI,当然找不到。所以就需要告诉编译器,iRI是使用C方式编译的。
//A.cpp:
extern "C"
{
int iRI;
}
int main()
{ iRI = 64;
//.....
}
//B.c
int iRI;
这样,当编译A.cpp时,编译器就知道iRI为C方式编译的。就会使用 _iRI。这样B.c提供的_iRI就可以被A.cpp找到了。

例4:C++调用C定义的function
//A.cpp
extern int functionA();

int main()
{
functionA();
}

//B.c
int functionA()
{
//....
}
gcc A.cpp -c
gcc B.c -c
都没有问题。但同样的,gcc A.o B.o -o test
则报错,找不到functionA();
这是因为gcc将A.cpp认为是C++方式编译,B.c是C方式编译。
所以functionA在B.c中为:_functionA. 在A.cpp中为:XX@XXX_functionA
所以在链接时A.cpp找不到XX@XX_function.
于是需要通知编译器,functionA()是C方式编译命名的。
//A.cpp
extern "C"
{
int functionA();
}

int main()
{
functionA();
}

//B.c
int functionA()
{
//....
}
于是,编译链接都可以通过。

Sam的具体应用:
另外:Sam这次是写了一个Linux 下Bluetooth库。libBTX.so. 这个库需要C,C++都可以使用。
但Sam这个库是C方式编译的。libBTX.so下,functionA()=>_functionA
所以C程序调用时没有问题,C++则会有问题。
所以需要在这个库提供的头文件中加入:
#ifndef _BTX_H
#define _BTX_H

#ifdef __cplusplus
extern "C" {
#endif


//xxxxxx


#ifdef __cplusplus
}
#endif
则当C程序下使用这个库时,
#include "BTX.h"
因为是C方式编译,所以不会进入#ifdef __cplusplus
当C++程序调用这个库,
#include "BTX.h"
则C++编译器可以进入#ifdef __cplusplus
所以extern "C"被启用。
这表示下面BTX所提供的function为C方式编译的。则C++程序也能够将functionA()编译为_functionA.这样就可以找到libBTX.so所提供的function.


最终结论:
Corner所得很对,只需要把function()放在extern "C" { ....} 内,但Sam的做法好象也没错。因为很多Linux程序也是这样做的。
同时,
extern "C"
{
functionA();
}//不止是声明,并且还指出:这个function请用C方式编译。所以不需要再次extern.
所以Corner的建议也是非常有道理的。
exter "C"
{
extern functionA();
}//这样做没什么太大意义。

2008年11月5日星期三

Linux下静态库,动态库的创建和使用,多个动态库使用

作者:Sam(甄峰) sam_code@hotmail.com

1.动态库和静态库的区别:
使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。
对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。
由于动态库节省空间,Linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。





Linux下静态库的创建:
ar rv libxxx.a xxx.c
ar rv libxxx.a xxx.o
例如:
libBT.a: bluetooth_remote.o cmd_rpt.o AnalysisDirection.o
arm-linux-ar rv libBT.a $?
注解:
ar命令可以用来创建、修改库,也可以从库中提出单个模块。库是一单独的文件,里面包含了按照特定的结构组织起来的其它的一些文件(称做此库文件的member)。原始文件的内容、模式、时间戳、属主、组等属性都保留在库文件中。

r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。

v:该选项用来显示执行操作选项的附加信息。



Linux下动态库的创建:
gcc -shared -fpic xxx.c -o libxxx.so
gcc -shared -fpic xxx.o -o libxxx.so
例如:
libBTX.so: BTX.o
$(CC) $(CFLAGS) -shared -fpic BTX.o -o libBTX.so



Linux下动态库的使用:
-lxxx
例1:某个动态库为 libBTX.so
gcc main.c -o bluetooth_test -L./ -lBTX

Linux下静态库的使用:
例2:某个静态库为 libBTX.a
gcc main.c libBTX.a -o bluetooth_test
gcc main.c -o bluetooth_test -lBTX
以上2个方法都可以链接到静态库


当几个动态库之间有依赖关系时:
例3:Sam在作libBTX.so动态库时,依赖了 libbluetooth.so这个动态库。
首先生成libBTX.so:
i686-linux-elf/bin/i686-cm-linux-gcc -D_SHOW -Wall -I../include -I/home/sam/work/current/Intel_CE_3110/Intel_Canmore/Canmore-1.1050/i686-linux-elf/include -shared -fpic BTX.o -o libBTX.so
因为依赖的是一个动态库--libbluetooth.so 所以在生成 libBTX.so时不必真的链接到libbluetooth.so

当要生成可执行文件时:
i686-linux-elf/bin/i686-cm-linux-gcc -D_SHOW -Wall -I../include -I/home/sam/work/current/Intel_CE_3110/Intel_Canmore/Canmore-1.1050/i686-linux-elf/include -L/home/sam/work/current/Intel_CE_3110/Intel_Canmore/Canmore-1.1050/i686-linux-elf/lib -L./ main.c -o BTX_Test -lpthread -lm -lBTX -lbluetooth
因为libBTX.so依赖 libbluetooth.所以需要将 libbluetooth.so也指定进来。否则libbluetooth.so所提供的function就无法找到。
注意:因为libBTX.so 依赖于libbluetooth.so.所以-lBTX 要放在 -lbluetooth之前。



静态库与动态库相依赖时与之类似。
例4:libBTX.a中依赖于libbluetooth.so
所以生成可执行文件时,也需要加入 -lbluetooth
gcc main.c -o bluetooth_test -lBTX -lbluetooth

静态库之间相互依赖:
例如,某程序需要使用 libBTX.a,libbluetooth_remote.a
则link时,需要把libbluetooth_remote.a放在libBTX.a之前。



当目录内同时存在动态库和静态库时指定使用哪个库:
-WI,-Bstatic:
这个特别的"-WI,-Bstatic"参数,实际上是传给了连接器ld。指示它与静态库连接,如果系统中只有静态库当然就不需要这个参数了。
如果某个程序需要link多个库,例如,BTX使用静态库,Bluetooth_remote使用动态库。则需要这样写:
gcc main.c -o BT_remote -WI,-Bstatic -lBTX -WI,-Bdynamic -lBluetooth_remote



编译时的 -L 选项与 环境变量 LD_LIBRARY_PATH之间的关系:
呵呵,其实他们之间没有关系,
-L 表明编译时该到什么地方去找动态,静态库。
例1:动态库libBTX.so放在 /usr/local/lib中,则 -L/usr/local/lib -lBTX
例2:动态库libBTX.so放在编译本目录中,则 -L./ -lBTX

export LD_LIBRARY_PATH则表示执行时在什么地方寻找动态库。
例3:动态库libBTX.so放在 /usr/lib/bluetooth中。则
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/bluetooth
程序执行时就会去 /usr/lib/bluetooth中寻找所需动态库。