嵌入式usb主机设计(硬件设计和软件设计)
嵌入式usb主机硬件设计
选用廉价的51系列单片机(89c52)控制usb主机接口芯片sl811hs,同时,通过max232芯片与pc机通信。硬件接线示意图如下所示:
嵌入式usb主机软件设计
下面将从底层到高层详细地介绍本系统的软件设计。
4.2.1单片机读写sl811hs
4.2.1.1读取sl811hs内存的数据
根据sl811hs的读写时序要求,读取数据前首先发送地址:
void sethostaddress(char addressp)
{
p_bus=addressp;/*数据总线发送地址,但此时地址还不会被sl811hs接收*/
p_ctrl=0x90;/*设置总线控制信号为sl811hs接收地址信号,具体含义如下所示:*/
/*p_ctrl;0x90,98,f0
p_ctrl.0; ---
p_ctrl.1; ---
p_ctrl.2; ---
a0; 010
nrst; 111
ncs; 001
nwr; 001
nrd; 111*/
nwr=1;
ncs=1;/*地址传输完毕后,关闭片选、写等信号*/
p_ctrl=0xf0;
}
地址发送完毕之后,sl811hs就接到了需要读取的内存单元地址(包括寄存器的地址)。紧接着单片机就可以读取数据:
unsigned char hostread(void)
{
a0=1;/* 满足sl811hs的时序要求,先保证a0和ncs的有效*/
ncs=0;
p_ctrl=0x58;/*设置控制位信号,读取sl811hs内的数据*/
/*p_ctrl;0x90,58,f0
p_ctrl.0; ---
p_ctrl.1; ---
p_ctrl.2; ---
a0; 010
nrst; 111
ncs; 001
nwr; 011
nrd; 101*/
return p_bus;/*函数返回读取的sl811hs内存的数据*/
}
4.2.1.2写入数据到sl811hs内存
与读数据类似,单片机要往sl811hs的内存单元写数据时,也要首先发送地址,然后再发送要写入的数据。为了简便起见,写数据过程中发送地址和数据的功能都放在一个函数中执行。
void hostwrite(char hostwriteaddress, char writeconstent)
{/*参数hostwriteaddress输入要写入数据的sl811hs内存的地址,writeconstent为要写入的地址*/
p_bus=hostwriteaddress;/*准备好需要发送的地址*/
p_ctrl=0x90;
/*p_ctrl;0x90,98,f0
p_ctrl.0; ---
p_ctrl.1; ---
p_ctrl.2; ---
a0; 010
nrst; 111
ncs; 001
nwr; 001
nrd; 111*/
nwr=1;
ncs=1;
p_bus=writecontent;/*准备好需要发送的数据*/
p_ctrl=0x98;/*重新安排好控制信号,发送数据*/
p_ctrl=0xf4;
}
4.2.1.3读写批量数据
有时单片机和sl811hs之间要进行批量数据的传输,为方便操作,设计了能够批量进行数据读或写的函数:
/*批量写*/
void hostbulkwrite(char addr, unsigned char *s, char c)
{/*参数addr为sl811hs中写入数据的起始地址,*s为单片机内存放的需要写入的数据缓冲区,c为总共要写入的字节数*/
if(c<=0) return;
while(c--)
{
hostwrite(addr++,*s++);
}
}
/*批量读*/
void hostbulkread(char addr, unsigned char *s, char c)
{/*参数addr为sl811hs中读取数据的起始地址,*s为单片机内存放读取来的数据的数据缓冲区,c为总共要读入的字节数*/
if(c<=0) return;
while(c--)
{
sethostaddress(addr++);
*s++=hostread();
}
}
4.2.2阶段usb传输的实现
4.2.2.1 sl811hs的初始化
初始化主要是对sl811hs的部分内部寄存器进行设置:
void sl811hs_init(void)
{
hostwrite(intena,0x20);
hostwrite(csofcnt, 0xae);
hostwrite(ctrlreg, 0x08);
hostwrite(ctrlreg, 0x00);
hostwrite(csofcnt, 0xae);
hostwrite(ctrlreg, 0x08);
delayms(10);
hostwrite(ctrlreg, 0x00);
delayms(1);
hostwrite(intstatus, 0xff);
}
4.2.2.2三种阶段usb传输的实现
三种阶段usb传输都可以由这个函数(下称“阶段传输实现函数”)实现:
void usb_transaction(unsigned char pid, unsigned char ep_address, unsigned char address, int length, char *pdatabuf);
4.2.2.2.1发送或接收前的准备工作
4.2.2.2.1.1设置ep0status寄存器
本设计涉及到了多种阶段的usb传输,但这里需要考虑的只有3种,分别是建立(setup)、数据输入(in)和数据输出(out)阶段。阶段传输实现函数的输入参数中,pid就是用来区别这3种传输阶段。
#define pid_setup 0x2d
#define pid_in 0x69
#define pid_out 0xe1
阶段传输实现函数的第二个需要输入的参数就是端点号ep_address,大小为1字节(实际只有低4位有效,高4位为0),类型为unsigned char 。u盘等类似的usb mass storage类设备一般具有3个端点:一个是端点0,用于处理控制传输;另一个是批量输出bulk_out端点,该端点用于接收主机发来的批量数据,端点号有设备定义;还有一个就是批量输入bulk_in端点,用于给÷向主机发送批量数据,其端点号也由设备定义。
从硬件角度来讲,程序需要把pid和ep_address组合在一起后写入ep0status寄存器。
unsigned char pid_epa;/*用于储存pin和ep_address的组合值*/
pid_epa=pid&0x0f;/*pid的高4位位校验码,低4位为有效值*/
pid_epa=(pid_epa
{
currentlength=maxpacketsize;
}
else
currentlength=length;
hostwrith(ep0xferlen,(unsigned char)currentlength);/*注意这里的变量类型转换*/
4.2.2.2.4设置ep0address寄存器
最后一个需要确定是发送或接收数据的缓冲地址*pdatabuf,大小为1字节。
在这里pdatabuf是指向单片机内存单元的指针,但实际读写数据是要以sl811hs的数据缓冲区作为中介的。
为加快数据传输,把sl811hs的数据缓冲区分成两部分:sl811hs_buf0和sl811hs_buf1。sl811hs_buf0的起始地址就可以定为0x10。而sl811hs_buf1的地址就根据端点最大包尺寸进行调整。
unsigned char sl811_hs_buf0 =0x10, sl811hs_buf1;
if(length>maxpackesize)
{
sl811hs_buf1= sl811hs_buf0+maxpacketsize;
}
hostwrite(ep0address, sl811hs_buf0);/*当前数据发送从sl811hs_buf 0开始*/
如果主机要发送数据给设备,就需要把*pdatabuf中的数据复制到sl811hs的数据缓冲区中:
hostbulkwrite(sl811hs_buf0,pdatabuf,currentlength);
如果是主机接收数据,那么在以下的处理中,就会把sl811hs缓冲区中的接收到的设备的数据通过hostbulkread()函数复制到单片机的缓冲区中。
hostbulkread(sl811hs_buf0,pdatabuf,currentlength);
4.2.2.2.5启动发送或接收
启动usb数据的发送或接收实际上是通过向sl811hs的ep0control寄存器发送命令字cmdword来实现的。
首先,pid等参数的不同,cmdword的值也不同,根据ep0control寄存器每一位的属性,有如下配置程序:
unsigned char cmdword;
if(pid==pid_setup)
{
cmdword=0x03;/*控制传输的setup事务*/
}
else
{
if(ep_address==0)
{
if(pid==pid_in)
{
cmdword=0x47;/*控制传输的输入in事务*/
}
else
{
cmdword=0x43;/*控制传输的out事务*/
}
}
else
{
if(pid==pid_in)
{
cmdword=0x07;/*批量传输in事务*/
}
else
{
cmdword=0x03;/*批量传输out事务*/
}
}
}
将cmdword命令字发送到sl811hs的ep0control寄存器后,就启动了数据包的发送或接收了:
hostwrite(intstatus, 0xff);/*清除中断状态位*/
hostwrite(ep0control, cmdword);
剩下的工作就是查询sl811hs的intstatus寄存器,以查看发送或接收的完成情况,有需要时,最后还可以查看ep0status获取握手包的有关信息,但其实所有的握手包信息都是有硬件自动完成的。
4.2.3事务usb传输的实现
4.2.3.1控制传输
包含了三个阶段:建立阶段、可选数据阶段以及状态阶段。
void control_transfer(prequestcmd requestcmd, unsigned char* pdatabuf_x);
4.2.3.1.1建立阶段的实现
任务就是发送建立的8字节请求命令,命令的数据结构为(注意该段定义是放在control_transfer()函数之外的):
typedef struct{
unsigned char bmrequest type;
unsigned char brequest;
unsigned int wvalue;
unsigned int windex;
unsigned int wlength;
}requestcmd,*prequestcmd;
在这里只需调用一次usb_transaction()函数即可:
usb_transaction(pid_setup, 0 , device_address, 0x08, (char *)requestcmd);
/*发送的令牌为pid_setup,端点号为0,设备地址为device_address,发送数据长度为8字节,发送内容为相应的请求命令*/
4.2.3.1.2可选数据阶段的实现
注意数据传输方向,实现过程如下:
if(requestcmd->wlength)
{
if(requestcmd->bmrequesttype & 0x80)/*判断为pid_out*/
{
usb_transaction(pid_out, 0, device_address, requestcmd->wlength, pdatabuf_x);
}
else/*判断为pid_in*/
{
usb_transaction(pid_in, 0, device_address, requestcmd->wlength, pdatabuf_x);
}
}
4.2.3.1.3状态信息阶段的实现
在需要时(如可选数据阶段为in),主机发送控制传输的状态信息:
usb_transaction(pid_out, 0, device_address, 0, pdatabuf_x);
4.2.3.2批量传输
类似与控制传输中的可选数据阶段,有两个函数,分别对应于批量传输in和批量传输out:
void bulk_transfer_in(int length_bi, unsigned char* pdatabuf_bi)
{
usb_transaction(pid_in,ep_bulk_in,device_address, length_bi, pdatabuf_bi);
}
void bulk_transfer_out(int length_bo, unsigned char* pdatabuf_bo)
{
usb_transaction(pid_out,ep_bulk_out,device_address, length_bi, pdatabuf_bo);
}
4.2.4 usb设备枚举的实现
现在,各种枚举所需的传输实现函数已经好了,要实现usb请求动作进而实现设备枚举是很容易的了。下面是两个典型请求动作的实现函数:
/*获取描述符请求命令*/
void get_descriptor(unsigned int wvalue_d, unsigned char length_d, unsigned char * pdatabuf_d)
{
requestcmd requestcmd;/*建立该请求命令的结构*/
requestcmd.bmrequesttype=0x80;/*填入该请求命令的内容*/
requestcmd.brequest=get_descriptor;
requestcmd.wvalue=wvalue_d;
requestcmd.windex=0;
requestcmd.wlength=length_d;
control_transfer(&requstcmd,pdatabuf_d);
}
/*设置设备地址请求命令*/
void set_address(unsigned int device_address_e)
{
requestcmd requestcmd;/*建立该请求命令的结构*/
requestcmd.bmrequesttype=0x00;/*填入该请求命令的内容*/
requestcmd.brequest=set_address;
requestcmd.wvalue=w device_address_e;
requestcmd.windex=0;
requestcmd.wlength=0;
control_transfer(&requstcmd,0);
}
嵌入式usb主机
Spotify发明一项AI技术监督词曲作者剽窃行为
苹果并没有想象中安全?不信你看苹果iTunes欺诈交易是怎么回事?
人工智能和物联网的融合为全球企业释放了巨大潜力
华为P10 一买就跌一卖就涨,7台“安卓机皇”即将上市
vivo折叠屏智能手机新专利公开:主要涉及手写笔方面
嵌入式USB主机设计(硬件设计和软件设计)
芯片工作原理是什么
广汽新推出新能源汽车AIon LX,具有3.9秒破百的超跑性能
第三代宽禁带半导体材料,是我国弯道超车的机会
红米2A拆解 内部结构如何
功率放大器居然有记忆效应?
未来我国机器人的应用领域将会越来越多
硬件“攻城狮”晶振选型的苦恼
持续研究面向智慧光网的关键技术,筑5G承载坚实底座
中国5G牌照的颁发宣告了中国正式推开了5G大门进入了5G时代
低功耗蓝牙芯片的应用可显著降低功耗和成本
蓄电池的常见故障及异常处理
清美教授助力理工男 共同打造机器人设计经典
Vivado的XDC设置输出延时问题
电池温度智能监测系统设计[图]