feat(mic): try1: setup sound with usb
This commit is contained in:
238
firmware/App/Src/audio_adc.c
Normal file
238
firmware/App/Src/audio_adc.c
Normal file
@@ -0,0 +1,238 @@
|
||||
#include "audio_adc.h"
|
||||
#include <string.h>
|
||||
#include "stm32f1xx.h"
|
||||
|
||||
// Двойные буферы для DMA
|
||||
static audio_sample_t audio_buffer_0[AUDIO_BUFFER_SIZE] = {0};
|
||||
static audio_sample_t audio_buffer_1[AUDIO_BUFFER_SIZE] = {0};
|
||||
|
||||
// Указатель на активный буфер для обработки
|
||||
static volatile uint8_t active_buffer_index = 0;
|
||||
|
||||
// Callback функция
|
||||
static audio_buffer_ready_callback_t user_callback = NULL;
|
||||
|
||||
// Статистика (для отладки)
|
||||
static volatile uint32_t buffer_count = 0;
|
||||
static volatile uint32_t dma_half_transfer_count = 0;
|
||||
static volatile uint32_t dma_full_transfer_count = 0;
|
||||
|
||||
// Private Function Prototypes
|
||||
static void audio_gpio_init(void);
|
||||
static void audio_timer_init(void);
|
||||
static void audio_adc_hw_init(void);
|
||||
static void audio_dma_init(void);
|
||||
|
||||
bool audio_adc_init(audio_buffer_ready_callback_t callback) {
|
||||
if (callback == NULL) { return false; }
|
||||
|
||||
user_callback = callback;
|
||||
|
||||
// Инициализация компонентов
|
||||
audio_gpio_init();
|
||||
audio_timer_init();
|
||||
audio_adc_hw_init();
|
||||
audio_dma_init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void audio_adc_start(void) {
|
||||
// Запускаем DMA
|
||||
DMA1_Channel1->CCR |= DMA_CCR_EN;
|
||||
|
||||
// Запускаем ADC
|
||||
ADC1->CR2 |= ADC_CR2_ADON;
|
||||
|
||||
// Запускаем Timer2, начинаем генерировать TRGO события
|
||||
TIM2->CR1 |= TIM_CR1_CEN;
|
||||
}
|
||||
|
||||
void audio_adc_stop(void) {
|
||||
// Останавливаем Timer
|
||||
TIM2->CR1 &= ~TIM_CR1_CEN;
|
||||
|
||||
// Останавливаем ADC
|
||||
ADC1->CR2 &= ~ADC_CR2_ADON;
|
||||
|
||||
// Останавливаем DMA
|
||||
DMA1_Channel1->CCR &= ~DMA_CCR_EN;
|
||||
}
|
||||
|
||||
uint32_t audio_adc_get_buffer_count(void) {
|
||||
return buffer_count;
|
||||
}
|
||||
|
||||
// Private Functions
|
||||
|
||||
static void audio_gpio_init(void) {
|
||||
// Включаем тактирование GPIOA
|
||||
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
||||
|
||||
// PA1 как Analog Input (CNF=00, MODE=00)
|
||||
GPIOA->CRL &= ~(GPIO_CRL_CNF1 | GPIO_CRL_MODE1);
|
||||
// По умолчанию уже 0000, но явно устанавливаем
|
||||
GPIOA->CRL &= ~(0xF << 4); // Биты [7:4] для Pin 1
|
||||
}
|
||||
|
||||
static void audio_timer_init(void) {
|
||||
// Включаем тактирование Timer2
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
|
||||
|
||||
// Настраиваем Timer2 для генерации TRGO на частоте 22050 Hz
|
||||
TIM2->PSC = AUDIO_TIMER_PRESCALER;
|
||||
TIM2->ARR = AUDIO_TIMER_PERIOD;
|
||||
|
||||
// Master Mode Selection: Update event as TRGO
|
||||
// MMS[2:0] = 010 (Update)
|
||||
TIM2->CR2 &= ~TIM_CR2_MMS;
|
||||
TIM2->CR2 |= TIM_CR2_MMS_1; // 010
|
||||
|
||||
// Auto-reload preload enable
|
||||
TIM2->CR1 |= TIM_CR1_ARPE;
|
||||
|
||||
// Генерируем Update event для загрузки PSC/ARR
|
||||
TIM2->EGR |= TIM_EGR_UG;
|
||||
}
|
||||
|
||||
static void audio_adc_hw_init(void) {
|
||||
// Включаем тактирование ADC1
|
||||
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
|
||||
|
||||
// ADC Clock = PCLK2 / 6 = 72MHz / 6 = 12MHz (макс 14MHz для STM32F1)
|
||||
RCC->CFGR &= ~RCC_CFGR_ADCPRE;
|
||||
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;
|
||||
|
||||
// Сброс ADC1
|
||||
ADC1->CR2 = 0;
|
||||
ADC1->CR1 = 0;
|
||||
|
||||
// Конфигурация ADC1
|
||||
|
||||
// Scan mode disabled (один канал)
|
||||
ADC1->CR1 &= ~ADC_CR1_SCAN;
|
||||
|
||||
// Continuous mode disabled (будет триггериться Timer2)
|
||||
ADC1->CR2 &= ~ADC_CR2_CONT;
|
||||
|
||||
// External trigger: Timer2 TRGO
|
||||
// EXTSEL[2:0] = 011 (Timer2 TRGO для ADC1)
|
||||
ADC1->CR2 &= ~ADC_CR2_EXTSEL;
|
||||
ADC1->CR2 |= ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1; // 011
|
||||
|
||||
// External trigger enable for regular channels
|
||||
ADC1->CR2 |= ADC_CR2_EXTTRIG;
|
||||
|
||||
// 5. DMA mode enable
|
||||
ADC1->CR2 |= ADC_CR2_DMA;
|
||||
|
||||
// Data alignment: right aligned
|
||||
ADC1->CR2 &= ~ADC_CR2_ALIGN;
|
||||
|
||||
// Настройка канала
|
||||
|
||||
// Regular sequence length = 1 (только один канал)
|
||||
ADC1->SQR1 &= ~ADC_SQR1_L;
|
||||
|
||||
// First conversion in regular sequence: Channel 1 (PA1)
|
||||
ADC1->SQR3 &= ~ADC_SQR3_SQ1;
|
||||
ADC1->SQR3 |= (AUDIO_ADC_CHANNEL << ADC_SQR3_SQ1_Pos);
|
||||
|
||||
// Sample time для Channel 1: 7.5 cycles (минимум для 12MHz ADC)
|
||||
// SMPR2[5:3] для CH1 = 001 (7.5 cycles)
|
||||
ADC1->SMPR2 &= ~ADC_SMPR2_SMP1;
|
||||
ADC1->SMPR2 |= ADC_SMPR2_SMP1_0; // 001
|
||||
|
||||
// Калибровка ADC
|
||||
|
||||
// Включаем ADC
|
||||
ADC1->CR2 |= ADC_CR2_ADON;
|
||||
|
||||
// Ждем stabilization time (1 мкс)
|
||||
for (volatile int i = 0; i < 1000; i++);
|
||||
|
||||
// Запускаем калибровку
|
||||
ADC1->CR2 |= ADC_CR2_CAL;
|
||||
|
||||
// Ждем завершения калибровки
|
||||
while (ADC1->CR2 & ADC_CR2_CAL);
|
||||
|
||||
// Выключаем ADC (для запуска использовать audio_adc_start)
|
||||
ADC1->CR2 &= ~ADC_CR2_ADON;
|
||||
}
|
||||
|
||||
static void audio_dma_init(void) {
|
||||
// Включаем тактирование DMA1
|
||||
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
||||
|
||||
// Отключаем DMA1 Channel1 перед конфигурацией
|
||||
DMA1_Channel1->CCR &= ~DMA_CCR_EN;
|
||||
|
||||
// Ждем отключения
|
||||
while (DMA1_Channel1->CCR & DMA_CCR_EN);
|
||||
|
||||
// Конфигурация DMA
|
||||
|
||||
// Peripheral address: ADC1 Data Register
|
||||
DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR);
|
||||
|
||||
// Memory address: начинаем с buffer_0
|
||||
DMA1_Channel1->CMAR = (uint32_t)audio_buffer_0;
|
||||
|
||||
// Number of data to transfer: размер обоих буферов (для circular mode)
|
||||
DMA1_Channel1->CNDTR = AUDIO_BUFFER_SIZE * 2;
|
||||
|
||||
// Конфигураци
|
||||
uint32_t ccr = 0;
|
||||
ccr |= DMA_CCR_MINC; // Memory increment mode
|
||||
ccr |= DMA_CCR_CIRC; // Circular mode
|
||||
ccr |= DMA_CCR_HTIE; // Half transfer interrupt enable
|
||||
ccr |= DMA_CCR_TCIE; // Transfer complete interrupt enable
|
||||
ccr |= DMA_CCR_PL_1; // Priority level: High (10)
|
||||
|
||||
// Data size: 16-bit (MSIZE и PSIZE = 01)
|
||||
ccr |= DMA_CCR_MSIZE_0; // Memory size: 16-bit
|
||||
ccr |= DMA_CCR_PSIZE_0; // Peripheral size: 16-bit
|
||||
|
||||
DMA1_Channel1->CCR = ccr;
|
||||
|
||||
// Включаем прерывание DMA1_Channel1 в NVIC
|
||||
NVIC_SetPriority(DMA1_Channel1_IRQn, 6); // Приоритет 6 (ниже USB)
|
||||
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
|
||||
}
|
||||
|
||||
// DMA Interrupt Handler
|
||||
|
||||
void DMA1_Channel1_IRQHandler(void) {
|
||||
uint32_t isr = DMA1->ISR;
|
||||
|
||||
// Half Transfer Interrupt (первая половина буфера заполнена)
|
||||
if (isr & DMA_ISR_HTIF1) {
|
||||
DMA1->IFCR = DMA_IFCR_CHTIF1; // Сбрасываем флаг
|
||||
dma_half_transfer_count++;
|
||||
|
||||
// Первая половина = buffer_0 готов к обработке
|
||||
if (user_callback != NULL) {
|
||||
user_callback(audio_buffer_0, AUDIO_BUFFER_SIZE);
|
||||
}
|
||||
buffer_count++;
|
||||
}
|
||||
|
||||
// Transfer Complete Interrupt (вторая половина буфера заполнена)
|
||||
if (isr & DMA_ISR_TCIF1) {
|
||||
DMA1->IFCR = DMA_IFCR_CTCIF1; // Сбрасываем флаг
|
||||
dma_full_transfer_count++;
|
||||
|
||||
// Вторая половина = buffer_1 готов к обработке
|
||||
if (user_callback != NULL) {
|
||||
user_callback(audio_buffer_1, AUDIO_BUFFER_SIZE);
|
||||
}
|
||||
buffer_count++;
|
||||
}
|
||||
|
||||
// Transfer Error
|
||||
if (isr & DMA_ISR_TEIF1) {
|
||||
DMA1->IFCR = DMA_IFCR_CTEIF1; // Сбрасываем флаг
|
||||
// TODO: обработка ошибки
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user