1
0
Fork 0
mirror of https://github.com/EdgeTX/edgetx.git synced 2025-07-15 20:35:14 +03:00
edgetx/radio/src/targets/common/arm/stm32/stm32_adc.cpp
richardclli cc17046e7a
feat: Flysky PL18 Support (#4105)
Co-authored-by: raphaelcoeffic <raphael.coeffic@frafos.com>
Co-authored-by: rotorman <risto.koiva@web.de>
Co-authored-by: Xy201207 <yaoxu@flysky.com>
Co-authored-by: Peter Feerick <peter.feerick@gmail.com>
2023-12-06 09:00:44 +10:00

605 lines
16 KiB
C++

/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "stm32_adc.h"
#include "stm32_dma.h"
#include "stm32_gpio_driver.h"
#include "timers_driver.h"
#include "opentx.h"
#define ADC_COMMON ((ADC_Common_TypeDef *)ADC_BASE)
#define OVERSAMPLING 4
#define SAMPLING_TIMEOUT_US 500
// Please note that we use the same prio for DMA TC and ADC IRQs
// to avoid issues with preemption between these 2
#define ADC_IRQ_PRIO configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
// Max 32 inputs supported
static uint32_t _adc_input_mask;
static uint32_t _adc_input_inhibt_mask = 0;
static volatile uint32_t _adc_inhibit_mask;
// DMA buffers
static uint16_t _adc_dma_buffer[MAX_ADC_INPUTS] __DMA;
// ADCs started
static volatile uint8_t _adc_started_mask;
static volatile uint8_t _adc_completed;
static const stm32_adc_t* _adc_ADCs;
static uint8_t _adc_n_ADC;
static const stm32_adc_input_t* _adc_inputs;
static uint8_t _adc_n_inputs;
// Need for oversampling and decimation
static uint8_t _adc_run;
static uint8_t _adc_oversampling_disabled;
static uint16_t _adc_oversampling[MAX_ADC_INPUTS];
void stm32_hal_mask_inputs(uint32_t inputs)
{
_adc_input_inhibt_mask |= inputs;
}
// STM32 uses a 25K+25K voltage divider bridge to measure the battery voltage
// Measuring VBAT puts considerable drain (22 µA) on the battery instead of
// normal drain (~10 nA)
void enableVBatBridge()
{
if (adcGetMaxInputs(ADC_INPUT_RTC_BAT) < 1) return;
// Set internal measurement path for vbat sensor
LL_ADC_SetCommonPathInternalCh(ADC_COMMON, LL_ADC_PATH_INTERNAL_VBAT);
auto channel = adcGetInputOffset(ADC_INPUT_RTC_BAT);
_adc_inhibit_mask &= ~(1 << channel);
}
void disableVBatBridge()
{
if (adcGetMaxInputs(ADC_INPUT_RTC_BAT) < 1) return;
auto channel = adcGetInputOffset(ADC_INPUT_RTC_BAT);
_adc_inhibit_mask |= (1 << channel);
// Set internal measurement path to none
LL_ADC_SetCommonPathInternalCh(ADC_COMMON, LL_ADC_PATH_INTERNAL_NONE);
}
bool isVBatBridgeEnabled()
{
// && !(_adc_inhibit_mask & (1 << channel));
return LL_ADC_GetCommonPathInternalCh(ADC_COMMON) == LL_ADC_PATH_INTERNAL_VBAT;
}
static void adc_enable_clock(ADC_TypeDef* ADCx)
{
uint32_t adc_idx = (((uint32_t) ADCx) - ADC1_BASE) / 0x100UL;
uint32_t adc_msk = RCC_APB2ENR_ADC1EN << adc_idx;
LL_APB2_GRP1_EnableClock(adc_msk);
}
static bool adc_disable_dma(DMA_TypeDef* DMAx, uint32_t stream);
static void adc_dma_clear_flags(DMA_TypeDef* DMAx, uint32_t stream);
static void adc_init_pins(const stm32_adc_gpio_t* GPIOs, uint8_t n_GPIO)
{
LL_GPIO_InitTypeDef pinInit;
LL_GPIO_StructInit(&pinInit);
pinInit.Mode = LL_GPIO_MODE_ANALOG;
pinInit.Pull = LL_GPIO_PULL_NO;
const stm32_adc_gpio_t* gpio = GPIOs;
while (n_GPIO > 0) {
pinInit.Pin = 0;
for (uint8_t pin_idx = 0; pin_idx < gpio->n_pins; pin_idx++) {
uint32_t pin = gpio->pins[pin_idx];
uint32_t mode = LL_GPIO_GetPinMode(gpio->GPIOx, pin);
// Output or AF: pin is probably used somewhere else
if (mode != LL_GPIO_MODE_INPUT && mode != LL_GPIO_MODE_ANALOG) continue;
pinInit.Pin |= pin;
}
stm32_gpio_enable_clock(gpio->GPIOx);
LL_GPIO_Init(gpio->GPIOx, &pinInit);
gpio++; n_GPIO--;
}
}
static const uint32_t _seq_length_lookup[] = {
LL_ADC_REG_SEQ_SCAN_DISABLE,
LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_3RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_4RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_5RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_6RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_7RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_8RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_9RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_10RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_11RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_12RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_13RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_14RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_15RANKS,
LL_ADC_REG_SEQ_SCAN_ENABLE_16RANKS,
};
static void adc_setup_scan_mode(ADC_TypeDef* ADCx, uint8_t nconv)
{
// ADC must be disabled for the functions used here
LL_ADC_Disable(ADCx);
LL_ADC_InitTypeDef adcInit;
LL_ADC_StructInit(&adcInit);
if (nconv > 1) {
adcInit.SequencersScanMode = LL_ADC_SEQ_SCAN_ENABLE;
} else {
adcInit.SequencersScanMode = LL_ADC_SEQ_SCAN_DISABLE;
}
adcInit.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
LL_ADC_Init(ADCx, &adcInit);
LL_ADC_REG_InitTypeDef adcRegInit;
LL_ADC_REG_StructInit(&adcRegInit);
adcRegInit.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
adcRegInit.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
if (nconv > 1) {
adcRegInit.SequencerLength = _seq_length_lookup[nconv - 1];
adcRegInit.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
}
LL_ADC_REG_Init(ADCx, &adcRegInit);
// Enable ADC
// TODO: check ADC_CR2_DDS (normally only for circular DMA
// but the old was using it (no clue why)
LL_ADC_Enable(ADCx);
}
static const uint32_t _rank_lookup[] = {
LL_ADC_REG_RANK_1,
LL_ADC_REG_RANK_2,
LL_ADC_REG_RANK_3,
LL_ADC_REG_RANK_4,
LL_ADC_REG_RANK_5,
LL_ADC_REG_RANK_6,
LL_ADC_REG_RANK_7,
LL_ADC_REG_RANK_8,
LL_ADC_REG_RANK_9,
LL_ADC_REG_RANK_10,
LL_ADC_REG_RANK_11,
LL_ADC_REG_RANK_12,
LL_ADC_REG_RANK_13,
LL_ADC_REG_RANK_14,
LL_ADC_REG_RANK_15,
LL_ADC_REG_RANK_16,
};
static uint8_t adc_init_channels(const stm32_adc_t* adc,
const stm32_adc_input_t* inputs,
const uint8_t* chan,
uint8_t nconv)
{
if (!chan || !nconv) return 0;
uint8_t rank = 0;
uint32_t channel_mask = 0;
while (nconv > 0) {
uint8_t input_idx = *chan;
const stm32_adc_input_t* input = &inputs[input_idx];
if (_adc_input_inhibt_mask & (1 << input_idx)) {
// skip input
nconv--; chan++;
continue;
}
// internal channel don't have a GPIO + pin defined
uint32_t mask = (1 << (ADC_CHANNEL_ID_MASK & input->ADC_Channel));
if (!__LL_ADC_IS_CHANNEL_INTERNAL(input->ADC_Channel)) {
uint32_t mode = LL_GPIO_GetPinMode(input->GPIOx, input->GPIO_Pin);
if (mode != LL_GPIO_MODE_ANALOG) {
// skip channel
nconv--; chan++;
continue;
}
} else {
// Internal channels are inhibited until explicitely enabled
_adc_inhibit_mask |= (1 << input_idx);
}
// channel is already used, probably a secondary input
// using the same ADC channel
if (channel_mask & mask) {
nconv--; chan++;
continue;
}
// update mask for used channels
channel_mask |= mask;
// update mask for valid inputs
_adc_input_mask |= (1 << input_idx);
LL_ADC_REG_SetSequencerRanks(adc->ADCx, _rank_lookup[rank],
input->ADC_Channel);
LL_ADC_SetChannelSamplingTime(adc->ADCx, input->ADC_Channel,
adc->sample_time);
nconv--;
rank++;
chan++;
}
return rank;
}
static bool adc_init_dma_stream(ADC_TypeDef* adc, DMA_TypeDef* DMAx,
uint32_t stream, uint32_t channel,
uint16_t* dest, uint8_t nconv)
{
stm32_dma_enable_clock(DMAx);
// Disable DMA before continuing (see ref. manual "Stream configuration procedure")
if (!adc_disable_dma(DMAx, stream))
return false;
// Clear Interrupt flags
adc_dma_clear_flags(DMAx, stream);
// setup DMA request
LL_DMA_ConfigAddresses(DMAx, stream, CONVERT_PTR_UINT(&adc->DR),
CONVERT_PTR_UINT(dest),
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetDataLength(DMAx, stream, nconv);
LL_DMA_SetChannelSelection(DMAx, stream, channel);
// Very high priority, 1 byte transfers, increment memory
LL_DMA_ConfigTransfer(DMAx, stream,
LL_DMA_PRIORITY_VERYHIGH | LL_DMA_MDATAALIGN_HALFWORD |
LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MEMORY_INCREMENT |
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
// disable direct mode, half full FIFO
LL_DMA_EnableFifoMode(DMAx, stream);
LL_DMA_SetFIFOThreshold(DMAx, stream, LL_DMA_FIFOTHRESHOLD_1_2);
return true;
}
bool stm32_hal_adc_init(const stm32_adc_t* ADCs, uint8_t n_ADC,
const stm32_adc_input_t* inputs,
const stm32_adc_gpio_t* ADC_GPIOs, uint8_t n_GPIO)
{
_adc_input_mask = 0;
_adc_inhibit_mask = 0;
_adc_oversampling_disabled = 0;
adc_init_pins(ADC_GPIOs, n_GPIO);
// Init common to all ADCs
LL_ADC_CommonInitTypeDef commonInit;
LL_ADC_CommonStructInit(&commonInit);
commonInit.CommonClock = LL_ADC_CLOCK_SYNC_PCLK_DIV2;
LL_ADC_CommonInit(ADC_COMMON, &commonInit);
_adc_input_mask = 0;
// uint16_t* dma_buffer = _adc_dma_buffer;
const stm32_adc_t* adc = ADCs;
while (n_ADC > 0) {
uint8_t nconv = adc->n_channels;
if (nconv > 0) {
// enable periph clock
adc_enable_clock(adc->ADCx);
// configure each channel
const uint8_t* chan = adc->channels;
nconv = adc_init_channels(adc, inputs, chan, nconv);
adc_setup_scan_mode(adc->ADCx, nconv);
if (nconv > 1) {
if (adc->DMAx) {
uint16_t* dma_buffer = _adc_dma_buffer + adc->offset;
if (!adc_init_dma_stream(adc->ADCx, adc->DMAx, adc->DMA_Stream,
adc->DMA_Channel, dma_buffer, nconv))
return false;
NVIC_SetPriority(adc->DMA_Stream_IRQn, ADC_IRQ_PRIO);
NVIC_EnableIRQ(adc->DMA_Stream_IRQn);
} else {
// multiple channels on the same ADC
// without a DMA is NOT supported
return false;
}
} else {
// single conversion
NVIC_SetPriority(ADC_IRQn, ADC_IRQ_PRIO);
NVIC_EnableIRQ(ADC_IRQn);
}
}
// move to next ADC definition
adc++; n_ADC--;
}
return true;
}
#define DMA_Stream0_IT_MASK (uint32_t)(DMA_LISR_FEIF0 | DMA_LISR_DMEIF0 | \
DMA_LISR_TEIF0 | DMA_LISR_HTIF0 | \
DMA_LISR_TCIF0)
#define DMA_Stream4_IT_MASK (uint32_t)(DMA_HISR_FEIF4 | DMA_HISR_DMEIF4 | \
DMA_HISR_TEIF4 | DMA_HISR_HTIF4 | \
DMA_HISR_TCIF4)
static void adc_dma_clear_flags(DMA_TypeDef* DMAx, uint32_t stream)
{
// no other choice, sorry for that...
if (stream == LL_DMA_STREAM_4) {
/* Reset interrupt pending bits for DMA2 Stream4 */
WRITE_REG(DMAx->HIFCR, DMA_Stream4_IT_MASK);
} else if (stream == LL_DMA_STREAM_0) {
/* Reset interrupt pending bits for DMA2 Stream0 */
WRITE_REG(DMAx->LIFCR, DMA_Stream0_IT_MASK);
}
}
static inline DMA_Stream_TypeDef* _dma_get_stream(DMA_TypeDef *DMAx, uint32_t Stream)
{
return ((DMA_Stream_TypeDef*)((uint32_t)((uint32_t)DMAx + STREAM_OFFSET_TAB[Stream])));
}
static void adc_start_dma_conversion(ADC_TypeDef* ADCx, DMA_TypeDef* DMAx, uint32_t stream)
{
// Clear Interrupt flags
adc_dma_clear_flags(DMAx, stream);
// Clear ADC status register & disable ADC IRQ
CLEAR_BIT(ADCx->SR, ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR);
CLEAR_BIT(ADCx->CR1, ADC_CR1_EOCIE);
// Enable DMA
auto dma_stream = _dma_get_stream(DMAx, stream);
SET_BIT(dma_stream->CR, DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE);
SET_BIT(dma_stream->CR, DMA_SxCR_EN);
// Trigger ADC start
SET_BIT(ADCx->CR2, ADC_CR2_SWSTART);
}
static bool adc_disable_dma(DMA_TypeDef* DMAx, uint32_t stream)
{
LL_DMA_DisableStream(DMAx, stream);
// wait until DMA EN bit gets cleared by hardware
uint16_t timeout = 1000;
while (LL_DMA_IsEnabledStream(DMAx, stream)) {
if (--timeout == 0) {
// Timeout. Failed to disable DMA
return false;
}
}
return true;
}
static void adc_start_normal_conversion(ADC_TypeDef* ADCx)
{
// clear status flags
CLEAR_BIT(ADCx->SR, ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR);
// enble ADC IRQ
SET_BIT(ADCx->CR1, ADC_CR1_EOCIE);
// and start!
SET_BIT(ADCx->CR2, ADC_CR2_SWSTART);
}
static void adc_start_read(const stm32_adc_t* ADCs, uint8_t n_ADC)
{
// Start all ADCs in parallel
uint8_t adc_mask = 1;
_adc_started_mask = 0;
const stm32_adc_t* adc = ADCs;
while (n_ADC > 0) {
// if the ADC has no active channels,
// it has not been enabled at all
auto ADCx = adc->ADCx;
if (!LL_ADC_IsEnabled(ADCx)) {
adc++; n_ADC--; adc_mask <<= 1;
continue;
}
// more than one channel enabled on this ADC
if (LL_ADC_GetSequencersScanMode(ADCx) == LL_ADC_SEQ_SCAN_ENABLE) {
// Disable DMA before continuing (see ref. manual "Stream configuration procedure")
auto DMAx = adc->DMAx;
auto stream = adc->DMA_Stream;
if (DMAx && adc_disable_dma(DMAx, stream)) {
_adc_started_mask |= adc_mask;
adc_start_dma_conversion(ADCx, DMAx, stream);
}
} else {
// only one channel
_adc_started_mask |= adc_mask;
adc_start_normal_conversion(ADCx);
}
// move to next ADC
adc++; n_ADC--; adc_mask <<= 1;
}
}
bool stm32_hal_adc_start_read(const stm32_adc_t* ADCs, uint8_t n_ADC,
const stm32_adc_input_t* inputs, uint8_t n_inputs)
{
_adc_completed = 0;
_adc_run = 0;
_adc_ADCs = ADCs;
_adc_n_ADC = n_ADC;
_adc_inputs = inputs;
_adc_n_inputs = n_inputs;
memclear(_adc_oversampling, sizeof(_adc_oversampling));
adc_start_read(_adc_ADCs, _adc_n_ADC);
return true;
}
static void copy_adc_values(uint16_t* src, const stm32_adc_t* adc,
const stm32_adc_input_t* inputs)
{
for (uint8_t i=0; i < adc->n_channels; i++) {
uint8_t channel = adc->channels[i];
// if input disabled, skip
if (~_adc_input_mask & (1 << channel)) {
continue;
}
// skip sampled but inhibited channels
if (_adc_inhibit_mask & (1 << channel)) {
src++;
continue;
}
// TODO: move inversion to the generic ADC driver?
if (inputs[channel].inverted)
_adc_oversampling[channel] += ADC_INVERT_VALUE(*src);
else
_adc_oversampling[channel] += *src;
#if defined(JITTER_MEASURE)
if (JITTER_MEASURE_ACTIVE()) {
rawJitter[channel].measure(dst[channel]);
}
#endif
src++;
}
}
void stm32_hal_adc_wait_completion(const stm32_adc_t* ADCs, uint8_t n_ADC,
const stm32_adc_input_t* inputs, uint8_t n_inputs)
{
(void)ADCs;
(void)n_ADC;
(void)inputs;
(void)n_inputs;
auto timeout = timersGetUsTick();
while(!_adc_completed) {
// busy wait
if ((uint32_t)(timersGetUsTick() - timeout) >= SAMPLING_TIMEOUT_US) {
TRACE("ADC timeout");
_adc_started_mask = 0;
return;
}
}
}
void stm32_hal_adc_disable_oversampling()
{
_adc_oversampling_disabled = 1;
}
static void _adc_mark_completed(const stm32_adc_t* adc)
{
uint8_t adc_idx = (adc - _adc_ADCs);
_adc_started_mask &= ~(1 << adc_idx);
}
static void _adc_chain_conversions(const stm32_adc_t* adc)
{
_adc_mark_completed(adc);
if (_adc_started_mask != 0) return;
if (!_adc_oversampling_disabled && (++_adc_run < OVERSAMPLING)) {
adc_start_read(_adc_ADCs, _adc_n_ADC);
return;
}
auto adcValues = getAnalogValues();
for (uint8_t i = 0; i < _adc_n_inputs; i++) {
if (~_adc_input_mask & (1 << i)) continue;
if (_adc_inhibit_mask & (1 << i)) continue;
adcValues[i] = _adc_oversampling[i] / OVERSAMPLING;
}
// we're done!
_adc_completed = 1;
}
void stm32_hal_adc_dma_isr(const stm32_adc_t* adc)
{
// Disable IRQ
adc_dma_clear_flags(adc->DMAx, adc->DMA_Stream);
uint16_t* dma_buffer = _adc_dma_buffer + adc->offset;
copy_adc_values(dma_buffer, adc, _adc_inputs);
_adc_chain_conversions(adc);
}
void stm32_hal_adc_isr(const stm32_adc_t* adc)
{
// check if this ADC triggered the IRQ
auto ADCx = adc->ADCx;
if (!LL_ADC_IsActiveFlag_EOCS(ADCx) || !LL_ADC_IsEnabledIT_EOCS(ADCx))
return;
// Disable end-of-conversion IRQ
CLEAR_BIT(ADCx->CR1, ADC_CR1_EOCIE);
uint16_t* dma_buffer = _adc_dma_buffer + adc->offset;
*dma_buffer = adc->ADCx->DR;
copy_adc_values(dma_buffer, adc, _adc_inputs);
_adc_chain_conversions(adc);
}