CY7C68031A固件程序FW.C详解(1)

本来要一次传上去,百度空间嫌文章太长,只好分为两篇

FW.C文件,我当初看了一个星期,也没看懂的。这里我们逐字逐句研读,
边理解,边一行一行的注释.

//以下是Cypress公司的官方程序,我不做改动,原英文注释保留,只增加注释
//简单语句就不说了
    //???//是我不懂得的地方,希望高手补充
    //###//是以后开发可能需要改动的地方//
我加的所有注释都用四个连斜杠,便于以后不需要的时候屏蔽掉
这是在Keil UV2里编辑的,没有其它格式字符,可以直接编译

 

//-----------------------------------------------------------------------------
//   File:      fw.c
//   Contents: Firmware frameworks task dispatcher and device request parser
//
// $Archive: /USB/Examples/FX2LP/bulkext/fw.c $
// $Date: 3/23/05 2:53p $
// $Revision: 8 $
//
//
//-----------------------------------------------------------------------------
// Copyright 2003, Cypress Semiconductor Corporation
//-----------------------------------------------------------------------------


#include "fx2.h"
fx2.h 定义EZUSB的宏、数据类型等的头文件
         
#include "fx2regs.h"
fx2regs.h 定义EZUSB寄存器定义的头文件

#include "syncdly.h"            // SYNCDELAY macro
syncdly.h 同步延时宏定义

//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
#define DELAY_COUNT   0x9248*8L // Delay for 8 sec at 24Mhz, 4 sec at 48
#define _IFREQ 48000            // IFCLK constant for Synchronization Delay
#define _CFREQ 48000            // CLKOUT constant for Synchronization Delay
以上设置时钟频率为48MHZ

//-----------------------------------------------------------------------------
// Random Macros
//-----------------------------------------------------------------------------
#define   min(a,b) (((a)<(b))?(a):(b))
#define   max(a,b) (((a)>(b))?(a):(b))

//-----------------------------------------------------------------------------
// Global Variables
全局变量
//-----------------------------------------------------------------------------
volatile BOOL   GotSUD;
GotSUD是令牌包标志,准确的说是“令牌阶段数据到来”,什么是令牌包?
首先,USB一连串的数据传输、处理、响应等就叫做USB事务。
例如,上位机要读取一个描述符,那么就会触发一次USB事务。
一个完整的USB事务处理有三个阶段:令牌阶段,数据阶段,握手阶段。
每个阶段数据传输是有各种包组成的,例如令牌阶段:同步字段+令牌包+EOP构成。
USB主机启动事务处理,开始发送令牌包,这个时候假如说我们当前的USB设备地址号
为2(重枚举时分配的),而主机发送的地址号也为2,那么这个USB设备硬件会产生
中断,进入中断处理。也就是periph.c文件中的void ISR_Sudav(void)函数,
在这个中断处理中,设置 GotSUD标志为TRUE,表示收到令牌数据,要启动USB传输了。
然后我们固件中判断if(GotSUD),GotSUD为真,则执行SetupCommand()函数,
在这里处理控制传输,读取描述符,设置特性,处理Vendor命令等。
完毕后置GotSUD = FALSE;然后检查USB各种状态并处理:

BOOL      Rwuen;
Rwuen 远程唤醒标志

BOOL      Selfpwr;
Selfpwr 禁止或使能自供电模式

volatile BOOL   Sleep;                  // Sleep mode enable flag
Sleep   //禁止或使能休眠模式


以下是定向USB描述符
WORD   pDeviceDscr;   // Pointer to Device Descriptor; Descriptors may be moved
设备描述符指针

WORD   pDeviceQualDscr;
设备限定描述符指针

WORD   pHighSpeedConfigDscr;
高速配置描述符指针

WORD   pFullSpeedConfigDscr;
全速配置描述符指针
  
WORD   pConfigDscr;
配置描述符指针

WORD   pOtherConfigDscr;
其他速率配置描述指针
  
WORD   pStringDscr;
字符串描述符指针

//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
硬件程序的函数入口。主要有以下这些方法:
功能在函数中说明

void SetupCommand(void);
void SetupCommand(void);     //握手命令处理
void TD_Init(void);
void TD_Init(void);   //初始化,完成配置,启动时调用一次
void TD_Poll(void);
void TD_Poll(void);   //用户处理程序,循环调用

