kopia lustrzana https://github.com/skuep/AIOC
Implemented OPAMP functionality for v1.2 hardware
rodzic
b61957be94
commit
868ac9323b
|
@ -54,7 +54,7 @@
|
|||
/* #define HAL_I2S_MODULE_ENABLED */
|
||||
/* #define HAL_IRDA_MODULE_ENABLED */
|
||||
#define HAL_IWDG_MODULE_ENABLED
|
||||
/* #define HAL_OPAMP_MODULE_ENABLED */
|
||||
#define HAL_OPAMP_MODULE_ENABLED
|
||||
/* #define HAL_PCD_MODULE_ENABLED */
|
||||
#define HAL_PWR_MODULE_ENABLED
|
||||
#define HAL_RCC_MODULE_ENABLED
|
||||
|
|
|
@ -76,8 +76,11 @@ static void Timer_ADC_Init(void);
|
|||
static void Timer_DAC_Init(void);
|
||||
static void ADC_Init(void);
|
||||
static void DAC_Init(void);
|
||||
static void RX_Config(usb_audio_rxgain_t rxGain);
|
||||
static void TX_Config(usb_audio_txboost_t txBoost);
|
||||
static void Timeout_Timers_Init(void);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API Implementations
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -516,6 +519,7 @@ bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, u
|
|||
|
||||
if (microphoneState == STATE_START) {
|
||||
/* Start ADC sampling as soon as device stacks starts loading data (will be a ZLP for first frame) */
|
||||
RX_Config(USB_AUDIO_RXGAIN_1X);
|
||||
NVIC_EnableIRQ(ADC1_2_IRQn);
|
||||
microphoneState = STATE_RUN;
|
||||
|
||||
|
@ -539,8 +543,9 @@ bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, u
|
|||
|
||||
if (speakerState == STATE_START) {
|
||||
if (count >= SPEAKER_BUFFERLVL_TARGET) {
|
||||
/* Wait until whe are at buffer target fill level, then start DAC output */
|
||||
/* Wait until we are at buffer target fill level, then start DAC output */
|
||||
speakerState = STATE_RUN;
|
||||
TX_Config(USB_AUDIO_TXBOOST_OFF);
|
||||
NVIC_EnableIRQ(TIM6_DAC1_IRQn);
|
||||
|
||||
/* Update debug register */
|
||||
|
@ -718,10 +723,19 @@ TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t
|
|||
|
||||
void ADC1_2_IRQHandler (void)
|
||||
{
|
||||
if (ADC2->ISR & ADC_ISR_EOS) {
|
||||
ADC2->ISR = ADC_ISR_EOS;
|
||||
if ( (ADC1->ISR & ADC_ISR_EOS) || (ADC2->ISR & ADC_ISR_EOS) ) {
|
||||
int16_t sample = 0;
|
||||
|
||||
/* Get ADC sample */
|
||||
int16_t sample = ((int32_t) ADC2->DR - 32768) & 0xFFFFU;
|
||||
if (ADC1->ISR & ADC_ISR_EOS) {
|
||||
ADC1->ISR = ADC_ISR_EOS;
|
||||
sample = ((int32_t) ADC1->DR - 32768) & 0xFFFFU;
|
||||
}
|
||||
|
||||
if (ADC2->ISR & ADC_ISR_EOS) {
|
||||
ADC2->ISR = ADC_ISR_EOS;
|
||||
sample = ((int32_t) ADC2->DR - 32768) & 0xFFFFU;
|
||||
}
|
||||
|
||||
/* Automatic COS */
|
||||
uint16_t cosThreshold = (settingsRegMap[SETTINGS_REG_VCOS_LVLCTRL] & SETTINGS_REG_VCOS_LVLCTRL_THRSHLD_MASK) >> SETTINGS_REG_VCOS_LVLCTRL_THRSHLD_OFFS;
|
||||
|
@ -844,6 +858,7 @@ void TIM17_IRQHandler(void)
|
|||
|
||||
static void GPIO_Init(void)
|
||||
{
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
|
||||
GPIO_InitTypeDef ADCInGpio;
|
||||
|
@ -854,6 +869,22 @@ static void GPIO_Init(void)
|
|||
ADCInGpio.Alternate = 0;
|
||||
HAL_GPIO_Init(GPIOB, &ADCInGpio);
|
||||
|
||||
GPIO_InitTypeDef OPAMP1InGpio;
|
||||
OPAMP1InGpio.Pin = GPIO_PIN_5;
|
||||
OPAMP1InGpio.Mode = GPIO_MODE_ANALOG;
|
||||
OPAMP1InGpio.Pull = GPIO_NOPULL;
|
||||
OPAMP1InGpio.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
OPAMP1InGpio.Alternate = 0;
|
||||
HAL_GPIO_Init(GPIOA, &OPAMP1InGpio);
|
||||
|
||||
GPIO_InitTypeDef OPAMP2OutGpio;
|
||||
OPAMP2OutGpio.Pin = GPIO_PIN_6;
|
||||
OPAMP2OutGpio.Mode = GPIO_MODE_ANALOG;
|
||||
OPAMP2OutGpio.Pull = GPIO_NOPULL;
|
||||
OPAMP2OutGpio.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
OPAMP2OutGpio.Alternate = 0;
|
||||
HAL_GPIO_Init(GPIOA, &OPAMP2OutGpio);
|
||||
|
||||
GPIO_InitTypeDef SamplerateGpio;
|
||||
SamplerateGpio.Pin = GPIO_PIN_0;
|
||||
SamplerateGpio.Mode = GPIO_MODE_AF_PP;
|
||||
|
@ -869,6 +900,14 @@ static void GPIO_Init(void)
|
|||
DACOutGpio.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
DACOutGpio.Alternate = 0;
|
||||
HAL_GPIO_Init(GPIOA, &DACOutGpio);
|
||||
|
||||
GPIO_InitTypeDef DACAttenGpio;
|
||||
DACAttenGpio.Pin = GPIO_PIN_3;
|
||||
DACAttenGpio.Mode = GPIO_MODE_OUTPUT_OD;
|
||||
DACAttenGpio.Pull = GPIO_NOPULL;
|
||||
DACAttenGpio.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
DACAttenGpio.Alternate = 0;
|
||||
HAL_GPIO_Init(GPIOA, &DACAttenGpio);
|
||||
}
|
||||
|
||||
static void Timer_ADC_Init(void)
|
||||
|
@ -926,9 +965,13 @@ static void Timer_DAC_Init(void)
|
|||
|
||||
static void ADC_Init(void)
|
||||
{
|
||||
/* We use two ADCs. ADC1 is used, when OPAMP(PGA) is used. Otherwise ADC2 with direct hardware connection is used */
|
||||
__HAL_RCC_ADC1_CLK_ENABLE();
|
||||
__HAL_RCC_ADC2_CLK_ENABLE();
|
||||
|
||||
ADC1->CR = 0x00 << ADC_CR_ADVREGEN_Pos;
|
||||
ADC2->CR = 0x00 << ADC_CR_ADVREGEN_Pos;
|
||||
ADC1->CR = 0x01 << ADC_CR_ADVREGEN_Pos;
|
||||
ADC2->CR = 0x01 << ADC_CR_ADVREGEN_Pos;
|
||||
|
||||
for (uint32_t i=0; i<200; i++) {
|
||||
|
@ -938,32 +981,35 @@ static void ADC_Init(void)
|
|||
/* Select AHB clock */
|
||||
ADC12_COMMON->CCR = (0x1 << ADC12_CCR_CKMODE_Pos) | (0x00 << ADC12_CCR_MULTI_Pos);
|
||||
|
||||
ADC1->CR |= ADC_CR_ADCAL;
|
||||
ADC2->CR |= ADC_CR_ADCAL;
|
||||
|
||||
while (ADC2->CR & ADC_CR_ADCAL)
|
||||
while ( (ADC1->CR & ADC_CR_ADCAL) || (ADC2->CR & ADC_CR_ADCAL) )
|
||||
;
|
||||
|
||||
ADC1->CR |= ADC_CR_ADEN;
|
||||
ADC2->CR |= ADC_CR_ADEN;
|
||||
|
||||
/* Wait for ADC to be ready */
|
||||
while (!(ADC2->ISR & ADC_ISR_ADRDY))
|
||||
while (!(ADC1->ISR & ADC_ISR_ADRDY) || !(ADC2->ISR & ADC_ISR_ADRDY) )
|
||||
;
|
||||
|
||||
/* External Trigger on TIM3_TRGO, left aligned data with 12 bit resolution */
|
||||
ADC1->CFGR = (0x01 << ADC_CFGR_EXTEN_Pos) | (0x04 << ADC_CFGR_EXTSEL_Pos) | (ADC_CFGR_ALIGN) | (0x00 << ADC_CFGR_RES_Pos);
|
||||
ADC2->CFGR = (0x01 << ADC_CFGR_EXTEN_Pos) | (0x04 << ADC_CFGR_EXTSEL_Pos) | (ADC_CFGR_ALIGN) | (0x00 << ADC_CFGR_RES_Pos);
|
||||
|
||||
/* Maximum sample time of 601.5 cycles for channel 12. */
|
||||
/* Maximum sample time of 601.5 cycles for channel 3/channel 12. */
|
||||
ADC1->SMPR1 = 0x7 << ADC_SMPR1_SMP3_Pos;
|
||||
ADC2->SMPR2 = 0x7 << ADC_SMPR2_SMP12_Pos;
|
||||
|
||||
/* Sample only channel 12 in a regular sequence */
|
||||
/* Sample only channel 3/channel 12 in a regular sequence */
|
||||
ADC1->SQR1 = ( 3 << ADC_SQR1_SQ1_Pos) | (0 << ADC_SQR1_L_Pos);
|
||||
ADC2->SQR1 = (12 << ADC_SQR1_SQ1_Pos) | (0 << ADC_SQR1_L_Pos);
|
||||
|
||||
/* Enable Interrupt Request */
|
||||
ADC1->IER = ADC_IER_EOSIE;
|
||||
ADC2->IER = ADC_IER_EOSIE;
|
||||
|
||||
/* Start ADC */
|
||||
ADC2->CR |= ADC_CR_ADSTART;
|
||||
|
||||
NVIC_SetPriority(ADC1_2_IRQn, AIOC_IRQ_PRIO_AUDIO);
|
||||
}
|
||||
|
||||
|
@ -973,9 +1019,79 @@ static void DAC_Init(void)
|
|||
|
||||
/* Select TIM6 TRGO as trigger and enable DAC */
|
||||
DAC->CR = (0x0 << DAC_CR_TSEL1_Pos) | DAC_CR_TEN1 | DAC_CR_EN1;
|
||||
|
||||
/* Output VDD/2 */
|
||||
DAC1->DHR12L1 = 32768;
|
||||
}
|
||||
|
||||
static void Timeout_Timers_Init()
|
||||
static void RX_Config(usb_audio_rxgain_t rxGain)
|
||||
{
|
||||
/* Disable OPAMPs */
|
||||
OPAMP1->CSR = 0x00;
|
||||
OPAMP2->CSR = 0x00;
|
||||
|
||||
if (rxGain == USB_AUDIO_RXGAIN_1X) {
|
||||
/* Legacy mode that is compatible with pre-v1.2 hardware */
|
||||
OPAMP2->CSR = OPAMP_FOLLOWER_MODE | OPAMP_VREF_50VDDA | OPAMP_CSR_FORCEVP | OPAMP2_CSR_OPAMP2EN; /* 50% VDD for bias */
|
||||
|
||||
/* Start ADC2 with direct hardware ADC input (no PGA in between) */
|
||||
if (ADC1->CR & ADC_CR_ADSTART) ADC1->CR |= ADC_CR_ADSTP;
|
||||
if (!(ADC2->CR & ADC_CR_ADSTART)) ADC2->CR |= ADC_CR_ADSTART;
|
||||
} else {
|
||||
/* Initialize OPAMPs so that OPAMP1 is a PGA (non inverting) and OPAMP2 produces the correct DC-bias voltage according to OPAMP1 gain. */
|
||||
static const uint32_t pgaConfig[] = {
|
||||
[USB_AUDIO_RXGAIN_2X] = OPAMP_PGA_MODE | OPAMP_PGA_GAIN_2, /* ADCin: 0.825V +/- 0.825V */
|
||||
[USB_AUDIO_RXGAIN_4X] = OPAMP_PGA_MODE | OPAMP_PGA_GAIN_4, /* ADCin: 0.4125V +/- 0.4125V */
|
||||
[USB_AUDIO_RXGAIN_8X] = OPAMP_PGA_MODE | OPAMP_PGA_GAIN_8, /* ADCin: 0.20625V +/- 0.20625V */
|
||||
[USB_AUDIO_RXGAIN_16X] = OPAMP_PGA_MODE | OPAMP_PGA_GAIN_16 /* ADCin: 0.103125V +/- 0.103125V */
|
||||
};
|
||||
|
||||
static const uint32_t biasConfig[] = {
|
||||
[USB_AUDIO_RXGAIN_2X] = OPAMP_PGA_MODE | OPAMP_PGA_GAIN_8, /* 3.3V * 3.3% * 8 = 0.8712V */
|
||||
[USB_AUDIO_RXGAIN_4X] = OPAMP_PGA_MODE | OPAMP_PGA_GAIN_4, /* 3.3V * 3.3% * 4 = 0.4356V */
|
||||
[USB_AUDIO_RXGAIN_8X] = OPAMP_PGA_MODE | OPAMP_PGA_GAIN_2, /* 3.3V * 3.3% * 2 = 0.2178V */
|
||||
[USB_AUDIO_RXGAIN_16X] = OPAMP_FOLLOWER_MODE /* 3.3V * 3.3% * 1 = 0.1089V */
|
||||
};
|
||||
|
||||
/* Set up OPAMP1 PGA */
|
||||
OPAMP1->CSR = pgaConfig[rxGain] | OPAMP_NONINVERTINGINPUT_IO3 | OPAMP1_CSR_OPAMP1EN;
|
||||
|
||||
/* Add a trimming offset adjustment to get closer to the required bias voltage reference of 1.65V/16 = 103mV.
|
||||
* Default setting is 3.3V * 3.3% = 109mV.
|
||||
* These return the factory trim values, because the CSR register (and thus the USERTRIM bit) is 0 */
|
||||
uint32_t trimmingOffsetP2 = (OPAMP2->CSR & OPAMP2_CSR_TRIMOFFSETP_Msk) >> OPAMP2_CSR_TRIMOFFSETP_Pos;
|
||||
uint32_t trimmingOffsetN2 = (OPAMP2->CSR & OPAMP2_CSR_TRIMOFFSETN_Msk) >> OPAMP2_CSR_TRIMOFFSETN_Pos;
|
||||
int8_t trimAdjust = 7; /* Found empirically. It looks like 1 step amounts to roughly 1mV input offset */
|
||||
|
||||
/* Observe the register limits, do saturated arithmetic */
|
||||
trimmingOffsetP2 = (((int32_t) trimmingOffsetP2 + trimAdjust) <= 0x1F) ? trimmingOffsetP2 + trimAdjust : 0x1F;
|
||||
trimmingOffsetN2 = (((int32_t) trimmingOffsetN2 - trimAdjust) >= 0x00) ? trimmingOffsetN2 - trimAdjust : 0x00;
|
||||
|
||||
/* Set up OPAMP2 bias voltage generation, disable the factory trimming */
|
||||
OPAMP2->CSR = biasConfig[rxGain] | OPAMP_VREF_3VDDA | OPAMP_CSR_FORCEVP | OPAMP2_CSR_OPAMP2EN | OPAMP_TRIMMING_USER;
|
||||
|
||||
/* Load trimming values */
|
||||
OPAMP2->CSR |= (OPAMP2->CSR & ~(OPAMP2_CSR_TRIMOFFSETP_Msk | OPAMP2_CSR_TRIMOFFSETN_Msk)) |
|
||||
((trimmingOffsetP2 << OPAMP2_CSR_TRIMOFFSETP_Pos) & OPAMP2_CSR_TRIMOFFSETP_Msk) |
|
||||
((trimmingOffsetN2 << OPAMP2_CSR_TRIMOFFSETN_Pos) & OPAMP2_CSR_TRIMOFFSETN_Msk);
|
||||
|
||||
/* Start ADC1 using PGA output as ADC input */
|
||||
if (ADC2->CR & ADC_CR_ADSTART) ADC2->CR |= ADC_CR_ADSTP;
|
||||
if (!(ADC1->CR & ADC_CR_ADSTART)) ADC1->CR |= ADC_CR_ADSTART;
|
||||
}
|
||||
}
|
||||
|
||||
static void TX_Config(usb_audio_txboost_t txBoost)
|
||||
{
|
||||
/* Set PINA3 (ATTEN Hi-Z) in TX Boost mode, reset PINA3 (pull ATTEN low) in normal mode */
|
||||
if (txBoost == USB_AUDIO_TXBOOST_ON) {
|
||||
GPIOA->BSRR = GPIO_BSRR_BS_3;
|
||||
} else {
|
||||
GPIOA->BRR = GPIO_BRR_BR_3;
|
||||
}
|
||||
}
|
||||
|
||||
static void Timeout_Timers_Init(void)
|
||||
{
|
||||
uint32_t timerFreq = (HAL_RCC_GetHCLKFreq() == HAL_RCC_GetPCLK2Freq()) ? HAL_RCC_GetPCLK2Freq() : 2 * HAL_RCC_GetPCLK2Freq();
|
||||
uint32_t pttTimeout = (settingsRegMap[SETTINGS_REG_VPTT_TIMCTRL] & SETTINGS_REG_VPTT_TIMCTRL_TIMEOUT_MASK) >> SETTINGS_REG_VPTT_TIMCTRL_TIMEOUT_OFFS;
|
||||
|
|
|
@ -3,6 +3,19 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
USB_AUDIO_RXGAIN_1X,
|
||||
USB_AUDIO_RXGAIN_2X,
|
||||
USB_AUDIO_RXGAIN_4X,
|
||||
USB_AUDIO_RXGAIN_8X,
|
||||
USB_AUDIO_RXGAIN_16X,
|
||||
} usb_audio_rxgain_t;
|
||||
|
||||
typedef enum {
|
||||
USB_AUDIO_TXBOOST_OFF,
|
||||
USB_AUDIO_TXBOOST_ON
|
||||
} usb_audio_txboost_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t bufLevelMin;
|
||||
uint16_t bufLevelMax;
|
||||
|
|
Ładowanie…
Reference in New Issue