stm32f411-i2s
All Classes Files Functions Variables Enumerations Pages
stm32-i2s.h
Go to the documentation of this file.
1 
10 #pragma once
11 
12 #include <assert.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #include "Arduino.h"
21 #include "PinConfigured.h"
22 #include "stm32-config-i2s.h"
23 #include "stm32f4xx_hal.h"
24 
25 extern uint32_t g_anOutputPinConfigured[MAX_NB_PORT];
26 
27 namespace stm32_i2s {
28 
29 using byte = uint8_t;
30 
31 extern "C" void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s);
32 extern "C" void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s);
33 extern "C" void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s);
34 extern "C" void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s);
35 extern "C" void DMA1_Stream0_IRQHandler(void);
36 extern "C" void DMA1_Stream5_IRQHandler(void);
37 extern "C" void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s);
38 extern "C" void HAL_I2S_MspDeInit(I2S_HandleTypeDef *hi2s);
39 extern "C" void Report_Error();
40 extern "C" void STM32_LOG(const char *fmt, ...);
41 extern bool stm32_i2s_is_error;
42 
44 enum I2SPinFunction { mclk, bck, ws, data_out, data_in };
45 
53 struct I2SPin {
54  I2SPin(I2SPinFunction f, PinName n, int alt) {
55  function = f;
56  pin = n;
57  altFunction = alt;
58  }
59  I2SPinFunction function;
60  PinName pin;
61  int altFunction;
62 
63  void begin() {
64  end();
65  // define the pin function
66  pin_function(pin, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, altFunction));
67  }
68 
70  void end() {
71  PinName p = pin;
72  if (p != NC) {
73  // If the pin that support PWM or DAC output, we need to turn it off
74 #if (defined(HAL_DAC_MODULE_ENABLED) && !defined(HAL_DAC_MODULE_ONLY)) || \
75  (defined(HAL_TIM_MODULE_ENABLED) && !defined(HAL_TIM_MODULE_ONLY))
76  if (is_pin_configured(p, g_anOutputPinConfigured)) {
77 #if defined(HAL_DAC_MODULE_ENABLED) && !defined(HAL_DAC_MODULE_ONLY)
78  if (pin_in_pinmap(p, PinMap_DAC)) {
79  dac_stop(p);
80  } else
81 #endif // HAL_DAC_MODULE_ENABLED && !HAL_DAC_MODULE_ONLY
82 #if defined(HAL_TIM_MODULE_ENABLED) && !defined(HAL_TIM_MODULE_ONLY)
83  if (pin_in_pinmap(p, PinMap_TIM)) {
84  pwm_stop(p);
85  }
86 #endif // HAL_TIM_MODULE_ENABLED && !HAL_TIM_MODULE_ONLY
87  { reset_pin_configured(p, g_anOutputPinConfigured); }
88  }
89 #endif
90  }
91  }
92 };
93 
100  IRQn_Type irq1 = DMA1_Stream0_IRQn;
101  IRQn_Type irq2 = DMA1_Stream5_IRQn;
102 
103 #ifdef PLLM
104  uint32_t pllm = PLLM;
105 #endif
106  uint32_t plln = PLLN;
107  uint32_t pllr = PLLR;
108 
109  DMA_Stream_TypeDef *rx_instance = DMA1_Stream0;
110  uint32_t rx_channel = DMA_CHANNEL_3;
111  uint32_t rx_direction = DMA_PERIPH_TO_MEMORY;
112 
113  DMA_Stream_TypeDef *tx_instance = DMA1_Stream5;
114  uint32_t tx_channel = DMA_CHANNEL_0;
115  uint32_t tx_direction = DMA_MEMORY_TO_PERIPH;
116 
117  I2SPin pins[5] = STM_I2S_PINS;
118 
119  int buffer_size = 512;
120 
121  HardwareConfig() {
122  // overwrite processor specific default settings if necessary
123  }
124 };
125 
132  uint32_t mode = I2S_MODE_MASTER_TX;
133  uint32_t standard = I2S_STANDARD_PHILIPS;
134  uint32_t fullduplexmode = I2S_FULLDUPLEXMODE_ENABLE;
135  uint32_t sample_rate = I2S_AUDIOFREQ_44K;
136  uint32_t data_format = I2S_DATAFORMAT_16B;
137  HardwareConfig hardware_config;
138  // optioinal reference that will be provided by the callbacks
139  void *ref = nullptr;
140 };
141 
148  friend void DMA1_Stream0_IRQHandler(void);
149  friend void DMA1_Stream5_IRQHandler(void);
150  friend void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s);
151  friend void HAL_I2S_MspDeInit(I2S_HandleTypeDef *hi2s);
152  friend void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s);
153  friend void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s);
154  friend void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s);
155  friend void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s);
156 
157  public:
159  bool begin(I2SSettingsSTM32 settings, bool transmit, bool receive) {
160  this->use_dma = false;
161  this->settings = settings;
162  this->hw = settings.hardware_config;
163  int buffer_size = hw.buffer_size;
164  bool result = true;
165 
166  if (!i2s_begin()) {
167  return false;
168  }
169 
170  if (!transmit && !receive) {
171  return false;
172  }
173 
174  if (use_dma) {
175  if (transmit && !receive) {
176  if (HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t *)dma_buffer_tx,
177  buffer_size) != HAL_OK) {
178  STM32_LOG("error HAL_I2S_Transmit_DMA");
179  Report_Error();
180  result = false;
181  }
182  }
183 
184  if (receive && !transmit) {
185  if (HAL_I2S_Receive_DMA(&hi2s3, (uint16_t *)dma_buffer_rx,
186  buffer_size) != HAL_OK) {
187  STM32_LOG("error: HAL_I2S_Receive_DMA");
188  Report_Error();
189  result = false;
190  }
191  }
192 
193  if (receive && transmit) {
194  if (HAL_I2SEx_TransmitReceive_DMA(&hi2s3, (uint16_t *)dma_buffer_tx,
195  (uint16_t *)dma_buffer_rx,
196  buffer_size) != HAL_OK) {
197  STM32_LOG("error HAL_I2SEx_TransmitReceive_DMA");
198  Report_Error();
199  result = false;
200  }
201  }
202  }
203 
204  return result;
205  }
206 
209  void (*readToTransmit)(uint8_t *buffer, uint16_t byteCount,
210  void *ref) = nullptr) {
211  this->use_dma = true;
212  this->settings = settings;
213  this->hw = settings.hardware_config;
214  int buffer_size = hw.buffer_size;
215  readToTransmitCB = readToTransmit;
216  if (dma_buffer_tx == nullptr) dma_buffer_tx = new byte[buffer_size];
217  bool result = true;
218  if (!i2s_begin()) {
219  return false;
220  }
221  // start circular dma
222  if (HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t *)dma_buffer_tx, buffer_size) !=
223  HAL_OK) {
224  STM32_LOG("error HAL_I2S_Transmit_DMA");
225  Report_Error();
226  result = false;
227  }
228  return result;
229  }
230 
233  void (*writeFromReceive)(uint8_t *buffer,
234  uint16_t byteCount,
235  void *ref) = nullptr) {
236  this->use_dma = true;
237  this->settings = settings;
238  this->hw = settings.hardware_config;
239  int buffer_size = hw.buffer_size;
240  bool result = true;
241  writeFromReceiveCB = writeFromReceive;
242  if (dma_buffer_rx == nullptr) dma_buffer_rx = new byte[buffer_size];
243  if (!i2s_begin()) {
244  return false;
245  }
246  // start circular dma
247  if (HAL_I2S_Receive_DMA(&hi2s3, (uint16_t *)dma_buffer_rx, buffer_size) !=
248  HAL_OK) {
249  STM32_LOG("error: HAL_I2S_Transmit_DMA");
250  Report_Error();
251  result = false;
252  }
253  return result;
254  }
255 
258  void (*readToTransmit)(uint8_t *buffer,
259  uint16_t byteCount,
260  void *) = nullptr,
261  void (*writeFromReceive)(uint8_t *buffer,
262  uint16_t byteCount,
263  void *) = nullptr) {
264  this->use_dma = true;
265  this->settings = settings;
266  this->hw = settings.hardware_config;
267  int buffer_size = hw.buffer_size;
268  bool result = true;
269  readToTransmitCB = readToTransmit;
270  writeFromReceiveCB = writeFromReceive;
271 
272  if (dma_buffer_tx == nullptr) dma_buffer_tx = new byte[buffer_size];
273 
274  if (dma_buffer_rx == nullptr) dma_buffer_rx = new byte[buffer_size];
275 
276  if (!i2s_begin()) {
277  return false;
278  }
279  if (HAL_I2SEx_TransmitReceive_DMA(&hi2s3, (uint16_t *)dma_buffer_tx,
280  (uint16_t *)dma_buffer_rx,
281  buffer_size) != HAL_OK) {
282  STM32_LOG("error HAL_I2SEx_TransmitReceive_DMA");
283  Report_Error();
284  result = false;
285  }
286  return result;
287  }
288 
289  void end() {
290  if (use_dma) HAL_I2S_DMAStop(&hi2s3);
291  HAL_I2S_DeInit(&hi2s3);
292  HAL_I2S_MspDeInit(&hi2s3);
293  if (dma_buffer_tx != NULL) {
294  delete[] (dma_buffer_tx);
295  }
296  if (dma_buffer_rx != NULL) {
297  delete[] (dma_buffer_rx);
298  }
299  }
300 
305  size_t write(const uint8_t *data, size_t bytes) {
306  HAL_StatusTypeDef rc = HAL_OK;
307  if (!this->use_dma) {
308  int samples = bytes / getBytes();
309  HAL_StatusTypeDef rc =
310  HAL_I2S_Transmit(&hi2s3, (uint16_t *)data, samples, HAL_MAX_DELAY);
311  }
312  return rc == HAL_OK ? bytes : 0;
313  }
314 
319  size_t readBytes(uint8_t *data, size_t bytes) {
320  HAL_StatusTypeDef rc = HAL_OK;
321  if (!this->use_dma) {
322  int samples = bytes / getBytes();
323  HAL_StatusTypeDef rc =
324  HAL_I2S_Receive(&hi2s3, (uint16_t *)data, samples, HAL_MAX_DELAY);
325  }
326  return rc == HAL_OK ? bytes : 0;
327  }
328 
329  protected:
330  I2SSettingsSTM32 settings;
331  I2S_HandleTypeDef hi2s3;
332  byte *dma_buffer_tx = nullptr;
333  byte *dma_buffer_rx = nullptr;
334  void (*readToTransmitCB)(uint8_t *buffer, uint16_t byteCount, void *ref);
335  void (*writeFromReceiveCB)(uint8_t *buffer, uint16_t byteCount, void *ref);
336  DMA_HandleTypeDef hdma_i2s3_ext_rx;
337  DMA_HandleTypeDef hdma_i2s3_ext_tx;
338  HardwareConfig hw;
339  bool use_dma = false;
340 
341  int getBytes() {
342  if (settings.data_format == I2S_DATAFORMAT_16B) return 2;
343  if (settings.data_format == I2S_DATAFORMAT_24B) return 4;
344  if (settings.data_format == I2S_DATAFORMAT_32B) return 4;
345  STM32_LOG("unsuppoted data_format");
346  return 2;
347  }
348 
351  void cb_TxRxComplete(I2S_HandleTypeDef *hi2s) {
352  // second half finished, filling it up again while first half is playing
353  uint8_t *dma_buffer_tx = (uint8_t *)hi2s->pTxBuffPtr;
354  uint8_t *dma_buffer_rx = (uint8_t *)hi2s->pRxBuffPtr;
355  uint16_t buffer_size_tx = hi2s->TxXferSize * 2; // XferSize is in words
356  uint16_t buffer_size_rx = hi2s->RxXferSize * 2;
357  if (readToTransmitCB != NULL)
358  readToTransmitCB(&(dma_buffer_tx[buffer_size_tx / 2]), buffer_size_tx / 2,
359  settings.ref);
360  if (writeFromReceiveCB != NULL)
361  writeFromReceiveCB(&(dma_buffer_rx[buffer_size_rx / 2]),
362  buffer_size_rx / 2, settings.ref);
363  }
364 
367  void cb_TxRxHalfComplete(I2S_HandleTypeDef *hi2s) {
368  // second half finished, filling it up again while first half is playing
369  uint8_t *dma_buffer_tx = (uint8_t *)hi2s->pTxBuffPtr;
370  uint8_t *dma_buffer_rx = (uint8_t *)hi2s->pRxBuffPtr;
371  uint16_t buffer_size_tx = hi2s->TxXferSize * 2; // XferSize is in words
372  uint16_t buffer_size_rx = hi2s->RxXferSize * 2;
373  if (readToTransmitCB != NULL)
374  readToTransmitCB(&(dma_buffer_tx[0]), buffer_size_tx / 2, settings.ref);
375  if (writeFromReceiveCB != NULL)
376  writeFromReceiveCB(&(dma_buffer_rx[0]), buffer_size_rx / 2, settings.ref);
377  }
378 
380  inline void cb_dmaIrqRx() { HAL_DMA_IRQHandler(&hdma_i2s3_ext_rx); }
381 
383  inline void cb_dmaIrqTx() { HAL_DMA_IRQHandler(&hdma_i2s3_ext_tx); }
384 
386  inline void cb_HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s) {
387  cb_i2s_MspInit(hi2s);
388  }
389 
391  inline void cb_HAL_I2S_MspDeInit(I2S_HandleTypeDef *hi2s) {
392  cb_i2s_MspDeInit(hi2s);
393  }
394 
396  bool i2s_begin() {
397  stm32_i2s_is_error = false;
398  if (settings.sample_rate == 0) {
399  STM32_LOG("sample_rate must not be 0");
400  return false;
401  }
402  STM32_LOG("use_dma: %s", use_dma ? "true" : "false");
403  stm32_i2s_is_error = false;
404  /* Reset of all peripherals, Initializes the Flash interface and the
405  * Systick.
406  */
407  // HAL_Init();// Not needed -> called by Arduino
408  /* Configure the system clock */
409  // SystemClock_Config(); // Not needed -> called by Arduino
410  /* Initialize all configured peripherals */
411  MX_GPIO_Init();
412  if (use_dma) MX_DMA_Init();
413  MX_I2S3_Init();
414  return !stm32_i2s_is_error;
415  }
416 
423  virtual void MX_GPIO_Init(void) {
424  // GPIO Ports Clock Enable
425  __HAL_RCC_GPIOH_CLK_ENABLE();
426 
427  // Define pins
428  for (I2SPin &pin : hw.pins) {
429  pin.begin();
430  }
431  }
432 
436  virtual void MX_DMA_Init(void) {
437  /* DMA controller clock enable */
438  __HAL_RCC_DMA1_CLK_ENABLE();
439 
440  /* DMA interrupt init */
441  /* DMA1_Stream0_IRQn interrupt configuration */
442  HAL_NVIC_SetPriority(hw.irq1, 0, 0);
443  HAL_NVIC_EnableIRQ(hw.irq1);
444  /* DMA1_Stream5_IRQn interrupt configuration */
445  HAL_NVIC_SetPriority(hw.irq2, 0, 0);
446  HAL_NVIC_EnableIRQ(hw.irq2);
447  }
448 
454  virtual void MX_I2S3_Init(void) {
455  hi2s3.Instance = SPI_INSTANCE_FOR_I2S;
456  hi2s3.Init.Mode = settings.mode;
457  hi2s3.Init.Standard = settings.standard;
458  hi2s3.Init.FullDuplexMode = settings.fullduplexmode;
459  hi2s3.Init.AudioFreq = settings.sample_rate;
460  hi2s3.Init.DataFormat = settings.data_format;
461  hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
462  hi2s3.Init.CPOL = I2S_CPOL_LOW;
463  hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
464  if (HAL_I2S_Init(&hi2s3) != HAL_OK) {
465  Report_Error();
466  }
467  }
468 
475  virtual void cb_i2s_MspInit(I2S_HandleTypeDef *hi2s) {
476  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
480  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
481 #ifdef PLLM
482  PeriphClkInitStruct.PLLI2S.PLLI2SM = hw.pllm; // 16;
483 #endif
484 #ifdef PLLN
485  PeriphClkInitStruct.PLLI2S.PLLI2SN = hw.plln; // 192;
486 #endif
487 #ifdef PLLR
488  PeriphClkInitStruct.PLLI2S.PLLI2SR = hw.pllr; // 2;
489 #endif
490  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
491  Report_Error();
492  }
493 
494  /* Peripheral clock enable */
495  __HAL_RCC_SPI3_CLK_ENABLE();
496 
497  if (use_dma) {
498 
499  /* I2S3 DMA Init */
500  if (dma_buffer_rx != nullptr) {
501  setupDMA(hdma_i2s3_ext_rx, hw.rx_instance, hw.rx_channel,
502  hw.rx_direction);
503  __HAL_LINKDMA(hi2s, hdmarx, hdma_i2s3_ext_rx);
504  }
505 
506  if (dma_buffer_tx != nullptr) {
507  setupDMA(hdma_i2s3_ext_tx, hw.tx_instance, hw.tx_channel,
508  hw.tx_direction);
509  __HAL_LINKDMA(hi2s, hdmatx, hdma_i2s3_ext_tx);
510  }
511  }
512  }
513 
518  void setupDMA(DMA_HandleTypeDef &dma, DMA_Stream_TypeDef *instance,
519  uint32_t channel, uint32_t direction) {
520  dma.Instance = instance;
521  dma.Init.Channel = channel;
522  dma.Init.Direction = direction;
523  dma.Init.PeriphInc = DMA_PINC_DISABLE;
524  dma.Init.MemInc = DMA_MINC_ENABLE;
525  dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
526  dma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
527  dma.Init.Mode = DMA_CIRCULAR;
528  dma.Init.Priority = DMA_PRIORITY_HIGH;
529  dma.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
530  dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
531  dma.Init.MemBurst = DMA_PBURST_INC4;
532  dma.Init.PeriphBurst = DMA_PBURST_INC4;
533  if (HAL_DMA_Init(&dma) != HAL_OK) {
534  Report_Error();
535  }
536  }
537 
544  virtual void cb_i2s_MspDeInit(I2S_HandleTypeDef *hi2s) {
545  if (hi2s->Instance == SPI3) {
546  /* Peripheral clock disable */
547  __HAL_RCC_SPI3_CLK_DISABLE();
548 
549  /* I2S3 DMA DeInit */
550  HAL_DMA_DeInit(hi2s->hdmarx);
551  HAL_DMA_DeInit(hi2s->hdmatx);
552  }
553  }
554 };
555 
557 extern Stm32I2sClass I2S;
558 
559 } // namespace stm32_i2s
I2S API for STM32.
Definition: stm32-i2s.h:147
virtual void MX_I2S3_Init(void)
I2S3 Initialization Function.
Definition: stm32-i2s.h:454
bool beginReadDMA(I2SSettingsSTM32 settings, void(*writeFromReceive)(uint8_t *buffer, uint16_t byteCount, void *ref)=nullptr)
Start to receive I2S data.
Definition: stm32-i2s.h:232
bool beginWriteDMA(I2SSettingsSTM32 settings, void(*readToTransmit)(uint8_t *buffer, uint16_t byteCount, void *ref)=nullptr)
Start to transmit I2S data.
Definition: stm32-i2s.h:208
size_t write(const uint8_t *data, size_t bytes)
Write method which needs to be called when ansync mode is disabled.
Definition: stm32-i2s.h:305
void cb_HAL_I2S_MspDeInit(I2S_HandleTypeDef *hi2s)
Callback I2S de-intitialization.
Definition: stm32-i2s.h:391
virtual void cb_i2s_MspInit(I2S_HandleTypeDef *hi2s)
I2S MSP Initialization This function configures the hardware resources used in this example.
Definition: stm32-i2s.h:475
virtual void MX_GPIO_Init(void)
GPIO Initialization Function for I2S pins.
Definition: stm32-i2s.h:423
void cb_TxRxComplete(I2S_HandleTypeDef *hi2s)
Callback for double buffer.
Definition: stm32-i2s.h:351
void cb_dmaIrqTx()
Callback for DMA interrupt request.
Definition: stm32-i2s.h:383
virtual void MX_DMA_Init(void)
Definition: stm32-i2s.h:436
size_t readBytes(uint8_t *data, size_t bytes)
Read method which needs to be called when ansync mode is disabled.
Definition: stm32-i2s.h:319
virtual void cb_i2s_MspDeInit(I2S_HandleTypeDef *hi2s)
I2S MSP De-Initialization This function freeze the hardware resources used in this example.
Definition: stm32-i2s.h:544
void cb_TxRxHalfComplete(I2S_HandleTypeDef *hi2s)
Callback for double buffer.
Definition: stm32-i2s.h:367
void cb_HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s)
Callback I2S intitialization.
Definition: stm32-i2s.h:386
bool begin(I2SSettingsSTM32 settings, bool transmit, bool receive)
start I2S w/o DMA: use write and readBytes
Definition: stm32-i2s.h:159
bool beginReadWriteDMA(I2SSettingsSTM32 settings, void(*readToTransmit)(uint8_t *buffer, uint16_t byteCount, void *)=nullptr, void(*writeFromReceive)(uint8_t *buffer, uint16_t byteCount, void *)=nullptr)
Start to receive and transmit I2S data.
Definition: stm32-i2s.h:257
bool i2s_begin()
Starts the i2s processing.
Definition: stm32-i2s.h:396
void setupDMA(DMA_HandleTypeDef &dma, DMA_Stream_TypeDef *instance, uint32_t channel, uint32_t direction)
DMA Initialization This function configures and initializes the DMA.
Definition: stm32-i2s.h:518
void cb_dmaIrqRx()
Callback for DMA interrupt request.
Definition: stm32-i2s.h:380
I2SPinFunction
i2s pin function used as documentation
Definition: stm32-i2s.h:44
Processor specific settings that are needed to set up I2S.
Definition: stm32-i2s.h:99
Define individual Pin. This is used to set up processor specific arrays with all I2S pins and to setu...
Definition: stm32-i2s.h:53
void end()
Undo the current pin function.
Definition: stm32-i2s.h:70
Currently supported parameters.
Definition: stm32-i2s.h:131