STM Урок 187. LL. STM32F1. ADC. Regular Continuous. Interrupt



Продолжаем работать с АЦП (ADC) контроллера STM32F1 с использованием библиотеки LL. Также работать мы пока будем с регулярным каналом и отслеживать окончание процесса преобразования мы также будем при помощи механизма прерываний от АЦП, только в данном уроке мы попробуем уже поработать не с однократным преобразованием, которое нужно каждый раз в каждой итерации заново запускать, а применим автоматический механизм запуска.

Наш АЦП такой режим поддерживает, причём следующее преобразование запускается не сразу после преобразования, а через определённый интервал, который можно настроить посредством определённой битовой маски, причём мы знаем какой. В обработчик прерывания мы попадаем также не после преобразования, а уже после истечения данного интервала, что даёт более точный результат измерения.

Схема урока со времён двух предыдущих уроков не изменилась

 

 

Проект мы, как обычно, сделаем из проекта прошлого урока с именем LL_ADC_REG_ONCE_INT и назовём его LL_ADC_REG_CONT_INT.

Откроем наш проект в Cube MX и в свойствах ADC1 включим кольцевой (автоматический) режим преобразования сигнала

 

 

Также для того, чтобы преобразования запускались не слишком часто, установим наибольший интервал между выборками

 

 

Теперь мы можем посчитать, с какой частотой у нас будут происходить измерения сигнала на ножке, если учесть то, что АЦП наш тактируется частотой 12 МГц. Для этого мы посчитаем общее количество циклов, что составит 12,5 циклов преобразования плюс 239,5 циклов между выборками и получим всего 252 цикла. Затем 12 мегагерц разделим на количество циклов (252) и получим частоту около 47,5 кГц. Это, в принципе, не так уж и часто, но если учесть то, что отображать мы будем преобразованное значение всего 10 раз в секунду, то не так уж и редко. Тем не менее, учитывая то, что основная тактовая частота у нас 72 МГц, то прерывание основной программы у нас будет происходить не очень часто, да и ненадолго, так как мы будем в обработчике данного прерывания всего лишь забирать сырое измеренное значение из регистра и записывать его в переменную.

Сгенерируем проект и откроем его в Keil, настроим автоперезагрузку после прошивки, отключим оптимизацию, подключим к дереву проекта файлы lcd.c и i2c_user.c, откроем main.c и посмотрим, что у нас изменилось в инициализации АЦП в результате включения режима Continuous. Для этого зайдём в тело функции MX_ADC1_Init и посмотрим изменения.

Первые изменения мы видим вот в этой строке

 

ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS;

 

В результате этого в функции LL_ADC_REG_Init в регистре CR2 установится бит CONT.

 

 

Вернёмся в функцию LL_ADC_REG_Init и увидим там также далее изменение второго аргумента в вызове вот этой функции

 

LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_239CYCLES_5);

 

Засчёт этого в регистре SMPR2 в битовой маске SMP1, соответствующей нашему каналу, установятся все три бита, так как мы установили максимальный интервал между выборками.

Если мы прошагаем данный вызов в отладке, то сможем это увидеть

 

 

Больше в инициализации у нас никаких изменений не произошло.

Теперь начнём исправлять код, так как у нас теперь творится непонятно что, ибо мы запускаем преобразования каждые 100 милисекунд, а у нас ещё настроен их автоматический запуск. Хотя если собрать проект и прошить, у нас все равно всё будет работать, так как мы просто устанавливаем постоянно уже установленный бит.

Для начала удалим локальную переменную для хранения сырого значения из функции main()

 

__IO uint16_t ADC_Data;

 

И объявим теперь данную переменную в глобальной секции

 

 

 

А пользовательский флаг удалим, он нам теперь не нужен, мы ничего не будем отслеживать

 

__IO uint8_t fl_adc;

 

В обработчике прерываний мы теперь не устанавливаем никакой флаг, а считываем значение из регистра и записываем его в нашу переменную, поэтому сначала удалим установку флага

 

fl_adc = 1;

 

А затем считаем преобразованное значение

 

 

Вернёмся в main() и исправим надпись

 

LCD_String("Regular Continuous");

 

Запустим преобразование до бесконечного цикла, так как запускать нам его нужно теперь только один раз, и подождём немного, чтобы нам до него точно уже иметь измеренное значение

 

 

В бесконечном цикле мы должны теперь удалить запуск преобразования, отслеживание пользовательского флага, а также считывание преобразованных данных из регистра

 

LL_ADC_REG_StartConversionSWStart(ADC1);

while (!fl_adc) {}

fl_adc = 0;

ADC_Data = LL_ADC_REG_ReadConversionData12(ADC1);

 

Вот и все изменения в коде. Теперь преобразования у нас будут происходить автоматически и в обработчике прерываний значение из регистра с преобразованным значением будет записываться в переменную, а в бесконечном цикле мы теперь только пересчитываем считанное значение в вольты и отображаем его на дисплее. Коллизии с одновременным обращением к глобальной переменной исключены, так как запись в неё в обработчике прерываний происходит, когда основная программа остановлена.

Соберём код, прошьём контроллер и увидим, что у нас всё по-прежнему отлично работает

 

 

Итак, на данном занятии мы освоили механизм автоматического запуска преобразований АЦП, что позволило нам ещё более разгрузить основной код.

Всем спасибо за внимание!

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

 

Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

Дисплей LCD 16×2

Переходник I2C to LCD можно приобрести здесьI2C to LCD1602 2004

Логический анализатор 16 каналов можно приобрести здесь

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM LL. STM32F1. ADC. Regular Continuous. Interrupt

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*