//----------------------------------------------------------------------------- // L1C8051.c //----------------------------------------------------------------------------- // L1 SPI to USB interface // The SPI interface is configured in 3-wire master mode // // Port usage: // P0.0 SPI SCK, out // P0.1 SPI MISO, in // P0.2 SPI MOSI, out // P0.3 Slave reset, in // P0.4 INT0, in // P0.5 L1ResetOut, out // P0.6 Slave reset, out // // P1.7 Analogue, in // // P2.2 LED1 // P2.3 LED2 // //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- #include #include #include "USB_API.h" #include "l1c8051.h" //----------------------------------------------------------------------------- // Global CONSTANTS //----------------------------------------------------------------------------- sbit PINT0 = P0 ^ 4; sbit SPISlaveResetOut = P0^6; // Assert to reset slave sbit SPISlaveResetIn_b = P0^3; // ='1' when slave is ready sbit Led1 = P2^2; // LED='1' means OFF sbit Led2 = P2^3; sbit P20 = P2^0; sbit P21 = P2^1; sbit L1ResetOut = P0^5; // Fast DAQ reset code const BYTE VendorString[] = {0x2a,0x03,'C',0,'a',0,'v',0,'e',0,'n',0,'d',0,'i',0,'s',0,'h',0,' ',0,'L',0,'a',0,'b',0,'.',0,' ',0,'(',0,'H',0,'E',0,'P',0,')',0}; code const BYTE ProductString[] = {0x12,0x03,'L',0,'1',0,' ',0,'R',0,'e',0,'v',0,' ',0,'3',0}; // Serial[] is the USB serial number used in enumeration code const BYTE Serial[] = {0x0A,0x03,'0',0,'0',0,'0',0,'3',0}; // serialNumber and ipoffset are loaded into slave register 31 on // reset. The two highest IP octets are hard-coded in the FPGA. The two // low octets are computed as (ipoffset + serialNumber)&0x7f code const BYTE serialNumber = 3; code const unsigned short ipoffset = 0x210; code const BYTE MaxPower = 15; // Max current = 16 mA (8 * 2) code const BYTE PwAttributes = 0x80; // Self-powered, remote wakeup not supported code const UINT bcdDevice = 0x100; // Device release number 1.00 code const unsigned char maxRegisters = 32; unsigned short nByte; devicePacket_t packet; devicePacketISR_t packet_isr; unsigned char rxInterrupt = 0; //unsigned char txInterrupt = 0; unsigned char int0_pending = 0; unsigned char resetRequest = 0; unsigned char waitUSB = 1; unsigned short seq = 0; unsigned char txSlots[2] = {0}; unsigned char nextSlot = 0; unsigned char nextSlotISR = 0; //----------------------------------------------------------------------------- // Main Routine //----------------------------------------------------------------------------- void main(void) { unsigned rc; unsigned char txPending = 0; unsigned char registerId; unsigned char dummy; unsigned char byteNumber; BYTE cmd; unsigned char writeMode; unsigned short iplow16; PCA0MD &= ~0x40; // Disable Watchdog timer USB_Init(0xCBCB,0x0003,VendorString,ProductString,Serial,MaxPower,PwAttributes,bcdDevice); OSCICN |= 0x3; // System clock divide. Set for fastest system clock speed. Initialize(); USB_Int_Enable(); Led1 = 1; Led2 = 1; // Wait for the USB to be ready while ( waitUSB == 1 ) ; while ( SPISlaveResetIn_b == 0 ) ; // Wait until the slave is ready Led1 = 0; // All USB packets to the host have 64 byte payload packet.length.uc[0] = 64; packet.length.uc[1] = 0; // Continuous transaction loop while ( 1 ) { L1ResetOut = 0; SPISlaveResetOut = 1; while ( SPISlaveResetIn_b == 1 ) ; // Wait until the slave is reset SPISlaveResetOut = 0; while ( SPISlaveResetIn_b == 0 ) ; // Wait until the slave is ready resetRequest = 0; // Cause an initial configuration message to be sent containing the // board serial number and IP source address. iplow16 = 0x7fff & (ipoffset + serialNumber); packet_isr.cmd = ConfigurationData; packet_isr.cdata[0] = 31; packet_isr.cdata[3] = 0x80 | (iplow16>>8); packet_isr.cdata[2] = 0xff & iplow16; rxInterrupt = 1; while ( (SPISlaveResetOut == 0) && (resetRequest == 0) ) // Jump out when the reset is asserted (from ISR) { if ( int0_pending == 1 ) { int0_pending = 0; packet.cmd = Int0; txPending = 1; } if ( (txPending == 1) && (txSlots[nextSlot] == 0) ) { txSlots[nextSlot] = 1; nextSlot ^= 0x1; rc = Block_Write( (BYTE *)&packet, 68 ); txPending = 0; // Transmit packet queued } if ( rxInterrupt == 1 ) { rxInterrupt = 0; if ( (packet_isr.cmd == StatusRequest) || (packet_isr.cmd == ConfigurationData) ) { for ( registerId=0; registerId < maxRegisters; registerId++ ) { writeMode = (packet_isr.cmd == ConfigurationData) && (registerId == packet_isr.cdata[0]) ? 1 : 0; for ( byteNumber=0; byteNumber < 4; byteNumber++ ) { if ( writeMode == 1 ) { SPI0DAT = registerId | 0x40; // Slave register ID (write) } else { SPI0DAT = registerId; // Slave register ID (read) } while ( SPIF == 0 ); // Wait for transaction end SPIF = 0; dummy = SPI0DAT; // Should be Idle character SPI0DAT = Idle; // Idle character while ( SPIF == 0 ); // Wait for transaction end SPIF = 0; dummy = SPI0DAT; // Should be echoed register ID // packet.cdata[2*registerId] = SPI0DAT; // D[0] from slave SPI0DAT = packet_isr.cdata[3]; // D[0] to slave while ( SPIF == 0 ); // Wait for transaction end SPIF = 0; packet.cdata[2*registerId+1] = SPI0DAT; // D[0] from slave // dummy = SPI0DAT; // Should be echoed register ID SPI0DAT = packet_isr.cdata[2]; // D[1] to slave while ( SPIF == 0 ); // Wait for transaction end SPIF = 0; packet.cdata[2*registerId] = SPI0DAT; // D[1] from slave } } // Update also the status from the MCU // packet.cdata[0] = P0; // Port 0 // packet.cdata[1] = P1; // Port 1 // packet.cdata[2] = P2; // Port 2 // packet.cdata[3] = seq++; // Sequence counter packet.cmd = Status; txPending = 1; // Packet is ready for transmission } packet_isr.cmd = Idle; } else { SPI0DAT = Idle; while ( SPIF == 0 ); // Wait for transaction end SPIF = 0; cmd = SPI0DAT; } switch ( cmd ) { case Idle: case Idle2: break; default: break; } } } } //----------------------------------------------------------------------------- // Initialization Subroutines //----------------------------------------------------------------------------- //------------------------- // Port_Init //------------------------- // Port Initialization // - Configure the Crossbar and GPIO ports for SPI. // Note: open-drain outputs are high-impedance when the corresponding bit in th Pn register is set. // void Port_Init(void) { IT01CF = 0x0c; // P0.4 INT0, active high IT0 = 0; // INT0 type: 0=level 1=edge sensitive EX0 = 0; // Disable INT0 // Port 0 P0MDIN = 0xFF; // P0.0-7 set digital input P0MDOUT = 0x65; // P0.0,2,6 set push-pull (SCK,MOSI,L1ResetOut,ResetOut) P0 = ~0x65; // Open-drain outputs set high impedance // Port 1 /* P1MDIN = 0x7F; // P1.7 set as analog input P1MDOUT = 0x00; // P1.0-7 set open-drain P1SKIP = 0x80; // P1.7 skipped by crossbar P1 = 0xFF; // Open drain outputs set high impedance */ P1MDIN = 0xFF; // All digital input P1MDOUT = 0xF0; // Upper push pull, lower open drain P1 = ~0xF0; // Set lower to high impedance (inputs) // Port 2 P2MDIN = 0xFF; // P2.0-7 set digital input P2MDOUT = 0x0F; // P2.2,3 set push-pull (LEDs) P2 = ~0x0F; // Open-drain outputs set high impedance XBR0 = 0x02; // Enable SPI IO XBR1 = 0x40; // Enable Crossbar } // SPI0_Init void SPI0_Init(void) { SPI0CFG = 0x40; // MSTEN=1, CKPHA=0, CKPOL=0 SPI0CKR = 0x0; // Max. SPI clock rate = SYSCLK/2 ESPI0 = 0; // Disable SPI0 interrupt (we do not use it) SPI0CN = 0x1; // Enable } //------------------------- // Suspend_Device //------------------------- // Called when a DEV_SUSPEND interrupt is received. // - Calls USB_Suspend() // void Suspend_Device(void) { // Don't disable peripherals. Device is self-powered. USB_Suspend(); // Put the device in suspend state } //------------------------- // Initialize //------------------------- void Initialize(void) { SPI0_Init(); // Initialize SPI Port_Init(); // Initialize crossbar and GPIO } // Int0_ISR // Called on external interrupt. void Int0_ISR(void) interrupt 0 { int0_pending = 1; EX0 = 0; } // USB ISR void USB_Device_ISR(void) interrupt 16 { BYTE INTVAL = Get_Interrupt_Source(); if (INTVAL & RX_COMPLETE) { Block_Read( (BYTE *)&packet_isr, 8 ); switch ( packet_isr.cmd ) { case StatusRequest: // Status request. case ConfigurationData: // Update config register rxInterrupt = 1; break; case PIOWrite: // Write to port 1,2 // Make sure lower (open drain) input bits are forced to high-Z P1 = packet_isr.cdata[1] | 0xf; // P2 = packet_isr.cdata[2]; break; case Int0Enable: EX0 = 1; break; case Int0Disable: EX0 = 0; break; case ResetRequest: resetRequest = 1; break; case L1ResetRequest: L1ResetOut = 1; L1ResetOut = 0; break; default: break; } } if (INTVAL & TX_COMPLETE) { if ( txSlots[nextSlotISR] == 1 ) { txSlots[nextSlotISR] = 0; nextSlotISR ^= 0x1; } } if (INTVAL & DEV_SUSPEND) { Suspend_Device(); } if (INTVAL & DEV_CONFIGURED) { waitUSB = 0; // Initialize(); } }