图5. 选择TINIm400配置选项。
选择Tini→Open COMx在xxx baud菜单选项打开串口。接着选择Tini→Reset 选项复位评估板。会出现DS80C400的装载提示,如下所示:
DS80C400 SILicon SOFtware - Copyright (C) 2002 Maxim Integrated Products
DetaiLED product information available at http://www.maxim-ic.com
Welcome to the TINI DS80C400 Auto Boot Loader 1.0.1
>
从File菜单中选择Load HEX File.找到并选择我们刚才生成的hello_world.hex文件。加载程序后,有两种方法运行它。因为我们将程序加载到40区,您可以输入:
> B40
> X
要选择40区并运行那里的代码,您也可以输入:
> E
这会使ROM查找可执行代码。它查找一个标识当前区具有可执行代码的特定标签。此标签由文本'TINI'和随后的当前区号码组成,并位于当前区的0x0002地址。应用程序的起始代码采用下面几行声明该标签:
?VECTOR_TABLE:
sjmp ?INIT
DB 'TINI' ; Tag for TINI Environment 1.02c
; or later (ignored in 1.02b)
DB high(?INIT) ; Target bank
注意sjmp ?INIT语句位于0x40区的0x0000地址。其后紧跟着可执行标签{ 'T', 'I', 'N', 'I', 0h},由于sjmp语句为两个字节,所以该标签地址位于0x0002处。当您键入E时,ROM从C0h区开始向下搜索可执行代码。如果您键入E时,执行了其它代码,则意味着ROM在一个比您的代码装载位置0x400000更高的地址找到了一个可执行标签。如果出现这种情况,您可能需要找到此标签的位置,并删除那个区的内容。
与ROM以及IAR ROM库接口
在高速微控制器用户指南DS80C4003补充资料中说明了在汇编语言中调用ROM函数的过程。但是,在C中调用这些ROM函数会复杂一些。必须将参数从IAR C编译器的规则转换成ROM使用的规则。IAR编译器通过硬件堆栈和寄存器相结合的方式传递参数。ROM函数以多种不同方式接受参数。例如,socket函数接收存储在一个参数缓冲器中的参数。相反,许多功能函数接收由特殊功能寄存器或堆栈存储器传递的参数。为了从IAR调用方式转换为ROM参数方式,Dallas Semiconductor已经编写了访问ROM函数的库。
在您的C程序中使用ROM函数只需包含一个头文件并与相应的库文件连接即可。用于IAR编译器的ROM库包括:
ROM初始化程序
DHCP客户端
进程调度
Sockets (TCP、UDP和Multicast)
TFTP客户端
功能函数(CRC16, 随机数)
在撰写本文时,还没有为IAR编译器提供包括文件系统、邮件客户端和HTTP服务器之类的扩展库。请关注IAR库主页上的DS80C4004升级信息,我们会添加更多支持IAR的库。
简单应用: HTTP服务器
这里编写了一个简单的http服务器说明如何使用一些ROM库函数,特别是socket和进程调度库。该示例应用程序由两个模块组成:一个HTTP服务器和一个SNTP客户端。主程序生成一个新的子任务来运行http服务器,用于处理80端口上的客户连接。父任务每60秒会试图通过时间服务器同步当前时间。
SNTP客户端模块
以下代码实现SNTP客户端模块的核心功能。
socket_handle = socket(0, SOCKET_TYPE_DATAGRAM, 0);
for (i=0;i<256;i++)
buffer[i] = 0;
// set a timeout of about 2 seconds
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 = htons(NTP_PORT) // port number
sendto(socket_handle, buffer, 48, 0, &address, sizeof(struct sockaddr));
recvfrom(socket_handle, buffer, 256, 0, &address, sizeof(struct sockaddr));
//IAR 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, "London", last_time_reading_1);
formatTimeString(timeStamp - (6 * SECONDS_PER_HOUR), "Dallas", last_time_reading_2);
formatTimeString(timeStamp + (5 * SECONDS_PER_HOUR) + (30 * SECONDS_PER_MINUTE), "Bangalore", last_time_reading_3);
formatTimeString(timeStamp - (10 * SECONDS_PER_HOUR), "Honolulu",
last_time_reading_4);
last_reading_seconds = getTimeSeconds();
closesocket(socket_handle);
SNTP客户端模块是通过RFC 1361实现的。SNTP模块通过使用UDP协议和time.nist.gov通信,并请求一个时间标记。需注意撰写本应用笔记时还不能提供DNS查找支持,因此time.nist.gov的IP地址是人工设定的。
首先,创建一个数据包socket并分配一个大约2秒(0x800==2048毫秒)的超时。这样会保证如果和我们选中的服务器通信失败,我们不会无休止地等待响应。
接下来的一行用来设置请求选项。在RFC 1361的第3节对这些位进行了说明。0x23在一个闰秒不产生告警,要求使用版本4 NTP,并声明模式为Client.我们使用普通数据包函数sendto和recvfrom请求发送并接收响应后,将时间标记的秒赋予变量timeStamp,然后调整至参考日期1970年1月1号。用函数formatTimeString将时间标记转换成一个可读字符串,比如说"In London it is 05:33:19 on May 11, 2005".
用函数getTimeSeconds 确定基于DS80C400内部时钟的最后一次更新时间。由于程序大约每60秒更新一次,HTML网页time.html将会使用该数值来报告上一次时间更新后已经过了多长时间。最后,关闭socket,SNTP客户端进入另一个60秒的休眠期。
简单HTTP服务器
这个时间服务器应用程序的另一个子模块为web服务器。此应用程序中的HTTP服务器实现了一个RFC 2068中描述的HTTP服务器简易版本。在我们的版本中,仅支持GET方法,忽略输入头文件,并且几乎不给出输出包头。在撰写这篇应用笔记时尚未提供文件系统库,因此示例应用程序动态地生成HTML网页。
通过调用Berkley-style socket函数来创建服务器socket.这使得建立一个服务器socket十分容易。以下代码给出我们的简易HTTP服务器的创建、绑定和接受新连接。
struct sockaddr LOCal;
unsigned int socket_handle, new_socket_handle, temp;
socket_handle = socket(0, SOCKET_TYPE_STREAM, 0);
local.sin_port = htons(80);
bind(socket_handle, &local, sizeof(local));
listen(socket_handle, 5);
printf("Ready to accept HTTP connections…r
");
// here is the main loop of the HTTP server
while (1)
{
new_socket_handle = accept(socket_handle, &address, sizeof(address));
handleRequest(new_socket_handle);
closesocket(new_socket_handle);
}
注意当接收到一个新的socket时,这一简易应用程序不会启动一个新的线程或进程处理该请求,而是在同一进程中处理请求。任何优于该演示的HTTP服务器都会在一个新的线程中处理到来的请求,允许同时发生多个连接并能进行处理。请求处理完毕后我们关闭socket并等待下一个到来的连接。
handleRequest方法从接入的请求中解析出文件名并且验证请求方法为GET.不允许使用其它方法(即使是POST、HEAD或OPTIONS)。