Arduino PulseWire Transceiver Library
Loading...
Searching...
No Matches
TxDriverRP2040.h
1#pragma once
2#include <Arduino.h>
3#include <PIO.h>
4#include <RP2040.h>
5
6#include "pulse/Codec.h"
7#include "pulse/TxDriverCommon.h"
8#include "pulse/TxProtocol.h"
9#include "pulse/Vector.h"
10
11namespace pulsewire {
12
18 public:
19 TxProtocolRP2040() = default;
20
21 void setFrameSize(uint16_t size) override {
22 _frameSize = size;
23 _frameBuffer.resize(size);
24 }
25
26 bool begin(Codec* p_codec, uint8_t pin) override {
27 this->_codec = p_codec;
28 this->_txPin = pin;
29
30 if (!_codec->begin()) {
31 Logger::error("Codec initialization failed");
32 return false;
33 }
34
35 pinMode(_txPin, OUTPUT);
36
37 // Load PIO program only once
38 if (offset == 0) {
39 offset = pio.add_program(ir_tx_pio_program);
40 if (offset == 0) {
41 Logger::error("PIO program loading failed");
42 return false;
43 }
44 }
45
46 // Configure PIO state machine
47 bool pin_ok = pio.set_pin_direction(_txPin, true);
48 bool sm_ok = pio.set_sm_config(sm, offset, _txPin);
49 if (!pin_ok || !sm_ok) {
50 Logger::error("PIO state machine configuration failed");
51 return false;
52 }
53
54 return true;
55 }
56
57 void sendPreamble() override {
58 if (is_frame_closed) {
59 sum = 0;
60 output.clear();
61 _codec->getPreamble().getEdges(output);
62 is_frame_closed = false;
63 }
64 }
65
66 void sendData(const uint8_t* data, uint8_t len, uint32_t bitPeriod) override {
67 if (len == 0) return;
68
69 for (uint8_t i = 0; i < len; ++i) {
70 uint8_t b = data[i];
71 sum += b;
72 _codec->encode(b, bitPeriod, output);
73 }
74
75 // process data in chunks to avoid large buffers
76 sendEdgesPIO(output);
77 output.clear();
78 }
79
80 void sendEnd(bool& useChecksum, bool isDelayAfterFrame,
81 uint32_t bitPeriod) override {
82 if (is_frame_closed) return;
83
84 if (useChecksum) {
85 _codec->encode(sum, bitPeriod, output);
86 sum = 0;
87 }
88
90 output.push_back(OutputEdge(_codec->getEndOfFrameUs(), 0));
91 output.push_back(OutputEdge(0, 0));
92 }
93
94 sendEdgesPIO(output);
95 is_frame_closed = true;
96 }
97
98 bool isFrameClosed() const override { return is_frame_closed; }
99
100 protected:
101 Codec* _codec = nullptr;
102 uint8_t _txPin;
103 uint16_t _frameSize = 64;
104 Vector<OutputEdge> output;
105 uint8_t sum = 0;
106 bool is_frame_closed = true;
107
108 PIO pio = PIO(0);
109 uint8_t sm = 0;
110 uint32_t offset = 0;
111
112 void sendEdgesPIO(const Vector<OutputEdge>& edges) {
113 pio.sm_set_enabled(sm, true);
114
116 for (const auto& edge : edges) {
117 txBuffer.push_back(edge.pulseUs);
118 txBuffer.push_back(edge.level ? 1 : 0);
119 }
120
121 // Setup DMA transfer
127 channel_config_set_dreq(&c, pio_get_dreq(pio.pio(), sm, true));
128
130 &pio.pio()->txf[sm], // Write address (PIO TX FIFO)
131 txBuffer.data(), // Read address (buffer)
132 txBuffer.size(), // Number of transfers
133 true); // Start immediately
134
137 }
138
139 // PIO assembly for IR carrier output
140 static const char* ir_tx_pio_program;
141};
142
143const char* TxProtocolRP2040::ir_tx_pio_program = R"(
144 .program ir_tx
145 pull block
146 mov x, osr
147 pull block
148 mov y, osr
149 jmp y mark
150space:
151 set pins, 0
152 nop [31]
153 jmp x-- space
154 set pins, 0
155 jmp ir_tx
156mark:
157 set pins, 1
158 nop [carrier_half_period]
159 set pins, 0
160 nop [carrier_half_period]
161 jmp x-- mark
162 set pins, 0
163 jmp ir_tx
164)";
165
171 public:
172 TxDriverRP2040(Codec& codec, uint8_t pin, uint32_t carrierHz = CARRIER_HZ,
173 uint32_t freqHz = DEFAULT_BIT_FREQ_HZ,
174 bool useChecksum = false) {
175 begin(codec, pin, freqHz, useChecksum);
176 }
177
178 bool begin(Codec& codec, uint8_t pin, uint32_t carrierHz = CARRIER_HZ,
179 uint32_t freqHz = DEFAULT_BIT_FREQ_HZ, bool useChecksum = false) {
180 if (!TxDriverCommon::begin(protocol, codec, pin, freqHz, useChecksum)) {
181 Logger::error("TxDriverCommon initialization failed");
182 return false;
183 }
184 if (!protocol.begin(&codec, pin)) {
185 Logger::error("TxProtocolRP2040 initialization failed");
186 return false;
187 }
188 return true;
189 }
190
191 protected:
192 TxProtocolRP2040 protocol;
193
194 void sendPreamble() { protocol.sendPreamble(); }
195
196 void sendData(const uint8_t* data, uint8_t len) {
197 protocol.sendData(data, len, _bitPeriod);
198 }
199
200 void sendEnd() { protocol.sendEnd(_useChecksum, true, _bitPeriod); }
201};
202
203} // namespace pulsewire
Abstract base class for IR protocol encoding and decoding.
Definition Codec.h:41
virtual size_t encode(uint8_t byte, Vector< OutputEdge > &output)
Fill output vector with protocol-specific OutputSpec(s) for a byte.
Definition Codec.h:162
virtual bool begin(uint32_t bitFrequencyHz)
initialization method for codecs that require setup before use (e.g., loading PIO programs,...
Definition Codec.h:56
Preamble & getPreamble()
Get the preamble detector associated with this codec.
Definition Codec.h:77
static void error(const char *format,...)
Log an error message with formatting.
Definition Logger.h:37
virtual int getEdges(pulsewire::Vector< pulsewire::OutputEdge > &output) const =0
Returns the expected preamble edges for this protocol.
Provides common logic for transmitting signals using various framing modes.
RP2040-specific TxDriver implementation that uses TxProtocolRP2040 for transmission.
RP2040-specific TxProtocol implementation using PIO and DMA for precise timing.
Abstract base class for defining transmission protocols.
Definition TxProtocol.h:125
Small, header-only vector replacement for non-STL environments.
Definition Vector.h:29
void resize(size_t n)
Resize vector to n elements.
Definition Vector.h:150
void clear()
Remove all elements.
Definition Vector.h:120
void push_back(const T &value)
Add element to end of vector.
Definition Vector.h:92
T * data()
Pointer to underlying data.
Definition Vector.h:106
size_t size() const
Number of elements in vector.
Definition Vector.h:116
Specifies a single IR signal segment for protocol-agnostic transmission.
Definition Preamble.h:23