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