void IO_Init(void);   //8051 IO初始化
void REG_Init(void); //8051寄存器初始化

BOOL TD_Suspend(void);
BOOL TD_Suspend(void);    //挂起处理
BOOL TD_Resume(void);
BOOL TD_Resume(void);     //唤醒处理

以下为各种描述符的获取和设置函数,重枚举时自动调用
功能在函数中说明

BOOL DR_GetDescriptor(void);
DR_GetDescriptor(void)获得描述符
BOOL DR_SetConfiguration(void);
BOOL DR_SetConfiguration(void)设置配置子函数
BOOL DR_GetConfiguration(void);
BOOL DR_GetConfiguration(void) 获取配置子函数
BOOL DR_SetInterface(void);
DR_SetInterface(void)配置接口子函数
BOOL DR_GetInterface(void);
DR_GetInterface(); 获取接口子函数
BOOL DR_GetStatus(void);
/DR_GetStatus(void) 获得状态
BOOL DR_ClearFeature(void);
DR_ClearFeature(void) 清除特性
BOOL DR_SetFeature(void);
DR_SetFeature(void)设置特性
BOOL DR_VendorCmnd(void);
DR_VendorCmnd(void) 自定义的用户请求

 

// this table is used by the epcs macro     
   //???//
const char code EPCS_Offset_Lookup_Table[] =
{
   0,    // EP1OUT
   1,    // EP1IN
   2,    // EP2OUT
   2,    // EP2IN
   3,    // EP4OUT
   3,    // EP4IN
   4,    // EP6OUT
   4,    // EP6IN
   5,    // EP8OUT
   5,    // EP8IN
};

// macro for generating the address of an endpoint's control and status register (EPnCS)  
   //???//
#define epcs(EP) (EPCS_Offset_Lookup_Table[(EP & 0x7E) | (EP > 128)] + 0xE6A1)

//-----------------------------------------------------------------------------
// Code
//-----------------------------------------------------------------------------

