Skip to content

Commit 5abaf03

Browse files
committed
[rp] PIO with compile-time assembler
1 parent cedb3aa commit 5abaf03

File tree

9 files changed

+1349
-0
lines changed

9 files changed

+1349
-0
lines changed

examples/rp_pico/pio_ws2812/main.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright (c) 2016, Sascha Schade
3+
* Copyright (c) 2017, Niklas Hauser
4+
*
5+
* This file is part of the modm project.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
10+
*/
11+
// ----------------------------------------------------------------------------
12+
13+
#include <modm/board.hpp>
14+
using namespace Board;
15+
16+
/* from https://github.com/raspberrypi/pico-examples/blob/master/pio/ws2812/ws2812.pio */
17+
18+
// constants WS2812
19+
// static constexpr uint32_t T0H = 350; // ns
20+
// static constexpr uint32_t T0L = 800;
21+
// static constexpr uint32_t T1H = 700;
22+
// static constexpr uint32_t T1L = 600;
23+
24+
// constants WS2812B
25+
static constexpr uint32_t T0H = 400; // ns
26+
static constexpr uint32_t T0L = 850;
27+
static constexpr uint32_t T1H = 850;
28+
static constexpr uint32_t T1L = 400;
29+
30+
static constexpr uint32_t TimeScale = 50;
31+
32+
static constexpr uint32_t TH_Common = T0H/TimeScale;
33+
static constexpr uint32_t TH_Add = (T1H-T0H)/TimeScale;
34+
static constexpr uint32_t TL_Common = T1L/TimeScale;
35+
static constexpr uint32_t TL_Add = (T0L-T1L)/TimeScale;
36+
37+
static constexpr uint32_t T3 = 12;//
38+
39+
// labels
40+
struct bitloop {};
41+
struct do_one {};
42+
struct do_zero {};
43+
44+
45+
/*
46+
.wrap_target
47+
bitloop:
48+
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
49+
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
50+
do_one:
51+
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
52+
do_zero:
53+
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
54+
.wrap
55+
*/
56+
// PIO program
57+
static constexpr auto pio_prog = modm::platform::PIOProgram::begin()
58+
.sideset<1>()
59+
.wrapTarget()
60+
.label<bitloop>()
61+
.instr(pio::Out().x<1>() .side<0>().delay<TL_Common-1>())
62+
.instr(pio::Jmp().not_x().to<do_zero>() .side<1>().delay<TH_Common-1>())
63+
.label<do_one>()
64+
.instr(pio::Jmp().to<bitloop>() .side<1>().delay<TH_Add-1>())
65+
.label<do_zero>()
66+
.instr(pio::Nop() .side<0>().delay<TL_Add-1>())
67+
.wrap()
68+
.end();
69+
70+
71+
struct HW
72+
{
73+
using PIO = modm::platform::Pio0;
74+
using PIO_SM = PIO::StateMachine<0>;
75+
using DataGpio = modm::platform::GpioOutput23;
76+
};
77+
78+
79+
int
80+
main()
81+
{
82+
83+
84+
Board::initialize();
85+
86+
HW::DataGpio::setOutput(Gpio::OutputType::PushPull, Gpio::SlewRate::Fast);
87+
HW::DataGpio::setDriveStrength(Gpio::DriveStrength::mA_12);
88+
89+
auto pio_prog_offset = HW::PIO::addProgram(pio_prog);
90+
91+
HW::PIO::connect<HW::DataGpio::Pad>();
92+
93+
HW::PIO_SM::addOutput<HW::DataGpio>();
94+
95+
HW::PIO_SM::config()
96+
.setup(pio_prog_offset,pio_prog)
97+
.setSidesetPins<HW::DataGpio,1,false,false>()
98+
.setFifoJoinTx()
99+
.setOutShift<false,true,24>()
100+
.setFrequency<Board::SystemClock,1000000000/TimeScale>()
101+
.init(pio_prog_offset+pio_prog.getOffset<bitloop>());
102+
103+
HW::PIO_SM::setEnabled(true);
104+
105+
constexpr auto delay_val = 5ms;
106+
107+
while (true)
108+
{
109+
uint32_t clr = 0;
110+
while (clr!=0xff0000) {
111+
clr = clr + 0x010000;
112+
HW::PIO_SM::writeBlocking(clr);
113+
modm::delay(delay_val);
114+
}
115+
while (clr!=0x000000) {
116+
clr = clr - 0x010000;
117+
HW::PIO_SM::writeBlocking(clr);
118+
modm::delay(delay_val);
119+
}
120+
while (clr!=0x00ff00) {
121+
clr = clr + 0x000100;
122+
HW::PIO_SM::writeBlocking(clr);
123+
modm::delay(delay_val);
124+
}
125+
while (clr!=0x000000) {
126+
clr = clr - 0x000100;
127+
HW::PIO_SM::writeBlocking(clr);
128+
modm::delay(delay_val);
129+
}
130+
while (clr!=0x0000ff) {
131+
clr = clr + 0x000001;
132+
HW::PIO_SM::writeBlocking(clr);
133+
modm::delay(delay_val);
134+
}
135+
while (clr!=0x000000) {
136+
clr = clr - 0x000001;
137+
HW::PIO_SM::writeBlocking(clr);
138+
modm::delay(delay_val);
139+
}
140+
141+
}
142+
143+
return 0;
144+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<library>
2+
<extends>modm:rp-pico</extends>
3+
<options>
4+
<option name="modm:build:build.path">../../../build/rp_pico/pio_ws2812</option>
5+
</options>
6+
<modules>
7+
<module>modm:build:scons</module>
8+
<module>modm:platform:pio:0</module>
9+
</modules>
10+
</library>

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2024, 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+
14+
class Instance(Module):
15+
def __init__(self, instance):
16+
self.instance = instance
17+
18+
def init(self, module):
19+
module.name = str(self.instance)
20+
module.description = "PIO {} instance".format(self.instance)
21+
22+
def prepare(self, module, options):
23+
return True
24+
25+
def build(self, env):
26+
properties = {
27+
"id": self.instance,
28+
}
29+
env.substitutions = properties
30+
env.outbasepath = "modm/src/modm/platform/pio"
31+
32+
env.template("pio.hpp.in", "pio_{}.hpp".format(self.instance))
33+
env.template("pio.cpp.in", "pio_{}.cpp".format(self.instance))
34+
35+
36+
37+
def init(module):
38+
module.name = ":platform:pio"
39+
module.description = "Programmable IO block (PIO)"
40+
41+
def prepare(module, options):
42+
device = options[":target"]
43+
if not device.has_driver("pio:rp*"):
44+
return False
45+
46+
module.depends(
47+
":platform:gpio",
48+
":platform:clockgen",
49+
":architecture:interrupt",
50+
":processing:resumable")
51+
52+
for instance in listify(device.get_driver("pio")["instance"]):
53+
module.add_submodule(Instance(instance))
54+
55+
return True
56+
57+
def build(env):
58+
env.substitutions = {
59+
"use_fiber": env.query(":processing:fiber:__enabled", False)
60+
}
61+
env.outbasepath = "modm/src/modm/platform/pio"
62+
env.copy("pio_asm.hpp")
63+
env.copy("pio_program.hpp")
64+
env.copy("pio_sm.hpp")
65+
env.template("pio_sm_impl.hpp.in", "pio_sm_impl.hpp")
66+
pass

src/modm/platform/pio/rp/pio.cpp.in

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 <modm/architecture/driver/atomic/queue.hpp>
13+
#include <modm/architecture/interface/atomic_lock.hpp>
14+
#include <modm/architecture/interface/interrupt.hpp>
15+
#include <modm/platform/core/resets.hpp>
16+
17+
#include "../device.hpp"
18+
19+
#include "pio_{{ id }}.hpp"
20+
21+
// ----------------------------------------------------------------------------
22+
23+
namespace modm::platform
24+
{
25+
uint32_t Pio{{ id }}::used_instruction_space = 0;
26+
}
27+
28+
29+
30+
// ----------------------------------------------------------------------------
31+
MODM_ISR(PIO{{ id }}_IRQ_0)
32+
{
33+
34+
}
35+
36+
MODM_ISR(PIO{{ id }}_IRQ_1)
37+
{
38+
39+
}

src/modm/platform/pio/rp/pio.hpp.in

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
#pragma once
13+
14+
#include <modm/architecture/interface/peripheral.hpp>
15+
#include <modm/platform/core/peripherals.hpp>
16+
#include <modm/platform/gpio/connector.hpp>
17+
#include <modm/math/algorithm/prescaler.hpp>
18+
19+
#include "pio_program.hpp"
20+
#include "pio_sm.hpp"
21+
#include <hardware/structs/pio.h>
22+
23+
namespace modm::platform
24+
{
25+
26+
27+
/**
28+
* Programmable IO block (PIO)
29+
*
30+
* @ingroup modm_platform_pio
31+
* @author Andrey Kunitsyn
32+
*/
33+
class Pio{{ id }} : public ::modm::PeripheralDriver
34+
{
35+
static inline pio_hw_t& pio() { return *pio{{ id }}_hw; }
36+
37+
static uint32_t used_instruction_space;
38+
static int findOffset(const PIOProgram& prg) {
39+
uint32_t program_mask = (1u << prg.length) - 1;
40+
if (prg.origin >= 0) {
41+
if (prg.origin > 32 - prg.length) return -1;
42+
return used_instruction_space & (program_mask << prg.origin) ? -1 : prg.origin;
43+
} else {
44+
// work down from the top always
45+
for (int i = 32 - prg.length; i >= 0; i--) {
46+
if ((used_instruction_space & (program_mask << static_cast<unsigned int>(i)))==0) {
47+
return i;
48+
}
49+
}
50+
return -1;
51+
}
52+
}
53+
static void addProgramAtOffset(size_t offset,const PIOProgram& prg) {
54+
for (uint8_t i = 0; i < prg.length; ++i) {
55+
auto& instr = prg.instructions[i];
56+
pio().instr_mem[offset + i] = instr.store(offset);
57+
}
58+
uint32_t program_mask = (1u << prg.length) - 1;
59+
used_instruction_space |= program_mask << offset;
60+
}
61+
62+
template <typename Pio,size_t SM>
63+
friend class pio::StateMachine;
64+
65+
public:
66+
static constexpr Peripheral peripherial = Peripheral::Pio{{ id }};
67+
template <size_t SM>
68+
class StateMachine : public modm::platform::pio::StateMachine<Pio{{ id }},SM> {};
69+
70+
template< class... Signals >
71+
static void
72+
connect()
73+
{
74+
using Connector = GpioConnector<peripherial, Signals...>;
75+
Connector::connect();
76+
}
77+
78+
template< class Pin >
79+
static void connectPin() {
80+
using Connector = GpioConnector<peripherial, typename Pin::Pad>;
81+
Connector::connect();
82+
}
83+
84+
template <typename Program>
85+
static size_t addProgram(const Program& prg_data) {
86+
auto prg = PIOProgram::get(prg_data);
87+
auto offset = findOffset(prg);
88+
if (offset < 0) {
89+
return offset;
90+
}
91+
auto res = static_cast<size_t>(offset);
92+
addProgramAtOffset(res,prg);
93+
return res;
94+
}
95+
};
96+
97+
} // namespace modm::platform
98+

0 commit comments

Comments
 (0)