aboutsummaryrefslogtreecommitdiff
path: root/platforms/avr/drivers/spi_master.c
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/avr/drivers/spi_master.c')
-rw-r--r--platforms/avr/drivers/spi_master.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/platforms/avr/drivers/spi_master.c b/platforms/avr/drivers/spi_master.c
new file mode 100644
index 000000000..4e8fd3bcd
--- /dev/null
+++ b/platforms/avr/drivers/spi_master.c
@@ -0,0 +1,180 @@
1/* Copyright 2020
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17#include "spi_master.h"
18
19#include "timer.h"
20
21#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__)
22# define SPI_SCK_PIN B1
23# define SPI_MOSI_PIN B2
24# define SPI_MISO_PIN B3
25#elif defined(__AVR_ATmega32A__)
26# define SPI_SCK_PIN B7
27# define SPI_MOSI_PIN B5
28# define SPI_MISO_PIN B6
29#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
30# define SPI_SCK_PIN B5
31# define SPI_MOSI_PIN B3
32# define SPI_MISO_PIN B4
33#endif
34
35#ifndef SPI_TIMEOUT
36# define SPI_TIMEOUT 100
37#endif
38
39static pin_t currentSlavePin = NO_PIN;
40static uint8_t currentSlaveConfig = 0;
41static bool currentSlave2X = false;
42
43void spi_init(void) {
44 writePinHigh(SPI_SS_PIN);
45 setPinOutput(SPI_SCK_PIN);
46 setPinOutput(SPI_MOSI_PIN);
47 setPinInput(SPI_MISO_PIN);
48
49 SPCR = (_BV(SPE) | _BV(MSTR));
50}
51
52bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
53 if (currentSlavePin != NO_PIN || slavePin == NO_PIN) {
54 return false;
55 }
56
57 currentSlaveConfig = 0;
58
59 if (lsbFirst) {
60 currentSlaveConfig |= _BV(DORD);
61 }
62
63 switch (mode) {
64 case 1:
65 currentSlaveConfig |= _BV(CPHA);
66 break;
67 case 2:
68 currentSlaveConfig |= _BV(CPOL);
69 break;
70 case 3:
71 currentSlaveConfig |= (_BV(CPOL) | _BV(CPHA));
72 break;
73 }
74
75 uint16_t roundedDivisor = 1;
76 while (roundedDivisor < divisor) {
77 roundedDivisor <<= 1;
78 }
79
80 switch (roundedDivisor) {
81 case 16:
82 currentSlaveConfig |= _BV(SPR0);
83 break;
84 case 64:
85 currentSlaveConfig |= _BV(SPR1);
86 break;
87 case 128:
88 currentSlaveConfig |= (_BV(SPR1) | _BV(SPR0));
89 break;
90 case 2:
91 currentSlave2X = true;
92 break;
93 case 8:
94 currentSlave2X = true;
95 currentSlaveConfig |= _BV(SPR0);
96 break;
97 case 32:
98 currentSlave2X = true;
99 currentSlaveConfig |= _BV(SPR1);
100 break;
101 }
102
103 SPCR |= currentSlaveConfig;
104 if (currentSlave2X) {
105 SPSR |= _BV(SPI2X);
106 }
107 currentSlavePin = slavePin;
108 setPinOutput(currentSlavePin);
109 writePinLow(currentSlavePin);
110
111 return true;
112}
113
114spi_status_t spi_write(uint8_t data) {
115 SPDR = data;
116
117 uint16_t timeout_timer = timer_read();
118 while (!(SPSR & _BV(SPIF))) {
119 if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) {
120 return SPI_STATUS_TIMEOUT;
121 }
122 }
123
124 return SPDR;
125}
126
127spi_status_t spi_read() {
128 SPDR = 0x00; // Dummy
129
130 uint16_t timeout_timer = timer_read();
131 while (!(SPSR & _BV(SPIF))) {
132 if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) {
133 return SPI_STATUS_TIMEOUT;
134 }
135 }
136
137 return SPDR;
138}
139
140spi_status_t spi_transmit(const uint8_t *data, uint16_t length) {
141 spi_status_t status;
142
143 for (uint16_t i = 0; i < length; i++) {
144 status = spi_write(data[i]);
145
146 if (status < 0) {
147 return status;
148 }
149 }
150
151 return SPI_STATUS_SUCCESS;
152}
153
154spi_status_t spi_receive(uint8_t *data, uint16_t length) {
155 spi_status_t status;
156
157 for (uint16_t i = 0; i < length; i++) {
158 status = spi_read();
159
160 if (status >= 0) {
161 data[i] = status;
162 } else {
163 return status;
164 }
165 }
166
167 return SPI_STATUS_SUCCESS;
168}
169
170void spi_stop(void) {
171 if (currentSlavePin != NO_PIN) {
172 setPinOutput(currentSlavePin);
173 writePinHigh(currentSlavePin);
174 currentSlavePin = NO_PIN;
175 SPSR &= ~(_BV(SPI2X));
176 SPCR &= ~(currentSlaveConfig);
177 currentSlaveConfig = 0;
178 currentSlave2X = false;
179 }
180}