Страница на этапе разработки
В данном уроке мы с Вами изучим работу АЦП (ADC) микроконтроллера STM32F103, но сначала рассмотрим возможности нашего АЦП:
Сами каналы АЦП микроконтроллеров STM32 делятся на две группы:
Результаты измерений регулярных каналов хранятся в одном регистре и требуют сохранения результатов в памяти микроконтроллера, в то время как инжектированные каналы имеют собственные регистры для хранения результата.
Кроме того АЦП можно настроить на работу в качестве аналогового сторожа (watchdog), то есть задаются верхний и нижний пороги входного сигнала, АЦП отслеживает уровень сигнала и, если сигнал выходит за указанные пределы, генерируется прерывание.
1. Single-channel (Одноканальный) АЦП выполняет одно преобразование одного канала, сохраняет полученное значение в выходном регистре и останавливается.
2. Single continuous (Одноканальный длительный) Этот режим аналогичен первому, но АЦП не останавливается, а продолжает работу с выбранным каналом. При этом результат постоянно перезаписывается в выходном регистре.
3. Scan (Многоканальный) В этом режиме возможно сконфигурировать АЦП для выполнения последовательных преобразований нескольких каналов в заданной последовательности. Преобразовании также настраивается отдельно для каждого канала. После обработки указанного числа каналов АЦП останавливается.
4. Scan continuous (Многоканальный длительный) То же самое что и Scan, только АЦП не останавливается после опроса всех каналов, а снова начинает обработку каналов. При этом все результаты сохраняются в один регистр и надо вовремя забирать данные, пока они не будут затертые данным преобразования следующего канала.
5. Discontinuous (Прерывистый) Сканируются не все каналы за раз, а только те, которые заранее установленные, при следующем сканировании, сканируется следующая группа каналов и так далее.
Входы питания:
Vref+ — вход опорного напряжения
Vdda — вход аналогового питания
Vssa — вход общего потенциала аналогового питания
Для начала изучим работу 4 инжекторных каналов (в микроконтроллере STM32F103 их 4 штуки). В данном случаи результат каждого измерения будет помещен в свой регистр и нам не нужно «переживать», что результаты измерений будут затертые чужим каналом.
Но вначале нам необходимо рассмотреть некоторые особенности работы АЦП:
1. Калибровка АЦП — так как внутренние конденсаторы МК имеют не однородность, для уменьшения погрешности преобразования проводят калибровку. Во время калибровки вычисляется цифровое значение АЦП для каждого конденсатора в виде корректирующего кода.
Перед началом калибровки АЦП должно находиться в отключенном состоянии, то есть бит ADON должен быть равен нулю. Запуск калибровки производиться установкой бита CAL в регистре ADC_CR2. Как только калибровка закончится, бит CAL сбросится аппаратно, после чего можно выполнять преобразования. Рекомендуется калибровать АЦП при подачи питания.
2. Время преобразования — для каждого канала можно выставить свое время преобразования, которое возможно выбрать из диапазона 1,5-239,5 дискретно (8 значений). Выставляется время в регистрах ADC_SMPR1 и ADC_SMPR2.
Полное время преобразования Т=(программное время выборки + 12,5 циклов)/частота преобразования.
Например частота ADCCLK задана равной 14МГц, а время выборки 1,5 цикла, тогда Т=(1,5+12,5)/14= 1мкс.
Напишем программный код:
int main(void) {
uint16_t ADC_value0, ADC_value1;
while (1) {
HAL_ADCEx_InjectedStart(&hadc1);
HAL_Delay(100);
ADC_value0 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_1);
ADC_value1 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_2);
HAL_Delay(100);
}
Теперь когда у нас появились данные, прежде чем продолжить рассмотрим вначале самый простой фильтр, который можно применить для фильтрации данных (конечно его параметры зависят от скорости изменения аналоговых значений), в качестве примера рассмотрим классический RC фильтр:
*картинка*
*объяснения*
Теперь рассмотрим зависимость скорости обработки данных от выходного сопротивления источника аналогового сигнала:
Если мы используем резистивный делитель, то выходное сопротивление рассчитывается как параллельное сопротивление этих резисторов.
Приблизне емпіричне правило:
Rвих⋅C≪tacqR_{\text{вих}} \cdot C \ll t_{\text{acq}}Rвих⋅C≪tacq
Де:
RвихR_{\text{вих}}Rвих — вихідний опір (наприклад, R1⋅R2R1+R2\frac{R_1 \cdot R_2}{R_1 + R_2}R1+R2R1⋅R2),
CCC — вихідний конденсатор,
tacqt_{\text{acq}}tacq — час вибірки АЦП (acquisition time), наприклад, 1–2 мкс для STM32.
R1=100 kΩR_1 = 100\,\text{k}\OmegaR1=100kΩ, R2=100 kΩR_2 = 100\,\text{k}\OmegaR2=100kΩ → Rвих=50 kΩR_{\text{вих}} = 50\,\text{k}\OmegaRвих=50kΩ
Щоб RC < 1 мкс:
C≫1⋅10−650⋅103=20 нФC \gg \frac{1 \cdot 10^{-6}}{50 \cdot 10^3} = 20\,\text{нФ}C≫50⋅1031⋅10−6=20нФ➡️ Рекомендується 100 нФ або більше.
Теперь проделаем все тоже самое но для режима с DMA. В данном случаи мы переложим все действия на внутреннюю периферию, тем самым освободим ядро от дополнительных вычислений и действий. Все что нам нужно будет это забрать наши значения после всех преобразований.
DMA работает только с ADC1, в STM32F103C8T6.
Для настройки DMA нам нужно будет сделать следующее:
Вкладка Analog: включите ADC1_IN0, ADC1_IN1, ADC1_IN2
Scan Conversion Mode: Enabled
Continuous Conversion Mode: Enabled
DMA Continuous Requests: Enabled
Nbr of Conversion: 3
Rank 1: Channel 0 (PA0)
Rank 2: Channel 1 (PA1)
Rank 3: Channel 2 (PA2)
DMA1 Channel1
Mode: Circular
Data width: Half Word
Direction: Peripheral to Memory
Программный код:
#define ADC_CHANNELS 3
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
uint16_t adc_values[ADC_CHANNELS];
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
if (hadc->Instance == ADC1) {
// Здесь можно обработать новые значения
// adc_values[0] -> PA0
// adc_values[1] -> PA1
// adc_values[2] -> PA2
}
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_values, ADC_CHANNELS);
while (1) {
// adc_values[] автоматически обновляется
}
}