STM Урок 185. LL. STM32F1. ADC. Regular Once. Часть 2

 

 

 

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

 

Теперь, думаю, можно приступить к проекту, который мы, чтобы не мучиться с настройкой дисплея (а дисплей мы возьмём символьный разрешением 4 x 20 символов), сделаем из проекта урока 151 с именем LL_I2C_LCD1602 и назовём его LL_ADC_REG_ONCE.

Откроем наш проект в Cube MX, включим 1-й канал ADC1

 

 

Если мы раскроем данный раздел, то увидим следующее

 

 

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

У нас включилась ножка PA1

 

 

Перейдём в раздел Clock Configuration и настроим делители и множители следующим образом

 

 

Включим для ADC1 использование библиотеки LL

 

 

Сгенерируем проект, откроем его в Keil, настроим программатор на автоперезагрузку, отключим оптимизацию и добавим к дереву проектов файлы lcd.c и i2c_user.c.

Так как дисплей у нас 4-строчный, а не двух, как в донорском проекте, а также мы кое-что ещё подправили там за время прошедшее со времён того проекта, то перейдём в файл lcd.c и внесём некоторые поправки.

Ниже функции DelayMicro добавим функцию задержки в наносекундах

 

 

В функции sendhalfbyte удалим вот эту задержку

 

DelayMicro(1);

 

А вместо неё применим вот такую

 

 

А вот эту задержку удалим совсем

 

DelayMicro(50);

 

В функции sendbyte добавим задержку вот здесь

 

 

В функции LCD_SetPos у нас и так 4 варианта, так что ничего добавлять не придётся.

Теперь внесём некоторые исправления в функцию инициализации LCD_ini, в которой для начала включим режим записи вот здесь

 

 

Вот эту задержку

 

sendhalfbyte(0x03);

DelayMicro(200);

 

меняем на 4500

 

 

Вот в этой строке

 

sendbyte(0x28,0);//режим 4 бит, 2 линии (для нашего большого дисплея это 4 линии, шрифт 5х8

 

пока выключаем дисплей

 

 

а включаем его здесь

 

 

Идём в main.c и в функции main() удалим вот эту локальную переменную

 

uint16_t i;

 

а добавим вот такие

 

 

В символьном массиве добавим немного элементов

char str01[15];

Удалим инициализацию переменной

 

i=0;

 

Удалим вывод строк на дисплей

 

LCD_String("String 1");

LCD_SetPos(5,1);

LCD_String("String 2");

LL_mDelay(2000);

LCD_SetPos(5,1);

LCD_String(" ");

 

Вместо них добавим вот такие строки

 

 

Из бесконечного цикла пока также удалим весь пользовательский код.

Перейдём к схеме. Дисплей к плате подключен так же, как и в уроке 151, только дисплей у нас немного другой и в качестве источника его питания мы будем использовать контакт 5v ST-Link. Также мы подключим в качестве делителя многооборотный подстроечный резистор на 10 килоом, расположив его на маленькой макетной плате. На крайние выводы данного резистора мы подключим питание 3,3 вольта с контроллера и общий провод, а с центрального контакта резистора будем изменяемое напряжение подавать на ножку PA1, настроенную как вход нашего АЦП

 

 

Подключим ST-Link к ПК, соберём наш код, прошьём контроллер и посмотрим на дисплей

 

 

Всё выводится, теперь мы уверены, что у нас работает хотя бы дисплей.

Поэтому вернёмся в проект и традиционно проанализируем код инициализации АЦП, автоматически сгенерированный с помощью Cube MX. В функции инициализации АЦП MX_ADC1_Init сначала мы видим запуск тактирования периферии ADC1, также запуск тактирования порта GPIOA и затем настройку ножки PA1.

Далее мы видим инициализацию полей некой структуры

 

 

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

 

 

Далее указатель на данную структуру передаётся в функцию библиотеки LL_ADC_Init, в которой данные настройки заносятся в бит ADC_CR1_SCAN регистра CR1 и в бит ADC_CR2_ALIGN регистра CR2

 

 

Возвращаемся в функцию MX_ADC1_Init, в которой затем инициализируется поле другой структуры

 

 

Указатель на данную структуру передаётся в другую библиотечную функцию LL_ADC_CommonInit, в которой проверяется вот это условие

 

 

