ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Понедельник
25 августа
1538068
nanorobot (Вчера, 19:39 - 22:07, просмотров: 204)
Пытаюсь запустить I2S кодек PCM5102A на процессоре STM32H743/ChibiOS. Использую интерфейс SAI1A. Драйвера для этого интерфейса в последней версии ChibiOS нет, написал сам минимально необходимый. Почти работает. Все в порядке с частотами MCLK, BCLK, LRCK - все они в верных соотношениях, и полностью управляются настройками sample rate. Но частота прерываний по окончании передачи с одной стороны слишком велика - 4.4 КГц, а с другой стороны не зависит от изменения sample 

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);
  }
}