概述
本来要一次传上去,百度空间嫌文章太长,只好分为两篇
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),
//加载驱动等,完成重枚举。
最后
以上就是紧张爆米花为你收集整理的CY7C68031A固件程序FW.C详解(1)的全部内容,希望文章能够帮你解决CY7C68031A固件程序FW.C详解(1)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复