Оно у нас не выполнится, так как у нас равно, поэтому мы попадаем в отрицательную ветвь конструкции, в которой выполнится макрос, который очистит все биты битовой маски ADC_CR1_DUALMOD. В третьем параметре всё равно нули, поэтому ничего больше не произойдёт

 

 

Опять возвращаемся в функцию , в которой теперь инициализируется следующая структура

 

 

Здесь происходят настройки программного старта АЦП (не от внешнего события), того, что мы не сканируем каналы по списку, производим одну конвертацию и не используем DMA.

Затем указатель на данную структуру передаётся в следующую библиотечную функцию LL_ADC_REG_Init, в которой также проверяется уже вот такое условие

 

 

Здесь также условие не выполняется и мы попадаем в ветку else конструкции, в которой очищается бит ADC_CR1_DISCEN и битовая маска ADC_CR1_DISCNUM регистра CR1, и ничего не устанавливается, так как в третьем параметре у нас везде нули

 

 

Затем в регистре CR2 сбрасываем все биты битовой маски ADC_CR2_EXTSEL, биты ADC_CR2_CONT и ADC_CR2_DMA. В третьем параметре также все нули, поэтому никакие биты не устанавливаются

 

 

Затем пармаетр SequencerLength нашей структуры передаётся в функцию LL_ADC_REG_SetSequencerLength, в которой сбрасываются все биты битового поля L регистра SQR1. В данном регистре ничего больше не устанавливается, так как в третьем параметре макроса также нули

 

 

Возвращаемся в функцию MX_ADC1_Init, в которой далее вызывается функция LL_ADC_REG_SetSequencerRanks, в которой в регистре (или в нескольких регистрах) SQRx, соответствующем выбранному каналу (или маске из нескольких каналов), очистится битовое поле SQx, также соответствующее данному каналу (или нескольким), а затем при помощи третьего параметра макроса установится количество преобразований для канала (или нескольких)

 

 

Затем идёт вызов функции LL_ADC_SetChannelSamplingTime, в которой в регистре SMPRx, соответствующем каналу, очищаются все биты битового поля SMPx, соответствующего данному каналу, а затем устанавливаются биты данного поля, соответствующие нужному интервалу между выборками (в нашем случае 1,5 цикла, что соответствует комбинации битов 000, то есть не устанавливается ничего)

 

 

Вот и вся наша функция инициализации АЦП.

Вернёмся в функцию main() и для начала проинициализируем нулём переменную для хранения сырых данных АЦП

 

 

Разрешим работу АЦП

 

 

Запустим калибровку АЦП, выждав перед этим необходимый таймаут с помощью нехитрой процедуры

 

 

С помощью вызова функции библиотеки LL LL_ADC_StartCalibration мы устанавливаем бит ADC_CR2_CAL в регистре CR2.

Дождёмся окончания калибровки

 

 

Здесь с помощью функции LL_ADC_IsCalibrationOnGoing мы дожидаемся сброса данного бита.

В бесконечном цикле запускаем преобразование сигнала

 

 

С помощью вызванной функции устанавливаются биты ADC_CR2_SWSTART и ADC_CR2_EXTTRIG регистра CR2.

Дождёмся окончания преобразования

 

 

Данная функция узнаёт состояние бита EOS регистра SR.

Дождавшись, очищаем данный флаг, как требует того техническая документация

 

 

С помощью специальной функции библиотеки считаем состояние регистра DR в переменную

 

 

С помощью нехитрого макроса превратим сырые считанные данные в миливольты, передав в макрос напряжение питание (я его измерил)

 

 

Отобразим полученное значение напряжение на дисплее и подождём 0,1 секунды

 

 

Вот и весь код.

Соберём его, прошьём контроллер и, вращая ось подстроечного резистора, посмотрим за изменениями напряжения

 

 

 

 

 

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

Итак, на данном уроке мы научились работать с АЦП контроллера STM32F1 с использованием библиотеки LL. Пусть мы пока использовали для этого самый простой режим работы АЦП, но зато на данном уроке мы очень много узнали по работе с аппаратной части модуля АЦП в нашем контроллере.

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

 

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

 

Исходный код

 

 

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

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

Дисплей LCD 16×2

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

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

 

 

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

 

STM LL. ADC. Regular Once

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

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

*