STM Урок 46. I2S AUDIO. Часть 1



Урок 46

 

Часть 1

 

I2S AUDIO

 

Сегодня мы начинаем изучение нового для нас интерфейса – это шина I2S, чем то похожая на I2C, но заточенная непосредственно на передачу цифрового аудиопотока. Описание и диаграммы протокола данной шины можно найти на странице 894 STM32F4 Reference manual. Ну если кратко, данная шина является также синхронной, причем синхронизация обеспечивается не только для каждого переданного бита, как у I2C, а также поканально. То есть отдельный провод синхронизации задействован так, что при передачи полностью всех битов (их может быть 8, 16, 24) одного канала он находится в состоянии 1, а при передачи всех битов другого канала – в 0. Данное условие обеспечивает невозможность случайного обмена каналов между собой вследствие искажения сигнала потока.

Свой проект мы создадим из одного из предыдущих проектов USB_HOST_MSC_FATFS, так как работать мы будем с USB Flash Drive, ибо нам для изучения преобразования цифрового аудио надо это цифровое аудио откуда-то взять. Было принято решение взять его из WAV файлов, расположенных на данном носителе. Проект мы назовём по наименованию шины I2S_AUDIO. Так как мы ещё будем подключать к плате Discovery носитель USB Flash Drive, то необходимо туда скопировать для WAV-файла со звуком: Track1.wav и Track2.wav. Частота сэмплирования данных файлов может быть любая, но желательно не больше 48 кГц.

Посмотрим подключение аудио-микросхемы в плате Discovery (нажмите на картинку для увеличения изображения)

 

image00_0500

 

Данная микросхема именуется CS43L22. Основной её характеристикой является поддерживаемая частота дискретизации. Это от 4 кГц до 96 кГц. Откроем её даташит. Там существуют четыре вида протокола I2S. Мы используем самый первый стандарт. Единственное из даташита непонятно зачем нужен контакт MCLK. В даташите дано следующее описание данного контакта:

MCLK – Master Clock (Input) — Clock source for the delta-sigma modulators.

Пролистав несколько форумов, я нашел, что это ещё третий вид синхронизации – тактирование сэмплов.

Вот так у нас всё подключено к плате

 

image18

 

Откроем наш проект в MS Cube и сделаем необходимое добавление определённых настроек. Включим первым делом саму шину I2S

 

image02

 

Ножки никакие не переопределяем. Оставим так как есть. Именно по этим ножкам и подключена микросхема Аудио ЦАП.

Внесем также некоторые корректировки в настройки I2S

 

image01

 

Для работы лапки RESET микросхемы нам будет ещё необходимо включить на выход и настроить данную лапку порта (PD4)

 

 image04

image03

 

 

Также добавим и настроим DMA на шине I2S

 

image06

 

А так как управление микросхемой происходит в отличии от основного аудио-потока уже по шине I2C, то необходимо включить ещё и эту шину

 

image05

 

Только I2C3 мы отдадим под символьный дисплей, а I2C1 нам понадобится именно для аудио-микросхемы. Мало того, лапку PB7 нужно будет переопределить на PB9. Я думаю, все уже умеют это делать. Сначала сбросим её, а затем переопределим

 

   image08image07

 

Теперь переходник дисплея у нас будет подключен к другим лапкам портов:

SCL – PA8

SDA – PC9.

Сгенерируем проект для среды Keil, настроим программатор на авторезет, добавим в дерево проекта файл lcd.c и скомпилируем проект.

Создадим и добавим в проект новые файлы, предназначенные для работы со звуком, audioplay.c и audioplay.h следующего содержания:

audioplay.c:

#include «audioplay.h»

#include «fatfs.h»

#include «usb_host.h»

#include «lcd.h»

//——————————————————

 

audioplay.h:

#ifndef AUDIOPLAY_H_

#define AUDIOPLAY_H_

 

#ifdef __cplusplus

 extern «C» {

#endif

//————————————————

#include «stm32f4xx_hal.h»

//————————————————

 

#define I2S3                            SPI3

 

#ifdef __cplusplus

}

#endif

#endif /* AUDIOPLAY_H_ */

 

Также добавим подключение нашей библиотеки в главный модуль main.h

 

#include «lcd.h»

#include «audioplay.h»

 

 

Из-за того, что LCD-дисплей теперь находится на 3й шине I2C, то внесем некоторые изменения в файл lcd.c:

 

extern I2C_HandleTypeDef hi2c3;

HAL_I2C_Master_Transmit(&hi2c3,(uint16_t) 0x4E,buf,1,1000);

 

