2015-01-24 20:50:47 Pnoter 阅读数 8243
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

    6010 人正在学习 去看看 朱有鹏

一 · 在windows操作系统下,实现Java串口通信,需要用到sun提供的串口通信包,javacomm【可以去Java站点去下载最新版本的压缩包,然后解压】。

解压之后要用到压缩包中的三个文件,将这三个文件按照如下方式配置【一下路径为相对路径,视个人电脑Java安装路径不同而异】:

1、comm.jar放置到 JAVA_HOME/jre/lib/ext;【jre/lib(也就是在JAVA文件夹下的jre)】

2、win32com.dll放置到 JAVA_HOME/bin;

3、javax.comm.properties两个地方都要放;

二 · 在这里我们首先了解一下Java串口通信API说明【CommunicationAPI(包含于javax.comm包中)】,这样有利进一步了解Java串口通信包,ava提供了 CommunicationAPI(包含于javax.comm包中)用于通过与机器无关的方式,控制各种外部设备。Communications API,是标准的Java的扩展部分,它在JavaAPI中是没有附带的。

三 · Communications API 简介:

Communications API 的核心是抽象的CommPort类及其两个子类:SerialPort类和ParallePort类。其中,SerialPort类是用于串口通信的类,ParallePort类是用于并行口通信的类。CommPort类还提供了常规的通信模式和方法,例如:getInputStream( )方法和getOutputStream( )方法,专用于与端口上的设备进行通信。然而,这些类的构造方法都被有意的设置为非公有的(non-public)。所以,不能直接构造对象,而是先通过静态的CommPortIdentifer.getPortIdentifiers()获得端口列表;再从这个端口列表中选择所需要的端口,并调用CommPortIdentifer对象的Open( )方法,这样,就能得到一个CommPort对象。当然,还要将这个CommPort对象的类型转换为某个非抽象的子类,表明是特定的通讯设备。该子类可以是SerialPort类和ParallePort类中的一个。下面将分别对CommPort类,CommPortIdentifier类,串口类SerialPort进行详细的介绍。 

四 · CommPortIdentifier类 :

addPortName(String, int, CommDriver) 添加端口名到端口列表里 
addPortOwnershipListener(CommPortOwnershipListener) 添加端口拥有的监听器 
removePortOwnershipListener(CommPortOwnershipListener) 移除端口拥有的监听器 
getCurrentOwner() 得到当前占有端口的对象或应用程序 
getName() 得到端口名称 
getPortIdentifier(CommPort) 得到参数打开的端口的CommPortIdentifier类型对象 
getPortIdentifier(String) 得到以参数命名的端口的CommPortIdentifier类型对象 
getPortIdentifiers() 得到系统中的端口列表 
getPortType() 得到端口的类型 
isCurrentlyOwned() 判断当前端口是否被占用 
open(FileDescriptor) 用文件描述的类型打开端口 
open(String, int) 打开端口,两个参数:程序名称,延迟时间(毫秒数) 

五 · SerialPort类:

