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

📄 liveosci.c

📁 10MS/s USB-2.0 ("high speed") oscilloscope with two 8 bit sampling inputs
💻 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 + -