Skip to content

Commit cd5c740

Browse files
andryblacksalkinium
authored andcommitted
[rp] Add SPI module
1 parent 11cf507 commit cd5c740

File tree

5 files changed

+696
-0
lines changed

5 files changed

+696
-0
lines changed

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
class Instance(Module):
14+
def __init__(self, driver, instance):
15+
self.instance = int(instance)
16+
self.driver = driver
17+
18+
def init(self, module):
19+
module.name = str(self.instance)
20+
module.description = "Instance {}".format(self.instance)
21+
22+
def prepare(self, module, options):
23+
return True
24+
25+
def build(self, env):
26+
env.substitutions = {"id": self.instance}
27+
env.outbasepath = "modm/src/modm/platform/spi"
28+
29+
env.template("spi_master.hpp.in", "spi_master_{}.hpp".format(self.instance))
30+
env.template("spi_master.cpp.in", "spi_master_{}.cpp".format(self.instance))
31+
32+
if env.has_module(":platform:dma"):
33+
env.template("spi_master_dma.hpp.in", "spi_master_{}_dma.hpp".format(self.instance))
34+
env.template("spi_master_dma_impl.hpp.in", "spi_master_{}_dma_impl.hpp".format(self.instance))
35+
36+
37+
def init(module):
38+
module.name = ":platform:spi"
39+
module.description = "Serial Peripheral Interface (SPI)"
40+
41+
def prepare(module, options):
42+
device = options[":target"]
43+
if not device.has_driver("spi:rp20*"):
44+
return False
45+
46+
module.depends(
47+
":architecture:spi",
48+
":cmsis:device",
49+
":math:algorithm",
50+
":platform:gpio",
51+
":platform:clockgen")
52+
53+
for driver in device.get_all_drivers("spi:rp20*"):
54+
for instance in driver["instance"]:
55+
module.add_submodule(Instance(driver, instance))
56+
57+
return True
58+
59+
def build(env):
60+
pass
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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+
18+
void modm::platform::SpiMaster{{ id }}::reset()
19+
{
20+
Resets::reset(RESETS_RESET_SPI{{ id }}_BITS);
21+
}
22+
23+
void modm::platform::SpiMaster{{ id }}::unreset()
24+
{
25+
Resets::unresetWait(RESETS_RESET_SPI{{ id }}_BITS);
26+
}
27+
28+
uint8_t
29+
modm::platform::SpiMaster{{ id }}::acquire(void *ctx, ConfigurationHandler handler)
30+
{
31+
if (context == nullptr)
32+
{
33+
context = ctx;
34+
count = 1;
35+
// if handler is not nullptr and is different from previous configuration
36+
if (handler and configuration != handler) {
37+
configuration = handler;
38+
configuration();
39+
}
40+
return 1;
41+
}
42+
43+
if (ctx == context)
44+
return ++count;
45+
46+
return 0;
47+
}
48+
49+
uint8_t
50+
modm::platform::SpiMaster{{ id }}::release(void *ctx)
51+
{
52+
if (ctx == context)
53+
{
54+
if (--count == 0)
55+
context = nullptr;
56+
}
57+
return count;
58+
}
59+
// ----------------------------------------------------------------------------
60+
61+
modm::ResumableResult<uint8_t>
62+
modm::platform::SpiMaster{{ id }}::transfer(uint8_t data)
63+
{
64+
// this is a manually implemented "fast resumable function"
65+
// there is no context or nesting protection, since we don't need it.
66+
// there are only two states encoded into 1 bit (LSB of state):
67+
// 1. waiting to start, and
68+
// 2. waiting to finish.
69+
70+
// LSB != Bit0 ?
71+
if ( !(state & Bit0) )
72+
{
73+
// wait for previous transfer to finish
74+
if (txFifoFull())
75+
return {modm::rf::Running};
76+
77+
// start transfer by copying data into register
78+
write(data);
79+
80+
// set LSB = Bit0
81+
state |= Bit0;
82+
}
83+
84+
if (rxFifoEmpty())
85+
return {modm::rf::Running};
86+
87+
// transfer finished
88+
state &= ~Bit0;
89+
return {modm::rf::Stop, read()};
90+
}
91+
92+
modm::ResumableResult<void>
93+
modm::platform::SpiMaster{{ id }}::transfer(
94+
const uint8_t * tx, uint8_t * rx, std::size_t length)
95+
{
96+
// this is a manually implemented "fast resumable function"
97+
// there is no context or nesting protection, since we don't need it.
98+
// there are only two states encoded into 1 bit (Bit1 of state):
99+
// 1. initialize index, and
100+
// 2. wait for 1-byte transfer to finish.
101+
102+
// we need to globally remember which byte we are currently transferring
103+
static std::size_t index = 0;
104+
105+
// we are only interested in Bit1
106+
switch(state & Bit1)
107+
{
108+
case 0:
109+
// we will only visit this state once
110+
state |= Bit1;
111+
112+
// initialize index and check range
113+
index = 0;
114+
while (index < length)
115+
{
116+
default:
117+
{
118+
// if tx == 0, we use a dummy byte 0x00
119+
// else we copy it from the array
120+
// call the resumable function
121+
modm::ResumableResult<uint8_t> result = transfer(tx ? tx[index] : 0);
122+
123+
// if the resumable function is still running, so are we
124+
if (result.getState() > modm::rf::NestingError)
125+
return {modm::rf::Running};
126+
127+
// if rx != 0, we copy the result into the array
128+
if (rx) rx[index] = result.getResult();
129+
}
130+
index++;
131+
}
132+
133+
// clear the state
134+
state &= ~Bit1;
135+
return {modm::rf::Stop};
136+
}
137+
}
138+
139+
void modm::platform::SpiMaster{{ id }}::transferBlocking(
140+
const uint8_t *tx, std::size_t length)
141+
{
142+
uint8_t index = 0;
143+
while(index < length) {
144+
// Wait for tx empty
145+
while(txFifoFull()) __NOP();
146+
// Write next byte
147+
write(tx ? tx[index] : 0);
148+
index++;
149+
}
150+
151+
drainRx();
152+
}
153+

0 commit comments

Comments
 (0)