Тёмный

Программирование STM32: Работа с датчиком MPU6050. Using STM32 I2C to read data from MPU6050 

Подписаться
Просмотров 15 тыс.
% 366

В рамках задачи по написанию автопилота под микроконтроллер STM32, мы постараемся детально разобраться во взаимодействии с датчиком MPU-6050 по шине I2C.
Выпуск получился большим, кому нужно быстро пробежаться глазами по информации, сделал отдельное текстовое описание
abaidulin.com/stm32_programming/stm32_readdata_mpu6050/
Скачать исходный код проекта abaidulin.com/autopilot_sourcecode_download/
Временные метки:
2:00 Используемые модули
3:30 Создаем проект
5:20 Работа с STM32CubeMX
7:30 Работа с PlatformIO
9:30 Работаем с шиной I2C
13:30 Инициализация MPU-6050
17:50 Считывание данных с акселерометра и гироскопа во внутреннюю память STM32
24:10 Обработка поступивших данных гироскопа, интегрирование
27:40 Подключение модуля GY-251/MPU6050 к плате STM32F1
29:00 Тестирование программы и датчика

Наука

Опубликовано:

 

22 авг 2018

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 35   
@olegpetrochenko3192
@olegpetrochenko3192 5 лет назад
Ого, интересный проект! Только есть пара замечаний: Первое замечание: Отправка данных по i2c относительно медленная операция - камень работает на 24MHz, т.е. за 1ms, грубо говоря, процессор выполняет 24к инструкций (на деле меньше из-за того, что инструкции выполняются дольше чем за 1 такт, но порядок оценить хватит). В то же время, при стандартной частоте шины i2c в 100кбит/с за 1ms отправится 100 бит данных. В функции MPU6050_GgetAllData считывается 14 байт + на шину сначала отправляется адрес микросхемы, что ещё 1 байт. В итоге, по шине придётся перегнать 120 бит данных, что займёт примерно 1.2 ms не учитывая накладные расходы. Во время считывания данных по i2c камень тупо ждёт в цикле пока данные не отправятся => на 1.2 ms камень впадает в ступор, хотя за это время мог выполнить примерно 28к каких-нибудь полезных инструкций. Но это не самое страшное, хуже всего, что все эти 1.2 ms камень проводит внутри прерывания HAL_SYSTICK_Callback, таким образом блокируя все остальные прерывания (HAL_SYSTICK_Callback это просто враппер над SysTick, а SysTick - это прерывание ядра Cortex, оно имеет приоритет выше, чем приоритет любой периферии от STM), в том числе и сам HAL_SYSTICK_Callback, т.к. HAL_SYSTICK_Callback вызывается 1 раз в 1ms, а чтение с i2c занимает 1.2 ms. Это очень грубая ошибка, которая может вызвать серьёзные проблемы в самых неожиданных местах, особенно если говорить об авиации. Золотое правило программирования под МК(микроконтроллер) - внутри прерывания нельзя выполнять длительные операции. Т.е. чтение из сенсора внутри HAL_SYSTICK_Callback выполнять нельзя. Правильный путь делать чтение из гироскопа внутри главного цикла с использованием delay() или аналога для формирования задержки между считыванием показаний. Во время delay() процессор также впустую крутится в цикле, но уже способен моментально реагировать на прерывания. Бывает так, что использование delay() также не допустимо. На этот счёт есть несколько решений. Поступают обычно так: создаётся где-нибудь uint32_t или uint64_t счётчик, назовём его curr, при старте МК curr инициализируется нолём. В прерывании HAL_SYSTICK_Callback curr увеличивается на единицу (т.е. фактически в счётчике хранится число миллисекунд, прошедших со старта работы МК). Также создаётся дополнительная переменная, в которую можно положить значение curr, назовём её prev. Перед главным циклом в prev записывается значение curr. В главном цикле из curr вычитается prev - это число миллисекунд, прошедшее после обновления prev. Ждём пока разница между prev и curr не будет равна нужной нам задержке, обновляем prev, делаем то что нам нужно и так по кругу. Допустим, нам нужно считать значение из сенсора 50 раз в секунду, или каждые 20ms, тогда код чтения будет выглядеть следующим образом: volatile uint32_t curr; int main(void) { uint32_t prev = curr; while(1) { if (curr - prev >= 20) { prev = curr; // payload ReadSenssor(); } } } void HAL_SYSTICK_Callback(void) { curr++; } В Arduino IDE есть показательный пример на эту тему: Digital->BlinkWithoutDelay. Переменную curr необходимо пометить ключевым словом volatile, иначе компилятор подумает что curr никогда не обновляется и тупо выкинет весь код из if (curr - prev >= 20). В stm32 есть замечательная штука, называется dma, позволяет взаимодействовать периферии с оперативкой напрямую, в обход процессора. Т.е. можно просто дать задание на перекачку данных, и как только мы перегнали все 14 байт с гироскопа, сработает прерывание и уже в нём можно обработать результат. Таким образом можно по кд считывать данные с гироскопа, примерно раз в 1.5ms или 666 раз в секунду (можно ещё увеличить в настройках скорость i2c в 4 раза и обрабатывать показания 2.5к раз в секунду. Главное, чтобы сам гироскоп успевал выдавать показания с такой скоростью). При этом ресурсы процессора будут использоваться по минимуму, главный цикл программы будет просто пустой, вся обработка будет производится в прерываниях. Откровенно говоря, подход разработки под МК с использованием прерываний немного хитрее, чем может показаться на первый взгляд, приходится контролировать очень много тонких моментов, и чем больше проект - тем больше моментов. И если проект большой, имеет смысл глянуть в сторону FreeRTOS и писать код в более естественном стиле. Ресурсов у данного МК достаточно, оперативки маловато конечно, но если не слишком много места выделять под кучу или вообще без динамической памяти обойтись, то хватит с головой даже для автопилота. На крайняк использовать stm32f103c8t6 за 2 бакса с али, там оперативки 20кб, с ним хоть ракету в космос запускай :) Второе замечание касается оформления кода: хорошая практика - прятать реализацию функций MPU6050_Init, MPU6050_Calibrate, MPU6050_GgetAllData, MPU6050_GgetAllData в отдельный файлик, например, MPU6050.c, а в main.c или в любом другом месте просто дёргать API этих функций. Т.к. i2c актуален только в рамках работы с MPU6050, инициализацию I2C можно перенести из main() в MPU6050_Init(). Соответственно, функции I2C_ReadBuffer, I2C_WriteBuffer также перенести в MPU6050.c и пометить ключевым словом static, таким образом изолируя модуль нашей программы и унифицируя интерфейс работы с MPU6050. По сути мы создадим свой HAL для работы с MPU6050, если сменится микроконтроллер, нам нужно будет немного подправить код внутри MPU6050.c, не затрагивая всё остальное. Такие действия сильно помогут в будущем, когда проект разрастётся. Если планируется на одну шину i2c вешать несколько устройств (например, гироскоп + барометр + компас), то функции I2C_ReadBuffer, I2C_WriteBuffer лучше вынести в отдельную библиотеку и дёргать эти функции в драйверах гироскопа/барометра/компаса, но нужно следить за тем, чтобы одновременно разные драйверы в i2c не ломились, иначе на шине получится каша. Если возникнут вопросы - могу проконсультировать. С удовольствием буду следить за развитием проекта!
@RenatAbaidulin
@RenatAbaidulin 5 лет назад
Какой полезный комментарий! Спасибо! Обязательно применю все ваши Советы!)))
@logicfacts9964
@logicfacts9964 5 лет назад
А я могу проконсультировать Вас прежде чем Вы его проконсультировали, что бы знали что код в прерывании HAL исполнял бы если бы его писали Ваши ардуинчики. Так что все что Вы написали полный бред
@seagsmtrashseagsmtrash1906
@seagsmtrashseagsmtrash1906 4 года назад
Испльзование FreeRTOS в таких задачах оправданно только в случае необходимости испозования специальных стеков, типа TCP/IP, которые предоставляет FreeRTOS. В противном случае только конечный автомат. Никаких обработок данных в прерываниях от DMA. Претывание DMA только устанавливает флаг, что данные приняты и готовы. Основной цикл обработки в прерывании таймера. Там же инициализация DMA, опрашивающих датчики. Это позволит очень точно контролировать время обработки фрейма данных. И так далее. В main можно обрабатывать самое ненужное.
@sergeyrink3003
@sergeyrink3003 Год назад
MPU6050 обновляет данные 1 раз в мс. Даже если использовать другой датчик пор SPI все рано быстрее не будет. Этого вполне хватает для работы со стабилизацией на частоте 1кГц. время затрачиваемое на обработку гироскопов можно померить ну и я не сильно заморачиваюсь у меня уже летает STM32 на Ардуино и квадрик и крыло.
@intelektum
@intelektum Год назад
@@sergeyrink3003 А сколько времени занимает чтение данных с гироскопа по И2Ц ? что то мне подсказывает что больше 1 мс вот тут СПИ и позволиит читать с такой скоростью., А глянуть на код вашего квадрика можно ?
@TEENASPECT
@TEENASPECT 5 лет назад
Вот это отличная серия уроков будет. В закладочки.
@mrsuratech8344
@mrsuratech8344 5 лет назад
Крутота!!!
@user-dd2qt4wk4l
@user-dd2qt4wk4l 4 года назад
Вот то что нужно.+++ за видос + подписка.
@laaamedaniel4943
@laaamedaniel4943 2 месяца назад
26:29 могли бы Вы рассказать как получены эти формулы? И ещё. В следующем видео, чтобы определять угол рыскания, Вы пользуетесь показаниями магнитометра. Однако для пересчёта крена и тангажа относительно рыскания, вы пользуетесь показаниями угла рыскания с гироскопа. Почему для пересчёта тангажа и крена можно использовать рыскание с гироскопа, а для определения самого рыскания Вы используете магнитометр?
@laaamedaniel4943
@laaamedaniel4943 2 месяца назад
Насчёт второго вопроса понял: в отличие от крена и тангажа, именно угол рыскания полученный с гироскопа со временем накапливает ошибку. Избавляться от накопленной ошибки крена и тангажа помогает акселерометр, а ему в свою очередь вектор ускорения свободного падения, а к корректированию рыскания подход подразумевающий сверку с вектором g неприменим. Вот и выходит, что текущий угол рыскания измеряется отдельно магнитометром. Таким образом с гироскопа удовлетворительно можно получить только "накопленные" крен и тангаж, дрейф двух этих показаний гироскопа MPU6050 мал, что нельзя сказать про показания рыскания. Однако если говорить не про "накопленные" углы ( то есть сумму всех предыдущих "моментальных" измерений, что и будет давать текущее положение), а говорить про "моментальные" показания или прибавку угла за единицу времени, то тогда "моментальное" рыскание вполне можно использовать как достоверное. А потому что "моментальное" показание это единоразовое измерение, нет самого факта накопления ошибки. Насчёт первого вопроса про формулы до сих пор не догоняю) что нам может дать умножение угла на синус другого угла? Хочется как то связать с получением проекции, но тогда надо брать сторону и синус угла, а тут произведение угла и синуса другого угла...
@user-qr2br1ut2d
@user-qr2br1ut2d 5 лет назад
Здравствуйте! Как я вижу вы пишите код в VS code. Пробовал прикрутить компилятор и отладчик к QTCreator, но путного ничего не вышло. Не могли бы вы поделиться мануалом/статьей с инструкцией по подключению и работе в VS code под stm32? Или вы все делали опираясь на свои знания?
@RenatAbaidulin
@RenatAbaidulin 5 лет назад
Добрый день! А я работаю через плагин Platformio platformio.org/platformio-ide
@mira3999
@mira3999 4 года назад
Класное видео, спасибо! Но что такое рысканье?
@RenatAbaidulin
@RenatAbaidulin 4 года назад
Спасибо! ru.wikipedia.org/wiki/%D0%A0%D1%8B%D1%81%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5 Вот тут подробно расписано с иллюстрациями)
@sergeyrink3003
@sergeyrink3003 Год назад
Ну и как продолжение было? :)) У меня уже летает и квадрик и крыло правда перешел с AVR на STM32F103.
@vbif96
@vbif96 3 года назад
Добрый день, не могли бы Вы предоставить исходный код? Спасибо.
@RenatAbaidulin
@RenatAbaidulin 3 года назад
Исходный код уже предоставить не могу, но основная математика в статье на сайте предоставлена. Там несколько статей идут друг за другом, вы можете интегрировать математику в свой проект без проблем)
@vbif96
@vbif96 3 года назад
спасибо) Просто основная проблема возникла с описанием метода MPU6050_Data, никак у вас его не могу найти и в видео тоже.
@poseidoncreative4606
@poseidoncreative4606 4 года назад
can you share your source code?
@RenatAbaidulin
@RenatAbaidulin 4 года назад
Email me at renat@abaidulin.com and i will send you old version of project. The new one is under DNA) sorry)
@maddocrusmad464
@maddocrusmad464 5 лет назад
Можно шрифт по максимому, на смартфоне не видно. Я за то что бы использовать всю полезную область для предостовления информации...
@RenatAbaidulin
@RenatAbaidulin 5 лет назад
Ок)
@avantux
@avantux 5 лет назад
Есть готовые значительно более компактные решения на F3/4 от коптеров и самолетов, прошивка с открытым исходным кодом
@RenatAbaidulin
@RenatAbaidulin 5 лет назад
Возможно) Но цель не дрон запустить, дабы летал) цель разобраться с программированием под STM32, путём написания своей прошивки, заточенной под специализированную задачу) да и насчёт «более компактных» можно долго дискутировать)
@Igor_Sidorov_
@Igor_Sidorov_ 5 лет назад
Согласен, основная цель - изучение основных принципов работы, т.к. на stm32 не только полётники построены. Иначе готовые полётники уже существуют. Тоже подписался.
@RenatAbaidulin
@RenatAbaidulin 5 лет назад
Спасибо! Скоро вторая серия выйдет)
@rl3kj
@rl3kj 10 месяцев назад
Здравствуйте. Ссылка на код никуда не ведет.
@RenatAbaidulin
@RenatAbaidulin 10 месяцев назад
Здравствуйте! К сожалению хостер (nic.ru) потерял все мои данные после нового года. Сначала кормили завтраками, а потом вообще отказались восстанавливать. Поэтому уже нет ни последней версии исходного кода, ни БД с данными по обучению ИИ...
@rl3kj
@rl3kj 10 месяцев назад
@@RenatAbaidulin Понятно. Спасибо за ответ.
@m4rder599
@m4rder599 2 года назад
Не понимаю как код компилится, если в функциях есть куча неиницилизованных переменных
@laaamedaniel4943
@laaamedaniel4943 Месяц назад
Я тоже не понимаю. Разобрался ли кто-нибудь с вопросом?
@user-uv5qt8wh5x
@user-uv5qt8wh5x Месяц назад
​, это плюсы тут такая переменная автоматом считается как =0
@user-dd2qt4wk4l
@user-dd2qt4wk4l 4 года назад
Вот то что нужно.+++ за видос + подписка.
Далее