Skyward boardcore
Loading...
Searching...
No Matches
SPIBus.h
Go to the documentation of this file.
1/* Copyright (c) 2021 Skyward Experimental Rocketry
2 * Authors: Luca Erbetta, Alberto Nidasio
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#pragma once
24
25#include <assert.h>
26#include <interfaces/delays.h>
27#include <utils/ClockUtils.h>
28
29#include "SPIBusInterface.h"
30
31#ifndef USE_MOCK_PERIPHERALS
32using SPIType = SPI_TypeDef;
33#else
34#include <utils/TestUtils/FakeSpiTypedef.h>
35using SPIType = Boardcore::FakeSpiTypedef;
36#endif
37
38namespace Boardcore
39{
40
60class SPIBus : public SPIBusInterface
61{
62public:
63 SPIBus(SPIType* spi);
64
66 SPIBus(const SPIBus&) = delete;
67 SPIBus& operator=(const SPIBus&) = delete;
68 SPIBus(SPIBus&&) = delete;
69 SPIBus& operator=(SPIBus&&) = delete;
70
74 SPIType* getSpi();
75
79 void reset();
80
84 void enable();
85
89 void disable();
90
91#ifdef _ARCH_CORTEXM7_STM32F7
96 void set8bitRXNE();
97
102 void set16bitRXNE();
103#endif
104
105 void set8BitFrameFormat();
106
108
110
112
114
115 void setBitOrder(SPI::Order bitOrder);
116
117 void setClockDiver(SPI::ClockDivider divider);
118
120
122
123 void setMode(SPI::Mode mode);
124
125 void enableTxDMARequest();
126
127 void disableTxDMARequest();
128
129 void enableRxDMARequest();
130
131 void disableRxDMARequest();
132
133 void waitPeripheral();
134
135 void flushRxBuffer();
136
145 void configure(SPIBusConfig newConfig) override;
146
150 void select(GpioType cs) override;
151
155 void deselect(GpioType cs) override;
156
157 // Read, write and transfer operations
158
164 uint8_t read() override;
165
171 uint16_t read16() override;
172
178 uint32_t read24() override;
179
185 uint32_t read32() override;
186
193 void read(uint8_t* data, size_t size) override;
194
201 void read16(uint16_t* data, size_t size) override;
202
208 void write(uint8_t data) override;
209
215 void write16(uint16_t data) override;
216
222 void write24(uint32_t data) override;
223
229 void write32(uint32_t data) override;
230
237 void write(const uint8_t* data, size_t size) override;
238
245 void write16(const uint16_t* data, size_t size) override;
246
253 uint8_t transfer(uint8_t data) override;
254
261 uint16_t transfer16(uint16_t data) override;
262
269 uint32_t transfer24(uint32_t data) override;
270
277 uint32_t transfer32(uint32_t data) override;
278
285 void transfer(uint8_t* data, size_t size) override;
286
293 void transfer16(uint16_t* data, size_t size) override;
294
295private:
296 SPIType* spi;
297 SPIBusConfig config{};
298 bool firstConfigApplied = false;
299};
300
301inline SPIBus::SPIBus(SPIType* spi) : spi(spi)
302{
304}
305
306inline SPIType* SPIBus::getSpi() { return spi; }
307
308inline void SPIBus::reset()
309{
310 spi->CR1 = 0;
311 spi->CR2 = 0;
312 spi->DR = 0;
313 spi->RXCRCR = 0;
314 spi->TXCRCR = 0;
315}
316
317inline void SPIBus::enable() { spi->CR1 |= SPI_CR1_SPE; }
318
319inline void SPIBus::disable() { spi->CR1 &= ~SPI_CR1_SPE; }
320
330#ifndef _ARCH_CORTEXM7_STM32F7
331
332inline void SPIBus::set8BitFrameFormat() { spi->CR1 &= ~SPI_CR1_DFF; }
333
334#else
335
336inline void SPIBus::set8bitRXNE() { spi->CR2 |= SPI_CR2_FRXTH; }
337
338inline void SPIBus::set8BitFrameFormat()
339{
340 spi->CR2 &= ~SPI_CR2_DS;
341 set8bitRXNE();
342}
343
344#endif
345
346inline void SPIBus::enableSoftwareSlaveManagement() { spi->CR1 |= SPI_CR1_SSM; }
347
349{
350 spi->CR1 &= ~SPI_CR1_SSM;
351}
352
353inline void SPIBus::enableInternalSlaveSelection() { spi->CR1 |= SPI_CR1_SSI; }
354
356{
357 spi->CR1 &= ~SPI_CR1_SSI;
358}
359
360inline void SPIBus::setBitOrder(SPI::Order bitOrder)
361{
362 // First clear the configuration
363 spi->CR1 &= ~SPI_CR1_LSBFIRST;
364
365 // Set the new value
366 spi->CR1 |= static_cast<uint32_t>(bitOrder);
367}
368
370{
371 // First clear the configuration
372 spi->CR1 &= ~SPI_CR1_BR;
373
374 // Set the new value
375 spi->CR1 |= static_cast<uint32_t>(divider);
376}
377
378inline void SPIBus::setSlaveConfiguration() { spi->CR1 &= ~SPI_CR1_MSTR; }
379
380inline void SPIBus::setMasterConfiguration() { spi->CR1 |= SPI_CR1_MSTR; }
381
382inline void SPIBus::setMode(SPI::Mode mode)
383{
384 // First clear the configuration
385 spi->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA);
386
387 // Set the new value
388 spi->CR1 |= static_cast<uint32_t>(mode);
389}
390
391inline void SPIBus::enableTxDMARequest() { spi->CR2 |= SPI_CR2_TXDMAEN; }
392
393inline void SPIBus::disableTxDMARequest() { spi->CR2 &= ~SPI_CR2_TXDMAEN; }
394
395inline void SPIBus::enableRxDMARequest() { spi->CR2 |= SPI_CR2_RXDMAEN; }
396
397inline void SPIBus::disableRxDMARequest() { spi->CR2 &= ~SPI_CR2_RXDMAEN; }
398
400{
401 while ((spi->SR & SPI_SR_TXE) == 0)
402 ;
403 while ((spi->SR & SPI_SR_BSY) > 0)
404 ;
405}
406
408{
409 while ((spi->SR & SPI_SR_RXNE) != 0)
410 spi->DR;
411}
412
413inline void SPIBus::configure(SPIBusConfig newConfig)
414{
415 // Do not reconfigure if already in the correct configuration.
416 if (!firstConfigApplied || newConfig != config)
417 {
418 // Save the new configuration
419 config = newConfig;
420 firstConfigApplied = true;
421
422 // Wait until the peripheral is done before changing configuration
424
425 // Disable the peripheral
426 disable();
427
428 // Configure clock polarity and phase
429 setMode(config.mode);
430
431 // Configure clock frequency
433
434 // Configure bit order
435 setBitOrder(config.bitOrder);
436
437 // Configure chip select and master mode
441
443
444 // Enable the peripheral
445 enable();
446 }
447}
448
450{
451 cs.low();
452
453 if (config.csSetupTimeUs > 0)
454 miosix::delayUs(config.csSetupTimeUs);
455}
456
458{
459 if (config.csHoldTimeUs > 0)
460 miosix::delayUs(config.csHoldTimeUs);
461
462 cs.high();
463}
464
465inline uint8_t SPIBus::read() { return transfer(0); }
466
467inline uint16_t SPIBus::read16() { return transfer16(0); }
468
469inline uint32_t SPIBus::read24() { return transfer24(0); }
470
471inline uint32_t SPIBus::read32() { return transfer32(0); }
472
473inline void SPIBus::read(uint8_t* data, size_t nBytes)
474{
475 for (size_t i = 0; i < nBytes; i++)
476 data[i] = read();
477}
478
479inline void SPIBus::read16(uint16_t* data, size_t nBytes)
480{
481 // nBytes to be read must be a multiple of 2
482 assert(nBytes % 2 == 0);
483
484 uint16_t temp[2] = {0};
485 for (size_t i = 0; i < nBytes / 2; i++)
486 {
487 // Receiving MSB
488 temp[0] = static_cast<uint16_t>(read());
489 // Receiving LSB
490 temp[1] = static_cast<uint16_t>(read());
491
492 // MSB | LSB
493 data[i] = temp[0] << 8 | temp[1];
494 }
495}
496
497inline void SPIBus::write(uint8_t data) { transfer(data); }
498
499inline void SPIBus::write16(uint16_t data) { transfer16(data); }
500
501inline void SPIBus::write24(uint32_t data) { transfer24(data); }
502
503inline void SPIBus::write32(uint32_t data) { transfer32(data); }
504
505inline void SPIBus::write(const uint8_t* data, size_t nBytes)
506{
507 for (size_t i = 0; i < nBytes; i++)
508 transfer(data[i]);
509}
510
511inline void SPIBus::write16(const uint16_t* data, size_t nBytes)
512{
513 // nBytes to be read must be a multiple of 2
514 assert(nBytes % 2 == 0);
515
516 for (size_t i = 0; i < nBytes / 2; i++)
517 {
518 // Sending MSB
519 write(static_cast<uint8_t>(data[i] >> 8));
520 // Sending LSB
521 write(static_cast<uint8_t>(data[i]));
522 }
523}
524
525inline uint8_t SPIBus::transfer(uint8_t data)
526{
527 /*
528 * On STM32F7xx and STM32F4xx series chips, on SPI3 only, the RXNE flag
529 * may be erroneously set at the beginning of the transaction with the
530 * RX buffer containing garbage data.
531 * On F7xx chips the issue can be reproduced by re-configuring the SPI from
532 * Mode 0 (CPOL=0, CPHA=0) to Mode 3 (CPOL=1, CPHA=1), after performing at
533 * least one transaction in Mode 0.
534 *
535 * We work around this issue by flushing the RX buffer at the beginning of
536 * the transaction.
537 */
539
540 // Wait until the peripheral is ready to transmit
541 while ((spi->SR & SPI_SR_TXE) == 0)
542 ;
543
544 // Write the data item to transmit
545 *(volatile uint8_t*)&spi->DR = static_cast<uint8_t>(data);
546
547 // Make sure transmission is complete
548 while ((spi->SR & SPI_SR_TXE) == 0)
549 ;
550 while ((spi->SR & SPI_SR_BSY) > 0)
551 ;
552
553 // Wait until data is received
554 while ((spi->SR & SPI_SR_RXNE) == 0)
555 ;
556
557 // Read the received data item
558 return static_cast<uint8_t>(spi->DR);
559}
560
561inline uint16_t SPIBus::transfer16(uint16_t data)
562{
563 uint16_t temp[2] = {0};
564 // Transferring MSB, putting the read value in the MSB of temp
565 temp[0] = static_cast<uint16_t>(transfer(static_cast<uint8_t>(data >> 8)));
566 // Transferring LSB, putting the read value in the LSB of temp
567 temp[1] = static_cast<uint16_t>(transfer(static_cast<uint8_t>(data)));
568 // MSB | LSB
569 return temp[0] << 8 | temp[1];
570}
571
572inline uint32_t SPIBus::transfer24(uint32_t data)
573{
574 uint32_t temp[3] = {0};
575 // Transferring MSB, putting the read value in the MSB of temp
576 temp[0] = static_cast<uint32_t>(transfer(static_cast<uint8_t>(data >> 16)));
577 temp[1] = static_cast<uint32_t>(transfer(static_cast<uint8_t>(data >> 8)));
578 // Transferring LSB, putting the read value in the LSB of temp
579 temp[2] = static_cast<uint32_t>(transfer(static_cast<uint8_t>(data)));
580
581 return temp[0] << 16 | temp[1] << 8 | temp[2];
582}
583
584inline uint32_t SPIBus::transfer32(uint32_t data)
585{
586 uint32_t temp[4] = {0};
587 // Transferring MSB, putting the read value in the MSB of temp
588 temp[0] = static_cast<uint32_t>(transfer(static_cast<uint8_t>(data >> 24)));
589 temp[1] = static_cast<uint32_t>(transfer(static_cast<uint8_t>(data >> 16)));
590 temp[2] = static_cast<uint32_t>(transfer(static_cast<uint8_t>(data >> 8)));
591 // Transferring LSB, putting the read value in the LSB of temp
592 temp[3] = transfer(static_cast<uint8_t>(data));
593
594 return temp[0] << 24 | temp[1] << 16 | temp[2] << 8 | temp[3];
595}
596
597inline void SPIBus::transfer(uint8_t* data, size_t nBytes)
598{
599 for (size_t i = 0; i < nBytes; i++)
600 data[i] = transfer(data[i]);
601}
602
603inline void SPIBus::transfer16(uint16_t* data, size_t nBytes)
604{
605 // nBytes to be read must be a multiple of 2
606 assert(nBytes % 2 == 0);
607
608 uint16_t temp[2] = {0};
609
610 for (size_t i = 0; i < nBytes / 2; i++)
611 {
612 temp[0] =
613 static_cast<uint16_t>(transfer(static_cast<uint8_t>(data[i] >> 8)));
614 temp[1] =
615 static_cast<uint16_t>(transfer(static_cast<uint8_t>(data[i])));
616 data[i] = temp[0] << 8 | temp[1];
617 }
618}
619
620} // namespace Boardcore
SPI_TypeDef SPIType
Definition SPIBus.h:32
miosix::GpioPin GpioType
Driver for STM32 low level SPI peripheral.
Definition SPIBus.h:61
uint32_t transfer32(uint32_t data) override
Full duplex transmission of 32 bits on the bus.
Definition SPIBus.h:584
SPIBus(SPIType *spi)
Delete copy/move contructors/operators.
Definition SPIBus.h:301
uint8_t transfer(uint8_t data) override
Full duplex transmission of 8 bits on the bus.
Definition SPIBus.h:525
uint32_t read32() override
Reads 32 bits from the bus.
Definition SPIBus.h:471
void waitPeripheral()
Definition SPIBus.h:399
void configure(SPIBusConfig newConfig) override
Configures and enables the bus with the provided configuration.
Definition SPIBus.h:413
void setBitOrder(SPI::Order bitOrder)
Definition SPIBus.h:360
void enableRxDMARequest()
Definition SPIBus.h:395
void flushRxBuffer()
Definition SPIBus.h:407
void disable()
Disables the peripheral.
Definition SPIBus.h:319
void write16(uint16_t data) override
Writes 16 bits to the bus.
Definition SPIBus.h:499
void write(uint8_t data) override
Writes 8 bits to the bus.
Definition SPIBus.h:497
void setMasterConfiguration()
Definition SPIBus.h:380
void setClockDiver(SPI::ClockDivider divider)
Definition SPIBus.h:369
void setMode(SPI::Mode mode)
Definition SPIBus.h:382
SPIBus(SPIBus &&)=delete
void enable()
Enables the peripheral.
Definition SPIBus.h:317
SPIType * getSpi()
Retrieve the pointer to the peripheral currently used.
Definition SPIBus.h:306
void enableTxDMARequest()
Definition SPIBus.h:391
void setSlaveConfiguration()
Definition SPIBus.h:378
SPIBus(const SPIBus &)=delete
void disableInternalSlaveSelection()
Definition SPIBus.h:355
void disableTxDMARequest()
Definition SPIBus.h:393
SPIBus & operator=(SPIBus &&)=delete
uint16_t transfer16(uint16_t data) override
Full duplex transmission of 16 bits on the bus.
Definition SPIBus.h:561
uint8_t read() override
Reads 8 bits from the bus.
Definition SPIBus.h:465
void enableInternalSlaveSelection()
Definition SPIBus.h:353
void write32(uint32_t data) override
Writes 32 bits to the bus.
Definition SPIBus.h:503
void write24(uint32_t data) override
Writes 24 bits to the bus.
Definition SPIBus.h:501
void disableRxDMARequest()
Definition SPIBus.h:397
void set8BitFrameFormat()
Definition SPIBus.h:332
uint16_t read16() override
Reads 16 bits from the bus.
Definition SPIBus.h:467
void enableSoftwareSlaveManagement()
Definition SPIBus.h:346
uint32_t transfer24(uint32_t data) override
Full duplex transmission of 24 bits on the bus.
Definition SPIBus.h:572
void disableSoftwareSlaveManagement()
Definition SPIBus.h:348
void select(GpioType cs) override
See SPIBusInterface::select().
Definition SPIBus.h:449
void deselect(GpioType cs) override
See SPIBusInterface::deselect().
Definition SPIBus.h:457
uint32_t read24() override
Reads 24 bits from the bus.
Definition SPIBus.h:469
SPIBus & operator=(const SPIBus &)=delete
void reset()
Resets the peripheral configuration.
Definition SPIBus.h:308
Interface for low level access of a SPI bus as a master.
bool enablePeripheralClock(void *peripheral)
Enables a peripheral clock source from the APB1 and APB2 peripheral buses.
Definition ClockUtils.h:155
ClockDivider
SPI Clock divider.
Definition SPIDefs.h:70
This file includes all the types the logdecoder script will decode.
SPI Bus configuration for a specific slave.
unsigned int csSetupTimeUs
How long to hold cs after the end of a tranmission (us)
SPI::Mode mode
MSBit or LSBit first.
SPI::ClockDivider clockDivider
< Peripheral clock division