feat(mic): try1: setup sound with usb
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -56,3 +56,6 @@ Mkfile.old
|
|||||||
dkms.conf
|
dkms.conf
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/c
|
# End of https://www.toptal.com/developers/gitignore/api/c
|
||||||
|
|
||||||
|
Build/
|
||||||
|
*.bin
|
||||||
|
|||||||
29
firmware/App/Inc/audio_adc.h
Normal file
29
firmware/App/Inc/audio_adc.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef AUDIO_ADC_H
|
||||||
|
#define AUDIO_ADC_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "audio_config.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Инициализация ADC, DMA и Timer для audio capture
|
||||||
|
* @param callback Функция, вызываемая при заполнении буфера
|
||||||
|
* @return true если успешно, false при ошибке
|
||||||
|
*/
|
||||||
|
bool audio_adc_init(audio_buffer_ready_callback_t callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Запуск непрерывного захвата аудио
|
||||||
|
*/
|
||||||
|
void audio_adc_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Остановка захвата аудио
|
||||||
|
*/
|
||||||
|
void audio_adc_stop(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить текущее количество обработанных буферов
|
||||||
|
*/
|
||||||
|
uint32_t audio_adc_get_buffer_count(void);
|
||||||
|
|
||||||
|
#endif /* AUDIO_ADC_H */
|
||||||
35
firmware/App/Inc/audio_config.h
Normal file
35
firmware/App/Inc/audio_config.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef AUDIO_CONFIG_H
|
||||||
|
#define AUDIO_CONFIG_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Audio Configuration
|
||||||
|
#define AUDIO_SAMPLE_RATE 22050U // Hz (22.05 kHz)
|
||||||
|
#define AUDIO_BUFFER_SIZE 512U // Samples per buffer (для FFT)
|
||||||
|
#define AUDIO_NUM_BUFFERS 2U
|
||||||
|
|
||||||
|
// ADC Configuration
|
||||||
|
#define AUDIO_ADC_CHANNEL 1U // PA1 = ADC1_IN1
|
||||||
|
#define AUDIO_ADC_GPIO_PORT GPIOA
|
||||||
|
#define AUDIO_ADC_GPIO_PIN 1U // PA1
|
||||||
|
|
||||||
|
// Timer Configuration
|
||||||
|
// Timer2 будет триггерить ADC на частоте AUDIO_SAMPLE_RATE
|
||||||
|
// APB1 Timer clock = 72 MHz (на STM32F103 при SYSCLK=72MHz)
|
||||||
|
// Формула: Timer_Freq = Timer_Clock / ((PSC + 1) * (ARR + 1))
|
||||||
|
// Для 22050 Hz: 72000000 / 22050 = 3265.3
|
||||||
|
// Используем PSC=0, ARR=3264 → 72MHz / 3265 = 22051 Hz (погрешность 0.004%)
|
||||||
|
#define AUDIO_TIMER_PRESCALER 0U
|
||||||
|
#define AUDIO_TIMER_PERIOD 3264U // ARR value
|
||||||
|
|
||||||
|
// DMA Configuration
|
||||||
|
#define AUDIO_DMA_CHANNEL DMA1_Channel1 // ADC1 использует DMA1_CH1
|
||||||
|
|
||||||
|
// Data Types
|
||||||
|
typedef uint16_t audio_sample_t;
|
||||||
|
|
||||||
|
// Callback вызывается когда буфер заполнен
|
||||||
|
typedef void (
|
||||||
|
*audio_buffer_ready_callback_t)(audio_sample_t* buffer, uint32_t size);
|
||||||
|
|
||||||
|
#endif /* AUDIO_CONFIG_H */
|
||||||
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: обработка ошибки
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,30 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include "FreeRTOS.h"
|
#include "FreeRTOS.h"
|
||||||
|
#include "audio_adc.h"
|
||||||
#include "stm32f1xx.h"
|
#include "stm32f1xx.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
|
||||||
|
// System Clock Configuration
|
||||||
|
|
||||||
void SystemClock_Config(void) {
|
void SystemClock_Config(void) {
|
||||||
|
// Настройка на 72 MHz через HSE + PLL
|
||||||
|
|
||||||
// 1. Включаем HSE (внешний кварц 8 МГц)
|
// 1. Включаем HSE (внешний кварц 8 МГц)
|
||||||
RCC->CR |= RCC_CR_HSEON;
|
RCC->CR |= RCC_CR_HSEON;
|
||||||
while (!(RCC->CR & RCC_CR_HSERDY));
|
while (!(RCC->CR & RCC_CR_HSERDY));
|
||||||
|
|
||||||
// 2. Настраиваем Flash: 1 цикл ожидания (достаточно для 48 МГц)
|
// 2. Настраиваем Flash: 2 цикла ожидания для 72 MHz
|
||||||
FLASH->ACR = FLASH_ACR_LATENCY_1;
|
FLASH->ACR = FLASH_ACR_LATENCY_2;
|
||||||
|
|
||||||
// 3. Настраиваем PLL:
|
// 3. Настраиваем PLL: 8 MHz * 9 = 72 MHz
|
||||||
// Множитель x6: 8 МГц * 6 = 48 МГц
|
RCC->CFGR &= ~RCC_CFGR_PLLMULL;
|
||||||
RCC->CFGR &= ~RCC_CFGR_PLLMULL; // Сброс битов множителя
|
RCC->CFGR |= RCC_CFGR_PLLMULL9;
|
||||||
RCC->CFGR |= RCC_CFGR_PLLMULL6;
|
RCC->CFGR |= RCC_CFGR_PLLSRC; // Источник PLL = HSE
|
||||||
|
|
||||||
// Источник PLL = HSE
|
// 4. USB делитель: 72 MHz / 1.5 = 48 MHz
|
||||||
RCC->CFGR |= RCC_CFGR_PLLSRC;
|
RCC->CFGR &= ~RCC_CFGR_USBPRE; // USBPRE = 0 (делитель 1.5)
|
||||||
|
|
||||||
// 4. Настраиваем USB делитель:
|
|
||||||
// USBPRE = 1 (делитель /1). Т.е. 48 МГц / 1 = 48 МГц
|
|
||||||
RCC->CFGR |= RCC_CFGR_USBPRE;
|
|
||||||
|
|
||||||
// 5. Включаем PLL
|
// 5. Включаем PLL
|
||||||
RCC->CR |= RCC_CR_PLLON;
|
RCC->CR |= RCC_CR_PLLON;
|
||||||
@@ -32,21 +35,79 @@ void SystemClock_Config(void) {
|
|||||||
RCC->CFGR |= RCC_CFGR_SW_PLL;
|
RCC->CFGR |= RCC_CFGR_SW_PLL;
|
||||||
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
|
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
|
||||||
|
|
||||||
// Обновляем глобальную переменную частоты
|
SystemCoreClock = 72000000;
|
||||||
SystemCoreClock = 48000000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Задача USB
|
// Audio Processing Callback
|
||||||
|
|
||||||
|
// Буфер для отправки статистики через USB
|
||||||
|
static char usb_tx_buffer[128];
|
||||||
|
static volatile uint32_t total_samples = 0;
|
||||||
|
|
||||||
|
void audio_buffer_ready(audio_sample_t *buffer, uint32_t size) {
|
||||||
|
// Эта функция вызывается из прерывания DMA
|
||||||
|
// Не делать тяжелых операций здесь
|
||||||
|
|
||||||
|
total_samples += size;
|
||||||
|
|
||||||
|
// мин/макс значения
|
||||||
|
uint16_t min_val = 4095;
|
||||||
|
uint16_t max_val = 0;
|
||||||
|
uint32_t sum = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < size; i++) {
|
||||||
|
uint16_t val = buffer[i];
|
||||||
|
if (val < min_val) min_val = val;
|
||||||
|
if (val > max_val) max_val = val;
|
||||||
|
sum += val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t avg = sum / size;
|
||||||
|
|
||||||
|
// Каждые 2 секунды отправляем статистику: каждый 100-й буфер
|
||||||
|
static uint32_t report_counter = 0;
|
||||||
|
report_counter++;
|
||||||
|
|
||||||
|
if (report_counter >= 100) {
|
||||||
|
report_counter = 0;
|
||||||
|
|
||||||
|
snprintf(
|
||||||
|
usb_tx_buffer,
|
||||||
|
sizeof(usb_tx_buffer),
|
||||||
|
"ADC Stats - Min: %4u, Max: %4u, Avg: %4u, Total samples: %lu\r\n",
|
||||||
|
min_val,
|
||||||
|
max_val,
|
||||||
|
avg,
|
||||||
|
total_samples);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeRTOS Tasks
|
||||||
|
|
||||||
void usb_device_task(void *param) {
|
void usb_device_task(void *param) {
|
||||||
(void)param;
|
(void)param;
|
||||||
while (1) { tud_task(); }
|
while (1) {
|
||||||
|
tud_task();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Задача CDC
|
|
||||||
void cdc_task(void *param) {
|
void cdc_task(void *param) {
|
||||||
(void)param;
|
(void)param;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (tud_cdc_connected()) {
|
if (tud_cdc_connected()) {
|
||||||
|
// Отправка статистики если есть данные
|
||||||
|
if (usb_tx_buffer[0] != '\0') {
|
||||||
|
size_t len = strlen(usb_tx_buffer);
|
||||||
|
if (tud_cdc_write_available() >= len) {
|
||||||
|
tud_cdc_write(usb_tx_buffer, len);
|
||||||
|
tud_cdc_write_flush();
|
||||||
|
usb_tx_buffer[0] = '\0'; // Очищаем буфер
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo для тестирования
|
||||||
if (tud_cdc_available()) {
|
if (tud_cdc_available()) {
|
||||||
uint8_t buf[64];
|
uint8_t buf[64];
|
||||||
uint32_t count = tud_cdc_read(buf, sizeof(buf));
|
uint32_t count = tud_cdc_read(buf, sizeof(buf));
|
||||||
@@ -54,21 +115,24 @@ void cdc_task(void *param) {
|
|||||||
tud_cdc_write_flush();
|
tud_cdc_write_flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Задача LED (отдельно!)
|
|
||||||
void led_task(void *param) {
|
void led_task(void *param) {
|
||||||
(void)param;
|
(void)param;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
GPIOC->BSRR = GPIO_BSRR_BR13; // LED ON
|
GPIOC->BSRR = GPIO_BSRR_BR13; // LED ON
|
||||||
vTaskDelay(pdMS_TO_TICKS(500));
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
GPIOC->BSRR = GPIO_BSRR_BS13; // LED OFF
|
GPIOC->BSRR = GPIO_BSRR_BS13; // LED OFF
|
||||||
vTaskDelay(pdMS_TO_TICKS(500));
|
vTaskDelay(pdMS_TO_TICKS(900));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// USB Reset
|
||||||
|
|
||||||
void force_usb_reset(void) {
|
void force_usb_reset(void) {
|
||||||
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
||||||
GPIOA->CRH &= ~GPIO_CRH_CNF12;
|
GPIOA->CRH &= ~GPIO_CRH_CNF12;
|
||||||
@@ -79,10 +143,12 @@ void force_usb_reset(void) {
|
|||||||
GPIOA->CRH |= GPIO_CRH_CNF12_0;
|
GPIOA->CRH |= GPIO_CRH_CNF12_0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
SystemClock_Config();
|
SystemClock_Config();
|
||||||
|
|
||||||
// Настройка LED
|
// Настройка LED (PC13)
|
||||||
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
|
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
|
||||||
GPIOC->CRH &= ~GPIO_CRH_CNF13;
|
GPIOC->CRH &= ~GPIO_CRH_CNF13;
|
||||||
GPIOC->CRH |= GPIO_CRH_MODE13_1;
|
GPIOC->CRH |= GPIO_CRH_MODE13_1;
|
||||||
@@ -93,7 +159,6 @@ int main(void) {
|
|||||||
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
||||||
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
|
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
|
||||||
|
|
||||||
// Устанавливаем приоритет USB прерываний больше 5, больше FreeRTOS
|
|
||||||
NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, 6);
|
NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, 6);
|
||||||
NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 6);
|
NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 6);
|
||||||
NVIC_SetPriority(USBWakeUp_IRQn, 6);
|
NVIC_SetPriority(USBWakeUp_IRQn, 6);
|
||||||
@@ -104,6 +169,21 @@ int main(void) {
|
|||||||
|
|
||||||
tusb_init();
|
tusb_init();
|
||||||
|
|
||||||
|
// Инициализация Audio ADC
|
||||||
|
if (!audio_adc_init(audio_buffer_ready)) {
|
||||||
|
// Ошибка инициализации - быстро мигаем LED
|
||||||
|
while (1) {
|
||||||
|
GPIOC->BSRR = GPIO_BSRR_BR13;
|
||||||
|
for (volatile int i = 0; i < 100000; i++);
|
||||||
|
GPIOC->BSRR = GPIO_BSRR_BS13;
|
||||||
|
for (volatile int i = 0; i < 100000; i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запускаем захват аудио
|
||||||
|
audio_adc_start();
|
||||||
|
|
||||||
|
// Создаем задачи FreeRTOS
|
||||||
xTaskCreate(
|
xTaskCreate(
|
||||||
usb_device_task,
|
usb_device_task,
|
||||||
"usbd",
|
"usbd",
|
||||||
@@ -115,9 +195,12 @@ int main(void) {
|
|||||||
xTaskCreate(led_task, "led", 128, NULL, 1, NULL);
|
xTaskCreate(led_task, "led", 128, NULL, 1, NULL);
|
||||||
|
|
||||||
vTaskStartScheduler();
|
vTaskStartScheduler();
|
||||||
|
|
||||||
while (1);
|
while (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// USB Interrupt Handlers
|
||||||
|
|
||||||
void USB_HP_CAN1_TX_IRQHandler(void) {
|
void USB_HP_CAN1_TX_IRQHandler(void) {
|
||||||
tud_int_handler(0);
|
tud_int_handler(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ BUILD_DIR = Build
|
|||||||
# 1. Приложение
|
# 1. Приложение
|
||||||
C_SOURCES = \
|
C_SOURCES = \
|
||||||
App/Src/main.c \
|
App/Src/main.c \
|
||||||
|
App/Src/audio_adc.c \
|
||||||
App/Src/usb_descriptors.c \
|
App/Src/usb_descriptors.c \
|
||||||
App/Src/system_stm32f1xx.c \
|
App/Src/system_stm32f1xx.c \
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user