//----------------------------------------------------------------------------- // standalone.c // Firmware used when USB adapter is used standalone (no SPI). //----------------------------------------------------------------------------- // Reserved port usage: // // P0.4 INT0, in // // P2.2 LED1 // P2.3 LED2 // //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- #include #include #include "USB_API.h" #include "chimaera.h" sfr16 TMR2RL = 0xca; // Timer2 reload value sfr16 TMR2 = 0xcc; // Timer2 counter sfr16 ADC0 = 0xbe; sbit PINT0 = P0 ^ 4; // P2^0 ADC channel 0 // P2^1 ADC channel 1 sbit Led1 = P2^2; // LED='1' means OFF sbit Led2 = P2^3; 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[] = {0xe,0x03,'C',0,'B',0,' ',0,'U',0,'S',0,'B',0}; code const BYTE Serial[] = {0x0A,0x03,'0',0,'0',0,'0',0,'1',0}; 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 devicePacket_t packet; devicePacketISR_t packet_isr; unsigned int0_pending = 0; unsigned isr_pending = 0; unsigned tx_ready = 1; unsigned waitUSB = 1; unsigned char adcChannel = 0; unsigned PCAMatchCount = 0; typedef struct adcChannel_t { unsigned char uc[2]; } adcChannel_t; adcChannel_t adcData[2]; void main(void) { unsigned rc; PCA0MD &= ~0x40; // Disable Watchdog timer // USB_Init(0,0xEA61,VendorString,ProductString,Serial,MaxPower,PwAttributes,bcdDevice); USB_Init(0xCBCB,0x0001,VendorString,ProductString,Serial,MaxPower,PwAttributes,bcdDevice); // Although USBXpress says this register is off-limits, I think the clock divide bits should not affect the USB. 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 ) ; Led1 = 0; // Continuous transaction loop while ( 1 ) { while ( tx_ready == 0 ); if ( int0_pending == 1 ) { int0_pending = 0; packet.cmd = Int0; packet.length.uc[0] = 64; packet.length.uc[1] = 0; while ( tx_ready == 0 ); tx_ready = 0; rc = Block_Write( (BYTE *)&packet, 68 ); } if ( isr_pending == 1 ) { isr_pending = 0; if ( packet_isr.cmd == StatusRequest ) { packet.cdata[0] = P0; // Port 0 packet.cdata[1] = P1; // Port 1 packet.cdata[2] = P2; // Port 2 packet.cdata[20] = adcData[0].uc[0]; // ch 0 ADC LSB packet.cdata[21] = adcData[0].uc[1]; // ch 0 ADC MSB packet.cdata[22] = adcData[1].uc[0]; // ch 1 ADC LSB packet.cdata[23] = adcData[1].uc[1]; // ch 1 ADC MSB // Send the data to the USB host packet.cmd = Status; packet.length.uc[0] = 64; packet.length.uc[1] = 0; while ( tx_ready == 0 ); tx_ready = 0; rc = Block_Write( (BYTE *)&packet, 68 ); } packet_isr.cmd = Idle; } } } //------------------------- // Port_Init //------------------------- // Port Initialization // 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 = 0x0F; // P0.0-3 push pull (outputs), P0.4-7 open drain (inputs) P0 = ~0x0F; // Open-drain outputs set high impedance // Port 1 P1MDIN = 0xFF; // All digital input P1MDOUT = 0xF0; // Upper push pull, lower open drain P1 = ~0xF0; // Set lower to high impedance (inputs) // Port 2 P2MDIN = 0xFC; // P2.0,2-7 set digital input, P2.1 analogue P2MDOUT = 0x0C; // P2.2,3 set push-pull (LEDs) P2 = ~0x0C; // Open-drain outputs set high impedance P2SKIP = 0x03; // P2.0,1 skipped by crossbar XBR1 = 0x41; // Enable Crossbar, route CEX0 to PIO return; } //------------------------- // 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 return; } //------------------------- // Initialize //------------------------- void Initialize(void) { Port_Init(); // Initialize crossbar and GPIO Timer_Init(); Adc_Init(); return; } // Int0_ISR // Called on external interrupt. void Int0_ISR(void) interrupt 0 { int0_pending = 1; EX0 = 0; return; } // 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. isr_pending = 1; break; case PIOWrite: // Write to port 0,1 // Make sure input bits remain high-Z P0 = packet_isr.cdata[0] | 0xf0; P1 = packet_isr.cdata[1] | 0x0f; break; case GenerateClockBurst: TL0 = packet_isr.cdata[0]; TH0 = packet_isr.cdata[1]; PCA0CPL0 = packet_isr.cdata[2]; // Initial match value PCA0CPH0 = packet_isr.cdata[2]; // Match increment value TR0 = 1; CR = 1; break; case Int0Enable: EX0 = 1; break; case Int0Disable: EX0 = 0; break; default: break; } } if (INTVAL & TX_COMPLETE) { tx_ready = 1; } if (INTVAL & DEV_SUSPEND) { Suspend_Device(); } if (INTVAL & DEV_CONFIGURED) { waitUSB = 0; // Initialize(); } return; } //------------------------- // Timer_Init //------------------------- // Timer initialization // Timer 0 used to time a burst of clocks generated with PCA0 // Timer 2 used for ADC continuous conversion // void Timer_Init(void) { // Timer 0 setup TR0 = 0; // Stop Timer0 TF0 = 0; // Clear overflow flag TMOD = 0x01; // 16-bit counter ET0 = 1; // Enable Timer 0 interrupt // Timer 2 setup TMR2CN = 0x00; // Stop Timer2; Clear TF2; TMR2RL = -(24000000 / 12); // Initialize reload value TMR2 = 0xffff; // Set to reload immediately ET2 = 1; // Enable Timer2 interrupts // Clock setup for T0 and T2 CKCON = 0x02; // Enable timer 2 (timer 0 enabled on demand) TR2 = 1; // Start Timer2 // PCA0 setup (frequency output mode) CR = 0; CF = 0; CCF0 = 0; PCA0MD = (PCA0MD & 0xf0) | 0x08; // Use system clock PCA0CPM0 = 0x46; // Select frequency output mode PCA0L = 0x00; // Set initial value of counter PCA0H = 0x00; // Not used in frequency mode PCA0CPL0 = 0x40; // Initial match value PCA0CPH0 = 0x40; // Match increment value } //------------------------- // Adc_Init //------------------------- // ADC initialization // - Configures ADC for single ended continuous conversion or Timer2 // void Adc_Init(void) { REF0CN = 0x0E; // Enable voltage reference VREF AMX0P = 0x09; // Positive input is P2.1 AMX0N = 0x1F; // Single ended mode(negative input = gnd) ADC0CF = 0xF8; // SAR Period 0x1F, Right adjusted output ADC0CN = 0xC2; // Continuous converion on timer 2 overflow // with low power tracking mode on EIE1 |= 0x08; // Enable conversion complete interrupt } //------------------------- // Timer0_ISR //------------------------- // Called when timer 0 overflows. // void Timer0_ISR(void) interrupt 1 { TR0 = 0; CR = 0; // Stop the PCA0 frequency generator } //------------------------- // Timer2_ISR //------------------------- // Called when timer 2 overflows. // void Timer2_ISR(void) interrupt 5 { TF2H = 0; // Clear Timer2 interrupt flag } //------------------------- // Adc_ConvComplete_ISR //------------------------- // Called after a conversion of the ADC has finished void Adc_ConvComplete_ISR(void) interrupt 10 { // Save the value adcData[adcChannel].uc[0] = ADC0L; adcData[adcChannel].uc[1] = ADC0H; // Switch to other channel adcChannel ^= 0x1; AMX0P = adcChannel + 0x8; // Reset inetrrupt flag AD0INT = 0; }