flipperzero-firmware/wiki/fw/api/SPI-Devices-API.md
coreglitch 942bbfaefe
Core api concept (#144)
* add input debounce code from old fw

* exampl of input api

* change input API to get/release

* revert input API to read

* pointer instead of instance

* add input API description

* add display API

* rewrite display names

* migrate to valuemanager

* add LED API

* add closing brakets

* add sound api

* fix led api

* basic api

* rename API pages

* change pubsub implementation

* move FURI AC -> flapp, add valuemutex example, add valuemanager implementation

* pubsub usage example

* user led example

* update example

* simplify input

* add composed display

* add SPI/GPIO and CC1101 bus

* change cc1101 api

* spi api and devices

* spi api and devices

* move SPI to page, add GPIO

* not block pin open

* backlight API and more

* add minunit tests

* fix logging

* ignore unexisting time service on embedded targets

* fix warning, issue with printf

* Deprecate furi_open and furi_close (#167)

Rename existing furi_open and furi_close to deprecated version

* add exitcode

* migrate to printf

* indicate test by leds

* add testing description

* rename furi.h

* wip basic api

* add valuemutex, pubsub, split files

* add value expanders

* value mutex realization and tests

* valuemutex test added to makefile

* do not build unimplemented files

* fix build furmware target f2

* redesigned minunit tests to allow testing in separate files

* test file for valuemutex minunit testing

* minunit partial test valuemutex

* local cmsis_os2 mutex bindings

* implement furi open/create, tests

* migrate concurrent_access to ValueMutex

* add spi header

* Lib: add mlib submodule.

Co-authored-by: rusdacent <rusdacentx0x08@gmail.com>
Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
2020-10-13 11:22:43 +03:00

3.3 KiB

SPI

HAL struct SPI_HandleTypeDef* used for handling SPI info.

For transmit/receive data use spi_xfer function:

bool spi_xfer(
    SPI_HandleTypeDef* spi,
    uint8_t* tx_data, uint8_t* rx_data, size_t len,
    PubSubCallback cb, void* ctx);
  • tx_data and rx_data size must be equal (and equal len)
  • cb called after spi operation is completed, (NULL, ctx) passed to callback.

Blocking verison:

inline static bool spi_xfer_block(SPI_HandleTypeDef* spi, uint8_t* tx_data, uint8_t* rx_data, size_t len) {
    semaphoreInfo s;
    osSemaphore block = createSemaphoreStatic(s);
    if(!spi_xfer(spi, tx_data, rx_data, len, RELEASE_SEMAPHORE, (void*)block)) {
        osReleaseSemaphore(block);
        return false;
    }
    osWaitSemaphore(block);
    return false;
}

SPI Bus

Common implementation of SPI bus: serial interface + CS pin

typedef struct {
    GpioPin* cs; ///< CS pin
    ValueMutex* spi; ///< <SPI_HandleTypeDef*>
} SpiBus;

SPI device

For dedicated work with one device there is SpiDevice entity. It contains ValueMutex around SpiBus: after you acquire device you can acquire spi to work with it (don't forget SPI bus is shared around many device, release it after every transaction as quick as possible).

typedef struct {
    ValueMutex* bus; ///< <SpiBus*>
} SpiDevice;

SPI IRQ device

Many devices (like CC1101 and NFC) present as SPI bus and IRQ line. For work with it there is special entity SpiIrqDevice. Use subscribe_pubsub for subscribinq to irq events.

typedef struct {
    ValueMutex* bus; ///< <SpiBus*>
    PubSub* irq;
} SpiIrqDevice;

Display device

Special implementation of SPI bus: serial interface + CS, Res, D/I lines.

typedef struct {
    GpioPin* cs; ///< CS pin
    GpioPin* res; ///< reset pin
    GpioPin* di; ///< D/I pin
    ValueMutex* spi; ///< <SPI_HandleTypeDef*>
} DisplayBus;

```C
typedef struct {
    ValueMutex* bus; ///< <DisplayBus*>
} DisplayDevice;

SPI devices (F2)

  • /dev/sdcard - SD card SPI, SpiDevice
  • /dev/cc1101_bus - Sub-GHz radio (CC1101), SpiIrqDevice
  • /dev/nfc - NFC (ST25R3916), SpiIrqDevice
  • /dev/display - DisplayDevice
  • /dev/spiext - External SPI (warning! Lock PA4, PA5, PA6, PA7)

Application example

// Be careful, this function called from IRQ context
void handle_irq(void* _arg, void* _ctx) {
}

void cc1101_example() {
    SpiIrqDevice* cc1101_device = open_input("/dev/cc1101_bus");
    if(cc1101_device == NULL) return; // bus not available, critical error

    subscribe_pubsub(cc1101_device->irq, handle_irq, NULL);

    {
        // acquire device as device bus
        SpiBus* spi_bus = acquire_mutex(cc1101_device->bus, 0);
        if(spi_bus == NULL) {
            printf("Device busy\n");
            // wait for device
            spi_bus = acquire_mutex_block(cc1101_device->bus);
        }
        
        // make transaction
        uint8_t request[4] = {0xDE, 0xAD, 0xBE, 0xEF};
        uint8_t response[4];

        {
            SPI_HandleTypeDef* spi = acquire_mutex_block(spi_bus->spi);

            gpio_write(spi_bus->cs, false);
            spi_xfer_block(spi, request, response, 4);
            gpio_write(spi_bus->cs, true);

            release_mutex(cc1101_device->spi, spi);
        }

        // release device (device bus)
        release_mutex(cc1101_device->bus, spi_bus);
    }
}