Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: RTOS_project RTOS_project_fork_01 RTOS_project_fork_02
Revision 0:7b8e6b90c874, committed 2013-09-17
- Comitter:
- gatedClock
- Date:
- Tue Sep 17 20:40:03 2013 +0000
- Commit message:
- update.
Changed in this revision
mmSPI_RTOS.cpp | Show annotated file Show diff for this revision Revisions of this file |
mmSPI_RTOS.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r 7b8e6b90c874 mmSPI_RTOS.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmSPI_RTOS.cpp Tue Sep 17 20:40:03 2013 +0000 @@ -0,0 +1,442 @@ +/*----------------------------------------------//------------------------------ + student : m-moore + email : gated.clock@gmail.com + class : embedded RTOS + directory : URTOS_project/mmSPI_RTOS + file : mmSPI_RTOS.cpp + date : september 19, 2013. +----copyright-----------------------------------//------------------------------ + licensed for personal and academic use. + commercial use of original code must be approved by the account-holder of + gated.clock@gmail.com +----description---------------------------------//------------------------------ + this library provides the low-level SPI data and clock signaling + for communication with the altera development board, via four i/o + pins available on the mbed development board's zigbee header. + + this library also provides higher-level calls to send/receive register + and main-memory data to/from the CPU implemented in the altera. + + the SPI clock and the CPU clock are both built by this library. + + the SPI clock (*pSCLK) is generated with quarter-phase resolution, + although at the moment, only half-phase resolution is being used. + + within the FPGA, the SPI scan registers are called 'shadow registers' + because they parallel load-to/store-from the CPU registers. + + character vectors pcSend and pcReceive are used to scan-in and scan-out + to/from the altera shadow registers. note that the prefix 'pc' means + 'pointer-of-type-character' and not 'personal-computer'. + + the shadow registers in the altera are arranged as follows: + + <-- MISO (altera-out to mbed-in) + ^ + | + scan_08 U14_spi_control = pcSend/pcReceive[7] + scan_08 U08_shadowR0 = pcSend/pcReceive[6] + scan_08 U09_shadowR1 = pcSend/pcReceive[5] + scan_08 U10_shadowR2 = pcSend/pcReceive[4] + scan_08 U11_shadowR3 = pcSend/pcReceive[3] + scan_08 U12_shadowPC = pcSend/pcReceive[2] + scan_16 U13_shadowIR = pcSend/pcReceive[1:0] + ^ + | + --> MOSI (altera-in from mbed-out) + + when U14_spi_control<1> is high, the mbed takes over CPU operation. + this means that the CPU instruction decoder decodes U13_shadowIR + rather than the CPU's actual IR. + + when U14_spi_control<2> is high, the mbed writes into the IR. + this means that the CPU instruction decoder load-enable outputs + are gated-off so that the mbed may write into the instrucition + register without immediate consequence in the CPU. writing to + the IR this way is more for the ability to show that it can be done, + rather than any likely useful purpose. + + debug/test: + this library was debugged by using the altera FPGA logic analyzer + known as 'signal tap'. the main program uses all of the memory and + register access calls, which were used as an informal unit test. +-----includes-----------------------------------//----------------------------*/ + #include "mmSPI_RTOS.h" +//==============================================//============================== + mmSPI_RTOS::mmSPI_RTOS() // constructor. + { + allocations(); // object allocations. + + *pSCLK = 0; // initialize. + *pCPUclk = 0; // initialize. + ulSPIclkCount = 0; // initialize. + ulCPUclkCount = 0; // initialize. + } // constructor. +//----------------------------------------------//------------------------------ + mmSPI_RTOS::~mmSPI_RTOS() // destructor. + { + // deallocations. + if (pMOSI) {delete pMOSI; pMOSI = NULL;} + if (pMISO) {delete pMISO; pMISO = NULL;} + if (pSCLK) {delete pSCLK; pSCLK = NULL;} + if (pCPUclk) {delete pCPUclk; pCPUclk = NULL;} + } // destructor. +//----------------------------------------------//------------------------------ + void mmSPI_RTOS::allocations(void) // object allocations. + { + pMOSI = new DigitalOut(mmSPI_RTOS_MOSI);// SPI MOSI pin object. + if (!pMOSI) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pMOSI. \n\r"); + + pMISO = new DigitalOut(mmSPI_RTOS_MISO);// SPI MISO pin object. + if (!pMISO) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pMISO. \n\r"); + + pSCLK = new DigitalOut(mmSPI_RTOS_SCLK);// SPI SCLK pin object. + if (!pSCLK) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pSCLK. \n\r"); + + pCPUclk = new DigitalOut(mmCPU_CLK); // SPI SCLK pin object. + if (!pCPUclk) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pCPUclk. \n\r"); + } // allocations. +//----------------------------------------------//------------------------------ + // set SPI clock frequency. + void mmSPI_RTOS::setSPIfrequency(float fFreq) + { + fSPIfreq = fFreq; // promote to object scope. + if (fSPIfreq < .05) // don't get near divide-by-zero. + error("\n\r mmSPI_RTOS::setSPIfrequency : FATAL SPI frequency set too low. \n\r"); + fSPIquarterP = (1 / fSPIfreq) / 4; // figure quarter-cycle period. + } +//----------------------------------------------//------------------------------ + // obtain SPI send buffer pointer. + void mmSPI_RTOS::setSendBuffer(char * pcSendBuffer) + { + pcSend = pcSendBuffer; // promote to object scope. + } // setSendBuffer. +//----------------------------------------------//------------------------------ + // obtain SPI receive buffer pointer. + void mmSPI_RTOS::setReceiveBuffer(char * pcReceiveBuffer) + { + pcReceive = pcReceiveBuffer; // promote to object scope. + } // setReceiveBuffer. +//----------------------------------------------//------------------------------ + // obtain number of SPI bytes. + void mmSPI_RTOS::setNumberOfBytes(int dNumberOfBytes) + { + dNumBytes = dNumberOfBytes; // promote to object scope. + } // setNumberOfBytes. +//----------------------------------------------//------------------------------ +/* + this method has four 'components' which may be switched on/off by the caller: + 1. cPreCPU = 1 means cycle the altera CPU clock once. + 2. cPreSPI = 1 means cycle the SPI clock once. + 3. cScan = 1 means run a full SPI scan-in/scan-out cycle. + 4. cPostCPU = 1 means cycle the alter CPU clock once, then the SPI clock once. + + in the altera, upon the falling edge of any CPU clock, a load-enable is + asserted into the scan registers, such that at the next rising edge of + the SPI clock, the scan registers perform a parallel-load of the CPU + registers, so that that data can subsequently be scanned back into the mbed + via a full SPI scan-in cycle. (this load-enable is turned-off upon the first + falling edge of the subsequent SPI clock.) + therefore, the regular input switches to this method are (1,1,1,0). + this means that the altera CPU clock will cycle, + and the result of that CPU operation will be parallel-loaded into the + shadow registers for scan into the mbed on the subsequent full-scan cycle. + + a finer level of sequencing these 'components' is needed by the 'step' method, + which is why they are under four-input-parameter control. + */ + void mmSPI_RTOS::transceive_vector(char cPreCPU, char cPreSPI, char cScan, char cPostCPU) + { + int dClear; // clear-loop index. + int dIndex; // total scan index. + int dMosiByteIndex; // SPI-MOSI byte index. + int dMosiBitIndex; // SPI-MOSI bit index. + int dMisoByteIndex; // SPI-MISO byte index. + int dMisoBitIndex; // SPI-MISO bit index. + + // initializations. these are needed. + dIndex = (dNumBytes * 8) - 1; // calculate scan count. + dMosiByteIndex = dIndex / 8; // calculate current byte index. + dMosiBitIndex = dIndex % 8; // calculate current bit iindex. + + // clear the SPI receive vector. + for (dClear = 0; dClear < dNumBytes; dClear++) pcReceive[dClear] = 0; +//--- + if (cPreCPU) // if pre-CPU clock. + { + *pCPUclk = 1; // pulse the CPU clock. + wait(fSPIquarterP); + wait(fSPIquarterP); + *pCPUclk = 0; + wait(fSPIquarterP); + wait(fSPIquarterP); + ulCPUclkCount++; + } // if pre-CPU clock. + //--- + if (cPreSPI) // if pre-SPI pulse. + { + *pSCLK = 1; // pulse the SPI clock for parallel load. + wait(fSPIquarterP); + wait(fSPIquarterP); + *pSCLK = 0; + ulSPIclkCount++; + } // if pre-SPI pulse. + //--- + if (cScan) // if cScan. + { + // pre-assert MOSI. + *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1; + wait(fSPIquarterP); + wait(fSPIquarterP); + + + // main SPI scan loop. + for (dIndex = (dNumBytes * 8) - 1; dIndex >= 0; dIndex--) + { + dMisoByteIndex = dIndex / 8; + dMisoBitIndex = dIndex % 8; + pcReceive[dMisoByteIndex] = pcReceive[dMisoByteIndex] | (*pMISO << dMisoBitIndex); + *pSCLK = 1; + wait(fSPIquarterP); + wait(fSPIquarterP); + *pSCLK = 0; + if (dIndex < 0) dIndex = 0; + dMosiByteIndex = (dIndex - 1) / 8; + dMosiBitIndex = (dIndex - 1) % 8; + *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1; + wait(fSPIquarterP); + wait(fSPIquarterP); + ulSPIclkCount++; + } // main SPI scan loop. + } // if cScan. + //--- + if (cPostCPU) // if post-CPU pulse. + { + *pCPUclk = 1; // pulse the CPU clock. + wait(fSPIquarterP); + wait(fSPIquarterP); + *pCPUclk = 0; + wait(fSPIquarterP); + wait(fSPIquarterP); + ulCPUclkCount++; + + *pSCLK = 1; // clear-out the SPI parallel-load enable. + wait(fSPIquarterP); + wait(fSPIquarterP); + *pSCLK = 0; + ulSPIclkCount++; + } // if post-CPU pulse. + } // transceive_vector. +//----------------------------------------------//------------------------------ +/* + cRegister -> CPU_register + 0 R0 + 1 R1 + 2 R2 + 3 R3 + 4 PC + 5 <use write_IR instead> + 6 <nothing> + 7 <nothing> + + in order to write to a CPU register, pcSend[7] is set to 0x02, + which informs the CPU instruction decoder to decode what we are + here scanning into the shadow instruction-register, rather than + the CPU instruction decoder decoding the regular instruction-register. + here, we set-up pcSend to scan the instruction + 'load Rx with immediate data yy', and this is executed on the + subsequent CPU clock. + + said another way, this method writes a value into a CPU register + by effectively taking over the CPU and doing a write-register-immediate. +*/ + + // write to a CPU register. + void mmSPI_RTOS::write_register(char cRegister, char cValue) + { + clear_transmit_vector(); // clear transmit vector. + + pcSend[7] = 0x02; // mbed sends a command. + + // align into instruction word. + pcSend[1] = ((cRegister & 0x07) << 2) | 0xA0; + pcSend[0] = cValue & 0xFF; // immediate value to i.w. + + transceive_vector(1,1,1,0); // transmit command. + + clear_transmit_vector(); // clear transmit vector. + } // write_register. +//----------------------------------------------//------------------------------ +/* + for writing to the instruction-register, this method is used. + for reading from the instruction-register, the 'regular' + read_register method is used, once for each of the two IR data bytes. + + unlike method 'write_register', this method works by gating-off the + outputs of the CPU instruction decoder, and having the instruction-register + content loaded in from the shadow instruction-register. +*/ + // write instruction register. + void mmSPI_RTOS::write_IR(char cValueH, char cValueL) + { + clear_transmit_vector(); // clear transmit vector. + + pcSend[7] = 0x06; // mbed sends a command. + + pcSend[1] = (cValueH & 0xFF); // load IR shadow with new IR content. + pcSend[0] = (cValueL & 0xFF); + + transceive_vector(1,1,1,0); // transmit command. + + clear_transmit_vector(); // clear transmit vector. + } // write instruction register. +//----------------------------------------------//------------------------------ +/* + cRegister -> CPU_register pcReceive + 0 -> R0 [6] + 1 -> R1 [5] + 2 -> R2 [4] + 3 -> R3 [3] + 4 -> PC [2] + 5 -> IR-H [1] + 6 -> IR-L [0] + 7 -> <never-use> + + this method works by taking over the instruction decoder as described above, + but not issuing any instruction, but the scan registers will parallel-load + all of the CPU register data and scan them into pcReceive, where they are + picked up below. +*/ + // return content of a CPU register. + char mmSPI_RTOS::read_register(char cRegister) + { + clear_transmit_vector(); // clear transmit vector. + + pcSend[7] = 0x02; // suppress cpu operation. + + transceive_vector(1,1,1,0); // snap & scan-out reg contents. + + return (pcReceive[6 - cRegister]); // return the particular reg value. + } // read_register. +//----------------------------------------------//------------------------------ +//----------------------------------------------//------------------------------ +/* + since the Tk UI often wants to obtain the content of all of the CPU registers, + this method may be used to obtain all of them at once, speading up the UI + read cycle. + + pRegisters[6] <- pcReceive[6] = R0 + pRegisters[5] <- pcReceive[5] = R1 + pRegisters[4] <- pcReceive[4] = R2 + pRegisters[3] <- pcReceive[3] = R3 + pRegisters[2] <- pcReceive[2] = PC + pRegisters[1] <- pcReceive[1] = IR-H + pRegisters[0] <- pcReceive[0] = IR-L +*/ + + void mmSPI_RTOS::read_all_registers(char * pcRegisters) + { + int dLoop; // loop index. + + clear_transmit_vector(); // clear transmit vector. + + pcSend[7] = 0x02; // suppress cpu operation. + + transceive_vector(1,1,1,0); // snap & scan-out reg contents. + + // propagate register data. + for (dLoop = 0; dLoop < 7; dLoop++) pcRegisters[dLoop] = pcReceive[dLoop]; + + } // read_all_registers. +//----------------------------------------------//------------------------------ +/* + this method works by taking over the instruction decoder, and issuing + the CPU commands which load the MM address/data into {R3,R2,R1} + followed by issuing a write-pulse. +*/ + // write a word to main-memory. + void mmSPI_RTOS::write_memory(char cHData, char cLdata, char cAddress) + { + clear_transmit_vector(); // clear transmit vector. + + write_register(0x03,cAddress); // R3 <- address. + write_register(0x02,cHData); // R2 <- high-data. + write_register(0x01,cLdata); // R1 <- low-data. + + pcSend[7] = 0x02; // mbed sends command. + pcSend[1] = 0x02; // write-enable high. + pcSend[0] = 0x00; // remainder of instruction. + transceive_vector(1,1,1,0); + + pcSend[7] = 0x02; // mbed sends command. + pcSend[1] = 0x00; // write-enable low. + pcSend[0] = 0x00; // remainder of instruction. + transceive_vector(1,1,1,0); + + clear_transmit_vector(); // clear transmit vector. + } // write_memory. +//----------------------------------------------//------------------------------ +/* + this command works by taking over the instruction decoder, loading + R3 with the MM address, then issuing load-from-MM commands so that + R2,R1 are loaded with MM[R3], and then reading the result from + R1,R2. +*/ + // fetch a word from main memory. + unsigned int mmSPI_RTOS::read_memory(char cAddress) + { + unsigned int udMemoryContent; // return variable. + char cHData; // returned data-high. + char cLData; // returned data-low. + + clear_transmit_vector(); // clear transmit vector. + + write_register(0x03,cAddress); // R3 <= address. + + pcSend[7] = 0x02; // mbed sends command. + pcSend[1] = 0xC8; // R2 <- MM[R3] + pcSend[0] = 0x00; + transceive_vector(1,1,1,0); // send command. + + pcSend[7] = 0x02; // mbed sends command. + pcSend[1] = 0xC4; // R1 <- MM[R3] + pcSend[0] = 0x00; + transceive_vector(1,1,1,0); // send command. + + cHData = read_register(0x02); // obtain MM high-data-byte. + cLData = read_register(0x01); // obtain MM low-data-byte. + + udMemoryContent = (cHData << 8) + cLData; // build the memory word. + + clear_transmit_vector(); // clear transmit vector. + + return udMemoryContent; // return the memory word. + } // read_memory. +//----------------------------------------------//------------------------------ + void mmSPI_RTOS::step(void) // step the CPU. + { + clear_transmit_vector(); // clear transmit vector. + transceive_vector(0,0,1,0); // enable CPU mode. + transceive_vector(0,0,0,1); // advance CPU, clear shadow-load. + pcSend[7] = 0x02; // ready to disable CPU mode. + transceive_vector(0,0,1,0); // disable CPU mode. + } // step. +//----------------------------------------------//------------------------------ + void mmSPI_RTOS::clear_transmit_vector(void)// fill transmit buffer with 0. + { + int dLoop; + for (dLoop = 0; dLoop < dNumBytes; dLoop++) pcSend[dLoop] = 0x00; + } // clear_transmit_vector. +//----------------------------------------------//------------------------------ + // getters. + unsigned long mmSPI_RTOS::SPIClockCount() {return ulSPIclkCount;} + unsigned long mmSPI_RTOS::CPUClockCount() {return ulCPUclkCount;} +//----------------------------------------------//------------------------------ + + + + + + + +
diff -r 000000000000 -r 7b8e6b90c874 mmSPI_RTOS.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmSPI_RTOS.h Tue Sep 17 20:40:03 2013 +0000 @@ -0,0 +1,76 @@ +#ifndef mmSPI_RTOS_H // include guard. +#define mmSPI_RTOS_H // include guard. +/*----------------------------------------------//------------------------------ + student : m-moore + email : gated.clock@gmail.com + class : embedded RTOS + directory : RTOS_project/mmSPI_RTOS + file : mmSPI_RTOS.h + date : september 19, 2013. +----copyright-----------------------------------//------------------------------ + licensed for personal and academic use. + commercial use of original code must be approved by the account-holder of + gated.clock@gmail.com +----description---------------------------------//------------------------------ + this library provides the low-level SPI data and clock signaling + for communication with the altera development board, via four i/o + pins available on the mbed development board's zigbee header. +----notes---------------------------------------//------------------------------ +------------------------------------------------//----------------------------*/ + #include "mbed.h" // standard mbed.org class. +//---defines------------------------------------//------------------------------ + #define mmSPI_RTOS_MOSI p29 // SPI interface pin. + #define mmSPI_RTOS_MISO p30 // SPI interface pin. + #define mmSPI_RTOS_SCLK p9 // SPI interface pin. + #define mmCPU_CLK p10 // soft CPU system clock. +//==============================================//============================== + class mmSPI_RTOS + { + public: + mmSPI_RTOS(); // constructor. + ~mmSPI_RTOS(); // destructor. + void allocations(); // object allocations. + + void setSPIfrequency (float); // initializations. + void setSendBuffer (char * pcSendBuffer); + void setReceiveBuffer(char * pcReceiveBuffer); + void setNumberOfBytes(int dNumberOfBytes); + + // SPI transceive loop. + void transceive_vector(char cPreCPU, char cPreSPI, char cScan, char cPostCPU); + + // write/read CPU registers. + void write_register (char cRegister, char cValue); + void write_IR (char cValueH, char cValueL); + char read_register (char cRegister); + void read_all_registers(char * pcRegisters); + + // write/read CPU main-memory. + void write_memory(char cHData, char cLdata, char cAddress); + unsigned int read_memory (char cAddress); + + void step(); // step the CPU. + + void clear_transmit_vector(); // fill with 0. + + unsigned long SPIClockCount(); // return SPI clock count. + unsigned long CPUClockCount(); // return CPU clock count. + + private: + + DigitalOut * pMOSI; // SPI pin. + DigitalOut * pMISO; // SPI pin. + DigitalOut * pSCLK; // SPI pin. + DigitalOut * pCPUclk; // soft cpu clock. + char * pcSend; // SPI transmit vector. + char * pcReceive; // SPI receive vector. + float fSPIfreq; // SPI clock frequency. + float fSPIquarterP; // SPI quarter period. + int dNumBytes; // number of SPI bytes. + int dLoop01; // loop index. + int dLoop02; // loop index. + unsigned long ulSPIclkCount; // SPI clock count. + unsigned long ulCPUclkCount; // CPU clock count. + }; +//----------------------------------------------//------------------------------ +#endif // include guard. \ No newline at end of file