YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
spi_rp2040.cpp
1// ---------------------------------------------
2// This file is part of
3// _ _ __ _ _ __ __
4// ( \/ ) /__\ ( )_( ) /__\ ( )
5// \ / /(__)\ ) _ ( /(__)\ )(__
6// (__)(__)(__)(_) (_)(__)(__)(____)
7//
8// Yet Another HW Abstraction Library
9// Copyright (C) Andreas Terstegge
10// BSD Licensed (see file LICENSE)
11//
12// ---------------------------------------------
13//
14// The STE line is never used in HW mode, because
15// STE is deactivated after every byte, which is
16// not appropriate for most applications. We always
17// pass in a CS gpio pin, which might be used by
18// higher layers or automatically in this driver.
19
20#include "spi_rp2040.h"
21#include "system_rp2040.h"
22#include <cassert>
23
24using namespace _IO_BANK0_;
25using namespace _RESETS_;
26
27function<void(uint8_t)> spi_rp2040::_intHandler[2];
28
29int8_t spi_rp2040::_spi_miso_pins[2][4] =
30 { { 0, 4, 16, 20 }, { 8, 12, 24, 28 } };
31
32spi_rp2040::spi_rp2040( uint8_t index ,
33 gpio_pin_t miso_pin,
34 gpio_pin_t mosi_pin,
35 gpio_pin_t sclk_pin,
36 gpio_interface & cs_pin,
37 const bool spi_master,
38 uint16_t mode)
39 : _index(index), _miso(miso_pin), _mosi(mosi_pin), _sclk(sclk_pin),
40 _cs(cs_pin), _master(spi_master), _mode(mode), _init(false),
41 _generate_CS(true), _baud(0) {
42
43 assert(index < 2);
44 _spi = (index==0) ? &SPI0 : &SPI1;
45 _spi_set = (index==0) ? &SPI0_SET : &SPI1_SET;
46 _spi_clr = (index==0) ? &SPI0_CLR : &SPI1_CLR;
47 bool miso_found = false;
48 bool mosi_found = false;
49 bool sclk_found = false;
50 for (int i = 0; i < 4; ++i) {
51 if (miso_pin == _spi_miso_pins[index][i]) miso_found = true;
52 if (mosi_pin == _spi_miso_pins[index][i]+3) mosi_found = true;
53 if (sclk_pin == _spi_miso_pins[index][i]+2) sclk_found = true;
54 }
55 (void)miso_found; // suppress warnings
56 (void)mosi_found;
57 (void)sclk_found;
58 assert(miso_found && mosi_found && sclk_found);
59}
60
61spi_rp2040::~spi_rp2040() {
62 // Wait for pending operations
63// while (_EUSCI_STATW & EUSCI_A_STATW_BUSY);
64
65 // Reset CTLW0 register to default values
66 // (EUSCI_A is in reset state)
67// _EUSCI_CTLW0 = EUSCI_A_CTLW0_SWRST;
68}
69
70void spi_rp2040::init() {
71 // Take SPI out of reset state
72 if (_index) RESETS_CLR.RESET.spi1 = 1;
73 else RESETS_CLR.RESET.spi0 = 1;
74 // Configure GPIO pins MISO, MOSI & SCLK
75 _miso.setSEL(GPIO_CTRL_FUNCSEL__spi);
76 _mosi.setSEL(GPIO_CTRL_FUNCSEL__spi);
77 _sclk.setSEL(GPIO_CTRL_FUNCSEL__spi);
78 // Configure CS pin
79 _cs.gpioMode(_master ? GPIO::OUTPUT | GPIO::INIT_HIGH : GPIO::INPUT);
80 // Configure SPI port
81 _spi_set->SSPCR0 = _mode;
82 if (!_master) {
83 _spi_set->SSPCR1.MS = 1;
84 }
85 // Finally enable EUSCI module
86 _spi_set->SSPCR1.SSE = 1;
87 _init = true;
88}
89
90int16_t spi_rp2040::spiTxRx(const uint8_t *txbuf, uint8_t *rxbuf, uint16_t len)
91{
92 if (!_init) init();
93 if (_master) {
94 // Activate CS line
95 if (_generate_CS) _cs.gpioWrite(LOW);
96 } else {
97 // Wait for CS to be LOW
98 while(_cs.gpioRead()) ;
99 }
100
101 // Never have more transfers in flight than will fit into the RX FIFO,
102 // else FIFO will overflow if this code is heavily interrupted.
103// const uint16_t fifo_depth = 8;
104// uint16_t rx_remaining = len, tx_remaining = len;
105//
106// while (rx_remaining || tx_remaining) {
107// if (tx_remaining && _spi->SSPSR.TNF && rx_remaining < tx_remaining + fifo_depth) {
108// _spi->SSPDR.DATA = (*txbuf++);
109// --tx_remaining;
110// }
111// if (rx_remaining && _spi->SSPSR.RNE) {
112// *rxbuf++ = (uint8_t)(_spi->SSPDR.DATA);
113// --rx_remaining;
114// }
115// }
116
117 for (int i = 0; i < len; ++i)
118 {
119 // Wait until next byte can be written
120 while(_spi->SSPSR.TNF == 0) ;
121 // Transfer single char to TX buffer
122 _spi->SSPDR = (uint16_t) (txbuf[i]);
123 // Wait for next received byte
124 while(_spi->SSPSR.RNE == 0) ;
125 rxbuf[i] = (uint8_t)(_spi->SSPDR);
126 }
127
128 if (_master) {
129 // De-activate CS line
130 if (_generate_CS) _cs.gpioWrite(HIGH);
131 } else {
132 // Wait for CS to be HIGH
133 while(!_cs.gpioRead()) ;
134 }
135 return len;
136}
137
138int16_t spi_rp2040::spiTx(const uint8_t *txbuf, uint16_t len) {
139 if (!_init) init();
140 if (_master) {
141 // Activate CS line
142 if (_generate_CS) _cs.gpioWrite(LOW);
143 } else {
144 // Wait for CS to be LOW
145 while(_cs.gpioRead()) ;
146 }
147
148 for (int i = 0; i < len; ++i) {
149 //uint8_t dummy;
150 // Wait until last data was sent
151 while( (_spi->SSPSR.TNF) == 0) ;
152 // Transfer single char to TX buffer
153 _spi->SSPDR = (uint16_t) (txbuf[i]);
154 // Wait for next received byte
155 while(_spi->SSPSR.RNE == 0) ;
156 (void)(uint8_t)(_spi->SSPDR);
157
158
159// while(_spi->SSPSR.RNE) (void)_spi->SSPDR;
160// while(_spi->SSPSR.BSY) ;
161// // Wait for next received byte
162// while(_spi->SSPSR.RNE) (void)_spi->SSPDR;
163// _spi->SSPICR.RORIC = 1;
164 }
165
166 if (_master) {
167 // De-activate CS line
168 if (_generate_CS) _cs.gpioWrite(HIGH);
169 } else {
170 // Wait for CS to be HIGH
171 while(!_cs.gpioRead()) ;
172 }
173 return len;
174}
175
176int16_t spi_rp2040::spiRx(uint8_t tx_byte, uint8_t *rxbuf, uint16_t len) {
177 if (!_init) init();
178 if (_master) {
179 // Activate CS line
180 if (_generate_CS) _cs.gpioWrite(LOW);
181 } else {
182 // Wait for CS to be LOW
183 while(_cs.gpioRead()) ;
184 }
185
186 for (int i = 0; i < len; ++i)
187 {
188 // Wait until next byte can be written
189 while(_spi->SSPSR.TNF == 0) ;
190 // Transfer single char to TX buffer
191 _spi->SSPDR = tx_byte;
192 // Wait for next received byte
193 while(_spi->SSPSR.RNE == 0) ;
194 rxbuf[i] = (uint8_t)(_spi->SSPDR);
195 }
196
197 if (_master) {
198 // De-activate CS line
199 if (_generate_CS) _cs.gpioWrite(HIGH);
200 } else {
201 // Wait for CS to be HIGH
202 while(!_cs.gpioRead()) ;
203 }
204 return len;
205}
206
207
208void spi_rp2040::setSpeed(uint32_t baud)
209{
210 if (!_init) init();
211
212 uint32_t freq_in = CLK_PERI;
213 uint32_t prescale, postdiv;
214
215 // Find smallest prescale value which puts output frequency in range of
216 // post-divide. Prescale is an even number from 2 to 254 inclusive.
217 for (prescale = 2; prescale <= 254; prescale += 2) {
218 if (freq_in < (prescale + 2) * 256 * (uint64_t) baud)
219 break;
220 }
221 assert(prescale <= 254); // Frequency too low
222
223 // Find largest post-divide which makes output <= baudrate. Post-divide is
224 // an integer in the range 1 to 256 inclusive.
225 for (postdiv = 256; postdiv > 1; --postdiv) {
226 if (freq_in / (prescale * (postdiv - 1)) > baud)
227 break;
228 }
229 _spi->SSPCPSR = prescale;
230 _spi->SSPCR0.SCR = postdiv - 1;
231 _baud = freq_in / (prescale * postdiv);
232
233// _spi_set->SSPCR1.SSE = 1;
234}
235
236void spi_rp2040::generateCS(bool val)
237{
238 if (!_init) init();
239 _generate_CS = val;
240}
241
242void spi_rp2040::setCS(bool val) {
243 if (!_init) init();
244 _cs.gpioWrite(val);
245}
246
247void spi_rp2040::spiAttachRxIrq(function<void(uint8_t data)> f) {
248 // Register handler
249 _intHandler[_index] = f;
250 // Enable receive IRQ
251 //_spi-> _EUSCI_IE = EUSCI_A_IE_RXIE;
252 // Enable IRQ in NVIC
253 NVIC_EnableIRQ((IRQn_Type)(SPI0_IRQ_IRQn + _index));
254}
255
256
257extern "C"
258{
259
260void SPI0_IRQ_Handler(void) {
261 spi_rp2040::_intHandler[0]( SPI0.SSPDR );
262}
263
264void SPI1_IRQ_Handler(void) {
265 spi_rp2040::_intHandler[1]( SPI1.SSPDR );
266}
267
268} // extern "C"