DS80C400包含一个提供网络栈、内存管理和进程调度的ROM,可以灵活地用于由Java、C和8051汇编编程的应用中。SDCC为8051器件提供了一个免费、开放源码的编译器,并兼容DS80C400的24位寻址模式。用C编写的复杂应用程序在Dallas Semiconductor提供的库的帮助下,可以很容易地使用DS80C400 ROM功能创建。
本应用笔记阐述了如何使用SDCC工具来创建DS80C400应用程序。从一个HelloWorld应用程序开始,然后说明如何使用ROM库来实现一个简单的HTTP服务器。这里的应用程序是针对TINIm400参考模块编写和创建的,用于具有其它存储器配置的设计时必须进行相应修改。
遵循以下步骤,使用SDCC编译器来完成您的第一个DS80C400的C应用程序:
安装SDCC编译器¹
使用你喜欢的文本编辑器创建一个新文件"main.c"。在文件中写入以下代码:
#include < stdio.h >
void main ()
{
printf("Hello Universe!!!!....Welcome to SDCC Tini Test Program");
while (1)
{
}
}
一定要保存文件内容。
从SDCC C库站点²中拷贝文件startup400.a51和reg400.inc (包含在启动代码下载中),并保存到您保存main.c文件的目录中。此文件包含startup_code函数,将在应用程序启动时调用该函数,从而对DS80C400芯片进行初始化。启动代码完成以下工作:
从SDCC C库站点拷贝ROM initialization库文件(从init库文件下载的rominit.lib和rom400.h),并将其解压缩至相同目录下。库文件是压缩的,使用WinZip或gunzip/tar解压缩包。
在编译我们的"Hello Universe"应用程序之前,我们需要在一个SDCC安装的支持文件中作一个小改动,覆盖缺省的DS80C400支持函数并使用Dallas Semiconductor的C库代替。进行以下改动:
构建"Hello Universe"应用程序...
要由我们的startup400.a51文件创建一个目标文件(.rel),在命令行执行以下命令:
asx8051 -losffgp startup400.a51
asx8051是SDCC工具提供的汇编器。汇编器提供的参数选项有:| Option | Purpose |
| -------- | ----------------------------------------------------- |
| l | generates a list file |
| o | generates an object file |
| s | generates a symbol file |
| ff | flag reolcatable references by mode in listing file |
| g | make undefined symbols be global |
| p | disables listing pagination |
"los"参数是必须的,因为连接器需要列表、目标和符号文件来生成可执行文件。"ff"和"p"参数生成一个可读的列表文件。"g"参数通知汇编器在发现一个没有定义的符号且该符号未声明为外部变量时不报错。
为了由main.c生成一个目标文件,执行以下命令:
sdcc -c -mds400 --model-flat24 --stack-10bit --no-xinit-opt main.c
sdcc为编译器。
传递给编译器的参数选项为:
Option | Purpose |
---|---|
-c | compiles main.c and creates an object file |
-mds400 | generates code for the DS80C400 processor |
--model-flat24 | use the 24-bit contiguous memory model |
--stack-10bit | use the 1024-byte extended stack (10 bit stack addresses) |
--no-xinit-opt | don't initialize the external RAM memory area |
p | disables listing pagination |
注意列表中最后三个参数是双破折号。
为了连接目标文件并构建可执行文件,执行以下命令:
sdcc -mds400 --model-flat24 --stack-10bit -Wl-r --xram-loc
0x10000 --xram-size 0x3fff --code-loc 0x400000 main.rel startup400.rel -l
rominit.lib
这里使用的新参数为:| Option | Purpose |
| ------------- | -------------------------------------------------------------- |
| -WI | pass options through to the linker |
| --xram-loc | external RAM start address (only RAM for SDCC variable use!) |
| --xram-size | external RAM size (only RAM for SDCC variable use!) |
| --code-loc | code starting address |
| -l | include the specified libraries |
| p | disables listing pagination |
请注意xram-loc、 xram-size和code-loc参数为双破折号。也要注意给命令指定的RAM将会用来存储SDCC变量,不应该和init_rom函数中用来初始化DS80C400所使用的存储器范围冲突―此存储器用作网络栈和存储器管理。
为了压缩可执行文件并生成一个十六进制文件,执行以下命令:
packihx main.ihx>hellouniverse.hex
packihx命令通过将连续数据记录累积至16个字节来压缩可执行文件。
有了一个可执行文件后,我们需要将应用程序下载到TINIm400模块中并运行它。### 将示例应用程序加载到TINIm400模块
本节说明如何使用Maxim/Dallas Semiconductor提供的 微控制器工具包(MTK) 向TINIm400验证模块中加载由SDCC编译器生成的十六进制文件。目前可用的MTK版本只支持Windows®。
如果您的开发环境不是Windows,需要使用JavaKit应用程序来下载和执行应用程序。要使用JavaKit,您必须有Java运行环境³ (版本至少为1.2)并且安装Java Communications API ^4^ 。JavaKit工具包含在TINI软件开发包中。写本文的时候,发布的最新固件是固件版本1.13。运行JavaKit的指导说明可以在TINI SDK docs目录下的Running_JavaKit.txt文件中找到。如果您在运行MTK或JavaKit时遇到技术问题,可能其他人已经遇到过类似问题并且已经发表在Dallas Semiconductor的讨论区中。
最新版本的MTK应用软件可下载。要安装MTK,请运行安装文件并遵照提示操作。成功安装后,会增加一个新的菜单项: Start->All Programs->Dallas Semiconductor MTK。 MTK启动后,会出现图1所示的对话框。
图1. 启动时MTK选项
选择选项TINI,以操作TINIm400评估板。
选择了TINI之后,会打开MTK主窗口。从Options->Configure Serial Port菜单选项中选择您将用来和TINIm400通讯的串口。然后,选择Tini->Tini Options菜单项,就会出现下面的对话框。选择DSTINIm400按钮,配置MTK用于和TINIm400板通讯。图2显示了带有DSTINIm400按钮的对话框。
图2. 选择TINIm400配置选项
选择Tini->Open COMx at xxx baud菜单选项打开串口。接着选择Tini->Reset选项复位评估板。会出现DS80C400的加载提示:
DS80C400 Silicon Software - Copyright (C) 2002 Maxim Integrated
Detailed product information available at http://www.maximintegrated.com
Welcome to the TINI DS80C400 Auto Boot Loader 1.0.1
>
从文件菜单中选择Load HEX File。找到我们刚刚生成的hellouniverse.hex文件并选中。一旦您的程序加载后有两种方法运行它。因为我们将程序加载到40区,您可以输入:
> B40
> X
要选择40区并运行那里的代码。您也可以输入:
> E
这会使ROM查找可执行的代码。它查找一个标识当前区具有可执行代码的特定标签。此标签由文本'TINI'组成,其后面紧跟着当前区的号码(或零),并位于当前区的0002h地址。SDCC编译器在生成的汇编代码中插入此标签。如果打开为hellouniverse工程生成的main.asm源代码,您会找到下面的代码段:
.area CSEG (CODE)
interrupt_vect:
; DS80C400 IVT must be generated at runtime.
sjmp __sdcc_400boot
.ascii 'TINI' ; required signature for 400 boot loader.
.db 0 ; selected bank or zero...
__sdcc_400boot:
ljmp __sdcc_gsinit_startup
注意sjmp__sdcc_400boot语句位于40区的0000h地址。其后跟随可执行标签{ 'T', 'I', 'N', 'I', 0h},由于simp语句为两个字节,因此该标签位于地址0002处。当您键入'E'时,ROM从C0h区开始向下搜索可执行代码。如果您键入'E'时,执行了其它的代码,则意味着ROM在一个比您的代码加载位置400000h更高的地址找到了一个可执行标签。您可能需要找到此标签的位置,并删除那个区的内容。### 和ROM以及SDCC ROM库接口
在高速微控制器用户指南DS80C400补充资料中说明了在汇编语言中调用ROM函数的过程。但是,在C中调用这些ROM函数会复杂一些。必须将参数从SDCC C编译器的规则转换成ROM使用的规则。SDCC编译器通过硬件堆栈、累加器和数据指针相结合的方式来传递参数。ROM函数采用许多不同的方式来接受参数。例如,socket函数接收存储在一个外部RAM缓冲区中的参数。相反地,许多功能函数接收由特殊功能寄存器或直接存储器位置传递的参数。为了从SDCC调用方式转换成ROM参数方式,Dallas Semiconductor已经编写了访问ROM函数的库。
在您的C程序中使用ROM函数只需包含一个头文件并与相应的库文件连接即可。用于SDCC编译器的ROM库包括:
在写本文时,还没有为SDCC编译器提供包括如文件系统、邮件客户端和HTTP服务器之类的扩展库。请关注SDCC库主页上的DS80C400升级信息,我们会添加支持SDCC的库。### 简单应用:HTTP服务器
编写了一个简单的http服务器来说明如何使用一些ROM库函数,特别是socket和进程调度库。这个示例会偶尔通过网络时间服务器更新它的时间,并且通过它的web服务器提供这个信息。
示例应用程序由两个模块组成,一个HTTP服务器和SNTP客户端。主程序生成一个新的子任务来运行http服务器,用于处理80端口上的客户连接。父任务每60秒会试图通过时间服务器同步当前时间。
以下代码段实现SNTP客户端模块的核心功能。
socket_handle = socket(0, SOCKET_TYPE_DATAGRAM, 0);
// set a timeout of about 2 seconds
for (i=0;i< 256;i++)
buffer[i] = 0;
buffer[0] = 0x0;
buffer[1] = 0x0;
buffer[2] = 0x8;
buffer[3] = 0x0;
setsockopt(socket_handle, 0, SO_TIMEOUT, buffer, 200);
buffer[2] = 0; //reset since we used this in call to setsockopt
buffer[0] = 0x23; // No warning/NTP Ver 4/Client
address.sin_addr[12] = TIME_NIST_GOV_IP_MSB;
address.sin_addr[13] = TIME_NIST_GOV_IP_2;
address.sin_addr[14] = TIME_NIST_GOV_IP_3;
address.sin_addr[15] = TIME_NIST_GOV_IP_LSB;
address.sin_port_high = (NTP_PORT/0x100); //higher byte of port number
address.sin_port_low = (NTP_PORT%0x100); //lower byte of port number
sendto(socket_handle, buffer, 48, 0, &address, sizeof(struct sockaddr));
recvfrom(socket_handle, buffer, 256, 0, &address, sizeof(struct sockaddr));
//SDCC uses little Endian for storing data, so reorganize the data before converting it to long
buffer[0]=buffer[43];
buffer[1]=buffer[42];
buffer[2]=buffer[41];
buffer[3]=buffer[40];
timeStamp = *(unsigned long *)(&buffer[0]);
formatTimeString(timestamp - (5 * SECONDS_PER_HOUR), "Tampa, USA",
last_time_reading_1);
formatTimeString(timeStamp - (3 * SECONDS_PER_HOUR), "Sao Paulo, Brazil",
last_time_reading_2);
formatTimeString(timeStamp + (1 * SECONDS_PER_HOUR),"Marseille, France",
last_time_reading_3);
formatTimeString(timeStamp + (5 * SECONDS_PER_HOUR) + (30 *
SECONDS_PER_MINUTE), "Bangalore, India",
last_time_reading_4);
formatTimeString(timeStamp + (8 * SECONDS_PER_HOUR), "Hsinchu, Taiwan",
last_time_reading_5);
last_reading_seconds = getTimeSeconds();
closesocket(socket_handle);