diff --git a/examples/rp_pico/pio_ws2812/main.cpp b/examples/rp_pico/pio_ws2812/main.cpp new file mode 100644 index 0000000000..846c1d1028 --- /dev/null +++ b/examples/rp_pico/pio_ws2812/main.cpp @@ -0,0 +1,79 @@ +/* + * 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 +#include "ws2812.pio.hpp" +using namespace Board; + + +struct HW +{ + using PIO = modm::platform::Pio0; + using PIO_SM = PIO::StateMachine<0>; + using DataGpio = modm::platform::GpioOutput23; +}; + + +int +main() +{ + + + Board::initialize(); + + HW::DataGpio::setOutput(Gpio::OutputType::PushPull, Gpio::SlewRate::Fast); + HW::DataGpio::setDriveStrength(Gpio::DriveStrength::mA_12); + + auto pio_program_offset = HW::PIO::addProgram(ws2812::program); + + ws2812::init(pio_program_offset); + + constexpr auto delay_val = 5ms; + + while (true) + { + uint32_t clr = 0; + while (clr!=0xff0000) { + clr = clr + 0x010000; + HW::PIO_SM::writeBlocking(clr); + modm::delay(delay_val); + } + while (clr!=0x000000) { + clr = clr - 0x010000; + HW::PIO_SM::writeBlocking(clr); + modm::delay(delay_val); + } + while (clr!=0x00ff00) { + clr = clr + 0x000100; + HW::PIO_SM::writeBlocking(clr); + modm::delay(delay_val); + } + while (clr!=0x000000) { + clr = clr - 0x000100; + HW::PIO_SM::writeBlocking(clr); + modm::delay(delay_val); + } + while (clr!=0x0000ff) { + clr = clr + 0x000001; + HW::PIO_SM::writeBlocking(clr); + modm::delay(delay_val); + } + while (clr!=0x000000) { + clr = clr - 0x000001; + HW::PIO_SM::writeBlocking(clr); + modm::delay(delay_val); + } + + } + + return 0; +} diff --git a/examples/rp_pico/pio_ws2812/project.xml b/examples/rp_pico/pio_ws2812/project.xml new file mode 100644 index 0000000000..c932d97d93 --- /dev/null +++ b/examples/rp_pico/pio_ws2812/project.xml @@ -0,0 +1,10 @@ + + modm:rp-pico + + + + + modm:build:scons + modm:platform:pio:0 + + diff --git a/examples/rp_pico/pio_ws2812/ws2812.pio.hpp b/examples/rp_pico/pio_ws2812/ws2812.pio.hpp new file mode 100644 index 0000000000..fb58096c3e --- /dev/null +++ b/examples/rp_pico/pio_ws2812/ws2812.pio.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include + +/* from https://github.com/raspberrypi/pico-examples/blob/master/pio/ws2812/ws2812.pio */ + +/* .program ws2812 */ +namespace ws2812 +{ + using namespace modm::platform::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; + + // labels + struct bitloop {}; + struct do_one {}; + struct do_zero {}; + + /* + .side_set 1 + .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 program = PIOProgram::begin() + .sideset<1> + .wrapTarget<> + .label + .instr(pio::Out.x<1> .side<0>.delay) + .instr(pio::Jmp.not_x.to .side<1>.delay) + .label + .instr(pio::Jmp.to .side<1>.delay) + .label + .instr(pio::Nop .side<0>.delay) + .wrap<> + .end(); + + static_assert(program.code[0]==0x6721,"invalid program code[0]"); + static_assert(program.code[1]==0x1723,"invalid program code[1]"); + /* + pio_gpio_init(pio, pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); + + pio_sm_config c = ws2812_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, pin); + sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; + float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); + */ + // initialization code + template + static constexpr void init(uint16_t offset) { + PIO::template connect(); + SM::template addOutput(); + + SM::config(offset,program) + .template setSidesetPins() + .template setOutShift() + .setFifoJoinTx() + .template setFrequency() + .init(offset+program.getOffset()); + SM::setEnabled(true); + } + +} diff --git a/src/modm/platform/gpio/rp/base.hpp.in b/src/modm/platform/gpio/rp/base.hpp.in index 3cf164344e..b3d0b74dba 100644 --- a/src/modm/platform/gpio/rp/base.hpp.in +++ b/src/modm/platform/gpio/rp/base.hpp.in @@ -59,13 +59,6 @@ struct Gpio PushPull }; - enum class - OutputSpeed - { - Slow = 0, - Fast = 1, - }; - enum class DriveStrength { diff --git a/src/modm/platform/gpio/rp/set.hpp.in b/src/modm/platform/gpio/rp/set.hpp.in index 73794548ae..ae8a865f44 100644 --- a/src/modm/platform/gpio/rp/set.hpp.in +++ b/src/modm/platform/gpio/rp/set.hpp.in @@ -62,15 +62,15 @@ public: setOutput(); } - static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::Slow) + static void setOutput(OutputType type, SlewRate speed = SlewRate::Slow) { setOutput(); configure(type, speed); } - static void configure(OutputType, OutputSpeed speed = OutputSpeed::Slow) + static void configure(OutputType, SlewRate speed = SlewRate::Slow) { - (PortRegs::set_speed(Gpios::pin, speed), ...); + (PortRegs::set_slewrate(Gpios::pin, uint8_t(speed)), ...); } static void setInput() @@ -96,6 +96,11 @@ public: (PortRegs::set_slewrate(Gpios::pin, uint8_t(rate)), ...); } + static void setDriveStrength(DriveStrength drive) + { + (PortRegs::set_drive(Gpios::pin, uint8_t(drive)), ...); + } + static void set() { %% for port, id in ports.items() diff --git a/src/modm/platform/gpio/rp/static.hpp.in b/src/modm/platform/gpio/rp/static.hpp.in index 4829f918e8..31f72c4165 100644 --- a/src/modm/platform/gpio/rp/static.hpp.in +++ b/src/modm/platform/gpio/rp/static.hpp.in @@ -45,14 +45,14 @@ public: static void setFunction(uint8_t func) { Regs::set_funcsel(pin, func); } static void setDriveStrength(DriveStrength strength) - { Regs::set_drive(pin, strength); } + { Regs::set_drive(pin, uint8_t(strength)); } public: static void setOutput() { PinSet::setOutput(); } static void setOutput(bool value) { PinSet::setOutput(value); } - static void setOutput(OutputType type, OutputSpeed speed=OutputSpeed::Slow) + static void setOutput(OutputType type, SlewRate speed=SlewRate::Slow) { PinSet::setOutput(type, speed); } - static void configure(OutputType type, OutputSpeed speed=OutputSpeed::Slow) + static void configure(OutputType type, SlewRate speed=SlewRate::Slow) { PinSet::configure(type, speed); } static void set() { PinSet::set(); } diff --git a/src/modm/platform/pio/rp/module.lb b/src/modm/platform/pio/rp/module.lb new file mode 100644 index 0000000000..8189850770 --- /dev/null +++ b/src/modm/platform/pio/rp/module.lb @@ -0,0 +1,66 @@ +#!/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", + ":processing:resumable") + + for instance in listify(device.get_driver("pio")["instance"]): + module.add_submodule(Instance(instance)) + + return True + +def build(env): + env.substitutions = { + "use_fiber": env.query(":processing:fiber:__enabled", False) + } + env.outbasepath = "modm/src/modm/platform/pio" + env.copy("pio_asm.hpp") + env.copy("pio_program.hpp") + env.copy("pio_sm.hpp") + env.template("pio_sm_impl.hpp.in", "pio_sm_impl.hpp") + pass diff --git a/src/modm/platform/pio/rp/pio.cpp.in b/src/modm/platform/pio/rp/pio.cpp.in new file mode 100644 index 0000000000..0df95ea6fe --- /dev/null +++ b/src/modm/platform/pio/rp/pio.cpp.in @@ -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 +#include +#include +#include + +#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) +{ + +} diff --git a/src/modm/platform/pio/rp/pio.hpp.in b/src/modm/platform/pio/rp/pio.hpp.in new file mode 100644 index 0000000000..fe688d8366 --- /dev/null +++ b/src/modm/platform/pio/rp/pio.hpp.in @@ -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 +#include +#include +#include + +#include "pio_program.hpp" +#include "pio_sm.hpp" +#include + +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(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 + friend class pio::implementation::StateMachine; + + public: + static constexpr Peripheral peripherial = Peripheral::Pio{{ id }}; + template + class StateMachine : public modm::platform::pio::implementation::StateMachine {}; + + template< class... Signals > + static void + connect() + { + using Connector = GpioConnector; + Connector::connect(); + } + + template< class Pin > + static void connectPin() { + using Connector = GpioConnector; + Connector::connect(); + } + + template + 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(offset); + addProgramAtOffset(res,prg); + return res; + } + }; + +} // namespace modm::platform + diff --git a/src/modm/platform/pio/rp/pio_asm.hpp b/src/modm/platform/pio/rp/pio_asm.hpp new file mode 100644 index 0000000000..f87d899583 --- /dev/null +++ b/src/modm/platform/pio/rp/pio_asm.hpp @@ -0,0 +1,830 @@ +/* + * 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 +#include +#include + +namespace modm::platform::pio +{ + namespace implementation + { + struct Instruction { + uint16_t value; + constexpr Instruction(uint16_t val) : value(val) {} + constexpr uint16_t store(size_t offset) const { + return ((value & 0xe000) == 0x0000) ? (value + offset) : value; + } + constexpr Instruction operator | (uint16_t params) const { + return Instruction(value | params); + } + constexpr bool operator == (uint16_t val) const { + return value == val; + } + }; + + enum class Opcode : uint16_t { + JMP = 0x0000 << 13, + WAIT = 0x0001 << 13, + IN = 0x0002 << 13, + OUT = 0x0003 << 13, + PUSH = 0x0004 << 13, + PULL = (0x0004 << 13) | (0x0001 << 7), + MOV = 0x0005 << 13, + IRQ = 0x0006 << 13, + SET = 0x0007 << 13, + }; + + struct OptionalValue { + uint16_t value{0}; + bool is_set{false}; + }; + + static constexpr uint16_t NO_SIDE_VALUE = 0xffff; + + template + struct SideDelay { + static constexpr const uint16_t side_value = (side == NO_SIDE_VALUE) ? 0 : side; + static constexpr const bool side_value_present = side != NO_SIDE_VALUE; + static constexpr const uint16_t delay_value = delay; + }; + + template + struct InstructionEncoding { + static constexpr const Opcode opcode = Res::opcode; + static constexpr const uint16_t DELAY_SIDESET_SHIFT = 8; + static constexpr const uint16_t DELAY_SIDESET_MASK = 0x1f << DELAY_SIDESET_SHIFT; + static constexpr const uint16_t ARG1_SHIFT = 5; + + using SideDelayConfig = SideDelay<>; + + template + struct SideValue : Res { + using SideDelayConfig = SideDelay; + constexpr SideValue() {} + template + struct DelayValue : Res { + constexpr DelayValue() {} + using SideDelayConfig = SideDelay; + template + static constexpr Instruction encode(const Encoder& coder) { + return Res::encode(coder) | (coder.template encodeSideDelay() << DELAY_SIDESET_SHIFT); + } + }; + template + static constexpr auto delay = DelayValue(); + + template + static constexpr Instruction encode(const Encoder& coder) { + return Res::encode(coder) | (coder.template encodeSideDelay() << DELAY_SIDESET_SHIFT); + } + }; + template + struct DelayValue : Res { + constexpr DelayValue() {} + using SideDelayConfig = SideDelay; + template + static constexpr Instruction encode(const Encoder& coder) { + return Res::encode(coder) | (coder.template encodeSideDelay() << DELAY_SIDESET_SHIFT); + } + }; + + constexpr InstructionEncoding() {} + template + static constexpr auto side = SideValue(); + template + static constexpr auto delay = DelayValue(); + + template + static constexpr Instruction encode(const Encoder& ) { + return Instruction(uint16_t(opcode)); + } + }; + + template + static constexpr bool check(const T& t,uint16_t value) { + struct Encoder { + constexpr uint16_t encodeSideDelay(const OptionalValue& /*side*/,uint16_t /*delay*/) const { + return 0; + } + }; + return t.encode(Encoder()) == value; + } + + enum class JmpCondition : uint16_t { + ALWAYS = 0x00, + NOT_X = 0x01, + X_DEC = 0x02, + NOT_Y = 0x03, + Y_DEC = 0x04, + X_NE_Y = 0x05, + PIN = 0x06, + NOT_ORSE = 0x07, + }; + template + static constexpr uint16_t getJmpAddr(const Encoder& coder,uint16_t addr_value) { + if constexpr (std::is_same_v) { + return addr_value; + } else { + return coder.template getOffset