Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Обработка звука в реальном времени (или Full Duplex I2S на STM32)

Дата публикации: 20-07-2026 10:18:48

В этом тексте я написал про то как настроить I2S трансивер работать в полнодуплексном режиме (Full Duplex). Показал как настроить на микроконтроллере STM32F407VG одновременное воспроизведение и запись встречного аудио потока по интерфейсу I2S. Читать далее

Основное содержимое страницы с новостью.

f42feb07dfe2971ebdebf8e3aeb2890b.png

Многие цифровые интерфейсы поддерживают полнодуплексный режим работы. Это когда данные передаются и принимаются одновременно. Такое возможно в интерфейсах RMII, RS232, USB 3.0, PCI Express, SPI, 100BASE-TX, HDMI, 100BASE-FX, UART, SATA Express, 100BaseT1 и I2S.

У меня уже был отдельный текст про воспроизведение и отдельный текст про запись звука.

В этом же тексте я написал про то как сделать одновременно и воспроизведение и запись звука, написал как на STM32 настроить I2S-трансивер работать в полнодуплексном (Full Duplex) режиме.

Постановка задачи
Настроить на микроконтроллере STM32F407 одновременное воспроизведение и запись аудио потока на I2S2. При этом мне нужен не просто одновременный запуск I2S2_TX и I2S2_EXT_RX, а так, чтобы DMA канал приема I2S_RX был смещен по фазе относительно DMA канала передачи I2S_TX на 180 градусов (на полпериода). То есть в момент прерывания по половине отправки надо только начинать приём по DMA. Мастером в I2S интерфейсе должен быть микроконтроллер. Slave устройством должен быть аудиокодек WM8731. Использовать DMA каналы с прерываниями. Передавать следует цифровой двухканальный звук на частоте 48kHz. Размер семпла должен составлять 16 бит. Master Clock наружу микроконтроллера не выводить.

Сделать две тестировочные STM32 сборки для проверки работы режима full duplex I2S:
1) цифровое эхо (слуховой аппарат)
2) одновременное воспроизведение с SD карты и запись звука на SD карту.

Процессорное ядро должно работать на максимальных оборотах (168 MHz).

Вот, пожалуй, и всё.

В чем проблема?
Проблема в том, что в STM32 HAL API SDK отсутствует функция запуска I2S full duplex с фазовым сдвигом 180 градусов. Что это значит? Если вызывать стандартную SDK функцию HAL_I2SEx_TransmitReceive_DMA, то чтение и запись будут абсолютно синхронными. А это значит, что DMA прерывания по окончанию отправки и DMA прерывания по окончанию приема будут вступать в гонки. Возникнет race conrition. Аналогичная проблема для DMA прерываний по половине отправки. Очевидно, что для преодоления этой неприятности надо разнести по фазе DMA процессы чтения и записи по I2S. Вот про это и будет данный текст.

Теория

Вы можете спросить:

" Зачем на практике надо делать смещение приема относительно передачи? "

Ответ в том, что благодаря разнесению чтения и записи по фазе на 180 градусов Вы можете реализовать потоковую запись и потоковое чтение в реальном масштабе времени. То есть делать воспроизведение и запись звука одновременно. Это как раз то, что необходимо для работы начиная от радаров с сонарами, и заканчивая УЗИ аппаратами и дефектоскопами. Механизм работы показан на временной диаграмме.

a11c45ae4c10a96409e9bdea040ae686.png

Еще смещение фаз RX и TX нужно для потоковой обработки звука в реальном масштабе времени по схеме Rx-Proc-Tx.

a9c9656b7a58cc0277b5937871f78e20.png

Это используется в музыкальных приборах для создания гитарных звуковых эффектов и в приборах подмены голоса в реальном времени.

Аппаратная часть

Эксперименты я провел на учебно-тренировочной электронной плате DevEBox STM32F4XX_M_V3. Аудиокодек - это всё тот же ASIC WM8731 и PCB модуль на его основе. Надо собрать прототип вот по такой схеме. Следует установить всего 10 перемычек. Вот так, просто и незатейливо:

9dacf2ecc76ee5931c3b0d9056aaf4ac.png

Микрофон я выбрал aceline AMIC-7. Надо собрать такой прототип.

fb696025fc32bd4ed1111b6728d7c521.png