DATABITS_5 数据位为5 STOPBITS_2 停止位为2 PARITY_ODD 奇检验 
DATABITS_6 数据位为6 STOPBITS_1 停止位为1 PARITY_MARK 标记检验 
DATABITS_7 数据位为7 STOPBITS_1_5 停止为1.5 PARITY_NONE 空格检验 
DATABITS_8 数据位为8 PARITY_EVEN 偶检验 PARITY_SPACE 无检验 
SerialPort对象的关于串口参数的函数 
getBaudRate() 得到波特率 getParity() 得到检验类型 
getDataBits() 得到数据位数 getStopBits() 得到停止位数 
setSerialPortParams(int, int, int, int) 设置串口参数依次为(波特率,数据位,停止位,奇偶检验) 
SerialPort关于事件的静态成员变量 
BI Break interrupt中断 FE Framing error错误 
CD Carrier detect载波侦听 OE Overrun error错误 
CTS Clear to send清除以传送 PE Parity error奇偶检验错误 
DSR Data set ready数据备妥 RI Ring indicator响铃侦测 
DATA_AVAILABLE 串口中的可用数据 OUTPUT_BUFFER_EMPTY 输出缓冲区空 
SerialPort中关于事件的方法 
isCD() 是否有载波 isCTS() 是否清除以传送 isDSR() 数据是否备妥 
isDTR() 是否数据端备妥 isRI() 是否响铃侦测 isRTS()   是否要求传送 
addEventListener(SerialPortEventListener)    向SerialPort对象中添加串口事件监听器 
removeEventListener() 移除SerialPort对象中的串口事件监听器 
notifyOnBreakInterrupt(boolean) 设置中断事件true有效,false无效 
notifyOnCarrierDetect(boolean) 设置载波监听事件true有效,false无效 
notifyOnCTS(boolean) 设置清除发送事件true有效,false无效 
notifyOnDataAvailable(boolean) 设置串口有数据的事件true有效,false无效 
notifyOnDSR(boolean) 设置数据备妥事件true有效,false无效 
notifyOnFramingError(boolean) 设置发生错误事件true有效,false无效 
notifyOnOutputEmpty(boolean) 设置发送缓冲区为空事件true有效,false无效 
notifyOnParityError(boolean) 设置发生奇偶检验错误事件true有效,false无效 
notifyOnRingIndicator(boolean) 设置响铃侦测事件true有效,false无效 
getEventType() 得到发生的事件类型返回值为int型 
sendBreak(int) 设置中断过程的时间,参数为毫秒值 
setRTS(boolean) 设置或清除RTS位 
setDTR(boolean) 设置或清除DTR位 
close() 关闭串口 
getOutputStream() 得到OutputStream类型的输出流 
getInputStream() 得到InputStream类型的输入流

六 · 看一段用Java写的一个往串口写(Write)字符串的完整程序【如果你的电脑没有那么多的commport可以去网上搜索一个虚拟端口的软件,在你的电脑上开几个虚拟的commport,推荐软件Virtual Serial Port Driver ,个人表示很好用,可惜是收费的,不过可以试用,你懂!】

如下为往我的电脑com1口发送字符串的实例【已经经过测试,完整无误!】

//添加类包
import javax.comm.*;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;

//创建一个类JavaSerialPort,通过关键词implements实声明自己使用Runnable和SerialPortEventListener这两个接口
public class myComPortServlet  {
	// 定义、声明变量
	private String appName = "Java串口通信";
	private int timeout = 2000;// 定义一个打开端口的最大等待时间
	public static String PortName;
	private CommPortIdentifier commPort;
	private SerialPort serialPort;
	private OutputStream outputStream;
	public static OutputStream out;
	public static String messageString = "点亮单片机上的第**个LED灯!";// 给选定端口发送的字符

