ESPixelStick Firmware
Firmware for the ESPixelStick
Loading...
Searching...
No Matches
OutputRmt.hpp
Go to the documentation of this file.
1#pragma once
2/*
3* OutputRmt.hpp - RMT driver code for ESPixelStick RMT Channel
4*
5* Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver
6* Copyright (c) 2015, 2026 Shelby Merrick
7* http://www.forkineye.com
8*
9* This program is provided free for you to use in any way that you wish,
10* subject to the laws and regulations where you are using it. Due diligence
11* is strongly suggested before using this code. Please give credit where due.
12*
13* The Author makes no warranty of any kind, express or implied, with regard
14* to this program or the documentation contained in this document. The
15* Author shall not be liable in any event for incidental or consequential
16* damages in connection with, or arising out of, the furnishing, performance
17* or use of these programs.
18*
19*/
20
21#include "ESPixelStick.h"
22#ifdef ARDUINO_ARCH_ESP32
23#include <driver/rmt.h>
24#include <hal/rmt_ll.h>
25#include "OutputPixel.hpp"
26#include "OutputSerial.hpp"
27
28class c_OutputRmt
29{
30public:
31 enum RmtDataBitIdType_t
32 {
33 RMT_DATA_BIT_ZERO_ID = 0, // 0 UART 00
34 RMT_DATA_BIT_ONE_ID, // 1 UART 01
35 RMT_DATA_BIT_TWO_ID, // 2 UART 10
36 RMT_DATA_BIT_THREE_ID, // 3 UART 11
37 RMT_INTERFRAME_GAP_ID, // 4 UART Break / MAB
38 RMT_STARTBIT_ID, // 5
39 RMT_STOPBIT_ID, // 6 UART Stop/start bit
40 RMT_END_OF_FRAME, // 7
41 RMT_LIST_END, // 8 This must be last
42 RMT_INVALID_VALUE,
43 RMT_NUM_BIT_TYPES = RMT_LIST_END,
44 RMT_STOP_START_BIT_ID = RMT_STOPBIT_ID,
45 };
46
47 struct ConvertIntensityToRmtDataStreamEntry_t
48 {
49 rmt_item32_t Translation;
50 RmtDataBitIdType_t Id;
51 };
52 typedef ConvertIntensityToRmtDataStreamEntry_t CitrdsArray_t;
53
54 struct OutputRmtConfig_t
55 {
56 rmt_channel_t RmtChannelId = rmt_channel_t(-1);
57 gpio_num_t DataPin = gpio_num_t(-1);
58 rmt_idle_level_t idle_level = rmt_idle_level_t::RMT_IDLE_LEVEL_LOW;
59 uint32_t IntensityDataWidth = 8;
60 bool SendInterIntensityBits = false;
61 bool SendEndOfFrameBits = false;
62 uint8_t NumFrameStartBits = 1;
63 uint8_t NumFrameStopBits = 1;
64 uint8_t NumIdleBits = 6;
65 enum DataDirection_t
66 {
67 MSB2LSB = 0,
68 LSB2MSB
69 };
70 DataDirection_t DataDirection = DataDirection_t::MSB2LSB;
71 const CitrdsArray_t *CitrdsArray = nullptr;
72
73 c_OutputPixel *pPixelDataSource = nullptr;
74 #if defined(SUPPORT_OutputType_FireGod) || defined(SUPPORT_OutputType_DMX) || defined(SUPPORT_OutputType_Serial) || defined(SUPPORT_OutputType_Renard)
75 c_OutputSerial *pSerialDataSource = nullptr;
76 #endif // defined(SUPPORT_OutputType_FireGod) || defined(SUPPORT_OutputType_DMX) || defined(SUPPORT_OutputType_Serial) || defined(SUPPORT_OutputType_Renard)
77 };
78
79struct isrTxFlags_t
80{
81 uint32_t End = 0;
82 uint32_t Err = 0;
83 uint32_t Thres = 0;
84};
85
86private:
87#ifdef CONFIG_IDF_TARGET_ESP32S3
88#define MAX_NUM_RMT_CHANNELS 4
89#else
90#define MAX_NUM_RMT_CHANNELS 8
91#endif // def CONFIG_IDF_TARGET_ESP32S3
92
93#define RMT_INT_BIT uint32_t(1 << uint32_t (OutputRmtConfig.RmtChannelId))
94#define InterrupsAreEnabled (0 != (RMT.int_ena.val & RMT_INT_BIT))
95
96#define _NUM_RMT_SLOTS (sizeof(RMTMEM.chan[0].data32) / sizeof(RMTMEM.chan[0].data32[0]))
97
98 const uint32_t NUM_RMT_SLOTS = _NUM_RMT_SLOTS;
99 OutputRmtConfig_t OutputRmtConfig;
100
101 rmt_item32_t Intensity2Rmt[RmtDataBitIdType_t::RMT_LIST_END];
102 bool OutputIsPaused = false;
103
104 uint32_t NumRmtSlotsPerIntensityValue = 8;
105 uint32_t NumRmtSlotOverruns = 0;
106 const uint32_t MaxNumRmtSlotsPerInterrupt = (_NUM_RMT_SLOTS/2);
107
108 #define NumSendBufferSlots 64
109 rmt_item32_t SendBuffer[NumSendBufferSlots];
110 uint32_t RmtBufferWriteIndex = 0;
111 uint32_t SendBufferWriteIndex = 0;
112 uint32_t SendBufferReadIndex = 0;
113 uint32_t NumUsedEntriesInSendBuffer = 0;
114
115#define MIN_FRAME_TIME_MS 25
116
117 uint32_t TxIntensityDataStartingMask = 0x80;
118 RmtDataBitIdType_t InterIntensityValueId = RMT_INVALID_VALUE;
119
120 inline void IRAM_ATTR ISR_TransferIntensityDataToRMT (uint32_t NumEntriesToTransfer);
121 inline void IRAM_ATTR ISR_CreateIntensityData ();
122 inline void IRAM_ATTR ISR_WriteToBuffer(uint32_t value);
123 inline bool IRAM_ATTR ISR_MoreDataToSend();
124 inline bool IRAM_ATTR ISR_GetNextIntensityToSend(uint32_t &DataToSend);
125 inline void StartNewDataFrame();
126 inline void ResetRmtBlockPointers();
127
128#ifndef HasBeenInitialized
129 bool HasBeenInitialized = false;
130#endif // ndef HasBeenInitialized
131
132 TaskHandle_t SendIntensityDataTaskHandle = NULL;
133
134public:
135 c_OutputRmt ();
136 virtual ~c_OutputRmt ();
137
138 void Begin (OutputRmtConfig_t config, c_OutputCommon * pParent);
139 bool StartNewFrame ();
140 bool StartNextFrame () { return ((nullptr != pParent) & (!OutputIsPaused)) ? pParent->RmtPoll() : false; }
141 void GetStatus (ArduinoJson::JsonObject& jsonStatus);
142 void PauseOutput (bool State);
143 void GetDriverName (String &value) { value = CN_RMT; }
144
145__attribute__((always_inline))
146inline void IRAM_ATTR DisableRmtInterrupts()
147{
148 rmt_ll_enable_tx_thres_interrupt(&RMT, OutputRmtConfig.RmtChannelId, false);
149 rmt_ll_enable_tx_end_interrupt(&RMT, OutputRmtConfig.RmtChannelId, false);
150 rmt_ll_enable_tx_err_interrupt(&RMT, OutputRmtConfig.RmtChannelId, false);
151 ClearRmtInterrupts();
152}
153
154__attribute__((always_inline))
155inline void IRAM_ATTR EnableRmtInterrupts()
156{
157 rmt_ll_enable_tx_thres_interrupt(&RMT, OutputRmtConfig.RmtChannelId, true);
158 rmt_ll_enable_tx_end_interrupt(&RMT, OutputRmtConfig.RmtChannelId, true);
159 rmt_ll_enable_tx_err_interrupt(&RMT, OutputRmtConfig.RmtChannelId, true);
160}
161
162__attribute__((always_inline))
163inline void IRAM_ATTR ClearRmtInterrupts()
164{
165 rmt_ll_clear_tx_thres_interrupt(&RMT, OutputRmtConfig.RmtChannelId);
166 rmt_ll_clear_tx_end_interrupt(&RMT, OutputRmtConfig.RmtChannelId);
167 rmt_ll_clear_tx_err_interrupt(&RMT, OutputRmtConfig.RmtChannelId);
168}
169
170 bool DriverIsSendingIntensityData() {return 0 != InterrupsAreEnabled;}
171
172#define RMT_ClockRate 80000000.0
173#define RMT_Clock_Divisor 2.0
174#define RMT_TickLengthNS float ( (1/ (RMT_ClockRate/RMT_Clock_Divisor)) * float(NanoSecondsInASecond))
175
176 void UpdateBitXlatTable(const CitrdsArray_t * CitrdsArray);
177 bool ValidateBitXlatTable(const CitrdsArray_t * CitrdsArray);
178 void SetIntensity2Rmt (rmt_item32_t NewValue, RmtDataBitIdType_t ID) { Intensity2Rmt[ID] = NewValue; }
179
180 bool ThereIsDataToSend = false;
181
182 void IRAM_ATTR ISR_Handler (isrTxFlags_t isrFlags);
183 c_OutputCommon * pParent = nullptr;
184
185// #define USE_RMT_DEBUG_COUNTERS
186#ifdef USE_RMT_DEBUG_COUNTERS
187// #define IncludeBufferData
188 // debug counters
189 uint32_t DataCallbackCounter = 0;
190 uint32_t DataTaskcounter = 0;
191 uint32_t ISRcounter = 0;
192 uint32_t FrameStartCounter = 0;
193 uint32_t SendBlockIsrCounter = 0;
194 uint32_t RanOutOfData = 0;
195 uint32_t UnknownISRcounter = 0;
196 uint32_t IntTxEndIsrCounter = 0;
197 uint32_t IntTxThrIsrCounter = 0;
198 uint32_t RxIsr = 0;
199 uint32_t ErrorIsr = 0;
200 uint32_t IntensityValuesSent = 0;
201 uint32_t IntensityBitsSent = 0;
202 uint32_t IntensityValuesSentLastFrame = 0;
203 uint32_t IntensityBitsSentLastFrame = 0;
204 uint32_t IncompleteFrame = 0;
205 uint32_t BitTypeCounters[RmtDataBitIdType_t::RMT_NUM_BIT_TYPES];
206 uint32_t RmtEntriesTransfered = 0;
207 uint32_t RmtXmtFills = 0;
208 uint32_t RmtWhiteDetected = 0;
209 uint32_t FailedToSendAllData = 0;
210
211#define RMT_DEBUG_COUNTER(p) p
212
213#else
214
215#define RMT_DEBUG_COUNTER(p)
216
217#endif // def USE_RMT_DEBUG_COUNTERS
218
219};
220#endif // def #ifdef ARDUINO_ARCH_ESP32
const CN_PROGMEM char CN_RMT[]
Definition ConstNames.cpp:183
Definition OutputCommon.hpp:31
Definition OutputPixel.hpp:28
struct FSEQParsedRangeEntry __attribute__
config_t config
Definition main.cpp:98
void GetDriverName(String &Name)
Definition main.cpp:120