Приветствую всех на нашем сайте 🤝 У меня лежит комплект датчиков с AliExpress, так вот я решил сделать своего рода обзор и пример программы для работы с ними. Речь идет о магнитных энкодерах AS5048. Задача определения угла в различных проектах встречается довольно часто, а эти датчики себя проявили с наилучшей стороны, поэтому нельзя обойти их вниманием. И для примера подключения энкодера мы будем использовать контроллер STM32.
Классификация энкодеров как таковых достаточно обширна. Например, различают два больших класса:
- Инкрементальный энкодер - позволяет отследить изменение положения относительно некой начальной точки. Эта начальная точка соответствует положению датчика в момент подачи на него питания.
- Абсолютный энкодер - в любой момент времени позволяет получить точную однозначную информацию о своем положении в определенном диапазоне и конкретных единицах.
Очевидно, что использование абсолютного энкодера в большинстве задач сильно облегчает жизнь. Но как и всегда, не все так просто, абсолютные энкодеры обычно дороже инкрементальных, что сужает возможность их применения.
Два других крупных класса энкодеров определяют их внутреннее устройство и принцип действия:
- Оптические энкодеры - состоят из источника света, специального оптического диска и фотоприемника. Диск содержит прозрачные и непрозрачные области, что позволяет по данным фотоприемника получить информацию об изменении положения.
- Магнитные энкодеры - как уже понятно из названия, в этих устройствах определение положения происходит путем отслеживания изменений магнитного поля. Ключевым элементом таких энкодеров является датчик Холла.
Не будем углубляться во всевозможные типы энкодеров и принцип их работы (об этом, пожалуй, стоит написать отдельную статью), сконцентрируемся, в первую очередь, на нашем подопытном AS5048.
Итак, наш датчик является абсолютным магнитным энкодером, позволяющим отследить изменение угла в пределах от 0 до 360 градусов, то есть нам доступен полный оборот. Разрешающая способность энкодера - 14 бит, что соответствует:
\frac{360\degree}{16384} = 0,02197\degree
По моему опыту использования и подключения энкодера AS5048 этого обычно хватает с запасом ) Для того, чтобы получить данные о положении доступны несколько вариантов:
- Интерфейс SPI - для AS5048A.
- Подключение энкодера по I2C - для AS5048B.
- Выход PWM - этот вариант доступен для всех модификаций. Но для PWM разрешающая способность не 14, а 12 бит.
Именно на третьем способе мы и остановимся. Во-первых, это зачастую удобнее, не надо отправлять никаких команд для запроса данных, ШИМ-сигнал генерируется постоянно, и нам требуется только измерять его параметры. И, во-вторых, для считывания сигнала таким образом требуется меньше сигнальных линий, в некоторых случаях это оказывается ключевым аспектом при выборе интерфейса.
Принцип использования датчика максимально прост - постоянный магнит (идет в комплекте с платой энкодера) закрепляется на подвижном валу, а сам энкодер - на неподвижной части. При вращении происходит изменение магнитного поля, которое фиксируется датчиками Холла и впоследствии преобразуется в электрический сигнал. А с этим сигналом мы уже можем работать.
Минус использования PWM-выхода заключается в том, что функция установки нулевого положения датчика в этом случае недоступна. Идея тут в том, что при установке платы энкодера и магнита их положение друг относительно друга будет, в общем-то, случайным. Из-за этого нулевое положение датчика будет всегда разным, что обычно недопустимо. И поэтому в AS5048 добавлена возможность установить нулевое положение специальной командой, отправляемой по SPI или I2C.
Но, с другой стороны, у этой проблемы есть очень простое и доступное решение, которое даже не требует использования этой функции. Мы можем просто добавить смещение в код нашей программы. То есть, рассчитав в ПО для микроконтроллера абсолютное положение энкодера, мы добавляем к нему определенную величину. Значение это всегда одинаковое (при постоянстве монтажа), поскольку определяется только взаимным положением магнита и датчика.
С этим разобрались, переходим к формату выходных данных, точнее к механизму, который позволит нам из ШИМ-сигнала получить положение в пространстве. Выходной сигнал выглядит так:
Возьмем за основу наш пример из статьи про режим таймера Input Capture и будем измерять период сигнала и длительность импульса. Кстати по даташиту частота PWM равна 1 КГц, то есть период - 1 мс. На практике, период оказывается несколько другим, по моим наблюдениям, может быть отклонение до 10%. В связи с этим измерение периода позволит нам значительно повысить точность (относительно случая, если бы мы просто приняли его равным 1 мс).
Алгоритм действий будет таким:
1). Измеряем период и длительность импульса ШИМ-сигнала, получаем значения в микросекундах.
2). Пересчитаем длительность в отсчеты энкодера (clocks на картинке выше):
t_{clocks} = 4119 * \frac{t}{T}
Здесь 4119 отсчетов - это весь период - 16 + 4095 + 8.
3). Из формы выходного сигнала видим, что в случае, если не возникло никакой ошибки, длительность импульса составит минимум 16 отсчетов. Это число получается из длительности поля Init (12 отсчетов) и поля Error (4). Максимальная же длительность импульса равна разности между длительностью всего периода и поля Exit, что равно 4119 - 8 = 4111 отсчетов.
4). Если значение длительности удовлетворяет требованиям из пункта 3, то есть лежит в промежутке от 16 до 4111, то определяем из него текущую ширину поля Data в отсчетах:
t_{data\medspace clocks} = t_{clocks} - 16
5). И, наконец, ничего нам не мешает рассчитать значение угла:
\alpha = \frac{t_{data\medspace clocks}}{4095} * 360\degree
Давайте реализуем все эти шаги в программе и для начала определим константы:
#define PERIOD_CLOCKS 4119 #define ANGLE_MAX 360 #define PULSE_CLOCKS_MIN 16 #define PULSE_CLOCKS_MAX 4111 #define DATA_CLOCKS_MAX 4095
Дополняем код прерывания таймера и все готово:
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { period = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); pulseWidth = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2); TIM2->CNT = 0; if (pulseWidth < period) { uint16_t pulseWidthClocks = PERIOD_CLOCKS * pulseWidth / period; if ((pulseWidthClocks >= PULSE_CLOCKS_MIN) && (pulseWidthClocks <= PULSE_CLOCKS_MAX)) { uint16_t dataClocks = pulseWidthClocks - PULSE_CLOCKS_MIN; angle = ((float)dataClocks) / DATA_CLOCKS_MAX * ANGLE_MAX; } } } } }
И в завершение статьи несколько осциллограмм с выхода энкодера и соответствующие им результаты работы нашей программы (значения здесь в отсчетах таймера, в нашем примере один отсчет - 1 мкс):
На этом заканчиваем сегодняшний обзор, всем большое спасибо за внимание, надеюсь материал окажется вам полезен 🤝
Ссылка на проект - MT_Encoder_AS5048.
Спасибо. Эта статья наталкнула на идею, которая кажется хорошей.
Отлично!
Здравствуйте, а не подскажете как подключить энкодер к микроконтроллеру ? Не могу разобраться какие пины куда....
Добрый день! Как в этой статье - https://microtechnics.ru/stm32-i-timer-input-capture-rezhim-zahvata-signala/. На любой входной канал любого таймера с возможностью захвата сигнала.
Спасибо!
Если что обращайтесь, помогу )
Здравствуйте. Я установил подобный энкодер на бесколлекторный двигатель. Как писали выше, совместить ось магнита, мотора и датчика не просто. Вот у меня мотор работает не стабильно. Из-за эксцентриситета. Их ведь как-то калибруют? Я могу вращать мотор в открытом цикле с постоянной скоростью. Возможно ли параллельно снимать показания датчика и составить таблицу коррекции?