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