-
Notifications
You must be signed in to change notification settings - Fork 136
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[rp] PIO with compile-time assembler
- Loading branch information
1 parent
cedb3aa
commit 86ea8a5
Showing
8 changed files
with
1,280 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/* | ||
* Copyright (c) 2016, Sascha Schade | ||
* Copyright (c) 2017, Niklas Hauser | ||
* | ||
* This file is part of the modm project. | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
// ---------------------------------------------------------------------------- | ||
|
||
#include <modm/board.hpp> | ||
using namespace Board; | ||
|
||
/* from https://github.com/raspberrypi/pico-examples/blob/master/pio/ws2812/ws2812.pio */ | ||
|
||
// constants WS2812 | ||
// static constexpr uint32_t T0H = 350; // ns | ||
// static constexpr uint32_t T0L = 800; | ||
// static constexpr uint32_t T1H = 700; | ||
// static constexpr uint32_t T1L = 600; | ||
|
||
// constants WS2812B | ||
static constexpr uint32_t T0H = 400; // ns | ||
static constexpr uint32_t T0L = 850; | ||
static constexpr uint32_t T1H = 850; | ||
static constexpr uint32_t T1L = 400; | ||
|
||
static constexpr uint32_t TimeScale = 50; | ||
|
||
static constexpr uint32_t TH_Common = T0H/TimeScale; | ||
static constexpr uint32_t TH_Add = (T1H-T0H)/TimeScale; | ||
static constexpr uint32_t TL_Common = T1L/TimeScale; | ||
static constexpr uint32_t TL_Add = (T0L-T1L)/TimeScale; | ||
|
||
static constexpr uint32_t T3 = 12;// | ||
|
||
// labels | ||
struct bitloop {}; | ||
struct do_one {}; | ||
struct do_zero {}; | ||
|
||
|
||
/* | ||
.wrap_target | ||
bitloop: | ||
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls | ||
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse | ||
do_one: | ||
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse | ||
do_zero: | ||
nop side 0 [T2 - 1] ; Or drive low, for a short pulse | ||
.wrap | ||
*/ | ||
// PIO program | ||
static constexpr auto pio_prog = modm::platform::PIOProgram::begin() | ||
.sideset<1>() | ||
.wrapTarget() | ||
.label<bitloop>() | ||
.instr(pio::Out().x<1>() .side<0>().delay<TL_Common-1>()) | ||
.instr(pio::Jmp().not_x().to<do_zero>() .side<1>().delay<TH_Common-1>()) | ||
.label<do_one>() | ||
.instr(pio::Jmp().to<bitloop>() .side<1>().delay<TH_Add-1>()) | ||
.label<do_zero>() | ||
.instr(pio::Nop() .side<0>().delay<TL_Add-1>()) | ||
.wrap() | ||
.end(); | ||
|
||
|
||
struct HW | ||
{ | ||
using PIO = modm::platform::Pio0; | ||
using PIO_SM = PIO::StateMachine<0>; | ||
using DataGpio = modm::platform::GpioOutput23; | ||
}; | ||
|
||
static inline void write(uint32_t d) { | ||
while (HW::PIO_SM::txFifoFull()) {__NOP();} | ||
HW::PIO_SM::write(d << 8); | ||
} | ||
|
||
int | ||
main() | ||
{ | ||
|
||
|
||
Board::initialize(); | ||
|
||
HW::DataGpio::setOutput(Gpio::OutputType::PushPull, Gpio::SlewRate::Fast); | ||
HW::DataGpio::setDriveStrength(Gpio::DriveStrength::mA_12); | ||
|
||
auto pio_prog_offset = HW::PIO::addProgram(pio_prog); | ||
|
||
HW::PIO::connect<HW::DataGpio::Pad>(); | ||
|
||
HW::PIO_SM::addOutput<HW::DataGpio>(); | ||
|
||
HW::PIO_SM::config() | ||
.setup(pio_prog_offset,pio_prog) | ||
.setSidesetPins<HW::DataGpio,1,false,false>() | ||
.setFifoJoinTx() | ||
.setOutShift<false,true,24>() | ||
.setFrequency<Board::SystemClock,1000000000/TimeScale>() | ||
.init(pio_prog_offset+pio_prog.getOffset<bitloop>()); | ||
|
||
HW::PIO_SM::setEnabled(true); | ||
|
||
constexpr auto delay_val = 5ms; | ||
|
||
while (true) | ||
{ | ||
uint32_t clr = 0; | ||
while (clr!=0xff0000) { | ||
clr = clr + 0x010000; | ||
write(clr); | ||
modm::delay(delay_val); | ||
} | ||
while (clr!=0x000000) { | ||
clr = clr - 0x010000; | ||
write(clr); | ||
modm::delay(delay_val); | ||
} | ||
while (clr!=0x00ff00) { | ||
clr = clr + 0x000100; | ||
write(clr); | ||
modm::delay(delay_val); | ||
} | ||
while (clr!=0x000000) { | ||
clr = clr - 0x000100; | ||
write(clr); | ||
modm::delay(delay_val); | ||
} | ||
while (clr!=0x0000ff) { | ||
clr = clr + 0x000001; | ||
write(clr); | ||
modm::delay(delay_val); | ||
} | ||
while (clr!=0x000000) { | ||
clr = clr - 0x000001; | ||
write(clr); | ||
modm::delay(delay_val); | ||
} | ||
|
||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<library> | ||
<extends>modm:rp-pico</extends> | ||
<options> | ||
<option name="modm:build:build.path">../../../build/rp_pico/pio_ws2812</option> | ||
</options> | ||
<modules> | ||
<module>modm:build:scons</module> | ||
<module>modm:platform:pio:0</module> | ||
</modules> | ||
</library> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/usr/bin/env python3 | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (c) 2024, Andrey Kunitsyn | ||
# | ||
# This file is part of the modm project. | ||
# | ||
# This Source Code Form is subject to the terms of the Mozilla Public | ||
# License, v. 2.0. If a copy of the MPL was not distributed with this | ||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
# ----------------------------------------------------------------------------- | ||
|
||
|
||
class Instance(Module): | ||
def __init__(self, instance): | ||
self.instance = instance | ||
|
||
def init(self, module): | ||
module.name = str(self.instance) | ||
module.description = "PIO {} instance".format(self.instance) | ||
|
||
def prepare(self, module, options): | ||
return True | ||
|
||
def build(self, env): | ||
properties = { | ||
"id": self.instance, | ||
} | ||
env.substitutions = properties | ||
env.outbasepath = "modm/src/modm/platform/pio" | ||
|
||
env.template("pio.hpp.in", "pio_{}.hpp".format(self.instance)) | ||
env.template("pio.cpp.in", "pio_{}.cpp".format(self.instance)) | ||
|
||
|
||
|
||
def init(module): | ||
module.name = ":platform:pio" | ||
module.description = "Programmable IO block (PIO)" | ||
|
||
def prepare(module, options): | ||
device = options[":target"] | ||
if not device.has_driver("pio:rp*"): | ||
return False | ||
|
||
module.depends( | ||
":platform:gpio", | ||
":platform:clockgen", | ||
":architecture:interrupt") | ||
|
||
for instance in listify(device.get_driver("pio")["instance"]): | ||
module.add_submodule(Instance(instance)) | ||
|
||
return True | ||
|
||
def build(env): | ||
env.outbasepath = "modm/src/modm/platform/pio" | ||
env.copy("pio_asm.hpp") | ||
env.copy("pio_program.hpp") | ||
env.copy("pio_sm.hpp") | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* Copyright (c) 2022, Andrey Kunitsyn | ||
* | ||
* This file is part of the modm project. | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
// ---------------------------------------------------------------------------- | ||
|
||
#include <modm/architecture/driver/atomic/queue.hpp> | ||
#include <modm/architecture/interface/atomic_lock.hpp> | ||
#include <modm/architecture/interface/interrupt.hpp> | ||
#include <modm/platform/core/resets.hpp> | ||
|
||
#include "../device.hpp" | ||
|
||
#include "pio_{{ id }}.hpp" | ||
|
||
// ---------------------------------------------------------------------------- | ||
|
||
namespace modm::platform | ||
{ | ||
uint32_t Pio{{ id }}::used_instruction_space = 0; | ||
} | ||
|
||
|
||
|
||
// ---------------------------------------------------------------------------- | ||
MODM_ISR(PIO{{ id }}_IRQ_0) | ||
{ | ||
|
||
} | ||
|
||
MODM_ISR(PIO{{ id }}_IRQ_1) | ||
{ | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* Copyright (c) 2022, Andrey Kunitsyn | ||
* | ||
* This file is part of the modm project. | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
// ---------------------------------------------------------------------------- | ||
|
||
#pragma once | ||
|
||
#include <modm/architecture/interface/peripheral.hpp> | ||
#include <modm/platform/core/peripherals.hpp> | ||
#include <modm/platform/gpio/connector.hpp> | ||
#include <modm/math/algorithm/prescaler.hpp> | ||
|
||
#include "pio_program.hpp" | ||
#include "pio_sm.hpp" | ||
#include <hardware/structs/pio.h> | ||
|
||
namespace modm::platform | ||
{ | ||
|
||
|
||
/** | ||
* Programmable IO block (PIO) | ||
* | ||
* @ingroup modm_platform_pio | ||
* @author Andrey Kunitsyn | ||
*/ | ||
class Pio{{ id }} : public ::modm::PeripheralDriver | ||
{ | ||
static inline pio_hw_t& pio() { return *pio{{ id }}_hw; } | ||
|
||
static uint32_t used_instruction_space; | ||
static int findOffset(const PIOProgram& prg) { | ||
uint32_t program_mask = (1u << prg.length) - 1; | ||
if (prg.origin >= 0) { | ||
if (prg.origin > 32 - prg.length) return -1; | ||
return used_instruction_space & (program_mask << prg.origin) ? -1 : prg.origin; | ||
} else { | ||
// work down from the top always | ||
for (int i = 32 - prg.length; i >= 0; i--) { | ||
if ((used_instruction_space & (program_mask << static_cast<unsigned int>(i)))==0) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
} | ||
} | ||
static void addProgramAtOffset(size_t offset,const PIOProgram& prg) { | ||
for (uint8_t i = 0; i < prg.length; ++i) { | ||
auto& instr = prg.instructions[i]; | ||
pio().instr_mem[offset + i] = instr.store(offset); | ||
} | ||
uint32_t program_mask = (1u << prg.length) - 1; | ||
used_instruction_space |= program_mask << offset; | ||
} | ||
|
||
template <typename Pio,size_t SM> | ||
friend class pio::StateMachine; | ||
|
||
public: | ||
static constexpr Peripheral peripherial = Peripheral::Pio{{ id }}; | ||
template <size_t SM> | ||
class StateMachine : public modm::platform::pio::StateMachine<Pio{{ id }},SM> {}; | ||
|
||
template< class... Signals > | ||
static void | ||
connect() | ||
{ | ||
using Connector = GpioConnector<peripherial, Signals...>; | ||
Connector::connect(); | ||
} | ||
|
||
template< class Pin > | ||
static void connectPin() { | ||
using Connector = GpioConnector<peripherial, typename Pin::Pad>; | ||
Connector::connect(); | ||
} | ||
|
||
template <typename Program> | ||
static size_t addProgram(const Program& prg_data) { | ||
auto prg = PIOProgram::get(prg_data); | ||
auto offset = findOffset(prg); | ||
if (offset < 0) { | ||
return offset; | ||
} | ||
auto res = static_cast<size_t>(offset); | ||
addProgramAtOffset(res,prg); | ||
return res; | ||
} | ||
}; | ||
|
||
} // namespace modm::platform | ||
|
Oops, something went wrong.