用户终端硬件结构及RTL8019的配置
1、用户终端结构硬件结构图如图2所示:系统由两块单片机组成,一块负责五表读
数、安防红外遥控器及安防信号的采集工作,另一块负责驱动RTL8019通讯、家电
控制,并响应键盘及驱动液晶显示。两块MCU之间通过串口交换数据。
图2 用户终端结构图(略)
2、RTL8019的配置
RTL8019是一款高度集成的以太网控制芯片,它有两种工作模式:一种是跳线模式
jumper,一种是非跳线模式 jumperless,后者也支持Plug and Play。在单片机中
一般都采用跳线模式选项。I/O端口的基地址IO_BASE_ADDRESS由单片机和8019之间
的接线决定。此外8019还具有8个IRQ接口,本系统中没用到IRQ,采用查询方式。
8019输入输出地址共32个,地址偏移量为00H――1FH:
其中00H--0FH共16个地址,为寄存器地址,寄存器分成4页PAGE0――PAGE3,与
NE2000兼容的寄存器只有3页(Page0-Page2),为了保证驱动程序对所有Ne2000的
网卡有效,不要去操作第四页的寄存器。
10H--17H共8个地址,为DMA地址。
18H--1FH共8个地址,为软复位端口。8019的硬件复位很简单,只需在上电时对
RSTDRV输出一高电平就可以了。8019复位的过程将执行一些操作,比如将93c46读
入,将内部寄存器初始化等,至少需要2毫秒的时间。推荐等待更久的时间之后才
对网卡操作,比如100毫秒之后才对它操作,以确保完全复位。
ICS16B=LOW时采用8位DMA操作模式,上面的地址中只有18个是有用的: 00H--
0FH共16个寄存器地址。10H DMA地址 (10H--17H的8个地址是一样的,都可以用
来做DMA端口,只要用其中的一个就可以了)。1FH 复位地址(18H到1FH共8个地址
都是复位地址,每个地址的功能都是一样的,只要其中的一个就可以了,但实际上
只有18H、1AH、1CH、1EH这几个复位端口是有效的,其他不要使用,有些兼容卡不
支持19H、1BH、1DH等奇数地址的复位)。
网卡驱动及TCP/IP协议栈的简化
从程序员的角度来说,对8019的操作是比较简单的,驱动程序只需要将要发送的数
据按一定的格式写入芯片并启动发送命令,8019会自动添加接收状态、下一页指针
、以太网帧长度和校验FCS段,并将数据包转换成物理帧格式在物理信道上传输。
反之,8019收到物理信号后将其还原成数据,按指定格式存放在芯片RAM中以便主
机程序取用。简言之就是8019完成数据包和电信号之间的相互转换:数据包<===>
电信号。以太网协议由芯片硬件自动完成,对程序员透明。驱动程序有3种功能:
芯片初始化、收包、发包。发送数据包是先将待发送的数据包通过DMA写操作存入
网卡芯片RAM,并给出发送缓冲区首地址(TPSR0、TPSR1)和数据包长度(TBCR0,
TBCR1),启动发送命令,网卡芯片会自动按以太网协议完成发送并将结果写入状态
寄存器。接收数据包时,采用查询的方式,根据CURR==BNRY+1?可以判断是否收
到新的数据包,如果有则通过DMA读操作从网卡芯片RAM读出数据。发送、接收子程
序如下所示:
1发送子程序
bit Transmit(void)
{
CardCopyDown();
XBYTE[IO_BASE_ADDRESS + NIC_COMMAND] = CR_NO_DMA|CR_STOP|CR_PAGE0;//停止
8019
XBYTE[IO_BASE_ADDRESS + NIC_INTR_STATUS] = 0xFF;//清中断标志
XBYTE[IO_BASE_ADDRESS + NIC_XMIT_START] = XMIT_START;//设置发送开始地址
XBYTE[IO_BASE_ADDRESS + NIC_XMIT_CONFIG] = TCR_NO_LOOPBACK;//设置为一般
模式
XBYTE[IO_BASE_ADDRESS+NIC_DATA_CONFIG]=DCR_FIFO_8_BYTE|DCR_NORMAL|DCR_BY
TE_WIDE;//设置8位DMA模式
XBYTE[IO_BASE_ADDRESS + NIC_XMIT_COUNT_LSB] = 100;//设置发送数据长度
XBYTE[IO_BASE_ADDRESS + NIC_XMIT_COUNT_MSB] = 0;
XBYTE[IO_BASE_ADDRESS + NIC_COMMAND] = CR_START|CR_XMIT|CR_PAGE0;//启动
8019
return (TRUE);
}
2接收子程序
bit Receive(void)
{
uint TempShort;
uchar Temp;
uchar CURR;
uchar BNRY;
uint i;
//停止网卡
XBYTE[IO_BASE_ADDRESS + NIC_COMMAND] = CR_STOP|CR_NO_DMA|CR_PAGE1;
//读取当前CURRENT的值
CURR= XBYTE[IO_BASE_ADDRESS + NIC_CURRENT];
//读取当前BOUNDARY的值
XBYTE[IO_BASE_ADDRESS + NIC_COMMAND] = CR_STOP|CR_NO_DMA|CR_PAGE0;
BNRY= XBYTE[IO_BASE_ADDRESS + NIC_BOUNDARY];
if(CURR==0)
return (FAULSE);
if((++BNRY)> PAGE_STOP)
BNRY=PAGE_START;
if(CURR!=BNTY)//表示有包收到
{
//设置远端DMA地址和长度
XBYTE[IO_BASE_ADDRESS + NIC_RMT_ADDR_LSB] = 0x00;
XBYTE[IO_BASE_ADDRESS + NIC_RMT_ADDR_MSB] = BOUNDARY;
XBYTE[IO_BASE_ADDRESS+NIC_RMT_COUNT_LSB]=__dread&0xFF;
XBYTE[IO_BASE_ADDRESS + NIC_RMT_COUNT_MSB] = (__dread>>8)&0xFF;
//设置DMA读
XBYTE[IO_BASE_ADDRESS + NIC_COMMAND] = CR_START|CR_DMA_READ|CR_PAGE0;
//重复读DMA端口
for (i=0;i<__dread;i++)
{
Receive_data = XBYTE[IO_BASE_ADDRESS + NIC_RACK_NIC];
}
//等待DMA停止
TempShort = 0xFFFF;
while(TempShort)
{
Temp = XBYTE[IO_BASE_ADDRESS + NIC_INTR_STATUS];
if (Temp&ISR_DMA_DONE) break;
TempShort --;
}
return (TRUE);
}
else
return(FAULSE);
}
3协议栈的精简
单片机中TCP/IP的实现与PC机不同,在PC里可支持比较完整的TCP/IP协议组,但在
单片机里无法做到,这是因为单片机根本没有足够的代码空间来支持这些协议。一
般在单片机里实现与需要有关的部分,而不使用的协议则一概不支持。例如文件共
享SMB协议,在UNIX、WINDOWS都支持,但单片机上却没有必要。一般只能在单片机
中实现:ARP、IP,ICMP、TCP/UDP这些协议,而更高层的协议,HTTP、SMTP、FTP
一般是不需要支持的。虽然有些单片机例如AVR上网方案实现了这些协议,但实用
性不大。因为单片机应用的TCP/IP协议大多是为了完成数据采集和数据传输,而不
是网页浏览、文件传输这些功能。另外由于单片机资源的有限性,对某一协议而言
,也有可能要作简化。本系统中实现的协议只有ARP、IP、ICMP、UDP。在选择传输
层协议时我们放弃了面向链接的TCP,因为TCP为了保证数据的可靠性采用一问一答
的方式,这在数据量并不大的嵌入式系统中,反而增加了网络的负担;另外TCP的
很多机制,如分段机制、窗口机制等都适合于数据量大、处理能力强的PC机网络,
并不适合于嵌入式系统。UDP虽然不提供面向链接的、可靠的服务,但是协议简单
,实时性更强,应用层也可以做相应的定时等待、重发处理等辅助性的操作来弥补
它的缺陷。
a.ARP:ARP的本质就是实现IP地址到MAC地址的转换,抓住了这一点实现就比较简
单。嵌入式系统中没有必要实现PC机网络中地址表的更新功能,只需完成地址转换
。管理中心机在初始化CDT时发送ARP请求,CDT只需响应中心对它的ARP请求,但不
主动发送ARP请求。
b.ICMP:ICMP的种类很多,在本系统中只要实现ICMP信息回显功能。管理中心在
执行PING命 令 时,发送ICMP请求信息,CDT中只需实现ICMP回应信息。
c.IP:IP包最大可达65k,单片机中无法存放如此大的数据包,因此一般不支持分
段,以发送小数据的方式来避免分段。另外IP层的选项功能都可以完全忽略,IP层
只需要根据协议类型分包。
d.应用层:应用层采用自己的数据协议格式为:
头部+命令字段+布防字段+报警字段+五表参数字段+五表读数字段+家电控制
字段
接收方根据命令段的含义来分别实现布防、报警、五表参数设置、五表读书、家电
控制等功能。
结束语
本文以基于TCP/IP技术的智能小区家庭智能终端系统的研究为例,详细讲述了
RTL8019网卡芯片的驱动方法,并给出了单片机中TCP/IP协议的精简方案。本系统
已经成功地运用于多个小区,其可行性、可靠性和灵活性已经得到证实,并且基于
以太网智能小区系统低廉的造价、简洁的布线更使得基于TCP/IP的以太网技术成为
了替代传统现场总线技术的一种重要手段。
另外我在用LPC2214的外部中断时,经常引起JTAG口没反应,使仿真器挂起,需要烧录另外一个好的程序启动一下,再用仿真器就又可以了,不知道你们有没有遇到这种情况。
============================================================
======================另外一例================================
==============================================================
*******************************************************************************
* Copyright (C) 2005 Ben Ning
* rtl8019.c: Send & Receive Implementation for RTL8019AS
*
*******************************************************************************/
#pragma LARGE ORDER NOPRINT OPTIMIZE(2)
#include <stdlib.h>
#include <stdio.h>
#include <absacc.h>
#include <string.h>
#include "NetAPI.h"
#include "NET.H"
//#include "SERIAL.H"
#include "w78e58.h"
#include "rtl8019.h"
//-----------------------------------------------------------------------------
// select RTL8019AS register page
//-----------------------------------------------------------------------------
void rtl8019_page(UCHAR page_num)
{
UCHAR data temp;
temp = CR; //command register
temp=temp&0x3B ; // 0011 1011 bit2 = 0 for to disabel TXP
page_num = page_num << 6;
temp=temp | page_num;
CR = temp;
}
//-----------------------------------------------------------------------------
// reset RTL8019AS chip
//-----------------------------------------------------------------------------
void rtl8019_reset()
{
UCHAR i, j, temp;
// hardware reset
RTL8019_RESET = 1;
for (i=0; i<100; i++) for (j=0; j<255; j++);
RTL8019_RESET = 0;
for (i=0; i<100; i++) for (j=0; j<255; j++);
// software reset
temp = REST_PORT;
for (i=0; i<100; i++) for (j=0; j<255; j++);
REST_PORT = temp;
for (i=0; i<100; i++) for (j=0; j<255; j++);
}
//-----------------------------------------------------------------------------
// Initialize the RTL8019AS chip
//-----------------------------------------------------------------------------
extern UCHAR xdata my_hwaddr[6];
void rtl8019_init(void)
{
UCHAR i, j;
// for (i=0; i<255; i++);
rtl8019_reset();
CR = 0x21; // stop mode for setting registers and select page 0
for (i=0; i<10; i++)
for (j=0; j<255; j++); // delay about 10ms
DCR = 0xc8; // byte dma 8 bit dma mode
RBCR0 = 0x00;
RBCR1 = 0x00;
TCR = 0x02;
// RCR = 0xcc; //monitor mode (no packet receive)
RCR = 0xe0; //monitor mode (no packet receive)
PSTART = RXSTART_INIT;
// PSTOP = 0x80; // for 16 bit mode
PSTOP = RXSTOP_INIT; // for 8 bit mode
BNRY = RXSTART_INIT;
TPSR = TXSTART_INIT;
ISR = 0xff; // clear all interrupt flags
IMR = 0x00; // disable all interrupt
CR = 0x61; // select page 1
CURR = RXSTART_INIT;
// CR = 0x22; // Let chip to work
// TCR = 0xe0;
// CR = 0x62;
// set Mac address
PAR0 = my_hwaddr[0];
PAR1 = my_hwaddr[1];
PAR2 = my_hwaddr[2];
PAR3 = my_hwaddr[3];
PAR4 = my_hwaddr[4];
PAR5 = my_hwaddr[5];
// set muticast address
MAR0 = 0xFF;
MAR1 = 0xFF;
MAR2 = 0xFF;
MAR3 = 0xFF;
MAR4 = 0xFF;
MAR5 = 0xFF;
MAR6 = 0xFF;
MAR7 = 0xFF;
CR = 0x21; // select page 0
RCR = 0xcc; // normal mode
TCR = 0xe0;
CR = 0x22; // Normal work
ISR = 0xff; // clear all interrupt flags
}
//-----------------------------------------------------------------------------
// This functions checks RTL8019AS status then sends an ethernet frame to it.
//-----------------------------------------------------------------------------
void rtl8019_send_frame(UCHAR xdata * outbuf, UINT len)
{
UINT data i, j, n;
rtl8019_page(0);
if (len<60) n = 60; else n = len;
// set start address for sending
RSAR1 = TXSTART_INIT; // write dma address -- high byte
RSAR0 = 0x00; // write dma address -- low byte
// set the count of bytes that will be send out
RBCR1 = n >> 8;
RBCR0 = n & 0xff;
CR = 0x12; //write dma, page0
// Write data to dma
for (i=0; i<len; i++) DMA_PORT = *(outbuf+i);
// If necessary, to file some PAD bytes with 0x00
for (i=len; i<n; i++) DMA_PORT = 0x00;
// Disable DMA OP
RBCR1 = 0x00; // write count high
RBCR0 = 0x00; // write count low;
CR = 0x22; //complete dma page 0
for(i=0;i<16;i++) // Try to resend 16 times
{
for (j=0; j<1000; j++) {
if (!(CR & TXP)) break; // If has transmited, break.
}
if (TSR & PTX) break; // If sending has completed
CR = 0x3e;
}
ISR = 0xff;
TPSR = TXSTART_INIT; //txd packet start;
TBCR1 = n >> 8; //high byte counter
TBCR0 = n & 0xff; //low byte counter
ISR = 0xff;
CR = 0x3e; //to sendpacket;
// Release memory for re-use
// free(outbuf);
}
//------------------------------------------------------------------------
// This function gets an incoming Ethernet frame from the RTL8019AS.
// There may be more than 1 waiting but just allocate memory for
// one and read one in. Use the RTL8019AS to queue incoming packets.
//------------------------------------------------------------------------
//extern UCHAR idata rcve_buf_allocated;
UCHAR xdata * rtl8019_rcve_frame(void)
{
UINT data len, i;
UCHAR xdata * data buf;
UCHAR xdata * data p;
UCHAR data curr, bnry;
UCHAR data status, next_page, len_l, len_h;
rtl8019_page(0);
status = ISR;
// Check if buffer overflow
if (status & 0x90)
rtl8019_OverflowRecover();
status = RSR;
if (!(status & 0x01)) // no data received
return NULL;
bnry = BNRY; //bnry page have read
rtl8019_page(1);
curr = CURR; //curr write page
rtl8019_page(0);
if (bnry==curr) return NULL; // no arrived package
CRDA1 = bnry; // current read dma address high
CRDA0 = 0x00; // current read dma address low
RBCR1 = 0x00; // count high
RBCR0 = 0x04; // count low
CR = 0x0a; // begin to read dma
// read 8019 header
status = DMA_PORT;
next_page = DMA_PORT;
len_l = DMA_PORT;
len_h = DMA_PORT;
RBCR1 = 0x00; // to read a page (256 bytes)
RBCR0 = 0x00;
CR = 0x22; // DMA completed page0
// if boundary pointer is invalid
// if (!(status & 0x01) || (next_page>0x7f) || (next_page<0x4c) || (len_h>0x06) )
if (!(status & 0x01) || (next_page>=RXSTOP_INIT) || (next_page<RXSTART_INIT) || (len_h>0x06) )
{
rtl8019_page(1);
curr = CURR;
rtl8019_page(0);
bnry = curr;
BNRY = bnry;
ISR = 0xff;
return NULL;
}
len = ((UINT) len_h << 8) | (UINT) len_l;
len = len - 4; // subract 4 bytes for eth CRC
// Allocate enough memory to hold the incoming frame
buf = (UCHAR xdata *) malloc(len);
if (buf == NULL) {
return NULL;
}
p = buf;
CRDA1 = bnry; // read DMA address high
CRDA0 = 0x04; // read DMA address low, skip 4 byte that is 8019 header
RBCR1 = len >> 8; // count high
RBCR0 = len & 0x00ff; // count low
CR = 0x0a; // begin to read DMA
for (i=0; i<len; i++) {
*p++ = DMA_PORT;
}
RBCR1 = 0x00; // to read a page (256 bytes)
RBCR0 = 0x00;
CR = 0x22; // DMA completed page0
ISR = 0xff;
BNRY = next_page; //bnry; // Update BNRY
return (buf);
}
void rtl8019_OverflowRecover(void)
{
UCHAR data data_L, resend;
UCHAR idata i, j;
data_L = CR;
CR = 0x21; // stop mode for setting registers and select page 0
for (i=0; i<10; i++) for (j=0; j<255; j++); // delay about 10ms
RBCR0 = 0x00;
RBCR1 = 0x00;
if (data_L & TXP) {
data_L = ISR;
if((data_L & 0x02) || (data_L & 0x08))
resend = 0;
else
resend = 1;
}
else
resend = 0;
TCR = 0x02;
CR = 0x22;
BNRY = RXSTART_INIT;
CR = 0x62;
CURR = RXSTART_INIT;
CR = 0x22;
ISR = 0x10;
TCR = TCR_INIT;
if(resend)
CR = 0x26;
ISR = 0xFF;
}
tengjingshu_112148725 2009-5-12 18:02