ESPHome  2024.12.2
led_strip.cpp
Go to the documentation of this file.
1 #include "led_strip.h"
2 
3 #ifdef USE_BK72XX
4 
5 #include "esphome/core/helpers.h"
6 #include "esphome/core/log.h"
7 
8 extern "C" {
9 #include "rtos_pub.h"
10 #include "spi.h"
11 #include "arm_arch.h"
12 #include "general_dma_pub.h"
13 #include "gpio_pub.h"
14 #include "icu_pub.h"
15 #undef SPI_DAT
16 #undef SPI_BASE
17 };
18 
19 static const uint32_t SPI_TX_DMA_CHANNEL = GDMA_CHANNEL_3;
20 
21 // TODO: Check if SPI_PERI_CLK_DCO depends on the chip variant
22 static const uint32_t SPI_PERI_CLK_26M = 26000000;
23 static const uint32_t SPI_PERI_CLK_DCO = 120000000;
24 
25 static const uint32_t SPI_BASE = 0x00802700;
26 static const uint32_t SPI_DAT = SPI_BASE + 3 * 4;
27 static const uint32_t SPI_CONFIG = SPI_BASE + 1 * 4;
28 
29 static const uint32_t SPI_TX_EN = 1 << 0;
30 static const uint32_t CTRL_NSSMD_3 = 1 << 17;
31 static const uint32_t SPI_TX_FINISH_EN = 1 << 2;
32 static const uint32_t SPI_RX_FINISH_EN = 1 << 3;
33 
34 namespace esphome {
35 namespace beken_spi_led_strip {
36 
37 static const char *const TAG = "beken_spi_led_strip";
38 
39 struct spi_data_t {
40  SemaphoreHandle_t dma_tx_semaphore;
41  volatile bool tx_in_progress;
42  bool first_run;
43 };
44 
45 static spi_data_t *spi_data = nullptr;
46 
47 static void set_spi_ctrl_register(unsigned long bit, bool val) {
48  uint32_t value = REG_READ(SPI_CTRL);
49  if (val == 0) {
50  value &= ~bit;
51  } else if (val == 1) {
52  value |= bit;
53  }
54  REG_WRITE(SPI_CTRL, value);
55 }
56 
57 static void set_spi_config_register(unsigned long bit, bool val) {
58  uint32_t value = REG_READ(SPI_CONFIG);
59  if (val == 0) {
60  value &= ~bit;
61  } else if (val == 1) {
62  value |= bit;
63  }
64  REG_WRITE(SPI_CONFIG, value);
65 }
66 
67 void spi_dma_tx_enable(bool enable) {
68  GDMA_CFG_ST en_cfg;
69  set_spi_config_register(SPI_TX_EN, enable ? 1 : 0);
70  en_cfg.channel = SPI_TX_DMA_CHANNEL;
71  en_cfg.param = enable ? 1 : 0;
72  sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_DMA_ENABLE, &en_cfg);
73 }
74 
75 static void spi_set_clock(uint32_t max_hz) {
76  int source_clk = 0;
77  int spi_clk = 0;
78  int div = 0;
79  uint32_t param;
80  if (max_hz > 4333000) {
81  if (max_hz > 30000000) {
82  spi_clk = 30000000;
83  } else {
84  spi_clk = max_hz;
85  }
86  sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_DOWN, &param);
87  source_clk = SPI_PERI_CLK_DCO;
88  param = PCLK_POSI_SPI;
89  sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_DCO, &param);
90  param = PWD_SPI_CLK_BIT;
91  sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, &param);
92  } else {
93  spi_clk = max_hz;
94 #if CFG_XTAL_FREQUENCE
95  source_clk = CFG_XTAL_FREQUENCE;
96 #else
97  source_clk = SPI_PERI_CLK_26M;
98 #endif
99  param = PCLK_POSI_SPI;
100  sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, &param);
101  }
102  div = ((source_clk >> 1) / spi_clk);
103  if (div < 2) {
104  div = 2;
105  } else if (div >= 255) {
106  div = 255;
107  }
108  param = REG_READ(SPI_CTRL);
109  param &= ~(SPI_CKR_MASK << SPI_CKR_POSI);
110  param |= (div << SPI_CKR_POSI);
111  REG_WRITE(SPI_CTRL, param);
112  ESP_LOGD(TAG, "target frequency: %d, actual frequency: %d", max_hz, source_clk / 2 / div);
113 }
114 
115 void spi_dma_tx_finish_callback(unsigned int param) {
116  spi_data->tx_in_progress = false;
117  xSemaphoreGive(spi_data->dma_tx_semaphore);
119 }
120 
122  ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip...");
123 
124  size_t buffer_size = this->get_buffer_size_();
125  size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
126 
128  this->buf_ = allocator.allocate(buffer_size);
129  if (this->buf_ == nullptr) {
130  ESP_LOGE(TAG, "Cannot allocate LED buffer!");
131  this->mark_failed();
132  return;
133  }
134 
135  this->effect_data_ = allocator.allocate(this->num_leds_);
136  if (this->effect_data_ == nullptr) {
137  ESP_LOGE(TAG, "Cannot allocate effect data!");
138  this->mark_failed();
139  return;
140  }
141 
142  this->dma_buf_ = allocator.allocate(dma_buffer_size);
143  if (this->dma_buf_ == nullptr) {
144  ESP_LOGE(TAG, "Cannot allocate DMA buffer!");
145  this->mark_failed();
146  return;
147  }
148 
149  memset(this->buf_, 0, buffer_size);
150  memset(this->effect_data_, 0, this->num_leds_);
151  memset(this->dma_buf_, 0, dma_buffer_size);
152 
153  uint32_t value = PCLK_POSI_SPI;
154  sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, &value);
155 
156  value = PWD_SPI_CLK_BIT;
157  sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, &value);
158 
159  if (spi_data != nullptr) {
160  ESP_LOGE(TAG, "SPI device already initialized!");
161  this->mark_failed();
162  return;
163  }
164 
165  spi_data = (spi_data_t *) calloc(1, sizeof(spi_data_t));
166  if (spi_data == nullptr) {
167  ESP_LOGE(TAG, "Cannot allocate spi_data!");
168  this->mark_failed();
169  return;
170  }
171 
172  spi_data->dma_tx_semaphore = xSemaphoreCreateBinary();
173  if (spi_data->dma_tx_semaphore == nullptr) {
174  ESP_LOGE(TAG, "TX Semaphore init faild!");
175  this->mark_failed();
176  return;
177  }
178 
179  spi_data->first_run = true;
180 
181  set_spi_ctrl_register(MSTEN, 0);
182  set_spi_ctrl_register(BIT_WDTH, 0);
183  spi_set_clock(this->spi_frequency_);
184  set_spi_ctrl_register(CKPOL, 0);
185  set_spi_ctrl_register(CKPHA, 0);
186  set_spi_ctrl_register(MSTEN, 1);
187  set_spi_ctrl_register(SPIEN, 1);
188 
189  set_spi_ctrl_register(TXINT_EN, 0);
190  set_spi_ctrl_register(RXINT_EN, 0);
191  set_spi_config_register(SPI_TX_FINISH_EN, 1);
192  set_spi_config_register(SPI_RX_FINISH_EN, 1);
193  set_spi_ctrl_register(RXOVR_EN, 0);
194  set_spi_ctrl_register(TXOVR_EN, 0);
195 
196  value = REG_READ(SPI_CTRL);
197  value &= ~CTRL_NSSMD_3;
198  value |= (1 << 17);
199  REG_WRITE(SPI_CTRL, value);
200 
201  value = GFUNC_MODE_SPI_DMA;
202  sddev_control(GPIO_DEV_NAME, CMD_GPIO_ENABLE_SECOND, &value);
203  set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, 0);
204 
205  GDMA_CFG_ST en_cfg;
206  GDMACFG_TPYES_ST init_cfg;
207  memset(&init_cfg, 0, sizeof(GDMACFG_TPYES_ST));
208 
209  init_cfg.dstdat_width = 8;
210  init_cfg.srcdat_width = 32;
211  init_cfg.dstptr_incr = 0;
212  init_cfg.srcptr_incr = 1;
213  init_cfg.src_start_addr = this->dma_buf_;
214  init_cfg.dst_start_addr = (void *) SPI_DAT; // SPI_DMA_REG4_TXFIFO
215  init_cfg.channel = SPI_TX_DMA_CHANNEL;
216  init_cfg.prio = 0; // 10
217  init_cfg.u.type4.src_loop_start_addr = this->dma_buf_;
218  init_cfg.u.type4.src_loop_end_addr = this->dma_buf_ + dma_buffer_size;
219  init_cfg.half_fin_handler = nullptr;
220  init_cfg.fin_handler = spi_dma_tx_finish_callback;
221  init_cfg.src_module = GDMA_X_SRC_DTCM_RD_REQ;
222  init_cfg.dst_module = GDMA_X_DST_GSPI_TX_REQ; // GDMA_X_DST_HSSPI_TX_REQ
223  sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_TYPE4, (void *) &init_cfg);
224  en_cfg.channel = SPI_TX_DMA_CHANNEL;
225  en_cfg.param = dma_buffer_size;
226  sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_TRANS_LENGTH, (void *) &en_cfg);
227  en_cfg.channel = SPI_TX_DMA_CHANNEL;
228  en_cfg.param = 0;
229  sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_WORK_MODE, (void *) &en_cfg);
230  en_cfg.channel = SPI_TX_DMA_CHANNEL;
231  en_cfg.param = 0;
232  sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_SRCADDR_LOOP, &en_cfg);
233 
235 
236  value = REG_READ(SPI_CONFIG);
237  value &= ~(0xFFF << 8);
238  value |= ((dma_buffer_size & 0xFFF) << 8);
239  REG_WRITE(SPI_CONFIG, value);
240 }
241 
242 void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency) {
243  this->bit0_ = bit0;
244  this->bit1_ = bit1;
245  this->spi_frequency_ = spi_frequency;
246 }
247 
249  // protect from refreshing too often
250  uint32_t now = micros();
251  if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
252  // try again next loop iteration, so that this change won't get lost
253  this->schedule_show();
254  return;
255  }
256  this->last_refresh_ = now;
257  this->mark_shown_();
258 
259  ESP_LOGVV(TAG, "Writing RGB values to bus...");
260 
261  if (spi_data == nullptr) {
262  ESP_LOGE(TAG, "SPI not initialized");
263  this->status_set_warning();
264  return;
265  }
266 
267  if (!spi_data->first_run && !xSemaphoreTake(spi_data->dma_tx_semaphore, 10 / portTICK_PERIOD_MS)) {
268  ESP_LOGE(TAG, "Timed out waiting for semaphore");
269  return;
270  }
271 
272  if (spi_data->tx_in_progress) {
273  ESP_LOGE(TAG, "tx_in_progress is set");
274  this->status_set_warning();
275  return;
276  }
277 
278  spi_data->tx_in_progress = true;
279 
280  size_t buffer_size = this->get_buffer_size_();
281  size_t size = 0;
282  uint8_t *psrc = this->buf_;
283  uint8_t *pdest = this->dma_buf_ + 64;
284  // The 64 byte padding is a workaround for a SPI DMA bug where the
285  // output doesn't exactly start at the beginning of dma_buf_
286 
287  while (size < buffer_size) {
288  uint8_t b = *psrc;
289  for (int i = 0; i < 8; i++) {
290  *pdest++ = b & (1 << (7 - i)) ? this->bit1_ : this->bit0_;
291  }
292  size++;
293  psrc++;
294  }
295 
296  spi_data->first_run = false;
298 
299  this->status_clear_warning();
300 }
301 
303  int32_t r = 0, g = 0, b = 0;
304  switch (this->rgb_order_) {
305  case ORDER_RGB:
306  r = 0;
307  g = 1;
308  b = 2;
309  break;
310  case ORDER_RBG:
311  r = 0;
312  g = 2;
313  b = 1;
314  break;
315  case ORDER_GRB:
316  r = 1;
317  g = 0;
318  b = 2;
319  break;
320  case ORDER_GBR:
321  r = 2;
322  g = 0;
323  b = 1;
324  break;
325  case ORDER_BGR:
326  r = 2;
327  g = 1;
328  b = 0;
329  break;
330  case ORDER_BRG:
331  r = 1;
332  g = 2;
333  b = 0;
334  break;
335  }
336  uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
337  uint8_t white = this->is_wrgb_ ? 0 : 3;
338 
339  return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
340  this->buf_ + (index * multiplier) + g + this->is_wrgb_,
341  this->buf_ + (index * multiplier) + b + this->is_wrgb_,
342  this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
343  &this->effect_data_[index],
344  &this->correction_};
345 }
346 
348  ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:");
349  ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
350  const char *rgb_order;
351  switch (this->rgb_order_) {
352  case ORDER_RGB:
353  rgb_order = "RGB";
354  break;
355  case ORDER_RBG:
356  rgb_order = "RBG";
357  break;
358  case ORDER_GRB:
359  rgb_order = "GRB";
360  break;
361  case ORDER_GBR:
362  rgb_order = "GBR";
363  break;
364  case ORDER_BGR:
365  rgb_order = "BGR";
366  break;
367  case ORDER_BRG:
368  rgb_order = "BRG";
369  break;
370  default:
371  rgb_order = "UNKNOWN";
372  break;
373  }
374  ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
375  ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
376  ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_);
377 }
378 
380 
381 } // namespace beken_spi_led_strip
382 } // namespace esphome
383 
384 #endif // USE_BK72XX
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition: light_state.h:63
T * allocate(size_t n)
Definition: helpers.h:690
mopeka_std_values val[4]
void spi_dma_tx_finish_callback(unsigned int param)
Definition: led_strip.cpp:115
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:27
void spi_dma_tx_enable(bool enable)
Definition: led_strip.cpp:67
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition: component.cpp:18
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
An STL allocator that uses SPI or internal RAM.
Definition: helpers.h:675
void set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency)
Definition: led_strip.cpp:242
light::ESPColorView get_view_internal(int32_t index) const override
Definition: led_strip.cpp:302
void write_state(light::LightState *state) override
Definition: led_strip.cpp:248
bool state
Definition: fan.h:34