原文
随着嵌入式应用的普及,嵌入式操作系统的问题日益引人关注。由于DSP代表了一类重要的嵌入式应用,以DSP为核心的嵌入式操作系统也正在成为人们研究的热点。为了对自己的DSP产品提供软件支持,TI公司发行了DSP/BIOS程序包。但是关于DSP/BIOS的定位却存在分歧。一方面,在TI的正式资料和文档中均回避把DSP/BIOS称为实时操作系统,另一方面,为了满足在DSP应用中对操作系统环境的迫切需求,TI及其产品的用户均将DSP/BIOS视为一个“简单的”实时操作系统。
事实上DSP/BIOS并不是真正的实时操作系统,而只是用于帮助程序员开发实时操作系统的软件包。而且,它不包含网络功能。所以,在用DSP/BIOS设计网络操作系统时,需要从两方面入手:一,利用DSP/BIOS提供的资源进行裁减和整合;二,补充DSP/BIOS中未能提供的网络功能。 1 DSP/BIOS的构成
一般认为DSP/BIOS由三部分内容组成,即
1)DSP/BIOS实时库和API
2)DSP/BIOS配置工具
3)DSP/BIOS插件
其中配置工具用于提供可视化的编程环境,而插件用于支持调试过程。所以,实时库和API才是DSP/BIOS的核心。DSP/BIOS实时库可以为最终生成的嵌入式实时操作系统提供运行时的基本服务,包括线程调度,中断管理等功能。DSP/BIOS API由十几个模块组成,如DEV设备驱动接口,MEM存贮段管理器等。程序员通过调用API来使用DSP/BIOS。
DSP/BIOS API由十几个模块组成,每个模块又由相关的数据结构和函数组成。在有关DSP/BIOS的资料中混杂使用了模块(module)、对象(object)等术语,不过,DSP/BIOS与面向对象技术并没有直接的关联。所谓模块,是指一个逻辑上的概念,表示一组数据结构和以此为基础的一组函数。对象则是特指按模块中的数据结构所创建的变量(与C++中的类与对象不是同一个概念)。
如果程序员定义了某个对象,他将可以使用模块中的相应函数,并用这些函数代码来组成自己的嵌入式操作系统。
在程序员开发自己的嵌入式操作系统时,一般不会同时需要DSP/BIOS的全部模块。所以,DSP/BIOS中的内容是按需使用的。但是,开发平台会自动选择部分核心模块,并为它们创建对象。例如,开发平台会自动选择任务管理器( Multi-Task Manager)模块,以支持空闲处理功能:TSK_idle (),即:
TSK_Obj TSK_idle (iFXN, iARG0, iARG1, iARG2, iARG3, iARG4, iARG5, iARG6, iARG7, iAUTOSTK, iMANSTK, iSTKSZ, iSTKSEG, iPRI, iENV, iEXITFLAG, iUSETSKNAME, iSTATREG)
由DSP/BIOS实时库生成的代码和程序员新编制的代码链接到一起就构成了DSP的嵌入式应用系统。这些代码之间的执行关系由boot.c文件规定,该文件的缺省执行顺序为:
一,初始化DSP:由复位中断向量将程序引导到c_int00,开始初始化寄存器等硬件资源;二,初始化DSP/BIOS模块:通过调用BIOS_init完成,BIOS_init由开发平台中的配置工具自动生成;三,初始化用户应用环境:通过调用main例程实现;四,启动DSP/BIOS:由BIOS_start启动由DSP/BIOS实时库中的相关例程,BIOS_start也是由开发平台中的配置工具自动生成的。但是,通过调整boot.c可以改变系统的执行顺序。
2 NDK开发环境
为了加速其高档DSP的网络化进程,TI结合其C6000系列推出了TCP/IP NDK (Network Developer’s Kit)。该开发包采用紧凑的设计方法,实现了用较少的资源耗费支持TCP/IP。从实用效果看,NDK仅用200-250K程序空间和95K数据空间即可支持常规的TCP/IP服务,包括应用层的telnet,DHCP,HTTP等。所以,NDK很适合目前嵌入式系统的硬件环境,是实现DSP上网的重要支撑工具。
与常规的TCP/IP应用环境不同,为了最大限度地减少资源消耗,TI为其NDK采用了许多特殊技巧,例如:低层驱动程序与协议栈之间通过指针传递数据,不对包进行复制考贝。因为在嵌入式系统中,低层驱动程序和应用程序一样均需要开发者自行设计。也就是说,在以NDK为基础的开发中,开发人员需要分别设计低层驱动程序和应用程序,这两部分程序通过NDK提供的TCP/IP包发生关联。程序的执行过程是:应用程序调用TCP/IP包,TCP/IP包再调用低层驱动程序。
在NDK中对低层驱动程序与TCP/IP包之间的接口作了明确规室,换言之低层驱动程序必须符合接口约定。以PPP为例,其要点是:
1)由低层驱动程序调用TCP/IP包函数创建PPP连接实例,在连接实例中,以回调函数的形式将用于处理数据发送的函数名传递给TCP/IP包;
2)当TCP/IP包有数据需要发送时,直接调用PPP创建时由低层驱动程序传递来的函数名;
3)当低层驱动程序接收到网络数据时,调用TCP/IP包函数发送到IP层。
低层驱动程序直接面向硬件,为了适应硬件的多样性,在NDK中也提供了多种实现低层连接的方法,也为用户设计符合自己硬件特点的低层连接提供了接口规范。
3 利用NDK增加网络功能
如何在嵌入式系统中增添DSP/BIOS实时库中没有的功能是设计基于DSP/BIOS的实时操作系统中最主要的问题之一。解决这个问题一般需要两个步骤:一,程序员定制开发代码;二,与系统中的其它功能绑定在一起。现在,我们介绍如何利用NDK为其增加网络功能。
与常规的TCP/IP开发不同,在开发嵌入式应用时,开发人员必须对网络环境和应用需求作更细致的设置。在以NDK为基础的开发中,程序员需要完成的主要工作有:
1) 通过DSP/BIOS.cdb或DSP/BIOS API调用NETCTRL任务线程。该线程不是直正的网络任务线程,它以初始化线程的形式出现,起TCP/IP协议栈的事件调度线程的作用。
2) 调用初始化函数NC_SystemOpen()。该函数完成对协议栈其及所需要的内存的初始化。
3)创建系统配置。该系统配置用于对协议栈的控制和管理,可用CfgNew()和CfgLoad()等函数操作。
4)调用NETCTRL函数NC_NewStart()启动网络。NC_NewStart()函数的参数中包含三个回调函数指针,分别处理“Start”,“Stop”和“IP Address Event”事件,其中“Start”和“Stop”只执行一次,“IP Address Event”则响应每次IP地址的变化。
由于NDK已经提供了完整的TCP/IP库函数,程序员开发的代码只须按需要进行配置即可。下面是将嵌入式设备配置为车间局域网节点的核心代码:
char *LocalIPAddr ="128.247.117.12";
char *LocalIPMask ="255.255.254.0";
char *GatewayIP ="128.247.116.1";
char *DomainName ="demo.net";
int NetworkConfig()
{
int rc;
CI_IPNET NA;
CI_ROUTE RT;
HANDLE hCfg;
NC_SystemOpen();
// Create a new configuration TCP/IP Stack Initialization and Configuration
hCfg = CfgNew();
if( !hCfg )
{ goto main_exit; }
// Manually configure our local IP address
bzero( &NA, sizeof(NA) );
NA.IPAddr = inet_addr(LocalIPAddr);
NA.IPMask = inet_addr(LocalIPMask);
strcpy( NA.Domain, DomainName );
NA.NetType = 0;
// Add the address to interface 1
CfgAddEntry( hCfg, CFGTAG_IPNET, 1, 0,
sizeof(CI_IPNET), (UINT8 *)&NA, 0 );
// Add the default gateway.
bzero( &RT, sizeof(RT) );
RT.IPDestAddr = 0;
RT.IPDestMask = 0;
RT.IPGateAddr = inet_addr(GatewayIP);
// Add the route
CfgAddEntry( hCfg, CFGTAG_ROUTE, 0, 0,
sizeof(CI_ROUTE), (UINT8 *)&RT, 0 );
do
{
rc = NC_NetStart( hCfg, NetworkStart, NetworkStop, NetworkIPAddr );
} while( rc > 0 );
// Delete Configuration
CfgFree( hCfg );
// Close the OS
main_exit:
NC_SystemClose();
return(0);
}
为了让NetworkConfig与系统中的其它功能绑定在一起,可以通过开发平台创建一个TSK任务管理器对象,并将其定义为一个独立的线程任务。这样,TCPStackStart就加入到嵌入式系统中了。
The basic process of stack initialization is as follows:
----用初始化函数对操作系统环境进行初始化。NC_SystemOpen()
---创建一个新配置:CfgNew()
---通过配置函数API调用来构建新配置或加载一个原来存在的配置CfgLoad() 。
---启动带配置的堆栈NC_NetStart( hCfg, pfnStart, pfnStop, pfnNetIP )
---一些主要的初始化后NC_NetStart() 产生一个新的线程(调用用户的回调函数),产生网络要求的任务线程。
---正常工作下,网络不会关闭(直到调用NC_NetStop() ),当在某点调用NC_NetStop() 时,原先NC_NetStart()thread 调用用户回调函数来停止,
---运用程序可以在再次调用NC_NetStart() 时立即重新启动,而不需要重载一个新的配置。
---当NC_NetStart() 返回 and the session is over时,调用CfgFree() 释放配置句柄。
---所有资源被释放后,调用NC_SystemClose() 来完成系统关闭。
2009.3.9
1、
int CfgAddEntry(HANDLE hCfg, uint Tag, uint Item, uint Mode, uint Size, UINT8 *pData,
HANDLE *phCfgEntry);
功能:为配置创建一个新的配置入口。
参数:
hCfg- Handle to configuration配置句柄
Tag -Tag value of new entry 新入口的标签值
Item -Item value of new entry 新入口的项目值
Mode- Mode flags for how to add entry 如何增加入口的模式标志
Size- Size of entry data pointed to by pData 入口数据大小
pData -Pointer to entry data 入口数据指针
phCfgEntry- Pointer to where to write handle of new configuration entry
返回值:(四种情况)
1-Returns 1 on success with successful processing by a service callback function。
0-Returns 0 on success with no processing performed by a service callback function
CFGERROR_SERVICE<返回值<0 的情况有三种如下:
CFGERROR_BADHANDLE Invalid hCfg handle
CFGERROR_BADPARAM Invalid function parameter
CFGERROR_RESOURCES Memory allocation error while adding entry
小于CFGERROR_SERVICE
备注:To modify the default behavior, one or more of the followingflags can be set:
CFG_ADDMODE_UNIQUE :Replace all previous entry instances with this single entry.
CFG_ADDMODE_DUPLICATE :Allow full duplicate entry (duplicate Tag, Item, and entry data). Requests to add duplicates are normally ignored.
CFG_ADDMODE_NOSAVE :Do not include this entry in the linear buffer in CfgSave().
2、
void CfgFree(HANDLE hCfg);
参数:hCfg Handle to configuration
描述:Destroys a configuration. Unloads and frees all configuration entries and frees theconfiguration handle. After this call, the configuration handle hCfg is invalid.
3、
int CfgLoad(HANDLE hCfg, int Size, UINT8 *pData);
功能:把一线性存储块转化为一个配置。(通过配置入口来实现)
参数:
hCfg -Handle to configuration
Size -Size of memory block to load
pData -Pointer to memory block to load
返回值:Returns the number of bytes loaded, or less than 0 on an error. CFGERROR_BADHANDLE :Invalid hCfg handle
CFGERROR_BADPARAM :Invalid function parameter
4、
HANDLE CfgNew();
功能:创建一个新配置。这个配置可以被配置函数作为参数,
返回值:Returns handle to a new configuration or NULL on memory allocation error.
5、
int CfgRemoveEntry(HANDLE hCfg, HANDLE hCfgEntry);
功能:从配置中通过句柄来移除配置入口
返回值:成功返回0,失败返回下面错误代码:
CFGERROR_BADHANDLE- Invalid hCfg handle
CFGERROR_BADPARAM -Invalid function parameter