mwf_periph_spi
- SPI
This implements the_spi1
, a class object for using the SPI bus.
- SPI0 support is not included in the code.
- Some definitions are included for non-blocking operations, but only blocking APIs are available in the current version.
Code Example
#include "mwf_periph_spi.hpp"
void func_spi_init() {
if (!mwf::the_spi1) {
mwf::spi::global_init_spi1_manager();
Serial << crlf << "the_spi1 constructed.";
}
mwf::spi::config conf{};
conf.baud = 4000000UL; // 4MHz
conf.bits = 8; // 8bit
conf.dir = mwf::spi::E_SPI_DIR::MSB_FIRST;
conf.mode = mwf::spi::E_SPI_MODE::MODE_3_INV_RISE;
conf.pin_ssel[0] = mwf::spi::E_PIN_SSEL::SSEL0_PIO3;
conf.pin_ssel[1] = mwf::spi::E_PIN_SSEL::SSEL1_PIO16;
mwf::the_spi1->init(conf);
}
void func_spi_transfer() {
uint8_t tx[16] = { 0xa5, 0x5a, 0x11, 0x88 }; // data to transmit
uint8_t rx[16]; // receive buffer
mwf::the_spi1->ssel_select(0);
mwf::the_spi1->transfer_blocking(tx, rx, 4); // four bytes transfer
CLOCK_uDelay(5); // wait 5us
mwf::the_spi1->transfer_blocking(tx, rx, 4); // four bytes transfer
mwf::the_spi1->ssel_deselect();
}
class mwf::periph::spi
struct config
The mwf::periph::spi::config
structure is defined as follows:
struct config {
// evaluated only in init()
E_PIN_MAIN pin_conf; // master pin configuration (so far not used)
E_PIN_SSEL pin_ssel[3]; // SSEL0..2 (assignment settings for slave select pins.
// At least pin_ssel[0] shall be configured.
// evaluated in conf(), reconf()
uint32_t baud; // SPI frequency (default 50Mhz)
E_SPI_DIR dir; // transfer LSB first
E_SPI_MODE mode; // SPI mode (clock polarity and detect edge)
uint8_t bits; // bit width (0:default=8, ...)
uint8_t ssel; // 0..3 or 0x80..0x80 (if MSB is set, assert/deassert SSEL automatically)
};
You set values in the structure and call init()
. The structure’s settings are copied internally during the init()
call, so you can discard the structure’s memory area afterward.
Here is an explanation of each member of the structure:
Signal Name | Description |
---|---|
pin_conf | Specifies the pin assignment (primary or alternate). If E_PIN_CONF::NODEF (=0) is selected, the setting from global_init_spi1_manager() is used. |
pin_ssel[3] | Specifies the SELECT pins. Index 0 of the array specifies the pin for SPI SELECT 0 (SSEL0), index 1 for SSEL1, and index 2 for SSEL2. SSEL0 must always be specified, while SSEL1 and SSEL2 should be set to E_PIN_SSEL::SSEL_VOID=0 . If you use two types of pins, specify SSEL0 and SSEL1; if you use three, store values in all of them.*Note: If you specify a software-controlled pin (e.g., E_PIN_SSEL::SSEL0_PIO3 ), all select pins will be software-controlled. With software control, the pins are set to a continuous HIGH level output and change to a LOW level when selected. TWENETcmpt (AHI library compatible) uses software control.*Note: With hardware control, the SELECT pin control follows the behavior of SPI_MasterTransferNonBlocking() in fsl_spi.c. |
baud | Specifies the SPI clock frequency. It can be set up to 32MHz, but frequencies around 1MHz are often used for sensor devices.<br />(Behavior follows spi_master_config_t::bandRate_Bps in fsl_spi.h and SPI_MasterSetBaud() in fsl_spi.c) |
mode | SPISEL is specified in init() . |
bits | This is the transfer unit. 8 is usually specified. It can be set from 1 to 16. If 9 bits or more are specified, the byte array for data transfer is read in 2-byte units, requiring twice the amount of data. The first byte represents 8 bits from the LSB, and the second byte represents the remaining bits. (Behavior follows SPI_MasterTransferBlocking() in fsl_spi.h) |
ssel | This is the SPI SELECT. Specify 0 to 2. |
enum class E_PIN_CONF
enum class E_PIN_CONF : uint8_t {
NODEF = 0, // Not specified
PRIMARY = 1, // Primary assignment (PIO10/11)
ALT = 2 // Alternate assignment (PIO15/16)
};
// Type for assignments, comparisons, etc. between enum class and int types.
using wE_PIN_CONF = mwf::enum_wapper<E_PIN_CONF>;
This is an enumeration for specifying pin assignments.
enum class E_PIN_SSEL
enum class E_PIN_SSEL : uint8_t {
SSEL_VOID = 0, // Undefined
SSEL_PRIMARY, // Primary setting pin
SSEL_ALT, // Alternate setting pin
SSEL_SOFT = 0x10,
SSEL0_PIO3, // SSEL0 is software-controlled, using PIO3
SSEL1_PIO16, // SSEL1 is software-controlled, using PIO16
SSEL2_PIO17, // SSEL2 is software-controlled, using PIO17
SSEL1_PIO4, // SSEL1 is software-controlled, using PIO4
SSEL2_PIO13, // SSEL2 is software-controlled, using PIO13
};
This enumeration determines the arrangement of the SPI SELECT pins. The corresponding value is stored in spi::config::pin_ssel[3]
.
Set
pin_ssel[]
according to the number of devices used.- For one device, set
pin_ssel[0]
.pin_ssel[1]
andpin_ssel[2]
should beSSEL_VOID
. - For two devices, set
pin_ssel[0]
andpin_ssel[1]
.pin_ssel[2]
should beSSEL_VOID
. - For three devices, set
pin_ssel[0]
,pin_ssel[1]
, andpin_ssel[2]
.
- For one device, set
To specify hardware control, use
SSEL_PRIMARY
orSSEL_ALT
(andSSEL_VOID
). If you mix them, it will result in software control.
enum class E_SPI_DIR
enum class E_SPI_DIR {
MSB_FIRST = 0,
LSB_FIRST
};
This specifies the bit order. MSB_FIRST
is typically used.
enum class E_SPI_MODE
enum class E_SPI_MODE {
MODE_0_RISE = 0,
MODE_1_FALL = 1,
MODE_2_INV_FALL = 2,
MODE_3_INV_RISE = 3,
};
This specifies the SPI transfer mode. It determines the clock’s detection edge and whether the H/L value at that time is 0 or 1. Set this according to the connected device’s datasheet.
global_init_spi1_manager()
, global_deinit_spi1_manager()
static void global_init_spi1_manager(E_PIN_MAIN pin_conf = E_PIN_MAIN::PRIMARY);
static void global_deinit_spi1_manager();
These functions create and destroy the class object for using the SPI1 bus. During class object creation, you select the pin combination to use with pin_conf
. You can specify E_PIN_MAIN::PRIMARY
(value 0) or E_PIN_MAIN::ALT
(value 1) for the pin configuration.
init()
void init();
void init(spi::config& cnf);
void init(spi::config&& cnf);
This function initializes the SPI bus. By providing the cnf
parameter, you can set several configurations. If the parameter is omitted, it re-initializes with the previous settings.
- Even if you are software-controlling the select pin in your user program, you must specify at least one. The specified pin will be configured as a GPIO output. By calling
unset_ssel_auto()
, the library will no longer control that pin.
reconf()
void reconf();
This function re-applies the peripheral parameters. To access the internal settings, use spi::config& get_conf()
. Pin settings (pin_ssel[]
) are not reflected by this procedure.
set_data_width()
void set_data_width(uint8_t bits);
This function changes the transfer data width. It is a lighter procedure than reconf()
.
set|unset|get_ssel_auto()
void set_ssel_auto();
void unset_ssel_auto();
bool get_ssel_auto();
Sets, unsets, and gets the automatic control flag for the select pin.
- This is only effective when using software control.
- When
transfer_blocking()
is called, the SELECT pin is automatically set to LOW. It is set back to HIGH upon completion.
ssel_select()
void ssel_select(uint8_t select);
This function specifies the select pin. select
takes values of 0, 1, or 2, corresponding to SSEL0 through SSEL2.
- With hardware or software control and the automatic control flag (
set_ssel_auto()
) enabled, pin control is performed when the transfer API is called. - With software control, regardless of the automatic control flag, the SELECT pin is set to LOW immediately after the
ssel_select()
call. - With hardware control, it calls
reconf()
to re-initialize. This process has a high time cost, so if you frequently switch between devices for transfers, please use software control.
ssel_deselect()
void ssel_deselect();
This function deselects the select pins.
- With software control, all select pins are returned to a HIGH level.
- With hardware control, it does nothing.
- It does not change the pin specification from
ssel_select()
.
Other
bool has_init(); // Returns true if init() has been executed
spi::config& get_conf(); // Accesses internally stored configuration information
Pin Assignments
For E_PIN_CONF::PRIMARY
Signal | PIO Number | Description |
---|---|---|
SCK | 0 | Clock signal |
MOSI | 2 | SPIMOSI. TWELITE side is output, external SPI device side is input. |
MISO | 5 | SPIMISO. TWELITE side is input, external SPI device side is output. |
Select Pins
pin_ssel | E_PIN_SSEL | PIO | Remarks |
---|---|---|---|
SSEL0 pin_ssel[0] | SSEL_PRIMARY | 3 | |
SSEL_ALT | 16 | ||
SSEL0_PIO3 | 3 | Software-controlled | |
SSEL0_PIO16 | 16 | Software-controlled | |
SSEL1 pin_ssel[1] | SSEL_PRIMARY | 4 | |
SSEL_ALT | 14 | ||
SSEL1_PIO4 | 4 | Software-controlled | |
SSEL1_PIO14 | 14 | Software-controlled | |
SSEL1_PIO16 | 16 | Software-controlled (Cannot specify SSEL0_PIO16 ) | |
SSEL_VOID | Only use SSEL0 for selection | ||
SSEL2 pin_ssel[2] | SSEL_PRIMARY | 13 | |
SSEL2_PIO13 | 13 | Software-controlled | |
SSEL2_PIO17 | 17 | Software-controlled | |
SSEL_VOID | Only use SSEL0 and SSEL1 for selection |
- A maximum of three select pins can be set in order from
SSEL0
toSSEL1
andSSEL2
. - If any of the
pin_ssel[]
specifications are for software control, all select pins will be software-controlled by the library.
For E_PIN_CONF::ALT
Signal | PIO Number | Description |
---|---|---|
SCK | 15 | Clock signal |
MOSI | 17 | SPIMOSI. TWELITE side is output, external SPI device side is input. |
MISO | 18 | SPIMISO. TWELITE side is input, external SPI device side is output. |
Select Pins
pin_ssel | E_PIN_SSEL | PIO | Remarks |
---|---|---|---|
SSEL0 pin_ssel[0] | SSEL_PRIMARY | 16 | |
SSEL_ALT | 3 | ||
SSEL0_PIO3 | 3 | Software-controlled | |
SSEL0_PIO16 | 16 | Software-controlled | |
SSEL1 pin_ssel[1] | SSEL_PRIMARY | 14 | SSEL0 must be specified |
SSEL_ALT | 4 | ||
SSEL1_PIO4 | 4 | Software-controlled | |
SSEL1_PIO14 | 14 | Software-controlled | |
SSEL_VOID | Only use SSEL0 for selection | ||
SSEL2 pin_ssel[2] | SSEL_PRIMARY | 13 | |
SSEL_ALT | 5 | Note that this is also the PRG pin. | |
SSEL2_PIO5 | 5 | Software-controlled. Note that this is also the PRG pin. | |
SSEL2_PIO13 | 13 | Software-controlled | |
SSEL_VOID | Only use SSEL0 and SSEL1 for selection |
- A maximum of three select pins can be set in order from
SSEL0
toSSEL1
andSSEL2
. - If any of the
pin_ssel[]
specifications are for software control, all select pins will be software-controlled by the library.
class mwf::periph::spi
(sys_ev_handler
)
on_sleep()
As a procedure before sleep, the SPI is returned to an unused state. If it has been initialized by init()
, this state is saved.
on_wakeup()
If it was in an initialized state before sleep, it is re-initialized so that the SPI bus can be used.