Skip to content

Commit fa14cbc

Browse files
committed
[rp2040] spi
1 parent 6b704d4 commit fa14cbc

File tree

4 files changed

+416
-2
lines changed

4 files changed

+416
-2
lines changed

src/modm/platform/gpio/rp/config.hpp.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ enum class PeripheralPin
3333
Sda,
3434
Scl,
3535
/* spi */
36-
Miso,
37-
Mosi,
36+
/* Miso - Rx for master, Tx for slave */
37+
/* Mosi - Tx for master, Rx for slave */
3838
Sclk,
3939
/* pwm */
4040
A,

src/modm/platform/spi/rp/module.lb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2022, Andrey Kunitsyn
5+
#
6+
# This file is part of the modm project.
7+
#
8+
# This Source Code Form is subject to the terms of the Mozilla Public
9+
# License, v. 2.0. If a copy of the MPL was not distributed with this
10+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
11+
# -----------------------------------------------------------------------------
12+
13+
def get_properties(env):
14+
device = env[":target"]
15+
driver = device.get_driver("spi")
16+
properties = {}
17+
properties["target"] = device.identifier
18+
properties["features"] = driver["feature"] if "feature" in driver else []
19+
return properties
20+
21+
class Instance(Module):
22+
def __init__(self, driver, instance):
23+
self.instance = int(instance)
24+
self.driver = driver
25+
26+
def init(self, module):
27+
module.name = str(self.instance)
28+
module.description = "Instance {}".format(self.instance)
29+
30+
def prepare(self, module, options):
31+
module.depends(":platform:spi")
32+
return True
33+
34+
def build(self, env):
35+
properties = get_properties(env)
36+
properties["id"] = self.instance
37+
38+
env.substitutions = properties
39+
env.outbasepath = "modm/src/modm/platform/spi"
40+
41+
env.template("spi_master.hpp.in", "spi_master_{}.hpp".format(self.instance))
42+
env.template("spi_master.cpp.in", "spi_master_{}.cpp".format(self.instance))
43+
44+
def init(module):
45+
module.name = ":platform:spi"
46+
module.description = "Serial Peripheral Interface (SPI)"
47+
48+
def prepare(module, options):
49+
device = options[":target"]
50+
if not device.has_driver("spi:rp20*"):
51+
return False
52+
53+
module.depends(
54+
":architecture:register",
55+
":architecture:spi",
56+
":cmsis:device",
57+
":math:algorithm",
58+
":platform:gpio")
59+
60+
for driver in device.get_all_drivers("spi:rp20*"):
61+
for instance in driver["instance"]:
62+
module.add_submodule(Instance(driver, instance))
63+
64+
return True
65+
66+
def build(env):
67+
env.substitutions = get_properties(env)
68+
env.outbasepath = "modm/src/modm/platform/spi"
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright (c) 2022, Andrey Kunitsyn
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#include "spi_master_{{id}}.hpp"
13+
#include <modm/platform/core/resets.hpp>
14+
15+
// ----------------------------------------------------------------------------
16+
17+
static inline bool tx_fifo_full() {
18+
return (spi{{ id }}_hw->sr & SPI_SSPSR_TNF_BITS) == 0;
19+
}
20+
static inline bool is_busy() {
21+
return (spi{{ id }}_hw->sr & SPI_SSPSR_BSY_BITS);
22+
}
23+
static inline bool rx_fifo_empty() {
24+
return (spi{{ id }}_hw->sr & SPI_SSPSR_RNE_BITS) == 0;
25+
}
26+
27+
void modm::platform::SpiMaster{{ id }}::reset()
28+
{
29+
Resets::reset(RESETS_RESET_SPI{{ id }}_BITS);
30+
}
31+
32+
void modm::platform::SpiMaster{{ id }}::unreset()
33+
{
34+
Resets::unresetWait(RESETS_RESET_SPI{{ id }}_BITS);
35+
}
36+
37+
uint8_t
38+
modm::platform::SpiMaster{{ id }}::acquire(void *ctx, ConfigurationHandler handler)
39+
{
40+
if (context == nullptr)
41+
{
42+
context = ctx;
43+
count = 1;
44+
// if handler is not nullptr and is different from previous configuration
45+
if (handler and configuration != handler) {
46+
configuration = handler;
47+
configuration();
48+
}
49+
return 1;
50+
}
51+
52+
if (ctx == context)
53+
return ++count;
54+
55+
return 0;
56+
}
57+
58+
uint8_t
59+
modm::platform::SpiMaster{{ id }}::release(void *ctx)
60+
{
61+
if (ctx == context)
62+
{
63+
if (--count == 0)
64+
context = nullptr;
65+
}
66+
return count;
67+
}
68+
// ----------------------------------------------------------------------------
69+
70+
modm::ResumableResult<uint8_t>
71+
modm::platform::SpiMaster{{ id }}::transfer(uint8_t data)
72+
{
73+
// this is a manually implemented "fast resumable function"
74+
// there is no context or nesting protection, since we don't need it.
75+
// there are only two states encoded into 1 bit (LSB of state):
76+
// 1. waiting to start, and
77+
// 2. waiting to finish.
78+
79+
// LSB != Bit0 ?
80+
if ( !(state & Bit0) )
81+
{
82+
// wait for previous transfer to finish
83+
if (tx_fifo_full())
84+
return {modm::rf::Running};
85+
86+
// start transfer by copying data into register
87+
write(data);
88+
89+
// set LSB = Bit0
90+
state |= Bit0;
91+
}
92+
93+
if (rx_fifo_empty())
94+
return {modm::rf::Running};
95+
96+
// transfer finished
97+
state &= ~Bit0;
98+
return {modm::rf::Stop, read()};
99+
}
100+
101+
modm::ResumableResult<void>
102+
modm::platform::SpiMaster{{ id }}::transfer(
103+
const uint8_t * tx, uint8_t * rx, std::size_t length)
104+
{
105+
// this is a manually implemented "fast resumable function"
106+
// there is no context or nesting protection, since we don't need it.
107+
// there are only two states encoded into 1 bit (Bit1 of state):
108+
// 1. initialize index, and
109+
// 2. wait for 1-byte transfer to finish.
110+
111+
// we need to globally remember which byte we are currently transferring
112+
static std::size_t index = 0;
113+
114+
// we are only interested in Bit1
115+
switch(state & Bit1)
116+
{
117+
case 0:
118+
// we will only visit this state once
119+
state |= Bit1;
120+
121+
// initialize index and check range
122+
index = 0;
123+
while (index < length)
124+
{
125+
default:
126+
{
127+
// if tx == 0, we use a dummy byte 0x00
128+
// else we copy it from the array
129+
// call the resumable function
130+
modm::ResumableResult<uint8_t> result = transfer(tx ? tx[index] : 0);
131+
132+
// if the resumable function is still running, so are we
133+
if (result.getState() > modm::rf::NestingError)
134+
return {modm::rf::Running};
135+
136+
// if rx != 0, we copy the result into the array
137+
if (rx) rx[index] = result.getResult();
138+
}
139+
index++;
140+
}
141+
142+
// clear the state
143+
state &= ~Bit1;
144+
return {modm::rf::Stop};
145+
}
146+
}
147+
148+
void modm::platform::SpiMaster{{ id }}::transferBlocking(
149+
const uint8_t *tx, std::size_t length)
150+
{
151+
uint8_t index = 0;
152+
while(index < length) {
153+
// Wait for tx empty
154+
while(tx_fifo_full()) __NOP();
155+
// Write next byte
156+
write(tx ? tx[index] : 0);
157+
index++;
158+
}
159+
160+
// Drain RX FIFO, then wait for shifting to finish (which may be *after*
161+
// TX FIFO drains), then drain RX FIFO again
162+
while (!rx_fifo_empty())
163+
(void)read();
164+
while (is_busy())
165+
__NOP();
166+
while (!rx_fifo_empty())
167+
(void)read();;
168+
169+
// Don't leave overrun flag set
170+
spi{{ id }}_hw->icr = SPI_SSPICR_RORIC_BITS;
171+
}
172+

0 commit comments

Comments
 (0)