USB Device Programming class project. This project allows a Python/Tk program running on a PC host to monitor/control a test-CPU programmed into an altera development board.
Dependencies: C12832_lcd USBDevice mbed-rtos mbed mmSPI
Diff: main.cpp
- Revision:
- 2:08655e2bb776
- Parent:
- 0:9a314675a67d
- Child:
- 9:81726c95be74
--- a/main.cpp Sun Sep 01 02:16:36 2013 +0000 +++ b/main.cpp Sun Sep 01 02:29:14 2013 +0000 @@ -1,12 +1,664 @@ -#include "mbed.h" +/*----------------------------------------------//------------------------------ + student : m-moore + email : gated.clock@gmail.com + class : usb device drivers + directory : USB_device_project + file : main.cpp + date : september 3, 2013. +----copyright-----------------------------------//------------------------------ + licensed for personal and academic use. + commercial use must be approved by the account-holder of + gated.clock@gmail.com +----description---------------------------------//------------------------------ + overview: + program to provide round-trip communication between a python test-control + program running on a pc host, and a device-under-test CPU implemented on + an altera board. the pc-host communication is over USBSerial, and the + altera communication is over SPI. + + features: + 1. multi-threaded design, use of memory-pools to transfer data between threads. + 2. use of USBDevice library for communication with PC host. + 3. use of mmSPI custom library for communication with FPGA. + 4. main thread provides USBSerial communication to/from host. + 5. SPI processing thread provides SPI communication to/from DUT. + 6. mmSPI library generates non-overlapping SPI and CPU clocks. + 7. background diagnostic thread provides LCD & LED updates. -DigitalOut myled(LED1); + indicators: (led<3:0> = LED<1:4>) + 1. LCD provides running counts for SPI and CPU clock cycles. + 2. led0 indicates main thread processing. + 3. led1 indicates SPI thread processing. + 4. led2 indicates LCD thread processing. + 5. led3 indicates reply-to-host underflow (should never turn on). + + implementation: + 1. main.processIncomingSerial(): accept incoming serial data from host, + and map it into tFromHost structures. + 2. SPIprocessingThread: take the incoming data structures instances, and + feed their content into mmSPI commands. + 3. mmSPI object: given commands/data passed from caller, + map them into SPI outgoing vectors and scan them into the FPGA. + 4. mmSPI object: receive incoming SPI vectors from FPGA. + make FPGA payload data available to caller. + 5. SPIprocessingThread: load tToHost structures with said FPGA payload data. + 6. main.processOutgoingSerial(): transfer tToHost structure data into a + serial outgoing buffer, and block-transfer it to the host PC. + + electrical: + 1. four pins (and ground) attached to the zigbee header, + programmed as three SPI pins and the CPU clock. + 2. each of the four signals over twisted-pair. + 3. but some ribbon cable is used at the FPGA end. + 4. best if only the mbed application board USB cable is attached + to the host; if the mbed CPU board USB cable is also attached to + the host, then the python program may attempt to use the wrong USB + connection. + 5. no particular power sequence is needed for this system to work. + + + testing: + the python UI provides the main testing mechanism. + + USB connect. + 00. press 'CONNECT' button in UI. verify connection info in shell. + + CPU register w/r + 01. type values into {R0,R1,R2,R3,PC,IR} UI entry-forms. + 02. press 'REG WRITE' UI button. + 03. press 'REG READ' UI button. + 04. verify that the read data is correct. + + CPU main-memory w/r + 05. type an address into 'mmADR' UI entry-form. + 06. type data into 'mmVAL' UI entry-form. + 07. press 'MM WRITE' UI button. + 08. type a different address into 'mmADR' UI entry-form. + 09. type different data into 'mmVAL' UI entry-form. + 10. press 'MM WRITE' UI button. + 11. type address from (05) into 'mmADR' UI entry-form. + 12. press 'MM READ' UI button. + 13. verify that the data from (06) is seen in the 'mmVAL' entry form. + 14. type address from (08) into 'mmADR' UI entry-form. + 15. press 'MM READ' UI button. + 16. verify that the data from (09) is seen in the 'mmVAL' entry form. + + CPU main-memory full load/dump. + 17. press 'PROGRAM' in the UI. select a program file in the dialog-popup. + 18. watch the load process in the shell text. + 19. press 'DUMP in the UI. select a main-memory dump file in the diaglog-popup. + 20. watch the dump process in the shell text. + + CPU step function. + 21. press 'STEP' in the UI repeatedly, watch the UI display the + CPU register states as the current program is executed one CPU clock + at a time. + + CPU run function. + 22. press 'RUN' in the UI. watch the current program run at high speed. + 23. press 'SLOW' in the UI. watch the current program run at slow speed. + 24. press 'STOP' in the UI. the program will stop execution. + + CPU test function. + 25. press 'TEST' in the UI. the program will load,execute,dump,compare. + 26. tail -f testlog.txt to see test status. + 27. the test will repeat until 'STOP TEST' is pressed. + 28. long test performed by allowing this mode to continue. + + UI exit function. + 29. press 'EXIT' in the UI. it will exit. +-----includes-----------------------------------//----------------------------*/ + #include "mbed.h" // general. + #include "USBSerial.h" // serial over USB. + #include "C12832_lcd.h" // LCD display. + #include "rtos.h" // RTOS. + #include "mmSPI.h" // SPI. +//---defines------------------------------------//------------------------------ + #define LCD1 lcd.locate(0, 0); // LCD line 1. + #define LCD2 lcd.locate(0,11); // LCD line 2. + #define LCD3 lcd.locate(0,22); // LCD line 3. + #define LCD3 lcd.locate(0,22); // LCD line 3. + #define SPI_BYTES 8 // number of SPI bytes. + #define SPI_HZ 100000 // SPI frequency in Hz. + #define SER_BYTES 8 // serial in/out # of bytes. + #define SER_ALIGN 7 // '$' location in shift-register. + #define THREAD_0_WAIT 8 // multitasking wait mS. + #define THREAD_1_WAIT 2 // multitasking wait mS. + #define THREAD_2_WAIT 128 // multitasking wait mS. + #define HB_MODULO 64 // heartbeat slowdown factor. + #define POOL_LEN 16 // memory pool dimension. + #define HCMD_SETREG 1 // host command 'set register'. + #define HCMD_GETREG 2 // host command 'get register'. + #define HCMD_SETMM 3 // host command 'set main-memory.' + #define HCMD_GETMM 4 // host command 'get main-memory.' + #define HCMD_STEP 5 // host command 'step-CPU'. + #define HCMD_SETIR 6 // host command 'set-IR'. + #define CPU_REG_0 0 // CPU register 0. + #define CPU_REG_1 1 // CPU register 1. + #define CPU_REG_2 2 // CPU register 2. + #define CPU_REG_3 3 // CPU register 3. + #define CPU_REG_PC 4 // CPU Program Counter. + #define CPU_IR_H 5 // CPU IR high-byte. + #define CPU_IR_L 6 // CPU IR low-byte. +//--global_definitions--------------------------//------------------------------ + struct tFromHost // command from host. + { + char cCommand; // command from host. + char cRegisterID; // which CPU register. + char cRegisterValue; // write this to CPU register. + char cIRValueH; // write this to IR. + char cIRValueL; // write this to IR. + char cMMaddress; // access this MM address. + char cMMdataH; // MM content high byte. + char cMMdataL; // MM content low byte. + }; + MemoryPool<tFromHost, POOL_LEN> mPoolFromHost; + Queue <tFromHost, POOL_LEN> qFromHost; + +//---- + + struct tToHost // reply to host. + { + char cCommand; // command executed. + char cRegisterID; // which CPU register read. + char cRegisterValue; // data from CPU register. + char cMMaddress; // which MM address read. + char cMMdataH; // MM content high byte. + char cMMdataL; // MM content low byte. + }; + MemoryPool<tToHost, POOL_LEN> mPoolToHost; + Queue <tToHost, POOL_LEN> qToHost; +//--global_variables----------------------------//------------------------------ + char gpcSerialFromHost[SER_BYTES]; // incoming serial buffer. + char gpcSerialToHost [SER_BYTES]; // outgoing serial buffer. + char gcNewCommand; // new command from host. + int gdRoundTrip; // +1 from host, -1 to host. + tToHost * gpToHost; // to-host structure. + osEvent gqToHostEvent; // incoming message event. + unsigned long gulSPIclkCount; // SPI clock count. + unsigned long gulCPUclkCount; // CPU clock count. +//--global_instances----------------------------//------------------------------ + USBSerial serial; // serial over usb. + C12832_LCD lcd; // LCD display. + DigitalOut led0(LED4); // thread heartbeat. + DigitalOut led1(LED3); // thread heartbeat. + DigitalOut led2(LED2); // thread heartbeat. + DigitalOut led3(LED1); // SPI reply underflow warning. +//-------prototypes-----------------------------//------------------------------ + int main(); // main. + void processIncomingSerial(); // process incoming host data. + void processOutgoingSerial(); // process outgoing data to host. + void SPIprocessingThread(void const *args); // SPI-side processing. + void diagnosticThread (void const *args); // LCD and LED notifications. + char ascii_nibble_to_binary(char cAscii); // ascii nibble -> binary. + char binary_to_ascii_nibble(char cBinary); // binary -> ascii nibble. + void clear_tFromHost(tFromHost *ptFromHost);// initialize structure. + void clear_tToHost (tToHost *ptToHost); // initialize structure. +//==============================================//============================== + int main(void) // USBSerial processing thread. + { + int dHeartbeat; // heartbeat counter. + int dLoop; // loop index. + + gpToHost = NULL; // initialize global. + gcNewCommand = 0; // initialize global. + gdRoundTrip = 1024; // initialize global. + gulSPIclkCount = 0; // initialize global. + gulCPUclkCount = 0; // initialize global. + dHeartbeat = 0; // initialize local. + dLoop = 0; // initialize local. + + // initialize serial-in shift-register. + for (dLoop = 0; dLoop < SER_BYTES; dLoop++) gpcSerialFromHost[dLoop] = 0; + + // thread-out SPI-side processing. + Thread thread_1(SPIprocessingThread,NULL,osPriorityHigh,DEFAULT_STACK_SIZE,NULL); + + // thread-out diagnostics. + Thread thread_2(diagnosticThread,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL); + + while(1) // main loop. + { + processIncomingSerial(); // process data in from host. + processOutgoingSerial(); // process data out to host. + + dHeartbeat++; // thread heartbeat. + if (!(dHeartbeat % HB_MODULO)) led0 = !led0; + Thread::wait(THREAD_0_WAIT); // multitasking. + } // main loop. + } // main. +/*----------------------------------------------//----------------------------*/ +/* + the python program running on the host is sending/receiving ascii characters + which represent command/data binary nibbles. the python program will send + the '$' character for command-string alignment. this function reads-in the + incoming serial stream when any serial data is available, into a shift-register, + and breaks upon detection of the '$' alignment character for python + command-processing. at that point the shift-register will look something like + [0] [1] [2] [3] [4] [5] [6] [7] + '1' '2' '3' '4' x x x '$' (means write 0x34 to CPU R2). + + + command-host register-number interpretation: + 0 = CPU R0. + 1 = CPU R1. + 2 = CPU R2. + 3 = CPU R3. + 4 = CPU program-counter. + 5 = CPU instruction-register high-byte. + 6 = CPU instruction-register low-byte. + + instruction-register write is specially implemented, + instruction-register read is implemented as two standard register-reads. + + host-command shift-register interpretation: + + gpcSerialFromHost[0] = command. + subsequent interpretation depends on the command. + +---- + if command = HCMD_SETREG (write-CPU-register) or HCMD_GETREG (read-CPU-register): + + gpcSerialFromHost[1] = register number (see above). + gpcSerialFromHost[2] = register content, high nibble. + gpcSerialFromHost[3] = register content, low nibble. + gpcSerialFromHost[4] = not used. + gpcSerialFromHost[5] = not used. + gpcSerialFromHost[6] = not used. + +---- + if command = HCMD_SETIR (write-CPU-instruction-register): + + gpcSerialFromHost[1] = IR register number, implied anyway. + gpcSerialFromHost[2] = IR write value high byte high nibble. + gpcSerialFromHost[3] = IR write value high byte low nibble. + gpcSerialFromHost[4] = IR write value low byte high nibble. + gpcSerialFromHost[5] = IR write value low byte low nibble. + gpcSerialFromHost[6] = not used. + +---- + if command = HCMD_SETMM (write to CPU main-memory) or HCMD_GETMM (read from CPU main-memory): + + gpcSerialFromHost[1] = MM address high nibble. + gpcSerialFromHost[2] = MM address low nibble. + gpcSerialFromHost[3] = MM content high byte high nibble. + gpcSerialFromHost[4] = MM content high byte low nibble. + gpcSerialFromHost[5] = MM content low byte high nibble. + gpcSerialFromHost[6] = MM content low byte low nibble. + + the above also applies to function 'processOutgoingSerial'. +*/ -int main() { - while(1) { - myled = 1; - wait(0.2); - myled = 0; - wait(0.2); - } -} + void processIncomingSerial(void) // process incoming serial data. + { + int dLoop; // loop index. + tFromHost * pFromHost; // from-host structure. + + while(serial.available()) // while data from host is available. + { + // shift-in the serial stream. + for (dLoop = 0; dLoop < (SER_BYTES - 1); dLoop++) + gpcSerialFromHost[dLoop] = gpcSerialFromHost[dLoop + 1]; + gpcSerialFromHost[SER_BYTES - 1] = serial._getc(); + + if (gpcSerialFromHost[SER_ALIGN] == '$')// data from host is aligned. + { + gcNewCommand = 1; // new host command just recognised. + break; // need to process aligned data. + } // data from host is aligned. + } // while data from host is available. + + // even if more data awaits from the + // incoming serial stream, we now need + // to process the aligned data recognised + // as a command from the host. + + if (gcNewCommand) // execute once per new command. + { + pFromHost = mPoolFromHost.alloc(); // allocate next pool entry. + if (!pFromHost) error("\n\r processIncomingSerial : FATAL malloc error for pFromHost. \n\r"); + clear_tFromHost(pFromHost); // initialize structure. + + // copy-in host message. + pFromHost->cCommand = ascii_nibble_to_binary(gpcSerialFromHost[0]); + + // host requests register access. + if (pFromHost->cCommand == HCMD_SETREG || pFromHost->cCommand == HCMD_GETREG) + { + pFromHost->cRegisterID = ascii_nibble_to_binary(gpcSerialFromHost[1]); + pFromHost->cRegisterValue = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) + + ascii_nibble_to_binary(gpcSerialFromHost[3]); + } + + + if (pFromHost->cCommand == HCMD_SETIR) // host requests IR write. + { + pFromHost->cIRValueH = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) + + ascii_nibble_to_binary(gpcSerialFromHost[3]); + pFromHost->cIRValueL = ((ascii_nibble_to_binary(gpcSerialFromHost[4])) << 4) + + ascii_nibble_to_binary(gpcSerialFromHost[5]); + } + + + // host requests main-memory access. + if (pFromHost->cCommand == HCMD_SETMM || pFromHost->cCommand == HCMD_GETMM) + { + pFromHost->cMMaddress = ((ascii_nibble_to_binary(gpcSerialFromHost[1])) << 4) + + ascii_nibble_to_binary(gpcSerialFromHost[2]); + pFromHost->cMMdataH = ((ascii_nibble_to_binary(gpcSerialFromHost[3])) << 4) + + ascii_nibble_to_binary(gpcSerialFromHost[4]); + pFromHost->cMMdataL = ((ascii_nibble_to_binary(gpcSerialFromHost[5])) << 4) + + ascii_nibble_to_binary(gpcSerialFromHost[6]); + } // host requests main-memory access. + + if (pFromHost->cCommand == HCMD_GETREG || pFromHost->cCommand == HCMD_GETMM) + gdRoundTrip++; // expected reply to host is pending. + qFromHost.put(pFromHost); // send out for processing. + gcNewCommand = 0; // don't execute until next new command. + } // execute once per new command. + Thread::wait(THREAD_0_WAIT); // multitasking. + } // processIncomingSerial +/*----------------------------------------------//----------------------------*/ + void processOutgoingSerial(void) // prepare/transmit data to host. + { + int dLoop; // loop index. + + gqToHostEvent = qToHost.get(1); // check for reply back to host. + + // if new reply to host: + if (gqToHostEvent.status == osEventMessage) + { + // bring it in from the queue. + gpToHost = (tToHost *) gqToHostEvent.value.p; + if (!gpToHost) error("\n\r processOutgoingSerial : FATAL null gpToHost pointer. \n\r"); + + // clear outgoing buffer. + for (dLoop = 0; dLoop < SER_BYTES; dLoop++) gpcSerialToHost[dLoop] = 0; + + // the commands from the host were + // looped-back into the to-host struct, + // make use of them here. + + if (gpToHost->cCommand == HCMD_GETREG) // reading from a register. + { + gpcSerialToHost[0] = binary_to_ascii_nibble( gpToHost->cCommand); + gpcSerialToHost[1] = binary_to_ascii_nibble( gpToHost->cRegisterID); + gpcSerialToHost[2] = binary_to_ascii_nibble(((gpToHost->cRegisterValue) >> 4) & 0x0F); + gpcSerialToHost[3] = binary_to_ascii_nibble(((gpToHost->cRegisterValue) >> 0) & 0x0F); + gpcSerialToHost[4] = '\n'; // signals end of transfer. + + // transmit to the host. + serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES); + gdRoundTrip--; // expected reply sent to host. + } + + if (gpToHost->cCommand == HCMD_GETMM) // reading from main-memory. + { + gpcSerialToHost[0] = binary_to_ascii_nibble( gpToHost->cCommand); + gpcSerialToHost[1] = binary_to_ascii_nibble(((gpToHost->cMMaddress) >> 4) & 0x0F); + gpcSerialToHost[2] = binary_to_ascii_nibble(((gpToHost->cMMaddress) >> 0) & 0x0F); + gpcSerialToHost[3] = binary_to_ascii_nibble(((gpToHost->cMMdataH ) >> 4) & 0x0F); + gpcSerialToHost[4] = binary_to_ascii_nibble(((gpToHost->cMMdataH ) >> 0) & 0x0F); + gpcSerialToHost[5] = binary_to_ascii_nibble(((gpToHost->cMMdataL ) >> 4) & 0x0F); + gpcSerialToHost[6] = binary_to_ascii_nibble(((gpToHost->cMMdataL ) >> 0) & 0x0F); + gpcSerialToHost[7] = '\n'; // signals end of transfer. + + // transmit to the host. + serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES); + gdRoundTrip--; // expected reply sent to host. + } + mPoolToHost.free(gpToHost); // done with this queue entry. + gpToHost = NULL; // clear pointer. + } // if new reply to host. + } // processOutgoingSerial. +/*----------------------------------------------//----------------------------*/ +// the pcSendBuffer and pcReceiveBuffer arrays are not used by this function, +// but they are declared by this function, and their pointers are passed +// down to the mmSPI library for its use of them. +// note- the prefix 'pc' means 'pointer of type character', not 'personal computer'. + + void SPIprocessingThread(void const *args) // process host-commands via SPI. + { + char pcSendBuffer [SPI_BYTES]; // SPI send buffer. + char pcReceiveBuffer[SPI_BYTES]; // SPI receive buffer. + int dHeartbeat; // heartbeat counter. + int dMemoryRead; // read main-memory into this. + int dLoop; // loop index. + osEvent qFromHostEvent; // incoming message event. + tFromHost * pFromHost; // message structure. + tToHost * gpToHost; // message structure. + mmSPI * pSPI; // SPI. + + pFromHost = NULL; // NULL pointers. + gpToHost = NULL; + pSPI = NULL; + dHeartbeat = 0; // clear variables. + dMemoryRead = 0; + dLoop = 0; + // clear SPI vectors. + for (dLoop = 0; dLoop < SPI_BYTES; dLoop++) pcSendBuffer [dLoop] = 0; + for (dLoop = 0; dLoop < SPI_BYTES; dLoop++) pcReceiveBuffer[dLoop] = 0; + + pSPI = new mmSPI; // SPI allocation. + if (!pSPI) error("\n\r SPIprocessingThread : FATAL malloc error for pSPI. \n\r"); + + pSPI->setSendBuffer (pcSendBuffer); // set SPI send buffer. + pSPI->setReceiveBuffer(pcReceiveBuffer); // set SPI receive buffer. + pSPI->setNumberOfBytes(SPI_BYTES); // set SPI number of bytes. + pSPI->setSPIfrequency (SPI_HZ); // set SPI clock frequency. + + while(1) // thread loop. + { + qFromHostEvent = qFromHost.get(1); // check for incoming host command. + + // if new host command: + if (qFromHostEvent.status == osEventMessage) + { + // bring it in from the queue. + pFromHost = (tFromHost *) qFromHostEvent.value.p; + if (!pFromHost) error("\n\r SPIprocessingThread : FATAL null pFromHost pointer. \n\r"); + + switch(pFromHost->cCommand) // host command decode. + { + case HCMD_SETREG : // set CPU register. + { + switch(pFromHost->cRegisterID) // which register to write to. + { + case CPU_REG_0 : {pSPI->write_register(CPU_REG_0 , pFromHost->cRegisterValue); break;} + case CPU_REG_1 : {pSPI->write_register(CPU_REG_1 , pFromHost->cRegisterValue); break;} + case CPU_REG_2 : {pSPI->write_register(CPU_REG_2 , pFromHost->cRegisterValue); break;} + case CPU_REG_3 : {pSPI->write_register(CPU_REG_3 , pFromHost->cRegisterValue); break;} + case CPU_REG_PC : {pSPI->write_register(CPU_REG_PC, pFromHost->cRegisterValue); break;} + default : {break;} + } // which register to write to. + break; + } // set CPU register. + + case HCMD_SETIR: // set instruction register. + { + pSPI->write_IR(pFromHost->cIRValueH, pFromHost->cIRValueL); + break; + } // set instruction register. + + case HCMD_GETREG : // get CPU register. + { + gpToHost = mPoolToHost.alloc(); // allocate next pool entry. + if (!gpToHost) error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r"); + clear_tToHost(gpToHost); // initialize structure. + + switch(pFromHost->cRegisterID) // which register to read from. + { + case CPU_REG_0 : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_0 ); break;} + case CPU_REG_1 : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_1 ); break;} + case CPU_REG_2 : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_2 ); break;} + case CPU_REG_3 : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_3 ); break;} + case CPU_REG_PC : {gpToHost->cRegisterValue = pSPI->read_register(CPU_REG_PC); break;} + case CPU_IR_H : {gpToHost->cRegisterValue = pSPI->read_register(CPU_IR_H ); break;} + case CPU_IR_L : {gpToHost->cRegisterValue = pSPI->read_register(CPU_IR_L ); break;} + default : {break;} + } // which register to read from. + + // loop-back to host. + gpToHost->cCommand = pFromHost->cCommand; + gpToHost->cRegisterID = pFromHost->cRegisterID; + + qToHost.put(gpToHost); // send up for processing. + break; + } // get CPU register. + + case HCMD_SETMM : // do main-memory write. + { + pSPI->write_memory(pFromHost->cMMdataH, pFromHost->cMMdataL, pFromHost->cMMaddress); + break; + } // do main-memory write. + + case HCMD_GETMM : // do main-memory read. + { + gpToHost = mPoolToHost.alloc(); // allocate next pool entry. + if (!gpToHost) error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r"); + clear_tToHost(gpToHost); // initialize structure. + + // read from CPU memory. + dMemoryRead = pSPI->read_memory(pFromHost->cMMaddress); + gpToHost->cMMdataH = (dMemoryRead >> 8) & 0xFF; + gpToHost->cMMdataL = (dMemoryRead >> 0) & 0xFF; + + // loop-back to host. + gpToHost->cCommand = pFromHost->cCommand; + gpToHost->cMMaddress = pFromHost->cMMaddress; + + qToHost.put(gpToHost); // send up for processing. + break; + } // do main-memory read. + + case HCMD_STEP : // step the CPU. + { + pSPI->step(); + break; + } // step the CPU. + default : break; + } // host command decode. + mPoolFromHost.free(pFromHost); // done with this queue entry. + pFromHost = NULL; // clear pointer. + } // if new host command. + + gulSPIclkCount = pSPI->SPIClockCount(); // propagate to global variable. + gulCPUclkCount = pSPI->CPUClockCount(); // propagate to global variable. + + // thread heartbeat. + dHeartbeat++; if (!(dHeartbeat % HB_MODULO)) led1 = !led1; + Thread::wait(THREAD_1_WAIT); // cooperative multitasking. + } // thread loop. + } // SPIprocessingThread. +/*----------------------------------------------//----------------------------*/ + void diagnosticThread(void const *args) // LCD and LED notifications. + { + int dHeartbeat; // heartbeat counter. + + dHeartbeat = 0; // initialize. + led3 = 0; // initialize. + + while(1) // thread loop. + { + // if message round trip + // count not consistent. + if (gdRoundTrip > 1025 || gdRoundTrip < 1024) led3 = 1; + + lcd.cls(); // clear LCD display. + LCD1; // lcd line 1. + lcd.printf(" USB DEV CLASS PROJECT"); + + LCD2; // lcd line 2. + lcd.printf(" %11lu = SPI clocks",gulSPIclkCount); + + LCD3; // lcd line 3. + lcd.printf(" %11lu = CPU clocks",gulCPUclkCount); + + dHeartbeat++; // thread heartbeat. + if (!(dHeartbeat % HB_MODULO)) led2 = !led2; + Thread::wait(THREAD_2_WAIT); // multitasking. + } // thread loop. + } // diagnosticThread. +/*----------------------------------------------//----------------------------*/ + char ascii_nibble_to_binary(char cAscii) // ascii nibble -> binary. + { + char cBinary; // converted value. + + switch(cAscii) + { + case 'F' : {cBinary = 15; break;} + case 'E' : {cBinary = 14; break;} + case 'D' : {cBinary = 13; break;} + case 'C' : {cBinary = 12; break;} + case 'B' : {cBinary = 11; break;} + case 'A' : {cBinary = 10; break;} + case 'f' : {cBinary = 15; break;} + case 'e' : {cBinary = 14; break;} + case 'd' : {cBinary = 13; break;} + case 'c' : {cBinary = 12; break;} + case 'b' : {cBinary = 11; break;} + case 'a' : {cBinary = 10; break;} + case '9' : {cBinary = 9; break;} + case '8' : {cBinary = 8; break;} + case '7' : {cBinary = 7; break;} + case '6' : {cBinary = 6; break;} + case '5' : {cBinary = 5; break;} + case '4' : {cBinary = 4; break;} + case '3' : {cBinary = 3; break;} + case '2' : {cBinary = 2; break;} + case '1' : {cBinary = 1; break;} + case '0' : {cBinary = 0; break;} + default : {cBinary = 0; break;} + } // switch(cAscii). + return(cBinary); // return the binary. + } // ascii_nibble_to_binary. +/*----------------------------------------------//----------------------------*/ + char binary_to_ascii_nibble(char cBinary) // binary -> ascii nibble. + { + char cAscii; // converted value. + + switch(cBinary) + { + case 15 : {cAscii = 'F'; break;} + case 14 : {cAscii = 'E'; break;} + case 13 : {cAscii = 'D'; break;} + case 12 : {cAscii = 'C'; break;} + case 11 : {cAscii = 'B'; break;} + case 10 : {cAscii = 'A'; break;} + case 9 : {cAscii = '9'; break;} + case 8 : {cAscii = '8'; break;} + case 7 : {cAscii = '7'; break;} + case 6 : {cAscii = '6'; break;} + case 5 : {cAscii = '5'; break;} + case 4 : {cAscii = '4'; break;} + case 3 : {cAscii = '3'; break;} + case 2 : {cAscii = '2'; break;} + case 1 : {cAscii = '1'; break;} + case 0 : {cAscii = '0'; break;} + default : {cAscii = '0'; break;} + } // switch(cBinary). + return(cAscii); // return the binary. + } // binary_to_ascii_nibble. +/*----------------------------------------------//----------------------------*/ + void clear_tFromHost(tFromHost * ptFromHost)// clear structure. + { + ptFromHost->cCommand = 0x00; + ptFromHost->cRegisterID = 0x00; + ptFromHost->cRegisterValue = 0x00; + ptFromHost->cIRValueH = 0x00; + ptFromHost->cIRValueL = 0x00; + ptFromHost->cMMaddress = 0x00; + ptFromHost->cMMdataH = 0x00; + ptFromHost->cMMdataL = 0x00; + } // clear_tFromHost. +/*----------------------------------------------//----------------------------*/ + void clear_tToHost(tToHost * ptToHost) // clear structure. + { + ptToHost->cCommand = 0x00; + ptToHost->cRegisterID = 0x00; + ptToHost->cRegisterValue = 0x00; + ptToHost->cMMaddress = 0x00; + ptToHost->cMMdataH = 0x00; + ptToHost->cMMdataL = 0x00; + } // clear_tToHost. +/*----------------------------------------------//----------------------------*/ \ No newline at end of file