Stm32f103C8T6 ADC (АЦП - аналого-цифровой преобразователь) основы
И снова здравствуйте. Сегодня я хотел бы рассказать о не очень сложной, но достаточно полезной штуке - АЦП (Analog to Digital Converter) в stm32.
В тексте будут встречаться отсылки на мою первую статью, которую вы можете найти тут: https://pikabu.ru/story/dlya_nachinayushchikh_stm32f103c8t6_...
Итак, АЦП можно представить, как вольтметр, выдающий различные цифровые значения в зависимости от входного напряжения. В STM32 АЦП 12 разрядный, а входное напряжение на его входе колеблется от 0 до 3.3v (напряжение питания контроллера). Конструируя собственные поделки на камнях STM32 необходимо понимать, как работает АЦП.
На вход его подается 2 напряжения - напряжение питания и напряжение входное (котоорое мы хотим отобразить в числовом эквиваленте). Исходя из их сравнения АЦП и выдает результат. Из этого следует, что мы должны иметь очень стабильное напряжение питания самого АЦП, иначе значения будут сильно плавать. К счастью на нашей тестовой плате все уже реализовано и ничего изменить не удастся.
Давайте перейдем к наглядным примерам. Имеется 4 светодиода, каждый из которых подключен к выходам контроллера PB12(красный) PB13(желтый) PB14(зеленый) и PB15 (синий). Давайте подключим так же потенциометр, который будет работать делителем напряжения, а выходное значение будет обрабатываться нашим АЦП. Так же параллельно потенциометру подключим вольтметр, который будет помогать нам определить, что в данный момент на выходе делителя и соответственно на входе АЦП (в нашем случае это будет PA0).
В программной части реализации разделим возможный диапазон на 4 части, которые будут начинаться со следующих значений (комментарий - из значение в вольтах):
#define ADC_0V_VALUE 0 //0V
#define ADC_1V_VALUE 1024 //0.825V
#define ADC_2V_VALUE 2048 //1.65V
#define ADC_3V_VALUE 3072 //2.475V
Итак, программа написана следующим образом. В зависимости от значения на выходе потенциометра будет устанавливаться высокий уровень (загораться светодиод) на выходах PB12-PB15.
Теперь перейем к созданию проекта. В CubeMX выберем нашу плату STM32F103C8Tx. Активируем ADC для IN0. Отметим порты PB12-PB15 как выходные. Включим дебаг через Serial Wire и подключим внешний кварц HSE (опционально).
Вкладка Clock Configuration не имеет никаких изменений по сравнению с предыдущей статьей:
А теперь зайдем в настройки AЦП:
Вполне возможно, вы читали какие то гайды и как правило во всех них, при использовании куба с этой отладочной платой имеется аналогичный скрин, где видна возможность настройки разрядности АЦП (вроде этого):
Но реальность увы не иная, возможно, ранее разрядность АЦП и можно было настроить для этого камня, но теперь куб такой возможности не предоставляет и окно конфига выглядит так:
Возможно, это обусловлено тем, что камень f103 один из самых старых, и stm просто уделяет ему меньше внимания, но это не так критично, ведь по умолчанию, хоть разрядность ацп и не указана, но она выставлена как максимальное значение 12 бит. Все что нам необходимо, это включить Continious Conversion Mode, как на скрине выше. Зачем? Давайте останоовимся немного на режимах работы АЦП их 3:
1. Scan Conversion Mode (Многоканальный) - этот режим используется в том случае, если у нас будет несколько входных каналов преобразователя. Т.к. в данном случае мы используем только один вход A0, то этот режим оставляем отключенным. Если же вы используете несколько входных каналов, то АЦП можно будет сконфигурировать для их опроса в заданной последовательности.
2. Continious Conversion Mode (Циклический) - если этот режим отключен, то опрос канала/каналов произойдет лишь однажды, результат запишется в выходной регистр АЦП и данные всегда будут неизменны. В случае активацции, опрос каналов будет происходить непрерывно, и данные в выходном регистре будут обновляться.
3. Discontinuous Conversion Mode (Непоследовательный) - этот режим позволяет настроить АЦП сканирующее несколько каналов так, что бы опрос происходил не по всем каналам, а по заранее заданным группам каналов, причем если групп несколько, то за раз будет опрашиваться толко одна, затем следующая и т.д.
Следующий раздел ADC_Regular_ConversionMode предоставляет возможность сконфигурировать работу нескольких АЦП. Его мы рассмотрим в следующих статьях в более сложных примерах, а сейчас просто оставим все заданные значения по умолчанию.
На этом конфигурацию проэкта в CoubMX можно считать законченной. Завершим создание проекта для Atollic по аналогии с прошлой статьей.
Вся программная релизация заключается в объявлении переменной, хранящей значение adcResult и объявлении выше описанных #define значений. После этого в цикле читаем значение с АЦП в переменную adcResult и останавливаем АЦП до следующей итерации цикла:
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
adcResult = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
Затем сравниваем полученное значение с объявленными выше и в зависимости от результата включаем заданный выход контроллера (загорается светодиод) и отключаем остальные.
Код приложения доступен для скачивания по ссылке: https://bitbucket.org/shurankain/adc_leds_f103/downloads/
Стоит заметить, что камень STM32F103C8 имеет встроенных 2 АЦП, каждый из которых может обрабатывать до 18 каналов. Каналами могут служить не только данные извне, но и от внутренних источников. На используемой нами плате доступны 10 внешних каналов (ADC1_IN0-ADC1_IN9 или ADC2_IN0-ADC2-IN9), а так же внутренний термометр встроенный в чип и опорное напряжение (1.20V но может меняться от температуры).
P.S. Дабы избежать ненужных вопросов в стиле: "а чем отличается от Адруино?", - отвечу сразу. Рарядностью. В ардуино 10и разрядный АЦП в stm32 12и разрядный. Что это значит? Ардуино может дискретизировать входной сигнал с точностью 1/1024 (1024 = 2 в 10й степени), в случае с stm32 это 1/4096 (2 в 12й степени). Соответственно, благодаря двум дополнительным разрядам, АЦП в stm32 выдает результат в 4 раза точнее.
P.S. Я обязательно расскажу об использовании нескольких АЦП в будущих статьях. Но в ближайших планируется рассказать о работе с i2c, подключении дисплеев и датчиков. Хочу поблагодарить своих подписчиков, которые у меня появились за интерес к данной теме! Буду рад отвтетить на ваши вопросы и услышать ценные советы с вашей стороны. Спасибо.
Пока далеко не ушел от осваивания АЦП. Поэкспериментируй со связкой АЦП+DMA. Например выделил буфер на 1000 отсчетов, пнул АЦП он сам себе оцифровывает а DMA складывает все в буфер, как только буфер заполнился DMA дает прерывание ну и дальше хочешь усредняй хочешь цифровую фильтрацию применяй (понятно, что не внутри прерывания).
Обычный способ в лоб, цифровать по одному отсчету сгодится разве что для наглядности и поиграться. На практике, дергать ядро для обработки каждого отсчета достаточно бесполезная вещь.
Вообще с наличием такого инструмента в stm32 как DMA желательно по возможности пихать его во всю периферию, ну и учиться ее применять не отходя от кассы.
Я вначале своего пути с stm забил на DMA, мол сложная непонятная херня. Зато потом сошло озарение) SPI, I2C, USART, ADC, DAC - чем во все это пихать по байту или доставать по байту, отвлекая ядро на все эти действия, проще один раз настроить DMA и забыть об этом)
А как быть если хочешь измерить больше 3.3 вольт или микровольты наоборот? Киньте в меня пожалуйста правильным запросом в гугл или книжкой. Я просто да же не представляю как это может называться, а АЦП заюзать хочу но до 24 вольт и микровольты...
Может ответ на почту сбросите: tabanez1942@mail.ru.
Работаю с дисплеем 1602L в среде Atollic.
#include <stdio.h>
#include "stm32F10x.h"
#include "Lcd1_driver.c"
#include "Lcd1_driver.h"
#include "stm32f10x_rcc.h"
void main()
{
lcd_init();
lcd_clear();
lcd_xy(0, 1) ; // выводить будем в верхнюю строку во вторую позицию
lcd_out("Hello world"); // выводим строку
while(1);
}
Выдаёт единственное предупреждение:
return type of 'main' is not 'int' и не формирует hex файл.
В настройках hex файл как выходной установлен.
elf файл тоже не создаётся.
В чём может быть дело?