// Task dispatcher
void main(void)
{
   DWORD   i;
   WORD   offset;
   DWORD   DevDescrLen;
   DWORD   j=0;
   WORD   IntDescrAddr;
   WORD   ExtDescrAddr;

   // Initialize Global States
   初始化全局状态变量
  
   Sleep = FALSE;               // Disable sleep mode
   Sleep = FALSE; //初始化用户变量 休眠使能--禁止
   Rwuen = FALSE;               // Disable remote wakeup
   Rwuen = FALSE;//远程唤醒--禁止
   Selfpwr = FALSE;            // Disable self powered
   Selfpwr = FALSE; //禁止自供电模式
   GotSUD = FALSE;               // Clear "Got setup data" flag
   GotSUD = FALSE; //清除SetUp令牌包到来标志

   // Initialize user device
   TD_Init();
   TD_Init(); 初始化USB

   // The following section of code is used to relocate the descriptor table.
   // The frameworks uses SUDPTRH and SUDPTRL to automate the SETUP requests
   // for descriptors. These registers only work with memory locations
   // in the EZ-USB internal RAM. Therefore, if the descriptors are located
   // in external RAM, they must be copied to in internal RAM.
   // The descriptor table is relocated by the frameworks ONLY if it is found
   // to be located in external memory.

   //取得定向USB描述符的指针(地址)
   //这段代码用来获取USB的各个描述符在68013内存中的地址,
   //准确说是在RAM中的地址,在dscrpt.a51文件中有定义,
   //所有的描述符组成了整个的描述符表,后面会用到。
   pDeviceDscr = (WORD)&DeviceDscr;
   pDeviceDscr = (WORD)&DeviceDscr; //设备描述符
   pDeviceQualDscr = (WORD)&DeviceQualDscr;
   pDeviceQualDscr = (WORD)&DeviceQualDscr; //设备限定描述符
   pHighSpeedConfigDscr = (WORD)&HighSpeedConfigDscr;
   pHighSpeedConfigDscr = (WORD)&HighSpeedConfigDscr; //高速配置描述符
   pFullSpeedConfigDscr = (WORD)&FullSpeedConfigDscr;
   pFullSpeedConfigDscr = (WORD)&FullSpeedConfigDscr; //全速配置描述符
   pStringDscr = (WORD)&StringDscr;
   pStringDscr = (WORD)&StringDscr;   //字符串描述符

   // Is the descriptor table in external RAM (> 16Kbytes)? If yes,
   // then relocate.
   // Note that this code only checks if the descriptors START in
   // external RAM. It will not work if the descriptor table spans
   // internal and external RAM.
   意思是说,这段代码用来判断描述符表首址,
也就是前面的DeviceDscr、DeviceQualDscr等是否位于68013的外部RAM区,
如果是,则移除,然后将描述符表移到内部RAM区,为什么要移到内部RAM区,
因为当描述符表位于外部RAM时,USB是不工作的。那么如何判断描述符地址
是否超出内部RAM的地址呢?首先,&DeviceDscr取得整个描述符表的首地址
(它也是DeviceDscr设备描述的首址),然后和0XC000相与,为什么要和0XC000相与?
这就牵涉到68013 FX2LP(注意是LP)的内部结构图:
见数据单
上图针对的是128pin的FX2LP,如果是56或100pin的,那么没有外部RAM,
只有内部RAM。可以看到,FX2LP内部RAM从0000-FFFF,其他为外部RAM。
而内部RAM中,只有从0000-3FFF和从E000-FFFF的区域可用,其他为系统保留。
从0000-3FFF这16K bytes的内部RAM空间,叫做主RAM,对56,100,128pin来说,
都可以同时作为程序或数据存储器(对128pin来说,EA=0)。
再看&DeviceDscr & 0xC000的结果,要为“真”的话,显然,
必须&DeviceDscr>=0X4000,也就是说判断的是描述表首址&DeviceDsc是否大于3FFF,
刚好是主RAM区的大小,这就是为什么要用&DeviceDscr 和 0xC000相与就来
判断实现了描述符表首地址的原因了(外部 or 内部 ram?)
    if ((WORD)&DeviceDscr & 0xC000)
   {
      // first, relocate the descriptors
      IntDescrAddr = INTERNAL_DSCR_ADDR;
      ExtDescrAddr = (WORD)&DeviceDscr;
      DevDescrLen = (WORD)&UserDscr - (WORD)&DeviceDscr + 2;
      for (i = 0; i < DevDescrLen; i++)
         *((BYTE xdata *)IntDescrAddr+i) = *((BYTE xdata *)ExtDescrAddr+i);
   判断发现描述符表首址位于外部RAM的后,紧接着就将外部RAM的描述符移到内部RAM。
   //这里就用到了前面定义的变量,IntDescrAddr保存内部RAM首址0X80,
   ExtDescrAddr保存我们获得的当前描述符表外部RAM的首址 ,DevDescrLen
   //是整个描述表的长度,从DeviceDscr段到UserDscr段,在dscrptr中有定义。
   //然后for循环,将从ExtDescrAddr地址开始的外部RAM中的数据逐个copy到从
   IntDescrAddr地址开始的内部RAM区。
      // update all of the descriptor pointers
   更新描述符指针
   完毕后更新描述符指针,指向内部RAM区,通过原指针减去一个偏移量得到。
      pDeviceDscr = IntDescrAddr;
      offset = (WORD)&DeviceDscr - INTERNAL_DSCR_ADDR;
      pDeviceQualDscr -= offset;
      pConfigDscr -= offset;
      pOtherConfigDscr -= offset;
      pHighSpeedConfigDscr -= offset;
      pFullSpeedConfigDscr -= offset;
      pStringDscr -= offset;
   }

   EZUSB_IRQ_ENABLE();            // Enable USB interrupt (INT2)
   EZUSB中断使能
   EZUSB_IRQ_ENABLE();预定义是EZUSB=1,ezusb是EIE寄存器的第0位,EIE.0=1,使能USB中断;

   EZUSB_ENABLE_RSMIRQ();            // Wake-up interrupt
   使能远程唤醒中断
   EZUSB_ENABLE_RSMIRQ();EICON |= 0x20,EICON.5=1,使能远程唤醒中断;
  


   INTSETUP |= (bmAV2EN | bmAV4EN);     // Enable INT 2 & 4 autovectoring
   使能INT2,4自动向量跳转
   INTSETUP |= (bmAV2EN | bmAV4EN);使能INT2,4自动向量跳转;

   USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT;   // Enable selected interrupts
   使能所选择中断
   相关的中断意义,后面慢慢学习补充;  

   EA = 1;                  // Enable 8051 interrupts
   开8051中断

#ifndef NO_RENUM
   // Renumerate if necessary. Do this by checking the renum bit. If it
   // is already set, there is no need to renumerate. The renum bit will
   // already be set if this firmware was loaded from an eeprom.

   if (!(USBCS & bmRENUM)) //如果RENUM位为0,则重列举
   {
       EZUSB_Discon(TRUE);   // renumerate 重列举
   }
#endif

这段代码即告诉USB进行重枚举,用软件设置,模拟USB断开与连接,
EZUSB_Discon(TRUE)完成断开连接
将USBCS寄存器的DICON位置1,断开USB,同时如果RENUM位为0,
则置1;然后重新连接USB。
那么,什么是重枚举呢?
首先,上面的USB枚举是对通用USB来说的,一般USB设备只有枚举过程,没有重枚举过程。
也就是说其实,对EZ-USB系列来说,上面的枚举举实际包含了EZ-USB枚举和重枚举
两个过程:
对EZ-USB来说,枚举过程就是USB上电复位到加载固件前这段过程,此时USB设备
地址号为默认的0号,枚举完成后,驱动为cypress...eeprom..missing
(FX2/FX2LP来说)。然后加载固件,进行重枚举,重枚举完成后,
显示驱动为cypress...ez-usb...example或其他自定义设备。
为什么EZ-USB有重枚举过程呢?这就是为了可以让主机在前期固件未加载前自动枚举,
识别USB,从而可以从上位机加载固件到68013的RAM中,而无需使用ROM,EEPROM,
FLASH等内存。实现“软件”架构加载程序代码,随时修改固件。
EZ-USB如何控制重枚举?
首先,EZUSB有一个寄存器USBCS,其中第1位USBCS.1为Renum控制枚举重枚举。
在USB上电未加载固件代码前,Renum=0,表示使用“EZUSB核心”对芯片的初始配置,
处理主机设备请求,并负责把固件下载到RAM中,这个过程就是“枚举”,
完成该枚举过程的设备叫做“缺省USB设备(地址号0)”,
即驱动显示为“...missing”的设备。当固件被下载到8051 RAM后,此时Renum=1,
表示使用“增强8051核心”处理主机设备请求,并按照固件的代码(读取所有描述符)
重新配置USB设备,这个过程就叫做“重枚举”,完成重枚举后,
显示自定义的USB设备“...example”,实际是模拟断开与连接的过程。
用以下表表示:

事件----处理设备请求--Renum-----8051动作
枚举----EZUSB核心-----Renum=0--8051置Renum=1
重枚举--8051----------Renum=1--8051重置Renum=0

在重枚举完成后,对控制端点0的设备请求可以由“EZUSB核心”处理或由
“增强8051核心”处理,有Renum的值决定。芯片上电时Renum=0,
由“EZUSB核心”处理;一旦8051开始运行,就可以设置Renum=1,
由“增强8051核心”处理,表示按照8051下载的固件代码处理。
当然,这时也可以设置Renum=0,让“EZUSB核心”处理端点0的设备请求,
而让8051完成具体的USB数据传输,这样做会大大简化8051固件代码。
然后再看接下来的代码:
注意,在这段代码之前,EZUSB已经枚举完成,当然,整个过程是自动的,
我们看不见的。
详见<<USB设备枚举过程.doc>>


    // unconditionally re-connect. If we loaded from eeprom we are
    // disconnected and need to connect. If we just renumerated this
    // is not necessary but doesn't hurt anything

   USBCS &=~bmDISCON;   
   重新连接

   CKCON = (CKCON&(~bmSTRETCH)) | FW_STRETCH_VALUE; // Set stretch
   // CKCON = (CKCON&(~bmSTRETCH)) | FW_STRETCH_VALUE;Set stretch to 0 (after renumeration)
   //EZ-USB执行指令MOVX(读取外部存储器)时,在两个MOVX之间可以增加的几个时钟周期数
   //这对于外部的较慢的存储器或外设(例如LCD)等是很合适的,外部存储器的等待时钟可以在固件中调整
   //但是程序存储器不会受到影响

 

   // clear the Sleep flag.
   Sleep = FALSE;
   清sleep休眠使能标志


//紧接着主机开始读描述符,并进行配置(发送SetConfiguration),
//加载驱动等,完成重枚举。

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值