Так выглядит прототип в натуре.

прототип проигрывателя в натуре.

прототип проигрывателя в натуре.

Реализация

Настройка GPIO

Я выбрал I2S2 трансивер. Вот его GPIO пины.

GPIO

PinFunc

Dir

PinMux

Pull

PB10

I2S2_CLK

out

AF5

Up

PB12

I2S2_WS

out

AF5

Up

PC2

I2S2_RX

in

AF6

Up

PC3

I2S2_TX

out

AF5

Up

Вот так это выглядит в диагностике GPIO.

-->gl
+------+--------+-------+------+------+-------+-------------+
| pad  |  mode  | level | dir  | pull |PinMux |   name      |
+------+--------+-------+------+------+-------+-------------+
|  PC2 |   ALT1 |   H   |   in | Down |   6   |  I2S2_SDEXT |
| PB13 |   ALT1 |   L   |  out | Down |   5   |     I2S2_CK |
| PB12 |   ALT1 |   L   |  out | Down |   5   |     I2S2_WS |
|  PC3 |   ALT1 |   L   |  out | Down |   5   |     I2S2_SD |
| PB10 |   ALT1 |   H   |  out |   Up |   4   |    I2C2_SCL |
| PB11 |   ALT1 |   H   |  io  |   Up |   4   |    I2C2_SDA |
|  PA2 |   ALT1 |   L   |  out |  Air |   7   |   USART2_TX |
|  PA3 |   ALT1 |   H   | in   |   Up |   7   |   USART2_RX |
| PC12 |   ALT1 |   H   |  Out |  Air |  12   |      SD_CLK |
|  PD2 |   ALT1 |   H   |  Out |   Up |  12   |      SD_CMD |
|  PC8 |   ALT1 |   H   |   io |   Up |  12   |       SD_D0 |
|  PC9 |   ALT1 |   H   |   io |   Up |  12   |       SD_D1 |
| PC10 |   ALT1 |   H   |   io |   Up |  12   |       SD_D2 |
| PC11 |   ALT1 |   H   |   io |   Up |  12   |       SD_D3 |
|  PA1 |    Out |   L   |  Out |  Air |   0   |    LedGreem |
|  PA8 |    Out |   L   |  Out |  Air |   0   | I2S2_TX_DMA |
|  PA4 |    Out |   L   |  Out |  Air |   0   | I2S2_RX_DMA |
|  PA0 |     In |   L   |   In | Down |   0   |        WKUP |
+------+--------+-------+------+------+-------+-------------+

Настройка I2S

I2S - это высокоскоростной синхронный полнодуплексный цифровой физический master-slave интерфейс передачи данных. Заточен для двухканальных звуковых семплов (Stereo PCM).

4e0ad7a17864e05d090564ed823567e3.png

В STM32F4x микроконтроллерах только два полноценных I2S трансивера: I2S2 и I2S3. Каждый из двух I2S трансиверов может работать в режиме полного дуплекса.

Трансивер

Base Address

Тандемный трансивер

I2S1

--

--

I2S2

0x4000 3800

I2S2ext

I2S3

0x4000 3C00

I2S3ext

I2S2ext

0x4000 3400

I2S2

I2S3ext

0x4000 4000

I2S3

Однако для активации режима полного дуплекса надо заставить два отдельных I2S трансивера работать синхронно. В тандеме. Надо в регистрах I2S сконфигурировать вот такую сцепку из двух I2S трансиверов.

f6a710e5a20bf849815fbd7a6199c3a1.png

После инициализации регистры I2S трансиверов должны принять вот такие значения

d95d8bd5c5217ef443dbe1b914247cf9.png

Чтобы начать трансляцию семплов надо первым делом запускать именно I2S write и уже внутри прерывания по половине отправки I2xTxHalfCallbackCustom запускать I2S read.


