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 знак., 25.08.2025 10:39)