АЦП STM32W108

Микроконтроллеры STM32W108 имеют один встроенный 12-разрядный Σ-Δ АЦП с дифференциальным входом и встроенный источник опорного напряжения (ИОН) с номинальным напряжением 1,2 В.

У микроконтроллеров с 48 выводами имеется вывод VREF (PB0) для подключения внешнего ИОН. Этот же вывод может использоваться, как выход внутреннего ИОН. У микроконтроллеров с 40 выводами такие возможности отсутствуют.

Напряжение внешнего ИОН должно лежать в диапазоне от 1,1 В до 1,3 В.

Проблема с ИОН заключается в том, что из документации не понятно, как вывести напряжение наружу и что сделать, чтобы АЦП работал с внешним ИОНом. В документации говорится, что надо вызывать какие-то системные функции, о которых можно узнать из документации на библиотеку. Но в документации на библиотеку никакой информации на этот счет нет. Библиотеки для программирования STM32W108 вообще очень плохо документированы.

АЦП может тактироваться двумя частотам: 1 МГц и 6 МГц. При этом можно выбрать количество периодов тактирования, за которое должно быть выполнено преобразование.

Все возможные режимы дискретизации АЦП:

Период дискретизации, мкс Частота дискретизации, кГц
Частота тактирования АЦП, МГц 1 6 1 6
Количество периодов для преобразования 32 32,0 5,333 31,2500000000 187,500000000
64 64,0 10,667 15,6250000000 93,750000000
128 128,0 21,333 7,8125000000 46,875000000
256 256,0 42,667 3,9062500000 23,437500000
512 512,0 85,333 1,9531250000 11,718750000
1024 1024,0 170,667 0,9765625000 5,859375000
2048 2048,0 341,333 0,4882812500 2,929687500
4096 4096,0 682,667 0,2441406250 1,464843750

При этом надо учитывать, что, чем меньше время преобразования, тем меньше разрешающая способность АЦП.

Единственный способ получения данных после преобразования — через DMA.

Буфер для DMA определяется двумя регистрами:

  • ADC_DMABEG — начальный адрес буфера, указатель на 16-битную знаковую переменную или массив из этих переменных;
  • ADC_DMASIZE — размер буфера, количество элементов в вышеназванном массиве или половина длины буффера в байтах.

Определить буфер можно так:

#define ADC_DMA_BUFFER_SIZE (10)
int16s ADC_DMA_buffer[ADC_DMA_BUFFER_SIZE];
ADC_DMABEG = (int32u)ADC_DMA_buffer;
ADC_DMASIZE = ADC_DMA_BUFFER_SIZE;

или так (для представления буффера одной переменной):

int16s ADC_DMA_buffer;
ADC_DMABEG = (int32u)&ADC_DMA_buffer;
ADC_DMASIZE = 1;

О ходе преобразования можно судить по флагам в регистре INT_ADCFLAG. Какое-либо событие устанавливает бит соответствующего флага в логическую 1. Флаги сбрасываются программно. Чтобы сбросить флаг в регистре INT_ADCFLAG, надо записать в соответствующий бит логическую 1. После этого из бита читается логический 0.

Пример работы с АЦП:

#include PLATFORM_HEADER
#include <hal/hal.h>
#include <simplemac/include/phy-library.h>

// Выделить буфер в который АЦП будет складывать данные
#define ADC_DMA_BUFFER_SIZE (1)
int16s ADC_DMA_buffer[ADC_DMA_BUFFER_SIZE];

int main()
{
  // Инициализировать микроконтроллер
  halCommonStartXtal();
  halInternalSetRegTrim(FALSE);
  halCommonCalibratePads();
  halCommonSwitchToXtal();
  halInternalCalibrateFastRc();
  halInternalStartSystemTimer();
  GPIO_DBGCFG &= ~GPIO_EXTREGEN; // Освободить PA7 от REG_EN

  // Настроить выводы МК к которым подключаются аналоговые цепи
  halGpioConfig(PORTB_PIN(7), GPIOCFG_ANALOG);
  halGpioConfig(PORTC_PIN(1), GPIOCFG_ANALOG);

  // Объяснить АЦП, где буфер и какого он размера
  ADC_DMABEG = (int32u)ADC_DMA_buffer;
  ADC_DMASIZE = ADC_DMA_BUFFER_SIZE;
  ADC_DMACFG = 0x3;

  // Настроить АЦП:
  //   частота тактирования: 6 МГц,
  //   частота дискретизации: 5,859375 кГц (1024 тактов на преобразование),
  //   входной канал: дифференциальный, ADC3 - положит., ADC2 - отриц.
  ADC_CFG = 0;
  ADC_CFG =     (ADC_MUX_ADC3           << ADC_MUXP_BIT)
              | (ADC_MUX_ADC2           << ADC_MUXN_BIT)
              | (ADC_SAMPLE_CLOCKS_1024 << ADC_PERIOD_BIT)
              | ADC_ENABLE; // запустить преобразование

  // Непрерывное преобразование и обработка дынных
  while(1)
  {
    double voltage;
    
    INT_ADCFLAG = 0xFFFF; // Сбросить флаги АЦп
    while (!(INT_ADCFLAG & INT_ADCULDFULL)); // Ждать завершения преобразования
    
    // Измеренное напряжение на входе АЦП
    voltage = 1.2 * ADC_DMA_buffer[0] / 0x3FFF;
    
    // Здесь должен быть код обработки данных.
    // Время обработки должно быть короче периода дискретизации,
    // чтобы не пропустить новые данные.
  }

  return 0;
}

Программирование STM32W108 дело не простое. Мешает плохая документация библиотек и микроконтроллера. Подробнее узнать о том, какую среду программирования выбрать, как её настроить и начать программировать можно узнать из другой моей статьи.