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.
mmSPI.cpp
00001 /*----------------------------------------------//------------------------------ 00002 student : m-moore 00003 email : gated.clock@gmail.com 00004 class : usb device drivers 00005 directory : USB_device_project/mmSPI 00006 file : mmSPI.cpp 00007 date : september 3, 2013. 00008 ----copyright-----------------------------------//------------------------------ 00009 licensed for personal and academic use. 00010 commercial use must be approved by the account-holder of 00011 gated.clock@gmail.com 00012 ----description---------------------------------//------------------------------ 00013 this library provides the low-level SPI data and clock signaling 00014 for communication with the altera development board, via four i/o 00015 pins available on the mbed development board's zigbee header. 00016 00017 this library also provides higher-level calls to send/receive register 00018 and main-memory data to/from the CPU implemented in the altera. 00019 00020 the SPI clock and the CPU clock are both built by this library. 00021 00022 the SPI clock (*pSCLK) is generated with quarter-phase resolution, 00023 although at the moment, only half-phase resolution is being used. 00024 00025 within the FPGA, the SPI scan registers are called 'shadow registers' 00026 because they parallel load-to/store-from the CPU registers. 00027 00028 character vectors pcSend and pcReceive are used to scan-in and scan-out 00029 to/from the altera shadow registers. note that the prefix 'pc' means 00030 'pointer-of-type-character' and not 'personal-computer'. 00031 00032 the shadow registers in the altera are arranged as follows: 00033 00034 <-- MISO (altera-out to mbed-in) 00035 ^ 00036 | 00037 scan_08 U14_spi_control = pcSend/pcReceive[7] 00038 scan_08 U08_shadowR0 = pcSend/pcReceive[6] 00039 scan_08 U09_shadowR1 = pcSend/pcReceive[5] 00040 scan_08 U10_shadowR2 = pcSend/pcReceive[4] 00041 scan_08 U11_shadowR3 = pcSend/pcReceive[3] 00042 scan_08 U12_shadowPC = pcSend/pcReceive[2] 00043 scan_16 U13_shadowIR = pcSend/pcReceive[1:0] 00044 ^ 00045 | 00046 --> MOSI (altera-in from mbed-out) 00047 00048 when U14_spi_control<1> is high, the mbed takes over CPU operation. 00049 this means that the CPU instruction decoder decodes U13_shadowIR 00050 rather than the CPU's actual IR. 00051 00052 when U14_spi_control<2> is high, the mbed writes into the IR. 00053 this means that the CPU instruction decoder load-enable outputs 00054 are gated-off so that the mbed may write into the instrucition 00055 register without immediate consequence in the CPU. writing to 00056 the IR this way is more for the ability to show that it can be done, 00057 rather than any likely useful purpose. 00058 00059 debug/test: 00060 this library was debugged by using the altera FPGA logic analyzer 00061 known as 'signal tap'. the main program uses all of the memory and 00062 register access calls, which were used as an informal unit test. 00063 -----includes-----------------------------------//----------------------------*/ 00064 #include "mmSPI.h" 00065 //==============================================//============================== 00066 mmSPI::mmSPI() // constructor. 00067 { 00068 allocations(); // object allocations. 00069 00070 *pSCLK = 0; // initialize. 00071 *pCPUclk = 0; // initialize. 00072 ulSPIclkCount = 0; // initialize. 00073 ulCPUclkCount = 0; // initialize. 00074 } // constructor. 00075 //----------------------------------------------//------------------------------ 00076 mmSPI::~mmSPI() // destructor. 00077 { 00078 // deallocations. 00079 if (pMOSI) {delete pMOSI; pMOSI = NULL;} 00080 if (pMISO) {delete pMISO; pMISO = NULL;} 00081 if (pSCLK) {delete pSCLK; pSCLK = NULL;} 00082 if (pCPUclk) {delete pCPUclk; pCPUclk = NULL;} 00083 } // destructor. 00084 //----------------------------------------------//------------------------------ 00085 void mmSPI::allocations(void) // object allocations. 00086 { 00087 pMOSI = new DigitalOut(mmSPI_MOSI); // SPI MOSI pin object. 00088 if (!pMOSI) error("\n\r mmSPI::allocations : FATAL malloc error for pMOSI. \n\r"); 00089 00090 pMISO = new DigitalOut(mmSPI_MISO); // SPI MISO pin object. 00091 if (!pMISO) error("\n\r mmSPI::allocations : FATAL malloc error for pMISO. \n\r"); 00092 00093 pSCLK = new DigitalOut(mmSPI_SCLK); // SPI SCLK pin object. 00094 if (!pSCLK) error("\n\r mmSPI::allocations : FATAL malloc error for pSCLK. \n\r"); 00095 00096 pCPUclk = new DigitalOut(mmCPU_CLK); // SPI SCLK pin object. 00097 if (!pCPUclk) error("\n\r mmSPI::allocations : FATAL malloc error for pCPUclk. \n\r"); 00098 } // allocations. 00099 //----------------------------------------------//------------------------------ 00100 void mmSPI::setSPIfrequency(float fFreq) // set SPI clock frequency. 00101 { 00102 fSPIfreq = fFreq; // promote to object scope. 00103 if (fSPIfreq < .05) // don't get near divide-by-zero. 00104 error("\n\r mmSPI::setSPIfrequency : FATAL SPI frequency set too low. \n\r"); 00105 fSPIquarterP = (1 / fSPIfreq) / 4; // figure quarter-cycle period. 00106 } 00107 //----------------------------------------------//------------------------------ 00108 // obtain SPI send buffer pointer. 00109 void mmSPI::setSendBuffer(char * pcSendBuffer) 00110 { 00111 pcSend = pcSendBuffer; // promote to object scope. 00112 } // setSendBuffer. 00113 //----------------------------------------------//------------------------------ 00114 // obtain SPI receive buffer pointer. 00115 void mmSPI::setReceiveBuffer(char * pcReceiveBuffer) 00116 { 00117 pcReceive = pcReceiveBuffer; // promote to object scope. 00118 } // setReceiveBuffer. 00119 //----------------------------------------------//------------------------------ 00120 // obtain number of SPI bytes. 00121 void mmSPI::setNumberOfBytes(int dNumberOfBytes) 00122 { 00123 dNumBytes = dNumberOfBytes; // promote to object scope. 00124 } // setNumberOfBytes. 00125 //----------------------------------------------//------------------------------ 00126 /* 00127 this method has four 'components' which may be switched on/off by the caller: 00128 1. cPreCPU = 1 means cycle the altera CPU clock once. 00129 2. cPreSPI = 1 means cycle the SPI clock once. 00130 3. cScan = 1 means run a full SPI scan-in/scan-out cycle. 00131 4. cPostCPU = 1 means cycle the alter CPU clock once, then the SPI clock once. 00132 00133 in the altera, upon the falling edge of any CPU clock, a load-enable is 00134 asserted into the scan registers, such that at the next rising edge of 00135 the SPI clock, the scan registers perform a parallel-load of the CPU 00136 registers, so that that data can subsequently be scanned back into the mbed 00137 via a full SPI scan-in cycle. (this load-enable is turned-off upon the first 00138 falling edge of the subsequent SPI clock.) 00139 therefore, the regular input switches to this method are (1,1,1,0). 00140 this means that the altera CPU clock will cycle, 00141 and the result of that CPU operation will be parallel-loaded into the 00142 shadow registers for scan into the mbed on the subsequent full-scan cycle. 00143 00144 a finer level of sequencing these 'components' is needed by the 'step' method, 00145 which is why they are under four-input-parameter control. 00146 */ 00147 void mmSPI::transceive_vector(char cPreCPU, char cPreSPI, char cScan, char cPostCPU) 00148 { 00149 int dClear; // clear-loop index. 00150 int dIndex; // total scan index. 00151 int dMosiByteIndex; // SPI-MOSI byte index. 00152 int dMosiBitIndex; // SPI-MOSI bit index. 00153 int dMisoByteIndex; // SPI-MISO byte index. 00154 int dMisoBitIndex; // SPI-MISO bit index. 00155 00156 // initializations. these are needed. 00157 dIndex = (dNumBytes * 8) - 1; // calculate scan count. 00158 dMosiByteIndex = dIndex / 8; // calculate current byte index. 00159 dMosiBitIndex = dIndex % 8; // calculate current bit iindex. 00160 00161 // clear the SPI receive vector. 00162 for (dClear = 0; dClear < dNumBytes; dClear++) pcReceive[dClear] = 0; 00163 //--- 00164 if (cPreCPU) // if pre-CPU clock. 00165 { 00166 *pCPUclk = 1; // pulse the CPU clock. 00167 wait(fSPIquarterP); 00168 wait(fSPIquarterP); 00169 *pCPUclk = 0; 00170 wait(fSPIquarterP); 00171 wait(fSPIquarterP); 00172 ulCPUclkCount++; 00173 } // if pre-CPU clock. 00174 //--- 00175 if (cPreSPI) // if pre-SPI pulse. 00176 { 00177 *pSCLK = 1; // pulse the SPI clock for parallel load. 00178 wait(fSPIquarterP); 00179 wait(fSPIquarterP); 00180 *pSCLK = 0; 00181 ulSPIclkCount++; 00182 } // if pre-SPI pulse. 00183 //--- 00184 if (cScan) // if cScan. 00185 { 00186 // pre-assert MOSI. 00187 *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1; 00188 wait(fSPIquarterP); 00189 wait(fSPIquarterP); 00190 00191 00192 // main SPI scan loop. 00193 for (dIndex = (dNumBytes * 8) - 1; dIndex >= 0; dIndex--) 00194 { 00195 dMisoByteIndex = dIndex / 8; 00196 dMisoBitIndex = dIndex % 8; 00197 pcReceive[dMisoByteIndex] = pcReceive[dMisoByteIndex] | (*pMISO << dMisoBitIndex); 00198 *pSCLK = 1; 00199 wait(fSPIquarterP); 00200 wait(fSPIquarterP); 00201 *pSCLK = 0; 00202 if (dIndex < 0) dIndex = 0; 00203 dMosiByteIndex = (dIndex - 1) / 8; 00204 dMosiBitIndex = (dIndex - 1) % 8; 00205 *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1; 00206 wait(fSPIquarterP); 00207 wait(fSPIquarterP); 00208 ulSPIclkCount++; 00209 } // main SPI scan loop. 00210 } // if cScan. 00211 //--- 00212 if (cPostCPU) // if post-CPU pulse. 00213 { 00214 *pCPUclk = 1; // pulse the CPU clock. 00215 wait(fSPIquarterP); 00216 wait(fSPIquarterP); 00217 *pCPUclk = 0; 00218 wait(fSPIquarterP); 00219 wait(fSPIquarterP); 00220 ulCPUclkCount++; 00221 00222 *pSCLK = 1; // clear-out the SPI parallel-load enable. 00223 wait(fSPIquarterP); 00224 wait(fSPIquarterP); 00225 *pSCLK = 0; 00226 ulSPIclkCount++; 00227 } // if post-CPU pulse. 00228 } // transceive_vector. 00229 //----------------------------------------------//------------------------------ 00230 /* 00231 cRegister -> CPU_register 00232 0 R0 00233 1 R1 00234 2 R2 00235 3 R3 00236 4 PC 00237 5 <use write_IR instead> 00238 6 <nothing> 00239 7 <nothing> 00240 00241 in order to write to a CPU register, pcSend[7] is set to 0x02, 00242 which informs the CPU instruction decoder to decode what we are 00243 here scanning into the shadow instruction-register, rather than 00244 the CPU instruction decoder decoding the regular instruction-register. 00245 here, we set-up pcSend to scan the instruction 00246 'load Rx with immediate data yy', and this is executed on the 00247 subsequent CPU clock. 00248 00249 said another way, this method writes a value into a CPU register 00250 by effectively taking over the CPU and doing a write-register-immediate. 00251 */ 00252 00253 // write to a CPU register. 00254 void mmSPI::write_register(char cRegister, char cValue) 00255 { 00256 clear_transmit_vector(); // clear transmit vector. 00257 00258 pcSend[7] = 0x02; // mbed sends a command. 00259 00260 // align into instruction word. 00261 pcSend[1] = ((cRegister & 0x07) << 2) | 0xA0; 00262 pcSend[0] = cValue & 0xFF; // immediate value to i.w. 00263 00264 transceive_vector(1,1,1,0); // transmit command. 00265 00266 clear_transmit_vector(); // clear transmit vector. 00267 } // write_register. 00268 //----------------------------------------------//------------------------------ 00269 /* 00270 for writing to the instruction-register, this method is used. 00271 for reading from the instruction-register, the 'regular' 00272 read_register method is used, once for each of the two IR data bytes. 00273 00274 unlike method 'write_register', this method works by gating-off the 00275 outputs of the CPU instruction decoder, and having the instruction-register 00276 content loaded in from the shadow instruction-register. 00277 */ 00278 // write instruction register. 00279 void mmSPI::write_IR(char cValueH, char cValueL) 00280 { 00281 clear_transmit_vector(); // clear transmit vector. 00282 00283 pcSend[7] = 0x06; // mbed sends a command. 00284 00285 pcSend[1] = (cValueH & 0xFF); // load IR shadow with new IR content. 00286 pcSend[0] = (cValueL & 0xFF); 00287 00288 transceive_vector(1,1,1,0); // transmit command. 00289 00290 clear_transmit_vector(); // clear transmit vector. 00291 } // write instruction register. 00292 //----------------------------------------------//------------------------------ 00293 /* 00294 cRegister -> CPU_register pcReceive 00295 0 -> R0 [6] 00296 1 -> R1 [5] 00297 2 -> R2 [4] 00298 3 -> R3 [3] 00299 4 -> PC [2] 00300 5 -> IR-H [1] 00301 6 -> IR-L [0] 00302 7 -> <never-use> 00303 00304 this method works by taking over the instruction decoder as described above, 00305 but not issuing any instruction, but the scan registers will parallel-load 00306 all of the CPU register data and scan them into pcReceive, where they are 00307 picked up below. 00308 */ 00309 00310 char mmSPI::read_register(char cRegister) // return content of a CPU register. 00311 { 00312 clear_transmit_vector(); // clear transmit vector. 00313 00314 pcSend[7] = 0x02; // suppress cpu operation. 00315 00316 transceive_vector(1,1,1,0); // snap & scan-out reg contents. 00317 00318 return (pcReceive[6 - cRegister]); // return the particular reg value. 00319 } // read_register. 00320 //----------------------------------------------//------------------------------ 00321 /* 00322 this method works by taking over the instruction decoder, and issuing 00323 the CPU commands which load the MM address/data into {R3,R2,R1} 00324 followed by issuing a write-pulse. 00325 */ 00326 // write a word to main-memory. 00327 void mmSPI::write_memory(char cHData, char cLdata, char cAddress) 00328 { 00329 clear_transmit_vector(); // clear transmit vector. 00330 00331 write_register(0x03,cAddress); // R3 <- address. 00332 write_register(0x02,cHData); // R2 <- high-data. 00333 write_register(0x01,cLdata); // R1 <- low-data. 00334 00335 pcSend[7] = 0x02; // mbed sends command. 00336 pcSend[1] = 0x02; // write-enable high. 00337 pcSend[0] = 0x00; // remainder of instruction. 00338 transceive_vector(1,1,1,0); 00339 00340 pcSend[7] = 0x02; // mbed sends command. 00341 pcSend[1] = 0x00; // write-enable low. 00342 pcSend[0] = 0x00; // remainder of instruction. 00343 transceive_vector(1,1,1,0); 00344 00345 clear_transmit_vector(); // clear transmit vector. 00346 } // write_memory. 00347 //----------------------------------------------//------------------------------ 00348 /* 00349 this command works by taking over the instruction decoder, loading 00350 R3 with the MM address, then issuing load-from-MM commands so that 00351 R2,R1 are loaded with MM[R3], and then reading the result from 00352 R1,R2. 00353 */ 00354 // fetch a word from main memory. 00355 unsigned int mmSPI::read_memory(char cAddress) 00356 { 00357 unsigned int udMemoryContent; // return variable. 00358 char cHData; // returned data-high. 00359 char cLData; // returned data-low. 00360 00361 clear_transmit_vector(); // clear transmit vector. 00362 00363 write_register(0x03,cAddress); // R3 <= address. 00364 00365 pcSend[7] = 0x02; // mbed sends command. 00366 pcSend[1] = 0xC8; // R2 <- MM[R3] 00367 pcSend[0] = 0x00; 00368 transceive_vector(1,1,1,0); // send command. 00369 00370 pcSend[7] = 0x02; // mbed sends command. 00371 pcSend[1] = 0xC4; // R1 <- MM[R3] 00372 pcSend[0] = 0x00; 00373 transceive_vector(1,1,1,0); // send command. 00374 00375 cHData = read_register(0x02); // obtain MM high-data-byte. 00376 cLData = read_register(0x01); // obtain MM low-data-byte. 00377 00378 udMemoryContent = (cHData << 8) + cLData; // build the memory word. 00379 00380 clear_transmit_vector(); // clear transmit vector. 00381 00382 return udMemoryContent; // return the memory word. 00383 } // read_memory. 00384 //----------------------------------------------//------------------------------ 00385 void mmSPI::step(void) // step the CPU. 00386 { 00387 clear_transmit_vector(); // clear transmit vector. 00388 transceive_vector(0,0,1,0); // enable CPU mode. 00389 transceive_vector(0,0,0,1); // advance CPU, clear shadow-load. 00390 pcSend[7] = 0x02; // ready to disable CPU mode. 00391 transceive_vector(0,0,1,0); // disable CPU mode. 00392 } // step. 00393 //----------------------------------------------//------------------------------ 00394 void mmSPI::clear_transmit_vector(void) // fill transmit buffer with 0. 00395 { 00396 int dLoop; 00397 for (dLoop = 0; dLoop < dNumBytes; dLoop++) pcSend[dLoop] = 0x00; 00398 } // clear_transmit_vector. 00399 //----------------------------------------------//------------------------------ 00400 // getters. 00401 unsigned long mmSPI::SPIClockCount() {return ulSPIclkCount;} 00402 unsigned long mmSPI::CPUClockCount() {return ulCPUclkCount;} 00403 //----------------------------------------------//------------------------------ 00404 00405 00406 00407 00408 00409 00410 00411
Generated on Sun Jul 24 2022 14:03:04 by
1.7.2