引言
在嵌入式应用中,单片机与外部通信非常频繁,通信方式种类繁多,如SPI、I2C总线、RS232、USB、以太网等。以太网传输速度快、传输距离远、通过连接网络设备可以轻易进行组网管理,是进行远程控制[12]、大规模数据采集、众多设备节点管理[34]非常有效的一种方式。很多单片机并无以太网协议栈硬件支持,在为这些单片机开发网络模块时,一般是通过单片机已有的通信接口外接以太网芯片来实现。本设计通过单片机中常见的SPI口外接以太网模块,实现以太网通信。
1以太网芯片CH395
本设计采用Microchip公司的dsPIC33EP256GM710(以下简称dsPIC33E)控制器,通过SPI口外接以太网芯片CH395来实现以太网功能。dsPIC33EP256GM710是16位数字信号处理器,是一款低功耗、高性能、低成本的处理器。它有3个SPI硬件模块,其中SPI1是一个高速的SPI模块,在本设计中被采用。
CH395芯片是沁恒公司的一款以太网协议栈[5]管理芯片,被单片机系统进行以太网通信。CH395 芯片自带10/100M以太网介质传输层(MAC)和物理层(PHY),完全兼容IEEE802.310/100M 协议,内置了PPPOE、IP、DHCP、ARP、ICMP、IGMP、UDP、TCP 等以太网协议栈固件。
单片机系统可以方便地通过CH395 芯片进行网络通信。CH395 支持3种通信接口:8 位并口、SPI 接口和异步串口。单片机/DSP/MCU/MPU 等控制器可以通过上述任何一种通信接口控制CH395 芯片进行以太网通信[6]。CH395与控制器的接口图1所示。
图1 H395与控制器的接口示意图
2CH395与处理器的硬件连接
SPI是串行外设接口,是一种高速、全双工、同步的通信总线,在引脚上只占用4根线,节约了芯片的引脚。SPI 同步串行接口信号线包括:SPI 片选输入引脚SCS、串行时钟输入引脚SCK、串行数据输入引脚SDI、串行数据输出引脚SDO。CH395 芯片的SCS 引脚由单片机的SPI片选输出引脚或者普通输出引脚驱动,SCK引脚由单片机的SPI 时钟输出引脚SCK驱动,SDI引脚由单片机的SPI 数据输出引脚SDO或MOSI驱动,SDO引脚则连接到单片机的SPI数据输入引脚SDI或MISO。
CH395的SPI接口也支持单片机用普通I/O引脚通过软件来模拟SPI接口进行通信。CH395总是从SPI时钟SCK的上升沿输入数据,并在允许输出时从SCK的下降沿输出数据。
设计采用的是dsPIC33系列处理器中的高速专用SPI1口,它作为SPI通信的主控器件,与CH395的硬件接口如图2所示,主控器件的SDO引脚连接从器件的SDI引脚,主控器件的SDI引脚连接从器件的SDO引脚。CH395的输出INT#引脚连接处理器的一个通用I/O引脚,用来处理来自CH395的中断。
图2 CH395与单片机的硬件接口图
3单片机前端程序设计[7]
CH395芯片的操作都是在主控SPI的CS片选信号有效的情况下进行的。单片机与CH395进行通信交互是通过操作码与数据相结合的方式进行的,首先单片机通过SPI的SDO引脚按位移出一个字节的操作码后,再输出若干字节的数据,在输出数据的同时通过SDI引脚的按位移入接收数据。CH395读写流程操作如图3所示。
图3 CH395的操作流程
要完成SPI接口向CH395传送数据,先要对dsPIC33E的SPI端口进行配置,本次设计中采用的是SPI1模块,先设置SPI1所使用的所有引脚为数字输入/输出功能,设置SCS、SDI、SCK引脚为输出,SDO为输入,因为CH395需要一个INT#用于中断处理连接控制器,所以采用一个通用I/O引脚作为INT#中断输入引脚。当SPI引脚映射好后,即可配置SPI口的功能参数,方法如下:
IFS0bits.SPI1IF=0; //清除SPI中断标志
IEC0bits.SPI1IE = 0; //禁止SPI中断
SPI1STATbits.SPIEN = 0x0;//禁止SPI使能SPI模块,//仅当前这一次操作
SPI1CON1bits.DISSCK = 0;//SCK 使能
SPI1CON1bits.DISSDO = 0;//SDO 使能
SPI1CON1bits.MODE16 = 0;//8位数据模式
SPI1CON1bits.SMP = 0;//数据采样输设置
SPI1CON1bits.CKE = 0;//0
SPI1CON1bits.SSEN = 0;//从模式禁止
SPI1CON1bits.CKP = 1;//高电平有效,低电平空闲
SPI1CON1bits.MSTEN = 1;//主模式使能
SPI1STATbits.SPIEN = 1 ;//SPI模块使能
3.1驱动程序的层次结构
要实现CH395的Socket功能,需要繁多的功能函数来完成,从程序开发者的角度来看,CH395的网络Socket程序可分为4个层次:应用层、中间层、数据命令层、SPI层。
(1) 应用层
对中间层提供的关于网络参数设置操作进行封装,向网络透明地传送数据,用于设置网络协议,创建Socket,连接目标进程,处理网络收发中断等。该层主要由以下函数来实现:
CH395Init();//CH395硬件初始化,设置网卡IP地址、网关、子网掩码等
CH395SocketInitOpen ();//Socket初始化,设置Socket的目的//端口、源端口、协议类型(TCP/UDP等)、打开Socket、TCP连接等
CH395GlobalInterrupt();//处理CH395的网络中断、接收中断、//发送中断等
(2) 中间层
该层使用命令数据层的接口,实现网络的具体操作,向上层提供网络参数设置功能。应用层的三个主要函数通过调用下列各函数来实现:
CH395CMDCheckExist();//硬件测试函数
CH395CMDSetIPAddr(); //设置CH395的IP地址
CH395CMDSetGWIPAddr();//设置网关地址
CH395CMDSetMASKAddr();//设置子网掩码,默认为255.255.255.0
CH395CMDInitCH395();//初始化CH395芯片
CH395SetSocketDesIP();//设置Socket 目标IP地址
CH395SetSocketProtType();//设置Socket 协议类型
CH395SetSocketDesPort();//设置Socket 目的端口
CH395SetSocketSourPort();//设置Socket 源端口
CH395OpenSocket();//打开Socket
StopIfError();//检查是否成功
CH395TCPConnect();//TCP连接
CH395CMDGetGlobIntStatus()
CH395GetSocketInt();//获取socket的中断状态
CH395GetRecvLength();//获取当前缓冲区内数据长度
CH395GetRecvData();//读取数据
CH395SendData();//发送数据
(3) 命令数据层
该层通过调用SPI硬件层接口,向CH395发送命令与数据,同时接收CH395返回的数据。主要函数如下:
xWriteCH395Cmd();//向CH395写命令操作
xWriteCH395Data();//向CH395写数据
xReadCH395Data();//读取CH395发送过来的数据
(4) SPI层
它是整个程序框架的最底层,向上层提供读写时序控制,实现SPI读取数据,主要由以下函数实现:
xEndCH395Cmd();//CS高电平无效信号
xCH395CmdStart( );//CS低电平操作有效信号
Spi395Exchange();//向发送SPI1字节的数据,//并接收1字节数据
CH395驱动程序的层次结构图略——编者注。
3.2SPI程序的实现
要实现CH395的网络功能,单片机dsPIC33E的SPI数据传送是最为关键的,SPI数据传送接口由以下3个函数来实现:
① #define xCH395CmdStart(){CH395_SPI_SCS = 0;} /*命令开始*/
② #define xEndCH395Cmd() {CH395_SPI_SCS = 1;} /*命令结束*/
为了加快程序运行,上面两个函数采用宏定义实现。
③ SPI读写数据函数Spi395Exchange()实现如下:
UINT8 Spi395Exchange(UINT8 d){
unsigned int temp;
temp = SPI1BUF;//读取SPIBUF中的已有数据
SPI1BUF = d; //向SPIBUF写数据
while(!SPI1STATbits.SPIRBF){//等待数据传送完毕
Nop();
}
emp = SPI1BUF;//读取数据
mDelayuS(10);//延时
return temp;
}
设计中,dsPIC33E单片机作为TCP客户端来使用,其控制程序在单片机的TCP客户端应用中,应用程序直接使用CH395提供的Socket与网络设备进行网络通信。在连接网络之前,其与PC系统上的过程一致,需要初始化网卡设备(CH395),配置相应的网络参数(如本地IP、网关、子网掩码等);在应用程序启动Socket进程之前,需要配置Socket的目的IP、端口、源端口、协议类型。配置好以上参数后,应用程序可以创建Socket连接服务器进程。CH395的网络控制处理流程如图4所示。
图4 CH395网络控制流程图
4服务器端程序实现
在设计中,用PC机作为服务器端。PC的服务器程序采用MFC来设计,PC与单片机之间通过Socket进行网络连接。
在PC端上运行TCP服务器程序,使用Socket之前应该在应用程序的InitInstance()函数中通过WSAStartup()API函数加载Windows Sockets套接字。在程序界面上通过一个启动按键来启动服务器的监听程序并创建一个消息线程向MFC的消息路由传递用户自定义消息,并在MFC的消息宏中增加用户自定义消息处理函数ON_MESSAGE(WM_RECVDATA,& CH395Dlg::OnRecvData)。接收线程函数如下:
DWORD WINAPI CH395Dlg:: RecvProc(LPVOID lpParameter){
OCKADDR addrFrom = ((RECVPARAM*)lpParameter)->clientAddr;
SOCKET sock = ((RECVPARAM*)lpParameter)->sock;
HWND hwnd = ((RECVPARAM*)lpParameter)->hwnd;
delete lpParameter;
int len = sizeof(SOCKADDR);
char recvBuf[512];
char tempBuf[512];
memset(recvBuf,0,512);
int retval;
int count = 0;
while (TRUE){
retval = recv(sock,recvBuf,512,0);
if (strlen(recvBuf) == 0)
continue;
if (SOCKET_ERROR == retval)//
break;
char *pIP = addrFrom.sa_data;
int port = (unsigned char )pIP[0];
port = port<<8;
port += (unsigned char)pIP[1];
sprintf(tempBuf,"%d.%d.%d.%d:%d :%s",(unsigned char )pIP[2],(unsigned char)pIP[3],
(unsigned char )pIP[4],(unsigned char )pIP[5],port,recvBuf);
::PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);
}
return 0;
}
5系统测试与总结
通过网线把单片机电路板与PC机相连,在PC机上运行TCP协议的服务器端程序,在单片机程序中,设置CH395芯片本地IP地址为192.168.0.5,本地端口号为6000,目的IP地址为192.168.0.10,目的端口号为5000。程序接收来自服务器端的信息字符串,并把字符串原样返回PC端。程序运行状态如图5所示。通过测试表明CH395模块成功连接上PC机。
图5 系统测试图
本次设计利用Microchip dsPIC33系列处理器与CH395模块芯片,实现了基于单片机系统的SPI口的以太网模块设计。该方案实现简单、成本低、实用性强,特别是在控制器缺少以太网协议栈硬件支持的情况下,可以节省开发周期,提高开发效率。