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

📄 firmware.html

📁 USB驱动使用比较详细的一个例子
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<html><head><title>FirmWare</title></head><body><p align="center"><font color="#FF0000"><b>MiniNurse(MN)固件源代码及其分析</b></font></p><blockquote>  <blockquote><p><br>对于像c51这样简单的芯片,其固件就相当于操作系统,一个相当原始和粗糙的操作系统。下面是一个"Hello World"性质的固件片断(C for51),我做了一些注释:</p>         <blockquote>      <p>void main (void)&nbsp;&nbsp;&nbsp; <font color="#808080">/* 芯片加电后PC为0,转到这里执行。         */</font><br>     {<br>    &nbsp;&nbsp;&nbsp; <font color="#808080">/* 这里可能会有中断、定时器、等的初始化代码         */</font><br>     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(1) {&nbsp;&nbsp;&nbsp; <font color="#808080">/* 注意!一个死循环,这正是固件成为操作系统的原因。         */</font><br>     &nbsp;&nbsp;&nbsp;&nbsp;<font color="#808080">/*        如果没有这个循环执行完后面的代码后,芯片会处于空闲状态。只有重置才能唤醒它。        */</font><br>    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; P1 ^0= 0x01;&nbsp;&nbsp;&nbsp; <font color="#808080">/*         这两行就是我们的示例代码,它们是唯一的“进程”,一直被执行。        */</font><br>    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf ("Hello World\n");&nbsp;&nbsp;&nbsp;        <font color="#808080">/* printf()通常会从51的串口输出。*/</font><br>     &nbsp;&nbsp;&nbsp; }<br>       }</p>    </blockquote><p>MN的固件要比这复杂。它的固件是分层的,这对固件的可理解性、可移植性以及健壮性都非常重要。比如,如果换用Motorola或者其它种类的单片机,只需要修改最底层的代码就可以了,这个工作量是非常小的。又如要扩展MN的功能,只要添加相应的厂商请求部分以及可能的主循环(mainloop.c)代码,这个工作量会因为扩展的功能而异,但如果不是分层,代码的维护将会是可怕的。<br> <br>MN的固件代码,分成以下几个层次:<a href="#硬件抽象层:base_io.c base_io.h">硬件抽象层</a>,<a href="#D12命令接口:D12_comm_if.c D12_comm_if.h">D12命令接口</a>,<a href="#中断服务例程:isr.c">中断服务例程</a>,<a href="#USB标准请求:usb_standard_request.c usb_standard_request.h">USB标准请求</a>、<a href="#USB厂商请求:usb_usr_request.c usb_usr_request.h">厂商请求</a>,<a href="#主循环:mainloop.c mainloop.h">主循环</a>等。</p>  </blockquote></blockquote><p> </p><blockquote>  <p><font color="#000080"><b><a name="硬件抽象层:base_io.c base_io.h">硬件抽象层:base_io.c base_io.h</a></b></font></p>      <blockquote><p>该层定义了C51和D12通讯的方法。base_io.h中声明了两个函数(复用方式):</p>     <blockquote>    <p>	void outportb(unsigned int Addr,unsigned char Data);<br>    	unsigned char inportb(unsigned int Addr);</p>       </blockquote><p>outportb()为发送信息到D12,inportb()相反。<br> <br>51和D12之间的信息有数据和地址之分。在51看来,它连接的是一个特殊的扩展RAM,这个RAM只有两个地址状态。这个两个地址状态对于D12来说则意味着下次到达的信息是数据还是对它的控制命令。D12的手册上说,偶数地址为数据,奇数地址为命令。参考outportb()的实现:</p>     <blockquote>    <p>void outportb(unsigned int Addr,unsigned char Data) {<br>    	*((unsigned char xdata *)Addr)=Data;<br>    	}</p>    </blockquote><p>在base_io.h中,有如下两行:</p>     <blockquote>    <p>#define D12_Command 0xff03<br>       #define D12_Data&nbsp;&nbsp;&nbsp; 0xff02</p>       </blockquote><p>在更高层的代码中,会经常看到类似如下的调用:</p>     <blockquote>    <p>outportb(D12_Command, 0xFD);</p>        </blockquote><p>这条语句说明0xFD是对D12的一个命令(该命令读取芯片ID)。实现中的xdata是C for 51的扩展,被这个关键词修饰的变量存储在外部RAM中,前面说过,D12就相当与51的一个扩展RAM,所以这里就是在D12中。C for 51还有其它的扩展,可以参阅具体的编译器文档,它们之间有细微差别,本文只在必要的时候做必要的解释。这里给出一种编译器的扩展规则,仅供参考:</p>    </blockquote></blockquote><p align="center"><img border="0" src="../../Set%20of%20Docutments/FIRMWARE_Explicitly%20Declared%20Memory%20Types.png" width="648" height="276"><br>截取自C51</p><blockquote>  <blockquote><p>inportb()的实现方法与outportb()绝类,只是方向相反。<br><br> D12除了支持上面使用的这种所谓“复用方式”外,还支持非复用方式。这种方式通过控制D12上的A0管脚区分指令和数据。具体的参看D12手册和硬件设计文档。这种方式下需要修改outportb()和inportb()的实现,代码和注释如下。不过这种方式并没有试验成功,目前还不确定是否是固件的问题,仅示意!语言顺序和那些看上去没有意义的语言成份都是试图符合D12的时序要求。</p>     <blockquote>    <p>void outportb(unsigned int Addr,unsigned char Data)	//send to D12<br>        {<br>    &nbsp;&nbsp;&nbsp; switch (Addr)&nbsp;&nbsp;&nbsp;	//in fact, 'Addr' should be described as 'Command or Data select word'<br>        &nbsp;&nbsp;&nbsp; {<br>       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case D12_Command:&nbsp;&nbsp;&nbsp;        //send command-byte to D12<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;        A0c=D12_Command;<br>    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case D12_Data:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;        //send data-byte to D12<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A0c=D12_Data;<br>       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>       &nbsp;&nbsp;&nbsp; }<br>       <br>    &nbsp;&nbsp;&nbsp; WRc=0;&nbsp;&nbsp;&nbsp; //Write immediately<br>        &nbsp;&nbsp;&nbsp; do {if (1+1==2);} while(0);&nbsp;&nbsp;&nbsp; //重复执行上行语句可能是更好的延时方法!<br>       &nbsp;&nbsp;&nbsp; P0=Data;<br>       &nbsp;&nbsp;&nbsp; do {if (1+1==2);} while(0);&nbsp;&nbsp;&nbsp; //重复执行上行语句可能是更好的延时方法!<br>       &nbsp;&nbsp;&nbsp; WRc=1;&nbsp;&nbsp;&nbsp; //enclose<br>       }<br>    <br>    unsigned char inportb(unsigned int Addr)	//receive from D12<br>        {<br>    &nbsp;&nbsp;&nbsp; unsigned char tmpData;<br>        &nbsp;&nbsp;&nbsp; switch (Addr)&nbsp;&nbsp;&nbsp;	//in fact, 'Addr' should be described as 'Command or Data select word'<br>        &nbsp;&nbsp;&nbsp; {<br>       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case D12_Command:<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A0c=D12_Command;<br>       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case D12_Data:<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A0c=D12_Data;<br>       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;        break;<br>    &nbsp;&nbsp;&nbsp; }<br>       <br>    &nbsp;&nbsp;&nbsp; RDc=0;&nbsp;&nbsp;&nbsp; //Read immediately<br>        &nbsp;&nbsp;&nbsp; do {if (1+1==2);} while(0);&nbsp;&nbsp;&nbsp; //重复执行上行语句可能是更好的延时方法!<br>       &nbsp;&nbsp;&nbsp; tmpData=P0;<br>       &nbsp;&nbsp;&nbsp; do {if (1+1==2);} while(0);&nbsp;&nbsp;&nbsp; //重复执行上行语句可能是更好的延时方法!<br>       &nbsp;&nbsp;&nbsp; RDc=1;&nbsp;&nbsp;&nbsp; //enclose<br>       &nbsp;&nbsp;&nbsp; return tmpData;<br>        }</p>    </blockquote>    <p align="center"><img border="0" src="../../Set%20of%20Docutments/FIRMWARE_D12_timing.PNG" width="743" height="468"><br>    截取自Philips PDIUSBD12手册</p>       <p align="center"> </p>    <p align="center"> </p>  </blockquote>  <p><b><font color="#000080"><a name="D12命令接口:D12_comm_if.c D12_comm_if.h">D12命令接口:D12_comm_if.c D12_comm_if.h</a></font></b></p>      <blockquote><p>D12向外提供了十数条指令,芯片手册上有详细的列表和语法说明。D12_comm_if.h声明了它们中的大多数,因为有几条指令并没有多少用处。D12_comm_if.c中的实现完全根据芯片手册的定义而写,没有什么可以说的。仍以读芯片ID的例子做示范性说明,这个命令似乎也没有实际的用途,不过却可以知道D12是否正常工作。<br> <br>读芯片ID的命令号是0xFD,这个刚才看过了。该指令后跟两个八位数据,方向是D12-->51。在实现中,可以看到发送命令后,读了两次数据。第一次读到的是低位,第二次是高位,做了位操作后拼装成一个16位整型返回。(是的,16位。8位单片机还能怎样?)为了方便,将代码再列下:</p>     <blockquote>    <p>unsigned short D12_ReadChipID(void)<br>        {<br>    &nbsp;&nbsp;&nbsp; unsigned short tmpi,tmpj;<br>        <br>    &nbsp;&nbsp;&nbsp; if(bEPPflags.bits.in_isr == 0)<br>        &nbsp;&nbsp;&nbsp; DISABLE;<br>       <br>    &nbsp;&nbsp;&nbsp; outportb(D12_Command, 0xFD);<br>        &nbsp;&nbsp;&nbsp; tmpi=inportb(D12_Data);<br>       &nbsp;&nbsp;&nbsp; tmpj=inportb(D12_Data);<br>       &nbsp;&nbsp;&nbsp; tmpi += (tmpj&lt;&lt;8);<br>        <br>    &nbsp;&nbsp;&nbsp; if(bEPPflags.bits.in_isr == 0)<br>        &nbsp;&nbsp;&nbsp; ENABLE;<br>       &nbsp;&nbsp;&nbsp; return tmpi;<br>        }</p>    </blockquote>    <p> </p>  </blockquote>

⌨️ 快捷键说明

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