Files
Maison/OpenSky/arch/stm32f1/hal_adc.c
2026-04-21 12:19:15 +02:00

186 lines
5.6 KiB
C

/*
Copyright 2017 fishpepper <AT> gmail.com
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http:// www.gnu.org/licenses/>.
author: fishpepper <AT> gmail.com
*/
#include "hal_adc.h"
#include "debug.h"
#include "wdt.h"
#include "config.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_adc.h"
// adc results
volatile uint16_t hal_adc_data[2];
void hal_adc_init(void) {
hal_adc_init_rcc();
hal_adc_init_gpio();
hal_adc_init_mode();
hal_adc_init_dma();
}
static void hal_adc_init_rcc(void) {
// ADC CLOCK = 24 / 4 = 6MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
// enable ADC clock
RCC_APBxPeriphClockCmd(ADC_CLK_RCC, ADC_CLK, ENABLE);
// enable dma clock
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// periph clock enable for port
RCC_APBxPeriphClockCmd(ADC_GPIO_CLK_RCC, ADC_GPIO_CLK, ENABLE);
}
static void hal_adc_init_gpio(void) {
GPIO_InitTypeDef gpio_init;
// set up analog inputs
gpio_init.GPIO_Pin = ADC_IN1_PIN | ADC_IN2_PIN;
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_GPIO, &gpio_init);
}
static void hal_adc_init_mode(void) {
ADC_InitTypeDef adc_init;
// ADC configuration
adc_init.ADC_Mode = ADC_Mode_Independent;
// convert multiple channels
adc_init.ADC_ScanConvMode = ENABLE;
// select continuous conversion mode
adc_init.ADC_ContinuousConvMode = ENABLE;
// select no external triggering
adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// right 12-bit data alignment in ADC data register
adc_init.ADC_DataAlign = ADC_DataAlign_Right;
// 2 channels conversion
adc_init.ADC_NbrOfChannel = 2;
// load structure values to control and status registers
ADC_Init(ADC, &adc_init);
// configure each channel
ADC_RegularChannelConfig(ADC, ADC_Channel_1, 1, ADC_SampleTime_41Cycles5);
ADC_RegularChannelConfig(ADC, ADC_Channel_2, 2, ADC_SampleTime_41Cycles5);
// Enable ADC
ADC_Cmd(ADC, ENABLE);
// enable DMA for ADC
ADC_DMACmd(ADC, ENABLE);
// Enable ADC1 reset calibration register
ADC_ResetCalibration(ADC);
// Check the end of ADC1 reset calibration register
while (ADC_GetResetCalibrationStatus(ADC)) {}
// Start ADC calibration
ADC_StartCalibration(ADC);
// Check the end of ADC1 calibration
while (ADC_GetCalibrationStatus(ADC)) {}
}
static void hal_adc_init_dma(void) {
DMA_InitTypeDef dma_init;
// reset DMA1 channe1 to default values
DMA_DeInit(ADC_DMA_CHANNEL);
// set up dma to convert 2 adc channels to two mem locations:
// channel will be used for memory to memory transfer
dma_init.DMA_M2M = DMA_M2M_Disable;
// setting normal mode (non circular)
dma_init.DMA_Mode = DMA_Mode_Circular;
// medium priority
dma_init.DMA_Priority = DMA_Priority_High;
// source and destination 16bit
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
// automatic memory destination increment enable.
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
// source address increment disable
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// Location assigned to peripheral register will be source
dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
// chunk of data to be transfered
dma_init.DMA_BufferSize = 2;
// source and destination start addresses
dma_init.DMA_PeripheralBaseAddr = (uint32_t)&ADC->DR;
dma_init.DMA_MemoryBaseAddr = (uint32_t)hal_adc_data;
// send values to DMA registers
DMA_Init(ADC_DMA_CHANNEL, &dma_init);
// Enable the DMA1 - Channel1
DMA_Cmd(ADC_DMA_CHANNEL, ENABLE);
// start conversion:
hal_adc_dma_arm();
#if ADC_DO_TEST
// TEST ADC
while (1) {
debug_putc('A');
wdt_reset();
if (ADC_GetFlagStatus(ADC, ADC_FLAG_EOC) == SET) {
uint16_t res = ADC_GetConversionValue(ADC);
debug("ADC = "); debug_put_uint16(res); debug_put_newline(); debug_flush();
ADC_ClearFlag(ADC, ADC_FLAG_EOC);
// start next ADC Software Conversion
ADC_SoftwareStartConvCmd(ADC, ENABLE);
}
}
#endif // ADC_DO_TEST
}
static void hal_adc_dma_arm(void) {
ADC_SoftwareStartConvCmd(ADC, ENABLE);
}
void hal_adc_process(void) {
// adc dma finished?
if (DMA_GetITStatus(ADC_DMA_TC_FLAG)) {
// fine, arm DMA again:
hal_adc_dma_arm();
} else {
// oops this should not happen
debug_putc('D');
// cancel and re arm dma ???
}
}
uint8_t hal_adc_get_scaled(uint8_t ch) {
if (ch < 2) {
// 12 bit adc -> scale to 8 bit -> shift by 4
return hal_adc_data[ch]>>4;
} else {
debug("hal_adc: channel index out of bounds ");
debug_put_uint8(ch);
debug("allowed 0,1)\n");
debug_flush();
return 0;
}
}