📄 liveosci.c
字号:
/* * liveosci.c * * Copyright (c) 2006 by Wolfgang Wieser. * * Parts of code based on SSRP project: * Copyright 2004 David Carr, based on code * Copyright 2003 Free Software Foundation, Inc. * * GNU GPL... */#define ALLOCATE_EXTERN#include <fx2regs.h>#include "../defines.h"// Our "TxD" is CkGen's RxD and vice versa. #define SET_TXD_HIGH() do { IOA|= 0x80U; } while(0)#define SET_TXD_LOW() do { IOA&=~0x80U; } while(0)#define RXD_LEVEL() ((IOA)&0x01) /* NOTE: Result is 0 or !=0. */#define SET_SCK_HIGH() do { IOA|= 0x08U; } while(0)#define SET_SCK_LOW() do { IOA&=~0x08U; } while(0)#define SET_CKGEN_INT1_SREG_RCK_HIGH() do { IOA|= 0x02U; } while(0)#define SET_CKGEN_INT1_SREG_RCK_LOW() do { IOA&=~0x02U; } while(0)typedef unsigned char uint8;typedef unsigned int uint16; // int is 16 bit. // GLOBAL VARS: // Current IO module config: static uint8 iomodule_config=0x00U;// TRM states: (p.15-115)// // The minimum delay length is a function of the IFCLK and CLKOUT // (CPU Clock) frequencies, and is determined by the equation:// // MinNumberOfCPUCycles = ceil( 1.5 * ( ifclk_period / clkout_period + 1) );// // The required delay length is smallest when the CPU is running at its // slowest speed (12 MHz, 83.2ns/cycle) and IFCLK is running at its fastest // speed (48 MHz, 20.8 ns/cycle) --> 2 cycles. // // The longest delay is required when the CPU is running at its fastest // speed (48MHz, 20.8 ns/cycle) and IFCLK is running much slower // (e.g., 5.2 MHz, 192 ns/cycle) --> 16 cycles// // The most-typical EZ-USB configuration, IFCLK and CLKOUT both running at // 48 MHz, requires a minimum delay of 3 cycles. // // Hmm, but experimental results seem to contradict the above calculation. // E.g. with a 48MHz clock, 17 NOPs require more than 7MHz to work sometimes // and 10MHz to work reliably while we'd expect 5.2MHz to be more than // enough... (Wolfgang)#define SYNCDELAY _asm \ nop; nop; nop; nop; nop; nop; nop; nop; \ nop; nop; nop; nop; nop; nop; nop; nop; \ nop; _endasm#define NOP _asm nop; _endasm// EP1OUTCS:// bit?: BUSY: 1=SIE owns buffer; do not read; 0=firmware may read the buffer// 1->0 transition: EP1OUT data available (from host). // // EP1OUTBC: 0..64: number of bytes in EP1OUTBUF; // firmware writes any value to EP1OUTBC to arm an EP1OUT transfer// // EP1INCS:// bit?: BUSY: 1=SIE owns buffer; do not write to buffer; 0=may write to buffer// 1->0 transition: EP1IN buffer free and ready to be loaded with new data// EP1INBC: firmware arms IN transfer (to host) by writing number of bytes // (0..64) which it has previously loaded into EP1INBUF. // // EP01STAT: 2: EP1INBSY, 1: EP1OUTBSY, 0: EP0BSY// // altinterface 2 for interrupt, 1 for bulk (for ep1)// Initialize the FX2 in 16bit sync fifo mode. static void Initialize(void){ SYNCDELAY; // CPUCS: 0 0 PORTCSTB CLKSPD1 CLKSPD0 CLKINV CLKOE 8051RES 00000010 // PORTCSTB=1: reads/writes to PORTC generate RD# and WR# strobes // CLKSPD1,0 = 8051 clock speed: 00=12, 01=24, 10=48, 11=X // CLKINV=1 to invert CLKOUT signal // CLKOE=1 to drive CLKOUT pin // 8051RES=1 to reset 8051 // Want: 0001 0010 <-- 48MHz, output enabled. // 0000 0010 <-- 12MHz, output enabled. CPUCS = 0x12; // 0x12 SYNCDELAY; // Set up IO ports: // Enable output on PA3,7 and PA1, all LOW. // Select input on PA0. (nothing to do) IOA=0x00U; OEA|=(1U<<1)|(1U<<3)|(1U<<7); IOA=0x00U; // a = 10; b = 11; c = 12; d = 13; e = 14; f = 15 // Okay, the most important config register: (default: 10000000) // bit7: 1 = internal clocking of IFCLK; 0 = external // bit6: 0 = 30MHz; 1 = 48MHz // bit5: 1 = enable output to IFCLK // bit4: 1 = invert IFCLK // bit3: 1 = async mode; 0 = sync // bit2: 1 = drive GSTATE[0:2] on PORTE[0:2] (irrelevant for 56-pin package) // bit1,0: 00: ports; 01: reserved; 10: GPIF; 11: Slave FIFO (ext master), IFCONFIG = 0x43; // 0100 0011 = 0x43 externally clocked sync mode //IFCONFIG = 0xcb; // 1100 1011 = 0xcb internally clocked async mode //IFCONFIG = 0xc3; // 1100 0011 = 0xc3 internally clocked sync mode (perf test) SYNCDELAY; // Based on TRM and SSRP. REVCTL = 0x03; // See TRM... SYNCDELAY; // Configure EP6 (IN): // EP6CFG: (default on the left) // 1 bit7: VALID: 1 = enable // 1 bit6: DIR: 1 = in; 0 = out // 10 bit5,4: TYPE1,0: 00 = ivalid; 01=isochronous; 10=bulk; 11=interrupt // 0 bit3: SIZE: 0 = 512 bytes; 1 = 1024 bytes // 0 bit2: 0 // 10 bit1,0: BUF1,0: 00=quad; 01=<inval>; 10=double; 11=triple // Want: 1110 0010 (enabled, IN, BULK, double-buffered 512 bytes) EP6CFG = 0xe0; // bulk: 0xe2 double-buffered; 0xe3 triple-; 0xe0 quad SYNCDELAY; // Configure EP8 (IN): Unused. //EP8CFG = 0x62; // 0110 0010 // (Note: EP8 can only be double-buffered.) //SYNCDELAY; // Configure EP1 input and output. // EP1OUTCFG: like EPxCFG but not all fields supported; // only: valid, type (int,bulk); always OUT, 64bytes, single-buffered // Want: 0xb2 = 1011 0010 (valid interrupt OUT endpint) // Want: 0xa2 = 1010 0010 (valid bulk OUT endpint) EP1OUTCFG = 0xa2; // EP1INCFG: like EPxCFG but not all fields supported; // only: valid, type (int,bulk); always IN, 64bytes, single-buffered // Want: 0xf2 = 1111 0010 (valid interrupt IN endpint) // Want: 0xe2 = 1110 0010 (valid bulk IN endpint) EP1INCFG = 0xe2; // To be sure, clear and reset all FIFOs although // this is probably not strictly required. FIFORESET = 0x80; SYNCDELAY; // NAK all requests from host. FIFORESET = 0x02; SYNCDELAY; // Reset individual EP (2,4,6,8) FIFORESET = 0x04; SYNCDELAY; FIFORESET = 0x06; SYNCDELAY; FIFORESET = 0x08; SYNCDELAY; FIFORESET = 0x00; SYNCDELAY; // Resume normal operation. // EP6FIFOCFG: // bit7: 0 // bit6: INFM6 See TRM 15-29 (p.351): Signal line one clock earlier. // bit5: OEP6 // bit4: AUTOOUT 1 = enable (?) // bit3: AUTOIN 1 = enable (?) // bit2: ZEROLENIN 1 = enable (?) // bit1: 0 // bit0: WORDWIDE 1 = 16bit (default) // Want: 0000 1101 -> 0x0d EP6FIFOCFG = 0x0d /*&0xfe*/; SYNCDELAY; // EP8FIFOCFG: We don't use EP8. //EP8FIFOCFG = 0x01; //SYNCDELAY; //PINFLAGSAB = 0x00; // defines FLAGA as prog-level flag, pointed to by FIFOADR[1:0]//SYNCDELAY; // FLAGB as full flag, as pointed to by FIFOADR[1:0]//PINFLAGSCD = 0x00; // FLAGC as empty flag, as pointed to by FIFOADR[1:0]// won't generally need FLAGD // PORTACFG: FLAGD SLCS(*) 0 0 0 0 INT1 INT0 PORTACFG = 0x00; SYNCDELAY; // maybe not needed // All default polarities: SLWR active low,... FIFOPINPOLAR=0x00; SYNCDELAY; // This determines how much data is accumulated in the FIFOs before a // USB packet is committed. Use 512 bytes. // Not sure if we need the sync delays (WW). EP6AUTOINLENH = 0x02; // MSB SYNCDELAY; EP6AUTOINLENL = 0x00; // LSB SYNCDELAY; //SYNCDELAY;//EP6FIFOPFH = 0x80; // you can define the programmable flag (FLAGA)//SYNCDELAY; // to be active at the level you wish//EP6FIFOPFL = 0x00; // Output enable can be set like: OEA=0x03; // Output value can be set like: IOA=0x01;}// Write and receive one byte talking to the clock generator. // Assumes that the clock generator's PD3 (INT1) is already set LOW. // Must have SCK set low upon entrance. static uint8 CkGen_SerialIOByte(uint8 to_send){ uint8 i,j; uint8 res=0U; // NOTE: The most significant bit is transferred first in both directions. for(i=0; i<8; i++) { res<<=1; // Put data on TxD line and then set SCK high. if(to_send & (0x80U>>i)) SET_TXD_HIGH(); else SET_TXD_LOW(); SET_SCK_HIGH(); // Wait for the clock generator to detect the edge. It's running // at 20MHz, to this should not take too long. for(j=0; j<8; j++) { NOP; } // Take the serial clock low again. SET_SCK_LOW(); // Read back the answer from the clock generator. if(RXD_LEVEL()) res|=1; // Wait a bit again... for(j=0; j<8; j++) { NOP; } } return(res);}static void WriteIOModuleConfig(uint8 keep_rck_high){ // Data is shifted on the positive edge of the SCK signal. // The latch is transferred to the outputs on the positive edge of // the RCK (PA3) signal. uint8 i,config=iomodule_config; // Be sure to have RCK and SCK low initially. SET_CKGEN_INT1_SREG_RCK_LOW(); SET_SCK_LOW(); for(i=0; i<8; i++) { // Output a bit. // The bit which is shifted first (with i=0) will be on the output // line Q7. Hence, we need to output the MSB first. if(config & (0x80U>>i)) SET_TXD_HIGH(); else SET_TXD_LOW(); // Pulse the clock. SET_SCK_HIGH(); SET_SCK_LOW(); } // Pulse RCK high to transfer the byte to the outputs. if(keep_rck_high) { SET_CKGEN_INT1_SREG_RCK_HIGH(); } else { // This is actually a problem since it will put INT1 high as well // and hence introduce a gap in the clock. // Okay, since the IO config was changed, we can probably live with // that. uint8 tmp=IOA; SET_CKGEN_INT1_SREG_RCK_HIGH(); IOA=tmp; }}// Talk to the clock generator and set the desired clock speed. // Clock speed value in multiples of 50kS/s; only certain values allowed. // 0 indicates success. static uint8 SetClockSpeed(uint8 ckspeed){ uint8 rv,j; // Well, there's a problem which is due to historical reasons and // post-production changes in wires on the PCB... // RCK and INT1 had to be connected to disentangle PKTEND from IO lines. // So, we cannot simply put INT1 HIGH because this would flush the // IO module config. Hence, we first need to set up the IO module // config and then keep INT1=RCK HIGH. This will return with SCK low // as expected. WriteIOModuleConfig(/*keep_rck_high=*/1); // We reset the FIFOs while changing the clock. // Hmm, this does not seem to be a good idea since we may then loose // samples right after changing the clock. Hmm... //FIFORESET = 0x80; SYNCDELAY; // NAK all requests from host. //FIFORESET = 0x06; // Reset EP 6 // FIFO reset done. //FIFORESET = 0x00; SYNCDELAY; // Resume normal operation. // Wait for clock generator to react. // This can take as long as 20us (when using 50kS/s), this is about // 1000 clock cycles for a 48MHz clock. // We're waiting MUCH too long here (100us mesaured)... for(j=0; j<100; j++) { NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; } CkGen_SerialIOByte(CMD_CKGEN_SET_CLOCK_SPEED); rv=(CkGen_SerialIOByte(ckspeed)!=CMD_CKGEN_SET_CLOCK_SPEED); // Stop talking to the clock generator. // This will enable sampling again (provided the clock speed was valid // and not 0). Note that the line is inverted. SET_CKGEN_INT1_SREG_RCK_LOW(); return(rv);}// Set the IO module config lines (3 per IO module) by putting the // passed value onto the shift register outputs. // Enter with CkGen's INT1 held HIGH becuase otherwise we'd talk to the // CkGen at the same time. static uint8 SetIOModuleConfig(uint8 config){ iomodule_config=config; WriteIOModuleConfig(/*keep_rck_high=*/0); return(0);}// Handle incoming EP1 messages. // EP1INBSY must be 0. static void HandleEP1Data(void){ uint8 len=EP1OUTBC; // 0..64 xdata uint8 *buf=EP1OUTBUF; uint8 rv=0xff; // "Unknown command." // The first byte is the command, second byte is serial. // Ignore empty packets. if(len<2) return; switch(*buf) { case CMD_OSCI_SET_CLOCK_SPEED: { // We expect length >=3 in this case. if(len>=3) { uint8 speed=buf[2]; rv=SetClockSpeed(speed); } } break; case CMD_OSCI_SET_IO_CONFIG: { // We expect length >=3 in this case. if(len>=3) { uint8 config=buf[2]; rv=SetIOModuleConfig(config); } } break; } // Send back response if we can. if(!(EP01STAT & 0x4U)) { xdata uint8 *dest=EP1INBUF; dest[0]=buf[0]; // Copy command. dest[1]=buf[1]; // Copy serial. dest[2]=len; // Store received size. dest[3]=rv; // Status code. // Arm buffer: This is the last step. EP1INBC=4; }}void InjectTestData(void);void main(){ uint8 oldstat; Initialize(); //InjectTestData(); oldstat=EP01STAT; // Arm EP1 OUT buffer (host to FX2) for the first time. // This is necessary. EP1OUTBC=0x01; // First, set the default startup sampling rate. // A value of 1 is the slowest speed (50kS/s) which aids in debugging. // Fastest possible value: 200. SetClockSpeed(1); // FIXME for(;;) { uint8 curstat=EP01STAT; // Check for incoming data (OUT direction) on EP1: // 1->0 transition of bit 1 (0x02) signals new data available. if(!(curstat & 0x02) && (oldstat && 0x02)) { // New data available. HandleEP1Data(); // Re-arm buffer by writing any value to EP1OUTBC. EP1OUTBC=0x01; } oldstat=curstat; // Life sign on PA7... For debugging only. //IOA|=0x80; IOA&=~0x80; // Inject some test data? (Debugging only.) //if((EP6CS & bmBIT2)) InjectTestData(); // Stress testing for the clock generator; this is used in order // to test correct startup of the clock generator using an oscilloscope. //SET_CKGEN_INT1_SREG_RCK_HIGH(); //{ uint16 i=0; for(; i<300; i++) { NOP; }} //SET_CKGEN_INT1_SREG_RCK_LOW(); //{ uint16 i=0; for(; i<65000; i++) { NOP; }} }}#if 0void InjectTestData(void){ // Manually inject data into EP6 (IN): xdata uint8 *p=EP6FIFOBUF; // Wait until host takes entire FIFO data (if any): // (Equivalent is 0x20 for EP8.)while( !( EP68FIFOFLGS & 0x02 ) ); // Reset EP6... /*FIFORESET = 0x80; SYNCDELAY; FIFORESET = 0x06; SYNCDELAY; FIFORESET = 0x00;*/ *(p++) = 0x00; *(p++) = 0x01; *(p++) = 0x02; *(p++) = 0x03; *(p++) = 0x99; *(p++) = 0x99; // EP6BCH,L is an 11-bit wide byte count register. // EP8BCH is just 10 bits wide. // NOTE: The byte count may not be as large as the buffer. // Don't know why but this was experimentally verified and // just doesn't make sense to me when looking at the code // snippets in the TRM... (Wolfgang) SYNCDELAY; EP6BCH = 0x01; SYNCDELAY; EP6BCL = 0xfe; // Pass newly-sourced buffer on to host. SYNCDELAY; // <-- Probably not needed. }#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -