MAX3420E便于使用通用微控制器设计USB外设。在介绍MAX3420E之后,本文重点介绍器件的SPI接口。本文演示了如何使用硬连线SPI单元或对通用I/O引脚进行位敲击来访问器件。给出了使用MAXQ2000微控制器的USB设计的示例C代码。
介绍
有关通用串行总线 (USB) 的文章,首先证明 USB 是个人计算机的新连接标准。值得庆幸的是,这不再是必需的,所以这个介绍可以很简短。如果您有嵌入式系统并想连接到PC,则主流管道是USB。
Maxim Integrated的新型芯片MAX3420E可轻松将USB添加到任何系统中。本文重点介绍MAX3420E的集成SPI(串行外设接口)接口,并提供通用SPI实现的示例C代码。本文最后介绍了一个简单的USB HID(人机接口设备)的代码,这是一个基于Windows的紧急按钮。
将 USB 添加到任何内容
微控制器 (μC) 的选择通常基于集成外设。一些处理器包括USB功能,但大多数处理器,尤其是真正低成本的版本,没有。您是否曾经选择过I/O和外设完美结合的微控制器,但发现它缺少USB?另外,是否要添加 USB 并继续使用现有的开发工具?
现在可以使用Maxim的新型MAX3420E将USB添加到任何微控制器中。该芯片提供USB全速收发器、智能USB串行接口引擎(SIE)和SPI从接口,可与高达26MHz的SCLK时钟信号一起运行。MAX3420E作为全速USB外设工作,具有一个控制端点、两个双缓冲64字节数据端点和一个64字节中断端点。
总线驱动的小部件
图1.USB 总线供电的小组件。
图 1 说明了一种常见的 USB 外设架构。The USB V总线电线为5.3V稳压器提供3V电源,3420.3V稳压器为微控制器和MAX4E供电,无需外部电源。SPI 接口可以包含 5、1 或 5 根电线。表 <> 显示了完整的 <> 引脚接口。
信号 | MAX3420E方向 | 描述 |
莫西 | 在 | SPI 主站出站,从站输入 |
酱 | 外 | SPI 主输入,从输出 |
高莱克 | 在 | 串行时钟 |
党卫军# | 在 | 从属选择 |
国际 | 外 | 中断(电平或脉冲) |
如果应用不需要中断(所有MAX3420E中断条件都可以通过读取寄存器位直接测试),则可以省去INT引脚,使用4引脚接口。如果您的SPI主机具有双向数据接口(MOSI/MISO位于同一双向引脚上),则可以再减少一个引脚。因此,不支持中断和双向数据引脚的SPI接口仅使用三个引脚。
如果微控制器没有SPI端口,该怎么办?没关系。通过直接切换通用I/O引脚,很容易制作固件驱动的SPI主机。USB 的一个强大功能是它是自节流的;它自动适应SPI侧的任何速度接口。(它通过使用 USB 端的 NAK 握手来指示“现在忙,请重试”来实现此目的。许多USB外设,尤其是那些连接到人类的外设,即使是最慢的SPI接口,也能非常灵敏地运行。
如果图1中的微控制器真的很小,可能不到10个引脚怎么办?您是否需要使用所有这些有价值的I / O引脚来与USB芯片通信?是的,但这正是MAX3420E提供3420路通用输入和3420路通用输出的原因。简单地说,MAX3420E有<>个通用I/O来代替与其通信所需的引脚,但MAX<>E增加了更多的I/O。因此,连接MAX<>E后,您的系统实际上有更多的I/O引脚。
大筹码
图2.连接到大芯片的一个小角落。
MAX3420E不限于小型系统。图 2 说明了如何将 USB 功能添加到大型 ASIC、FPGA、DSP 或其他大型芯片。这样做的一个明显原因是大芯片可能没有内置USB,或者里面的USB可能不是你想要的。这种架构的另一个很好的原因是,随着大型芯片在工艺几何形状中的缩小,它们不太能够触及“高”电压,例如USB所需的3.3V。具有低压SPI接口的外部USB芯片是应对这些设计挑战的良好解决方案。为了运行低压接口,MAX3420E具有内部电平转换器和一个VL引脚,用于将SPI接口的工作电压设置为1.7V至3.6V之间的任何电压。
隔离式 USB
图3.隔离 USB。
如上图3所示,SPI接口是放置光隔离的简单位置。这是因为SPI信号是单向的,它们可以在低频下工作,以支持低成本光耦合器。
SPI 接口
SPI是一个简单的串行接口,使用两条数据线、一个串行时钟和一个片选信号。SPI主机将SS#降至低电平以开始传输,然后驱动串行时钟SCLK,以同时对数据进行时钟输入和传出从设备。SPI 主机通过返回 SS# 高电平来终止传输。
SPI接口有四种时钟模式,反映两种模式信号,称为CPOL(时钟极性)和CPHA(时钟相位)。这些信号以(CPOL,CPHA)的形式表示。期望在第一个正边沿之前同时提供正边沿 SCKS 和 MOSI 数据的接口可以在模式 (0,0) 和 (1,1) 下工作而无需更改。该特性允许MAX3420E工作在上述任一模式,无需模式引脚。
图4和图5给出了微控制器(MAXQ2000,稍后介绍)和MAX3420E之间的相同数据传输。图 4 使用 SPI 模式 (0,0),图 5 使用 SPI 模式 (1,1)。区别在于SCLK信号的非活动电平,模式(0,0)为低电平,模式(1,1)为高电平。
图4.SPI 接口在模式 (0,0) 下工作。
图5.SPI 接口在模式 (1,1,) 下工作。
MAX3420E接受命令字节作为每次传输的第一个字节。命令字节包含寄存器编号和方向位。第二个和后续字节包含数据。图3420和图4中的命令字节输入(MOSI引脚)时,来自MAX5E (MISO引脚)的<>位是每次输入命令字节时可用的USB状态位。此功能仅对使用单独数据引脚 MISO 和 MOSI 的接口有效。
SPI 代码
为MAX3420E编写通用C代码的关键是将最少的SPI操作隔离在单独的模块中,并仅定制从SPI接口到SPI接口的模块。此模块至少只需要做三件事:
Initialize_SPI
读取字节
写入字节
此处的示例应用程序使用硬件 SPI 单元。对于没有这种单元的应用,我们将首先回顾一些通用的C代码,用于位敲击SPI接口。
位敲击 SPI
初始化 SPI
Initialize_SPI功能在不同处理器之间变化最大。它负责分配接口使用的特定 I/O 引脚,设置其方向,然后设置 SS = 1 和 SCLK = 0 的初始条件。(我们正在制作一个模式 (0,0) SPI 主站。
读寄存器、写寄存器
rreg是读取MAX3420E寄存器的C功能。宏(全部大写)将功能与各种微控制器的各种I/O方案隔离开来。使用宏使代码易于阅读且独立于处理器。wreg 是写入 MAX3420E 寄存器的例程。
如果更改处理器,则只需更改少量宏即可使用这些例程。例如,下面的宏适用于不包含硬件SPI单元的微控制器。
#define SCLK_HI OUTA = PINSA | 0x02; #define SCLK_LO OUTA = PINSA & 0xFD; #define SS_HI OUTA = PINSA | 0x04; #define SS_LO OUTA = PINSA & 0xFB; #define MOSI(v) OUTA = (PINSA & 0x7F) | (v & 0x80); #define MISO inval |= PINSA & 0x01; BYTE
#define SCLK_HI OUTA = PINSA | 0x02; #define SCLK_LO OUTA = PINSA & 0xFD; #define SS_HI OUTA = PINSA | 0x04; #define SS_LO OUTA = PINSA & 0xFB; #define MOSI(v) OUTA = (PINSA & 0x7F) | (v & 0x80); #define MISO inval |= PINSA & 0x01; BYTE rreg(BYTE r) // Read a register, return its value. { int j; BYTE bv,inval; inval = 0; SS_LO bv = r<<3; // Left-shift the reg number, WRITE=0 for (j=0; j<8; j++) // send the register number and direction bit { MOSI(bv) // put out a bit bv <<= 1; // shift one bit left SCLK_HI SCLK_LO } for (j=0; j<7; j++) // get 7 bits and shift left into 'inval' { SCLK_HI MISO inval <<= 1; // shift in one bit SCLK_LO } SCLK_HI // one more bit, but don't shift 'inval' this time MISO SCLK_LO SS_HI return inval; // return the byte we read in } void wreg(BYTE r,BYTE v) // register, value { int j; BYTE bv; SS_LO bv = (r<<3)+2; // Left-shift the reg number, set the WRITE direction bit for (j=0; j<8; j++) // send the register number and direction bit { MOSI(bv) // put out a bit bv <<= 1; // shift one bit left SCLK_HI SCLK_LO } for (j=0; j<8; j++) // send the register data { MOSI(v) // put out a bit v <<= 1; // shift one bit left SCLK_HI SCLK_LO } SS_HI }
硬件 SPI
本节讨论前面提到的MAXQ2000微控制器。简而言之,MAXQ2000是低功耗、16位、高性能RISC处理器系列中的首款产品。MAXQ2000中的“Q”表示“安静”,表示该架构设计为与敏感的模拟电路良好共存。MAXQ2000具有内置的SPI端口,使其对MAX3420E特别友好。以下示例使用MAXQ2000开发板和MAX3420E构建一个简单但有趣的Windows小部件。
MAXQ2000硬件SPI单元提供SCLK、MOSI和MISO,但不提供SS#。由于 SS# 的工作方式各不相同(例如,用于访问一个字节与字节突发),因此最好对 SS# 使用通用 I/O 引脚。
MAXQ I/O 单元
图6.一个 MAXQ I/O 单元。
图6所示为基本的MAXQ I/O单元。I/O 端口位以“port.bit”格式标记,其中“p”是端口,“b”是位。在本例中,我们专注于 I/O 端口 5 位 3(引脚标记为 P53)。
每个 I/O 单元都有一个触发器,在本例中,该触发器是使用称为 PO5.3 的位编写的。“O”表示输出。你总是可以写这个触发器;它是否连接到引脚取决于方向位。配置输出引脚时,为了避免毛刺,最好在将触发器连接到引脚之前写入触发器。
P53引脚的方向由称为PD5.3的位设置。“D”表示方向,D信号用作引脚驱动器的输出使能:1 = 驱动,0 = 浮点。引脚的状态始终可以在称为PI5.3的位中读取,其中“I”表示输入。无论引脚如何驱动,由内部触发器 (PD5.3 = 1) 或外部触发器 (PD5.3 = 0) 驱动,PI 位指示引脚状态。
此结构中有一个值得注意的功能。如果将P53引脚配置为输入(PD5.3 = 0),则触发器的输出不用作输出,因此可以重复使用为上拉电阻开关。当D = 0时,O信号被重新定义为“连接上拉电阻”,如图6中的虚线和开关所示。
一些I/O引脚具有中断能力,如图6下面的模块所示。中断块有三个信号:
在中断请求处于活动状态时设置并由 CPU 重置的标志位。
一个边沿选择位,用于确定是正信号转换还是负信号转换导致中断请求。
特定引脚的中断使能位。
我们的示例应用将MAX3420E INT输出引脚配置为正边沿触发中断。在MAXQ2000端,代码直接测试挂起USB中断的中断触发器,而不是使用MAXQ2000中断系统。该程序除了检查按钮的状态并响应USB请求外,什么都不做,因此只需要轮询循环。
初始化 SPI
MAXQ2000 I/O引脚在通用I/O和特殊功能硬件(如SPI单元)之间共享。要使用特殊硬件,请先配置硬件块,然后使其能够连接到 I/O 引脚。下面的 SPI_Init() 例程设置引脚方向,配置 SPI 接口,最后启用它。
void SPI_Init(void) { // MAXQ2000 SPI port CKCN = 0x00; // system clock divisor is 1 SS_HI // SS# high PD5 |= 0x070; // Set SPI output pins (SS, SCLK, DOUT) as output. PD5 &= ~0x080; // Set SPI input pin (DIN) as input. SPICK = 0x00; // fastest SPI clock--div by 2 SPICF = 0x00; // mode(0,0), 8 bit data SPICN_bit.MSTM = 1; // Set Q2000 as the master. SPICN_bit.SPIEN = 1; // Enable SPI // MAX3420E INT pin is tied to MAXQ2000 P60; make it an input PD6 &= ~0x01; // PD6.0=0 (turn off output) }
读寄存器、写寄存器
以下功能利用了MAXQ2000的硬件SPI单元,因此比位爆炸的同类产品更小、更快。
// Read a MAX3420E register, return its value. BYTE rreg(BYTE reg) { BYTE dum; SS_LO SPIB = reg<<3; // reg number w. dir=0 (IN) while(SPICN_bit.STBY); // loop if data still being sent dum = SPIB; // read and toss the input byte SPIB=0x00; // data is don't care, we're clocking in MISO bits while(SPICN_bit.STBY); // loop if data still being sent SS_HI return(SPIB); } // Write a MAX3420E register. void wreg(BYTE reg, BYTE dat) { SS_LO // Set SS# low SPIB = (reg<<3)+2; // send reg. number w. DIR bit (b1) set to WRITE while(SPICN_bit.STBY); // loop if data still being sent SPIB = dat; // send the data while(SPICN_bit.STBY); // loop if data still being sent SS_HI // set SS# high }
示例:基于 Windows 的紧急按钮
这个USB小部件是基于Windows的USB HID(人机接口设备),具有一个“紧急”按钮。当您按下按钮时,所有活动的PC窗口都将最小化,并且您正在查看桌面。再次按下它,所有应用程序窗口都会恢复生机。
USB键盘很有趣。如果插入多个键盘,它们将同时处于活动状态。因此,此紧急按钮可与您的普通键盘配合使用。
如果电脑挂起,紧急按钮将扮演新角色 - 它可以用作电脑的远程唤醒按钮。此操作高度依赖于你的电脑是否支持从 USB 唤醒。有些 PCS 可以,有些则不会。此按钮可帮助确定你的电脑是否具有此功能。
本代码示例在MAXQ2000开发板上运行,将小型USB子板(包含MAX3420E)插入扩展连接器。
USB 详细信息
此应用程序包含执行枚举基本工作的 USB 样板代码。本文档末尾的 Panic_Button_Enum_Data.h 列表中的字符数组完全描述了此设备的特性。
此应用程序使用两个终结点:必需的 CONTROL 终结点零和 EP3-IN(单缓冲 64 字节终结点)。虽然MAX3420E包含两个双缓冲64字节端点(EP1输出和EP2输入),但本应用不需要双缓冲的吞吐量优势。
一个常见的 HID 误解是 HID 设备仅以低速运行。此应用程序演示了即使是像键盘这样慢的东西也可以从全速运行中受益。这是正确的,因为键盘使用较少的总线带宽,发送 12MHz 而不是 1.5MHz 数据包。
图7.紧急按钮的流程图。
中断终结点具有轮询间隔,该间隔确定 USB 主机向 IN 终结点请求数据的频率。在每个时间间隔内,我们可以期望主机向设备的终结点 3 发送 IN 请求。图 7 说明了处理这些请求的简单状态机。枚举器件后,微控制器会重复执行此例程。为简化起见,此应用程序轮询中断引脚的活动。如果在微控制器中运行其他操作,则需要调用 Do_IN3 函数以响应中断。
状态机使用两个全局变量:状态和按钮。C 宏定义了三种状态:空闲、发布和等待。状态变量初始化为 IDLE。如果按下连接到MAX3420E GPIN0引脚的按钮,则可变按钮为高电平,否则为低电平。main()中的无限循环递增按钮检查定时器,到期时读取MAX3420E中的GPIO寄存器以确定按钮状态。这样可以节省不必要的 SPI 流量。
当按钮向上时,状态图采用两个向左的分支,并且不执行任何操作。如果在空闲状态下按下按钮,则是时候发送键码以清除活动窗口了。这是序列 08(Windows 键)00(保留)和 07(字母 d)。下一个状态设置为“发布”,并且操作已完成。
一旦MAX3420E通过USB发送该数据包,就会产生另一个EP3-IN中断请求,指示EP3-IN FIFO再次可用于加载数据。再次输入图 7 函数。此时间状态 = RELEASE,因此函数发送序列 00 00 00,指示“键启动”。下一个状态设置为 WAIT,意思是“等待按钮松开”。
现在,该函数只需要使用 WAIT 状态分支来检测按钮释放。当按钮保持按下状态时,没有任何反应。释放按钮时,状态图采用两个向右的分支,并将状态变量重新初始化为 IDLE,为下一次按下按钮准备函数。
大多数时间执行的代码非常小。下面是整个函数,它实现了图 7 中的流程图:
void Do_IN3(void) { switch(state) { case IDLE: if (button) { wreg(rEP3INFIFO,0x08); // "Windows" prefix key wreg(rEP3INFIFO,0); wreg(rEP3INFIFO,0x07); // "D" key wreg(rEP3INBC,3); // arm it state = RELEASE; // next state sends the "keys up" code } break; // else do nothing (and the SIE will NAK) // case RELEASE: { wreg(rEP3INFIFO,0x00); // key up wreg(rEP3INFIFO,0x00); wreg(rEP3INFIFO,0x00); // key up wreg(rEP3INBC,3); // arm it state = WAIT; // next state waits for the PB to be unpressed } break; case WAIT: if (!button) state = IDLE; break; default: state = IDLE; } // end switch }
代码花絮
代码中的一些细节值得评论。
时间关键型 USB 事件
MAX3420E通过驱动总线上的“K”状态10ms来发出远程唤醒信号。为了减轻SPI主机的计数负担,MAX3420E在内部对该信号进行计时(实际上,每隔一个USB时间敏感事件),然后在间隔结束时给SPI主机一个中断。SPI 主站不需要为这些事件使用自己的定时器;它只是启动操作,然后等待完成中断。
ACKSTAT 位
函数 rregAS 和 wregAS 做一件事与 rreg 和 wreg 不同;他们在 SPI 命令字节中设置了一个 ACK 状态位。SPI主机(在我们的例子中为MAXQ2000)使用该位告诉MAX3420E它已完成当前的控制传输,因此通过确认其状态级来终止CONTROL传输。尽管 ACKSTAT 作为内部寄存器位存在,但将其包含在 SPI 命令字节中可以更快地执行此常用操作,并且使用更少的代码。
readbytes(), writebytes() 函数
readbytes()、writebytes()功能利用了MAX3420E的突发功能。它们不是每字节发送两个 SPI 字节访问(一个命令字节和一个数据字节),而是首先丢弃 SS#,然后发送命令字节,输入/输出字节突发,最后引发 SS# 以终止 SPI 传输。
在哪里可以找到产品 ID
图8.此处显示产品 ID 字符串。
产品 ID (PID) 字符串(以 Panic_Button_Enum_Data.h 为单位)在您首次插入紧急按钮时显示为短消息。此 ID 字符串在枚举过程中弹出,该过程将紧急按钮标识为 HID,并将其与内置 Windows 驱动程序关联。
每个后续附件都是静音的,除了插入任何 USB 设备时听到的一点“ba-deep”Windows 声音。如果要随时检查设备状态,请转到图 8 中所示的屏幕。您可以通过右键单击“我的电脑”,选择“属性”、“硬件”选项卡、“设备管理器”按钮,展开“人机接口设备”项,右键单击“USB 人机接口设备”并选择“属性”来访问此屏幕。
符合 USB 标准
也许你看了代码,然后想,“对于一个一键式USB设备来说,这是很多工作。这是正确的,因为存在与任何 USB 设备关联的特定开销。幸运的是,USB 被精心指定,以至于此枚举代码可以用作任何 USB 设备的模板(如复制粘贴)。
像所有勤奋的开发人员一样,我们希望我们的设计可以通过USB-IF认证,这有助于确保它在任何PC上都能正常运行。此应用程序通过了 USB 命令验证程序(USBCV 版本 1.2.1.0)和 HID 测试,测试套件可供开发人员在 USB-IF 网站上使用。下面的图 9 是此紧急按钮的记分卡。
图9.此紧急按钮上的 USB 和 HID 测试的测试日志和状态报告。
结论
如果需要制作USB外设,可以考虑使用MAX3420E。该设备体积小,易于编程,并带有免费的示例代码。MAX3420E在设计中增加了I/O引脚,可在任何支持SPI的系统中很好地工作。由于SPI非常容易位爆炸,因此该设计包括每个微控制器。如果需要更高的性能,可以将SPI接口的时钟频率设置为高达26MHz。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !