⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 firmware.html

📁 USB驱动使用比较详细的一个例子
💻 HTML
📖 第 1 页 / 共 2 页
字号:
  <p><b><font color="#000080"><a name="中断服务例程:isr.c">中断服务例程:isr.c</a></font></b></p>  <blockquote><p>再介绍一个C for 51的扩展,将一个函数声明为中断服务例程的方法是:</p>        <blockquote>    <p>void FUN_ISR (void) interrupt N using M;</p>        </blockquote><p>其中N是该函数将要响应的中断号,常用的中断号是</p>     <blockquote>    <p>0&lt;-->外部中断0<br>    1&lt;-->定时器中断0<br>    2&lt;-->外部中断1<br>    3&lt;-->定时器中断1<br>    4&lt;-->串口中断</p>    </blockquote><p>一些51变种有更多的中断向量可用,不论。M对应51的通用寄存器组号。ISR的工作就是在全局变量bEPPflags的某些域中记录下中断来源,对于伴随着USB数据而产生的中断,ISR会操作D12将数据从D12的内部缓冲区读入主存(ControlData)并做适当的解析。相对于其它模块,ISR的流程图是最复杂的(得感谢USB-IF,是他们使得最复杂的部分也不那么可怕^_^)。<br> <br>首先是usb_isr() interrupt 0 {}声明中断,因为根据连线,D12的数据中断被引到51外部中断0。ISR里做的第一件事情是禁止所有中断(ENABLE/DISABLE是定义在base_io.h里的宏)。完全没有必要嵌套使用中断,而且这样做会带来复杂性。在操作系统教科书里能看到类似的解释。<br>   <br>在这个ISR里,可以处理如下事务:总线挂起、端点0的IN和OUT,端点1的IN和OUT。因为没有使用DMA和端点2,所以有时候即使声明了它们,定义部分也会是空的。<br>  <font color="#008000"><br> 端点0的IN包处理。</font>IN包的方向是从设备到主机。所以在MCU的角度来看,处理函数的名字应该是ep0_txdone()。它的代码如下:</p>     <blockquote>    <p><br>    void ep0_txdone(void)<br>        {<br>    &nbsp;&nbsp;&nbsp; short i = ControlData.wLength - ControlData.wCount;<br>        <br>    &nbsp;&nbsp;&nbsp; D12_ReadLastTransStatus(1); // Clear interrupt flag<br>        <br>    &nbsp;&nbsp;&nbsp; if (bEPPflags.bits.control_state != USB_TRANSMIT)<br>        &nbsp;&nbsp;&nbsp; return;<br>       <br>    &nbsp;&nbsp;&nbsp; if( i >= EP0_PACKET_SIZE) {<br>        &nbsp;&nbsp;&nbsp; D12_WriteEndP(1, EP0_PACKET_SIZE, ControlData.pData + ControlData.wCount);<br>        &nbsp;&nbsp;&nbsp; ControlData.wCount += EP0_PACKET_SIZE;<br>        <br>    &nbsp;&nbsp;&nbsp; bEPPflags.bits.control_state = USB_TRANSMIT;<br>        &nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; else if( i != 0) {<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D12_WriteEndP(1, i, ControlData.pData + ControlData.wCount);<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ControlData.wCount += i;<br>        <br>    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bEPPflags.bits.control_state = USB_IDLE;<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; else if (i == 0){<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D12_WriteEndP(1, 0, 0); // Send zero packet at the end ???<br>        <br>    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bEPPflags.bits.control_state = USB_IDLE;<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>       }</p>    </blockquote><p>D12_ReadLastTransStatus();实现D12的读最后状态寄存器命令,该命令可以清除D12内部寄存器的中断位,这样D12可以继续处理来自USB的数据。之后判断待传送的数据是否大于端点0的的包大小,如果大于,则要分多次传送,用bEPPflags.bits.control_state域标志记录一个逻辑上连续的数据是否传送完成。D12_WriteEndP();实现了D12写端口命令。关于这个命令的参数及其实现可以查看D12命令接口层和D12的手册。<br> <br> <font color="#008000">端点1的OUT包处理。</font>OUT和IN包方向相反,对于MCU来说,对这种包的处理是从D12读取数据。这段代码不在此列出。具体流程如下:</p>     <blockquote>    <p>1 和IN包处理一样,通过读取最后状态,返回中断源,清除相应中断位。<br>    	2 判断包是否为SETUP包。USB规范中,SETUP事务包通常包含了对设备的控制命令。如果读入正确,固件会做一组高低位转换工作,多数iNTEL处理器的字位顺序和网络通讯中的字位顺序是颠倒的。在iNTEL计算机上用socket编程也常需要转换。<br>    	3 之后需要对OUT包向USB主控发出响应,表明接受成功。<br>    	4 对于SETUP包,固件把各个域复制到全局变量复制到ControlData中;对于非SETUP包则只复制数据。</p>        </blockquote><p>端点1的处理端点0的类似,并且可能没有SETUP的处理还会更加简单。</p>   </blockquote>  <p> </p>  <p><br>  <b><font color="#000080"><a name="USB标准请求:usb_standard_request.c usb_standard_request.h">USB标准请求:usb_standard_request.c usb_standard_request.h</a></font></b></p>      <blockquote><p>USB1.1规范定义的十一个所有USB设备必须支持的命令(当然,有一两个不支持无碍),当前MN就只实现了九个:</p>     <blockquote>    <p>void get_status(void);<br>    	void clear_feature(void);<br>    	void set_feature(void);<br>    	void set_address(void);<br>    	void get_descriptor(void);<br>    	void get_configuration(void);<br>    	void set_configuration(void);<br>    	void get_interface(void);<br>    	void set_interface(void);</p>       </blockquote><p>所有这些命令均按照USB的规范实现,基本上可以算做“标准代码”。在此不做多议。<br> <br>在usb_standard_request.c中同时定义了一些数据结构,它们用code修饰,表明这些信息将会保存在MCU的程序存储器里(ROM)。这些数据结构是重要的。在USB术语中称其为描述符。它们会在设备枚举时由主控读取。<br> <br>描述符是结构化的,一个物理上存在的USB设备有且仅有一个设备描述符,这里的实例为DeviceDescr,它描述了MN的全局性信息:这是一个USB1.1设备,它的类是0xDC(没有意义),端点0的包大小为16字节,厂商ID是菲利普等等。<br> <br>设备描述符之下可以有超过一个的配置描述符。一个配置描述符通常表示一个逻辑上的设备。MN只有一个配置描述符,实例为ConfigDescr。配置描述符有更细节的参数,如MN有一个接口,支持自供电和远程唤醒,最多可以从总线上攫取100mA电流等等。<br><br>每个配置描述符又可以有多个接口描述符作为补充。接口代表一个具体的功能,在Linux中,驱动最终落实在接口上。MN有一个接口描述符实例InterfaceDescr。它指出MN的端口数,端口的协议等等。<br><br>端点是USB的原子通信单位,一个或多个端点组成接口(即功能)。端点很像传统外设中的端口寄存器。当USB主控以及操作系统和USB设备通讯时,实际上总要和总要某个端点通讯。人们把这个端点和主控之间的信道抽象,并称之为管道。端点是有方向的,或者IN或者OUT或者双向端点。一个端点号和传送方向构成一个唯一的端点识别码。端点0是默认端点,所有设备必须有端点0用于设备枚举。端点描述符中的最大数据包长度很像计算机网络中的MTU,表示一次的最大传输量。<br> <br>MN有四个端点描述符实例,分别是端点1和2的IN/OUT(端点描述符中的端点数不包括端点0!)。</p>   </blockquote>  <p><br>  <br>  <b><font color="#000080"><a name="USB厂商请求:usb_usr_request.c usb_usr_request.h">USB厂商请求:usb_usr_request.c usb_usr_request.h</a></font></b></p>      <blockquote><p>这段代码很短,但却是MN独有的。目前MN使用厂商请求(而不是类命令)完成其全部功能:对LED(或者开关)的控制。<br> <br>USB1.1中规定命令格式如下:</p>   </blockquote></blockquote><p align="center"><img border="0" src="../../Set%20of%20Docutments/FIRMWARE_Format%20of%20Setup%20Data.PNG" width="682" height="599"><br>截取自USB 1.1规范</p>   <blockquote>  <blockquote><p><br>可以看出在厂商请求下有足够的空间发挥。MN只使用了bRequest和wIndex。bRequest作为命令号:0为点亮LED,1相反。bRequest有一个字节的长度,也就是说有256条命令号可以选择,按照对MN的幻想我预定义了用于MN上LCD、SPEAKER等等的命令号。wIndex在LED命令集合中作为LED的序号。为了方便地控制LED,我定义了一套LED库,由myLED.c和myLED.h组成。这两个文件里的注解足够解释它们,不论。</p>   </blockquote>  <p><br>  <br>  <b><font color="#000080"><a name="主循环:mainloop.c mainloop.h">主循环:mainloop.c mainloop.h</a></font></b></p>      <blockquote><p>进入主循环,首先是初始化,当然这个工作也可以交给编译器自动完成,但是有些参数还是要自己选择。<br><br>之后会碰上函数reconnect_USB();。D12提供一个很有意思的能力:soft_connect。MCU可以控制D12何时连接至USB总线上。原理是:USB通过D+/-上的电平变化侦测新设备,并要求高速设备在D+上有一个1.5K欧姆的上拉,低速设备在D-上有上拉。而D12内部提供一个D+上的上拉电阻,通过指令可以现在是否连通。这样可以防止MCU在尚未初始化前,D12提前对USB响应。对我来说,只要按下MCU的重启键而不是重新拔插接头就进行固件与驱动联合测试,十分方便。<br><br>因为完全用厂商请求完成操作,所以主循环相对简单:反复检查标志位。只介绍几个重要成份。<br></p><p>void control_handler();这个函数用来判断SETUP包是USB标准请求还是厂商请求。并把参数传递给相应的组件。<br>这里有两个函数跳转表,分别用于厂商请求和标准请求。定义在文件的开头部分。<br> </p>  </blockquote>  <p><br>  <b><font color="#000080">两个重要的全局数据结构和整体结构</font></b></p>  <blockquote><p>定义于mainloop.h中的数据结构EPPFLAGS(及其实例:bEPPflags)和CONTROL_XFER(及其实例:ControlData),始终贯穿于整个固件。前者定义了各种事件并作为事件标记,后者保存来自D12内部缓冲区的数据。<br><br>把D12命令接口层和硬件抽象层看成最底层,中断服务例程是中间层,主循环和两个请求处理是上层,它们之间的调用关系和整体结构如下:</p>  </blockquote></blockquote><div align="center">  <center>  <table border="0" width="100%">    <tr>      <td width="50%" align="center"><img border="0" src="../../Set%20of%20Docutments/FIRMWARE_CallRelat.png" width="651" height="286"></td>      <td width="50%" align="center"><img border="0" src="../../Set%20of%20Docutments/FIRMWARE_BASE%20of%20Arch.png" width="651" height="286"></td>    </tr>    <tr>      <td width="100%" align="center" colspan="2">均参考自Firmware          Programming Guide for PDIUSBD12</td>     </tr>  </table>  </center></div><p><br></p></body></html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -