rate.
Прикладываю файлы драйвера: Драйвер передает данные из буфера посредством DMA в режиме двойной буферизации, непрерывное воспроизведение, один буфер воспроизводится, второй заполняется сэмплами, по окрнчании передачи буфера меняются ролями. использование в main
sai_drv.hsai_drv.h#define BUF_SIZE 4096
sai_drv_t * saip;
uint32_t buf_play[BUF_SIZE];
uint32_t buf_synt[BUF_SIZE];
void sai_cb(struct sai_drv * sai, uint32_t flags)
{
}
BaseSequentialStream * chp = NULL;
static sai_config_t sai_cfg = {
.buf_0 = buf_play,
.buf_1 = buf_synt,
.cb = sai_cb
}; . . .
saip = saiInit(&sai_cfg);
saiStart(saip);
if(saip){
LOG_INFO("Driver SAI init successfull %08X", saip);
saiStart(saip);
}
else{
LOG_ERROR("Driver SAI init fail");
}
настройка тактирования SAI производится ChibiOS в файле mcuconf.h, ее не привожу. Сейчас sample rate настроен на частоту 24 КГц. Процесс на
MCLK, BCLK, LRCLK наблюдаю осциллографом, кодек пока не подключен. При данной sample rate и размере буфера частота прерываний по окончании передачи должна быть доли герца. В обработчике прерываний пробовал закомментить сброс флагов прерывания, частота не изменилась. То есть сброса флагов не происходит(?), но я не первый раз использую API ChibiOS, проблем не случалось
в приложенных файлах проблeма с кодировкой, русские комментарии нечитабельны, выкладываю сюда.
sai_drv.h
/* * sai_drv.h * * Created on: Aug 22, 2025 * Author: rain */ #ifndef SAI_DRV_H_ #define SAI_DRV_H_ struct sai_drv; typedef void(*sai_callback_t)(struct sai_drv *, uint32_t flags); // структура драйвера SAI struct sai_drv{ const stm32_dma_stream_t *dmastp; // DmaStream SAI_TypeDef * sai; // указатель на структуру регистров SAI (для отлпдочных целей) SAI_Block_TypeDef * sai_block; // указатель на структуру регистров SAI_BLOCK (для отлпдочных целей) uint16_t * buf_play; // указатель на буфер воспроизведения uint16_t * buf_synth; // указатель на буфер синтеза sai_callback_t cb; // callback на прерывание по окончании передачи }; typedef struct sai_drv sai_drv_t; // структура конфигурации драйвера SAI struct sai_config { uint16_t * buf_0; // указатель на буфер воспроизведения uint16_t * buf_1; // указатель на буфер синтеза sai_callback_t cb; // callback на прерывание по окончании передачи }; typedef struct sai_config sai_config_t; sai_drv_t * saiInit(sai_config_t *); void saiStart(sai_drv_t *); #endif /* SAI_DRV_H_ */
sai_drv.c
/* * sai_drv.c * * Created on: Aug 22, 2025 * Author: rain */ #include "ch.h" #include "hal.h" #include "sai_drv.h" #include "stm32h743xx.h" #include "stm32_dma.h" #undef LOG_LEVEL #define LOG_LEVEL LOG_LEVEL_TRACE #include "log.h" static void sai_dma_isr(void *p, uint32_t flags) { sai_drv_t * saip = (sai_drv_t*)p; if (flags & STM32_DMA_ISR_TCIF) { // Проверяем флаг завершения передачи dmaStreamClearInterrupt(saip->dmastp); // Очищаем флаг прерывания palToggleLine(LINE_LED_RED); // инвертируем выход красного светодиода if(saip->cb) { // если callback установлен saip->cb(saip, flags); // вызовем его } } else{ palToggleLine(LINE_LED_GREEN); // инвертируем выход зеленого светодиода } } sai_drv_t *saiInit(sai_config_t *config) { sai_drv_t * saip = NULL; // локальный указатель на структуру драйвера saip = (sai_drv_t *)chHeapAlloc(0, sizeof(sai_drv_t));// выделим указателю на драйвер блок памяти if(saip){ // если успешно saip->sai = (SAI_TypeDef*)SAI1_BASE; // saip->sai_block = (SAI_Block_TypeDef*)SAI1_Block_A_BASE;// saip->buf_play = config->buf_0; // указатель на буфер для воспроизведения saip->buf_synth = config->buf_1; // указатель на буфер для синтеза saip->cb = config->cb; // callback на окончание передачи saip->dmastp = dmaStreamAllocI( // арендуем DmaStream STM32_DMA_STREAM_ID(1, 1), // DMA1, Stream1 10, // Приоритет прерывания (stm32_dmaisr_t)sai_dma_isr, // Обработчик прерывания saip); // указатель на тело драйвера if(saip->dmastp){ // если DmaStream успешно арендован RCC->APB2ENR |= RCC_APB2ENR_SAI1EN; // включим тактирование SAI1 __DSB(); // RCC->APB2RSTR |= RCC_APB2RSTR_SAI1RST; // RCC->APB2RSTR &= ~RCC_APB2RSTR_SAI1RST; // __DSB(); SAI1_Block_A->CR1 &= ~SAI_xCR1_SAIEN; while (SAI1_Block_A->CR1 & SAI_xCR1_SAIEN); // Ждем отключения //Конфигурируем блок A SAI1 в режиме Master Transmitter SAI1_Block_A->CR1 = 0; SAI1_Block_A->CR1 |= SAI_xCR1_MODE_0; // Режим: Master Transmitter SAI1_Block_A->CR1 |= (0b0100 << SAI_xCR1_DS_Pos); // 16-bit data size SAI1_Block_A->CR1 |= (8 << SAI_xCR1_MCKDIV_Pos); // MCKDIV=8 для fs=24 кГц SAI1_Block_A->CR1 &= ~(1U << 19); SAI1_Block_A->CR2 = 0; SAI1_Block_A->CR2 |= (1 << SAI_xCR2_FTH_Pos); // FIFO threshold: 1/4 SAI1_Block_A->CR2 &= ~(1U << 10); // ODD=0 (бит 10 в SAI_xCR2) // Настройка частоты и формата данных (I2S) SAI1_Block_A->FRCR = 0; SAI1_Block_A->FRCR |= (0 << SAI_xFRCR_FSOFF_Pos); // Frame Sync Offset: 0 (стандарт I2S) SAI1_Block_A->FRCR |= (0 << SAI_xFRCR_FSDEF_Pos); // Frame Sync Definition SAI1_Block_A->FRCR |= (63 << SAI_xFRCR_FRL_Pos); // Frame Length: 64 бита (стандарт I2S) SAI1_Block_A->FRCR |= (31 << SAI_xFRCR_FSALL_Pos); // Frame Sync Active Length: 32 бита // Настройка слотов SAI1_Block_A->SLOTR = 0; SAI1_Block_A->SLOTR |= (1 << SAI_xSLOTR_NBSLOT_Pos); // 2 слота (стерео) //SAI1_Block_A->SLOTR |= (0 << SAI_xSLOTR_SLOTSZ_Pos); // Размер слота: 16 бит SAI1_Block_A->SLOTR |= (0b10 << SAI_xSLOTR_SLOTSZ_Pos); // Slot size: 32 bits (0b10) SAI1_Block_A->SLOTR |= (1 << 16) | (1 << 17); // Активируем слот 0 и 1 SAI1_Block_A->CR1 |= SAI_xCR1_SAIEN; // while (0 == (SAI1_Block_A->CR1 & SAI_xCR1_SAIEN)); // // Настраиваем параметры DMA dmaSetRequestSource(saip->dmastp, STM32_DMAMUX1_SAI1_A); // привязка к SAI1_A dmaStreamSetPeripheral(saip->dmastp, &SAI1_Block_A->DR); // Периферийный адрес dmaStreamSetMemory0(saip->dmastp, saip->buf_play); // Первый буфер dmaStreamSetMemory1(saip->dmastp, saip->buf_synth); // Второй буфер (для double buffer) dmaStreamSetTransactionSize(saip->dmastp, 2048 * 2); // Размер данных // Настраиваем режим работы DMA dmaStreamSetMode(saip->dmastp, STM32_DMA_CR_CHSEL(0) | // Канал 0 для SAI1_A STM32_DMA_CR_PSIZE_HWORD | // Размер периферии: 16 бит STM32_DMA_CR_MSIZE_HWORD | // Размер памяти: 16 бит STM32_DMA_CR_MINC | // Инкремент адреса памяти STM32_DMA_CR_CIRC | // Циркулярный режим STM32_DMA_CR_DBM | // Режим двойного буфера STM32_DMA_CR_DIR_M2P | // Направление: память → периферия STM32_DMA_CR_TCIE); // Разрешение прерывания по завершению } else{ LOG_ERROR("dma stream allocate fail");}} else{ LOG_ERROR("sai_drv allocate fail");} return saip; } void saiStart(sai_drv_t * saip) { if(saip){ nvicEnableVector(DMA1_Stream1_IRQn, 12); SAI1_Block_A->CR1 |= SAI_xCR1_DMAEN; dmaStreamEnable(saip->dmastp); } }
-
- Разобрался... nanorobot(201 знак., Сегодня, 10:39)