Уберем вот это из main()

 

        char str[100];

  /* USER CODE END 1 */

 

Также в main.c функцию FileReadWrite исправим в MenuProcess и очистим полностью её тело:

 

/* USER CODE BEGIN 0 */

void MenuProcess(void)

{

}

 

Соответственно, в бесконечном цикле также произойдут определённые изменения

 

  while (1)

  {

  /* USER CODE END WHILE */

    MX_USB_HOST_Process();

 

  /* USER CODE BEGIN 3 */

        MenuProcess();        

  }

  /* USER CODE END 3 */

 

Добавим некоторое количество переменных и макроподстановок в заголовочный файл audioplay.h.

 

#define I2S3                            SPI3

 

typedef enum

{

  BUFFER_OFFSET_NONE = 0,  

  BUFFER_OFFSET_HALF,  

  BUFFER_OFFSET_FULL,    

}BUFFER_StateTypeDef;

 

typedef struct

{

  uint32_t   ChunkID;       /* 0 */

  uint32_t   FileSize;      /* 4 */

  uint32_t   FileFormat;    /* 8 */

  uint32_t   SubChunk1ID;   /* 12 */

  uint32_t   SubChunk1Size; /* 16 */  

  uint16_t   AudioFormat;   /* 20 */

  uint16_t   NbrChannels;   /* 22 */  

  uint32_t   SampleRate;    /* 24 */

 

  uint32_t   ByteRate;      /* 28 */

  uint16_t   BlockAlign;    /* 32 */  

  uint16_t   BitPerSample;  /* 34 */  

  uint32_t   SubChunk2ID;   /* 36 */  

  uint32_t   SubChunk2Size; /* 40 */    

 

}WAVE_FormatTypeDef;

/* Audio State Structure */    

typedef enum {

  AUDIO_IDLE = 0,

  AUDIO_WAIT,  

  AUDIO_EXPLORE,

  AUDIO_PLAYBACK,

  AUDIO_IN,  

}AUDIO_State;

 

/* Audio Demo State Machine Structure */

typedef struct _StateMachine {

  __IO AUDIO_State state;

  __IO uint8_t select;  

}AUDIO_StateMachine;

//————————————————

 

И это ещё не всё. Также добавим ещё некоторое количество переменных, структур и макросов в файл audioplay.c

 

#include «lcd.h»

//——————————————————

#define I2S_STANDARD                  I2S_STANDARD_PHILIPS

 

/* Audio status definition */    

#define AUDIO_OK                        0

#define AUDIO_ERROR                     1

#define AUDIO_TIMEOUT                   2

 

/* Position in the audio play buffer */

__IO BUFFER_StateTypeDef buffer_offset = BUFFER_OFFSET_NONE;

 

/* Codec output DEVICE */

#define OUTPUT_DEVICE_SPEAKER         1

#define OUTPUT_DEVICE_HEADPHONE       2

#define OUTPUT_DEVICE_BOTH            3

#define OUTPUT_DEVICE_AUTO            4

 

/* MUTE commands */

#define AUDIO_MUTE_ON                 1

#define AUDIO_MUTE_OFF                0

 

/* Defines for the Audio playing process */

#define PAUSE_STATUS     ((uint32_t)0x00) /* Audio Player in Pause Status */

#define RESUME_STATUS    ((uint32_t)0x01) /* Audio Player in Resume Status */

#define IDLE_STATUS      ((uint32_t)0x02) /* Audio Player in Idle Status */

 

 

#define AUDIO_RESET_GPIO_CLK_ENABLE()         __GPIOD_CLK_ENABLE()

#define AUDIO_RESET_PIN                       GPIO_PIN_4

#define AUDIO_RESET_GPIO                      GPIOD

#define VOLUME_CONVERT(Volume)    (((Volume) > 100)? 100:((uint8_t)(((Volume) * 255) / 100)))

 

#define CODEC_STANDARD                0x04

 

/* Variables used in normal mode to manage audio file during DMA transfer */

uint32_t AudioTotalSize           = 0xFFFF; /* This variable holds the total size of the audio file */

int32_t AudioRemSize             = 0xFFFF; /* This variable holds the remaining data in audio file */

uint16_t *CurrentPos ;             /* This variable holds the current position of audio pointer */

static uint8_t Is_cs43l22_Stop = 1;

__IO uint32_t PauseResumeStatus = IDLE_STATUS;  

 

#define   CS43L22_REG_MISC_CTL            0x0E

 

