Sample SPI drivers for a number of the Adesto Technologies flash devices.
spi_driver.c
Go to the documentation of this file.
1 /*
2  * The Clear BSD License
3  * Copyright (c) 2018 Adesto Technologies Corporation, Inc
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without modification,
7  * are permitted (subject to the limitations in the disclaimer below) provided
8  * that the following conditions are met:
9  *
10  * o Redistributions of source code must retain the above copyright notice, this list
11  * of conditions and the following disclaimer.
12  *
13  * o Redistributions in binary form must reproduce the above copyright notice, this
14  * list of conditions and the following disclaimer in the documentation and/or
15  * other materials provided with the distribution.
16  *
17  * o Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived from this
19  * software without specific prior written permission.
20  *
21  * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE.
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
25  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
26  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
41 #include "spi_driver.h"
42 
43 void SPI_PinInit(uint32_t port, uint32_t pin, enum directionIO direction)
44 {
45  USER_CONFIG_PinInit(port, pin, direction);
46 }
47 void SPI_PinClear(uint32_t port, uint32_t pin)
48 {
49  USER_CONFIG_PinClear(port, pin);
50 }
51 void SPI_PinSet(uint32_t port, uint32_t pin)
52 {
53  USER_CONFIG_PinSet(port, pin);
54 }
55 
56 uint32_t SPI_PinRead(uint32_t port, uint32_t pin)
57 {
58  return USER_CONFIG_PinRead(port, pin);
59 }
60 
62 {
63  /* Configure each of the 4 pins needed for testing. */
64  // CSb - PTD4 for Moneta shield, PTC1 for Dataflash shield
67  // SCK - PTD1
69  // MOSI - PTD2
71  // MISO - PTD3
73  // HOLDb - PTD4
75  // WPb - PTC5
77  // Trigger - PTC2
80  // Set both WPb and HOLDb to high.
83 }
84 
86 {
87  // MOSI - PTD2
89  // MISO - PTD3
91  // HOLDb - PTD4
93  // WPb - PTC5
95 
96  // Set both WPb and HOLDb to high.
99 }
100 
102 {
103  // MOSI - PTD2
106 }
107 
109 {
110  // MOSI - PTD2
111  // MISO - PTD3
113  // MOSI - PTD2
115  // HOLDb - PTD4
117  // WPb - PTC5
119 }
120 
122 {
123  // MISO - PTD3
126 }
127 
129 {
130  // MISO - PTD3
132  // MOSI - PTD2
134  // HOLDb - PTD4
136  // WPb - PTC5
138 }
139 
140 void SPI_Delay(uint32_t delayTime)
141 {
142  volatile uint32_t i = 0;
143  for (i = 0; i < delayTime; ++i)
144  {
145  __asm("NOP"); /* delay */
146  }
147 }
148 
150 {
151  SPI_Delay(DELAY);
153  SPI_Delay(DELAY);
155 }
156 
157 void SPI_SendBit(uint8_t transmittedBit)
158 {
159  // Guarantee clock is set to low
161  // Set MOSI based on transmittedBit
162  if(transmittedBit)
164  else
166  // Toggle clock
168 }
169 
170 void SPI_SendByte(uint8_t transmittedByte)
171 {
172  int32_t i = 0;
173  // Send byte
174  for(i = 7; i >= 0; i--)
175  {
176  SPI_SendBit((transmittedByte >> i) & 1);
177  }
178 }
179 
180 void SPI_DualSendByte(uint8_t transmittedByte)
181 {
182  int32_t i = 0;
183  uint8_t bit_tx = 0;
184  // Send byte
185  for(i = 7; i >= 1; i-= 2)
186  {
187  // Guarantee clock is set to low
189 
190  // Send MSB first
191  bit_tx = (transmittedByte >> i) & 1;
192  // Set MOSI based on bit_tx
193  if(bit_tx)
195  else
197 
198  // Send lower bit
199  bit_tx = (transmittedByte >> (i-1)) & 1;
200  // Set MOSI based on bit_tx
201  if(bit_tx)
203  else
205 
206  // Toggle clock
208  }
209 }
210 
211 void SPI_QuadSendByte(uint8_t transmittedByte)
212 {
213  int32_t i = 0;
214  uint8_t bit_tx = 0;
215  // Send byte
216  for(i = 7; i >= 3; i-= 4)
217  {
218  // Guarantee clock is set to low
220 
221  // Send MSB first
222  bit_tx = (transmittedByte >> i) & 1;
223  if(bit_tx)
225  else
227 
228  bit_tx = (transmittedByte >> (i-1)) & 1;
229  if(bit_tx)
231  else
233 
234  bit_tx = (transmittedByte >> (i-2)) & 1;
235  if(bit_tx)
237  else
239 
240  bit_tx = (transmittedByte >> (i-3)) & 1;
241  if(bit_tx)
243  else
245 
246  // Toggle clock
248  }
249 }
250 
252 {
253  int32_t i = 0;
254  uint8_t input = 0;
255 
256  // Receive byte
257  for(i = 7; i >= 0; i--)
258  {
260  input |= (1 << i);
261  else
262  input &= ~(1 << i);
264  }
265  return input;
266 }
267 
269 {
270  int32_t i = 0;
271  uint8_t input = 0;
272 
273  // Receive dual byte
274  for(i = 7; i >= 1; i-=2)
275  {
276  // Store MSB first, then lower bit
278  input |= (1 << i);
279  else
280  input &= ~(1 << i);
281 
283  input |= (1 << (i-1));
284  else
285  input &= ~(1 << (i-1));
286 
288  }
289  return input;
290 }
291 
293 {
294  int32_t i = 0;
295  uint8_t input = 0;
296 
297  // Receive dual byte
298  for(i = 7; i >= 3; i-=4)
299  {
300  // Store MSB first, then lower bit
302  input |= (1 << i);
303  else
304  input &= ~(1 << i);
305 
307  input |= (1 << (i-1));
308  else
309  input &= ~(1 << (i-1));
310 
312  input |= (1 << (i-2));
313  else
314  input &= ~(1 << (i-2));
315 
317  input |= (1 << (i-3));
318  else
319  input &= ~(1 << (i-3));
321  }
322  return input;
323 }
324 
325 void SPI_Exchange(uint8_t *txBuffer,
326  uint32_t txNumBytes,
327  uint8_t *rxBuffer,
328  uint32_t rxNumBytes,
329  uint32_t dummyNumBytes)
330 {
331  uint32_t i = 0;
332  // Begin data exchange
333  // Set clock to low
335  // Select chip
337 
338  // Send each byte
339  for(i = 0; i < txNumBytes; i = i+1)
340  SPI_SendByte(txBuffer[i]);
341  // Receive each byte
342  for(i = 0; i < dummyNumBytes; i = i+1)
343  SPI_ReceiveByte();
344  // Receive each byte
345  for(i = 0; i < rxNumBytes; i = i+1)
346  rxBuffer[i] = SPI_ReceiveByte();
347 
348  // End data exchange
349  // Set clock to low
351  // Deselect chip
353 }
354 
355 void SPI_DualExchange(uint8_t standardSPINumBytes,
356  uint8_t *txBuffer,
357  uint32_t txNumBytes,
358  uint8_t *rxBuffer,
359  uint32_t rxNumBytes,
360  uint32_t dummyNumBytes)
361 {
362  uint32_t i = 0;
363  // Begin data exchange
364  // Set clock to low
366  // Select chip
368 
369  // Transmit bytes in standard single SPI
370  for(i = 0; i < standardSPINumBytes; i = i+1)
371  SPI_SendByte(txBuffer[i]);
372  // Transmit dummy bytes in standard single SPI mode
373  for(i = 0; i < dummyNumBytes; i = i+1)
374  SPI_ReceiveByte();
375  // If we're sending data to the flash device then configure for dual tx
376  // and transmit the remaining data in dual SPI mode.
377  if(standardSPINumBytes <= txNumBytes)
378  {
380  for(i = standardSPINumBytes; i < txNumBytes; i = i+1)
381  {
382  SPI_DualSendByte(txBuffer[i]);
383  }
384  }
385  // Receive each byte
386  // If we're receiving at least 1 byte, then configure the outputs for dual rx
387  // and receive the data in dual SPI mode.
388  if(rxNumBytes > 0)
389  {
391  for(i = 0; i < rxNumBytes; i = i+1)
392  {
393  rxBuffer[i] = SPI_DualReceiveByte();
394  }
395  }
396 
397  // End data exchange
398  // Set clock to low
400  // Deselect chip
402  // Reconfigure device for standard SPI operation
404 }
405 
406 void SPI_QuadExchange(uint8_t standardSPINumBytes,
407  uint8_t *txBuffer,
408  uint32_t txNumBytes,
409  uint8_t *rxBuffer,
410  uint32_t rxNumBytes,
411  uint32_t dummyNumBytes)
412 {
413  uint32_t i = 0;
414  // Determines if dummy bytes should be sent in standard SPI, or another mode.
415  uint8_t dummyBytesSSPI = 1;
416  // Begin data exchange
417  // Set clock to low
419  // Select chip
421 
422  // Transmit standardSPINumBytes bytes in standard single SPI.
423  // Unless in QPI mode, a transmission will take the 1-4-4 or
424  // 1-1-4 format, the 1 indicating SPI and the 4 indicating Quad.
425  // -- This assumes that the device is configured in single SPI.
426  for(i = 0; i < standardSPINumBytes; i = i+1)
427  SPI_SendByte(txBuffer[i]);
428  // If we're sending data to the flash device then configure for quad tx
429  // and transmit the remaining data in quad SPI mode.
430  if(standardSPINumBytes < txNumBytes)
431  {
432  dummyBytesSSPI = 0;
434  for(i = standardSPINumBytes; i < txNumBytes; i = i+1)
435  {
436  SPI_QuadSendByte(txBuffer[i]);
437  }
438  }
439  // Transmit the dummy bytes.
440  if(dummyNumBytes > 0)
441  {
442  // Need this in case it was all standardSPI. This puts the WP, HOLD, SO, SI pins in
443  // a high impedance state which is necessary for quad IO.
444  // Quad output just needs HOLD, WP, and SO to be Hi-Z.
445  // Quad IO needs all 4 to be Hi-Z.
447  if(dummyBytesSSPI == 1)
448  {
449  // Transmit dummy bytes in SSPI. (Really just clock toggling)
450  for(i = 0; i < dummyNumBytes; i = i+1)
451  {
452  SPI_ReceiveByte();
453  }
454  }
455  else
456  {
457  // Transmit dummy bytes in QSPI.
458  for(i = 0; i < dummyNumBytes; i = i+1)
459  {
461  }
462  }
463  }
464  // Receive each byte
465  // If we're receiving at least 1 byte, then configure the outputs for quad rx
466  // and receive the data in quad SPI mode.
467  if(rxNumBytes > 0)
468  {
470  for(i = 0; i < rxNumBytes; i = i+1)
471  {
472  rxBuffer[i] = SPI_QuadReceiveByte();
473  }
474  }
475 
476  // End data exchange
477  // Set clock to low
479  // Deselect chip
481  // Reconfigure device for standard SPI operation
483 }
484 
486 {
488  SPI_Delay(DELAY);
490 }
491 
493 {
494  // Clear CSb
496  // Clear MOSI
498  SPI_Delay(DELAY);
499 
500  // Set CSb
502  // Set MOSI
504  SPI_Delay(DELAY);
505 
506  // Clear CSb
508  SPI_Delay(DELAY);
509 
510  // Set CSb
512  // Clear MOSI
514  SPI_Delay(DELAY);
515 
516  // Clear CSb
518  SPI_Delay(DELAY);
519 
520  // Set CSb
522  // Set MOSI
524  SPI_Delay(DELAY);
525 
526  // Clear CSb
528  SPI_Delay(DELAY);
529 
530  // Set CSb
532  SPI_Delay(DELAY);
533 }
void SPI_ClockTick()
Toggles the clock: current_state->high->low.
Definition: spi_driver.c:149
void SPI_JEDECReset()
Performs a JEDEC reset on the SPI device.
Definition: spi_driver.c:492
#define SPI_SCK_PIN
Pin number for SCK.
Definition: spi_driver.h:98
#define SPI_MOSI_PIN
Pin number for MOSI.
Definition: spi_driver.h:100
#define SPI_WPB_PIN
Pin number for WPb - IO2.
Definition: spi_driver.h:104
void SPI_QuadExchange(uint8_t standardSPINumBytes, uint8_t *txBuffer, uint32_t txNumBytes, uint8_t *rxBuffer, uint32_t rxNumBytes, uint32_t dummyNumBytes)
Sends and receives bytes based on the function parameters. MOSI can used for the opcode and address...
Definition: spi_driver.c:406
uint32_t SPI_PinRead(uint32_t port, uint32_t pin)
Reads the voltage on a given pin.
Definition: spi_driver.c:56
void SPI_PinClear(uint32_t port, uint32_t pin)
Clears a given pin on a port to LOW.
Definition: spi_driver.c:47
uint8_t SPI_DualReceiveByte()
Receives a byte along both MOSI and MISO and returns the value received.
Definition: spi_driver.c:268
#define SPI_WPB_PORT
Base register used for WPb control.
Definition: spi_driver.h:81
uint8_t SPI_QuadReceiveByte()
Receives a byte along MOSI, MISO, HOLDb, and WPb and returns the value received.
Definition: spi_driver.c:292
#define DELAY
Half clock period delay interval.
Definition: spi_driver.h:108
void SPI_PinSet(uint32_t port, uint32_t pin)
Sets a given pin on a port to HIGH.
Definition: spi_driver.c:51
void SPI_ConfigureDualSPIIOsInput()
Configures SPIO IOs for dual input. This changes the MOSI pin from an output to an input so that the ...
Definition: spi_driver.c:101
#define SPI_MISO_PORT
Base register used for MISO control.
Definition: spi_driver.h:77
#define SPI_CSB_PORT
Base register used for CSb control.
Definition: spi_driver.h:61
void SPI_SendBit(uint8_t transmittedBit)
Sends a single bit along MOSI while toggling the clock.
Definition: spi_driver.c:157
void SPI_SendByte(uint8_t transmittedByte)
Sends a byte along MOSI.
Definition: spi_driver.c:170
#define SPI_TRIGGER_PIN
Pin number for TRIGGER.
Definition: spi_driver.h:96
void SPI_ConfigureQuadSPIIOsOutput()
Configures SPIO IOs for quad output. This changes the MISO pin to an output so that the master can dr...
Definition: spi_driver.c:128
void SPI_Trigger()
Triggers a falling edge on the SPI_TRIGGER_PORT/PIN output.
Definition: spi_driver.c:485
void SPI_QuadSendByte(uint8_t transmittedByte)
Sends a byte along MISO, MOSI, WPb, and HOLDb.
Definition: spi_driver.c:211
void SPI_DualSendByte(uint8_t transmittedByte)
Sends a byte along both MOSI and MISO.
Definition: spi_driver.c:180
void SPI_ConfigureQuadSPIIOsInput()
Configures SPIO IOs for quad input. This changes the MOSI, WPb, and HOLDb pins to inputs so that the ...
Definition: spi_driver.c:108
void SPI_ReturnToSingleSPIIOs()
Returns MISO and MOSI pins to their standard SPI state as an input and output.
Definition: spi_driver.c:85
#define SPI_HOLDB_PORT
Base register used for HOLDb control.
Definition: spi_driver.h:85
void USER_CONFIG_PinInit(uint32_t port, uint32_t pin, enum directionIO direction)
Initializes a given pin as either an input or output. When porting, this function must be redefined...
Definition: user_config.c:44
#define SPI_CSB_PIN
Definition: spi_driver.h:94
Declarations of spi_driver functions.
#define SPI_MOSI_PORT
Base register used for MOSI control.
Definition: spi_driver.h:73
directionIO
Enumeration used when initializing pins as inputs or outputs. OUTPUT = 0, INPUT = 1...
Definition: user_config.h:205
void SPI_DualExchange(uint8_t standardSPINumBytes, uint8_t *txBuffer, uint32_t txNumBytes, uint8_t *rxBuffer, uint32_t rxNumBytes, uint32_t dummyNumBytes)
Sends and receives bytes based on the function parameters. MOSI is used for the opcode and address...
Definition: spi_driver.c:355
#define SPI_HOLDB_PIN
Pin number for HOLDb - IO3.
Definition: spi_driver.h:106
void SPI_Delay(uint32_t delayTime)
Performs a delayTime number of NOPs.
Definition: spi_driver.c:140
void USER_CONFIG_PinClear(uint32_t port, uint32_t pin)
Clears a given pin on a port to LOW.
Definition: user_config.c:64
uint8_t SPI_ReceiveByte()
Receives a byte along MISO and returns the value received.
Definition: spi_driver.c:251
void SPI_PinInit(uint32_t port, uint32_t pin, enum directionIO direction)
Initializes a given pin as either an input or output.
Definition: spi_driver.c:43
uint8_t USER_CONFIG_PinRead(uint32_t port, uint32_t pin)
Reads the voltage on a given pin.
Definition: user_config.c:76
#define SPI_TRIGGER_PORT
Base register used for TRIGGER control.
Definition: spi_driver.h:65
void SPI_ConfigureDualSPIIOsOutput()
Configures SPIO IOs for dual output. This changes the MISO pin from an input to an output so that the...
Definition: spi_driver.c:121
void SPI_ConfigureSingleSPIIOs()
Configure the IOs for SPI bit banging usage. 4 pins are needed: CSb, SCK, MOSI, MISO.
Definition: spi_driver.c:61
#define SPI_MISO_PIN
Pin number for MISO.
Definition: spi_driver.h:102
void SPI_Exchange(uint8_t *txBuffer, uint32_t txNumBytes, uint8_t *rxBuffer, uint32_t rxNumBytes, uint32_t dummyNumBytes)
Sends and receives bytes based on the function parameters. MISO and MOSI fill their standard SPI role...
Definition: spi_driver.c:325
#define SPI_SCK_PORT
Base register used for SCK control.
Definition: spi_driver.h:69
void USER_CONFIG_PinSet(uint32_t port, uint32_t pin)
Sets a given pin on a port to HIGH.
Definition: user_config.c:70