	// listPort()方法的定义
	public void listPort() {
		CommPortIdentifier cpid;
		Enumeration en = CommPortIdentifier.getPortIdentifiers();
		//System.out.println("端口信息 :" + en);
		//System.out.println("端口列表如下:");
		while (en.hasMoreElements()) {
			cpid = (CommPortIdentifier) en.nextElement();
			if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				//System.out.println(cpid.getName() + ", "
						//+ cpid.getCurrentOwner());
			}
		}
	}

	// selectPort(String portName)方法的定义
	public void selectPort(String portName) {
		this.commPort = null;
		CommPortIdentifier cpid;
		Enumeration en = CommPortIdentifier.getPortIdentifiers();
		while (en.hasMoreElements()) {
			cpid = (CommPortIdentifier) en.nextElement();
			if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				if (cpid.getName().equals(portName)) {
					this.commPort = cpid;
				}
			}
		}
		openPort();
	}

	// openPort()方法的定义
	private void openPort() {
		try {
			serialPort = (SerialPort) commPort.open(appName, timeout);
		} catch (PortInUseException e) {
		}
		try {
			out = serialPort.getOutputStream();
		} catch (IOException e) {
		}
		try {
			serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
					SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
		} catch (UnsupportedCommOperationException e) {
		}
		try {
			out.write(messageString.getBytes());
		} catch (IOException e) {
		}
	}

	// checkPort()方法的定义
	private void checkPort() {
		if (commPort == null)
			throw new RuntimeException(
					"端口选择出错请用selectPort(String portName)方法选择正确的端口!");

		if (serialPort == null) {
			throw new RuntimeException("SerialPort 对象无效!");
		}
	}

	// write(String message)方法定义
	public void write(String message) {
		checkPort();

		try {
			outputStream = new BufferedOutputStream(
					serialPort.getOutputStream());
		} catch (IOException e) {
			throw new RuntimeException("获取端口的OutputStream出错:" + e.getMessage());
		}

		try {
			outputStream.write(message.getBytes());
			//log("信息发送成功!");
		} catch (IOException e) {
			throw new RuntimeException("向端口发送信息时出错:" + e.getMessage());
		} finally {
			try {
				outputStream.close();
			} catch (Exception e) {
			}
		}
	}

	// close()方法的定义
	public void close() {
		serialPort.close();
		serialPort = null;
		commPort = null;
	}

	// 类的主方法,Java程序运行首先是从这里开始的
	public static void main(String[] args) {
		myComPortServlet sp = new myComPortServlet();
		sp.listPort();
		sp.selectPort("COM1");// 选择COM1口交换数据数据

	}
}
七 · 我们再看一段用Java写的一个往串口写(Read)字符串的部分代码【只是在上面的程序中添加了几个方法,read()方法和serialEvent()端口监听方法以及run()计时方法】。
如下为读取我的电脑com2口字符串的实例【已经经过测试,完整无误!】
注意事项:关串口很重要,不然会出现运行错误【如下为close定义:
public void close() {
		serialPort.close();
		serialPort = null;
		commPort = null;
】。
	// 接收函数的定义
	public void startRead(int time) {
		try {
			inputStream = new BufferedInputStream(serialPort.getInputStream());
		} catch (IOException e) {
			throw new RuntimeException("获取端口的InputStream出错:" + e.getMessage());
		}
		try {
			serialPort.addEventListener(this);
		} catch (TooManyListenersException e) {
			throw new RuntimeException(e.getMessage());
		}
		serialPort.notifyOnDataAvailable(true);
		if (time > 0) {
			this.threadTime = time * 1000;
			Thread t = new Thread(this);
			t.start();
		}
	}

	// 端口事件监听函数定义
	public void serialEvent(SerialPortEvent arg0) {
		String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
		String dbURL = "jdbc:sqlserver://localhost:1433; DatabaseName=mytest";
		String userName = "sa"; // 默认用户名
		String userPwd = "9406"; // 密码
		Connection dbConn;
		switch (arg0.getEventType()) {
		case SerialPortEvent.BI:
		case SerialPortEvent.OE:
		case SerialPortEvent.FE:
		case SerialPortEvent.PE:
		case SerialPortEvent.CD:
		case SerialPortEvent.CTS:
		case SerialPortEvent.DSR:
		case SerialPortEvent.RI:
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			break;
		case SerialPortEvent.DATA_AVAILABLE:
			byte[] readBuffer = new byte[1024];
			try {
				while (inputStream.available() > 0) {
					inputStream.read(readBuffer);
					tempreture += new String(readBuffer).trim();
				}
				System.out.println(tempreture);
			} catch (IOException e) {
			}
			try {
				Class.forName(driverName);
				dbConn = DriverManager.getConnection(dbURL, userName, userPwd);
				if (!dbConn.isClosed()) {
					// pw.println("_>Succeed to connecting SQL!" + "
" + "
"); } Statement statement1 = dbConn.createStatement(); @SuppressWarnings("unused") Statement statement2 = dbConn.createStatement(); String sql1 = "INSERT INTO tempreture(tempreture) VALUES('" + tempreture + "')"; @SuppressWarnings("unused") int rs1 = statement1.executeUpdate(sql1); } catch (Exception e) { e.printStackTrace(); } } } // 计时函数 public void run() { try { Thread.sleep(threadTime); serialPort.close();// 关闭接收函数,端口关闭 } catch (Exception e) { e.printStackTrace(); } }




2019-07-28 10:55:53 kennychow 阅读数 187
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

    6010 人正在学习 去看看 朱有鹏

在传统观念里, 单片机WIFI通信,尤其是单片机高速WIFI通信传输,是一个不现实的梦想,原因一般在于:
(1) 能做高速通信的WIFI模块,一般只是USBSDIO、或PCIE之类的接口而大多数普通单片机都不会提供这些主机接口;
(2)即使某些款的单片机也能提供这些接口,也会单片机和WIFI模块的通信编程变得非常复杂,同时也会造成单片机选型的成本增加;
(3)普通单片机因为资源(性能和存储)有限,也难以支持实用的较好性能的单片机WIFI方案。

所以,大多数用在单片机系统上的WIFI模块,要么是以串口为主的,只能做一些基本的低速控制命令的传输,或者费了九牛二虎之力,集成了USB、 SDIO、或PCIE接口的WIFI模块,其做出来的效果也很一般,速度一般也不超过300K字节每秒。因此,在普通单片机做高速WIFI通信,基本上成为了一个不可实现的梦想,并甚至成为一种传统惯性认识。

其实,选择市面上某款基于SPI接口的高速WIFI模块,占用单片机资源少,在许多常见的单片机上都实现了高速传输,普通环境下,实测有效速度可以超过兆字节每秒,长时间运行不掉线、不丢包,在单片机音视频传输、高速采集数据传输场合都得到了超过了一年以上的产品化的验证。该方案功能强大、性能高、稳定,集成移植简单、适应面广,精致小巧。性价比超高。

该方案提供支持多款常见单片机的验证测试好的例程包,包括但不限于ST系列、Nuvoton系列、NXP系列、Freescale K60系列、TI MSP430系列、Holtek HT32系列。还有技术支持,所以淘宝上的口碑评价不错! 

更多详情可去某宝搜索”SPI 高速 多链接" 了解详情。
 

 

  

ALK8266高速WIFI模组 SPI接口 带WEB网页 音视频传输产品化验证

(在某宝搜索 “SPI 高速 多链接”  里面有更进一步的详细介绍,很技术化)

1. 有效通信速度快,效率高,通信实时性好
   1.1 “实测的”“有效”吞吐速度高,超过M字节每秒(MBytes/s),所以可用来传输音视频或大量数据
   1.2 准实时性收发,连续发包之间的间隔可在ms级或us级,可适应一些对传输性实时性有要求的场合。
2. 稳定可靠不丢包,长时间通信不掉线
   2.1 测试条件:普通办公室环境实测
   2.2 测试速度:速度稳定在兆字节每秒(MBytes/s)以上
   2.3 长时间运行测试:持续运行30天,不掉线、传输不停止/不卡死
   2.4 丢包测试:TCP通信持续测试过5小时以上,不丢包、不多包,不丢字节、不多多字节,收发方数据完全一样。
       测试场景包括:(1)TCPUDP抓包软件单纯发送, 模块单纯接收
                               (2)TCPUDP抓包软件单纯接收, 模块单纯发送
                               (3)TCPUDP抓包软件同时发收, 模块同时收发
                               (4)两个WIFI模块之间互相对发对收
3. 功能全面,使用灵活,使用场合适应面广
   3.1 灵活实用的无线通信
       3.1.1 模块支持 工作站STA, 热点AP 以及 STA+AP 混合模式,无论是否存在第三方热点,都可实现通信
       3.1.2 支持UDP,TCP客户端,TCP服务器,灵活方便
       3.1.3 UDP通信支持广播、组播以及单播,灵活高效
       3.1.4 支持多链接,每个链接独立随意配置,实用的多通道高速通信
       3.1.5 支持多客户端,模组作为TCP服务器可以同时和多个客户端通信
       3.1.6 支持大块数据阵发发送,适合一些文件等大块数据传输需求的场合
   3.2 内嵌WEB服务器,操作更灵活和便捷
       3.2.1 无需安装APP,直接通过常见的浏览器操作
       3.2.2 普通智能手机或电脑均可直接操作
       3.2.3 支持“自动弹出网页”功能
       3.2.4 可通过网页直接配网、配置热点、建立和查询链接通道信息等操作
   3.3 配网方式灵活多样方便
       3.3.1 WEB网页配网:勿需安装APP,直接输入,简单方便,受限条件少
       3.3.2 智能配网:SmartConfig/SmartLink, 微信Airkiss扫一扫;
             -特色 提供配网进展灯闪烁样式,提高智能配网操作方便性。
       3.3.3 直接配网:串口AT指令、SPI接口API函数 直接输入
   3.4 内嵌RSA加密和签名算法
       3.4.1 可作为加密芯片使用,支持单片机固件的防复制
   3.5 支持有意义的低功耗
       3.5.1 确保有用发射功耗足够强传输距离足够远的前提下的低功耗机制
       3.5.2 支持休眠,自动唤醒或手动唤醒
       3.5.3 深度休眠电流低于1mA
 4. 封装灵活、尺寸小巧
 
 4.1 整孔和半孔(邮票孔)一体化设计
   4.2 兼容插件方式或贴片装配方式
   4.3 大小和一枚1角硬币相当
5. 常见通用的SPI单片机主机接口,集成简单,占用单片机资源少
 
 5.1 主机接口为标准的SPI从,适配绝大多数的常见单片机,单片机选型范围广
       - 硬件接线简单,只需要标准的SPI总线管脚,无需额外的UART串口
       - 按照普通的SPI从机方式对模块进行读写,实现单片机与模块的配置查询以及传输通信
   5.2 模块上已集成了TCPIP协议栈,
       - 单片机无需再集成相关协议栈或操作系统,
       - 因此集成简单,占用单片机资源少,且通信效率也更高
6. 提供单片机例程包、集成说明等资料文档,和技术支持
   6.1 单片机例程包都经过实际测试和验证
   6.2 集成说明文档和开发使用技巧,都是基于开发经验和客户反馈总结编写,非简单的协议复制
   6.3 可提供目前市场上主流的单片机例程包,包括但不限于:
       - STM32系列(如F1/F2/F3/F4/F7 H7 L1/L4)
       - NXP LPC17xx系列
       - NXP K60, K27/28系列、
       - NXP i.MX RT10xx系列、
       - C8051系列、
       - Nuvoton新塘 NUC123xx系列、M45x系列、
       - TI MSP430系列
       - TI C2000/C5000(如TMS320F28335)
       - 等
在某宝搜索 “SPI 高速 多链接”  排名靠前的就是,里面有更进一步的详细介绍,很技术化。

 

2009-10-14 18:57:00 paopao0930 阅读数 614
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

    6010 人正在学习 去看看 朱有鹏

      我的毕业设计的题目是:VC编程实现与usb设备通信:计算机向单片机发送~!真的不知道该从哪里下手~闹心了~!希望哪个高人能给小女指点一下~!小女不胜感激~!!

      主要实现的功能:1利用vc变成实现上位机的界面;

                              2通过usb接口实现单片机与计算机的通信;

                              3通过usb接口将程序下载到单片机中并实现单片机对蜂鸣和液晶现实的控制

                              4当有数据传输时,发光二极管不段闪烁

    

 

 

 

      量化的技术指标:1 有完整的上位机操作界面,提供相应的按键操作

                             2 当计算机通过usb接口向单片机发送数据成功后蜂鸣器发出警示音

                             3 通过液晶显示器所传输的内容和当有数据传输时,发光二极管不断闪烁

        

 

      小女子跪谢啦~!实在是头疼~!!

2019-09-21 10:51:42 weixin_45658464 阅读数 35
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

    6010 人正在学习 去看看 朱有鹏

在我们设计的早年色选机系统中,单片机程序结构采用固定周期循环的程序结构。固定循环周期是200us。
在这固定的200us时间内处理单片机外围的各种任务,这些任务可以处理按键输入、LED显示、输入/输出、AD采样、脉冲计数、异步通信UART、EEPROM读写等;

在这200us的循环中,实现各种中断任务处理,这个比硬件中断的方式对实时性有更好的管控。例如PIC系列单片机利用UART接口和PC机的232接口通信,就有时间限制。对于57600的通信波特率,就要求200us内中断取数一次,否则就要丢数了!丢数了就出错了!理论上,串口通信的收发数据缓冲区如果大于2字节,那么就可以间隔更长时间去取数或发数。但是,如果一个任务的处理时间超过最大时间还是会丢数!

对于最基本的PIC系列单片机,最大时钟是20MHz,指令周期是200ns,对于200us的固定周期,一个周期内最多执行1000条指令。1000条指令可做很多事情了。进一步,当单片机的程序存储器的空间大于1k时,可以编写大于1k的程序,在不同的周期执行不同的程序。

循环周期的200us定时由单片机的硬件定时器来控制,每次循环的最后等待200us周期的结束,等待的过程就是查询硬件定时器,结束后再返回程序最上部。

可以在200us的循环周期里查询各种中断,查询是否有中断标志是“1”,是就处理相应的事件。或者在一段固定空操作代码中开通中断使能,让程序进入中断子程序执行一些中断处理。
(待续)

2006-06-10 09:13:00 wuxi_liujq 阅读数 6500
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

    6010 人正在学习 去看看 朱有鹏
基于单片机的USB接口的设计
 The Design Of USB Interface Based On The Single Chip
刘井权  王宪  戴旻 来源:自动化仪表杂志
(江南大学通信与控制工程学院 江苏无锡 214122)
摘要提出了一种基于单片机的智能仪表扩展USB接口的方法。介绍了USB接口芯片SL811HS[1]的结构和性能以及USB接口的硬件电路图,详细分析了USB接口的驱动程序的设计方法及FAT16[2]文件系统的结构。利用SCSI[3]传输命令集,通过BULK-IN和BULK-OUT[3]端点实现了主机与U盘设备之间的数据通讯。实验和应用结果表明,该方案具有控制方便、传输速度快、存储数据稳定可靠等优点。
关键词 USB  端点  枚举  配置  文件系统
Abstract A method of intelligent instrument expandation USB interface based on singlechip is stated. The structure and capability of the USB interface chip SL811HS, hardware circuit diagram of the USB interface is introduced, and the design method of the USB interface drivers,structure of the FAT16 file systems is analysed detailedly。The communication between the host computer and U disk device is completed via BULK-IN and BULK-OUT endpoint using SCSI command class. The results of experiment and application show that the scheme features convenient control、fast transmitting data and reliably storaging data.
Key words USB  Endpoint  Enumerate  File system
中图分类号:TP368 文献标识码:B
引言
USB (Universal Serial Bus)是近年来发展起来的一种快速、灵活的总线接口。它最大的特点是易于使用,可热插拔,接口连接灵活,并且能够提供外设电源[4],在嵌入式系统及智能仪表中获得广泛的应用。而51系列单片机以其优越的性能、成熟的技术、高性价比被广泛应用于测控仪器和自动化领域。因此用51系列单片机实现USB主机接口,进而实现对USB外设的控制,对提高整个系统的数据存储、数据传输、设备控制等性能都有很大的作用。本文论述的方案基于压强测试仪的应用环境,在开发压强测试仪的过程中根据实际的需求,要求能够存储大量数据,以往的解决方案是RS232C接口,但由于传输速度慢,在高速采样系统容易导致数据丢失,而且必须有上位机的参与,这对于室外作业很不方便。USB接口克服了上述缺点,测试仪器可以把采集到的数据保存到U盘,工作人员可以随时取下U盘,将数据拿到异地进行分析,另外笔者还开发了读写U盘的文件系统,存储的数据可以直接在PC机上打开,不需要编写其它的分析软件。本设计的另一优点是成本低廉,可应用于各种智能仪器及嵌入式设备中,实现对海量数据的存储。
1 硬件设计
1.1 SL811HS概述
SL811HSCypress公司推出的具有主/从两种工作模式的USB控制器[4],遵循USB1.1规范;可自动检测总线速率,支持全速12Mbps和低速1.5Mbps设备;具有8位双向的数据总线,易与单片机连接;片内256字节的SRAM(其中16字节用于工作寄存器),用于数据传输;可自动产生SOF
CRC5/16,简化软件工作量;片内具有根Hub;支持挂起/唤醒工作模式,减少功耗;支持地址自动加1功能,在连续读写过程中,只需设置一次地址,其内部寄存器地址自动增加,这在大容量数据的通讯中是非常必要的。
1.2单片机与SL811HS接口设计
  图一所示是AT89C51与SL811HS的硬件连接电路。在本设计中,由于所选用的单片机AT89C51及其外围元件的工作电压为5V,而SL811HS的工作电压为3.3V,所以系统应提供5V电压同时要进行电压转换。虽然SL811HS可以使用12MHz晶振,但在实际使用过程中,如果晶振质量不太好,电路稳定性就会比较差,因此,设计时推荐使用48MHz有源晶振。SL811HS的中断请求输出的是高电平,因此需要用反向器把它变换成低电平以满足AT89C51中断输入要求。此外,应注意SL811HS是低电平复位。为了便于调试,系统扩展了液晶显示器。硬件完成后要进行测试,先向SL811HS寄存器中写入数据,之后读出数据并在液晶显示器上显示,如果和写入的数据相同,说明SL811HS与单片机连接正确。再用示波器观察有源晶振是否起振,一切正常后便进入软件调试。   
2 软件设计
. USB枚举过程:
   USB总线一般包含四种基本数据传输类型:控制传输、中断传输、批传输以及同步传输,本系统使用的是控制传输和批量传输2。最基本的函数就是对SL811HS寄存器的读写,代码4如下:
xdata BYTE SL811H_ADDR            _at_   0x9800;
xdata BYTE SL811H_DATA               _at_   0x9801;
 
读单个寄存器:
BYTE SL811Read(BYTE a)
{SL811H_ADDR=a;
return (SL811H_DATA);
}
写单个寄存器:
void SL811Write(BYTE a, BYTE d)
{ SL811H_ADDR=a;
SL811H_DATA= d;
}
连续读SL811HS寄存器:
void SL811BufRead(BYTE addr, BYTE *s, BYTE c)
{
BYTE idata i;
   i=c;
   SL811H_ADDR = addr;
    while (i--)
   *s++ =SL811H_DATA;
}
连续写SL811HS寄存器:
void SL811BufWrite(BYTE addr, BYTE *s, BYTE c)
{
BYTE idata i;
   i=c;
   SL811H_ADDR= addr;
    while (i--)
   SL811H_DATA = *s++;
}
读写寄存器正常以后,便进入SL811HS底层函数6的编写,具体的函数可参照CYPRESS公司提供的代码修改。调试之后便进入枚举过程。枚举是对USB接口正确配置的过程,包括获取设备描述符、配置描述符、接口描述符、端点描述符,以及对接口、端点的正确配置。
请求设备描述符的setup数据包为 80h 06 00 01 00 00 12 00 ,通过读取设备描述符,可获得设备的子类。请求配置描述符的setup数据包为 80h 06 00 01 00 00 09 00 ,对于请求配置描述符,可以先进行首次请求,要求数据包长为9。接收到设备返回的数据,获得此描述符的总长,然后再发二次请求,获得全部描述符数据。第二次发送的setup数据包内容为 80h 06 00 01 00 00 2Eh 00
此时返回的数据包括了设备配置、接口、端点的全部描述信息2
在对U盘读写之前需正确配置SL811HS芯片,这部分工作通过枚举来完成。枚举过程代码6如下:
uDev.wPayLoad[0] = 64;  // 地址0、端点0的有效负荷为64字节
if(usbaddr =2)          // 设定USB的地址为2
USBReset();           // 芯片SL811HS复位
pDev =(pDevDesc)bBUF;
if (!GetDesc(uAddr,DEVICE,0,18,bBUF)) // 通过地址0获取描述符
return FALSE;
uDev.wPayLoad[0]=pDev->bMaxPacketSize0;
if (!SetAddress(usbaddr))   //设定USB地址值,此处为2
return FALSE;
uAddr = usbaddr;         //传输采用新地址
if (!GetDesc(uAddr,DEVICE,0,(pDev->bLength),bBUF)) //用新地址获取设备描述符
return FALSE;
pCfg = (pCfgDesc)bBUF;
if (!GetDesc(uAddr,CONFIGURATION,0,8,bBUF))    //获取配置描述符
return FALSE;
if (!GetDesc(uAddr,CONFIGURATION,0,WordSwap(pCfg->wLength),bBUF))//第二次请求配置描述符 
return FALSE;
pCfg = (pCfgDesc)bBUF;
pIfc = (pIntfDesc)(bBUF + 9);  // 分离出接口信息
uDev.bClass = pIfc->iClass;
uDev.bNumOfEPs = (pIfc->bEndPoints<=MAX_EP) ? pIfc->bEndPoints : MAX_EP;
if (!SetConfiguration(uAddr,DEVICE)) //设定USB的配置信息
return FALSE;
. U盘的读写及FAT16文件系统5
U盘属于大容量存储设备,当U盘枚举成功后,主机与USB设备通过BULK-ONLY传
输方式传输数据,所有的通讯数据都通过BULK-IN和BULK-OUT端点传输。在这种传输方式下,有三种类型的数据在USB和设备之间传送,CBW、CSW 和普通数据5CBW(Command Block Wrapper,即命令块包)是从USB Host 发送到设备的命令,命令格式遵从接口中bInterfaceSubClass 所指定的命令块,这里为SCSI 传输命令集。USB设备需要将SCSI 命令从CBW 中提取出来,执行相应的命令,完成以后,向Host 发出反映当前命令执行状态的CSW(Command Status Wrapper),Host 根据CSW 来决定是否继续发送下一个CBW 或是数据。Host 要求USB 设备执行的命令可能为发送数据,则此时需要将特定数据传送出去,完毕后发出CSW,以使Host 进行下一步的操作。
U盘一般支持FAT16文件系统,它可以分成4个部分,即保留区,FAT区,根目录区,数据区。保留区的第一个扇区也叫引导扇区,它含有对文件系统识别的关键信息。根目录区存放目录项,每个目录项为32 个字节,记录一个文件或目录的信息。FAT区即文件分配表,操作系统分配磁盘空间按簇来分配的。同一个文件的数据并不一定完整地存放在磁盘的一个连续的区域内,而往往会分成若干段,像一条链子一样存放。这种存储方式称为文件的链式存储。为了实现文件的链式存储,硬盘上必须准确地记录哪些簇已经被文件占用,还必须为每个已经占用的簇指明存储后继内容的下一个簇的簇号,对一个文件的最后一簇,则要指明本簇无后继簇。这些都是由FAT 表来保存的,FAT 表的对应表项中记录着它所代表的簇的有关信息:诸如是否空,是否是坏簇,是否已经是某个文件的尾簇等。目录项的所占的最后一个扇区之后,便是真正存放文件数据的位置了。只有清楚的了解FAT16的结构才能对文件正确读写,图4给出写文件的流程图,读文件相对比较简单,在此不再赘述。
 
3 结束语
本系统已经成功实现了对U盘的读写,经过多次试验测试,读写数据稳定可靠,可兼容市场上绝大多数U盘。作者在单片机系统中可以建立、删除、修改文件,可以把数据存储成多种文件格式,特别指出的是,在单片机系统内建立的BIN文件和HEX文件可以在PC机上利用UltraEdit-32软件直接打开,这为数据的分析提供了很大的方便,而不必花费精力去编写分析软件。
 
参考文献
1 Cypress semiconductor corporation. SL811HS date sheet[z].2002.
2 许永和. 8051单片机USB接口程序设计.北京:北京航空航天大学出版社.2004.
3 史波,田凯.通用串行总线USB技术概述[J].信息技术.2001(4).
4 马忠梅,籍顺心,张凯,等.单片机的C语言应用程序设计.北京:北京航空航天大学出版社.2003.
5 Microsoft corporation. FAT File System Specification[z].1999.
6  徐爱均,彭秀华.单片机高级语言C51应用程序设计.北京:电子工业出版社.1995.
7 Compaq,Inter,Microsoft,NEC. Universal Serial Bus Specification(Revision1.1).1998.
                     
没有更多推荐了,返回首页