feat(mic): try1: setup sound with usb

This commit is contained in:
2025-12-25 21:06:50 +03:00
parent 2f0527a3d8
commit 3306b8083b
7 changed files with 411 additions and 22 deletions

View 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: обработка ошибки
}
}