Потребовалось бюпилом измерить постоянное напряжение ±5 вольт. Спаял схему на одном операционнике напряжение сместилось, но не так как задумывалось. Поиск в сети дал интересный документ от Ti. https://images.shoutwiki.com/mindworks/8/81/SLOA097.pdf
Полный расчёт резисторов для схемы смещения уровня и коэффициента усиления на одном операционнике. Результат порадовал - при входном напряжении ±5 вольт выходное 0-3.3 вольт. Ну и лайф хак - операционнику нужно ±питание. На блюпиле запитал от +5, напряжение смещения от 3.3, а -5 от преобразователя +5 > -5 такого https://aliexpress.ru/item/1005004109272585.html
В блюпил загрузил программу. Усредняет по 50 токам за 20мс двумя чередующимися буферами и выдаёт в компорт. При подключении к входу батарейки показания скачут на единичку в последнем знаке.
Спойлер
#include <Arduino.h>
#include <HardwareTimer.h>
#define ADC_PIN PA1
#define LED_PIN PC13
#define SAMPLE_RATE 50000
#define BUFFER_SIZE 2000
// Буфер для DMA должен быть выровнен
__attribute__((aligned(4))) volatile uint16_t adcBuffer[BUFFER_SIZE];
volatile bool dataReady = false;
volatile uint16_t* currentHalf = nullptr;
void processData(volatile uint16_t* data, uint16_t length) {
uint32_t sum = 0;
for(uint16_t i = 0; i < length; i++) {
sum += data[i];
}
Serial.printf("%.3f %d\n", millis()/1000.0f, sum/length);
}
extern "C" {
void DMA1_Channel1_IRQHandler(void) {
if (DMA1->ISR & DMA_ISR_HTIF1) {
DMA1->IFCR = DMA_IFCR_CHTIF1;
currentHalf = adcBuffer;
dataReady = true;
}
else if (DMA1->ISR & DMA_ISR_TCIF1) {
DMA1->IFCR = DMA_IFCR_CTCIF1;
currentHalf = adcBuffer + BUFFER_SIZE/2;
dataReady = true;
}
}
}
void setupADC_DMA() {
// 1. Включение тактирования
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// 2. Калибровка АЦП
ADC1->CR2 |= ADC_CR2_ADON;
delay(1);
ADC1->CR2 |= ADC_CR2_CAL;
while(ADC1->CR2 & ADC_CR2_CAL);
// 3. Настройка АЦП
ADC1->SQR1 = 0;
ADC1->SQR3 = 1;
ADC1->SMPR2 = 7 << 3;
ADC1->CR2 |= ADC_CR2_CONT | ADC_CR2_DMA;
// 4. Настройка DMA
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
DMA1_Channel1->CMAR = (uint32_t)adcBuffer;
DMA1_Channel1->CNDTR = BUFFER_SIZE;
DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 |
DMA_CCR_CIRC | DMA_CCR_HTIE | DMA_CCR_TCIE | DMA_CCR_EN;
// 5. Настройка прерываний
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
NVIC_SetPriority(DMA1_Channel1_IRQn, 0);
// 6. Запуск АЦП
ADC1->CR2 |= ADC_CR2_ADON;
delay(1);
ADC1->CR2 |= ADC_CR2_SWSTART;
}
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(ADC_PIN, INPUT_ANALOG);
Serial.begin(115200);
while(!Serial);
setupADC_DMA();
Serial.println("System initialized");
}
void loop() {
static uint32_t lastBlinkTime = 0;
// Мигание светодиодом 1 Гц
if(millis() - lastBlinkTime >= 1000) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
lastBlinkTime = millis();
}
// Обработка данных
if(dataReady && currentHalf) {
processData(currentHalf, BUFFER_SIZE/2);
dataReady = false;
}
}