bool i2s_dma_write(uint8_t num, 
                   const uint16_t* const array, 
                   const  uint16_t words) {
    bool res = false;
    LOG_DEBUG(I2S, "I2S_%u,WriteDma,Words:%u", num, words);
    I2sHandle_t *Node = I2sGetNode(num);
    if(Node) {
        HAL_StatusTypeDef ret ;
        Node->pHandle->hdmatx->State = HAL_DMA_STATE_BUSY;
        ret = HAL_DMA_Abort_IT(Node->pHandle->hdmatx);
        Node->pHandle->State = HAL_I2S_STATE_READY;
        Node->pHandle->hdmatx->State = HAL_DMA_STATE_READY;
        ret = HAL_I2S_Transmit_DMA(Node->pHandle, (uint16_t*) array, words);
        if(HAL_OK == ret) {
            res = true;
        } else {
            LOG_DEBUG(I2S, "WrErr:%u %s", ret, HalStatusToStr(ret));
        }
    } else {
        LOG_DEBUG(I2S, "%u NodeErr", num);
    }
    return res;
}

Не наоборот. Если в режиме полного дуплекса запустить I2S read, то API отработает, но DMA прерывания не станут срабатывать.

#include "loopback_audio_isr.h"

#include "wav_player_mcal.h"
#include "i2s_full_duplex.h"
#include "loopback_audio_mcal.h"
#include "dma_channel_const.h"
#include "sound_recorder_mcal.h"

bool i2s_rx_dma_restart(uint8_t dma_node_num,
                        uint8_t i2s_num,
                        uint32_t DstAddress,
                        uint32_t DataLength ) {
    bool res = false;
    DmaChannelConfig_t *DmaChannel = DmaChannelGetConfig(dma_node_num);
    if(DmaChannel) {
        I2S_HandleTypeDef *pI2sHandle = i2s_num_to_handle(i2s_num);
        if(pI2sHandle) {
            HAL_StatusTypeDef ret;
            (void) ret;
            if(pI2sHandle->hdmarx) {
                pI2sHandle->hdmarx->State = HAL_DMA_STATE_BUSY;
                ret = HAL_DMA_Abort_IT(pI2sHandle->hdmarx);
                __HAL_UNLOCK(pI2sHandle->hdmarx);
                pI2sHandle->hdmarx->State = HAL_DMA_STATE_READY;
                pI2sHandle->hdmarx->Init.Direction = DMA_PERIPH_TO_MEMORY;

                /* Set the I2S Rx DMA Half transfer complete callback */
                pI2sHandle->hdmarx->XferHalfCpltCallback = I2S_DMARxHalfCplt;

                /* Set the I2S Rx DMA transfer complete callback */
                pI2sHandle->hdmarx->XferCpltCallback = I2S_DMARxCplt;

                /* Set the DMA error callback */
                pI2sHandle->hdmarx->XferErrorCallback = I2S_DMAError;

                ret = HAL_DMA_Start_IT(pI2sHandle->hdmarx,
                                       (uint32_t) DmaChannel->base_addr_source,
                                       DstAddress, DataLength);
                res = dma_channel_half_move_it_ctrl(DmaChannel->DmaChPad, true);
            }
        }
        res = true;
    }
    return res;
}

bool I2xTxHalfCallbackCustom(I2sHandle_t* const I2s) {
    bool res = false;
    LoopbackAudioHandle_t *Node = LoopbackAudioGetNode(1);
    if (Node) {
        if (false == Node->tx_rx_180) {
            if (Node->rec_need) {
                res = i2s_rx_dma_restart(DMA_CHANNEL_NUM_I2S2_RX, 
                                         I2s->num, 
                                         (uint32_t) Node->RxSampleArray,
                                         Node->sample_array_size);
                if (res) {
                    res = sound_recorder_start_indication(Node->rec_num);
                }
            }
            Node->tx_rx_180 = true;
        }
    }
    return res;
}

bool I2xTxDoneCallbackCustom(I2sHandle_t* const I2s) {
    return true;
}

Настройка DMA

Самым правильным режимом работы I2S трансивера является трансляция и запись звука через прямой доступ к памяти. Это режим direct memory access (DMA). DMA-это такой сопроцессор, который умеет только перемещать данные внутри физической памяти микропроцессора. По сути DMA - это аппаратная реализация функции memcpy() + способность работать с аппаратными подсистемами трансиверов. DMA хорош тем, что при окончании отправки он генерирует прерывание DONE.

5b06174c734c366a48fedd4de149a92b.png

Также DMA умеет генерировать прерывание об успешном перемещении половины от запланированного объёма данных (DmaMoveHalf).

Flow

DMA

Steam

Direction

Channel

Mode

Align

FIFO Mode

I2S2_TX

