Audio: STM32 ADC: extended driver to STM32H7 family
This commit is contained in:
parent
3d04759e8d
commit
1a15f793f3
|
|
@ -1,8 +1,8 @@
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Copyright (C) 2023 by Federico Amedeo Izzo IU2NUO, *
|
* Copyright (C) 2023 - 2024 by Federico Amedeo Izzo IU2NUO, *
|
||||||
* Niccolò Izzo IU2KIN *
|
* Niccolò Izzo IU2KIN *
|
||||||
* Frederik Saraci IU2NRO *
|
* Frederik Saraci IU2NRO *
|
||||||
* Silvano Seva IU2KWO *
|
* Silvano Seva IU2KWO *
|
||||||
* *
|
* *
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
* it under the terms of the GNU General Public License as published by *
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
|
@ -19,17 +19,36 @@
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include <kernel/scheduler/scheduler.h>
|
#include <kernel/scheduler/scheduler.h>
|
||||||
|
#include <core/cache_cortexMx.h>
|
||||||
|
#include <interfaces/delays.h>
|
||||||
|
#include <peripherals/gpio.h>
|
||||||
#include <DmaStream.hpp>
|
#include <DmaStream.hpp>
|
||||||
#include <Timer.hpp>
|
#include <hwconfig.h>
|
||||||
#include <miosix.h>
|
#include <miosix.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "stm32_adc.h"
|
#include "stm32_adc.h"
|
||||||
|
|
||||||
|
#if defined(STM32H743xx)
|
||||||
|
#include <Lptim.hpp>
|
||||||
|
|
||||||
|
typedef Lptim timebase_type;
|
||||||
|
#define TimebaseCh1 Lptim(LPTIM3_BASE, 168000000)
|
||||||
|
#define TimebaseCh2 Lptim(LPTIM3_BASE, 168000000)
|
||||||
|
#define TimebaseCh3 Lptim(LPTIM3_BASE, 168000000)
|
||||||
|
#else
|
||||||
|
#include <Timer.hpp>
|
||||||
|
|
||||||
|
typedef Timer timebase_type;
|
||||||
|
#define TimebaseCh1 Timer(TIM2_BASE)
|
||||||
|
#define TimebaseCh2 Timer(TIM2_BASE)
|
||||||
|
#define TimebaseCh3 Timer(TIM2_BASE)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct AdcPeriph
|
struct AdcPeriph
|
||||||
{
|
{
|
||||||
ADC_TypeDef *adc;
|
ADC_TypeDef *adc;
|
||||||
StreamHandler *stream;
|
StreamHandler *stream;
|
||||||
Timer tim;
|
timebase_type tim;
|
||||||
};
|
};
|
||||||
|
|
||||||
using Dma2_Stream0 = DmaStream< DMA2_BASE, 0, 0, 2 >; // DMA 2, Stream 2, channel 0, high priority (ADC1)
|
using Dma2_Stream0 = DmaStream< DMA2_BASE, 0, 0, 2 >; // DMA 2, Stream 2, channel 0, high priority (ADC1)
|
||||||
|
|
@ -44,9 +63,9 @@ static struct streamCtx *AdcContext[3];
|
||||||
|
|
||||||
static constexpr AdcPeriph periph[] =
|
static constexpr AdcPeriph periph[] =
|
||||||
{
|
{
|
||||||
{(ADC_TypeDef *) ADC1_BASE, &Dma2_Stream0_hdl, Timer(TIM2_BASE)},
|
{(ADC_TypeDef *) ADC1_BASE, &Dma2_Stream0_hdl, TimebaseCh1},
|
||||||
{(ADC_TypeDef *) ADC2_BASE, &Dma2_Stream2_hdl, Timer(TIM2_BASE)},
|
{(ADC_TypeDef *) ADC2_BASE, &Dma2_Stream2_hdl, TimebaseCh2},
|
||||||
{(ADC_TypeDef *) ADC3_BASE, &Dma2_Stream1_hdl, Timer(TIM2_BASE)},
|
{(ADC_TypeDef *) ADC3_BASE, &Dma2_Stream1_hdl, TimebaseCh3},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -56,8 +75,16 @@ static constexpr AdcPeriph periph[] =
|
||||||
*/
|
*/
|
||||||
static void stopTransfer(const uint8_t instNum)
|
static void stopTransfer(const uint8_t instNum)
|
||||||
{
|
{
|
||||||
|
ADC_TypeDef *adc = periph[instNum].adc;
|
||||||
|
|
||||||
|
#ifdef STM32H743xx
|
||||||
|
adc->CR |= ADC_CR_ADSTP;
|
||||||
|
while((adc->CR & ADC_CR_ADSTP) != 0) ;
|
||||||
|
#else
|
||||||
|
adc->CR2 &= ~ADC_CR2_ADON;
|
||||||
|
#endif
|
||||||
|
|
||||||
periph[instNum].tim.stop();
|
periph[instNum].tim.stop();
|
||||||
periph[instNum].adc->CR2 &= ~ADC_CR2_ADON;
|
|
||||||
AdcContext[instNum]->running = 0;
|
AdcContext[instNum]->running = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,16 +144,37 @@ void stm32adc_init(const uint8_t instance)
|
||||||
// Enable peripherals
|
// Enable peripherals
|
||||||
switch(instance)
|
switch(instance)
|
||||||
{
|
{
|
||||||
case 0:
|
case STM32_ADC_ADC1:
|
||||||
|
#ifdef STM32H743xx
|
||||||
|
RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN;
|
||||||
|
ADC12_COMMON->CCR = ADC_CCR_CKMODE_1
|
||||||
|
| ADC_CCR_CKMODE_0;
|
||||||
|
DMAMUX1_Channel8->CCR = 9;
|
||||||
|
#else
|
||||||
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
|
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case STM32_ADC_ADC2:
|
||||||
|
#ifdef STM32H743xx
|
||||||
|
RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN;
|
||||||
|
ADC12_COMMON->CCR = ADC_CCR_CKMODE_1
|
||||||
|
| ADC_CCR_CKMODE_0;
|
||||||
|
DMAMUX1_Channel10->CCR = 10;
|
||||||
|
#else
|
||||||
RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
|
RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case STM32_ADC_ADC3:
|
||||||
|
#ifdef STM32H743xx
|
||||||
|
RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN;
|
||||||
|
ADC3_COMMON->CCR = ADC_CCR_CKMODE_1
|
||||||
|
| ADC_CCR_CKMODE_0;
|
||||||
|
DMAMUX1_Channel9->CCR = 115;
|
||||||
|
#else
|
||||||
RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;
|
RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,24 +182,41 @@ void stm32adc_init(const uint8_t instance)
|
||||||
* Use TIM2 as trigger source for all the ADCs.
|
* Use TIM2 as trigger source for all the ADCs.
|
||||||
* TODO: change this with a dedicated timer for each ADC.
|
* TODO: change this with a dedicated timer for each ADC.
|
||||||
*/
|
*/
|
||||||
|
#ifdef STM32H743xx
|
||||||
|
RCC->APB4ENR |= RCC_APB4ENR_LPTIM3EN;
|
||||||
|
RCC->D3CCIPR |= RCC_D3CCIPR_LPTIM345SEL_0;
|
||||||
|
uint32_t adcTrig = 20 << ADC_CFGR_EXTSEL_Pos; // lptim3_out as trig. source
|
||||||
|
#else
|
||||||
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
|
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
|
||||||
uint32_t adcTrig = ADC_CR2_EXTSEL_1 // 0b0110 TIM2_TRGO trig. source
|
ADC->CCR |= ADC_CCR_ADCPRE_0;
|
||||||
| ADC_CR2_EXTSEL_2;
|
uint32_t adcTrig = ADC_CR2_EXTSEL_2 // 0b0110 TIM2_TRGO trig. source
|
||||||
|
| ADC_CR2_EXTSEL_1;
|
||||||
|
#endif
|
||||||
__DSB();
|
__DSB();
|
||||||
|
|
||||||
/*
|
|
||||||
* ADC configuration.
|
|
||||||
*
|
|
||||||
* ADC clock is APB2 frequency divided by 4, giving 21MHz.
|
|
||||||
* Channel sample time set to 84 cycles, total conversion time is 100
|
|
||||||
* cycles: this leads to a maximum sampling frequency of 210kHz.
|
|
||||||
* Convert one channel only, no overrun interrupt, 12-bit resolution,
|
|
||||||
* no analog watchdog, discontinuous mode, no end of conversion interrupts.
|
|
||||||
*/
|
|
||||||
ADC_TypeDef *adc = periph[instance].adc;
|
ADC_TypeDef *adc = periph[instance].adc;
|
||||||
|
|
||||||
ADC->CCR |= ADC_CCR_ADCPRE_0;
|
#ifdef STM32H743xx
|
||||||
|
adc->CR = ADC_CR_ADVREGEN
|
||||||
|
| ADC_CR_BOOST_1
|
||||||
|
| ADC_CR_BOOST_0;
|
||||||
|
|
||||||
|
while((adc->ISR & ADC_ISR_LDORDY) == 0) ;
|
||||||
|
|
||||||
|
adc->ISR = ADC_ISR_ADRDY; // Clear the ADRDY flag
|
||||||
|
adc->CR |= ADC_CR_ADEN;
|
||||||
|
while((adc->ISR & ADC_ISR_ADRDY) != 0) ;
|
||||||
|
|
||||||
|
adc->SMPR2 = 0x36DB6DB6;
|
||||||
|
adc->SMPR1 = 0x36DB6DB6;
|
||||||
|
adc->SQR1 = 0; // Convert one channel
|
||||||
|
adc->CFGR |= ADC_CFGR_DISCEN
|
||||||
|
| ADC_CFGR_EXTEN_0 // Trigger on rising edge
|
||||||
|
| adcTrig // Trigger source
|
||||||
|
| ADC_CFGR_RES_1 // 12-bit resolution
|
||||||
|
| ADC_CFGR_DMNGT_1 // Continuous DMA requests
|
||||||
|
| ADC_CFGR_DMNGT_0; // Enable DMA data transfer
|
||||||
|
#else
|
||||||
adc->SMPR2 = ADC_SMPR2_SMP2_2
|
adc->SMPR2 = ADC_SMPR2_SMP2_2
|
||||||
| ADC_SMPR2_SMP1_2;
|
| ADC_SMPR2_SMP1_2;
|
||||||
adc->SQR1 = 0; // Convert one channel
|
adc->SQR1 = 0; // Convert one channel
|
||||||
|
|
@ -160,6 +225,7 @@ void stm32adc_init(const uint8_t instance)
|
||||||
| adcTrig // Trigger source
|
| adcTrig // Trigger source
|
||||||
| ADC_CR2_DDS // Continuous DMA requests
|
| ADC_CR2_DDS // Continuous DMA requests
|
||||||
| ADC_CR2_DMA; // Enable DMA data transfer
|
| ADC_CR2_DMA; // Enable DMA data transfer
|
||||||
|
#endif
|
||||||
|
|
||||||
// Register end-of-transfer callbacks
|
// Register end-of-transfer callbacks
|
||||||
periph[instance].stream->setEndTransferCallback(std::bind(stopTransfer, instance));
|
periph[instance].stream->setEndTransferCallback(std::bind(stopTransfer, instance));
|
||||||
|
|
@ -178,7 +244,11 @@ void stm32adc_terminate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: turn off peripherals
|
// TODO: turn off peripherals
|
||||||
|
#ifdef STM32H743xx
|
||||||
|
RCC->APB1LENR &= ~RCC_APB1LENR_TIM2EN;
|
||||||
|
#else
|
||||||
RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN;
|
RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN;
|
||||||
|
#endif
|
||||||
__DSB();
|
__DSB();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,8 +268,17 @@ static int stm32adc_start(const uint8_t instance, const void *config, struct str
|
||||||
AdcContext[instance] = ctx;
|
AdcContext[instance] = ctx;
|
||||||
|
|
||||||
// Set conversion channel and enable the ADC
|
// Set conversion channel and enable the ADC
|
||||||
periph[instance].adc->SQR3 = (uint32_t) config;
|
ADC_TypeDef *adc = periph[instance].adc;
|
||||||
periph[instance].adc->CR2 |= ADC_CR2_ADON;
|
|
||||||
|
#ifdef STM32H743xx
|
||||||
|
uint32_t channel = (uint32_t) config;
|
||||||
|
adc->SQR1 = channel << ADC_SQR1_SQ1_Pos;
|
||||||
|
adc->PCSEL = 1 << channel;
|
||||||
|
adc->CR |= ADC_CR_ADSTART;
|
||||||
|
#else
|
||||||
|
adc->SQR3 = (uint32_t) config;
|
||||||
|
adc->CR2 |= ADC_CR2_ADON;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Start DMA stream
|
// Start DMA stream
|
||||||
bool circ = false;
|
bool circ = false;
|
||||||
|
|
@ -220,15 +299,18 @@ static int stm32adc_data(struct streamCtx *ctx, stream_sample_t **buf)
|
||||||
{
|
{
|
||||||
AdcPeriph *p = reinterpret_cast< AdcPeriph * >(ctx->priv);
|
AdcPeriph *p = reinterpret_cast< AdcPeriph * >(ctx->priv);
|
||||||
|
|
||||||
|
// Default case: linear mode
|
||||||
|
*buf = ctx->buffer;
|
||||||
|
int size = ctx->bufSize;
|
||||||
|
|
||||||
if(ctx->bufMode == BUF_CIRC_DOUBLE)
|
if(ctx->bufMode == BUF_CIRC_DOUBLE)
|
||||||
{
|
{
|
||||||
*buf = reinterpret_cast< stream_sample_t *>(p->stream->idleBuf());
|
*buf = reinterpret_cast< stream_sample_t *>(p->stream->idleBuf());
|
||||||
return ctx->bufSize/2;
|
size = ctx->bufSize/2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Linear mode: return the full buffer
|
miosix::markBufferAfterDmaRead(*buf, size*sizeof(int16_t));
|
||||||
*buf = ctx->buffer;
|
return size;
|
||||||
return ctx->bufSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stm32adc_sync(struct streamCtx *ctx, uint8_t dirty)
|
static int stm32adc_sync(struct streamCtx *ctx, uint8_t dirty)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Copyright (C) 2023 by Federico Amedeo Izzo IU2NUO, *
|
* Copyright (C) 2023 - 2024 by Federico Amedeo Izzo IU2NUO, *
|
||||||
* Niccolò Izzo IU2KIN *
|
* Niccolò Izzo IU2KIN *
|
||||||
* Frederik Saraci IU2NRO *
|
* Frederik Saraci IU2NRO *
|
||||||
* Silvano Seva IU2KWO *
|
* Silvano Seva IU2KWO *
|
||||||
* *
|
* *
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
* it under the terms of the GNU General Public License as published by *
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stm32f4xx.h>
|
|
||||||
#include <interfaces/audio.h>
|
#include <interfaces/audio.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue