Files
sound-analyze/firmware/App/Src/audio_adc.c

239 lines
7.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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: обработка ошибки
}
}