DMA1

Steam_4

MEM->PER

Channel_0

Circ

16bit

off

I2S2_RX

DMA1

Steam_3

PER->MEM

Channel_3

Circ

16bit

off

I2S2_TX поток выбран из таблицы 42. Трансиверу I2S2_TX соответствует DMA1 Stream_4 Channel_0.

cb1a72e40430530cec45f2923ca47f9d.png

Трансиверу I2S2_RX соответствует DMA1 Stream_3 Channel_3.

41ea2986cc5c3e96ff5e04d61fbc446a.jpg

Пример цифрового эха

C эхо примером все просто. Достаточно запустить i2s_write и начнет работать I2S эхо. Вот на данном наборе регистров работает демо-пример цифрового эха.

Настройки I2S регистров для цифрового эха
1:25-->i2s_diag_ll 2;i2s_diag_ll 4

87.024,+28365,291,I,[I2S],I2S2,Base:0x40003800
87.024,+0,292,W,[I2S],CR2:0x00000003=0000_0000|0000_0000|0000_0000|0000_0011
87.025,+1,293,I,[I2S],RXDMAEN:1
87.025,+0,294,I,[I2S],TXDMAEN:1
87.026,+1,295,I,[I2S],ERRIE:0
87.026,+0,296,I,[I2S],RXNEIE:0
87.027,+1,297,I,[I2S],TXEIE:0
87.027,+0,298,W,[I2S],SPI_SR:0x00000080=0000_0000|0000_0000|0000_0000|1000_0000
87.028,+1,299,I,[I2S],RXNE:0
87.028,+0,300,I,[I2S],TXE:0
87.029,+1,301,I,[I2S],CHSIDE:0
87.029,+0,302,I,[I2S],UDR:0
87.029,+0,303,I,[I2S],OVR:0
87.030,+1,304,I,[I2S],BSY:1
87.031,+1,305,I,[I2S],FRE:0
87.031,+0,306,W,[I2S],SPI_DR:0x0000F9D4=0000_0000|0000_0000|1111_1001|1101_0100
87.032,+1,307,I,[I2S],DR:63956=0xf9d4
87.033,+1,308,W,[I2S],SPI_DR:0x00000E00=0000_0000|0000_0000|0000_1110|0000_0000
87.034,+1,309,I,[I2S],CHLEN:0
87.036,+2,310,I,[I2S],DATLEN:0=16 Bit
87.037,+1,311,I,[I2S],CKPOL:0=ClkPolLow
87.037,+0,312,I,[I2S],I2SSTD:0=I2S
87.039,+2,313,I,[I2S],PCMSYNC:0=Short
87.040,+1,314,I,[I2S],I2SCFG:2=MASTER_TX
87.042,+2,315,I,[I2S],I2SE:1=enabled
87.043,+1,316,I,[I2S],I2SMOD:1=I2S
87.043,+0,317,W,[I2S],I2SPR:0x00000110=0000_0000|0000_0000|0000_0001|0001_0000
87.046,+3,318,I,[I2S],I2SDIV:16
87.046,+0,319,I,[I2S],ODD:1
87.049,+3,320,I,[I2S],MCKOE:0
1:27-->
87.068,+19,321,I,[I2S],I2S4,Base:0x40003400
87.068,+0,322,W,[I2S],CR2:0x00000023=0000_0000|0000_0000|0000_0000|0010_0011
87.069,+1,323,I,[I2S],RXDMAEN:1
87.070,+1,324,I,[I2S],TXDMAEN:1
87.070,+0,325,I,[I2S],ERRIE:1
87.070,+0,326,I,[I2S],RXNEIE:0
87.071,+1,327,I,[I2S],TXEIE:0
87.072,+1,328,W,[I2S],SPI_SR:0x00000086=0000_0000|0000_0000|0000_0000|1000_0110
87.072,+0,329,I,[I2S],RXNE:0
87.073,+1,330,I,[I2S],TXE:1
87.073,+0,331,I,[I2S],CHSIDE:1
87.073,+0,332,I,[I2S],UDR:0
87.074,+1,333,I,[I2S],OVR:0
87.074,+0,334,I,[I2S],BSY:1
87.075,+1,335,I,[I2S],FRE:0
87.075,+0,336,W,[I2S],SPI_DR:0x0000FA2C=0000_0000|0000_0000|1111_1010|0010_1100
87.076,+1,337,I,[I2S],DR:64044=0xfa2c
87.077,+1,338,W,[I2S],SPI_DR:0x00000D00=0000_0000|0000_0000|0000_1101|0000_0000
87.078,+1,339,I,[I2S],CHLEN:0
87.080,+2,340,I,[I2S],DATLEN:0=16 Bit
87.081,+1,341,I,[I2S],CKPOL:0=ClkPolLow
87.081,+0,342,I,[I2S],I2SSTD:0=I2S
87.084,+3,343,I,[I2S],PCMSYNC:0=Short
87.084,+0,344,I,[I2S],I2SCFG:1=SLAVE_RX
87.087,+3,345,I,[I2S],I2SE:1=enabled
87.087,+0,346,I,[I2S],I2SMOD:1=I2S
87.087,+0,347,W,[I2S],I2SPR:0x00000002=0000_0000|0000_0000|0000_0000|0000_0010
87.090,+3,348,I,[I2S],I2SDIV:2
87.091,+1,349,I,[I2S],ODD:0
87.093,+2,350,I,[I2S],MCKOE:0

Такой режим I2S можно уже использовать буквально как цифровой слуховой аппарат.

Одновременное воспроизведение и запись звука ( LoopBack Audio )

Особый интерес представляет

одновременное

воспроизведение и запись звука. У меня уже были два подробных текста про то как записывать и воспроизводить звук на микроконтроллере.

Однако ничто не мешает делать Play+Rec одновременно, подобно тому как сантехник и электрик могут работать одновременно в одной квартире. Я собрал отдельную demo прошивку для демонстрации LoopBack Audio. Прошивка скомпонована из ранее отлаженных программных компонентов согласно схеме.

Структура прошивки LoopBack Audio

Структура прошивки LoopBack Audio

Достаточно выполнить в UART2 консоли команду loopback_audio_play_rec_test и Вы получите одновременное воспроизведение с записью звуковой дорожки. По сути произойдет запись того, что было воспроизведено + окружающие шумы.

ffa753e8b1232cabb47a4cbbf0dd4c42.png

Тот же вывод в консоль, только в тексте

Одновременное воспроизведение и запись звука
 6.119-->
  6.151-->loopback_audio_play_rec_test 1 track48kHz_5s.wav

36.957,+36279,288,I,[LoopBackAudNum,Ok
36.957,+0,289,I,[LoopBackAudio],test_loopback_audio_rec_play_one():Play:[track48kHz_5s.wav]
36.962,+5,290,I,[WAV],OpenFile:[track48kHz_5s.wav]Ok
36.964,+2,291,I,[WAV],ReadHeader,Ok
36.964,+0,292,I,[WAV],SampleCnt:240000,SampleTime:20.833u s,PlayDir:5.000000 s
36.969,+5,293,I,[WAV],OpenFile:[track48kHz_5s.wav]Ok
36.970,+1,294,I,[WAV],ReadHeader,Ok
36.971,+1,295,I,[WAV],SampleCnt:240000,SampleTime:20.833u s,PlayDir:5.000000 s
36.972,+1,296,I,[WAV],File:,SamFreq:48000 Hz,state:0,state:1,i2s_error_cnt:0,rec_cnt:0,sample_cnt:240000,ErrCnt:0,total_file_size:960044,start_rec_ms:0,stop_rec_ms:0,data_size:960000,Spin:189432,cur_save_size:0,Init:On,
36.974,+2,297,I,[WAV],Duration: 5.00 s,ChunkId:RIFF,ChunkSize:960036 Byte,Format:WAVE,Subchunk1Id:fmt ,Subchunk1Size:16,
AudioFormat:0x0001,NumChannels:2,SampleRate:48000 Hz,ByteRate:192000 Byte,BlockAlign:4 Byte,BitsPerSample:16 bit,Subchunk2Id:data,DataSize:960000 Byt
36.992,+18,298,I,[WavPlayer],ReadHeaderOk,Duration: 5.00 s,ChunkId:RIFF,ChunkSize:960036 Byte,Format:WAVE,Subchunk1Id:fmt ,Subchunk1Size:16,
AudioFormat:0x0001,NumChannels:2,SampleRate:48000 Hz,ByteRate:192000 Byte,BlockAlign:4 Byte,BitsPerSample:16 bit,Subchunk2Id:data,DataSize:960000 Byt
36.993,+1,299,I,[I2S],I2S_2,Set,Operation:Tx
36.994,+1,300,I,[WavPlayer],1,SetAudioFreq:48000 Hz
36.994,+0,301,I,[I2S],I2S_2,Set,AudioFreq:48000 Hz
36.995,+1,302,I,[I2S],I2S_2 init
36.995,+0,303,W,[DmaChannel],N:2=I2S2_TX
36.996,+1,304,W,[Dma],2,DMA1,Stream_4,Init
36.997,+1,305,I,[Dma],Init Id:2 Dma:1 Stream:4 Ok!
36.997,+0,306,W,[DmaChannel],N:3=I2S2_RX
36.998,+1,307,W,[Dma],3,DMA1,Stream_3,Init
37.000,+2,308,I,[Dma],Init Id:3 Dma:1 Stream:3 Ok!
37.000,+0,309,I,[I2S],I2S2 initOk
37.001,+1,310,W,[GPIO],PB12,DrvSel:?,I2S2_WS,Dir:Out,Mode:ALT1,Mux:5,LL:H,Pull:Down,Speed:High,Pin:0,
37.003,+2,311,W,[GPIO],PB13,DrvSel:?, I2S2_CK,Dir:Out,Mode:ALT1,Mux:5,LL:H,Pull:Down,Speed:High,Pin:0,
37.006,+3,312,W,[GPIO],PC3,DrvSel:?,I2S2_SD(TX),Dir:Out,Mode:ALT1,Mux:5,LL:H,Pull:Down,Speed:High,Pin:0,
37.009,+3,313,I,[GPIO],Init,PB15,IN
42.124,+5115,314,I,[I2S],DmaStopOk!
42.124,+0,315,I,[SoundRecorder],I2sDmaStop,Ok
42.128,+4,316,I,[SoundRecorder],CloseFile:[R_4096_UT36965_5000Ms.wav]Ok
42.129,+1,317,I,[SoundRecorder],RecDuration:5086 ms
42.129,+0,318,I,[SoundRecorder],WrOkCnt:235
42.130,+1,319,E,[SoundRecorder],LongSaveErrorCnt:1
42.131,+1,320,I,[SoundRecorder],total_file_size:960044
42.131,+0,321,I,[SoundRecorder],Rec,SN:1
42.132,+1,322,I,[SoundRecorder],FileNAme:R_4096_UT36965_5000Ms.wav
42.132,+0,323,I,[I2S],I2S_2,Init
42.133,+1,324,I,[I2S],I2S_2 init
42.133,+0,325,W,[DmaChannel],N:2=I2S2_TX
42.134,+1,326,W,[Dma],2,DMA1,Stream_4,Init
42.134,+0,327,I,[Dma],Init Id:2 Dma:1 Stream:4 Ok!
42.135,+1,328,W,[DmaChannel],N:3=I2S2_RX
42.135,+0,329,W,[Dma],3,DMA1,Stream_3,Init
42.136,+1,330,I,[Dma],Init Id:3 Dma:1 Stream:3 Ok!
42.137,+1,331,I,[I2S],I2S2 initOk
44.519,+2382,332,I,[WavPlayer],ReadHeaderOk,Duration: 5.00 s,ChunkId:RIFF,ChunkSize:960036 Byte,Format:WAVE,Subchunk1Id:fmt ,Subchunk1Size:16,
AudioFormat:0x0001,NumChannels:2,SampleRate:48000 Hz,ByteRate:192000 Byte,BlockAlign:4 Byte,BitsPerSample:16 bit,Subchunk2Id:data,DataSize:960000 Byt
44.521,+2,333,I,[I2S],I2S_2,Set,Operation:Tx
44.522,+1,334,I,[WavPlayer],1,SetAudioFreq:48000 Hz
44.522,+0,335,I,[I2S],I2S_2,Set,AudioFreq:48000 Hz
44.523,+1,336,I,[I2S],I2S_2 init
44.523,+0,337,W,[DmaChannel],N:2=I2S2_TX
44.524,+1,338,W,[Dma],2,DMA1,Stream_4,Init
44.525,+1,339,I,[Dma],Init Id:2 Dma:1 Stream:4 Ok!
44.525,+0,340,W,[DmaChannel],N:3=I2S2_RX
44.526,+1,341,W,[Dma],3,DMA1,Stream_3,Init
44.528,+2,342,I,[Dma],Init Id:3 Dma:1 Stream:3 Ok!
44.528,+0,343,I,[I2S],I2S2 initOk
44.529,+1,344,W,[GPIO],PB12,DrvSel:?,I2S2_WS,Dir:Out,Mode:ALT1,Mux:5,LL:H,Pull:Down,Speed:High,Pin:0,
44.531,+2,345,W,[GPIO],PB13,DrvSel:?, I2S2_CK,Dir:Out,Mode:ALT1,Mux:5,LL:H,Pull:Down,Speed:High,Pin:0,
44.534,+3,346,W,[GPIO],PC3,DrvSel:?,I2S2_SD(TX),Dir:Out,Mode:ALT1,Mux:5,LL:H,Pull:Down,Speed:High,Pin:0,
44.537,+3,347,I,[GPIO],Init,PB15,IN
49.605,+5068,348,I,[I2S],DmaStopOk!
49.606,+1,349,I,[WavPlayer],I2s,Ok
49.606,+0,350,I,[WavPlayer],CloseFile:[R_4096_UT36965_5000Ms.wav]Ok
49.607,+1,351,E,[WavPlayer],SlowReadError:2
49.607,+0,352,I,[WavPlayer],File:[R_4096_UT36965_5000Ms.wav]
 52.046-->

Вы спросите:

"Для чего нужно одновременное воспроизведение и запись звука?"

Ответ прост. Это нужно для разработки сонаров. Сонар отправляет зондирующий ЛЧМ импульс и тотчас же записывает отраженные эхо сигналы. Далее над принятым сигналом вычисляется свертка с известным зондирующим сигналом (FIR фильтрация) и определяются все расстояния до окружающих предметов вокруг с погрешностью равной длительности одного семпла.

Отладка I2S-DMA Full Duplex.

Для контроля факта смещения фазы I2S приема и передачи я добавил в обработчики I2S-DMA прерываний GPIO функции для индикации событий: пин PA4 отражает поведение I2S2_RX, пин PA8 отражает поведение I2S2_TX.

Flow

GPIO

DMA move half

DMA move complete

I2S2_TX

PA8

Set 3.3V

Set 0V

I2S2_RX

PA4

Set 3.3V

Set 0V

Как видно, на осциллографе в самом деле прием и передача смещены во времени друг относительно друга на 180 градусов.

f9a4641d6379b00b6f61e9f63af5d5d2.png

Данный режим I2S2-DMA корректно работал вот на таких значениях DMA регистров.

e5f4aeb5a5b1ba56773bf99a9d532abb.png

То же в тексте

Скрытый текст
 55.662-->dcrm 1 3;dcrm 1 4

81.315,+68877,235,W,[DmaChannel]DMA1_Stream3,CH_0,[(null)], Regs
81.315,+0,236,I,[DmaChannel],Base:0x40026058,Cnt:6
+-----+--------------------+-------+------+------------+------------+-------------------------------------------+
|  N  |        Name        |offset | size |    Addr    |   ValHex   |                  ValBin                   |
+-----+--------------------+-------+------+------------+------------+-------------------------------------------+
|   1 |           DMA_SxCR | 0x000 |    0 | 0x40026058 | 0x06032d1f | 0b0000_0110|0000_0011|0010_1101|0001_1111 |
|   2 |         DMA_SxNDTR | 0x004 |    4 | 0x4002605c | 0x000004fd | 0b0000_0000|0000_0000|0000_0100|1111_1101 |
|   3 |          DMA_SxPAR | 0x008 |    4 | 0x40026060 | 0x4000340c | 0b0100_0000|0000_0000|0011_0100|0000_1100 |
|   4 |         DMA_SxM0AR | 0x00c |    4 | 0x40026064 | 0x20008238 | 0b0010_0000|0000_0000|1000_0010|0011_1000 |
|   5 |         DMA_SxM1AR | 0x010 |    4 | 0x40026068 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|   6 |          DMA_SxFCR | 0x014 |    4 | 0x4002606c | 0x00000020 | 0b0000_0000|0000_0000|0000_0000|0010_0000 |
+-----+--------------------+-------+------+------------+------------+-------------------------------------------+
1:21-->
81.349,+34,237,W,[DmaChannel],DMA1_Stream4,CH_0,[(null)], Regs
81.350,+1,238,I,[DmaChannel],Base:0x40026070,Cnt:6
+-----+--------------------+-------+------+------------+------------+-------------------------------------------+
|  N  |        Name        |offset | size |    Addr    |   ValHex   |                  ValBin                   |
+-----+--------------------+-------+------+------------+------------+-------------------------------------------+
|   1 |           DMA_SxCR | 0x000 |    0 | 0x40026070 | 0x00032d5f | 0b0000_0000|0000_0011|0010_1101|0101_1111 |
|   2 |         DMA_SxNDTR | 0x004 |    4 | 0x40026074 | 0x0000002a | 0b0000_0000|0000_0000|0000_0000|0010_1010 |
|   3 |          DMA_SxPAR | 0x008 |    4 | 0x40026078 | 0x4000380c | 0b0100_0000|0000_0000|0011_1000|0000_1100 |
|   4 |         DMA_SxM0AR | 0x00c |    4 | 0x4002607c | 0x20008238 | 0b0010_0000|0000_0000|1000_0010|0011_1000 |
|   5 |         DMA_SxM1AR | 0x010 |    4 | 0x40026080 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
|   6 |          DMA_SxFCR | 0x014 |    4 | 0x40026084 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 |
+-----+--------------------+-------+------+------------+------------+-------------------------------------------+
1:21-->

Итоги

Удалось настроить I2S-трансивер на микроконтроллере STM32F407VG в режим полного дуплекса. Удалось разнести по фазе приём и передачу аудиопотока. Удалось написать прошивку одновременного воспроизведения и записи звука на SD карту.

С настроенным I2S можно делать звуковые эффекты в реальном масштабе времени, приборы для слушания пульса, создавать сонары, дефектоскопы, слуховые аппараты и прочее.

Ссылки

Вопросы
1) Как в STM32 перезарядить работающий DMA канал? То есть как заставить DMA канал работать с начала? Заново выполнить операцию перемещения данных.

