aboutsummaryrefslogtreecommitdiff
path: root/drivers/spi_master.h
blob: d206b812bfd31b404b0c191487b888b673ec66aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2025 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "gpio.h"

/**
 * \file
 *
 * \defgroup spi_master SPI Master API
 *
 * \brief API to communicate with SPI devices.
 * \{
 */

// Hardware SS pin is defined in the header so that user code can refer to it
#ifdef __AVR__
#    if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
#        define SPI_SS_PIN B0
#    elif defined(__AVR_ATmega32A__)
#        define SPI_SS_PIN B4
#    elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
#        define SPI_SS_PIN B2
#    endif
#endif

typedef int16_t spi_status_t;

#define SPI_STATUS_SUCCESS (0)
#define SPI_STATUS_ERROR (-1)
#define SPI_STATUS_TIMEOUT (-2)

#define SPI_TIMEOUT_IMMEDIATE (0)
#define SPI_TIMEOUT_INFINITE (0xFFFF)

#ifdef __cplusplus
extern "C" {
#endif

typedef struct spi_start_config_t {
    pin_t    slave_pin;
    bool     lsb_first;
    uint8_t  mode;
    uint16_t divisor;
    bool     cs_active_low;
} spi_start_config_t;

/**
 * \brief Initialize the SPI driver. This function must be called only once, before any of the below functions can be called.
 */
void spi_init(void);

/**
 * \brief Start an SPI transaction.
 *
 * \param slavePin The GPIO pin connected to the desired device's `SS` line.
 * \param lsbFirst Determines the endianness of the transmission. If `true`, the least significant bit of each byte is sent first.
 * \param mode The SPI mode to use.
 * \param divisor The SPI clock divisor.
 *
 * \return `true` if the operation was successful, otherwise `false` if the supplied parameters are invalid or the SPI peripheral is already in use.
 */
bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);

bool spi_start_extended(spi_start_config_t *start_config);

/**
 * \brief Write a byte to the selected SPI device.
 *
 * \param data The byte to write.
 *
 * \return `SPI_STATUS_TIMEOUT` if the timeout period elapses, or `SPI_STATUS_SUCCESS`.
 */
spi_status_t spi_write(uint8_t data);

/**
 * \brief Read a byte from the selected SPI device.
 *
 * \return `SPI_STATUS_TIMEOUT` if the timeout period elapses, otherwise the byte read from the device.
 */
spi_status_t spi_read(void);

/**
 * \brief Send multiple bytes to the selected SPI device.
 *
 * \param data A pointer to the data to write from.
 * \param length The number of bytes to write. Take care not to overrun the length of `data`.
 *
 * \return `SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`.
 */
spi_status_t spi_transmit(const uint8_t *data, uint16_t length);

/**
 * \brief Receive multiple bytes from the selected SPI device.
 *
 * \param data A pointer to a buffer to read into.
 * \param length The number of bytes to read. Take care not to overrun the length of `data`.
 *
 * \return `SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`.
 */
spi_status_t spi_receive(uint8_t *data, uint16_t length);

/**
 * \brief End the current SPI transaction. This will deassert the slave select pin and reset the endianness, mode and divisor configured by `spi_start()`.
 *
 */
void spi_stop(void);

#ifdef __cplusplus
}
#endif

/** \} */