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_PRIMARYorSSEL_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
SSEL0toSSEL1andSSEL2. - 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
SSEL0toSSEL1andSSEL2. - 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.