#define AUDIO_I2C_ADDRESS                     0x94

#define CS43L22_CHIPID_ADDR    0x01

#define  CS43L22_ID            0xE0

#define  CS43L22_ID_MASK       0xF8

 

#define AUDIO_BUFFER_SIZE             0X8000

#define DMA_MAX_SZE                     0xFFFF

 

#define DMA_MAX(_X_)                (((_X_) <= DMA_MAX_SZE)? (_X_):DMA_MAX_SZE)

#define AUDIODATA_SIZE                  2   /* 16-bits audio data size */

extern FIL WavFile;

 

const uint32_t I2SFreq[8] = {8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000};

const uint32_t I2SPLLN[8] = {256, 429, 213, 429, 426, 271, 258, 344};

const uint32_t I2SPLLR[8] = {5, 4, 4, 4, 4, 6, 3, 1};

volatile uint8_t OutputDev = 0;

static uint32_t WaveDataLength = 0;

 

extern I2C_HandleTypeDef hi2c1;

extern I2S_HandleTypeDef hi2s3;

 

uint8_t Audio_Buffer[AUDIO_BUFFER_SIZE];

 

extern WAVE_FormatTypeDef *waveformat;

AUDIO_StateMachine     Audio;

uint32_t samplerate;

extern ApplicationTypeDef Appli_state;

char str2[20];

char str3[20];

uint32_t offsetpos;

uint32_t cnt;

uint32_t dur; //переменная для оставшегося времени воспроизведения файла

//——————————————————

 

Вот сколько нам всего понадобится. Ну конечно, всё это не зря, это делается для нашего же удобство. Ну и чтобы не показалось неправильным extern без основного добавления переменной, добавим её в main.c, а также ещё несколько переменных, заменив здесь существующие, раз уж мы сюда попали

 

/* USER CODE BEGIN PV */

/* Private variables ———————————————————*/

FATFS USBDISKFatFs;

FIL WavFile;

extern char USBH_Path[4];  /* USBH logical drive path */

extern ApplicationTypeDef Appli_state;

extern AUDIO_StateMachine     Audio;

WAVE_FormatTypeDef *waveformat =  NULL;

uint32_t WaveDataLength = 0;

char str[100];

char FileName[100]={0};

uint8_t info[44];

/* USER CODE END PV */

 

Ну, наконец-то, мы можем приступить к основному коду. Сначала напишем функцию ошибки в файле audioplay.c. Такой функцией мы пользуемся постоянно

 

void Error(void)

{

                HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);

}

//——————————————————

Напишем функцию инициализации

 

void AudioPlay_Init(uint32_t AudioFreq)

{

        samplerate = AudioFreq;

  __IO uint8_t volume = 70;

}

//————————————————-//————————————————-

 

Также создадим для данной функции прототип, так как она нам пригодится снаружи

 

}AUDIO_StateMachine;

//————————————————

void AudioPlay_Init(uint32_t AudioFreq);

//————————————————        

 

В следующей части урока мы напишем функцию извлечения информации из звукового файла о параметрах данного файла и вывода этих параметров на дисплей.

 

 

Предыдущий урок Программирование МК STM32 Следующая часть

 

Техническая документация на Аудио ЦАП CS43L22

 

 

Отладочную плату и дисплей можно приобрести здесь:

STM32F4-DISCOVERY

Дисплей LCD 20×4

 

 

Смотреть ВИДЕОУРОК

 

STM32 I2S AUDIO

4 комментария на “STM Урок 46. I2S AUDIO. Часть 1
  1. Алексей:

    Доброго времени суток! Имеется цап СS4334, подключен к STM32F103RET6 по даташиту. Как мне загрузить туда синусоиду с типом данных uint24_t, если функция HAL_I2S_Trasmit_DMA принимает параметрами лишь 16 бит переменные???
    Я работал с ЦАП-ом 12-бит, что встроен в МК. В том случае я делал воспроизведение синусоиды с типом данных uint16_t, но с амплитудой в 4095. Сейчас же понадобилось большее разрешение.

  2. Timur:

    Было бы очень интересно узнать как передавать сигнал с ПК через USB на выходы i2s ЦАПа с помощью stm32

  3. Владислав:

    Доброго времени суток! Хочется выразить Вам огромную благодарность за Ваши непосильные труды. Ну и вопрос по теме данного урока — повторил все по Ваши материалам, Файлы читает, информация о аудио отображается корректно ,счетчик тикает НО в динамиках белый шум. В чем может быть дело? Спасибо

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*