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

来源:互联网 发布:淘宝互刷平台2016 编辑:程序博客网 时间:2024/06/05 05:40

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

////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),
//加载驱动等,完成重枚举。

原创粉丝点击