2) Существуют ли микроконтроллеры, где DMA сопроцессор выдает прерывания на на 1/3; 2/3 и 3/3 перемещения массива?

3) Существуют ли в продаже готовые компактные учебные отладочные платы с ARM Cortex M4 микроконтроллером (STM32F4x), каким-нибудь простым аудиокодеком (как WM8731), audio-jack портом для наушников, микрофона (и желательно LineIn), разъемом для карты памяти microSD подключаемой по интерфейсу SDIO (или SPI), LEDом и желательно графическим монохромным дисплеем? Чтобы можно было полноценно прототипировать и отлаживать прошивки диктофонов, сонаров, направленных метрономов, будильников, звуковые эффекты для гитар, передачу и прием бинарных данных модулированным звуком, плейер и т. п..

4) Зачем в I2S трансиверах предусмотрена возможность выводить за корпус микроконтроллера пятый провод Master Clock , если и так есть отдельный провод Bit Clock?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовали 8 пользователей. Воздержавшихся нет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовали 9 пользователей. Воздержавшихся нет.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Запуск файловой системы LittleFS на SPI‑NAND Flash от GigaDevice011.7624-07-2026
2АКПП Voith не включается выше второй передачи: разбираемся в J193907.5106-08-2026
3Старый трансивер эфир не испортит. Часть 3. Умощняемся0505-07-2026
4Проектирование меню в стесненных условиях09.0726-07-2026
5Добавили потоков, стало медленнее: разбираемся с ложным разделением кеш‑линии09.2610-08-2026
6Считаем пакеты на Rust09.8704-07-2026
7Мой код терял бы 230 байт из 231. Чтобы это увидеть, пришлось патчить QEMU-110.6302-08-2026
8time.ticks_ms() в MicroPython: как не блокировать код и выполнять задачи конкурентно на ESP05.901-08-2026
9AOMedia выпустила открытый рендерер пространственного звука OAR v1017.1831-07-2026

Классификация: . Схожих патентов: 0. Схожих новостей: 9. Тональность: 0. Информативность: 9.2. Источник: habr.com.