tag:blogger.com,1999:blog-63954532811737669902024-03-16T11:50:12.592-07:00ЭлектротехникаСергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.comBlogger297125tag:blogger.com,1999:blog-6395453281173766990.post-1296243818777277652024-03-15T23:46:00.000-07:002024-03-15T23:46:54.929-07:00Системный таймер STM32<p> Микроконтроллеры STM32 содержат некоторое количество таймеров. Таймеры делятся на типы и отличаются друг от друга. Существуют, ранее рассмотренные, таймеры общего назначения (напр. TIM2 и TIM3), продвинутые таймеры (TIM1 почти такие же как таймеры общего назначения но с большим количеством дополнительных функций), сторожевые таймеры и системные таймеры (обычно системный таймер только один). Системный таймер самый простой и может использоваться когда не нужен какой либо сложный функционал. Данный тип таймера имеет некоторое небольшое количество регистров. Например регистр счетчика. В этом регистре находиться число которое показывает до скольки досчитал данный таймер. Считать он может только вниз т.е. только декрементировать (уменьшать на единицу) число в данном регистре. Этот регистр 23х разрядный:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTmdTn5SJZN-mvE_cAsZqRxD1loFR4yXxvanuin_DFOXLVbiheY78SB9b2-l5HQHj6fIBW8P6zs3z6u2MHGiLed2c-nb7ZvaPgdUI9BUL_2I3GH-axqz4ZLHxs1_WeAO1HKQi-rTY4kWAszUNB_9_zew1ZhyphenhyphenDmR5twkcnR0XDvp5z2xWFcBnyiTiQfU7Q/s660/27%20%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%20%D1%80%D0%B5%D0%BA%D1%83%D1%89%D0%B5%D0%B3%D0%BE%20%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%20%D1%81%D1%87%D0%B5%D1%82%D1%87%D0%B8%D0%BA%D0%B0%20%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="660" height="175" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTmdTn5SJZN-mvE_cAsZqRxD1loFR4yXxvanuin_DFOXLVbiheY78SB9b2-l5HQHj6fIBW8P6zs3z6u2MHGiLed2c-nb7ZvaPgdUI9BUL_2I3GH-axqz4ZLHxs1_WeAO1HKQi-rTY4kWAszUNB_9_zew1ZhyphenhyphenDmR5twkcnR0XDvp5z2xWFcBnyiTiQfU7Q/s320/27%20%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%20%D1%80%D0%B5%D0%BA%D1%83%D1%89%D0%B5%D0%B3%D0%BE%20%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%20%D1%81%D1%87%D0%B5%D1%82%D1%87%D0%B8%D0%BA%D0%B0%20%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%D0%B0.png" width="320" /></a></div><br /><p>После того как число в этом регистре достигло нуля, происходит запись числа в этот регистр из регистра перезагрузки:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiac76hwFZgY1Q6RtMoyko0wQm7DIuMN6jdmXTJKts0EZulokbXqgv-nhXPGK91QuksFbb_ScCZW5rXfbDH5UTA7PxCATfFRWdxNcSXG62ZFG3VHxxNz3LB-CbD9OqkfYXMhEJCTqcHgflZsf6PAyKWVKa62VrDSaYXeR33XYBNYf_g3suCQvZ1gEUlHWk/s688/26%20%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B4%D0%BB%D1%8F%20%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B9%20%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B8%20%D0%BA%D0%BE%D0%B3%D0%B4%D0%B0%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%20%D0%B4%D0%BE%D1%81%D1%87%D0%B8%D1%82%D0%B0%D0%BB.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="512" data-original-width="688" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiac76hwFZgY1Q6RtMoyko0wQm7DIuMN6jdmXTJKts0EZulokbXqgv-nhXPGK91QuksFbb_ScCZW5rXfbDH5UTA7PxCATfFRWdxNcSXG62ZFG3VHxxNz3LB-CbD9OqkfYXMhEJCTqcHgflZsf6PAyKWVKa62VrDSaYXeR33XYBNYf_g3suCQvZ1gEUlHWk/s320/26%20%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B4%D0%BB%D1%8F%20%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B9%20%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B8%20%D0%BA%D0%BE%D0%B3%D0%B4%D0%B0%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%20%D0%B4%D0%BE%D1%81%D1%87%D0%B8%D1%82%D0%B0%D0%BB.png" width="320" /></a></div>Т.е. можно сказать что в этом регистре находиться число того сколько раз считает таймер прежде чем произойдет его переполнение. После того как произошло переполнение, данного таймера, может быть вызвано прерывание. Чтобы разрешить прерывание нужно установить единицу в бит<div>1 регистра настройки таймера:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilnzGPSClohCokrSDaQnWhjlDSZN179Sbi0XWWfpNW9Zv0BurMuVQlFIbQXlRL6I4BXLuUJACyihKI8Y24YLBUDWVsHZRZFnvAGaJtv2FArOX5PmaLoLenlTeFPyrs5fdvpVtGUoo3Bi1Q4S1VD9B8FB30Y_eNd9jybUDOxNrjRP7Z_7XxezH2E18AUvA/s740/50%20%D0%B2%D0%BA%D0%BB%20%D0%B2%D1%8B%D0%BA%D0%BB%20%D0%BF%D0%B5%D1%80%D1%80%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="640" data-original-width="740" height="277" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilnzGPSClohCokrSDaQnWhjlDSZN179Sbi0XWWfpNW9Zv0BurMuVQlFIbQXlRL6I4BXLuUJACyihKI8Y24YLBUDWVsHZRZFnvAGaJtv2FArOX5PmaLoLenlTeFPyrs5fdvpVtGUoo3Bi1Q4S1VD9B8FB30Y_eNd9jybUDOxNrjRP7Z_7XxezH2E18AUvA/s320/50%20%D0%B2%D0%BA%D0%BB%20%D0%B2%D1%8B%D0%BA%D0%BB%20%D0%BF%D0%B5%D1%80%D1%80%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%D0%B0.png" width="320" /></a></div><br /><div>У данного таймера всего один регистр настройки в котором для настройки есть всего три бита. Также есть один флаг. Бит 1 (как было показано выше) отвечает за разблокировку прерывания по переполнению (других прерываний у данного таймера нет). Этот бит д.б. установлен в единицу чтобы прерывание срабатывало. Также есть бит 0 для включения таймера:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggebhyphenhyphenFNoygKGjXEuFiJCtz7UQZTaSDQMmqFsmCp3U_D674-0YTfyprpn4aDAQDs4m17lETpdyGyyjQs0Bnn27o71BcHtTAOVtO-gx-QyOor1mW-CXfeEoOdPo_AMZGfiLaND3reyWYk9TtitLXpb7iPNW3JF9Y1ytf7e03lK4fCSWl3ieNw_ny3zxEX0/s740/40%20%D0%B2%D0%BA%D0%BB%20%D0%B2%D1%8B%D0%BA%D0%BB%20%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BD%D1%8B%D0%B9%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="640" data-original-width="740" height="277" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggebhyphenhyphenFNoygKGjXEuFiJCtz7UQZTaSDQMmqFsmCp3U_D674-0YTfyprpn4aDAQDs4m17lETpdyGyyjQs0Bnn27o71BcHtTAOVtO-gx-QyOor1mW-CXfeEoOdPo_AMZGfiLaND3reyWYk9TtitLXpb7iPNW3JF9Y1ytf7e03lK4fCSWl3ieNw_ny3zxEX0/s320/40%20%D0%B2%D0%BA%D0%BB%20%D0%B2%D1%8B%D0%BA%D0%BB%20%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BD%D1%8B%D0%B9%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80.png" width="320" /></a></div>И бит 2 для выбора внутреннего источника тактирования:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig5NR13JJSdn1EgDORv5WjPqM3CnpvwBv8pcbkBdbGN1bin3ZDcguygOBvmoY-cavU7IKeK6_kSArEAQjQXliEufAObT0dA1f_RPQY4nsC5mm4pIAT9xHWBdhfoD4tPFUTX5H-hQ1-aVvmGKBKh-zw9sl4gUQ4kR_r6yxb23A13S4knTU4gSXU_XdffYs/s740/60%20%D1%82%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%D0%B0%200-%D0%BE%D1%82%20%D0%B2%D0%BD%D0%B5%D1%88%D0%B5%D0%BD%D0%B3%D0%BE%20%D0%B3%D0%B5%D0%BD%201-%D0%BE%D1%82%20%D0%B2%D0%BD%D1%83%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D0%B5%D0%B3%D0%BE%20%D0%B3%D0%B5%D0%BD.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="640" data-original-width="740" height="277" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig5NR13JJSdn1EgDORv5WjPqM3CnpvwBv8pcbkBdbGN1bin3ZDcguygOBvmoY-cavU7IKeK6_kSArEAQjQXliEufAObT0dA1f_RPQY4nsC5mm4pIAT9xHWBdhfoD4tPFUTX5H-hQ1-aVvmGKBKh-zw9sl4gUQ4kR_r6yxb23A13S4knTU4gSXU_XdffYs/s320/60%20%D1%82%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%D0%B0%200-%D0%BE%D1%82%20%D0%B2%D0%BD%D0%B5%D1%88%D0%B5%D0%BD%D0%B3%D0%BE%20%D0%B3%D0%B5%D0%BD%201-%D0%BE%D1%82%20%D0%B2%D0%BD%D1%83%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D0%B5%D0%B3%D0%BE%20%D0%B3%D0%B5%D0%BD.png" width="320" /></a></div><br /><div>единица, в данном бите, означает тактирование от внутреннего источника, ноль означает тактирование от внешнего источника. Т.е. для настройки таймера (если не нужно внешнее тактирование) все три бита нужно заполнить единицами. Предделителя у данного таймера нет поэтому снизить частоту возникновения прерывания можно например дополнительным программным счетчиком или же например установить внешнее низкочастонтное тактирование от например микросхемы - таймера 555 или от пина этого же микроконтроллера (правда в этом случае придется задействовать какой либо другой таймер если нужна точность). </div><div>Видео по данной теме:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/ccjJvm8PSkc" width="320" youtube-src-id="ccjJvm8PSkc"></iframe></div><br /><div><br /></div><div>Адрес биткоин кошелька для поддержки канала - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g</div><div><div><br /></div></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-33068886595211105102024-02-10T05:27:00.000-08:002024-02-10T05:27:25.288-08:00Переделка дальта 3D принтера в лазерный ЧПУ гравер<p> FDM 3D принтеры стали популярными ЧПУ станками для изготовления прототипов а также даже производства настоящих полнофункциональных изделий и деталей в основном из пластмассы. Чаще всего 3D принтеры делают детали из PLA или ABS пластиков которые весьма дорогие (на момент написания данной стати) в состоянии прутка пригодного для FDM 3D принтера. Большинство FDM 3D принтеров легко переделать в лазерный гравер путем замены экструдера пластика на лазер. Для лазера (в отличии от например фрезера) не нужна жесткая конструкция + 3D принтер может менять фокусировку лазера в процессе работы, чего не могут даже некоторые лазерные граверы. Чтобы переделать 3D принтер в лазерный гравер нужно заменить хотенд принтера на лазерный модуль. Лазерные модули бывают разные. Лазеры бывают разных типов и мощностей. Самые дешёвые, компактые и легкие - это полупроводниковые лазеры они также уступают по мощности например CO2 лазерам но зато + к меньшим габаритам они имеют меньшую стоимость и могут быть установлены на дельта 3D принтеры типа Rostock mini. Обычно дельта на дельта принтеры не устанавливаются тяжелые головки но практика показала лазерный модуль оптической мощностью 5Вт (потребляемая мощность 40Вт) способный резать фанеру и пластик а также гравировать большинство материалов может работать с принтером Rostock mini.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYcYcJcCtZVeebrJzQuNsYRhlKJRxXNIwn0yZbnlvDkcy1DSQv679cOgKAADSSyUYEzZum1ZUjCE5QSfxc9B6bavg4nKRaEIS0QpFFHhx2vLNJte2-nXx17R2bvqJHaFs3VscXZNBtpcmwI-IpxfPg71nvib_CT26JAE171078celDIAEORGrnvHk8GfQ/s264/%D1%84%D0%BE%D1%82%D0%BE.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="254" data-original-width="264" height="254" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYcYcJcCtZVeebrJzQuNsYRhlKJRxXNIwn0yZbnlvDkcy1DSQv679cOgKAADSSyUYEzZum1ZUjCE5QSfxc9B6bavg4nKRaEIS0QpFFHhx2vLNJte2-nXx17R2bvqJHaFs3VscXZNBtpcmwI-IpxfPg71nvib_CT26JAE171078celDIAEORGrnvHk8GfQ/s1600/%D1%84%D0%BE%D1%82%D0%BE.png" width="264" /></a></div>Данный лазерный модуль имеет радиатор, вентилятор для охлаждения и одновременно сдувания дыма от разрезаемой поверхности для улучшения прохождения лазера до этой самой поверхности а также есть сопло которое направляет поток воздуха в разрезаемую точку чтобы лучше сдувать дым. Также есть защитная красная насадка для защиты глаз (на которую не стоит сильно полагаться) и плата управления с тремя выводами. Один вывод питания +12В, второй - земля GND 0В и вывод управления на который подается ШИМ с уровнями напряжения 0В и +5В. В комплекте шел кабель с разъемами. В кабеле 3 провода: красный +12В питания, черный 0В, желтый - ШИМ 0...+5В. Питание подключается к источнику от которого питается принтер. А желтый провод к одному из пинов контроллера RAMPS но только не к тому с которого выходит 12В например стола или вентилятора т.к. эти пины сожгут испортят плату. Нужен именно управляющий пин. Это может быть например свободный пин D4<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQbvmy1a_x_yDUPo6ucnm9uZYsDqB_eWV3ZPRrrO89xUZQiPqX1JdWk8XegfItPPCxGfqTi2NxFgAYJUMc94oRnHshtcm-VkJs3ae2_zxdlX99dCHn7BaCx8zCGcl3xovzK9xxmE9bshlWwiJ-wwjqWO3CNZYYsR_xRCF45Bw1qzLkqcX8cHjI2BGQZLw/s874/d4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="626" data-original-width="874" height="229" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQbvmy1a_x_yDUPo6ucnm9uZYsDqB_eWV3ZPRrrO89xUZQiPqX1JdWk8XegfItPPCxGfqTi2NxFgAYJUMc94oRnHshtcm-VkJs3ae2_zxdlX99dCHn7BaCx8zCGcl3xovzK9xxmE9bshlWwiJ-wwjqWO3CNZYYsR_xRCF45Bw1qzLkqcX8cHjI2BGQZLw/s320/d4.png" width="320" /></a></div><br /><div><br /></div><div> который предназначен для подключения сервомотора но также его можно использовать как обычный свободный GPIO командой например M42:</div><div><br /></div><div>M42 P4 S150 ; включить пин D4 на гребенке с сервами с краю с ШИМом 150</div><div><br /></div><div>Где M42 - это название команды, P4 - номер пина, S150 - уровень шима который м.б. от 0 (лазер полностью выключен и не работает) до 255 (лазер полностью включен и работает на пределе). К сожалению в прошивке Мерлина данная команда не синхронизированна с командой G1 перемещения головки в пространстве, поэтому резка и гравировка не будут точными а также в варианте прошивки для дельта принтера вообще нет команды с синхронизированной работой лазера и перемещением головки. К счастью данная прошивка является скетчем для ардуины Меги который можно подправить. Можно например добавить собственный нестандартный G код с синхронизацией лазера и перемещения головки в пространстве. Что и было сделано. В прошивке Мерлина есть файл "Configuration.h" в который вносятся изменения для настройки принтера. В данном файле изменяются специальные константы которые специально вынесены в данный файл для пользователя чтобы было удобнее и безопаснее чтобы не испортить случайно всю прошивку но добавить новую команду через этот файл нельзя. Добавить её можно в файле "Marlin_main.cpp". В данном файле есть функция "void process_commands()" внутри которой есть ветвление "switch((int)code_value())" внутри которого есть команды. Было замечено что в прошивке для дельта принтера Rostock mini G команды заканчиваются на G4 потом идет перерыв и начинаются с G10 которые заканчиваются на M командах. Для G команд и M команд существует какой общепринятый то стандарт и команда с синхронизацией должна иметь специальное обозначение что возможно позволит использовать для генерации кодов какую либо готовую программу. Однако поскольку для полного понимания прошивки и изучения стандарта нужно тратить время то для ускорения и упрощения можно просто добавить новую команду по аналогии с какой либо имеющейся и написать генератор G кодов самостоятельно. Чтобы добавить новую команду G5 нужно скопировать код:</div><div><br /></div><div><textarea colls="100" readonly="readonly" rows="400" style="height: 600px; margin: 0px; width: 578px;">
case 5: // G5
if(Stopped == false) {
get_coordinates(); // For X Y Z E F
prepare_move(); // начать двигаться
st_synchronize(); // ничего не делать пока каретка не доедет до цели
if (code_seen('S')) // далее настройка и использование пина
{
int pin_status = code_value();
int pin_number = LED_PIN;
if (code_seen('P') && pin_status >= 0 && pin_status <= 255)
pin_number = code_value();
for(int8_t i = 0; i < (int8_t)(sizeof(sensitive_pins)/sizeof(int)); i++)
{
if (sensitive_pins[i] == pin_number)
{
pin_number = -1;
break;
}
}
if (pin_number > -1)
{
pinMode(pin_number, OUTPUT);
//digitalWrite(pin_number, pin_status);
analogWrite(pin_number, pin_status);
}
}
}
break;
</textarea></div><div>и вставить после обработчика команды G4. Пример использования новой самодельной команды</div><div> G5 X0 Y0 Z73 F300 P4 S150</div><div>означает переместиться в точку X0 Y0 Z73 со скоростью 300 и включить пин D4 с шимом 150.</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/WmanTEYEKdI" width="320" youtube-src-id="WmanTEYEKdI"></iframe></div><br /><div><span style="background-color: white; color: #0d0d0d; font-family: Roboto, Noto, sans-serif; font-size: 15px; white-space: pre-wrap;">Адрес биткоин кошелька для поддержки канала - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g</span></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-7477912094320056972024-01-08T05:50:00.000-08:002024-01-08T05:50:35.351-08:00Инфракрасный лазерный дальномер GY-530 GY-VL53L0X<p> Инфракрасный лазерный дальномер VL53L0X может быть подключен к плате Ардуино по шине I2C и использоваться для бесконтактного измерения расстояний от 50мм (проверено эксперементально (см. видео ниже)) до 2м с точностью до 1мм. Существуют также ультразвуковые дальномеры наподобии HC-SR04 но такие дальномеры больше подходят для обнаружения крупных препятствий в то время как VL53L0X способен обнаруживать препятствия толщиной с карандаш и возможно даже меньше. Инфракрасные дальномеры также обладают недостатками по сравнению с ультразвуковыми, например они менее далеко будут измерять поверхности хорошо поглощающие свет. Подключить дальномер VL53L0X к плате Arduino pro mini можно по схеме:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilWkVO0hG30lDoKLb4-O6VRkDThGOGQDG8n2gMIM_cOcpeYGhyqgOBNEnWZ8g8pZSf0aFyHwn8wQK4v_l2CvqLhh9rFz1V__qi1AiplEv6TnKVrC9pcC8LbfFx-sZLdVAZNh-2bIivbSvJcO3QYZcuzVkCYhpcKr5qU5bN850tDAMBHa4kzAgf-HPZo7M/s456/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="456" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilWkVO0hG30lDoKLb4-O6VRkDThGOGQDG8n2gMIM_cOcpeYGhyqgOBNEnWZ8g8pZSf0aFyHwn8wQK4v_l2CvqLhh9rFz1V__qi1AiplEv6TnKVrC9pcC8LbfFx-sZLdVAZNh-2bIivbSvJcO3QYZcuzVkCYhpcKr5qU5bN850tDAMBHa4kzAgf-HPZo7M/s320/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" width="320" /></a></div><br /><p><br /></p><p>USB-UART конвертер нужен для передачи данных на компьютер чтобы компьютер мог монитором показать результат. А также для того чтобы загружать скетч в Ардуино. Схема соединения простая, модуль толерантен к напряжению +5В поэтому такая схема возможна. Для того чтобы можно было использовать данный модуль с платой Ардуино и программировать в среде Ардуино, нужно подключить библиотеку в данной среде. Для этого нужно зайти в менеджер библиотек поп пути Скетчь -> Подключить библиотеку -> Управлять библиотеками</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifjTjaNJplv44bf-epW3X2Hr0oDWnojgxzwSmXcPwP5A0RKIbHQJWNYhbW_kY8vLpLjiPSczhk_ESTYi2kz41ah7lrunbuBZ1Sk4uKsZN-GW7nSW3EcyyqE4nVvZPBeFinzQ9cZPspwAOHeheeLjCUmmAUqNsmSYV3qCVwLy5bZO5gaSDplk2WTDt6-0w/s648/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="582" data-original-width="648" height="287" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifjTjaNJplv44bf-epW3X2Hr0oDWnojgxzwSmXcPwP5A0RKIbHQJWNYhbW_kY8vLpLjiPSczhk_ESTYi2kz41ah7lrunbuBZ1Sk4uKsZN-GW7nSW3EcyyqE4nVvZPBeFinzQ9cZPspwAOHeheeLjCUmmAUqNsmSYV3qCVwLy5bZO5gaSDplk2WTDt6-0w/s320/10.png" width="320" /></a></div>В поисковой строке менеджера библиотек вписать название модуля или первые буквы его названия чтобы найти библиотеку, после чего нажать кнопку "установка"<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbSwCq6K000WhXk0fsAKsrcbDadJIxMtKhWpD8nUe7cawaFUBqQFMytKbxkS4HUwv2HMVV6XlK3PehP8iFipXPgI25QG35KITs-KFmv4VKM5ngV_f7glXAvjVv5K3smB047OhAhazDTiwDVBJNQ4Ydnrvc1t6wzLIOl4T9r9YgkHZB0BVAYKqm2uC1QuE/s794/30.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="442" data-original-width="794" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbSwCq6K000WhXk0fsAKsrcbDadJIxMtKhWpD8nUe7cawaFUBqQFMytKbxkS4HUwv2HMVV6XlK3PehP8iFipXPgI25QG35KITs-KFmv4VKM5ngV_f7glXAvjVv5K3smB047OhAhazDTiwDVBJNQ4Ydnrvc1t6wzLIOl4T9r9YgkHZB0BVAYKqm2uC1QuE/s320/30.png" width="320" /></a></div><div>Далее появится окно предлагающее установить ещё некоторое количество библиотек необходимых для работы основной библиотеки тут естественно нужно нажать кнопку "Install all"</div><div>если конечно данные библиотеки ещё не установлены.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_NE59Pzs5YNKYAoRuRS8HdRkQk_3nmAQmeFz2c4gzR0ls7XvSLYFKn3QsON9CFMl6t095HmXSkQNSXZMqAnasHm4srM273SNvJXVppf7Bj9n2EjIDPgtmTudynjLbM7LbT1Waxvd4abzTjDB74EcLggbyjR5LJIGRO8QrRmc1HUXgVpu8Aj_4kWf_ToE/s690/40.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="358" data-original-width="690" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_NE59Pzs5YNKYAoRuRS8HdRkQk_3nmAQmeFz2c4gzR0ls7XvSLYFKn3QsON9CFMl6t095HmXSkQNSXZMqAnasHm4srM273SNvJXVppf7Bj9n2EjIDPgtmTudynjLbM7LbT1Waxvd4abzTjDB74EcLggbyjR5LJIGRO8QrRmc1HUXgVpu8Aj_4kWf_ToE/s320/40.png" width="320" /></a></div><div>После того как библиотеки будут установены, можно закрыть менеджер библиотек. Теперь можно открыть тестовый скетчь и загрузить его в плату</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDafQHrPLGCHUKsjcjZI3jMfryrEe68R6qqVAeR1WqI0iGMboJS3jYBk1uWFmgXe2xeHZYM3FNzbrj368mtqnUk-kEXj76A84bnQbxh05__h6RYqgioMuLEy6EcN0cVONF7ju6NDtbehjOyS2lIMEMYHPQY_hI5bKNcN7mFqt4oU-zmnWv6_Kc9Adic9I/s644/60.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="586" data-original-width="644" height="291" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDafQHrPLGCHUKsjcjZI3jMfryrEe68R6qqVAeR1WqI0iGMboJS3jYBk1uWFmgXe2xeHZYM3FNzbrj368mtqnUk-kEXj76A84bnQbxh05__h6RYqgioMuLEy6EcN0cVONF7ju6NDtbehjOyS2lIMEMYHPQY_hI5bKNcN7mFqt4oU-zmnWv6_Kc9Adic9I/s320/60.png" width="320" /></a></div><div><div>После того как скетчь загрузиться можно открыть монитор последовательного порта и измерять расстояние дальномером</div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwGPgquYmNkq-alQF1eyp310wkhfKyziBiu9ahTLg-wPvbIm3FLCAIf29cHgs5p9NGzJaSopn3zTkTubSUf5iMa8NhXp8chV2zRdrBQuQtcnaEr-6qd5lmfwDIoJv2js58Md7f0SkR-0dbBCBmM4i5a3JAHBfdpXFujwHCytECScRkRT2G9K_NdE6z8iA/s728/80.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="676" data-original-width="728" height="297" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwGPgquYmNkq-alQF1eyp310wkhfKyziBiu9ahTLg-wPvbIm3FLCAIf29cHgs5p9NGzJaSopn3zTkTubSUf5iMa8NhXp8chV2zRdrBQuQtcnaEr-6qd5lmfwDIoJv2js58Md7f0SkR-0dbBCBmM4i5a3JAHBfdpXFujwHCytECScRkRT2G9K_NdE6z8iA/s320/80.png" width="320" /></a></div>Тестовый скетч не сложный:<div><textarea colls="100" readonly="readonly" rows="600" style="height: 800px; margin: 0px; width: 578px;">
#include "Adafruit_VL53L0X.h"
Adafruit_VL53L0X lox = Adafruit_VL53L0X();
void setup() {
Serial.begin(115200);
// wait until serial port opens for native USB devices
while (! Serial) {
delay(1);
}
Serial.println("Adafruit VL53L0X test");
if (!lox.begin()) {
Serial.println(F("Failed to boot VL53L0X"));
while(1);
}
// power
Serial.println(F("VL53L0X API Simple Ranging example\n\n"));
}
void loop() {
VL53L0X_RangingMeasurementData_t measure;
Serial.print("Reading a measurement... ");
lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!
if (measure.RangeStatus != 4) { // phase failures have incorrect data
Serial.print("Distance (mm): "); Serial.println(measure.RangeMilliMeter);
} else {
Serial.println(" out of range ");
}
delay(100);
}
</textarea><br /><div><div>в самом верху подключается заголовочный файл библиотеки, далее объявляется объект класса библиотеки, в основной функции setup() инициализируется последовательный порт, модуль и выводятся стартовые сообщения. В основном цикле имеется объект для получения данных с модуля (measure) ниже данный объект заполняется данными если расстояние удалось измерить то оно выводиться в последовательный порт если не удалось то выводитья сообщение об этом</div><div>в самом конце задержка для того чтобы цифры не сильно мельтишили на экране.</div></div><div>Видео по дальномеру:</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/Nm7NWL6jC70" width="320" youtube-src-id="Nm7NWL6jC70"></iframe></div><br /><div><span style="background-color: white; color: #0d0d0d; font-size: 15px; white-space: pre-wrap;">Адрес биткоин кошелька для поддержки блога - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g </span></div></div><div><span style="background-color: white; color: #0d0d0d; font-size: 15px; white-space: pre-wrap;"><br /></span></div><div><span style="background-color: white; color: #0d0d0d; font-size: 15px; white-space: pre-wrap;"><br /></span>
<a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a>
</div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-5292856776936817602023-12-10T04:54:00.000-08:002023-12-10T05:00:36.638-08:00W801 стартовый блинк<p> На момент написания данной статьи в продаже существует множество разных плат с микроконтроллерами например Arduino, Blue pill, ESP32 и т.д. Наиболее интересными являются те из них которые имеют низкую цену и большие возможности, поэтому перечисленные Arduino, Blue pill, ESP32 являются самыми популярными. Однако, на момент написания данной статьи, есть ещё одна недорогая и имеющая большие возможности плата с микроконтроллером W801. Данный микроконтроллер 32х разрядный и имеет высокую тактовую частоту 240 МГц а также большое количество пинов GPIO и 2 МГб встроенной флеш памяти. Также есть много интересной и сложной периферии которой обычно нет в простых и дешевых микроконтроллерах. Например WIFI, bluetooth, DCMI. Нет USB и CAN шины поэтому если нужна аппаратная поддержка USB или CAN то нужно выбирать другой микроконтроллер например STM32 или ESP32. Также для W801, на момент написания данной статьи, нет нормальной документации и большого количества примеров в интернете как например для Blue Pill. В остальном это весьма неплохой микроконтроллер особенно с учетом его невысокой цены. W801 может быть интересен не только на готовой плате но и ввиде отдельного чипа т.к. встроенной flash памяти а также ОЗУ достаточно для того чтобы не разводить дорожки для отдельной микросхемы. Поскольку память работает на большой частоте то важно делать разводку платы по правилам разводки для высокочастотных схем а также иметь некоторый опыт в данной области т.к. простые рекомендации всё таки не могут охватить весь спектр возможных проблем. По традиции, изучение нового микроконтроллера следует изучать с блинка т.е. светодиодной мигалки. Проще всего это сделать через среду Arduino IDE. Которую нужно сначала настроить. Для настройки среды Arduino, откроем её, в верхнем меню выберем "файл" -> "настройки"</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHXQ_S9U0cAMCVrgpafEkeqdhXvgFF_7e3NMan6ChrIG5VDbREoHJ9HuJgkZ07ndU2ryrNUOxr1KtHb6xeIDhykEydmsxH6pyL_IYW1A16dT5dNCACrE0xiQCd6nw9OhmF79GH9FDrR1QctZz-r_UbdIbDXIJtnzfo5Trs-iLccop6OVsKC-3P-SmHBI8/s636/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="574" data-original-width="636" height="289" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHXQ_S9U0cAMCVrgpafEkeqdhXvgFF_7e3NMan6ChrIG5VDbREoHJ9HuJgkZ07ndU2ryrNUOxr1KtHb6xeIDhykEydmsxH6pyL_IYW1A16dT5dNCACrE0xiQCd6nw9OhmF79GH9FDrR1QctZz-r_UbdIbDXIJtnzfo5Trs-iLccop6OVsKC-3P-SmHBI8/s320/10.png" width="320" /></a></div><div>далее откроется уже знакомое окно с текстовым полем для добавления ссылки</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTMtPcQabor5nJHN_0OOb0L8BY6K4a9f0rxAajXG_0oO457oQ2zELLRO4OvYynWKFN1xAGT0u2y3Wkczja4lM8CzjLGZHjJNGYmBYbyczeVlRkDf-NIuz09Hy3MA8TDBTGTea8niHwayxoaZaXFy6BRc7bSB2SqWuKUIMavuLiHgZPyQmjxKqBtXLhAV8/s812/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="480" data-original-width="812" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTMtPcQabor5nJHN_0OOb0L8BY6K4a9f0rxAajXG_0oO457oQ2zELLRO4OvYynWKFN1xAGT0u2y3Wkczja4lM8CzjLGZHjJNGYmBYbyczeVlRkDf-NIuz09Hy3MA8TDBTGTea8niHwayxoaZaXFy6BRc7bSB2SqWuKUIMavuLiHgZPyQmjxKqBtXLhAV8/s320/11.png" width="320" /></a></div><div>Если данное поле пустое то в него можно сразудобавить ссылку если нет</div><div>то нужно кликнуть по кнопке справа от данного поля и вписать одну из ссылок </div><div><a href="https://raw.githubusercontent.com/board707/w80x_arduino/hal-v0.6.0/package_w80x_index.json" rel="nofollow" style="background-color: white; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; text-underline-offset: 0.2rem;">https://raw.githubusercontent.com/board707/w80x_arduino/hal-v0.6.0/package_w80x_index.json</a></div><div><a href="https://raw.githubusercontent.com/board707/w80x_arduino/hal-v0.6.0/package_w80x_test_index.json" rel="nofollow" style="background-color: white; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; text-underline-offset: 0.2rem;">https://raw.githubusercontent.com/board707/w80x_arduino/hal-v0.6.0/package_w80x_test_index.json</a></div><div><a href="http://dl.isme.fun/w80x_arduino/package_w80x_index.json" rel="nofollow" style="background-color: white; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; text-underline-offset: 0.2rem;">http://dl.isme.fun/w80x_arduino/package_w80x_index.json</a></div><div><a href="https://raw.githubusercontent.com/board707/w80x_arduino/hal-v0.6.0/package_w80x_isme_proxy_index.json" rel="nofollow" style="background-color: white; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; text-underline-offset: 0.2rem;">https://raw.githubusercontent.com/board707/w80x_arduino/hal-v0.6.0/package_w80x_isme_proxy_index.json</a></div><p>Если ссылка не сработает то нужно будет попробовать следующую и так далее пока ссылки не закончатся.</p><p>Далее нажимаем "ок" потом снова "ок" и в верхнем меню выбираем "инструменты" -> "платы" -> "менеджер плат" </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjX4qaO1BTmJsa8SvncEogzMxseJIh4zc03kBim6CiHBDhEBJrkkqzMHY6jP4og8aCIQBSg6unvt_2L4HRxpDuXVZiRtVX4o5Tr1qUWHryOM2jg4igcXKBDNN7Uv_zWuyxktuISi6aXvemQySCRHCwOd8cgLLbtpbwn1hIYCQE81gVe2RYceV4-KsE5Ds/s632/16.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="516" data-original-width="632" height="261" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjX4qaO1BTmJsa8SvncEogzMxseJIh4zc03kBim6CiHBDhEBJrkkqzMHY6jP4og8aCIQBSg6unvt_2L4HRxpDuXVZiRtVX4o5Tr1qUWHryOM2jg4igcXKBDNN7Uv_zWuyxktuISi6aXvemQySCRHCwOd8cgLLbtpbwn1hIYCQE81gVe2RYceV4-KsE5Ds/s320/16.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdq0XYdgJknKA5EqFacdgacubrS10KQqHJU-BqK9mRZUVs6iPl9JbbSHbabpGwlEOrEzUkWQVo3tIW2JPwE-Upl8Gtd19umV1uzKv29uIZsx9pdHpTBGgu8y8HfM1IfLRt1aPpa8SjbjMPefFBqzhQeBk23CLTC8AQ0apxqnHQ9mSpH5yFbc7fXf8jvAg/s760/17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="660" data-original-width="760" height="278" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdq0XYdgJknKA5EqFacdgacubrS10KQqHJU-BqK9mRZUVs6iPl9JbbSHbabpGwlEOrEzUkWQVo3tIW2JPwE-Upl8Gtd19umV1uzKv29uIZsx9pdHpTBGgu8y8HfM1IfLRt1aPpa8SjbjMPefFBqzhQeBk23CLTC8AQ0apxqnHQ9mSpH5yFbc7fXf8jvAg/s320/17.png" width="320" /></a></div>в поисковой строке окна менеждера плат вписываем "W801"<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkxFl_XXj7b_eWM-7dF9dY5qC8yVHWK8WEC0Os7pW2a7pusuCHDtuI9GmU5UQzXCK4Lhcst5-8bxvOWoUNqRXb7ao7jM5XimYcxRu0IPO8uiv3uEnTziCJd7XP5ckkZCtJN0Ln_V_X8FUQxi851MWN-zv5j2Auq4YsPfX-WCyAofacTdYr8bSJwyTMkXg/s798/18.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="440" data-original-width="798" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkxFl_XXj7b_eWM-7dF9dY5qC8yVHWK8WEC0Os7pW2a7pusuCHDtuI9GmU5UQzXCK4Lhcst5-8bxvOWoUNqRXb7ao7jM5XimYcxRu0IPO8uiv3uEnTziCJd7XP5ckkZCtJN0Ln_V_X8FUQxi851MWN-zv5j2Auq4YsPfX-WCyAofacTdYr8bSJwyTMkXg/s320/18.png" width="320" /></a></div><div>и после того как менеджер найдет плату нужно нажать на появившуюся кнопку "установить"</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjj4zGB9M3O6icDqu1Ql_Ah03Ef9lIUDSs-VZaUyUGmLYFLqzneoEIM-92KZ9PZr4dgZvfXBGQZfbRyICaZMi_fLCeJGrpE-Ix3rsvwYopDIMfa6_ZJ0nCgxXkzLlzr6Xi6onrHXmJTbHw_wbJhWPDfloUUAFwltIhTd1n7gA9dc7BkOZBY_Uh54Nm2b2c/s798/19.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="440" data-original-width="798" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjj4zGB9M3O6icDqu1Ql_Ah03Ef9lIUDSs-VZaUyUGmLYFLqzneoEIM-92KZ9PZr4dgZvfXBGQZfbRyICaZMi_fLCeJGrpE-Ix3rsvwYopDIMfa6_ZJ0nCgxXkzLlzr6Xi6onrHXmJTbHw_wbJhWPDfloUUAFwltIhTd1n7gA9dc7BkOZBY_Uh54Nm2b2c/s320/19.png" width="320" /></a></div>После успешной установки, менеджер плат можно закрыть<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXMFnDjxLYHlkiL82Wwj4fz3JyUc1KRliR2ybhYQt3RwWo2JhWE6v_dNTwB8FEXARPHQl8cZ8C45bhTtKTGgIlN7GyzAodcq3Py35jERwTwOxXBigyRcPzT6lhEUpozlJR33ryOuQiEqf_IaHh7s6ldKeNYCjn-HnNPDvpkrsuJXRk46J-78d4p9tlJ0A/s786/21.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="442" data-original-width="786" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXMFnDjxLYHlkiL82Wwj4fz3JyUc1KRliR2ybhYQt3RwWo2JhWE6v_dNTwB8FEXARPHQl8cZ8C45bhTtKTGgIlN7GyzAodcq3Py35jERwTwOxXBigyRcPzT6lhEUpozlJR33ryOuQiEqf_IaHh7s6ldKeNYCjn-HnNPDvpkrsuJXRk46J-78d4p9tlJ0A/s320/21.png" width="320" /></a></div>Далее по вкладке инструменты выбираем установленную плату<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEJ7HRH6kuHVIwInb5_TpRi1sBUVB7pNgLAjNQGTadjJIr7BDkMOB4VqwDbj_vWVZLpCQwlgb8R08ppFGFfB_vudunmXDds0lVte44MVj9fnLlqdoQUwdu0uBZYptuafCQQ-JtXo_6LUpwIfX4C0XkPe6jLpWQF08HZ9wEmSTOVbFajzCT3X4ZHa9Bnok/s848/23.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="632" data-original-width="848" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEJ7HRH6kuHVIwInb5_TpRi1sBUVB7pNgLAjNQGTadjJIr7BDkMOB4VqwDbj_vWVZLpCQwlgb8R08ppFGFfB_vudunmXDds0lVte44MVj9fnLlqdoQUwdu0uBZYptuafCQQ-JtXo_6LUpwIfX4C0XkPe6jLpWQF08HZ9wEmSTOVbFajzCT3X4ZHa9Bnok/s320/23.png" width="320" /></a></div><div>Теперь на вкладке "файл"->"примеры"->"BasicExamples" Можно выбрать скетч блинка</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6hjGOgRxMPJrBmhG_8603gFOEHSo2ZL9VLlPpTc7krwlLvw1y_pGUPULxkiE4cTOJlvRWWQGeh-cljrJWGMX0VsSvla2cNvo2sROKiHgb0U8iJqWe-SjimY1WApJZLAOJWMUH7AD1f9JVCp1o8krDokyYeHWVTpd9cHEzF8ZHmeePjQqdWhJbj6k4Aoo/s660/24.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="604" data-original-width="660" height="293" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6hjGOgRxMPJrBmhG_8603gFOEHSo2ZL9VLlPpTc7krwlLvw1y_pGUPULxkiE4cTOJlvRWWQGeh-cljrJWGMX0VsSvla2cNvo2sROKiHgb0U8iJqWe-SjimY1WApJZLAOJWMUH7AD1f9JVCp1o8krDokyYeHWVTpd9cHEzF8ZHmeePjQqdWhJbj6k4Aoo/s320/24.png" width="320" /></a></div><div>После чего загрузить его в плату кнопкой "загрузка"</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjtZkHPaX2YYt0OC98yzqyPU81kqVhfz115vmCLNAD7QvHDnyIIbWhAH3-w8UTf2AW5txBVjWUPPCEoCewBQlXr38YKRhyphenhyphencT040Iiq-Idqyl28-cbKIBoxTSHIynfhZ1cp3gBhPNi2U_W0_nRQA_DxQgNOdjgXK4aSi_JPRkC-wJBu-0QFyCk6KqjDE4Q/s690/26.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="632" data-original-width="690" height="293" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjtZkHPaX2YYt0OC98yzqyPU81kqVhfz115vmCLNAD7QvHDnyIIbWhAH3-w8UTf2AW5txBVjWUPPCEoCewBQlXr38YKRhyphenhyphencT040Iiq-Idqyl28-cbKIBoxTSHIynfhZ1cp3gBhPNi2U_W0_nRQA_DxQgNOdjgXK4aSi_JPRkC-wJBu-0QFyCk6KqjDE4Q/s320/26.png" width="320" /></a></div><br /><div><div>Если всё прошло успешно то плата замигает зеленым светодиодом.</div><div>Поздравляю с первым блинком платы W801</div></div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/aMpO6rHb5Qc" width="320" youtube-src-id="aMpO6rHb5Qc"></iframe></div><br /><div><br /></div><div><span style="background-color: white; color: #0d0d0d; font-family: Roboto, Noto, sans-serif; font-size: 15px; white-space: pre-wrap;">Адрес биткоин кошелька для поддержки канала - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g </span><br /><div><br /><div><br /><p><br /></p></div></div></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-78078755695216223132023-11-04T10:29:00.000-07:002023-12-10T05:01:13.834-08:00Правильная настройка таймера на STM32 CMSIS режим импульса<p> Таймеры микроконтроллеров STM32 имеют функцию одиночного импульса. Эта функция похожа на обычное использование таймера с циклической выдачей импульсов но только + к этому добавляется автоматическая аппаратная остановка сразу после выдачи первого импульса. Настраивается данная функция очень просто. Для этого просто устанавливается один третий бит регистра CR1 в единицу. Однако при использовании данного режима и обычной настройке (какая была описана в статье -> <span color="rgba(0, 0, 0, 0.52)" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif" style="background-color: white; font-size: 14px;"><a href="https://electe.blogspot.com/2021/12/stm32f103c8t6-cmsis_24.html">https://electe.blogspot.com/2021/12/stm32f103c8t6-cmsis_24.html</a></span>) будет заметна такая проблема как срабатывание прерывания таймера сразу после старта микроконтроллера. Это может быть большой проблемой т.к. некоторые действия будут выполняться в незапланированный промежуток времени. Чтобы этого избежать нужно выполнить определенную последовательность действий при настройке таймера. Рассмотрим код:</p><p><textarea colls="100" readonly="readonly" rows="600" style="height: 800px; margin: 0px; width: 578px;">
#include "stm32f10x.h"
#define DELAY_MS 3000 // задержка в миллисекундах
#define DEL DELAY_MS*10
#if DEL > 65535
#error "THE DELAY IS TOO BIG"
#endif
int main(void)
{
//настройка пинов
RCC->APB2ENR |= (1<<3)|(1<<4); //включить тактирование порта B и C
GPIOB->CRH &= ~3; //настраиваем PB8 как вход
GPIOB->CRH &= ~(1<<2); //с подтяжкой
GPIOB->CRH |= 1<<3; //к плюсу или минусу
GPIOB->ODR |= 1<<8; //ставим подтяжку PB8 к плюсу
// настраиваем пин 13 порта C на выход
GPIOC->CRH |= 1<<21; //с частотой до 2МГц
GPIOC->CRH &= ~(1<<22); //и выходом push-pull
// настройка таймера 3
RCC->APB1ENR |= 1<<1; // включить тактирование таймера 3
TIM3->PSC = 7200 - 1;
TIM3->ARR = DEL - 1; // до скольки таймер 3 считает (максимум 65535)
TIM3->CNT = 1; // сбросить счетчик таймера до единицы
// OPM (one pulse mode) включить режим одного импульса
//(автоматическое отключение бита 0 регистра CR1)
TIM3->CR1 |= (1<<3);
TIM3->EGR |= 1; //для установки значений регистров таймера
TIM3->SR &= ~1; //сбрасываем бит вызова прерывания
NVIC_EnableIRQ (TIM3_IRQn); // разрешить прерывания от таймера TIM3
TIM3->DIER |= 1; // разрешить прерывания от таймера TIM3
while(1)
{
if( !(GPIOB->IDR & (1<<8)) )//проверка нажатия на кнопку
{
TIM3->CR1 |= 1; // запустить таймер
}
}
}
void TIM3_IRQHandler() //Функция обработчика прерывания таймера 3
{
TIM3->SR &= ~1;//сбрасываем бит вызова прерывания
GPIOC->ODR ^= 1<<13;//инвертировать соятояние пина 13 порта C
}
</textarea></p><p>Константой DELAY_MS задается время задержки в миллисекундах. В константу DEL записывается значение для записи в регистр для создания задержки записанной в константе DELAY_MS . Следующий макрос с конструкцией if проверяет константу на не выход за допустимый предел значения. Если предел превышен то компилятор выдаст ошибку и компиляция не произойдет. В этом случае нужно уменьшить задержку. В основной функции производиться настройка пинов. Пин PC13 (со встроенным светодиодом на плате "blue pill") настраивается на выход в режиме push-pull. Пин PB8 настраивается на вход с подтяжкой к плюсу для подключения кнопки между этим пином и землей для запуска таймера по нажатию данной кнопки. При нажатии на кнопку запускается таймер, через некоторое время (заданное константой DELAY_MS ) срабатывает прерывание и светодиод меняет свое состояние вслед за изменением своего состояния пином PC13 микроконтроллера. Правильная последовательность настройки таймера выглядит так:</p><p> 1) - записать, что надо, прерывание еще не разрешать,</p><p> 2) - установить искусственно флаг UG в регистре EGR - это обновит PSC|ARR новыми значениями.</p><p>3) - сбросить установившиеся флаги прерываний</p><p>4) - только теперь разрешить прерывания и запустить таймер.</p><p>Посмотреть работу платы blue-pill, в таком режиме, можно на видео:</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/Ws4BlNvZ0wg" width="320" youtube-src-id="Ws4BlNvZ0wg"></iframe></div><br /><p><br /></p><p> источник -> <a href="https://electronix.ru/forum/index.php?app=forums&module=forums&controller=topic&id=120406">https://electronix.ru/forum/index.php?app=forums&module=forums&controller=topic&id=120406</a></p><div><br /></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-43657614086175180752023-10-20T08:59:00.000-07:002023-10-20T08:59:08.614-07:00WIFI кораблик<p>Популярные и недорогие WIFI модули ESP8266 можно использовать для создания, на их основе, системы дистанционного управления например самодельными кораблями. В качестве пульта управления для такого корабля можно использовать например смартфон. Дистанционно управляемый кораблик, также как и дистанционно управляемый танк, может иметь простую в реализации систему поворота путем создания ассиметричной тяги с разных сторон т.е. например если мотор с левой стороны корабля работает быстрее чем мотор с правой то корабль поворачивает на право. Если правый быстрее то налево. Моторы можно расположить по бокам и приделать к ним гребные винты или колеса. Т.к. моторы будут работать по воде а не по суше то их не обязательно дополнять редукторами для усиления и снижения оборотов. Для управления моторами можно использовать драйвер l293d или аналогичный (мотор, при этом, должен соответствовать драйверу по мощности т.е. не потреблять больше тока и напряжения чем может выдать драйвер) чтобы драйвер не сгорел. Проверить потребление тока мотором можно соединив его последовательно с амперметром и источником питания. Для драйвера L293D этот ток должен быть не боле 600мА. Для того чтобы WIFI модуль ESP8266 работал без перебоев (например при возникновении большой нагрузки на моторы) и не сгорел при превышении напряжения питания, питание на него следует подавать со стабилизатора напряжения с конденсатором для сглаживания пиков просадки и скачков напряжения. Электрическая схема кораблика, с данным модулем, будет не сложная:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKPieM29CAbxDcdcRx3rSznjlnLVloIgjIIVG_LK1HFteBZRu9Sq6O0mTlPlq3aLWH6_3Q4ioGgrU9lo3a-Q7VogAumNlVi8C4omIQJKV2OVkdTz-4P5cIvC7PnftUzt6zsO6oGBHOyX1XnhkYavMv_QTD_3vI1fd2gW_PZfAASh3tMwqfiENGD_-WKBI/s644/cxema.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="540" data-original-width="644" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKPieM29CAbxDcdcRx3rSznjlnLVloIgjIIVG_LK1HFteBZRu9Sq6O0mTlPlq3aLWH6_3Q4ioGgrU9lo3a-Q7VogAumNlVi8C4omIQJKV2OVkdTz-4P5cIvC7PnftUzt6zsO6oGBHOyX1XnhkYavMv_QTD_3vI1fd2gW_PZfAASh3tMwqfiENGD_-WKBI/w380-h318/cxema.png" width="380" /></a></div><p style="text-align: center;"><span> </span>Рисунок 1 - Схема WIFI кораблика</p><p style="text-align: left;">Скетчь также не сложный, он был сделан на основе заготовки описанной в статье <span color="rgba(0, 0, 0, 0.52)" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif" style="background-color: white; font-size: 14px;"><a href="https://electe.blogspot.com/2023/08/esp8266-esp32.html">https://electe.blogspot.com/2023/08/esp8266-esp32.html</a></span></p><p> <textarea colls="100" readonly="readonly" rows="800" style="height: 1800px; margin: 0px; width: 578px;">
#include <ESPAsyncWebServer.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#define LED_PIN1 4 // пин первый выхода
#define LED_PIN2 5 // пин второй выхода
#define LED_PIN3 14
#define LED_PIN4 16
const char* ssid = "id";
const char *password = "987654321"; // пароль обязательно должен быть длиннее 8ми символов
// Настройки IP адреса
IPAddress local_IP(192,168,4,22);
IPAddress gateway(192,168,4,9);
IPAddress subnet(255,255,255,0);
// Создаем объект AsyncWebServer на порту 80
AsyncWebServer server(80);
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta charset="Windows-1251">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0" /><meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\
<style>
body{overflow:hidden;width:100%;}
.stk{transform: rotate(-90deg);} /*минус-чтобы начало было внизу*/
.stk::-webkit-slider-thumb{padding:20px;}
.stk::-moz-range-thumb{padding:20px;} /*for Firefox*/
.body{padding:45px;display:flex;flex-direction:row;align-items:center;}
</style>
</head>
<body>
<div class="body">
<div>
<input class="stk" type="range" id="stick1" min="-255" max="255" value="0">
<div id="stick1_out"></div>
</div>
<div >
<input class="stk" type="range" id="stick2" min="-255" max="255" value="0">
<div id="stick2_out"></div>
</div>
</div>
<script>
var stick1 = document.getElementById('stick1');
stick1.addEventListener('input',function (){
document.getElementById('stick1_out').innerHTML = stick1.value;
fetch('/update?stick1='+stick1.value);
});
stick1.addEventListener('change',function (){
stick1.value = 0;
document.getElementById('stick1_out').innerHTML = stick1.value;
fetch('/update?stick1=0');
});
var stick2 = document.getElementById('stick2');
stick2.addEventListener('input',function (){
document.getElementById('stick2_out').innerHTML = stick2.value;
fetch('/update?stick2='+stick2.value);
});
stick2.addEventListener('change',function (){
stick2.value = 0;
document.getElementById('stick2_out').innerHTML = stick2.value;
fetch('/update?stick2=0');
});
</script>
</body>
</html>
)rawliteral";
void setup()
{
Serial.begin(115200);
// настройка пинов на выход
pinMode(LED_PIN1, OUTPUT);
pinMode(LED_PIN2, OUTPUT);
pinMode(LED_PIN3, OUTPUT);
pinMode(LED_PIN4, OUTPUT);
// для точки доступа
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ssid, password);
//WiFi.softAP(ssid);
delay(100);
// для обычного Wi-Fi
//WiFi.begin(ssid, password);
//while (WiFi.status() != WL_CONNECTED)
//{
// delay(1000);
// Serial.println("Connecting to WiFi..");
//}
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Распечатать локальный IP-адрес
//Serial.println(WiFi.localIP());
// Маршрут для корневой / веб-страницы
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/html", index_html);
});
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request)
{
String stick1;
int stick1_val=0;
String stick2;
int stick2_val=0;
if(request->hasParam("stick1"))
{
stick1 = request->getParam("stick1")->value();
stick1_val = stick1.toInt();
if(stick1_val>0){analogWrite(LED_PIN1,stick1_val);}
else if(stick1_val<0){analogWrite(LED_PIN3,-stick1_val);}
else{analogWrite(LED_PIN1,0);analogWrite(LED_PIN3,0);}
}
if(request->hasParam("stick2"))
{
stick2 = request->getParam("stick2")->value();
stick2_val = stick2.toInt();
if(stick2_val>0){analogWrite(LED_PIN2,stick2_val);}
else if(stick2_val<0){analogWrite(LED_PIN4,-stick2_val);}
else{analogWrite(LED_PIN2,0);analogWrite(LED_PIN4,0);}
}
request->send(200, "application/json", "");
});
// Запустить сервер
server.begin();
}
void loop(){}
</textarea> </p><p>Для того чтобы было удобнее управлять корабликом, применены специальные стили поворачивающие ползунки вертикально. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDonffHbVgqCaYsWhMSnDzO4dYfqqSkAU3_mMNTsJaKklvfQelrBqJ5ezQpg9VKOcZRiSqhvCcDB_ugHUvQ5Gm8BIhyW2CM3hYx6tLbS1G7jvsAVnS5PW-y9abGuvaSKPNn-haw__U-yv8Y1W_ZyQAWvWQ1ZiDHHQSdIaBWoqgOEX600Jl2R2_OwkzwBg/s3840/IMG_20230726_173525.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3840" data-original-width="2160" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDonffHbVgqCaYsWhMSnDzO4dYfqqSkAU3_mMNTsJaKklvfQelrBqJ5ezQpg9VKOcZRiSqhvCcDB_ugHUvQ5Gm8BIhyW2CM3hYx6tLbS1G7jvsAVnS5PW-y9abGuvaSKPNn-haw__U-yv8Y1W_ZyQAWvWQ1ZiDHHQSdIaBWoqgOEX600Jl2R2_OwkzwBg/s320/IMG_20230726_173525.jpg" width="180" /></a></div>Один ползунок управляет одним мотором, второй ползунок управляет вторым мотором. Если ползунки не трогать то они находятся по середине и моторы не крутятся. Поднятием ползунка можно крутить мотор в одну сторону со скоростью пропорциональной уровню поднятия. Опусканием крутить в другую сторону со скоростью пропорциональной уровню отпускания. Простого способа сделать один джойстик для двух двигателей стандартными средствами HTML найти не получилось по этому пришлось использовать два ползунка. Так HTML код получается коротким, при желании можно модифицировать код и сделать более удобный орган управления. Однако ползунки всё таки поддерживаются многими браузерами а также работают на смартфонах и для их использования не надо писать много кода поэтому были выбраны они. В скетче есть закомментированные строки для случая если например понадобиться (зачем то) управлять корабликом через роутер, есть строка для задания идентификатора точки доступа без задания пароля а также строка для вывода ip адреса в последовательный порт. IP адрес задается в строке <p></p><p>IPAddress local_IP(192,168,4,22);</p><p>После того как скетч загружен в модуль (о том как это сделать (загрузить скетч в модуль (а также настроить среду разработки Arduino IDE)) см. первую статью о модуле ESP8266 <span color="rgba(0, 0, 0, 0.52)" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif" style="background-color: white; font-size: 14px;"><a href="https://electe.blogspot.com/2023/06/esp8266-arduino-ide.html">https://electe.blogspot.com/2023/06/esp8266-arduino-ide.html</a></span>) можно подать питание на модуль (3.3В (если это не специальная плата с модулем и специальным стабилизатором для него и выводом для подачи другого напряжения)) и после того как WIFI сервер, на этом модуле, запуститься (что, как правило, происходит быстро) можно будет включить поиск WIFI точек на смартфоне и найти точку с идентификатором который был задан в строке </p><p> const char* ssid = "id";</p><p>Вместо id можно задать другое название например THE_GREAT_BOT или что в этом роде. Далее чтобы подключиться к точке доступа нужно будет ввести пароль который был указан в строке:</p><p>const char *password = "987654321"; // пароль обязательно должен быть длиннее 8ми символов</p><p>пароль обязательно должен быть длиннее 8ми символов т.к. иначе программа не заработает. Пароль можно сделать посложнее чтобы потенциальным злоумышленника было сложнее угнать великолепный самодельный игрушечный радиоуправляемый по WIFI кораблик. Далее для доступа к кораблику нужно открыть браузер на смартфоне и ввести в его адресной строке IP адрес кораблика который был указан в строке:</p><p>
</p><p> IPAddress local_IP(192,168,4,22);</p><p>Номер порта можно не указывать если он 80. После подключения к кораблику должен появиться незамысловатый интерфейс с двумя ползунками для управления. Подробнее про работу скетча можно узнать статьи о шаблоне для ESP8266 по ссылке наверху данной страницы. Тест кораблика можно посмотреть на видео:</p><p></p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/s5pbs-mReO8" width="320" youtube-src-id="s5pbs-mReO8"></iframe></div><br />testtesttesttestvideohere<p></p><div><span>Тема радиоуправления является весьма интересной и востребованной однако существует множество других интересных и востребованных тем связанных с электроникой, затронутых в данном блоге. Это например: машинное зрение, бесколлекторные моторы и их контроллеры, сварочные аппараты, аппарат наплавления, 3д сканер, 3д принтер и д.р. ЧПУ станки, всевозможные роботы (напр. BEAM роботы или роботы на ардуине, распберри и т.д.), уроки по микроконтроллерам, металлоискатель, гаусган, бесплатная энергия, всевозможные варианты дистанционного управления по средствам звука, света и т.д. и многое другое. Принять участие в выборе приоритетной темы предлагается посредством криптодонатинга. Для этого нужно прислать некоторое количество сатошей по адресу:</span></div><div><br /></div><div>bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g</div><div><br /></div><div>После чего прислать название темы + номер транзакции или номер отправителя например в сообщении к данной статье, в комментарии на ютуб канале или на почту: freedms@yandex.ru. Можно также, для этого, воспользоваться группой вконтакте или сообщением. Те темы которые будут набирать больше сатошей будут более приоритетными. Темы естественно д.б. по тематике блога и реально реализуемыми т.е. например космический корабль вряд ли будет реализован, в рамках данного блога, максимум что можно сделать по теме космического корабля - это словесно описать то как его можно было бы реализовать при наличии достаточного количества ресурсов и др. возможностей по его созданию. Проверка самодельного космического корабля на практике естественно не будет осуществлена, если конечно для этого не будет прислано достаточно сатошей в донате. Сатоши могут быть добыты различными способами. Например обменяны на другие валюты или товары, намайнены или добыты с биткоин кранов. Хотя последний вариант наименее прибыльный из всех, всё же он может сработать если желающих делать донаты будет мало или не будет совсем т.к. даже одна сатоша больше чем ноль сатош. При использовании биткоина важно соблюдать меры предосторожности. По возможности использовать холодные кошелки и ни в коем случае не забывать пароль от кошелька (лучше записать его на некотором количестве разных мест) т.к. надежность криптоалгоритмов биткоина очень высока.</div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-34486397761516700802023-09-20T11:36:00.000-07:002023-09-20T11:36:02.823-07:00Радиоуправление на транзисторах<p> Существует множество способов дистанционного управления различными электронными устройствами. Например это может быть управление светом, звуком, длинным проводом, радиоволнами а также есть другие способы. Использование радиоволн примечательно тем что для их создания и поглощения используются кусочки проволоки определенных форм и размеров которые можно например скрутить руками самостоятельно (чтобы тоже самое сделать для света нужны очень маленькие руки но это отдельная большая и сложная тема). Эти кусочки проволоки называются антеннами. Чтобы антенна могла излучать и принимать радиоволны, и это приносило бы пользу, нужно дополнить её дополнительными деталями т.е. сделать передатчик и приемник. Сделать данные устройства можно на специальных микросхемах или если их нет то можно даже на распространенных высокочастотных транзисторах общего назначения например на КТ315. Граничная частота коэффициента передачи тока, в схеме с общим эмиттером, для данного транзистора составляет 250 МГц. Данная частота может использоваться для радиоуправления но желательно делать некоторый запас в меньшую сторону. На рисунке ниже приведена схема двухканального приемника системы радиоуправления:</p><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZluGVV1jo380IOu9ZIcxhAw4UUQ7_naBMQhjd8-FTXv2Le7YhVOQ8-Fdof68jCpiIIj_dT4rwcaoaa4jmNK2-oPkIQuCq0DRDp4-6u4Az57Mmc9LocXxzf4y5t1Efc9q_wEkYb107Iq01GNaatHwmldKqjR3i7WlJfRDCm3ERIFkXvO4qaQ7wnkZ8xX0/s1001/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D0%BF%D1%80%D0%B8%D0%B5%D0%BC%D0%BD%D0%B8%D0%BA%D0%B0%20%D1%81%D0%BE%20%D1%81%D0%B2%D0%B5%D1%80%D1%85%D1%80%D0%B5%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%BC%20%D0%BA%D0%B0%D1%81%D0%BA%D0%B0%D0%B4%D0%BE%D0%BC.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="640" data-original-width="1001" height="301" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZluGVV1jo380IOu9ZIcxhAw4UUQ7_naBMQhjd8-FTXv2Le7YhVOQ8-Fdof68jCpiIIj_dT4rwcaoaa4jmNK2-oPkIQuCq0DRDp4-6u4Az57Mmc9LocXxzf4y5t1Efc9q_wEkYb107Iq01GNaatHwmldKqjR3i7WlJfRDCm3ERIFkXvO4qaQ7wnkZ8xX0/w469-h301/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D0%BF%D1%80%D0%B8%D0%B5%D0%BC%D0%BD%D0%B8%D0%BA%D0%B0%20%D1%81%D0%BE%20%D1%81%D0%B2%D0%B5%D1%80%D1%85%D1%80%D0%B5%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%BC%20%D0%BA%D0%B0%D1%81%D0%BA%D0%B0%D0%B4%D0%BE%D0%BC.png" width="469" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Приемник двухканальной системы радиоуправления</div><br /><p></p><p>Приемник имеет всего два канала, это не очень много но зато такой приемник гораздо проще изготовить без использования микросхем на одних транзисторах чем приемник с большим количеством каналов. Если нужно например 4 канала то можно сделать например два таких приемника настроенными на разные радиочастоты чтобы они друг другу не мешали. Для радиоприема в схеме имеется сверхрегенеративный каскад на транзисторе кт315. Радиочастота каскада с данными деталями не очень высокая. Для эффективного приема нужна длинная антенна. Однако практика показывает что при хорошей настройке контура, приемник может работать и с антенной длинной около 40 См. Энергия от антенны в высокочастотный контур передается через трансформатор с катушками L1 и L2. Катушка L1 располагается внутри катушки L2 и имеет больше витков, в два раза. Так, на практике, получилось добиться большей чувствительности приемника чем в случаях с одинаковым числом витков и большим на вторичной обмотке. Возможно (и скорее всего да) это не оптимальный вариант и можно экспериментальным путем добиться лучших результатов. Каскад настраивается потенциометром R2 по пьезодинамику подключенному на выход усилителя низких частот как показано на рисунке:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9GBRtBjLoW57QWzBxfjbTr39WjA16cVJ2N54j7aT_re7EDLW682HQLFCCmJc9SJwiTI08PVV3U7Wk-Oz8LLUnqV_SaxOpMDfCb9ncSRtyWiSn6_2_Pvy3K6KFILPjl_uPm7EI9SpFJvhh-wom7dbg297zsOEMh7PuWOsbcIVAt1ejbwIGXxanWX2uTwc/s1001/%D0%BF%D1%8A%D0%B5%D0%B7%D0%BE.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="640" data-original-width="1001" height="282" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9GBRtBjLoW57QWzBxfjbTr39WjA16cVJ2N54j7aT_re7EDLW682HQLFCCmJc9SJwiTI08PVV3U7Wk-Oz8LLUnqV_SaxOpMDfCb9ncSRtyWiSn6_2_Pvy3K6KFILPjl_uPm7EI9SpFJvhh-wom7dbg297zsOEMh7PuWOsbcIVAt1ejbwIGXxanWX2uTwc/w440-h282/%D0%BF%D1%8A%D0%B5%D0%B7%D0%BE.png" width="440" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 2 - Настройка РЧ каскада</div><div><br /></div>Резистор R1 нужен для защиты базы транзистора VT1 на случай установки потенциометра в крайнее положение при котором он мог бы соединить базу с плюсом питания. Данным потенциометром можно установить режим при котором в пьезодинамике слышен треск и режим при котором его не слышно. Потенциометром нужно установить уровень посередине между этими режимами со слабым треском или так чтобы он возникал при касании потенциометра тогда данная схема будет нормально работать. Если треск будет постоянным и большим то импульсы будут проходить через LC фильтр для сепарации полезных НЧ сигналов и запускать мотор в отсутствии полезного сигнала с приемника. Если приемник будет в глубоком режиме без треска то он будет слабо усиливать. Настраивать радиочастоту приемника можно растяжением и сжатием витков катушки L2 или же можно её оставить как есть и настраивать, таким же образов, контур передатчика на частоту приемника. Делать это можно также по звуку с пьезодинамика. Можно также использовать осциллограф для этого. Можно даже не собирать всю схему целиком для настройки приемника а после его настройки собрать низкочастотную часть после усилителя низких частот на транзисторах VT2, VT3. В усилителе низких частот на транзисторах VT2, VT3 возможно понадобиться подобрать сопротивления резисторов R5, R7 т.к. коэффициенты усиления по току у разных транзистором могут сильно различаться. Желательно добиться на коллекторах данных транзисторов напряжений близких к половине питания для того чтобы усиливаемые сигналы меньше ограничивались. После усилителя низких частот на транзисторах VT2, VT3 находится два низкочастотных LC фильтра. Фильтр на C3 L4 нужен для пропускания полезного высокочастотного сигнала и ослабления полезного низкочастотного сигнала. Фильтр на L5 C10 нужен для пропускания полезного низкочастотного сигнала и ослабления полезного высокочастотного сигнала. LC фильтры выбраны по тому что они проще активных и цифровых фильтров и сепарируют сигналы лучше чем пассивные RC фильтры. Можно сказать что LC фильтры имеют простоту RC фильтров и хорошую сепарацию как у активных фильтров. Недостаток LC фильтров в том что они имеют большой вес и габариты из за катушек которые должны иметь большую индуктивность и большое количество витков. По этой причине данные фильтры трудно изготовить т.к. нужно сделать много витков тонким проводом. Однако практика показала что можно использовать готовые катушки которые например использовались в старых часах или можно использовать например обмотки относительно высоковольтных ( напр. 24В со стороны обмотки) реле т.е. данные обмотки имеют большое количество витков.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6CioelzHndAZdFcJQ2TJg30U_qJRTLBuq6nbPrWsviXwXhnyaBzvUDmzL_UM7FtAkpF7riwOAaZVXKeNZI8b_hAfwi8aezZWsDoZBNNgs1Fo6v9Zkf22uI_al-tUynLuQYoNwUkwj0JFfzB27J2oyM9NpryMiNCYmztCr9BfGkSF3J-DspKjWWMcmGsw/s3840/IMG_20230903_152616.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2160" data-original-width="3840" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6CioelzHndAZdFcJQ2TJg30U_qJRTLBuq6nbPrWsviXwXhnyaBzvUDmzL_UM7FtAkpF7riwOAaZVXKeNZI8b_hAfwi8aezZWsDoZBNNgs1Fo6v9Zkf22uI_al-tUynLuQYoNwUkwj0JFfzB27J2oyM9NpryMiNCYmztCr9BfGkSF3J-DspKjWWMcmGsw/s320/IMG_20230903_152616.jpg" width="320" /></a></div><div> На выходе каждого LC фильтра стоит усилитель-детектор который выпрямляет и усиливает полезный сигнал. Далее стоит накопитель на конденсаторе. А после ещё один усилитель и эмиттерный повторитель для управления двигателем. Транзисторы в эмиттерном повторителе д.б. желательно комплементарные и достаточно мощные для управления двигателем. Поэтому Вместо кт315 и кт361 (у которого ток коллектора всего 50мА) поставлены более мощные комплементарные 2SC815 и 2SC539 (с током коллектора 200мА) которые часто встречаются в старой импортной аппаратуре. Вместо них можно также использовать любые другие подходящие по параметрам транзисторы.<div><span> Схема передатчика приведена на рисунке:</span><br /></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguACUPz_ZxxsGP0mfufZyN4H23AjZcvypObphZU1U2Y1XPVl2iP5ZJvHSzhsFjGksH9B403q2GS4Np22ySPXhlVkH7SvUmhLrPS5CIzZADBMqZAXbw1JBSRJGOHAyvAvaDK2_TkJndgel0LFFYL4NO6lJFU07BzYl3V24poTCHJKMQEdDhCqG0I35eKgI/s523/%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%82%D1%87%D0%B8%D0%BA.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="523" data-original-width="383" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguACUPz_ZxxsGP0mfufZyN4H23AjZcvypObphZU1U2Y1XPVl2iP5ZJvHSzhsFjGksH9B403q2GS4Np22ySPXhlVkH7SvUmhLrPS5CIzZADBMqZAXbw1JBSRJGOHAyvAvaDK2_TkJndgel0LFFYL4NO6lJFU07BzYl3V24poTCHJKMQEdDhCqG0I35eKgI/s320/%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%82%D1%87%D0%B8%D0%BA.png" width="234" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 3 - Схема передатчика двухканальной системы радиоуправления</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div><div><span> Передатчик будет эффективнее с двухтактным генератором чем с однотактным за счет большей мощности, для создания полезного сигнала которым будет модулироваться ВЧ сигнал, используется простой релаксационный генератор на двух транзисторах. При нажатии на кнопку S2 генератор полезного сигнала будет работать с конденсатором C5 на высокой частоте и этой частотой модулирует основной ВЧ генератор. При нажатии на кнопку S1 к конденсатору C5 параллельно подключается конденсатор C4 с большой ёмкостью и генератор полезного сигнала работает на низкой частоте. Т.о. при нажатии на одну кнопку мотор будет крутиться в одну сторону, при нажатии на другую кнопку мотор будет крутиться в другую сторону. Если нажать сразу на две кнопки то генератор полезного сигнала будет работать на низкой частоте и мотор будет крутиться в соответствующую, этой частоте, сторону. Антенна передатчика д.б. таких же размеров как и антенна приемника. Катушки L1 и L2 являются одной катушкой с отводом от середины. Передатчик настраивается на частоту приемника путем растяжения и сжатия витков данной катушки.</span><br /></div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjColoeeTa4mWaBIeo1DxbstJ1laDSigqlhJzoLUr15zE0vBzHHcsm2JpfoCOCC4HbCHjzlhVy_xBotEVfmSKrHg-J_ubVUkJUQVhSpwp1viBNAFJtLPEkukXOw5rN4Q-D7rC9WBrLDonioqscBhIzZvUQUZ9CAZglM_6VxFHdtWeV3KKlMCN-PxkRDEIQ/s3840/IMG_20230903_152624.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2160" data-original-width="3840" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjColoeeTa4mWaBIeo1DxbstJ1laDSigqlhJzoLUr15zE0vBzHHcsm2JpfoCOCC4HbCHjzlhVy_xBotEVfmSKrHg-J_ubVUkJUQVhSpwp1viBNAFJtLPEkukXOw5rN4Q-D7rC9WBrLDonioqscBhIzZvUQUZ9CAZglM_6VxFHdtWeV3KKlMCN-PxkRDEIQ/s320/IMG_20230903_152624.jpg" width="320" /></a></div>Запитать передатчик можно например от 4х никель метал гидридных аккумуляторов. Напряжение от них будет близко к 5В. Слишком высоким напряжением запитывать передатчик не надо т.к. ВЧ генератор легко может сгореть. Приемник можно запитать от двух литий полимерных аккумуляторов с общим напряжением около 7.4В. Для нормальной работы сверхрегенеративного каскада приемника желательно стабильное напряжение питания и аккумулятор можно дополнить стабилизатором но если аккумулятор достаточно мощный а мотор нет то возможно что аккумулятор будет хорошо держать напряжение и стабилизатор не понадобиться. По крайней мере с таким аккумулятором собранный радиоуправляемый автомобиль работал. Тест собранного радиоуправляемого автомобиля можно увидеть на видео:</div><div>vvvvvvvvvv</div><div>Можно заметить что иногда радиоуправляемый автомобиль дергается при движении в одну сторону. Возможно что это вызвано тем что антенна, при движении, качается а так как радиосигнал поляризованный то сигнал принимается слабее при наклоне антенны. Для лучшего приема с такими антеннами желательно чтобы они обе располагались вертикально относительно поверхности земли. Также было замечено что пъезодинамик приемника начинает звучать на гораздо большем расстоянии от передатчика чем мотор начинает крутиться. Мотор крутиться когда пульт находиться близко к приемнику. Это свидетельствует о том что ВЧ каскад и каскады усиления звуковой частоты работают хорошо но сигналы после частотной сепарации усиливаются недостаточно хорошо. Схему можно пробовать дорабатывать, возможно получиться увеличить усиление путем подбора номиналов или внесения изменений в схему, например добавлением транзисторов. Однако даже без этого получилась неплохая игрушка на транзисторах.</div><div><span> Тема радиоуправления является весьма интересной и востребованной однако существует множество других интересных и востребованных тем связанных с электроникой, затронутых в данном блоге. Это например: машинное зрение, бесколлекторные моторы и их контроллеры, сварочные аппараты, аппарат наплавления, 3д сканер, 3д принтер и д.р. ЧПУ станки, всевозможные роботы (напр. BEAM роботы или роботы на ардуине, распберри и т.д.), уроки по микроконтроллерам, металлоискатель, гаусган, бесплатная энергия, всевозможные варианты дистанционного управления по средствам звука, света и т.д. и многое другое. Принять участие в выборе приоритетной темы предлагается посредством криптодонатинга. Для этого нужно прислать некоторое количество сатошей по адресу:</span></div><div><br /></div><div>bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g</div><div><br /></div><div>После чего прислать название темы + номер транзакции или номер отправителя например в сообщении к данной статье, в комментарии на ютуб канале или на почту: freedms@yandex.ru. Можно также, для этого, воспользоваться группой вконтакте или сообщением. Те темы которые будут набирать больше сатошей будут более приоритетными. Темы естественно д.б. по тематике блога и реально реализуемыми т.е. например космический корабль вряд ли будет реализован, в рамках данного блога, максимум что можно сделать по теме космического корабля - это словесно описать то как его можно было бы реализовать при наличии достаточного количества ресурсов и др. возможностей по его созданию. Проверка самодельного космического корабля на практике естественно не будет осуществлена, если конечно для этого не будет прислано достаточно сатошей в донате. Сатоши могут быть добыты различными способами. Например обменяны на другие валюты или товары, намайнены или добыты с биткоин кранов. Хотя последний вариант наименее прибыльный из всех, всё же он может сработать если желающих делать донаты будет мало или не будет совсем т.к. даже одна сатоша больше чем ноль сатош. При использовании биткоина важно соблюдать меры предосторожности. По возможности использовать холодные кошелки и ни в коем случае не забывать пароль от кошелька (лучше записать его на некотором количестве разных мест) т.к. надежность криптоалгоритмов биткоина очень высока.<br /></div></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-7778440287187329592023-08-20T09:41:00.000-07:002023-08-20T09:41:33.775-07:00Заготовка асинхронный сервер на ESP8266, ESP32<p> WIFI модуль <a href="https://alii.pub/6oyjt1" target="_blank">ESP8266</a> может использоваться во множестве различных проектов. При написании кода для загрузки в данный модуль, можно заметить что некоторые его части пишутся повторно а также то что их приходиться вспоминать или искать какой нибудь пример. Поскольку основной и наиболее интересной функцией у данного модуля является WIFI то чаще всего он будет использоваться именно из за наличия этой функции для её использования поэтому наиболее полезным примером программы, для данного модуля, будет программа использующая WIFI. В качестве примера можно реализовать например устройство пропорционального управления двумя светодиодами и получения от модуля двух значений в асинхронном режиме т.е. без перезагрузки страницы с клиентской частью приложения для управления этим устройством. Асинхронный способ будет более быстрый и удобный чем синхронный и поэтому он более предпочтителен а также для его реализации существует специальная библиотека. Светодиоды к модулю можно припаять например к пинам GPIO4 и GPIO5 (которые скорее всего будут указаны на плате) через резисторы с сопротивлением 560 Ом.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghdLck90P_ycopKa-Sa53GSCT6aFLK69bL3f0l4rdq3JkQsZsS6AA5GqCmtDToQjqdueX1npXZrwK5cDCgTv7803nXDthFW-eun7HXnt7N7e9J3IT6YNb1tiR7aCyL-UJY6SZF68rPA4IuyJRCA5wCxC6ns3lclQN5mvsj4OdnZ78Wn-4uN-Y2Wxr-/s2728/IMG_20230501_140734_2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1840" data-original-width="2728" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghdLck90P_ycopKa-Sa53GSCT6aFLK69bL3f0l4rdq3JkQsZsS6AA5GqCmtDToQjqdueX1npXZrwK5cDCgTv7803nXDthFW-eun7HXnt7N7e9J3IT6YNb1tiR7aCyL-UJY6SZF68rPA4IuyJRCA5wCxC6ns3lclQN5mvsj4OdnZ78Wn-4uN-Y2Wxr-/s320/IMG_20230501_140734_2.png" width="320" /></a></div>Меньше, сопротивление этих резисторов, делать не надо т.к. нагрузочная способность пинов низка и они могут перегореть если, в достаточной мере, не ограничить ток. <div><textarea colls="100" readonly="readonly" rows="800" style="height: 1800px; margin: 0px; width: 578px;">
#include <ESPAsyncWebServer.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#define LED_PIN1 4 // пин первый выхода
#define LED_PIN2 5 // пин второй выхода
const char* ssid = "id";
const char *password = "987654321"; // пароль обязательно должен быть длиннее 8ми символов
// Настройки IP адреса
IPAddress local_IP(192,168,4,22);
IPAddress gateway(192,168,4,9);
IPAddress subnet(255,255,255,0);
// Создаем объект AsyncWebServer на порту 80
AsyncWebServer server(80);
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
</head>
<body>
<input id="led1"><br>
<input id="led2"><br>
<button id="btn">btn</button><br>
<div id="report1">report1</div>
<div id="report2">report2</div>
<script>
var btn = document.getElementById('btn');
var led1 = document.getElementById('led1');
var led2 = document.getElementById('led2');
var report1 = document.getElementById('report1');
var report2 = document.getElementById('report2');
btn.addEventListener('click',function(){
var req = '/update?led1='+led1.value+'&led2='+led2.value;
fetch(req)
.then(function(response){return response.json();})
.then(function(result){
report1.innerHTML = 'report1='+result.report1;
report2.innerHTML = 'report2='+result.report2;
});
});
</script>
</body>
</html>
)rawliteral";
void setup()
{
Serial.begin(115200);
// настройка пинов на выход
pinMode(LED_PIN1, OUTPUT);
pinMode(LED_PIN2, OUTPUT);
// для точки доступа
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ssid, password);
//WiFi.softAP(ssid);
delay(100);
// для обычного Wi-Fi
//WiFi.begin(ssid, password);
//while (WiFi.status() != WL_CONNECTED)
//{
// delay(1000);
// Serial.println("Connecting to WiFi..");
//}
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Распечатать локальный IP-адрес
//Serial.println(WiFi.localIP());
// Маршрут для корневой / веб-страницы
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/html", index_html);
});
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request)
{
String led1;
int led1_val=0;
String led2;
int led2_val=0;
String response = ""; // ответ клиенту
int report1 = 10; // выдать клиенту значение 1
int report2 = 20; // выдать клиенту значение 2
if(request->hasParam("led1"))
{
led1 = request->getParam("led1")->value();
led1_val = led1.toInt();
analogWrite(LED_PIN1,led1_val);
}
if(request->hasParam("led2"))
{
led2 = request->getParam("led2")->value();
led2_val = led2.toInt();
analogWrite(LED_PIN2,led2_val);
}
response += "{ \"report1\": "+String(report1);
response += ",\"report2\": "+String(report2)+" }";
request->send(200, "application/json", response);
});
// Запустить сервер
server.begin();
}
void loop(){}
</textarea></div><div>В начале скетча подключаются необходимые заголовочные файлы с конструкцией, для препроцессора, которая выберет файлы в зависимости от того какой модуль программируется ESP8266 или ESP32:</div><div><textarea colls="100" readonly="readonly" rows="40" style="height: 250px; margin: 0px; width: 578px;">
#include <ESPAsyncWebServer.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#define LED_PIN1 4 // пин первый выхода
#define LED_PIN2 5 // пин второй выхода
</textarea></div><div>Ниже нужно ввести иденитификатор, пароль и настройки WIFI сети. Если модуль будет работать в режиме точки доступа то можно придумать любой идентификатор (который будет отображаться как название WIFI сети при поиске её на смартфоне) и пароль который должен быть не короче 8ми символов, иначе идентификатор и пароль установяться по умолчанию. После того как сеть была найдена и к ней успешно произведено подключение по паролю, можно зайти в браузер и ввести IP адрес такой же какой установлен в скетче и попасть на страницу управления модулем. IP адрес указан в переменной local_IP. Чтобы помимо IP адреса, не было необходимости вводить ещё и номер порта, можно создать объект AsyncWebServer на порту 80:</div><div><textarea colls="100" readonly="readonly" rows="40" style="height: 250px; margin: 0px; width: 578px;">
const char* ssid = "id";
const char *password = "987654321"; // пароль обязательно должен быть длиннее 8ми символов
// Настройки IP адреса
IPAddress local_IP(192,168,4,22);
IPAddress gateway(192,168,4,9);
IPAddress subnet(255,255,255,0);
// Создаем объект AsyncWebServer на порту 80
AsyncWebServer server(80);
</textarea></div><div>В следующей конструкции вводиться html код web страницы:</div><div><textarea colls="100" readonly="readonly" rows="40" style="height: 520px; margin: 0px; width: 578px;">
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
</head>
<body>
<input id="led1"><br>
<input id="led2"><br>
<button id="btn">btn</button><br>
<div id="report1">report1</div>
<div id="report2">report2</div>
<script>
var btn = document.getElementById('btn');
var led1 = document.getElementById('led1');
var led2 = document.getElementById('led2');
var report1 = document.getElementById('report1');
var report2 = document.getElementById('report2');
btn.addEventListener('click',function(){
var req = '/update?led1='+led1.value+'&led2='+led2.value;
fetch(req)
.then(function(response){return response.json();})
.then(function(result){
report1.innerHTML = 'report1='+result.report1;
report2.innerHTML = 'report2='+result.report2;
});
});
</script>
</body>
</html>
)rawliteral";
</textarea></div><div>Данная конструкция является самым удобным способом вставить html код в ардуино скетч. В тэге body есть два поля ввода яркостей светодиодов. Яркости имеют диапазон от 0 до 255 т.е. диапазон ШИМа. Можно сделать ограничение на стороне клиента т.е. в html коде или на стороне сервера. Для простого шаблона это не принципиально важно поэтому тут его просто нет. Также есть кнопка для передачи и приема данных от модуля + два блока для вывода информации полученной от модуля. В тэге script происходит получение доступа к данным элементам интерфейса по их идентификаторам указанным в атрибутах id. На кнопку добавлен обработчик клика в котором создается GET запрос с яркостями светодиодов, полученных из текстовых полей. Данный GET запрос передается в функцию fetch которая также принимает ответ от сервера в формате json. Принятый json ответ является обычным javascript объектом (как и любой другой json ответ) поэтому получать данные из него можно через точку. Данный способ взаимодействия с сервером происходит асинхронно т.е. без перезагрузки web страницы что удобно т.к. происходит быстрее, чем в синхронном режиме, и данные текстовых полей (и любого другого прогресса на странице (если он, конечно же, есть)) не потеряются, в результате перезагрузки.</div><div><span> В начале функции setup настраиваются: последовательный порт, пины вывода, точка доступа:</span><br /></div><div><textarea colls="100" readonly="readonly" rows="40" style="height: 350px; margin: 0px; width: 578px;">
Serial.begin(115200);
// настройка пинов на выход
pinMode(LED_PIN1, OUTPUT);
pinMode(LED_PIN2, OUTPUT);
// для точки доступа
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ssid, password);
//WiFi.softAP(ssid);
delay(100);
// для обычного Wi-Fi
//WiFi.begin(ssid, password);
//while (WiFi.status() != WL_CONNECTED)
//{
// delay(1000);
// Serial.println("Connecting to WiFi..");
//}
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Распечатать локальный IP-адрес
//Serial.println(WiFi.localIP());
</textarea></div><div>Далее имеется обработчик выдающий основную web страницу при первом заходе на неё или её перезагрузке:</div><div><textarea colls="100" readonly="readonly" rows="40" style="height: 170px; margin: 0px; width: 578px;">
// Маршрут для корневой / веб-страницы
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/html", index_html);
});
</textarea></div><div>После есть обработчик срабатывающий после нажатия на кнопку отправки данных на сервер и получения от него ответа:</div><div><br /></div><div><textarea colls="100" readonly="readonly" rows="40" style="height: 470px; margin: 0px; width: 578px;">
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request)
{
String led1;
int led1_val=0;
String led2;
int led2_val=0;
String response = ""; // ответ клиенту
int report1 = 10; // выдать клиенту значение 1
int report2 = 20; // выдать клиенту значение 2
if(request->hasParam("led1"))
{
led1 = request->getParam("led1")->value();
led1_val = led1.toInt();
analogWrite(LED_PIN1,led1_val);
}
if(request->hasParam("led2"))
{
led2 = request->getParam("led2")->value();
led2_val = led2.toInt();
analogWrite(LED_PIN2,led2_val);
}
response += "{ \"report1\": "+String(report1);
response += ",\"report2\": "+String(report2)+" }";
request->send(200, "application/json", response);
});
</textarea></div><div>Данный обработчик срабатывает в ответ на запрос страницы /update. В нем есть проверка на наличие параметра led1 а также проверка на наличие параметра led2 (т.е. параметров содержащих ШИМы светодиодов) в GET запросе. Есть также получение значений параметров, преобразование их в тип int (приходят они в виде строки) и вывод принятых ШИМов (через функцию analogWrite) на пины к которым подключены светодиоды. После формируется ответ серверу в виде строки содержащей информацию в формате json с двумя параметрами с простыми цифрами которые можно, в последствии, заменить какой либо другой полезной информацией. В конце данная строка передается в фомате json на клиентскую часть, клиентская часть выводит цифры на экран.</div><div> В конце функции setup() запускается сервер. В основном цикле т.е. функции loop() пусто.</div><div><br /></div><div>
<textarea colls="100" readonly="readonly" rows="40" style="height: 100px; margin: 0px; width: 578px;">
// Запустить сервер
server.begin();
}
void loop(){}
</textarea>
</div><div>Посмотреть тест модуля с данным скетчем + данную статью в видеоформате можно на видео:</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/PShxFgSDmqs" width="320" youtube-src-id="PShxFgSDmqs"></iframe></div><br /><div><a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a></div><div><p><br /></p></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-31440905556544649062023-07-26T05:17:00.000-07:002023-07-26T05:17:52.197-07:00Фотоаппарат на ESP32 CAM<p> Популярный и недорогой (на момент написания данной статьи) модуль ESP32-CAM (<a href="http://alii.pub/69872w">http://alii.pub/69872w</a>) имеет на своей плате разъем для подключения камеры и слот для подключения SD карты а также мощный микроконтроллер и кнопку т.е. имеет всё необходимое для постройки, на его основе, компактного, недорогого малогабаритного фотоаппарата. Также для того чтобы записать прошивку в модуль нужен USB-UART конвертер который нужно соединить с модулем по схеме:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8-vvvX5th_VzY4Fokp7lyseSaGtzHKB3uPjnDaRM80_K8z_ivc2EovD40MUp82eKe00UhHIgm4s9KFQne8FRVlqrufVLDU_-2fBgm-H9xGGbk2eGbp2LLlWscDd3goQrr-8FDEigK4I7i2YxsMW0A6RitmL45lvPnTkXu4SKwYLv9wq4R1f8Hz3Hp/s676/9%20%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="354" data-original-width="676" height="168" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8-vvvX5th_VzY4Fokp7lyseSaGtzHKB3uPjnDaRM80_K8z_ivc2EovD40MUp82eKe00UhHIgm4s9KFQne8FRVlqrufVLDU_-2fBgm-H9xGGbk2eGbp2LLlWscDd3goQrr-8FDEigK4I7i2YxsMW0A6RitmL45lvPnTkXu4SKwYLv9wq4R1f8Hz3Hp/s320/9%20%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" width="320" /></a></div>О том как настроить среду Arduino и загрузить скетч в данный модуль можно почитать статью на странице: <span color="rgba(0, 0, 0, 0.52)" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif" style="background-color: white; font-size: 14px;"><a href="https://electe.blogspot.com/2022/03/esp32-cam.html">https://electe.blogspot.com/2022/03/esp32-cam.html</a>. Готовый скетч фотоаппарата также уже существует поэтому не обязательно "изобретать велосипед" и писать его заново. Можно просто скачать на странице: <a href="https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card/">https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card/</a> после разобраться как он работает и возможно в последствии переделывать его под свои нужды. Резисторы в схеме выше нужны для ограничения тока на случай перепутывания выводов при подключении. Их можно заменить перемычками если защита от ошибок подключения не нужна. В скетче всю самую сложную работу с камерой и SD картой, на низком уровне, делают библиотеки поэтому скетч получается просто и короткий:</span><div><span style="font-size: 14px;"><textarea colls="100" readonly="readonly" rows="800" style="height: 1210px; margin: 0px; width: 578px;"> #include "esp_camera.h"
#include "Arduino.h"
#include "FS.h" // SD Card ESP32
#include "SD_MMC.h" // SD Card ESP32
#include "soc/soc.h" // Disable brownour problems
#include "soc/rtc_cntl_reg.h" // Disable brownour problems
#include "driver/rtc_io.h"
#include <EEPROM.h> // read and write from flash memory
// define the number of bytes you want to access
#define EEPROM_SIZE 1
// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
int pictureNumber = 0;
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
//Serial.setDebugOutput(true);
//Serial.println();
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Init Camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
//Serial.println("Starting SD Card");
if(!SD_MMC.begin()){
Serial.println("SD Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD Card attached");
return;
}
camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
return;
}
// initialize EEPROM with predefined size
EEPROM.begin(EEPROM_SIZE);
pictureNumber = EEPROM.read(0) + 1;
// Path where new picture will be saved in SD Card
String path = "/picture" + String(pictureNumber) +".jpg";
fs::FS &fs = SD_MMC;
Serial.printf("Picture file name: %s\n", path.c_str());
File file = fs.open(path.c_str(), FILE_WRITE);
if(!file){
Serial.println("Failed to open file in writing mode");
}
else {
file.write(fb->buf, fb->len); // payload (image), payload length
Serial.printf("Saved file to path: %s\n", path.c_str());
EEPROM.write(0, pictureNumber);
EEPROM.commit();
}
file.close();
esp_camera_fb_return(fb);
// Turns off the ESP32-CAM white on-board LED (flash) connected to GPIO 4
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);
delay(2000);
Serial.println("Going to sleep now");
delay(2000);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop() {
}
</textarea></span></div><div><span style="font-size: 14px;">В начале скетча имеются подключения заголовочных файлов библиотек. Константа EEPROM_SIZE должна быть равна единице для того чтобы данный скетч исправно работал. Следующие константы задают пины для камеры. Переменная pictureNumber нужна для того чтобы фотографии имели названия с разными порядковыми номерами. строка</span></div><div><span><span style="font-size: 14px;">WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);</span></span></div><div><span><span style="font-size: 14px;">блокирует датчик понижения напряжения. Serial.begin(115200) -включает последовательный порт для отладки.</span></span></div><div><span style="font-size: 14px;">Далее заполняется структура config настройки камеры. Далее в строке</span></div><div><span style="font-size: 14px;">esp_err_t err = esp_camera_init(&config);</span></div><div><span style="font-size: 14px;">происходит инициализация камеры, после инициализируется SD карта функцией SD_MMC.begin(). Строкой</span></div><div><span style="font-size: 14px;">fb = esp_camera_fb_get();</span></div><div><span style="font-size: 14px;">делается фотография. Строки:</span></div><div><div><span style="font-size: 14px;">EEPROM.begin(EEPROM_SIZE);</span></div><div><span style="font-size: 14px;"> pictureNumber = EEPROM.read(0) + 1;</span></div></div><div><span style="font-size: 14px;">нужны для чтения текущего номера фотографии чтобы название следующей фотографии было иным нежели название текущей. В переменную path записывается путь на SD карте для хранения фотографии. Далее открывается файл, в него записывается фотография и файл закрывается. В строке: esp_camera_fb_return(fb); освобождается переменная для хранения фотографии. Далее выключается светодиод и осуществляется перевод модуля в режим сна до следующей перезагрузки. </span></div><div><span style="font-size: 14px;">Посмотреть видеорассказ о том как сделать фотоаппарат на модуле ESP32-CAM можно на видео:</span></div><div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/hjLzJmsBtII" width="320" youtube-src-id="hjLzJmsBtII"></iframe></div><br /><span style="font-size: 14px;"><br /></span></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-17128681757612460022023-06-10T08:23:00.002-07:002023-06-10T08:23:56.849-07:00ESP8266 настройка Arduino IDE и загрузка мигалки<p> Недорогой и компактный WIFI модуль <a href="https://alii.pub/6oyjt1" target="_blank">ESP8266</a> можно использовать для управления по WIFI каким либо прибором или получением от него каких либо данных. Данный модуль лучше подойдет для каких нибудь не сложных действий, например включение лампочки по WIFI, чем более современные и сложные <a href="https://alii.pub/6oyjw5" target="_blank">ESP32</a> т.к. имеет более низкую (хотя и не значительно) стоимость а также более компактные (тоже не сильно но всё таки) размеры. Существует некоторое количество вариантов таких модулей. Например <a href="https://alii.pub/6oyjul" target="_blank">ESP-12-F</a> -это один из минималистичных вариантов с антенной, светодиодом, экранирующим корпусом и некоторым количеством выводов. Для того чтобы загрузить в такой модуль скетч можно собрать схему например такую как на фотографии:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_xkPSD9Z0gJJxe1H-wEDT17SxwEIF8SaBXzFGF0Y4gZuvKs6WkMfpypZimbBdZ6WA_y9HAIs6L69Z5GRSA5awftPe9mQblsw9wkIaahyNXy7OalTZwUCIhx9oCVqTYR5iidksUwSjgOEVevKROVCpi4OOI_F5H9czX5sNts4SaWlVo5USCxgfEcdD/s3840/IMG_20230422_221832_222.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2160" data-original-width="3840" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_xkPSD9Z0gJJxe1H-wEDT17SxwEIF8SaBXzFGF0Y4gZuvKs6WkMfpypZimbBdZ6WA_y9HAIs6L69Z5GRSA5awftPe9mQblsw9wkIaahyNXy7OalTZwUCIhx9oCVqTYR5iidksUwSjgOEVevKROVCpi4OOI_F5H9czX5sNts4SaWlVo5USCxgfEcdD/s320/IMG_20230422_221832_222.png" width="320" /></a></div><br />На фотографии есть резистор с сопротивлением 100 Ом внизу на перекрученных проводах, он соединяет пин GPIO0 и GND, это сделано для того чтобы перевести модуль в режим программирования. В этот режим модуль перейдет после подачи питания при соединенных GPIO0 и GND. Эти пины можно соединить также обычной перемычкой, однако же резистор создает страховку на случай неправильного соединения. Для того чтобы модуль заработал в обычном рабочем, не загрузочном, режиме, нужно подать питание или перезагрузить модуль при разъединенных пинах GPIO0 и GND. Для того чтобы иметь возможность писать, компилировать и загружать программы, в данный модуль, через Arduino IDE, нужно сначала эту среду настроить. Ниже будут инструкции по настройке, они подходят для Arduino IDE версии 1.8.19 но могут не подходить для других версий. Чтобы настроить Arduino IDE, для работы с модулями ESP8266, нужно её открыть, выбрать пункт "настройки" во вкладке файл:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnoIJ9o_OHDVftqB31HRIRsM60xUCrrkRrgx7C-FhaGnCK0QIpoCeTWu4t-m3hwO41WFwJfH9vQ3LjJsvw4P4O1CxHLFjGBTmv7fMS1CdrI32qMT5JCf6a5E8jxDBY3hwY0WsoSEeBe3SvSP6KMCUSUf_sc4WDVkESESkC1_ieBd_cz7N9Lu7iTROw/s428/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="364" data-original-width="428" height="272" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnoIJ9o_OHDVftqB31HRIRsM60xUCrrkRrgx7C-FhaGnCK0QIpoCeTWu4t-m3hwO41WFwJfH9vQ3LjJsvw4P4O1CxHLFjGBTmv7fMS1CdrI32qMT5JCf6a5E8jxDBY3hwY0WsoSEeBe3SvSP6KMCUSUf_sc4WDVkESESkC1_ieBd_cz7N9Lu7iTROw/s320/1.png" width="320" /></a></div>В открывшемся окне надо найти поле "Дополнительные ссылки для менеджера плат" и если оно пустое то вставить в него ссылку:<div><br /><div>http://arduino.esp8266.com/stable/package_esp8266com_index.json</div><div><br /></div><div>Если в поле уже имеется какой то текст например:</div><div><br /></div><div>https://dl.espressif.com/dl/package_esp32_index.json</div><div><br /></div><div>для модуля ESP32 (об этом есть отдельная статья в данном блоге) то нужно нажать на кнопку которая находиться справа от этого поля:</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5u15-BjeDaESNRqxFvDamtc7GO1HgDxzDKkOvgs3Qx1HF6r3Sc1O0k2BEPf3IumTlU6Zx1SeaY3_lDIT27BaO2YqzQZxvjTsWaU-SUT7jhoLm7k_HKOlThTc1aCdd8n1mm2y5q4gRK63-QwUBG-3c3z5lQgRBzXHrDYMulenYqLs7oBsBM90oXRUA/s810/2.1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="492" data-original-width="810" height="194" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5u15-BjeDaESNRqxFvDamtc7GO1HgDxzDKkOvgs3Qx1HF6r3Sc1O0k2BEPf3IumTlU6Zx1SeaY3_lDIT27BaO2YqzQZxvjTsWaU-SUT7jhoLm7k_HKOlThTc1aCdd8n1mm2y5q4gRK63-QwUBG-3c3z5lQgRBzXHrDYMulenYqLs7oBsBM90oXRUA/s320/2.1.png" width="320" /></a></div><br /> и в открывшемся окне дописать данную ссылку в дополнении к тем или той которая уже имеется в данном окне:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgZ_xMeiWEXb1w_4ebRHFMNYYeUhFVB6oLOjVfnYS3IUSKlrWjZj7LjSoyLz7WNj3WnCWdY6Lr9t5Biqdy7s8EPsg8giOfwOCUdsKdvMrf_cwuurqsYDsbERJ9LHhx_b9HqXeSEBzZeko0wgWHXYVYP9UXuv6K-bxDaha-a8Nvz_VHSv0iwQ9vuHR0/s806/2.2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="482" data-original-width="806" height="191" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgZ_xMeiWEXb1w_4ebRHFMNYYeUhFVB6oLOjVfnYS3IUSKlrWjZj7LjSoyLz7WNj3WnCWdY6Lr9t5Biqdy7s8EPsg8giOfwOCUdsKdvMrf_cwuurqsYDsbERJ9LHhx_b9HqXeSEBzZeko0wgWHXYVYP9UXuv6K-bxDaha-a8Nvz_VHSv0iwQ9vuHR0/s320/2.2.png" width="320" /></a></div>После нужно нажать "ОК" на данном окне и том которое появилось до него чтобы перейти в основную часть программы где теперь нужно выбрать пункт "Менеджер плат" в пункте показывающем текущую выбранную плату не вкладке "инструменты":</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2lbBUxs5Gj8JGqNiCSH39lDKykiUDtgpDxoJ47yJAMBD73xlvgxt7nlCbQUfiEOdpYNcXuGLrC405PvfJHuG8_-enLmLhHFxF8V1NUBjs836KDiJaJJ5VlGmBHojUR3U5ClTv-pu_Uj_aB229w4diP4fuGmKbRZIpT89L8cdlVQ-MlRVWXlOzVuJC/s672/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="512" data-original-width="672" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2lbBUxs5Gj8JGqNiCSH39lDKykiUDtgpDxoJ47yJAMBD73xlvgxt7nlCbQUfiEOdpYNcXuGLrC405PvfJHuG8_-enLmLhHFxF8V1NUBjs836KDiJaJJ5VlGmBHojUR3U5ClTv-pu_Uj_aB229w4diP4fuGmKbRZIpT89L8cdlVQ-MlRVWXlOzVuJC/s320/3.png" width="320" /></a></div><br /><div>В открывшемся окне найти пакет ESP8266 чему поможет строка поиска сверху. После того как пакет найден его можно установить нажав соответствующую кнопку с надписью "установка":</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioj9bKS0cluchnjrGSvPkPrkXygR82xLsogJK10Gl_PTlLV0ZVDWwQmO76CBbYTG9DpXaugqZxM6ktiNpzCeMHtKD_piARK0tDMDh5MkfMLI5XMyttR4Khl1auwi7HKdowGq4JXUPlxo8DHsxtITgW3SJ02XVZ7KqDyqV8bCH4rNrzqk51_EXG79Iq/s792/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="502" data-original-width="792" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioj9bKS0cluchnjrGSvPkPrkXygR82xLsogJK10Gl_PTlLV0ZVDWwQmO76CBbYTG9DpXaugqZxM6ktiNpzCeMHtKD_piARK0tDMDh5MkfMLI5XMyttR4Khl1auwi7HKdowGq4JXUPlxo8DHsxtITgW3SJ02XVZ7KqDyqV8bCH4rNrzqk51_EXG79Iq/s320/4.png" width="320" /></a></div>После установки можно нажать кнопку "закрыть" в текущем окне, чтобы перейти в основное. В основном окне, на вкладке "инструменты" в пункте "плата" выбрать "Generic 8266 Module" чтобы можно было загружать скетч в модуль ESP-12-F (по крайней мере с моим модулем это сработало):<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI5-rO6rMh44i1rgEN32T0nzK9tZY6FuRglOmRCj1upccfXQ2C7wP5BU8jK0ygAy0NchX1tKvyR_FPcySLMSH5vVcgWJESwbbGADcz8vle2XhVh__8KlYAgeIrxMlUrYGOyPYIT7pvF4KFApFSsSzAf1U4A_ZTa7KOBZx4MkRGU8Hxw9T2OtHPj0w1/s696/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="696" data-original-width="668" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI5-rO6rMh44i1rgEN32T0nzK9tZY6FuRglOmRCj1upccfXQ2C7wP5BU8jK0ygAy0NchX1tKvyR_FPcySLMSH5vVcgWJESwbbGADcz8vle2XhVh__8KlYAgeIrxMlUrYGOyPYIT7pvF4KFApFSsSzAf1U4A_ZTa7KOBZx4MkRGU8Hxw9T2OtHPj0w1/s320/7.png" width="307" /></a></div>Чтобы проверить что всё работает, можно открыть пример мигалки:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi33ckWjMJmBZupntJnIGjFWzs5vcJ_6o7Ao1ahvw3wdGGbWQYQaER0r7CWZi5kw6D9DW0UwjeNLU45Xqv3O85n9wiMo6rJjLwiyG1tD7VSHvqY4A9M-N8bpZX5F_l7uT7wBhXYwftGgz9SGfgzWonEtn9_qPasTSgxJ4EbEBTqtUDdAjPljA1Rr7l_/s716/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="716" data-original-width="648" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi33ckWjMJmBZupntJnIGjFWzs5vcJ_6o7Ao1ahvw3wdGGbWQYQaER0r7CWZi5kw6D9DW0UwjeNLU45Xqv3O85n9wiMo6rJjLwiyG1tD7VSHvqY4A9M-N8bpZX5F_l7uT7wBhXYwftGgz9SGfgzWonEtn9_qPasTSgxJ4EbEBTqtUDdAjPljA1Rr7l_/s320/8.png" width="290" /></a></div>Или выбрать другой пример, после того как он откроется можно замкнуть GPIO0 на GND на модуле, воткнуть его в USB порт, выбрать правильный COM порт в настройках и нажать кнопку вгрузить:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM_xZ6ommFWpors4N5ke1OvnJ2k2CuoZIADB7Ri3-Id9tIUJRtfDeTglsBtvZKZyiJYds0V89kmRslf7-8DMHCTeAP-t8UYtfmL2luvucVC7jUOMhvFKfzkFqM1yi_fo2-fo6N3lvpBx_0lOo6qrpkbkcnxqNQz317GT2mMhZP21vssNY-Pbox6ubn/s576/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="576" data-original-width="492" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM_xZ6ommFWpors4N5ke1OvnJ2k2CuoZIADB7Ri3-Id9tIUJRtfDeTglsBtvZKZyiJYds0V89kmRslf7-8DMHCTeAP-t8UYtfmL2luvucVC7jUOMhvFKfzkFqM1yi_fo2-fo6N3lvpBx_0lOo6qrpkbkcnxqNQz317GT2mMhZP21vssNY-Pbox6ubn/s320/11.png" width="273" /></a></div>Другие настройки д.б. примерно такими:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhedfJDlz8BLruXVhKahksYZqc0CpSvAqHiIhYSWrbLmQF-y8v9RqjHZc7E5t-kIa9fw-Mm2sMeAGBtRYGgPGZSALKRuYDpSgIwG5xwyxiU2Lti1-78Xv-wnTUHWNPkNpOphInG-cOpYir9Brcg26_3l3L0AqLK97o63tNJnwXBqnBMNPbhTV8v6i4e/s642/14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="642" data-original-width="636" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhedfJDlz8BLruXVhKahksYZqc0CpSvAqHiIhYSWrbLmQF-y8v9RqjHZc7E5t-kIa9fw-Mm2sMeAGBtRYGgPGZSALKRuYDpSgIwG5xwyxiU2Lti1-78Xv-wnTUHWNPkNpOphInG-cOpYir9Brcg26_3l3L0AqLK97o63tNJnwXBqnBMNPbhTV8v6i4e/s320/14.png" width="317" /></a></div><br /><div>Они могут отличаться и если загрузка не удается то можно попробовать их поменять. После успешной загрузки можно вынуть переходник из USB порта. Разомкнуть GPIO0 от земли. Подать питание 3.3в на модуль и увидеть как мигает встроенный на плату светодиод:<br /><div><br /><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/xC1Xkn8Z3F8" width="320" youtube-src-id="xC1Xkn8Z3F8"></iframe></div><br /><div><a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a></div></div></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-37381658377869859002023-05-08T05:10:00.000-07:002023-05-08T05:10:40.764-07:00ESP32-CAM выделение границ на изображении перекресным оператором Робертса<p>Одной из задач машинного зрения является отделение одних объектов, на изображении, от других. А одним из путей решения данной задачи может быть например выделение границ путем определения переходов цветов на изображении. Одним из ранних алгоритмов для этого является выделение границ перекресным оператором робертса. Существуют более современные алгоритмы (оператор Собеля, оператор Айверсона и т.д.) но оператор Робертса является простым и быстрым по этому используется поныне и подходит для недорогой платы за 5 долларов. Реализовывать алгоритмы машинного зрения можно проще, используя например javascript библиотеку opencv. Но такой подход работает только если есть связь модуля с компьютером т.к. opencv слишком тяжелая и ресурсозатратная библиотека для модуля <a href="http://alii.pub/69872w" target="_blank">ESP32-CAM</a> если нужна автономность (которая может быть важна для автоматизации) то алгоритмы машинного зрения нужно реализовывать на стороне сервера. В данном блоге уже есть статья о том как получить массив пикселей с фотографии модуля ESP32 CAM. (см. датчик цвета на ESP32-CAM -> <a href="https://electe.blogspot.com/2022/11/esp32-cam.html">https://electe.blogspot.com/2022/11/esp32-cam.html</a>). Далее, после того как массив пикселей получен, можно подвергать его воздействию всевозможных алгоритмов с целью их исследования. Массив пикселей содержит 3 байта на каждый пиксель. Один байт для яркости синей составляющей, один для зеленой и один для красной. Именно в таком порядке, как было выяснено ранее, компоненты располагаются в получаемом массиве.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQLxmij-ncnZHK0gDYcLxontEi81sNauzwRUgMRgUO_gfpX2zEgztR0p7u4xzZu6zpeolNsH6KE7Pgmy4LMBlb3KvyM6UdKko-T0Z-Rctr0iODT2dRMenCHO5rHI7o3xAy4oDosXejnMxw1lZNu7s7YMmQIOtFzqe_CAqja4CSdQZc9YBUCxuy6pnG/s599/22.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="331" data-original-width="599" height="177" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQLxmij-ncnZHK0gDYcLxontEi81sNauzwRUgMRgUO_gfpX2zEgztR0p7u4xzZu6zpeolNsH6KE7Pgmy4LMBlb3KvyM6UdKko-T0Z-Rctr0iODT2dRMenCHO5rHI7o3xAy4oDosXejnMxw1lZNu7s7YMmQIOtFzqe_CAqja4CSdQZc9YBUCxuy6pnG/s320/22.png" width="320" /></a></div>Однако для понимания работы алгоритма порядок не важен. Предположим что каждый пиксель имеет всего одну компоненту яркости т.е. изображение не цветное. В таком случае для определения границы какого либо объекта в какой либо точке на изображении из 4х пикселей можно найти корень из суммы квадратов разностей двух пикселей находящихся на разных диагоналях:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7wWl6lSWoJEfPDTC-Zwn7XxiOPqIRFakBKfeiAlNWTMzzEYTQaWr1nhw_jaaM7BOWeWWOGe5CCXORP0dfFrklLmDUGvraDm304PZg8mYNGdJR3vzlGQLRAhvkMgFg0HKH6VNprXdH0lGpuQpPSGN_XBHPFJzxXBTUZoQjEUc6kfjzW8BCrMwSobD-/s743/28.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="552" data-original-width="743" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7wWl6lSWoJEfPDTC-Zwn7XxiOPqIRFakBKfeiAlNWTMzzEYTQaWr1nhw_jaaM7BOWeWWOGe5CCXORP0dfFrklLmDUGvraDm304PZg8mYNGdJR3vzlGQLRAhvkMgFg0HKH6VNprXdH0lGpuQpPSGN_XBHPFJzxXBTUZoQjEUc6kfjzW8BCrMwSobD-/s320/28.png" width="320" /></a></div>или сумму модулей разностей яркостей пикселей на диагоналях. Будут ли использоваться модули или корень - не важно с точки зрения результата абстрактного алгоритма но для работы на реальном процессоре, вариант с модулями более предпочтителен т.к. его быстродействие будет больше. Суть метода заключается в том что с какой бы стороны на какую не менялся цвет, в этом перекрестии, это изменение будет выражено положительным значением результата вычисления данной формулы. Для того чтобы увидеть границы объектов, на изображении, нужно пройтись данной формулой по всем этим точкам из 4 пискелей со смещением на один пиксель на каждой итерации не затрагивая крайние справа и снизу пиксели всего изображения т.к. они сильно не повлияют на результат какой либо реальной фотографии с не очень низким разрешением. Прелесть этого метода (помимо быстродействия) также заключается в том что результат вычисления можно записывать в ту же самую матрицу из которой берутся яркости пикселей изображения, что не требует дополнительного выделения оперативной памяти чем её экономит. Прежде чем применить данный алгоритм к цветному изображению, можно сначала пройтись по всей матрице и усреднить значение трех компонент яркости каждого пикселя или же пройтись оператором Робертса по каждым компонентам в отдельности. Как будет показано в видео, внизу страницы, на практике визуально результат получается практически одинаковым.<br /><div><p>Код скетча можно скопировать из текстового поля:</p><p><textarea colls="100" readonly="readonly" rows="800" style="height: 1210px; margin: 0px; width: 578px;">#include "esp_camera.h"
#include <WiFi.h>
#include "esp_http_server.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "fd_forward.h"
#include "fr_forward.h"
#define ENROLL_CONFIRM_TIMES 5
#define FACE_ID_SAVE_NUMBER 7
#define FACE_COLOR_WHITE 0x00FFFFFF
#define FACE_COLOR_BLACK 0x00000000
#define FACE_COLOR_RED 0x000000FF
#define FACE_COLOR_GREEN 0x0000FF00
#define FACE_COLOR_BLUE 0x00FF0000
#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN)
#define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN)
#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED)
//
// WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
// Ensure ESP32 Wrover Module or other board with PSRAM is selected
// Partial images will be transmitted if image exceeds buffer size
//
const char* ssid = "моя айди";//your id
const char* password = "моя пароль";//your password
char nul = 0;
char serial_buf[2];
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
//void startCameraServer();
typedef struct {
size_t size; //number of values used for filtering
size_t index; //current value index
size_t count; //value count
int sum;
int * values; //array to be filled with values
} ra_filter_t;
typedef struct {
httpd_req_t *req;
size_t len;
} jpg_chunking_t;
#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
static ra_filter_t ra_filter;
httpd_handle_t stream_httpd = NULL;
httpd_handle_t camera_httpd = NULL;
static mtmn_config_t mtmn_config = {0};
static int8_t detection_enabled = 0;
static int8_t recognition_enabled = 0;
static int8_t is_enrolling = 0;
static face_id_list id_list = {0};
static ra_filter_t * ra_filter_init(ra_filter_t * filter, size_t sample_size){
memset(filter, 0, sizeof(ra_filter_t));
filter->values = (int *)malloc(sample_size * sizeof(int));
if(!filter->values){
return NULL;
}
memset(filter->values, 0, sample_size * sizeof(int));
filter->size = sample_size;
return filter;
}
static int ra_filter_run(ra_filter_t * filter, int value){
if(!filter->values){
return value;
}
filter->sum -= filter->values[filter->index];
filter->values[filter->index] = value;
filter->sum += filter->values[filter->index];
filter->index++;
filter->index = filter->index % filter->size;
if (filter->count < filter->size) {
filter->count++;
}
return filter->sum / filter->count;
}
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
jpg_chunking_t *j = (jpg_chunking_t *)arg;
if(!index){
j->len = 0;
}
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
return 0;
}
j->len += len;
return len;
}
static esp_err_t capture_handler(httpd_req_t *req){
// 4 пикселя для расчета оператора робертса
byte x0y0;
byte x1y0;
byte x0y1;
byte x1y1;
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
int64_t fr_start = esp_timer_get_time();
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}
httpd_resp_set_type(req, "image/jpeg");
httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
size_t out_len, out_width, out_height;
uint8_t * out_buf;
bool s;
bool detected = false;
int face_id = 0;
dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
if (!image_matrix) {
esp_camera_fb_return(fb);
Serial.println("dl_matrix3du_alloc failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}
out_buf = image_matrix->item;
out_len = fb->width * fb->height * 3;
out_width = fb->width;
out_height = fb->height;
s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf);
esp_camera_fb_return(fb);
if(!s){
dl_matrix3du_free(image_matrix);
Serial.println("to rgb888 failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}else{
//проход по всем пикселям кроме последних по строкам и столбцам и расчет оператора робертса
for(int i=0;i<out_height-1;i++){
for(int j=0;j<out_width-1;j++){
x0y0 = (byte)(out_buf[(j+i*out_width)*3] + out_buf[(j+i*out_width)*3+1] + out_buf[(j+i*out_width)*3+2])/3;
x1y0 = (byte)(out_buf[(j+1+i*out_width)*3] + out_buf[(j+1+i*out_width)*3+1] + out_buf[(j+1+i*out_width)*3+2])/3;
x0y1 = (byte)(out_buf[(j+(i+1)*out_width)*3] + out_buf[(j+(i+1)*out_width)*3+1] + out_buf[(j+(i+1)*out_width)*3+2])/3;
x1y1 = (byte)(out_buf[(j+1+(i+1)*out_width)*3] + out_buf[(j+1+(i+1)*out_width)*3+1] + out_buf[(j+1+(i+1)*out_width)*3+2])/3;
out_buf[(j+i*out_width)*3] = (byte)((x0y0>x1y1?x0y0-x1y1:x1y1-x0y0)+(x1y0>x0y1?x1y0-x0y1:x0y1-x1y0));
out_buf[(j+i*out_width)*3+1] = out_buf[(j+i*out_width)*3];
out_buf[(j+i*out_width)*3+2] = out_buf[(j+i*out_width)*3];
}
}
}
jpg_chunking_t jchunk = {req, 0};
s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk);
dl_matrix3du_free(image_matrix);
if(!s){
Serial.println("JPEG compression failed");
return ESP_FAIL;
}
int64_t fr_end = esp_timer_get_time();
Serial.printf("FACE: %uB %ums %s%d\n", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start)/1000), detected?"DETECTED ":"", face_id);
return res;
}
static esp_err_t stream_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
dl_matrix3du_t *image_matrix = NULL;
bool detected = false;
int face_id = 0;
int64_t fr_start = 0;
int64_t fr_ready = 0;
int64_t fr_face = 0;
int64_t fr_recognize = 0;
int64_t fr_encode = 0;
static int64_t last_frame = 0;
if(!last_frame) {
last_frame = esp_timer_get_time();
}
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){
return res;
}
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
while(true){
detected = false;
face_id = 0;
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else {
fr_start = esp_timer_get_time();
fr_ready = fr_start;
fr_face = fr_start;
fr_encode = fr_start;
fr_recognize = fr_start;
if(!detection_enabled || fb->width > 400){
if(fb->format != PIXFORMAT_JPEG){
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
esp_camera_fb_return(fb);
fb = NULL;
if(!jpeg_converted){
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
} else {
image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
if (!image_matrix) {
Serial.println("dl_matrix3du_alloc failed");
res = ESP_FAIL;
} else {
if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item)){
Serial.println("fmt2rgb888 failed");
res = ESP_FAIL;
} else {
}
dl_matrix3du_free(image_matrix);
}
}
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(fb){
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if(_jpg_buf){
free(_jpg_buf);
_jpg_buf = NULL;
}
if(res != ESP_OK){
break;
}
int64_t fr_end = esp_timer_get_time();
int64_t ready_time = (fr_ready - fr_start)/1000;
int64_t face_time = (fr_face - fr_ready)/1000;
int64_t recognize_time = (fr_recognize - fr_face)/1000;
int64_t encode_time = (fr_encode - fr_recognize)/1000;
int64_t process_time = (fr_encode - fr_start)/1000;
int64_t frame_time = fr_end - last_frame;
last_frame = fr_end;
frame_time /= 1000;
uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time);
}
last_frame = 0;
return res;
}
static esp_err_t cmd_handler(httpd_req_t *req){
char* buf;
size_t buf_len;
char variable[32] = {0,};
char value[32] = {0,};
buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len > 1) {
buf = (char*)malloc(buf_len);
if(!buf){
httpd_resp_send_500(req);
return ESP_FAIL;
}
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK &&
httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) {
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
free(buf);
} else {
httpd_resp_send_404(req);
return ESP_FAIL;
}
int val = atoi(value);
sensor_t * s = esp_camera_sensor_get();
int res = 0;
if(!strcmp(variable, "led"))
{
pinMode(4,OUTPUT);
digitalWrite(4,!digitalRead(4));
}
else {
res = -1;
}
if(res){
return httpd_resp_send_500(req);
}
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
return httpd_resp_send(req, NULL, 0);
}
static esp_err_t index_handler(httpd_req_t *req){
httpd_resp_set_type(req, "text/html");
httpd_resp_set_hdr(req, "Content-Encoding", "html");
sensor_t * s = esp_camera_sensor_get();
return httpd_resp_sendstr(req, (const char *)"<html><head><meta charset=\"Windows-1251\"><meta name=\"viewport\" content=\"user-scalable=no, width=device-width, initial-scale=1.0\" /><meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\
<style>body{overflow:hidden;width:100%;}</style>\
</head><body> \
<img src=\"capture\">\
<br> <button id=\"captureon\">capture</button>\
<br> <button id=\"ledon\">led toggle</button>\
<br> <a href=\"capture\">photo</a>\
<script>document.addEventListener('DOMContentLoaded', function (event) {var baseHost=document.location.origin; var streamUrl=baseHost+':81'; \
function sendCommand(el,val){fetch(baseHost+'/control?var='+el+'&val='+val);} \
document.getElementById('ledon').addEventListener('click',function(){ sendCommand('led',1); }); \
document.getElementById('captureon').addEventListener('click',function(){ document.images[0].src='capture?'+Math.random(); }); \
});</script>\
</body></html>");
}
void startCameraServer(){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
httpd_uri_t cmd_uri = {
.uri = "/control",
.method = HTTP_GET,
.handler = cmd_handler,
.user_ctx = NULL
};
httpd_uri_t capture_uri = {
.uri = "/capture",
.method = HTTP_GET,
.handler = capture_handler,
.user_ctx = NULL
};
httpd_uri_t stream_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
ra_filter_init(&ra_filter, 20);
mtmn_config.type = FAST;
mtmn_config.min_face = 80;
mtmn_config.pyramid = 0.707;
mtmn_config.pyramid_times = 4;
mtmn_config.p_threshold.score = 0.6;
mtmn_config.p_threshold.nms = 0.7;
mtmn_config.p_threshold.candidate_number = 20;
mtmn_config.r_threshold.score = 0.7;
mtmn_config.r_threshold.nms = 0.7;
mtmn_config.r_threshold.candidate_number = 10;
mtmn_config.o_threshold.score = 0.7;
mtmn_config.o_threshold.nms = 0.7;
mtmn_config.o_threshold.candidate_number = 1;
face_id_init(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES);
Serial.printf("Starting web server on port: '%d'\n", config.server_port);
if (httpd_start(&camera_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(camera_httpd, &index_uri);
httpd_register_uri_handler(camera_httpd, &cmd_uri);
httpd_register_uri_handler(camera_httpd, &capture_uri);
}
config.server_port += 1;
config.ctrl_port += 1;
Serial.printf("Starting stream server on port: '%d'\n", config.server_port);
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(stream_httpd, &stream_uri);
}
}
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
sensor_t * s = esp_camera_sensor_get();
// drop down frame size for higher initial frame rate
s->set_framesize(s, FRAMESIZE_QVGA);
//WiFi.begin(ssid, password);
//while (WiFi.status() != WL_CONNECTED) {
// delay(500);
// Serial.print(".");
//}
//Serial.println("");
//Serial.println("WiFi connected");
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
startCameraServer();
Serial.print("Camera Ready! Use 'http://");
Serial.print(WiFi.localIP());
Serial.println("' to connect");
}
void loop() {
// put your main code here, to run repeatedly:
}
</textarea></p><p>Данный код является переделкой примера который скачивается вместе с библиотекой для данного модуля для среды Arduino. Код длинный а его полный разбор будет ещё длиннее поэтому лучше сосредоточиться на более интересных вещах. А разбор работы именно с аппаратными возможностями данного модуля, возможно будут выделены в отдельные статьи в данном блоге. Код алгоритма Робертса помещен в функцию capture_handler т.к. она вызывается после того как делается снимок, который можно разобрать на пиксели и обработать данным алгоритмом, после чего отправить результат по WIFI для просмотра на каком либо устройстве типа смартфона. Для того чтобы не было необходимости в роутере для получения результата. Добавлена возможность работы модуля в режиме точки доступа. В переменную ssid нужно записать название WIFI сети которую создаст модуль и к которой нужно будет подключиться для получения результата. В переменную password нужно записать пароль который нужно будет ввести при подключении к WIFI сети которую создаст модуль. IP адрес который нужно будет ввести в адресной строке браузера, после подключения к сети, можно посмотреть в мониторе последовательного порта среды Arduino IDE после запуска которого на модуле нужно будет нажать кнопку "reset" (единственная кнопка которая имеется на данном модуле) чтобы этот адрес появился. Модуль, при этом, должен быть подключен к компьютеру через usb-uart переходник. Схему такого подключения а также то как загрузить скетчь в данный модуль, можно посмотреть на странице <a href="https://electe.blogspot.com/2022/03/esp32-cam.html">https://electe.blogspot.com/2022/03/esp32-cam.html</a>. </p><p>В функцию cmd_handler можно добавить возможность включать и выключать светодиод чтобы влиять на способность модуля распознавать границы объектов путем изменения освещения этих объектов остальные возможности из этой функции можно удалить чтобы не занимали место в коде и не заставляли тратить много времени на его вертикальный скролинг.</p><p>В функции index_handler нужно найти вызов функции httpd_resp_set_hdr и поменять там строку GZIP на html чтобы можно было делать клиентскую часть на обычном html не конвертируя его в GZIP это удобнее и к томуже GZIP имеет меньший размер чем html только при больших размерах кода, при малых получается обратный эффект. Строка с HTML кодом клиентской части передается вторым параметром в функцию httpd_resp_sendstr. Чтобы текст можно было продолжить на следующей строке, можно ставить разделители обратные слеши. На фронтэнде т.е. клиентской стороне будет картинка с результатом обработки фотографии, кнопка чтобы сделать фотографию и выполнить обработку, кнопка чтобы включить/выключить освещение светодиодом и ссылка для перехода на страницу с результатом обработки фотографии. Также видно что в коде есть конструкция для того чтобы картинка бралась из сервера а не из кэша.</p><p>Алгоритм робертса в функции capture_handler будет реализовываться только в случае успешной конвертации фотографии в формат RGB888. Изображение состоит из пикселей, пиксель состоит из 3х составляющих красной, зеленой и синей на каждую состаляющую выделено 8 байт если усреднить составляющие то можно получить черно-белое изображение в данной реализации это делается на каждой итерации цикла для 4х соседних пикселей поэтому циклы проходят по всем пикселям кроме тех что в последней строке и последнем столбце для определения границы из правого нижнего пикселя вычитается левый верхний и из этой разницы берется модуль. Далее тоже самое производиться с оставшимися диагональными пикселями. Если берется модуль то, на самом деле, не важно какой из какого вычитаются главное чтобы это вычитание было перекресным в данной же реализации используется тренарный оператор для большей переносимости кода. После чего эти разницы по модулю складываются и получается результат, чем светлее пиксель тем резче переход цветов. Вот примерно такие получаются результаты обработки фотографий данным модулем с данным скетчем:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpDOXisFx94BFX8yAsyWZkTpDvxTpvMo3qTpTP9Bqzo84Jm2OYaD2UU0A4VQu9lok3gs3dIPethxy3Y98-jA61an20hdip3Qm85y_XwuABhuwMl2ZQVw-7iFrMf_cnHmpYdlrAuBApepBzjAR1PMlIQEdjRpxCJEnfuc16-Hnhvos043SfIZNTDlL0/s1920/Screenshot_2022-11-27-21-44-50.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1920" data-original-width="1080" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpDOXisFx94BFX8yAsyWZkTpDvxTpvMo3qTpTP9Bqzo84Jm2OYaD2UU0A4VQu9lok3gs3dIPethxy3Y98-jA61an20hdip3Qm85y_XwuABhuwMl2ZQVw-7iFrMf_cnHmpYdlrAuBApepBzjAR1PMlIQEdjRpxCJEnfuc16-Hnhvos043SfIZNTDlL0/s320/Screenshot_2022-11-27-21-44-50.jpg" width="180" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEPFtga1HOm-Ibj_ZFZulg26zJCWY2aC20QalMb8Ss18PGx41E2Hg2_fWtxyRTWZ8rtxNjHmWV7zKIiJBQrJZXqCh5oSklwJb9xeT5OwMUgwZRYPcHgPKnsFpfJDWha1nghx0T-BZCaXM08c3c2vZyNbBL3tMiaoPBk5H_eeksT8ycREagCoONEE5P/s1920/Screenshot_2022-11-27-21-49-35.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1920" data-original-width="1080" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEPFtga1HOm-Ibj_ZFZulg26zJCWY2aC20QalMb8Ss18PGx41E2Hg2_fWtxyRTWZ8rtxNjHmWV7zKIiJBQrJZXqCh5oSklwJb9xeT5OwMUgwZRYPcHgPKnsFpfJDWha1nghx0T-BZCaXM08c3c2vZyNbBL3tMiaoPBk5H_eeksT8ycREagCoONEE5P/s320/Screenshot_2022-11-27-21-49-35.jpg" width="180" /></a></div><div class="separator" style="clear: both; text-align: left;">Это результаты обработки фотографий после усреднения всех пикселей, результаты обработки по пикселям каждой компоненты выглядят немного по другому и если присмотреться то можно увидеть отдельные цвета но в целом видно что в основном компоненты складываясь дают, как бы, просто переход по уровню яркоти:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIXFIxceyK-qJ4bV24_cAx5J2eUCEaWN5ynghPt4lRfzXBT_dldG5G7NcVSUlLgfmTWygj9WBK2NbYubKXA00gbr5uABkpyk0eJ5Y7qsMMYwrnYszYH1AbhtKfSzdFkZ6h_iZhYuWhnOqdpf3K7v-q7wzlTiNQofatxXDWXyOwIlrG72s7eEZwHoAY/s1920/rgbScreenshot_2022-12-02-22-18-46.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1920" data-original-width="1080" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIXFIxceyK-qJ4bV24_cAx5J2eUCEaWN5ynghPt4lRfzXBT_dldG5G7NcVSUlLgfmTWygj9WBK2NbYubKXA00gbr5uABkpyk0eJ5Y7qsMMYwrnYszYH1AbhtKfSzdFkZ6h_iZhYuWhnOqdpf3K7v-q7wzlTiNQofatxXDWXyOwIlrG72s7eEZwHoAY/s320/rgbScreenshot_2022-12-02-22-18-46.jpg" width="180" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv_yHxyCKDOXSUcZAgR7q6P14gpmoQZTh-z5DMZHfTZemTEGsyee5sZOmlrlbeBfVeUiqbTwdnrNq4fdM-9I9zfbM6Ra9LpsRouXPdogehr1nRZC-EXavtB4iX0mmur9o9EJA8nzJGjKMYqW9Gwmtc700aegJhMCq2fM1KReeWjmM8kZ9q7AbxQrJa/s1920/rgbScreenshot_2022-12-02-22-20-28.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1920" data-original-width="1080" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv_yHxyCKDOXSUcZAgR7q6P14gpmoQZTh-z5DMZHfTZemTEGsyee5sZOmlrlbeBfVeUiqbTwdnrNq4fdM-9I9zfbM6Ra9LpsRouXPdogehr1nRZC-EXavtB4iX0mmur9o9EJA8nzJGjKMYqW9Gwmtc700aegJhMCq2fM1KReeWjmM8kZ9q7AbxQrJa/s320/rgbScreenshot_2022-12-02-22-20-28.jpg" width="180" /></a></div>Посмотреть результат работы также можно на видео:<br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/K-LcM7qMISE" width="320" youtube-src-id="K-LcM7qMISE"></iframe></div><div class="separator" style="clear: both; text-align: left;"><a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a></div></div><p><br /></p><p><br /></p></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-74104211444192555982023-04-08T08:29:00.000-07:002023-04-08T08:29:03.324-07:00DC-DC конвертер на LT1074<p>Импульсный преобразователь постоянного напряжения может быть сделан на дискретных элементах однако существует множество интегральных микросхем, со встроенными силовыми ключами и системами управления, построение преобразователей, на которых, требует малого количества дополнительных элементов обвязки что избавляет от необходимости проводить сложные расчеты, разводку платы и пайку множества элементов. Такой вариант, естественно, предпочтительнее если не требуются какие то специфические свойства или особенности преобразователя которые невозможно реализовать на готовых микросхемах. Одной из таких микросхем является <a href="http://alii.pub/6g38kp" target="_blank">LT1074</a> - понижающий импульсный преобразователь с максимальным выходным током - 5А и частотой 100кГц. В документации, на данную микросхему, есть схема преобразователя которую можно использовать. Можно также менять, в некоторых пределах, номиналы используемых деталей.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiPhuqLaU28gmoNdd6Fx9ZDcr7rdIlw30BsKUX62UEZTJIM4d7zhSMhzqx_rW901PKy7g4kO_cgFmCn4m8PhV1SnMxDL7AwDE2MgwYq6TDlFAgH_5z3mYZrbhcE-3Xob40iaT4jEk1iteYPIOtmeV3t4OJMSzZL8CPPXpDRVxfShC5xs4MAOZ_hJ6b/s387/an44fa_fig.12.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="247" data-original-width="387" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiPhuqLaU28gmoNdd6Fx9ZDcr7rdIlw30BsKUX62UEZTJIM4d7zhSMhzqx_rW901PKy7g4kO_cgFmCn4m8PhV1SnMxDL7AwDE2MgwYq6TDlFAgH_5z3mYZrbhcE-3Xob40iaT4jEk1iteYPIOtmeV3t4OJMSzZL8CPPXpDRVxfShC5xs4MAOZ_hJ6b/s320/an44fa_fig.12.jpg" width="320" /></a></div><br /><p>Резисторы R1 и R2 можно заменить потенциометром (с сопротивлением близким к сумме сопротивлений этих резисторов) и получить DC-DC преобразователь с регулируемым выходным напряжением. Диод должен быть быстродействующим (желательно Шоттки) с максимальным током который требуется на выходе (5А например). Дроссель желательно ставить с соответствующей индуктивностью. Если нет возможности точно определить индуктивность то установить подходящий дроссель можно методом подбора. Ток дросселя также должен быть не менее требуемого тока в нагрузке т.е. провод, которым он намотан, не должен быть слишком тонким чтобы не перегреваться. Посмотреть тест схемы можно на видео:</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/jQC0NGxwUX4" width="320" youtube-src-id="jQC0NGxwUX4"></iframe></div><br /><p><a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a></p>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-64639914978502442452023-03-08T06:52:00.000-08:002023-03-08T06:52:53.557-08:00Простое лазерное управление роботом<p> Дистанционное лазерное управление не часто используется т.к. для лазерного луча существует много препятствий в окружающем мире которые проще обходятся радиоволнами из за чего именно радиоуправление получило наиболее широкое распространение. Лазерному лучу может быть препятствием любой непрозрачный или частично прозрачный элемент окружающей среды например туман или гипсокартоновая стена. Однако же при ясной погоде и отсутствии стен, лазерное управление имеет множество преимуществ перед радиоуправлением. Например большая эффективность по соотношению мощность/дальность действия т.к. лазерный световой луч направлен и может быть сфокусирован на небольшую площадь, чего обычно очень трудно добиться для радиоволн т.к. длинна волны их велика по сравнению со световой а при использовании высокочастотных излучателей возникают такие же проблемы как и со светом по тому что чем выше частота излучения тем сильнее оно поглощается элементами окружающей среды. Направленность лазерного луча создает проблемы для перехвата передаваемой им инфрмации т.к. приемник должен находиться не где то в пространстве в радиусе действия излучения а на линии по которой идет луч. Лазерным лучем можно передавать больше информации за меньшее время чем это можно сделать для радиоволн т.к. скорость передачи информации ограничена частотой излучения. Помех для фотоприемников лазерного излучения существует меньше чем для радиоприемников из за большей популярности радиоволн для передачи радиоволн. Одним из самых важных достоинств лазерного управления является простота реализации электроники. Излучателем может быть обычный светодиодный лазер из лазерной указки а приемником например фотодиод или <a href="https://alii.pub/6mvvra" target="_blank">солнечная батарея</a>. Солнечные батарей могут использоваться как датчики т.к. они обладают хорошей чувствительностью к свету и большой площадью. Большая площадь поверхности солнечной батареи упрощает нацеливание лазерного луча на эту батарею и уменьшает вероятность промахнуться. Используя солнечные батареи и лазерную указку можно сделать простое лазерное управление роботом с поворотами по танковой схеме. Чтобы сделать схему, первое что приходит на ум, для этого можно использовать два компаратора (например LM339 -> <a href="https://alii.pub/6mvv1t">https://alii.pub/6mvv1t</a>) сравнивающих напряжения с солнечных батарей с каким то опорным напряжением и управляющих транзисторами управляющими моторами:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_tKGnJDVjsRFVE71TggUrni5-2McZ-sAGc_Nb2d2Wvie35ZR7OeGoqgF0RPU5urlnDK_oTQjlPDJDYpWmoux2KanASCRdaIadAoQqlI7oYsN74ZepAP0N_bpNwlKJCV8iVFc0o9_mJPcEK-aZq0hjynVrX1mkg4X9BSnK2Mw9y0mDhocvJ7tnnDqN/s739/%D1%81%D1%853.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="522" data-original-width="739" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_tKGnJDVjsRFVE71TggUrni5-2McZ-sAGc_Nb2d2Wvie35ZR7OeGoqgF0RPU5urlnDK_oTQjlPDJDYpWmoux2KanASCRdaIadAoQqlI7oYsN74ZepAP0N_bpNwlKJCV8iVFc0o9_mJPcEK-aZq0hjynVrX1mkg4X9BSnK2Mw9y0mDhocvJ7tnnDqN/w397-h281/%D1%81%D1%853.png" width="397" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Датчики света с солнечными батареями</div><div><br /></div>Схема простая однако имеет один серьезный недостаток который делает её не пригодной для применения на практике (за исключением случаев применения её в полной темноте). Трудность заключается в правильном выборе опорного напряжения компараторов а именно в том что оно, ввиду действия факторов окружающей среды, не может быть постоянным следовательно не может быть задано обычным делителем на резисторах т.к. небольшое изменение фонового излучения, при этом, приведет либо к тому что оба мотора будут работать либо они не заработают при попадании лазерного луча на солнечные панели т.к. напряжения выдаваемого ими не хватит для того чтобы быть большим чем опорное. Самый простой способ решить проблему - это добавить ещё одну такую же солнечную батарею которая будет находиться в таких же условиях как и две другие и использовать её для задания опорного напряжения. Напряжения с основных солнечных батарей, при этом, надо немного уменьшить делителями так чтобы при попадании на них лазерного луча напряжение на них превышало опорное. Схема, при этом, будет выглядеть примерно так:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG1x2kekaLYTLeYGBV5XHeCm2akqaSchpPtsyR9XGV_n4DTKV1vWVYfpmyc0PMGUfjBlFWmcmFQZeN7YGx1EYtIzvRaxITi14hUtRScWOjvG5fkSapdprv834fUjIzvK_2O7ysUA1d9532yxsnmPsCwpNnBk76CFWE2ql3VYqlvcfPIcw763y3ZYsy/s739/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="522" data-original-width="739" height="314" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG1x2kekaLYTLeYGBV5XHeCm2akqaSchpPtsyR9XGV_n4DTKV1vWVYfpmyc0PMGUfjBlFWmcmFQZeN7YGx1EYtIzvRaxITi14hUtRScWOjvG5fkSapdprv834fUjIzvK_2O7ysUA1d9532yxsnmPsCwpNnBk76CFWE2ql3VYqlvcfPIcw763y3ZYsy/w445-h314/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" width="445" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 2 - Схема робота с лазерным дистанционным управлением</div><div><br /></div>Схема, по прежнему, очень простая и устойчивая к изменению фонового светового излучения при использовании одинаковых солнечных батарей, прецезионных резисторов и аналогично сбалансированных остальных частях схемы. Реальные элементы электрических цепей не так хороши как в теории поэтому робот всё равно может начать вести себя непредсказуемо или отказаться работать. Есть некоторое колличество способов решить эту проблему. Можно например заменить делитель на R3 и R4 потенциометром на подстроечном резисторе и также поступить с делителем на резисторах R5 и R6. Можно управлять роботом в темноте и увеличить немного сопротивления резисторов R5 и R3. Можно регулировать количество падающего на солнечные батареи света как было сделано для робота на видео:<br /><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/SZOykSpETqA" width="320" youtube-src-id="SZOykSpETqA"></iframe></div><br /><div><br /><p>"лазерная указка" как на видео -> <a href="https://alii.pub/6mvw62">https://alii.pub/6mvw62</a></p></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com1tag:blogger.com,1999:blog-6395453281173766990.post-89977880402918953192023-02-11T10:24:00.001-08:002023-02-18T03:12:37.712-08:00Переделка игрушки девятки на радиоуправление на ардуине<p> В 90е годы была популярна радиоуправляемая игрушка модель автомобиля ВАЗ 2109 (или "девятка"). Дети обычно не очень бережно относятся к вещам поэтому часто такие игрушки часто ломаются но если целыми остались хотя бы корпус и колеса то такую игрушку можно реинкарнировать оснастив современной электроникой и моторами. Можно добавить задний ход которого не было в оригинале. В качестве электроники для данного автомобиля можно применить например уже проверенные схемы на <a href="https://alii.pub/6iuazl" target="_blank">ардуине</a>. Схема универсального пульта:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGNZhhy5gHznc_AIC052ZasSkh-9NlyLEvVahofQ4jRkVfrfgYHQWDNhpgGfCQpiLZC0M0ib4D1BA4EGCk8BGK7ZWmDyCrAYVfJ8_E3zv7MThAaoCwTbZxOO7UOyJgo883OW89PEh8wHY8Z4uR0YiOS42vhMagaY8ZXMWz1ThlhElqNZRGhXd4M048/s1156/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D0%BF%D1%83%D0%BB%D1%8C%D1%82%D0%B0%20%D1%80%D0%BE%D0%B1%D0%BE%D1%82%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="842" data-original-width="1156" height="233" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGNZhhy5gHznc_AIC052ZasSkh-9NlyLEvVahofQ4jRkVfrfgYHQWDNhpgGfCQpiLZC0M0ib4D1BA4EGCk8BGK7ZWmDyCrAYVfJ8_E3zv7MThAaoCwTbZxOO7UOyJgo883OW89PEh8wHY8Z4uR0YiOS42vhMagaY8ZXMWz1ThlhElqNZRGhXd4M048/s320/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D0%BF%D1%83%D0%BB%D1%8C%D1%82%D0%B0%20%D1%80%D0%BE%D0%B1%D0%BE%D1%82%D0%B0.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Схема пульта на Ардуине</div><div><br /></div>Схема приемника автомобиля ВАЗ 2109:<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmo3eNK0flmeZfOP2LR-QSV3qY-xXEiVGppZKWPvrMReiDzYfDZmGhDLISwbgEpU5FFhdrH9it1KzC5sYSp4Fbzo1hQswIJj_ZCMlPbY9GntYTrYeK4NEWA66Kn5VLwKrAn8eMZwmnSW7z52xLLXhE_clo5NXuBLAMAOu_eNrUiHXMEsHY_RViIGE6/s1196/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D0%BF%D1%80%D0%B8%D0%B5%D0%BC%D0%BD%D0%B8%D0%BA%D0%B0%20%D0%B4%D0%B5%D0%B2%D1%8F%D1%82%D0%BA%D0%B8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="952" data-original-width="1196" height="255" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmo3eNK0flmeZfOP2LR-QSV3qY-xXEiVGppZKWPvrMReiDzYfDZmGhDLISwbgEpU5FFhdrH9it1KzC5sYSp4Fbzo1hQswIJj_ZCMlPbY9GntYTrYeK4NEWA66Kn5VLwKrAn8eMZwmnSW7z52xLLXhE_clo5NXuBLAMAOu_eNrUiHXMEsHY_RViIGE6/s320/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D0%BF%D1%80%D0%B8%D0%B5%D0%BC%D0%BD%D0%B8%D0%BA%D0%B0%20%D0%B4%D0%B5%D0%B2%D1%8F%D1%82%D0%BA%D0%B8.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 2 - Схема приемника</div><div class="separator" style="clear: both; text-align: center;"><br /></div>Вместо батареек можно использовать например современные литий ионные аккумуляторы.<br /><p>Скетч передатчика:</p><p><textarea colls="100" readonly="readonly" rows="800" style="height: 1210px; margin: 0px; width: 578px;">#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(9,10);
const uint64_t pipe = 0xF0F0F0F0E1LL;
int joystick[7];
void setup()
{
delay(500);
radio.begin();
delay(2);
radio.setRetries(15,15);
radio.setChannel(9);
radio.setDataRate(RF24_250KBPS);
radio.setPALevel(RF24_PA_HIGH);
radio.openWritingPipe(pipe);
//инициализация входов
pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);
pinMode(6, INPUT);
pinMode(7, INPUT);
//вкл. внутр. подтягивающие резисторы
digitalWrite(2,HIGH);
digitalWrite(3,HIGH);
digitalWrite(4,HIGH);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
delay(500);//ещё задержка на всякий случай
}
void loop()
{
//считывание с потенциометров для сервомоторов
joystick[0] = analogRead(A0);
joystick[0] = map(joystick[0],0,1023,1,180);
joystick[1] = analogRead(A1);
joystick[1] = map(joystick[1],0,1023,1,180);
joystick[2] = analogRead(A2);
joystick[2] = map(joystick[2],0,1023,1,180);
joystick[3] = analogRead(A3);
joystick[3] = map(joystick[3],0,1023,1,180);
//обнулим чтобы проще было обрабатывать коды на приемнике
joystick[4] = 0;
//опрос кнопок
if(digitalRead(2)){joystick[4] |= 1<<0;}else{joystick[4] &= ~(1<<0);}
if(digitalRead(3)){joystick[4] |= 1<<1;}else{joystick[4] &= ~(1<<1);}
if(digitalRead(4)){joystick[4] |= 1<<2;}else{joystick[4] &= ~(1<<2);}
if(digitalRead(5)){joystick[4] |= 1<<3;}else{joystick[4] &= ~(1<<3);}
if(digitalRead(6)){joystick[4] |= 1<<4;}else{joystick[4] &= ~(1<<4);}
if(digitalRead(7)){joystick[4] |= 1<<5;}else{joystick[4] &= ~(1<<5);}
//отослать данные
radio.write( joystick, sizeof(joystick) );
}
</textarea></p><p>Скетч приемника:</p><p><textarea colls="100" readonly="readonly" rows="800" style="height: 1210px; margin: 0px; width: 578px;">#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>
RF24 radio(9,10);
const uint64_t pipe = 0xF0F0F0F0E1LL;
int d[7];
Servo servo1;
void setup()
{
//инициализация сервомоторов
servo1.attach(4);
delay(1000);
radio.begin();
radio.setRetries(15,15);
radio.setChannel(9);
radio.setDataRate(RF24_250KBPS);
radio.setPALevel(RF24_PA_HIGH);
radio.openReadingPipe(1,pipe);
radio.startListening();
delay(500);
}
void loop()
{
if ( radio.available() )
{
//закомментированный код использовать
//для старых версий библиотеки nRF24
//bool done = false;
//while (!done)
//{
radio.read( d, sizeof(d) );
//поворачиваем сервомоторы
servo1.write(d[0]);
//управление основным мотором
if (d[4] & (1<<0))
{
digitalWrite(2, LOW);
}
else
{
digitalWrite(2, HIGH);
}
if (d[4] & (1<<1))
{
digitalWrite(3, LOW);
}
else
{
digitalWrite(3, HIGH);
}
//}
}
}
</textarea></p><p>Видео с тест драйвом данного автомобиля:</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/d3GjqA8jWMY" width="320" youtube-src-id="d3GjqA8jWMY"></iframe></div>
<br />Поискать модели автомобиля можно например <a href="https://alii.pub/6m6e72">по этой ссылке</a>
<br /><p<a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a></p><p><br /></p>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-54931573116918706982023-01-14T09:02:00.000-08:002023-01-14T09:02:10.693-08:00Регулятор драйвер контроллер 5ти проводного униполярного шагового двигателя<p> Некоторые бытовые приборы (например принтеры, кондиционеры и т.д.) могут содержать в своей конструкции шаговые двигатели которые не имеют каких либо обозначений для их идентификации и нахождения их документации с целью подбора или изготовления регулятора, драйвера, контроллера для управления такими шаговыми двигателями. Если шаговый двигатель имеет 5 проводов то скорее всего это униполярый шаговый двигатель с 4мя фазами и одной общей точкой которая соединена с одним из проводов. Найти провод соединенной со средней точкой не трудно т.к. между этим проводом и остальными будет самое низкое сопротивление что можно определить мультиметром в режиме измерения активного сопротивления. Для того чтобы определить какое напряжение можно подавать на фазы, можно сначала подать небольшое напряжение на одну из фаз и увеличивать его до тех пор пока ротор мотора не повернется. Поворот возможно будет на очень небольшой угол, поэтому чтобы заметить этот поворот можно прицепить к ротору какой либо удлиннитель поворот которого виден при малых градусах. Это может быть какая нибудь длинная стрелка или лазерная указка. Для того чтобы двигатель крутил ротором нужно по очереди подавать напряжения на провода фаз относительно провода общей точки в нужном порядке который можно определить опытным путем перебирая, по очереди, разные варианты. После того как нужный порядок определен можно собирать схему:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiynFdlDDuNI7hio3AghQCAdA9hSBmyp4ct5ms8vXTtN_CM4Bw7y3zj8Ycw6tx040QMuaw0fNR9kqnzACOV4shdJWkm4QQLxwb82e9IP8U1QXuAoY_9zPPh0bq5RwFO2ni-vfiHMngWq4pMROKMN2VGtR9fjCmXGPd3XRuxRSkjamZvEDPYWUtJCZFH/s1208/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%82%D0%BE%D1%80%D0%B0%20%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%BB%D0%B5%D1%80%D0%B0%205%D1%82%D0%B8%20%D0%BF%D1%80%D0%BE%D0%B2%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%83%D0%BD%D0%B8%D0%BF%D0%BE%D0%BB%D1%8F%D1%80%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%88%D0%B0%D0%B3%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE%20%D0%B4%D0%B2%D0%B8%D0%B3%D0%B0%D1%82%D0%B5%D0%BB%D1%8F.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="1208" height="265" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiynFdlDDuNI7hio3AghQCAdA9hSBmyp4ct5ms8vXTtN_CM4Bw7y3zj8Ycw6tx040QMuaw0fNR9kqnzACOV4shdJWkm4QQLxwb82e9IP8U1QXuAoY_9zPPh0bq5RwFO2ni-vfiHMngWq4pMROKMN2VGtR9fjCmXGPd3XRuxRSkjamZvEDPYWUtJCZFH/w400-h265/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%82%D0%BE%D1%80%D0%B0%20%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%BB%D0%B5%D1%80%D0%B0%205%D1%82%D0%B8%20%D0%BF%D1%80%D0%BE%D0%B2%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%83%D0%BD%D0%B8%D0%BF%D0%BE%D0%BB%D1%8F%D1%80%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%88%D0%B0%D0%B3%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE%20%D0%B4%D0%B2%D0%B8%D0%B3%D0%B0%D1%82%D0%B5%D0%BB%D1%8F.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Регулятор драйвер контроллер 5ти проводного униполярного шагового двигателя</div><p>и правильно подключить к ней шаговый двигатель. Данная схема имеет три входа управления:</p><p>ENABLE - который д.б. заземлен для того чтобы схема работала, STEP - на этот вход подаются импульсы каждый из которых соответствует повороту ротора шагового двигателя на один шаг. DIR - вход определяющий направление вращения двигателя, если на этом пине будет высокий уровень напряжения (напряжение питания схемы) то двигатель будет вращать ротор в одну сторону, если на этом входе будет низкое напряжение 0В то двигатель будет вращать ротор в другую сторону т.е. противоположную той в которую вращается двигатель если на этом пине высокое напряжение. На вход STEP можно подавать импульсы например с мультивибратора или микроконтроллера, ардуины и т.д. В данной схеме имеется десятичный счетчик (для переключения фаз), аналоговый мультиплексор (для реверса) и усилитель сигналов с счетчика с обратными диодами. Этот усилитель (можно поискать таких много тут-><a href="https://alii.pub/6kpp45">https://alii.pub/6kpp45</a> с шаговым двигателем) рассчитан на ток до 500мА, поэтому если двигатель, при данном напряжении, потребляет больше то следует выбрать другой усилитель или использовать отдельные транзисторы и обратные диоды на соответствующий ток. Увидеть результат работы данной схемы можно на видео:</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/wYaT4ZT9p1g" width="320" youtube-src-id="wYaT4ZT9p1g"></iframe></div><br /><p><br /></p><p><br /></p>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-62364634187410856902022-12-04T03:24:00.000-08:002022-12-04T03:24:09.550-08:00STM32 CMSIS внешние прерывания и счетчик дребезга контактов<p>Внешние прерывания могут быть полезны например когда нужно зафиксировать кратковременные импульсы на пине микроконтроллера, по тому что обычный опрос пина не обязательно будет происходить в нужный момент времени. Для демонстрации полезности внешних прерываний, на микроконтроллере STM32, можно изготовить например счетчик дребезга контактов. Ранее, в данный блог, уже выкладывалась статья о <a href="http://electe.blogspot.com/2020/12/attiny2313.html" target="_blank">внешних прерываниях для ATtiny2313 и настройке их на ассемблере</a>. Такой счетчик показывает что дребезг есть при нажати на кнопку и даже при её отпускании, хотя при отпускании он заметно слабее и даже иногда может отсутствовать, хотя это зависит от конструкции кнопки. Счетчик дребезга контактов также может использоваться как генератор случайных чисел т.к. количество дребезга практически невозможно предсказать. М.б. на него можно, в некоторой степени повлиять например магнитом, но например в тандеме с псевдослучаным программным генератором, счетчик дребезга контактов может быть одновременно и устойчивым к внешним физическим воздействиям воздействиям и генерировать реально случайные числа. Схема может быть например такой:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2ANqbJislA71zBNQCjXX9u6glQx2NJcusGav8TqQlVkFCMA4ydMeJI10fKDWrejjQl1wRobLUD02LOFNnfsS15liF_K4JjOK5dqyKmNES0T9DeBHdc8YEpMZPG6PjkbPcaeppRiY2SqrRvV7MujsiaPT8HdgKpvoYeKZD-LSHveTipxR83vdqpW9m/s847/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="660" data-original-width="847" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2ANqbJislA71zBNQCjXX9u6glQx2NJcusGav8TqQlVkFCMA4ydMeJI10fKDWrejjQl1wRobLUD02LOFNnfsS15liF_K4JjOK5dqyKmNES0T9DeBHdc8YEpMZPG6PjkbPcaeppRiY2SqrRvV7MujsiaPT8HdgKpvoYeKZD-LSHveTipxR83vdqpW9m/s320/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Счетчик дребезга контактов кнопки</div><div><br /></div>В микроконтроллере <a href="http://alii.pub/3esv9s" target="_blank">STM32F103 на плате "blue pill"</a> может одновременно применяться не более 16ти датчиков внешних прерываний. Каждое из этих 16ти прерываний может быть переключено между портами к пинам соответствующим номеру прерывания т.е. например первое прерывание может быть на пине PA1 или PB1 (или PC1 если бы он был на данном микроконтроллере (на других микроконтроллерах STM32, с большим количеством пинов, это может быть)). По умолчанию все внешние прерывания работают на порту А поэтому, чтобы меньше возиться с настройками, можно использовать для внешних прерываний только пины порта A это сделает настройку внешних прерываний не сложной. Как обычно, нужно включить тактирование порта А в регистре APB2ENR, сделать это можно командой:<div><textarea style="border: none; height: 41px; max-height: 15px; max-width: 179px; width: 179px;">RCC->APB2ENR |= 1<<2;</textarea></div><div><p>Выбранный пин (пин А2) надо настроить на вход с альтернативной функцией и подтяжкой к +питания, сделать это можно командами:</p><p><textarea style="border: none; height: 141px; max-height: 79px; max-width: 440px; width: 513px;">GPIOA->CRL |= 1<<11;
GPIOA->CRL &= ~(1<<10);
GPIOA->ODR |= 1<<2;</textarea></p><p>Установить прерывания по восходящему фронту для пина А2 можно установкой в 1 бита 2 регистра XTI_RTSR:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_3UiPBeu-8GDEnHNS_WF0Z-D4D0k8UG2CfZMcptyMjVfSnwqSDqBIBlECbjaqoqSvqEnmj6a1WKkVrhD6serjBU5cCiH7hvA8q2aHBU_2HF-zKjcepcD3hrmvblTzeCHiH_CMLvTpALXhDMzCkQ2F6LI6DgJyLYNwNx_jq_0C3Dym72GhfCe2fatK/s638/7_1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="452" data-original-width="638" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_3UiPBeu-8GDEnHNS_WF0Z-D4D0k8UG2CfZMcptyMjVfSnwqSDqBIBlECbjaqoqSvqEnmj6a1WKkVrhD6serjBU5cCiH7hvA8q2aHBU_2HF-zKjcepcD3hrmvblTzeCHiH_CMLvTpALXhDMzCkQ2F6LI6DgJyLYNwNx_jq_0C3Dym72GhfCe2fatK/s320/7_1.png" width="320" /></a></div><p>Для этого в коде можно написать команду:</p><p><textarea style="border: none; height: 41px; max-height: 15px; max-width: 179px; width: 179px;">EXTI->RTSR |= (1<<2);</textarea></p><p>Для того чтобы аналогично включить прерывания по спаду, можно использовать регистр XTI_FTSR:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghq-dLrIn4OkN6sjDe_aWLSEWZ6tGW2P6xxnAkNbOb008Ek1DThFsnK1e-lXpaUkhCX6VyIgiwEX77qGuMvfEFDj83zwvJGkgjKyKKnEeqhP-HLrWSzNoryd90LTmbOb6Y5uy8qG8Wf_rU9wyTE3uUDn2mQ_ksLFmCloOF5pRwmhJU2_R3_FyOQdq-/s638/7_2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="452" data-original-width="638" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghq-dLrIn4OkN6sjDe_aWLSEWZ6tGW2P6xxnAkNbOb008Ek1DThFsnK1e-lXpaUkhCX6VyIgiwEX77qGuMvfEFDj83zwvJGkgjKyKKnEeqhP-HLrWSzNoryd90LTmbOb6Y5uy8qG8Wf_rU9wyTE3uUDn2mQ_ksLFmCloOF5pRwmhJU2_R3_FyOQdq-/s320/7_2.png" width="320" /></a></div><br /><p>Для разрешения внешних прерываний пина 2, бит 2 регистра EXTI_IMR</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6HPk2JRk8slhKGVZ4l-R9j-r3XCDq8WQOjkfU9ogqmxTVKH0vjC2-rcVKKSBJORb80Q83AfQzCmcBbxfjG9fVwCbf7YhOhGXh4qROKKl-ZlZOUGVsZ6l_oVzks0o5bBXLlPN8YYJPdegJsZubwvPdkobrdEUDAVnvZVcTTQwqm9-lrSA6REcRXQEa/s630/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="440" data-original-width="630" height="223" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6HPk2JRk8slhKGVZ4l-R9j-r3XCDq8WQOjkfU9ogqmxTVKH0vjC2-rcVKKSBJORb80Q83AfQzCmcBbxfjG9fVwCbf7YhOhGXh4qROKKl-ZlZOUGVsZ6l_oVzks0o5bBXLlPN8YYJPdegJsZubwvPdkobrdEUDAVnvZVcTTQwqm9-lrSA6REcRXQEa/s320/9.png" width="320" /></a></div> устанавливается в 1:</div><div><textarea style="border: none; height: 41px; max-height: 15px; max-width: 179px; width: 179px;">EXTI->IMR |= (1<<2);</textarea><br /><p>Теперь чтобы прерывание сработало осталось разрешить его в NVIC командой:</p><p>NVIC_EnableIRQ (EXTI2_IRQn);</p><p>После этого можно написать функцию-обработчик прерывания:</p><p><textarea style="border: none; height: 113px; max-height: 113px; max-width: 495px; width: 495px;">void EXTI2_IRQHandler(void)
{
counter ++;
EXTI->PR |= (1<<2); //сбросить флаг внешнего прерывания 2
}</textarea></p><p>Записью единицы в бит 2 регистра EXTI_PR сбрасывается флаг внешнего прерывания 2.</p><p>counter++ -инкремент счетчика дребезга контактов.</p><p>Помимо описанных выше команд, также понадобиться работа с UARTом (см. передача по UART - <a href="https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis.html">https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis.html</a> и прием по UART - <a href="https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis_7.html">https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis_7.html</a>) для вывода получения результатов и некоторые другие команды. Полный текст программы можно скопировать из поля:</p><p><textarea style="border: none; height: 450px; max-height: 450px; max-width: 668px; width: 668px;">#include "stm32f10x.h"
int counter = 0;
void sendSymbol(char symbol)
{
while (!(USART1->SR & 0b10000000)) {}
USART1->DR = symbol;
}
void sendDigit(char digit)
{
while (!(USART1->SR & 0b10000000)) {}
USART1->DR = digit+48;
}
int main(void)
{
char dat = '0';
RCC->APB2ENR |= 1<<2;// включить тактирование порта A
GPIOA->CRL |= 1<<11;
GPIOA->CRL &= ~(1<<10);
GPIOA->ODR |= 1<<2;// подтяжка PA2 к +
EXTI->RTSR |= (1<<2); //прерывания по восходящему фронту для пина 2
EXTI->IMR |= (1<<2); //разрешить внешние прерывания для пина 2
NVIC_EnableIRQ (EXTI2_IRQn); //разрешить прерывания в NVIC
//настройка UART1
RCC->APB2ENR |= 1<<14; //вкл. тактирование UART1
// настройка вывода PA9 (TX1) на режим альтернативной функции с активным выходом
GPIOA->CRH &= ~(1<<6);
GPIOA->CRH |= (1<<7) | (1<<4) | (1<<5);
// настройка вывода PA10 (RX1) на режим входа с подтягивающим резистором
GPIOA->CRH &= ~(1<<10);
GPIOA->CRH |= (1<<11);
GPIOA->BSRR |= (1<<10);
USART1->BRR = 7500; // 72 000 000 Гц / 9600 бод = 7500
USART1->CR1 |= (1<<2) | (1<<3) | (1<<13);//разблокировать UART1(13), прием (2) и передачу (3) по нему
while(1)
{
while (!(USART1->SR & (1<<5))) {} //ждем прихода байта данных по UART1
dat = USART1->DR; //принять байт данных от pc
if(dat == 'g') //'get' получить
{
for(int i=1000000;i>=10;i/=10)
{ //разделить число на цифры и вывести
sendDigit((char)((counter%i)/(i*0.1)));
}
sendSymbol('\n');
counter = 0;
}
else
{ // error - ошибка
sendSymbol('e');
sendSymbol('r');
sendSymbol('\n');
}
}
}
//Обработчик EXTI 2
void EXTI2_IRQHandler(void)
{
counter ++;
EXTI->PR |= (1<<2); //сбросить флаг внешнего прерывания 2
}</textarea></p><p>Результат работы счетчика дребезга контактов можно посмотреть на видео:</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/tsqN1Q_qz00" width="320" youtube-src-id="tsqN1Q_qz00"></iframe></div><br /><p><a href="http://electe.blogspot.com/p/blog-page.html" title="Здесь ссылки на статьи этого блога и другие полезные сайты">
<b>КАРТА БЛОГА (содержание)</b>
</a></p></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-49009057463432403822022-11-05T07:05:00.000-07:002022-11-05T07:05:02.661-07:00Датчик цвета на ESP32-CAM<p> Модуль ESP32-CAM<span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="background-color: white; font-size: 15.4px;"> (</span><a href="http://alii.pub/69872w" style="background-color: white; color: red; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 15.4px;">http://alii.pub/69872w</a><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="background-color: white; font-size: 15.4px;">)</span> может быть не только обычной дистанционной камерой для просмотра области пространства на которую она направлена но и также выполнять некоторые манипуляции с визуальными данными которые этот модуль получает и также самостоятельно принимать некоторые решения на основе этих данных. Микроконтроллер ESP32 имеет достаточно неплохую производительность (по сравнению с другими микроконтроллерами рассматриваемыми ранее в данном блоге) что позволяет использовать его для обработки изображений и машинного зрения. Ранее, в данном блоге, появилась статья про датчик цвета на основе Raspberry pi 3 и USB камеры - <a href="https://electe.blogspot.com/2020/05/usb-raspberry-pi.html">https://electe.blogspot.com/2020/05/usb-raspberry-pi.html</a>. Датчик цвета сделать получилось но работал от крайне медленно. На получение снимка, его обработку, сохранение, конвертирование и всё стальное в сумме уходило более одной секунды что было видно без специальных таймеров замера времени. Если к этой задержке прибавить время более сложной операции, нежели просто усреднение цветов пикселей, то скорость работы наверняка также не порадует. Более быстрой работы устройства можно добиться исключив ненужные действия из процесса. Добиться этого на Raspberry pi c USB камерой будет не просто т.к. придется работать с камерой на более низком программном уровне + добавляются ограничения по скорости передачи данных по USB. Эти проблемы можно было решить применением специальной камеры для Raspberry pi но на тот момент такие камеры стоили гораздо дороже чем USB камеры. Модуль же ESP32-CAM сам по себе не дорогой, имеет небольшие размеры и используя его и среду разработки Arduino IDE можно получить доступ к пикселям изображений делаемых камерой без больших сложностей. Конечно на данный модуль нельзя установить такую тяжёлую библиотеку как opencv но в большинстве случаев её функционал избыточен по тому что не всегда необходимо делать с изображениями все преобразования которые может данная библиотека. Чтобы модуль ESP32-CAM мог показать что он правильно поработал с пикселями и определил цвет, можно дополнить его например RGB светодиодом который будет показывать какой цвет обнаружил модуль. Конечно можно использовать имеющийся в модуле WIFI или bluetooth или даже uart (если совсем лень) но светодиод кажется нагляднее. При этом его не следует направлять в ту сторону куда смотрит камера, иначе модуль будет определять тот цвет который сам же и создает что приведет к положительной обратной связи всей системы и более трудному выводу её из устойчивого состояния. Схема может выглядеть например так:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYW6O1uTyCVtpyA18go6pBGFqZgSjXDNTFZXjLrg3hI-m00d3XOCcRO72BD41rQTB3jwDQoGRCcK60JaP6JEphnLBASNsCUP7PV5G4hjanvjQqmxmgIjELY0EQiV2JeexNtaZ1cuc6hXSW0ue9ZcficHDnqTra6_UNR1H5UJJjMGyzKtmyHxsJPgx6/s768/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="480" data-original-width="768" height="289" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYW6O1uTyCVtpyA18go6pBGFqZgSjXDNTFZXjLrg3hI-m00d3XOCcRO72BD41rQTB3jwDQoGRCcK60JaP6JEphnLBASNsCUP7PV5G4hjanvjQqmxmgIjELY0EQiV2JeexNtaZ1cuc6hXSW0ue9ZcficHDnqTra6_UNR1H5UJJjMGyzKtmyHxsJPgx6/w462-h289/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" width="462" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Схема датчика цвета на ESP32-CAM</div><div><br /></div>Модуль может питаться автономно от пауэрэнка даже без отключения программатора - это может быть удобно т.к. для перепрограммироания нужно делать только одно пересоединение. Скетч не сильно сложный и может быть такой:<div><textarea colls="100" readonly="readonly" rows="800" style="height: 1210px; margin: 0px; width: 578px;"> #include "esp_camera.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "fd_forward.h"
#include "fr_forward.h"
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;//YUV422,GRAYSCALE,RGB565,JPEG
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
sensor_t * s = esp_camera_sensor_get();
//понизить разрешение для увеличения скорости
s->set_framesize(s, FRAMESIZE_QQVGA);
//увеличить насыщенность для лучшего распознавания цвета
s->set_saturation(s, 2);
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
pinMode(15, OUTPUT);
}
void loop() {
camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
}
size_t out_len, out_width, out_height;
uint8_t * out_buf;
bool s;
dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
if (!image_matrix) {
esp_camera_fb_return(fb);
Serial.println("dl_matrix3du_alloc failed");
}
out_buf = image_matrix->item;
out_len = fb->width * fb->height * 3;
out_width = fb->width;
out_height = fb->height;
s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf);
esp_camera_fb_return(fb);
if(!s){
dl_matrix3du_free(image_matrix);
Serial.println("to rgb888 failed");
}
else{
double r_average = 0;
double g_average = 0;
double b_average = 0;
double r_row = 0;
double g_row = 0;
double b_row = 0;
for(int i=0;i<out_len;i+=3){
r_row+=out_buf[i+2];
g_row+=out_buf[i+1];
b_row+=out_buf[i];
}
r_average = r_row/(out_len/3);
g_average = g_row/(out_len/3);
b_average = b_row/(out_len/3);
Serial.printf("r=%d g=%d b=%d len=%d w=%d h=%d\n",(int)r_average,(int)g_average,(int)b_average,out_len,out_width,out_height);
if( r_average > g_average && r_average > b_average ) {
digitalWrite(12,LOW);
digitalWrite(13,HIGH);
digitalWrite(15,HIGH);
}
if( g_average > r_average && g_average > b_average ) {
digitalWrite(12,HIGH);
digitalWrite(13,LOW);
digitalWrite(15,HIGH);
}
if( b_average > r_average && b_average > g_average ) {
digitalWrite(12,HIGH);
digitalWrite(13,HIGH);
digitalWrite(15,LOW);
}
}
dl_matrix3du_free(image_matrix);
}
</textarea></div><div>В начале, как всегда, подключаются заголовочные файлы. После нужно добавить константы которые копируются из файла с константами примера для камеры. Константы нужно скопировать из блока для используемого модуля, в данном случае для ai thinker. В функции setup() включается последовательный порт для отладки. Далее заполняется структура для настройки и инициализации камеры. После чего имеется необходимая для запуска устройства последовательность строк скопированная из скетча примера. Далее делается инициализация камеры функцией esp_camera_init и проверка этой инициализации. После совершаются настройки. Снижается разрешение кадра чтобы сделать его быстрее и увеличивается насыщенность цвета чтобы датчик лучше различал цвета. Далее пины к которым подключен светодиод настраиваются на выход. В начале основного цикла объявлен указатель на структуру типа camera_fb_t который заполняется функцией esp_camera_fb_get после взятия фотографии от камеры. Если оно прошло неудачно то в последовательный порт передается соответствующее сообщение. Далее обявляются некоторые специальные переменные суть которых отражена в их названиях. Функцией dl_matrix3du_alloc выделяется память для матрицы изображения. Если этого сделать не получилось то оригинальное изображение возвращается функцией esp_camera_fb_return и в последовательный порт передается сообщение о неудаче. Далее заполняются специальные переменные. После чего функцией fmt2rgb888 изображение из камеры преобразуется из формата jpeg в формат rgb888 т.е. в обычный набор пикселей в котором на каждую компоненту цвета выделено по 8 бит. После успешного конвертирования набор пикселей будет доступен через указатель out_buf из которого можно извлекать пиксели так же как из обычного массива (такие особенности языка си) через квадратные скобки. Несмотря на то что в аббревиатуре RGB буква R, обозначающая красный, стоит на первом месте. Извлекать компоненты цвета из out_buf следует в обратном порядке т.е. синяя компонента первого пикселя будет в out_buf[0], зеленая в out_buf[1], красная в out_buf[2], синяя компонента второго пикселя будет в out_buf[3] и т.д. Конец этого хранилища записан в переменной out_len которую можно использовать для завершения цикла перебора пикселей. Для каждой компоненты цвета находиться среднее арифметическое после чего, обычным сравнением, определяется какая компонента преобладает и в соответствии с ней загорается нужный свет RGB светодиода. В конце выделенная память освобождается функцией dl_matrix3du_free. Результат работы датчика можно посмотреть на видео:</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/TgLOzis5v-A" width="320" youtube-src-id="TgLOzis5v-A"></iframe></div><br /><div><a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-21813001532215278292022-10-08T23:10:00.000-07:002022-10-08T23:10:57.995-07:00ГаусганГаусган (или <a href="http://alii.pub/6heu4z" target="_blank">пушка Гауса</a>, по русски) является интересным девайсом не имеющим большого практического значения ввиду своей низкой эффективности. Данный девайс преобразует электрическую энергию конденсаторов (или других накопителей) в кинетическую энергию снаряда. Существуют также такого тип двайсы (приборы, по русски) которые осуществляют аналогичное преобразование с гораздо большей эффективностью. Например электродвигатели обычно имеют КПД 70 и более процентов. На примере двигателей видно что для преобразователей электрической энергии в кинетическую нет существенных ограничений для достижения высокой эффективности близкой к 100% (такие ограничения например есть для преобразователей тепловой энергии в кинетическую это например второй закон термодинамики). Т.о. можно заключить что конструкция высокоэффективной электромагнитной пушки является возможной. Изготовить простейший гаусган, в домашних условиях, не трудно. Этому посвящено огромное колличество статей в интернете. Классический одноступенчатый гаусган представляет собой пусковую кутушку на стволе на которую, через тиристор, подается напряжение от конденсаторов которые заряжаются повышающим преобразователем работающим от батареек. Суммируя вышесказанное можно сгенерировать схему пушки гаусса например такую:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLI0NSdE8hZTCgbGUps8H-j0lwT3bUId37zevXPjbRwTr8Fp9pYaYMOSKlvB90le4KDPJvhj6KxfOqTF4zv6h8GsRK5zaZ1tyBeRK8DMQHSc7m1RO5dvfMw41-nwciYLJJ9OGzfG8eyzuhTtALrZwpO67zJWQms1oM-8pjq9Kp41KIuOEsIs0QiO7E/s710/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="279" data-original-width="710" height="167" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLI0NSdE8hZTCgbGUps8H-j0lwT3bUId37zevXPjbRwTr8Fp9pYaYMOSKlvB90le4KDPJvhj6KxfOqTF4zv6h8GsRK5zaZ1tyBeRK8DMQHSc7m1RO5dvfMw41-nwciYLJJ9OGzfG8eyzuhTtALrZwpO67zJWQms1oM-8pjq9Kp41KIuOEsIs0QiO7E/w424-h167/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" width="424" /><br /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Схема пушки Гаусса</div><div><br /></div>Повышающий преобразователь сделан по классической двухтактной схеме на двух транзисторах с трансформатором на тороидальном сердечнике. Такой сердечник может быть толщиной например 3X5мм и диаметром 10мм. Это маленький сердечник который не имеет большой мощности и сильно ограничивает ток но уже при нем данный преобразователь потребляет около 200 мА если питается от двух пальчиковых батареек а это не маленький ток для них. Заряд силовых конденсаторов (или лучше одного с силового конденсатора C4) происходит не быстро. Если использовать например мощные литиевые аккумуляторы (или один мощный литиевый аккумулятор) то магнитопровод можно использовать побольше а транзисторы VT1 и VT2 помощьнее и иметь более быструю скорость заряда силовых (силового) конденсаторов. Первичная обмотка имеет 6 витков, в одну сторону, с отводом от середины. Вторичная 140 витков. Толщина провода такая чтобы влезть во внутрь кольца. Напр. у вторички 0.1мм а у первички 0.2мм. Диоды VD1...VD4 д.б. высоковольтными и высокочастотными BYT25G для данного случая более чем подойдут. Все резисторы маломощьные напр. по 0.125 вт. В схеме не предусмотрен индикатор заряда конденсатора поэтому можно подключить к ней например вольтметр. После того как силовой конденсатор C4 зарядиться можно будет нажать кнопку SB1 (хотя правда до этого никто тоже не запрещает этого делать) что приведет к запуску снаряда (если он железный и был вставлен в ствол непосредственно перед катушкой до нажатия на кнопку. Если снаряда не было то он соответственно не полетит)<div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/6QHgas1rCWk" width="320" youtube-src-id="6QHgas1rCWk"></iframe></div><br /><div><br /><div><br /></div></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-38552421961088178372022-09-03T00:27:00.000-07:002022-09-03T00:27:03.896-07:00STM32 CMSIS ADC использование нескольких каналов АЦП<p> Микроконтроллеры STM32 (например <a href="http://alii.pub/3esv9s" target="_blank">stm32f103c8</a>), как правило, имеют некоторое количество каналов аналого-цифрового преобразователя (АЦП(ADC)) что может быть использовано например для снятия показаний например с некоторого количества потенциометров или аналоговых датчиков. О том как снимать показания с одного потенциометра уже имеется статья в данном блоге. Если взять код от туда и дополнить его 3мя строками инициализации АЦП то можно получить устройство которое снимает показания с двух потенциометров.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNYnpuEthWGbRNewGkguHReFLF2P7sLhsUI3sR9lRvQK5iLJv_EFDZ1wT2rW6DlpQrxa8D0S_yzox62YbGY0S5xzJeCJ7JqRF-w1jAFtnsyhT-zcP8ydvcuzRqeReTnfgE5KffQ169Y5ziRYEqL5DADXf13q76gijVm_A1n9RQeh_Da-qMYzu3MpM_/s847/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="660" data-original-width="847" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNYnpuEthWGbRNewGkguHReFLF2P7sLhsUI3sR9lRvQK5iLJv_EFDZ1wT2rW6DlpQrxa8D0S_yzox62YbGY0S5xzJeCJ7JqRF-w1jAFtnsyhT-zcP8ydvcuzRqeReTnfgE5KffQ169Y5ziRYEqL5DADXf13q76gijVm_A1n9RQeh_Da-qMYzu3MpM_/s320/10.png" width="320" /></a></div><p> Далее аналогично можно расширить устройство на больше количество каналов. Есть некоторое количество способов использования нескольких каналов но наиболее простой в настройке - это использование прерывистого режима. Микроконтроллер stm32f103c8 (установленный в популярной плате "<a href="http://alii.pub/3esv9s" target="_blank">plue pill</a>") может автоматически поочередно переключать, своим мультиплексором, свой АЦП между разными каналами но если это регулярные каналы то регистр данных у них один и данные в нем будут также автоматически перезаписываться после автоматического переключения, поэтому чтобы "спокойно" забрать результат из регистра данных нужно чтобы после преобразования наступила пауза. Если включить прерывистый режим, установкой бита 11 регистра CR1,</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiENzgp4R0SapCUi-q3kuvC-TevP6VjBOQUDg4kIUlzQnhYVsNseR6blz4ZyGtEY3PPLyD6BIXOEQaOgNIKLopjAZjm7HrQoh-FCJBBq-LcyyAk-Hd-f-0DCOk--BFHARiL6IJEO-jrQ4UKhUIqy69IMCBY0T9_5KmODQnyjMjhmTGuCAZbnvcpr509/s774/1)%20%D0%BF%D1%80%D0%B5%D1%80%D1%8B%D0%B2%D0%B8%D1%81%D1%82%D1%8B%D0%B9%20%D1%80%D0%B5%D0%B6%D0%B8%D0%BC%20%D0%B4%D0%BB%D1%8F%20%D0%B0%D0%B2%D1%82%D0%BE%D0%BF%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2%20%D0%90%D0%A6%D0%9F%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%20%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA%D0%B0%20%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="774" data-original-width="628" height="439" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiENzgp4R0SapCUi-q3kuvC-TevP6VjBOQUDg4kIUlzQnhYVsNseR6blz4ZyGtEY3PPLyD6BIXOEQaOgNIKLopjAZjm7HrQoh-FCJBBq-LcyyAk-Hd-f-0DCOk--BFHARiL6IJEO-jrQ4UKhUIqy69IMCBY0T9_5KmODQnyjMjhmTGuCAZbnvcpr509/w357-h439/1)%20%D0%BF%D1%80%D0%B5%D1%80%D1%8B%D0%B2%D0%B8%D1%81%D1%82%D1%8B%D0%B9%20%D1%80%D0%B5%D0%B6%D0%B8%D0%BC%20%D0%B4%D0%BB%D1%8F%20%D0%B0%D0%B2%D1%82%D0%BE%D0%BF%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2%20%D0%90%D0%A6%D0%9F%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%20%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA%D0%B0%20%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F.png" width="357" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXFfFnMzA_e5iYDt_KjU8CGbpUNB1rmLzqLD4yvW9r05BHmIkjQdGYmzGC5uIm2IiQ_-mafJ0MQKRKZnGSgFX8eXqxoU7d372dUwFVVPAhTQ9e-ShElvQ7IewRvwne7pcKR9g8QU7W_GuRAwcPYcCza_RlqtgpLWUiYFtBv9V-7yIfrvsYt0uRsYgw/s400/1.2)%20%D0%BF%D1%80%D0%B5%D1%80%D1%8B%D0%B2%D0%B8%D1%81%D1%82%D1%8B%D0%B9%20%D1%80%D0%B5%D0%B6%D0%B8%D0%BC%20%D0%B4%D0%BB%D1%8F%20%D0%B0%D0%B2%D1%82%D0%BE%D0%BF%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2%20%D0%90%D0%A6%D0%9F%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%20%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA%D0%B0%20%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="302" data-original-width="400" height="242" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXFfFnMzA_e5iYDt_KjU8CGbpUNB1rmLzqLD4yvW9r05BHmIkjQdGYmzGC5uIm2IiQ_-mafJ0MQKRKZnGSgFX8eXqxoU7d372dUwFVVPAhTQ9e-ShElvQ7IewRvwne7pcKR9g8QU7W_GuRAwcPYcCza_RlqtgpLWUiYFtBv9V-7yIfrvsYt0uRsYgw/s320/1.2)%20%D0%BF%D1%80%D0%B5%D1%80%D1%8B%D0%B2%D0%B8%D1%81%D1%82%D1%8B%D0%B9%20%D1%80%D0%B5%D0%B6%D0%B8%D0%BC%20%D0%B4%D0%BB%D1%8F%20%D0%B0%D0%B2%D1%82%D0%BE%D0%BF%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2%20%D0%90%D0%A6%D0%9F%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%20%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA%D0%B0%20%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F.png" width="320" /></a></div><p>то автоматическое переключение между каналами будет прерываться после того как конверсия пройдет с каналами количество которых установлено битами 13-15 данного регистра. По умолчанию там записаны нули, что означает что перерыв будет после конверсии по каждому одному каналу - а это именно тот режим который нужен т.к. если поставить больше то данные от каналов будут перезаписываться и из регистра данным можно будет забрать результат только от последнего канала. </p><p>Далее нужно регистрами SQR1,SQR2,SQR3 настроить количество и номера каналов на которые будет переключаться АЦП. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9n-grtETg-qjIzrqcQE_oVgCvV95G7hLzyEvaP1n_Ze7NffLilQFq8YStL3jruoEdbXcJkx8_bueF6VeiXSleLSAeRzbuiAb4X-hN6fKV2RdNC6Dlt81kVDLGYK06M8qZG5sQY7K76gZn_NsjWvyHbhdaOYaNIiXdDkwWrI28WunQKbNGfXhTypeh/s806/1.3)%2024%20%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D1%8B%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B95.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="590" data-original-width="806" height="297" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9n-grtETg-qjIzrqcQE_oVgCvV95G7hLzyEvaP1n_Ze7NffLilQFq8YStL3jruoEdbXcJkx8_bueF6VeiXSleLSAeRzbuiAb4X-hN6fKV2RdNC6Dlt81kVDLGYK06M8qZG5sQY7K76gZn_NsjWvyHbhdaOYaNIiXdDkwWrI28WunQKbNGfXhTypeh/w407-h297/1.3)%2024%20%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D1%8B%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B95.png" width="407" /></a></div><br /><p>Для настройки количества каналов используются биты 20...23 регистра SQR1, 0b0000 - означает 1 канал, 0b0001 - 2 канала и т.д.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAbTco88PtM7Xf13lLVEFxaAPx05JyHn_JVj4ij0qRoGMmlpuogssP2nDKhE-zApBx2bdzDrSr8E_ol48tbtgqRWpQIN5bs4yw9skdmITwdshvpH6AAMdkqcCAfE5UxH91icASf_9HKmnbJ0VUeY3X6znF83TZbLu4RtVmzi3qTk8gwisGvDjbmkd_/s640/2)%20%D0%90%D0%A6%D0%9F%20%D0%B4%D0%B2%D0%B5%20%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8%20%D0%B4%D0%BB%D1%8F%20%D1%81%D0%BD%D1%8F%D1%82%D0%B8%D1%8F%20%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2%20%D1%81%20%D0%B4%D0%B2%D1%83%D1%85%20%D0%BF%D0%B8%D0%BD%D0%BE%D0%B2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="464" data-original-width="640" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAbTco88PtM7Xf13lLVEFxaAPx05JyHn_JVj4ij0qRoGMmlpuogssP2nDKhE-zApBx2bdzDrSr8E_ol48tbtgqRWpQIN5bs4yw9skdmITwdshvpH6AAMdkqcCAfE5UxH91icASf_9HKmnbJ0VUeY3X6znF83TZbLu4RtVmzi3qTk8gwisGvDjbmkd_/s320/2)%20%D0%90%D0%A6%D0%9F%20%D0%B4%D0%B2%D0%B5%20%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8%20%D0%B4%D0%BB%D1%8F%20%D1%81%D0%BD%D1%8F%D1%82%D0%B8%D1%8F%20%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2%20%D1%81%20%D0%B4%D0%B2%D1%83%D1%85%20%D0%BF%D0%B8%D0%BD%D0%BE%D0%B2.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDetMbhUWL9W_R3yDRqShkqmp8wHSrnM2LhFOPhH5Qd2srt34Q2q9SadBunzQJQaRKOxpB8co2494iiRcTyGiyd2WaM05umO4thNGOO6YkdYldb14luGDx5OR8NfajDI6ye4NtveW8EC256Z6BgThJ2vxDpXwmV6dxfXZ8InXl4mtBVDhKV9jWgcTd/s470/2.2)%20%D0%90%D0%A6%D0%9F%20%D0%B4%D0%B2%D0%B5%20%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8%20%D0%B4%D0%BB%D1%8F%20%D1%81%D0%BD%D1%8F%D1%82%D0%B8%D1%8F%20%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2%20%D1%81%20%D0%B4%D0%B2%D1%83%D1%85%20%D0%BF%D0%B8%D0%BD%D0%BE%D0%B2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="298" data-original-width="470" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDetMbhUWL9W_R3yDRqShkqmp8wHSrnM2LhFOPhH5Qd2srt34Q2q9SadBunzQJQaRKOxpB8co2494iiRcTyGiyd2WaM05umO4thNGOO6YkdYldb14luGDx5OR8NfajDI6ye4NtveW8EC256Z6BgThJ2vxDpXwmV6dxfXZ8InXl4mtBVDhKV9jWgcTd/s320/2.2)%20%D0%90%D0%A6%D0%9F%20%D0%B4%D0%B2%D0%B5%20%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8%20%D0%B4%D0%BB%D1%8F%20%D1%81%D0%BD%D1%8F%D1%82%D0%B8%D1%8F%20%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2%20%D1%81%20%D0%B4%D0%B2%D1%83%D1%85%20%D0%BF%D0%B8%D0%BD%D0%BE%D0%B2.png" width="320" /></a></div><br /><p>Для того чтобы вторым каналом был второй, по счету, канал нудно в биты 5....9 регистра SQR1 записать единицу т.е. записать единицу в бит 5 (т.к. остальные, по умолчанию, нули).</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivWs1Jq_aoXOSUrH08rDLtr3lTPw961PS9zSsCDqiPWLTIvHPhy60hzfY1ihj6a7wPKMR4W88OudUv-O2nd-S5EFh2rvsRcw2N4Qmmh8m3Dd23PusyBWGL_Sc6SAxWNqJTxnJAo8zHpVp4pAvjwk8sJXX9rI-QzRbMLAJMyi9rh54SlAQWvCBIAcyv/s644/3)%20%D0%90%D0%A6%D0%9F%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9%20%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%20%D0%B4%D0%BB%D1%8F%20%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9%20%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="436" data-original-width="644" height="217" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivWs1Jq_aoXOSUrH08rDLtr3lTPw961PS9zSsCDqiPWLTIvHPhy60hzfY1ihj6a7wPKMR4W88OudUv-O2nd-S5EFh2rvsRcw2N4Qmmh8m3Dd23PusyBWGL_Sc6SAxWNqJTxnJAo8zHpVp4pAvjwk8sJXX9rI-QzRbMLAJMyi9rh54SlAQWvCBIAcyv/s320/3)%20%D0%90%D0%A6%D0%9F%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9%20%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%20%D0%B4%D0%BB%D1%8F%20%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9%20%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi19Rc3oIPsgWYa7o6PVjVfoWWRsBBl5KRniTlf94p9-mEaLXCRwtr_j9ih88phXFZ9InqGP_J5wYYvP3jCfQWFYHW90WrW4ieMEcpNJPNBF4cRRrBSwQhW1S7C51Oagdtf3JaDXNNdMRSK3OdKNnSkfC5ZFFh7v9gutxw1qlSb3tsszvUbRmzPuTXc/s532/3.2)%20%D0%90%D0%A6%D0%9F%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9%20%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%20%D0%B4%D0%BB%D1%8F%20%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9%20%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="316" data-original-width="532" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi19Rc3oIPsgWYa7o6PVjVfoWWRsBBl5KRniTlf94p9-mEaLXCRwtr_j9ih88phXFZ9InqGP_J5wYYvP3jCfQWFYHW90WrW4ieMEcpNJPNBF4cRRrBSwQhW1S7C51Oagdtf3JaDXNNdMRSK3OdKNnSkfC5ZFFh7v9gutxw1qlSb3tsszvUbRmzPuTXc/s320/3.2)%20%D0%90%D0%A6%D0%9F%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9%20%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%20%D0%B4%D0%BB%D1%8F%20%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9%20%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8.png" width="320" /></a></div><br /><p>Если нужно использовать 3 канала то аналогично настраиваются биты 10...15 данного канала (не забыв при этом про настройку количества). Если регистр SQR3 закончился то дальше идет переход на SQR2 а после на SQR1 до битов настройки количества. После добавления данных 3х строк программа уже будет выдавать в последовательный порт числа поочередно с одного и с другого каналов. Для большего удобства и вывода показаний в два столбца можно добавить специальную конструкцию.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia_lMT95C0wuJ5njW2TQ1V6EQIe1jxl10KYEiGhneyPyfOZmk2yPS-T5nFeEcEY9MWOtPIHSuEZi9pCQ__D22FYtXJglPAjKYo2kqF16QXwA-mzUnkK5g7izuBVDLvhBHztWJ1awjPHVyEQbBWpGal7C3ovOxzUykPsSb2nuWbRE8LAwHhWA103FRv/s602/4.1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="524" data-original-width="602" height="279" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia_lMT95C0wuJ5njW2TQ1V6EQIe1jxl10KYEiGhneyPyfOZmk2yPS-T5nFeEcEY9MWOtPIHSuEZi9pCQ__D22FYtXJglPAjKYo2kqF16QXwA-mzUnkK5g7izuBVDLvhBHztWJ1awjPHVyEQbBWpGal7C3ovOxzUykPsSb2nuWbRE8LAwHhWA103FRv/s320/4.1.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7H3fhOqWwA_nNuxkw3a4ZwuAQIc8ilfFPo6pEIoheo9nBNLJUVT78ZmAvkNw5GbRpQHARDwIP6f-orbPyDJpD7DttUu5lfM34NitJf5DH_CW_O3NDqoUPrV29TVXmV0E2RvS2qal7_n7Vw1vKkiFI1DgPZXaHPqMrFkHJtGzMrEoFvDqTtj7gz3th/s600/4.2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="498" data-original-width="600" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7H3fhOqWwA_nNuxkw3a4ZwuAQIc8ilfFPo6pEIoheo9nBNLJUVT78ZmAvkNw5GbRpQHARDwIP6f-orbPyDJpD7DttUu5lfM34NitJf5DH_CW_O3NDqoUPrV29TVXmV0E2RvS2qal7_n7Vw1vKkiFI1DgPZXaHPqMrFkHJtGzMrEoFvDqTtj7gz3th/s320/4.2.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Полный код можно скопировать из текстового поля:<div><br /></div><div><textarea colls="100" readonly="readonly" rows="2200" style="height: 1000px; margin: 0px; width: 650px;">
#include "stm32f10x.h"
int main(void)
{
char key = "\n";
RCC->CFGR |= 3<<14;//поделим частоту тактирования АЦП на 8
RCC->APB2ENR |= (1<<14) | (1<<2) | (1<<9);//включаем тактирование UART1 (14)
GPIOA->CRH |= 0b1011<<4; // PA9 - выход 50МГц, push-pull,
GPIOA->CRH &= 0b1011<<4; // альтернативная функция UART
//Входной пин АЦП //можно не трогать т.к. подтяжка не нужна из за потенциометра
USART1->BRR = 7500; // 72000000 Гц / 9600 бод = 7500
USART1->CR1 |= (1<<3) | (1<<13);//разблокировать UART(13), передачу(3)
ADC1->CR1 |= 1<<11;//включение прерывистого режима
ADC1->SQR1 |= 1<<20;//две конверсии
ADC1->SQR3 |= 1<<5;//вторая конверсия на втором канале
ADC1->CR2 |= 0b111<<17;//выбор программного запуска АЦП
ADC1->CR2 |= 1<<20;//Разрешение внешнего запуска преобразования для регулярных каналов
ADC1->CR2 |= 1;//включить АЦП
ADC1->CR2 |= 1<<2; // запуск калибровки
while (!(ADC1->CR2 & (1<<2))){} // ожидание окончания калибровки
while(1)
{
ADC1->CR2 |= 1<<22; // запуск АЦП
while(!(ADC1->SR & 0b10)){} // ожидание АЦП
int res = ADC1->DR; // забрать результат АЦП
//разделить число из АЦП на символы и отправить по UART
for(float i=1000;i>=1;i/=10)
{
while (!(USART1->SR & 0b10000000)){} //дождатся опустошения буфера отправки байта данных
USART1->DR = (char)((char)(res/i)%10)+48;
}
while (!(USART1->SR & 0b10000000)){} //дождатся опустошения буфера отправки байта данных
if(key=='\n')
{
key=' ';//для отправки пробела
}
else
{
key='\n';//для отправки перевода строки
}
USART1->DR = key;
for(long i=0;i<1000000;i++){} // задержка
}
}
</textarea><br /><p>Посмотреть результат работы программы можно на видео:</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/kf2ssLxesHY" width="320" youtube-src-id="kf2ssLxesHY"></iframe></div><br /><p><a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-6519484152553166242022-08-31T08:00:00.001-07:002022-08-31T10:07:47.494-07:00АЦП (ADC) на stm32f103, Си и cmsis<p> Аналого цифровой преобразователь (далее АЦП) является важным периферийным устройством в микроконтроллере т.к. аналоговые сигналы, наряду с цифровыми, часто используются. Примеров ситуаций при которых используются аналоговые сигналы можно привести много. Это например может быть какой нибудь датчик температуры, напряжения, токовый шунт, джойстик, резистивная кнопка и т.д. АЦП микроконтроллеров плохи тем что напряжения подаваемые на пин микроконтроллера часто небольшие (особенно для STM32 это 3.3В) а также тем что они могут уступать по быстродействию и/или разрядности специализированным микросхемам. Первый недостаток устраняется использованием резистивного делителя напряжения а второй тем что это не всегда имеет большое значение а также большим количеством настроек аппаратного АЦП данного микроконтроллера для управления точностью, скоростью и количеством каналов. Начать изучение АЦП микроконтроллеров <a href="http://alii.pub/3esv9s" target="_blank">STM32</a> желательно с чего нибудь простого. Например можно подключить к нему какой нибудь простой потенциометр и попытаться определить напряжение на среднем его выводе а результат выводить например по uart на пк и смотреть его в мониторе последовательного порта среды разработки Arduino IDE. Для начала нужно выбрать пин микроконтроллера к которому можно подключить средний вывод потенциометра. Для этого можно глянуть таблицу в даташите:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi4Fy-EAYDt9BciNFmn2bc1tPU12cyR1KMW-BgE7KXzBEjPzDIxUwNDr8wt5VKMzwtiX80pBfcZh0rL7MTvM5T9Hd3DKznErpMBo0NaE143epKjOj1kmHGEuEOTrqzHjpoFXQ6KmdueycYSLt_2u9ZE7HSfEx-GWtmfVKCk1BzEIgEVVt_uhlAFmjae=s632" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="518" data-original-width="632" height="262" src="https://blogger.googleusercontent.com/img/a/AVvXsEi4Fy-EAYDt9BciNFmn2bc1tPU12cyR1KMW-BgE7KXzBEjPzDIxUwNDr8wt5VKMzwtiX80pBfcZh0rL7MTvM5T9Hd3DKznErpMBo0NaE143epKjOj1kmHGEuEOTrqzHjpoFXQ6KmdueycYSLt_2u9ZE7HSfEx-GWtmfVKCk1BzEIgEVVt_uhlAFmjae=s320" width="320" /></a></div>Если выбрать пин PA0 то дальше настройка потенциометра будет немного проще чем если выбрать другой пин. Т.к, при таком варианте, не нужно будет настраивать регистры SQR. Схема может быть например такой:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEguMqX9zv7577g4ovTM2hIaJWqRryh5IWJBmWQ34kMS1p6_PbMBiCyOAeRig1MeNio9o-SCseB3seGawUOEvDm70xXEuGq6DODYCpoCqFPicMFVyjx8us7twZxr1ko3eSujNzhdUZI5fCabak6Y4D3h7EZlBngCQy1rKGEudlJOVJcv5Ypj4ZjZb2qy=s847" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="660" data-original-width="847" height="249" src="https://blogger.googleusercontent.com/img/a/AVvXsEguMqX9zv7577g4ovTM2hIaJWqRryh5IWJBmWQ34kMS1p6_PbMBiCyOAeRig1MeNio9o-SCseB3seGawUOEvDm70xXEuGq6DODYCpoCqFPicMFVyjx8us7twZxr1ko3eSujNzhdUZI5fCabak6Y4D3h7EZlBngCQy1rKGEudlJOVJcv5Ypj4ZjZb2qy=s320" width="320" /></a></div>Сопротивление резистора может быть другим но главное чтобы оно не было слишком низким чтобы не сделать слишком большую нагрузку на цепи питания. Конденсатор нужен для уменьшения погрешности измерения путем ослабления помех. Как обычно для периферийных устройств для АЦП нужно включить тактирование, но только теперь помимо этого нужно установить делитель частоты т.к. АЦП не может работать на частоте более 14МГц. Чтобы например сделать деление на 8 нужно записать единицы в 14й и 15й биты регистра конфигурации тактирования.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiAkgqp4Ez7OhOO_1xidepI2rHSvE2KLEng3n1-sZPBSDPwaKWVQ-r9yZ5b_FJ3q2442kWX2ZJng153G0EEhXeiyIfAECW9T02S-8mCpKmVqOmb9IHNrswWa5vOczAGqu3GyY-mQr2S_jX_GGE66qc3XN7scdOHrys6V0bMOsk55plRruYIHqMIXIQ7=s630" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="454" data-original-width="630" height="231" src="https://blogger.googleusercontent.com/img/a/AVvXsEiAkgqp4Ez7OhOO_1xidepI2rHSvE2KLEng3n1-sZPBSDPwaKWVQ-r9yZ5b_FJ3q2442kWX2ZJng153G0EEhXeiyIfAECW9T02S-8mCpKmVqOmb9IHNrswWa5vOczAGqu3GyY-mQr2S_jX_GGE66qc3XN7scdOHrys6V0bMOsk55plRruYIHqMIXIQ7=s320" width="320" /></a></div>Далее, как обычно (см. предыдущие уроки), настраивается тактирование периферийных устройств (GPIO, АЦП и UART) а также режимы работы GPIO (в данном случае пин PA0 настраивается на альтернативный вход) и UART1 настраивается на передачу.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhkk1rcl1ZLoXzt-5tVNNT7C3kL3EIDfVWQSQrpLDgFxzVe5RhIUtdhH286CAaUTS7NluBCn3ciTPDTf1XzVKbfiXIeUGzKUXjOg63Frwrw_K4lJgGc3BfhXoma6fezUZHitd5TZPTZdzcM8o8xMFrlreEBc1iUR3mg7GawbJcfWmmxMccPKCJ5-c5c=s604" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="362" data-original-width="604" height="192" src="https://blogger.googleusercontent.com/img/a/AVvXsEhkk1rcl1ZLoXzt-5tVNNT7C3kL3EIDfVWQSQrpLDgFxzVe5RhIUtdhH286CAaUTS7NluBCn3ciTPDTf1XzVKbfiXIeUGzKUXjOg63Frwrw_K4lJgGc3BfhXoma6fezUZHitd5TZPTZdzcM8o8xMFrlreEBc1iUR3mg7GawbJcfWmmxMccPKCJ5-c5c=s320" width="320" /></a></div>Далее если бы использовался бы пин не PA0 а другой то тогда бы пришлось настраивать регистры SQR. В данном микроконтроллере этих регистров 3 штуки ADC_SQR1, ADC_SQR2 и ADC_SQR3. В биты 20-23 регистра ADC_SQR1 записывается количество измерений за один проход. АЦП в данном микроконтроллере не один и каналов АЦП больше одного а также АЦП дает погрешность для уменьшения которой может понадобиться усреднение значения с некоторого количества измерений. В остальные биты данных регистров, за исключением битов 24-31 регистра ADC_SQR1, записываются номера каналов на которых будут производиться измерения. Первый из них записывается в биты 0...4 регистра ADC_SQR3 далее записываются остальные каналы. Если регистр ADC_SQR3 заканчивается то далее аналогично продолжается запись в регистр ADC_SQR2. Если и ADC_SQR2 тоже заканчивается то далее заполняется регистр ADC_SQR1 до бита 20 т.к. с этого бита начинается запись количества каналов. Т.к. по умолчанию в этих регистрах нули то это означает что измерение будет проводиться один раз на нулевом канале поэтому в данном случае данные регистры можно не трогать.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiKFXcZ3Mxss_thmoUoVg7QnZOoM-oIch2FHngV4kymg3ip5BLnzYLZcQYoFqsLtDl0cKqjHkkWyxIX3FFW52FjoA4Do3CZxC8RIDSTo_vZL4b8LQR0eLYk6ocsrr57XoMwf9APlE5gIjOFq4L2nflESJ8fkNVhYzPibGFPAHVZ0HthhWntoUISvAnh=s806" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="590" data-original-width="806" height="234" src="https://blogger.googleusercontent.com/img/a/AVvXsEiKFXcZ3Mxss_thmoUoVg7QnZOoM-oIch2FHngV4kymg3ip5BLnzYLZcQYoFqsLtDl0cKqjHkkWyxIX3FFW52FjoA4Do3CZxC8RIDSTo_vZL4b8LQR0eLYk6ocsrr57XoMwf9APlE5gIjOFq4L2nflESJ8fkNVhYzPibGFPAHVZ0HthhWntoUISvAnh=s320" width="320" /></a></div>Есть ещё регистр настройки времени выборки ADC_SMPR2<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjz7YugLj06QNYgX_uf66moQPJPiaU6Rt-szpcAcKwPmPvHa1bmM4nNbkfVHbsB_GFvgCN0E7dzXt-5sJFJwm_hD5Mek9wl5bXhYF-72cThy4yO32tUko_RrOA_KJ_KRckpMr5MyMA2xPbGg3OSLnwTf_jRAOzq7JiUySGRLDCLPaLQS6gWkiuGhuy6=s650" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="438" data-original-width="650" height="216" src="https://blogger.googleusercontent.com/img/a/AVvXsEjz7YugLj06QNYgX_uf66moQPJPiaU6Rt-szpcAcKwPmPvHa1bmM4nNbkfVHbsB_GFvgCN0E7dzXt-5sJFJwm_hD5Mek9wl5bXhYF-72cThy4yO32tUko_RrOA_KJ_KRckpMr5MyMA2xPbGg3OSLnwTf_jRAOzq7JiUySGRLDCLPaLQS6gWkiuGhuy6=s320" width="320" /></a></div>Можно пробовать изменять значение в этом регистре чтобы повлиять на точность измерений. Работа АЦП может быть запущена разными способами. Для установки запуска программным способом нужно в регистр в биты 17...19 регистра ADC_CR2 записать единицы. <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEheqMDstJVlCuvIFDNuJDbOqqq7jTco3KCycvCzhJFGGUZZLN9EpiRwnXo9d-gpoth4JMlKnYXBoTIZ1wbxeyXEqhg8jaoYdsX65Eg7DqeiZ9gYTzAKskVlfYyndfP6l0xtTEfjoGyDI6SS4YxTcnT_TsBW1us4h1z9dPuQWPkJY6oR7fFfRv2KTKEv=s712" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="646" data-original-width="712" height="290" src="https://blogger.googleusercontent.com/img/a/AVvXsEheqMDstJVlCuvIFDNuJDbOqqq7jTco3KCycvCzhJFGGUZZLN9EpiRwnXo9d-gpoth4JMlKnYXBoTIZ1wbxeyXEqhg8jaoYdsX65Eg7DqeiZ9gYTzAKskVlfYyndfP6l0xtTEfjoGyDI6SS4YxTcnT_TsBW1us4h1z9dPuQWPkJY6oR7fFfRv2KTKEv=s320" width="320" /></a></div><div><div>Также нужно установить бит 20 данного регистра в единицу для разрешения внешнего запуска.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgdSa-MY3X0qpZnwpwX389kJtAUBMeD-pPWr-rcR3Vr5wF7WGMRggMNso_ikSz7stgtLzL_Ywx6m_roItB8fVDOrcJojdTd8hEft8VJT98AgaXmVQ7lT2B7OIJDuqSZWSlJM0MHvLUe-HxsycWEuPxxucRDnriIwxpcVSiN4Px2GDE86ykA9xo8n6oC=s716" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="528" data-original-width="716" height="236" src="https://blogger.googleusercontent.com/img/a/AVvXsEgdSa-MY3X0qpZnwpwX389kJtAUBMeD-pPWr-rcR3Vr5wF7WGMRggMNso_ikSz7stgtLzL_Ywx6m_roItB8fVDOrcJojdTd8hEft8VJT98AgaXmVQ7lT2B7OIJDuqSZWSlJM0MHvLUe-HxsycWEuPxxucRDnriIwxpcVSiN4Px2GDE86ykA9xo8n6oC=s320" width="320" /></a></div>Для включения АЦП, в единицу устанавливается бит 0 данного регистра.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiaFVJ8qJwcGsF4CBKyzfQfDph0Sng_K0yAqP7vN-BMtuxnKh6x0gjfQn_toT6A2Rg3lgyECMs_EV9rG3-hHADxHaXAhEzCXsflaAi6J0p6HADt9GNGfIu4ebW158htT4FRGaNwCbS0pPjZ-e3qL6G9GY9UdC1MOmYbaWfTb-NCGaI5lV6pnoCo8bG2=s716" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="528" data-original-width="716" height="236" src="https://blogger.googleusercontent.com/img/a/AVvXsEiaFVJ8qJwcGsF4CBKyzfQfDph0Sng_K0yAqP7vN-BMtuxnKh6x0gjfQn_toT6A2Rg3lgyECMs_EV9rG3-hHADxHaXAhEzCXsflaAi6J0p6HADt9GNGfIu4ebW158htT4FRGaNwCbS0pPjZ-e3qL6G9GY9UdC1MOmYbaWfTb-NCGaI5lV6pnoCo8bG2=s320" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEill1-HT96hh6x-IQcfT67R2oxvaRW0ZTJurM6RZ2sjDtI5oDbsn-6yd0zsDTzaqdTBkge8aD2Lrm_BIQJLln3Y9vqO77IAWYi11Hs5rusfK0A_yPcQwdbhm2Yb2KC-4GKkHiJB6p1RbE6IL47jrqhZ1YAWDS2JJ-fKqmvWzxZH4S0Y4RlD0hAhl5Sc=s612" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="612" height="188" src="https://blogger.googleusercontent.com/img/a/AVvXsEill1-HT96hh6x-IQcfT67R2oxvaRW0ZTJurM6RZ2sjDtI5oDbsn-6yd0zsDTzaqdTBkge8aD2Lrm_BIQJLln3Y9vqO77IAWYi11Hs5rusfK0A_yPcQwdbhm2Yb2KC-4GKkHiJB6p1RbE6IL47jrqhZ1YAWDS2JJ-fKqmvWzxZH4S0Y4RlD0hAhl5Sc=s320" width="320" /></a></div>Для запуска калибровки АЦП нужно установить бит 2 данного регистра. <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjN3Sf-Mz6UUPBUkg-yQDCw6NMj4HcuTGvfLsCa1TQx9w9khpBLYnKCfcCr_djRUfxICRTZLIy_OgYmGfQuI5n5gDk2eYTV8Yz1FV8oPF-opEb3_LiOXUMNHQUu_bSYyajLQsyaouP12w_7qoE-yJQZeFU5znnObipZ6k2BHJ89_CYmTiLwKfF9Ugqd=s716" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="528" data-original-width="716" height="236" src="https://blogger.googleusercontent.com/img/a/AVvXsEjN3Sf-Mz6UUPBUkg-yQDCw6NMj4HcuTGvfLsCa1TQx9w9khpBLYnKCfcCr_djRUfxICRTZLIy_OgYmGfQuI5n5gDk2eYTV8Yz1FV8oPF-opEb3_LiOXUMNHQUu_bSYyajLQsyaouP12w_7qoE-yJQZeFU5znnObipZ6k2BHJ89_CYmTiLwKfF9Ugqd=s320" width="320" /></a></div><div>По окончании калибровки данный бит сам сброситься. Чтобы преждевременно не начать работу с АЦП можно организовать проверку данного бита в цикле.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhaQlv7ihLO9zbIeIHaWl2Jpa4Q1RSllfIxZKQriNsxHWe-nNbRcZjDuOzGOYCGOnixrsbGA28FmRgI8rqlNtacq8i4ryylEXW2Uogzz6pxqq7-DTelt-8GfKQ-U4d5D72L4RxeHBXNzopybeRsbc2tETVnQGoj7TZ4V9q2ZBrVdIuSEP0HRvLKTH9E=s614" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="364" data-original-width="614" height="190" src="https://blogger.googleusercontent.com/img/a/AVvXsEhaQlv7ihLO9zbIeIHaWl2Jpa4Q1RSllfIxZKQriNsxHWe-nNbRcZjDuOzGOYCGOnixrsbGA28FmRgI8rqlNtacq8i4ryylEXW2Uogzz6pxqq7-DTelt-8GfKQ-U4d5D72L4RxeHBXNzopybeRsbc2tETVnQGoj7TZ4V9q2ZBrVdIuSEP0HRvLKTH9E=s320" width="320" /></a></div>Для запуска АЦП, устанавливается бит 22 данного регистра<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEitSpSITevQcU_Xwg3FOajgYxSu_cDR8ywaTLhoaquY-VOy25w1DK8HpNRnZi8NCI3EFbFfnzxxRtOXzfveJMU-EhL6rktaYDGahI9gutqcXueYQJTEITnYJV-yhH84aoYazhNrA4jEeUlpAwoCZ_z0n83q6n-_IKQ296Xt8ntaGmFQg2ChVF-wZv_6=s716" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="528" data-original-width="716" height="236" src="https://blogger.googleusercontent.com/img/a/AVvXsEitSpSITevQcU_Xwg3FOajgYxSu_cDR8ywaTLhoaquY-VOy25w1DK8HpNRnZi8NCI3EFbFfnzxxRtOXzfveJMU-EhL6rktaYDGahI9gutqcXueYQJTEITnYJV-yhH84aoYazhNrA4jEeUlpAwoCZ_z0n83q6n-_IKQ296Xt8ntaGmFQg2ChVF-wZv_6=s320" width="320" /></a></div><br /><div><br /><div><br /></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgLO75wsZIvXcVw1Dic0hgOs5X31010Q2TkyBF4We6P_3xgHl_nnHr8b0-tca6gld6o5cjzlzN4F_usSayF3hDjiwAGRC4_CbyKblilXTzATWPePDUOEO-0AAYG1PQ73tF-ySGwp--nO30zwoy4ol3jq3ZZ4sZFoowYtWh40rZ0X-bnH10EjhjiUzSP=s612" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="370" data-original-width="612" height="193" src="https://blogger.googleusercontent.com/img/a/AVvXsEgLO75wsZIvXcVw1Dic0hgOs5X31010Q2TkyBF4We6P_3xgHl_nnHr8b0-tca6gld6o5cjzlzN4F_usSayF3hDjiwAGRC4_CbyKblilXTzATWPePDUOEO-0AAYG1PQ73tF-ySGwp--nO30zwoy4ol3jq3ZZ4sZFoowYtWh40rZ0X-bnH10EjhjiUzSP=s320" width="320" /></a></div>Об окончании аналого цифрового преобразования свидетельствует бит 1 регистра ADC_SR<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgBBizfSpoQTTZcTeXbsECRBG2gd74K68-42bYkE0ziBD_X2VuSVr3uNMPKMbYBKedfMryITpkcHD0PauD8ojsE22hgTjQBBNf2VMM51Z46rdmlpKkNfp15N3xHkR7TmA8Zk0q9PRUIuPRJBdUgIWGUKBDkQo1i8kqBoFfyLigs4gXjJwcVvrUjtnZ9=s726" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="550" data-original-width="726" height="242" src="https://blogger.googleusercontent.com/img/a/AVvXsEgBBizfSpoQTTZcTeXbsECRBG2gd74K68-42bYkE0ziBD_X2VuSVr3uNMPKMbYBKedfMryITpkcHD0PauD8ojsE22hgTjQBBNf2VMM51Z46rdmlpKkNfp15N3xHkR7TmA8Zk0q9PRUIuPRJBdUgIWGUKBDkQo1i8kqBoFfyLigs4gXjJwcVvrUjtnZ9=s320" width="320" /></a></div><br /><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjvuJDwYvfTJxa3sOmkQDFiW0gAifWOGe4tr6mF2-2mt9Ge3bdFkx5xCxK9VmiTjD5GhR3eTNM0F_j2b9duN3ocAjT5hvp6mWjLuiJNry_Em3wIophXxRBQu3-w5C5RjWqtX0AU9dFwzzfH5GTVbANBesmRqv_cBB974V0q4ejKLBlXRLg2Rfgi_7K7=s600" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="600" height="192" src="https://blogger.googleusercontent.com/img/a/AVvXsEjvuJDwYvfTJxa3sOmkQDFiW0gAifWOGe4tr6mF2-2mt9Ge3bdFkx5xCxK9VmiTjD5GhR3eTNM0F_j2b9duN3ocAjT5hvp6mWjLuiJNry_Em3wIophXxRBQu3-w5C5RjWqtX0AU9dFwzzfH5GTVbANBesmRqv_cBB974V0q4ejKLBlXRLg2Rfgi_7K7=s320" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;">Теперь можно забрать результат преобразования из регистра данных ADC1_DR</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi5x0UssIlAlGdoBTIhqXFsgmwdza5YJucaAPwxuqy22PlUV6f5VgEKkqkmw8nnRevdytqFNwUEh3yyLsrWQgdOnCCUBGfhUxWJzAZxKKp-QA9bfqjEsdhXi2e1B_VmScFCDp8E0vOlLr9eoUMhQCEzJOmdvwgETJOW7SPB6OO63tiVlY_aA2cMIzBS=s612" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="374" data-original-width="612" height="196" src="https://blogger.googleusercontent.com/img/a/AVvXsEi5x0UssIlAlGdoBTIhqXFsgmwdza5YJucaAPwxuqy22PlUV6f5VgEKkqkmw8nnRevdytqFNwUEh3yyLsrWQgdOnCCUBGfhUxWJzAZxKKp-QA9bfqjEsdhXi2e1B_VmScFCDp8E0vOlLr9eoUMhQCEzJOmdvwgETJOW7SPB6OO63tiVlY_aA2cMIzBS=s320" width="320" /></a></div>Чтобы увидеть результат можно его преобразовать в символы и отослать по UART на компьютер. Полный код программы можно скачать из текстового поля:<div><textarea colls="100" readonly="readonly" rows="1700" style="height: 700px; margin: 0px; width: 650px;">
#include "stm32f10x.h"
int main(void)
{
RCC->CFGR |= 3<<14;//поделим частоту тактирования АЦП на 8 (2на6,1на4,0на2) (72/8=9,72/6=12,дальше нельзя т.к. частота не д.б.>14)
RCC->APB2ENR |= (1<<14) | (1<<2) | (1<<9);//включаем тактирование UART1 (14), порта A (2), АЦП1 (9)
GPIOA->CRH |= 0b1011<<4; // PA9 - выход 50МГц, push-pull,
GPIOA->CRH &= 0b1011<<4; // альтернативная функция UART
//Входной пин АЦП //можно не трогать т.к. подтяжка не нужна из за потенциометра
USART1->BRR = 7500; // 72000000 Гц / 9600 бод = 7500
USART1->CR1 |= (1<<3) | (1<<13);//разблокировать UART(13), передачу(3)
ADC1->CR2 |= 0b111<<17;//выбор программного запуска АЦП
ADC1->CR2 |= 1<<20;//Разрешение внешнего запуска преобразования для регулярных каналов.
ADC1->CR2 |= 1;//включить АЦП
ADC1->CR2 |= 1<<2; // запуск калибровки
while (!(ADC1->CR2 & (1<<2))){} // ожидание окончания калибровки
while(1)
{
ADC1->CR2 |= 1<<22; // запуск АЦП
while(!(ADC1->SR & 0b10)){} // ожидание АЦП
int res = ADC1->DR; // забрать результат АЦП
//разбить число из АЦП на символы и отправить по UART
for(float i=1000;i>=1;i/=10)
{
while (!(USART1->SR & 0b10000000)){} //дождатся опустошения буфера отправки байта данных
USART1->DR = (char)((char)(res/i)%10)+48;
}
while (!(USART1->SR & 0b10000000)){} //дождатся опустошения буфера отправки байта данных
USART1->DR = '\n';//отправить перевод строки
for(long i=0;i<1000000;i++){} // задержка
}
}
</textarea><br /><div class="separator" style="clear: both; text-align: left;">Данный урок в видеоформате можно посмотреть на видео:</div></div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/ao2Ha02RAU0" width="320" youtube-src-id="ao2Ha02RAU0"></iframe></div><a href="http://electe.blogspot.com/p/blog-page.html" title="Здесь ссылки на статьи этого блога и другие полезные сайты">
<b>КАРТА БЛОГА (содержание)</b>
</a><br /><div class="separator" style="clear: both; text-align: left;"><br /></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-45280147604695904662022-08-06T03:07:00.000-07:002022-08-06T03:07:32.716-07:00Система двухканального радиоуправления без микросхем<p> Современные микросхемы и электронные модули значительно упрощают процесс изготовления разнообразных электронных устройств на их основе. Особенно это актуально для устройств работающих с радиоволнами т.к. для высокочастотных устройств имеются определнные требования к качеству разводки и изготовления плат и некоторым другим вещам которые бывает трудно реализовать в неподходящих, для этого, условиях. Однако же электронные системы, работающие с радиоволнами, существовали до появления микросхем а следовательно могут быть реализованы на дискретных элементах. Делать это может быть необходимо по разным причинам. Давайте представим например что сделать такое устройство необходимо например в гипотетической стране которая по каким то причинам не ведет торговлю с другими странами а производить может только дискретные элементы. Удивительная ситуация но допустим например чисто теоретически что она возникла. В одной из предыдущих статей рассматривалась многоканальная система радиоуправления с микросхемой-счетчиком для распределения одного канала импульсов на их большее количество. Одним из возможных вариантов построения многоканальной системы радиоуправления будет - замена двоичного интегрального счетчика на двоичный транзисторный счетчик. Двоичный счетчик может быть построен на Т-триггерах. Т-триггер - это такой триггер который меняет свое состояние на выходе при подаче сигнала на его вход т.о. получается что один такой триггер является одноразрядным двоичным счетчиком. Если соединить выход одного Т триггера со входом другого то получиться двухразрядный двоичный счетчик который можно применить для увеличения количества каналов системы радиоуправления. Однако построение такого счетчика на дискретных элементах - не простая задача т.к. для его реализации требуется большое количество деталей. Если пытаться его уменьшать всевозможными ухищрениями то можно ухудшить его качество. Под "качеством", в данном случае, можно подразумевать такие характеристики как стабильность, скорость работы (что важно для двухканальной системы "реального времени") и возможно некоторые другие. За основу для построения Т триггера, на дискретных элементах, можно взять обычный RS триггер на транзисторах:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuHaUvUq1OVw-ucSQBW7ySn6ZIukL5s8VkG1uH-ImT_sgDacFeT64VD-XvNUNkvWqCIM66m2N-tGIKW0FV7gRIuVAsFZf8dTP97Ly1pmy9L6msEqt7tGvfGeJL-G5z6SEiQZBVwXn7j5UtB7sHTLHJePxDMhL5tTgqFYBHcNhnSgU8eIKrxhTudSXH/s327/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="245" data-original-width="327" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuHaUvUq1OVw-ucSQBW7ySn6ZIukL5s8VkG1uH-ImT_sgDacFeT64VD-XvNUNkvWqCIM66m2N-tGIKW0FV7gRIuVAsFZf8dTP97Ly1pmy9L6msEqt7tGvfGeJL-G5z6SEiQZBVwXn7j5UtB7sHTLHJePxDMhL5tTgqFYBHcNhnSgU8eIKrxhTudSXH/s320/1.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - RS триггер на транзисторах</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>Для построения Т триггера на основе данного RS триггера, его нужно дополнить элементами задержки:</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFMsc3j4jonycF2Z5l4h-wjBKK59MKzfoF0ZLKWW0RccKxr2IFWMDBUfSDjf5otDTFtGNHTUIhVDyskAfbBmzb_DZ-68ozEXGsycF4nZqWns0oVje5kpw8SylVlC339uzoP1-AuRRb6HmeG2jqDYkTN2prJEK6OyNCi65HWb7oJ_qbLYMl08BgCttZ/s440/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="239" data-original-width="440" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFMsc3j4jonycF2Z5l4h-wjBKK59MKzfoF0ZLKWW0RccKxr2IFWMDBUfSDjf5otDTFtGNHTUIhVDyskAfbBmzb_DZ-68ozEXGsycF4nZqWns0oVje5kpw8SylVlC339uzoP1-AuRRb6HmeG2jqDYkTN2prJEK6OyNCi65HWb7oJ_qbLYMl08BgCttZ/s320/2.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 2 - RS триггер на транзисторах с элементами задержки</div><div><br /></div>Это нужно для того чтобы триггер мог поменять свое состояние относительно того которое, как бы временно сохранено в таком импровизированном ОЗУ. Иначе триггер бы переключившись, сразу же бы увидел новое состояние и начал бы снова переключаться и т.д. т.е. без элементов задержки сделать Т триггер нельзя. Далее понадобятся два логических элемента "И-НЕ" чтобы просадить на землю одну из половин триггера в зависимости от того какая из них просажена в данный момент и чтобы это происходило только в том случае если на вход подан короткий запускающий импульс. Запускающий импульс должен быть коротким для того чтобы напряжения на конденсаторах элементов задержки не успели сильно измениться и повлиять на правильную работу триггера. Данное поведение не сильно соответствует обычному Т триггеру который может работать с обычными сигналами но это плата за простоту схемы, иначе пришлось бы использовать большое количество транзисторов. Схема с добавленными элементами "И-НЕ" :</div><div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiB00TnVYQAoIub9M9Ek69Vqhm-D_ZPbR7TrfnzZEMm1HHHvf1BRF6meSL33VfJ3j3meP5JDCXHk2J1RiTR7qvpq4NPxGPfhkuRy4rCyhhBFLCYcRdrtqSVui5mwUlSUfoocREkl_tOMqi-ZeCKf8C8gbWLau_gDfWCAcfPlSdwEpO4Y7Qiw7aROpF/s778/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="355" data-original-width="778" height="146" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiB00TnVYQAoIub9M9Ek69Vqhm-D_ZPbR7TrfnzZEMm1HHHvf1BRF6meSL33VfJ3j3meP5JDCXHk2J1RiTR7qvpq4NPxGPfhkuRy4rCyhhBFLCYcRdrtqSVui5mwUlSUfoocREkl_tOMqi-ZeCKf8C8gbWLau_gDfWCAcfPlSdwEpO4Y7Qiw7aROpF/s320/3.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;">Рисунок 3 - RS триггер на транзисторах с элементами задержки и "И-НЕ"</div><br /> Для укорачивания запускающего импульса можно использовать конденсатор а для того чтобы он мог разрядиться можно поставить паралельно ему резистор с большим сопротивлением чтобы он не сильно влиял на вход триггера. К сожалению запустить такой триггер получается только применением ключа с низким сопротивлением в открытом состоянии поэтому из наиболее распространеных дискретных элементов запустить его получается только у реле. А так как коллекторы половин RS триггера очень чувствительные то для передачи сигнала от них получилось применить только оптроны. Возможно подбором элементов можно добиться других решений но в ходе не сильно времязатратных опытов, рабочим вариантом двухразрядного двоичного счетчика на дискретных элементах оказалась такая схема:</div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW62XlTrNZkk8qlKfwNDevAvh9oQl0KBrfrs5pRfbDh1DebMWf_BwWvbexqKPycx4rZNScrgqrkPgt1dgwiUEkUXfviGNlImiFmw4G6yNexm-kFaXZeVh9ykuLr5x_dJDtQ2Hy6q3C2l4H8VUQkuXrU3SEjVdYfRX31jJidUj7R9krZiLgu1EkcvLE/s1728/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="388" data-original-width="1728" height="125" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW62XlTrNZkk8qlKfwNDevAvh9oQl0KBrfrs5pRfbDh1DebMWf_BwWvbexqKPycx4rZNScrgqrkPgt1dgwiUEkUXfviGNlImiFmw4G6yNexm-kFaXZeVh9ykuLr5x_dJDtQ2Hy6q3C2l4H8VUQkuXrU3SEjVdYfRX31jJidUj7R9krZiLgu1EkcvLE/w557-h125/5.png" width="557" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 4 - Двухразрядный двоичный счетчик на дискретных элементах</div><br /><div>Данную схему можно совместить с приемником (рассмотренным в статье <a href="http://electe.blogspot.com/2020/04/blog-post.html">http://electe.blogspot.com/2020/04/blog-post.html</a>):</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZo3p0LP5hH86ImyGeFXtfBPBV7CmYbQmOeSQjCHkYLxpIK2V0Rm0WunyLAkViZkbZTPMX22HXWnr9cT3vufiKACe1HvRGlE6MIPInB5eNmWgRwd2GyiOhmEPqcW23fieJ1bBnM20b1k7aBJhdYiHZxxeQdFfZ8myyaACj6eexzn9efgG-s6Zsdj3y/s1788/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="848" data-original-width="1788" height="152" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZo3p0LP5hH86ImyGeFXtfBPBV7CmYbQmOeSQjCHkYLxpIK2V0Rm0WunyLAkViZkbZTPMX22HXWnr9cT3vufiKACe1HvRGlE6MIPInB5eNmWgRwd2GyiOhmEPqcW23fieJ1bBnM20b1k7aBJhdYiHZxxeQdFfZ8myyaACj6eexzn9efgG-s6Zsdj3y/s320/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 5 - Схема двухканального радиоприемника на дискретных элементах</div><div><br /></div>В одну из половин одного из триггеров добавлен неиспользуемый оптрон для равновесия триггера и увеличения стабильности работы схемы. Схема получилась громоздкой, медленной и ненадежной но тем не менее это двухканальный радиоприемник на дискретных элементах который можно пытаться совершенствовать. Увидеть его в действии (по светодиодам) можно на видео:<br /><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/w8Ay3B9vUCc" width="320" youtube-src-id="w8Ay3B9vUCc"></iframe></div><p><a href="http://electe.blogspot.com/p/blog-page.html" title="Здесь ссылки на статьи этого блога и другие полезные сайты">
<b>КАРТА БЛОГА (содержание)</b>
</a></p>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-43252895305971231522022-07-03T02:39:00.000-07:002022-07-03T02:39:35.977-07:00Связь STM32 c EEPROM по I2C на Си и CMSIS<p> Шина I2C является синхронной, последовательной и полудуплексной. Полудуплексность означает что в один момент времени по шине может происходить либо прием либо передача для какого либо одного устройства. Данная шина также ассиметричная что означает наличие ведомого (мастера который выдает импульсы синхронизации и управляет коммуникацией) и ведущего (слейва) на ней. Но в I2C, в отличии от SPI, мастеров может быть больше одного. Однако обычно мастер только один. В данной шине имеется два сигнальных провода, один нужен для передачи данных и называется SDA а другой для передачи синхроимпульсов и называется SCL. Синхроимпульсы поступают от мастера к слейвам. А данные могут поступать в обе стороны т.е. от мастера к слейву или от слейва к мастеру. Т.к. провод данных только один то пин микроконтроллера к которому подключен провод SDA должен переключаться между режимом входа и выхода в процессе работы. Если пин SDA работает как выход то этот выход должен быть с открытым стоком т.к. возможна ситуация при которой у двух и более разных устройств будут выходы на данном проводе одновременно и с разными уровнями напряжений. Тоже самое верно и для пина SCL т.к. мастеров м.б. больше одного. Данные пины, соответственно, подтягиваются к плюсу питания резисторами сопротивления которых не следует делать слишком большими т.к. чем больше сопротивление резистора тем длиннее фронт сигнала т.к. при меньшем токе медленнее заряжается паразитная емкость шины. Данная емкость также ограничивает максимальное количество устройств на шине. Однако, тем не менее, оно всё таки может быть достаточно велико и скорее всего больше чем устройств на SPI т.к. колличество слейвов на SPI ограничено количеством свободных пинов GPIO мастера. И хотя этот недостаток можно преодолеть использованием например десятичных счетчиков или сдвиговых регистров. I2C все таки более предпочтительная шина для связи большого количества устройств из за меньшего количества проводов, дополнительных деталей и большей безопасности выходов с открытым стоком по сравнению с полумостовыми выходами. Изучение данной шины лучше начать с организации связи микроконтроллера с какой нибудь не сложной спецаиализированной микросхемой, например с микросхемой EEPROM памяти. Микроконтроллеры STM32 обычно не имеют встроенной EEPROM памяти как например микроконтроллеры AVR или PIC. Но в этом нет большой необходимости т.к. EEPROM может быть заменена FLASH памятью. Внешняя EEPROM память обладает некоторыми преимуществами перед внутренней FLASH памятью. Например обычно EEPROM память обладает большим количеством циклов записи, в этом можно убедиться посмотрев даташит например на микроконтроллер ATtiny2313 где указано:</p><p><i><b> 2K Bytes of In-System Self Programmable Flash
Endurance 10,000 Write/Erase Cycles
– </b></i></p><p><i><b>128 Bytes In-System Programmable EEPROM
Endurance: 100,000 Write/Erase Cycles</b></i></p><p>Видна разница в 10 раз. Недостатком EEPROM памяти является то что срок хранения в ней информации ограничен. И хотя у современных микросхем он высок, он всё же конечен. Вынеся устройство постоянного хранения информации за пределы микроконтроллера, появляется возможность ремонта устройства с истекшим сроком хранения информации или количеством циклов записи, путем замены микросхемы памяти что дешевле и проще замены микроконтроллера. Ещё т.о. не занимая FLASH память, которая также используется для хранения инструкций выполняемых микроконтроллером, можно избежать некоторых возможных багов связанных с заимствованием FLASH памяти для кода программы и естественно так для кода программы места будет больше. Поэтому связь микроконтроллера по I2C с микросхемой EEPROM памяти имеет (помимо образовательной) также и некоторую практическую ценность. В микроконтроллере <a href="http://alii.pub/3esv9s">stm32f103c8</a> есть 2 интерфейса I2C. Для постоянного хранения информации можно использовать например микросхему EEPROM памяти AT24C64 в которой можно хранить до 64 кБ данных. Данная микросхема может хранить данные до 100 лет а количество циклов записи у неё 1 миллион раз. Она имеет пины I2C, 3 пина установки адреса (которые внутренне подтянуты к земле), пин зашиты записи (WP который внутренне подтянут к уровню отключающему данную опцию). Соединить данную микросхему с отладочной платой с микроконтроллером <a href="http://alii.pub/3esv9s" target="_blank">stm32f103c8</a> можно например так так на схеме:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgCvFuk4I5eeAbi1cJ1KwrIKsKhtBxd6hjJ_Z6uMLtQDvCwHME7wIUTVxhmDW0BSY9aK1DFzz5DmJ1rZfW9a_UtcRkPIkBMEk4HZpRXbgNPCKjdzePP6JsaONGRa63gIv_kkWB9x0b4alF8gg_N0y1temRQl9nQq6WX4IB6-SlGryFuMt4JtKcYLVG7=s1036" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="904" data-original-width="1036" height="279" src="https://blogger.googleusercontent.com/img/a/AVvXsEgCvFuk4I5eeAbi1cJ1KwrIKsKhtBxd6hjJ_Z6uMLtQDvCwHME7wIUTVxhmDW0BSY9aK1DFzz5DmJ1rZfW9a_UtcRkPIkBMEk4HZpRXbgNPCKjdzePP6JsaONGRa63gIv_kkWB9x0b4alF8gg_N0y1temRQl9nQq6WX4IB6-SlGryFuMt4JtKcYLVG7=s320" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Схема соединения платы "blue pill" с микросхемой AT24C64 по I2C</div><div><br /></div>Резисторы R3 и R4 делают подтяжку к + питания открытых стоков, чтобы помимо логических нулей могли быть и логические единицы. Они могут быть сопротивлением например 10к но для укорачивания фронтов выбраны сопротивления поменьше. Резисторы R1 и R2 нужны для ограничения тока на случай неправильного конфигурирования пинов микроконтроллера. При подключении данной микросхемы следует обратить внимание на расположение первого пина. Если на микросхеме отсутствует привычная точка для его нахождения (а такое бывает) то определить первый пин можно по скосу с одной стороны микросхемы.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjUH_MyJjD10ggX0I81jcHEqiwWxSPmstFmXLPm4BLRXYIIJsV6NlrJhlEDyNN0mmsSWfeHqqvBpGg6YTEXi-2tXSNkBrcT4vYtoOhKVNdao2587LEJZEz-gUcs1SJBD0E2rlqWb75bi5XXqHAwHMNpkpaXg2-YpqOt1awfq1AC_LYSPoKjYWFo4l-r=s750" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="550" data-original-width="750" height="235" src="https://blogger.googleusercontent.com/img/a/AVvXsEjUH_MyJjD10ggX0I81jcHEqiwWxSPmstFmXLPm4BLRXYIIJsV6NlrJhlEDyNN0mmsSWfeHqqvBpGg6YTEXi-2tXSNkBrcT4vYtoOhKVNdao2587LEJZEz-gUcs1SJBD0E2rlqWb75bi5XXqHAwHMNpkpaXg2-YpqOt1awfq1AC_LYSPoKjYWFo4l-r=s320" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 2 - Стороны корпуса микросхемы AT24C64</div><div class="separator" style="clear: both; text-align: center;"><br /></div>В даташите, почему то, не уточнили что на нижнем рисунке показан передний конец а не задний. Если микросхему перевернуть и подать напряжение наоборот (на GND + а на Vcc -) то данная микросхема начнет потреблять нехарактерно большой для неё ток. В даташите также есть диаграммы которые помогут реализовать связь с данной микросхемой по I2C.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhGLXu4RUdGU0alQfXBjzPh6wn-o4SrSGFjIiGNxiTUZi2Pf9wdHjPrXm1WCd0BMUtVgPeu6vSc_Pw5jBDIqbRaKrFaI0F_c1hlFz9RN7eih3g7eT7Djrxhq9_e8AXyLy5778HBHBe3Y2NZOQcznuR7qGjr8tOeJas20LTe1j-gE2Vk_-grmESsnK0G=s580" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="312" data-original-width="580" height="172" src="https://blogger.googleusercontent.com/img/a/AVvXsEhGLXu4RUdGU0alQfXBjzPh6wn-o4SrSGFjIiGNxiTUZi2Pf9wdHjPrXm1WCd0BMUtVgPeu6vSc_Pw5jBDIqbRaKrFaI0F_c1hlFz9RN7eih3g7eT7Djrxhq9_e8AXyLy5778HBHBe3Y2NZOQcznuR7qGjr8tOeJas20LTe1j-gE2Vk_-grmESsnK0G=s320" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 3 - Диаграмма записи одного байта в ячейку памяти микросхемы EEPROM памяти AT24C64</div><div><br /></div>Чтобы записать один байт данных в какую либо ячейку памяти микросхемы <span style="text-align: center;">AT24C64, нужно сначала передать ей, по I2C, старт сигнал. Микроконтроллер сам правильно формирует этот сигнал при выполнении соответствующей команды. После чего надо послать лог. 1 если микроконтроллер будет работать в режиме мастер и лог.0 если микроконтроллер будет слейвом. Слейвом, в данном случае, микроконтроллер точно не будет т.к. EEPROM работает только в режиме слейва и не знает к каким ячейкам её памяти необходимо обращаться в какие моменты времени. Далее, именно для данной микросхемы, передаются 3 фиксированных бита адреса, после чего 3 настраиваемых. Эти 3 настраиваемых бита можно выставить меняя логические уровни напряжений на пинах A0...A2 микросхемы </span><span style="text-align: center;">AT24C64. Если оставить данные пины никуда не подключенными то на них будут нули и следовательно 3 настраиваемых бита адреса тоже будут нулями. Если нужно увеличить память то можно подключить некоторое количество (до 8 шт. (больше адресное пространство не позволяет)) таких микросхем и использовать возможность установки адреса. Следующий бит указывает будет ли осуществляться запись байта данных в текущую ячейку EEPROM памяти (лог.0) или чтение </span><span style="text-align: center;">(лог.1)</span><span style="text-align: center;"> его из этой ячейки. После того как слейв получил все эти 8 бит он формирует ответ просаживая линию SDA на землю или формирует отсутствие ответа не просаживая линию </span><span style="text-align: center;">SDA на землю т.е. оставляя лог.1 на ней. Для чтения данного ответа или его отсутствия в микроконтроллере есть специальный флаг. После получения ответа микроконтроллер должен отослать слейву два байта адреса ячейки к которой требуется обратиться для чтения или записи в неё. Т.к. памяти в данной микросхеме 64кб то нужно именно два байта т.к. в один не поместятся все адреса а если байтов будет больше двух то как минимум один будет лишним. После передачи одного байта адреса, слейв также формирует ответ, после которого нужно отправить второй байт адреса. Если передача адреса прошла успешно то в микросхеме </span><span style="text-align: center;">EEPROM памяти произойдет переключение на ячейку памяти по переданному адресу и к этой ячейке можно будет обращаться. </span><span style="text-align: center;">EEPROM подтверждает данный успех выдавая ответ просаживанием линии SDA на 0 питания. Если последний бит байта адреса слейва указывал запись то после того как </span><span style="text-align: center;">EEPROM переключился на какую то ячейку памяти, передачей в данный</span><span style="text-align: center;"> </span><span style="text-align: center;">EEPROM ещё одного байта, запуститься запись этого байта в текущую ячейку памяти. После чего от этой микросхемы придет ответ и если больше ничего записывать не надо то слейву выставляется сигнал "стоп". Если нужно записать больше одного байта то они просто передаются по очереди а текущий адрес ячейки инкрементируется сам. Если байт нужно не прочитать а записать то сначала нужно перейти к требуемой ячейке памяти. </span><span style="text-align: center;">Сделать это так как это делалось бы для случая записи. После перехода к требуемой ячейке памяти, передается сигнал "старт" ещё раз. После чего передается адрес слейва но последний бит теперь должен указывать на чтение а не на запись т.е. он д.б. лог.1. После успешного перехода в режим чтения, слейв выдает ответ и матер может подать синхроимпульс на SCL и прочитать бит данных по SDA после чего подать ещё один синхроимпульс и прочитать ещё один бит данных и так до тех пор пока не прочтется один байт, после этого мастер либо подает ответ слейву (так как ранше делал только слейв для мастера) и прочитать ещё один байт данных либо не передать ответ. После не передачи ответа, мастер передает сигнал "стоп" и соединение заканчивается.</span><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhLN6vycIMHw0tCZOIqQR8HS2pRQV2LmJKdaLZ-m7u7YApKYR91v0Yk51QL94Y9phuQ3vz-7UxkLeKUNC4T5zZIza6towFrZ8HYWbUgK7l-pBca9PFLt-CxlCAg9sB3Xkb_w8hv3xvMgfD-rFUCvedSAX5hhQYDtLfdWlw12gCMQpvWHKv87iV9ITvC=s538" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="398" data-original-width="538" height="237" src="https://blogger.googleusercontent.com/img/a/AVvXsEhLN6vycIMHw0tCZOIqQR8HS2pRQV2LmJKdaLZ-m7u7YApKYR91v0Yk51QL94Y9phuQ3vz-7UxkLeKUNC4T5zZIza6towFrZ8HYWbUgK7l-pBca9PFLt-CxlCAg9sB3Xkb_w8hv3xvMgfD-rFUCvedSAX5hhQYDtLfdWlw12gCMQpvWHKv87iV9ITvC=s320" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 4 - Диаграмма чтения одного байта данных из микросхемы AT24C64</div><div><br /></div>В начале кода, как всегда, будет подключение заголовочного файла библиотеки CMSIS. После две глобальные переменные (хотя их можно сделать локальными) для чтения и для записи. Также будет вспомогательная функция для отправки символа по UART. В функции main() нужно включить тактирование всей необходимой периферии (UART1, I2C2, GPIO порт A и GPIO порт B). Пины <span style="text-align: center;">SCL и SDA нужно настроить на альтернативный выход с открытым стоком.</span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiVTw3EEuVtkHK1S38ZWIjehvt35ZW9V1o9ccE66xFC_i-grtKOGXYACI8cp0E3XulOxLL_JIoV6OPZSXXtJV8Rahhyx0MCtSpJvGfSClE1SnsZoWg1QT5eyvNBStLWNntZDsNB9gTBWcqDfthCHO40z0At25GUlegvOg-13PDo0LzU3L1AjUFVuxL3=s400" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="294" data-original-width="400" height="365" src="https://blogger.googleusercontent.com/img/a/AVvXsEiVTw3EEuVtkHK1S38ZWIjehvt35ZW9V1o9ccE66xFC_i-grtKOGXYACI8cp0E3XulOxLL_JIoV6OPZSXXtJV8Rahhyx0MCtSpJvGfSClE1SnsZoWg1QT5eyvNBStLWNntZDsNB9gTBWcqDfthCHO40z0At25GUlegvOg-13PDo0LzU3L1AjUFVuxL3=w497-h365" width="497" /></a></div><br /><span style="text-align: center;">В регистр I2C2->CR2 записывается частота шины от которой тактируется I2C2. По умолчанию это 36 МГц. В регистр </span><span style="text-align: center;">I2C2->CСR записывается число на которое надо поделить частоту шины APB1 чтобы получилась удвоенная частота работы I2C. Т.к. I2C это синхронная шина то частота м.б. любой но обычно используются две, 100 кГц - обычный режим и 400 кГц - ускоренный. Чтобы настроить I2C2 на обычный режим нужно в регистр </span><span style="text-align: center;">I2C2->CСR записать число 180 т.к. 100 000 Гц * 2 = 36 000 000 Гц / 180. В регистр </span><span style="text-align: center;">I2C2->TRISE нужно записать число на единицу большее чем в регистре </span><span style="text-align: center;">I2C2->CR2 т.е. чем частота шины APB1. </span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgjyGsFtxGDBFMRvJnm_1WRuJNFPrXwUcHvIXKUfD_sI2ZkQLZnlol92NqCqF2NqKJRQ-qFPw50ko4suX1YnAjRLYgOyYbnwgdgPik0g-btpNgctITwp8nBXi8fbahZtRmx-r1mloEZxvPrE51LZMb6Yo1hutDj6oiOBvYDwpR8HtxPCh26XSXS6U3k=s580" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="382" data-original-width="580" height="287" src="https://blogger.googleusercontent.com/img/a/AVvXsEgjyGsFtxGDBFMRvJnm_1WRuJNFPrXwUcHvIXKUfD_sI2ZkQLZnlol92NqCqF2NqKJRQ-qFPw50ko4suX1YnAjRLYgOyYbnwgdgPik0g-btpNgctITwp8nBXi8fbahZtRmx-r1mloEZxvPrE51LZMb6Yo1hutDj6oiOBvYDwpR8HtxPCh26XSXS6U3k=w435-h287" width="435" /></a></div>Чтобы принимать и отправлять байты для записи и чтения в EEPROM нужно также настроить UART, об этом есть отдельные статьи в данном блоге (передача по UART -> <a href="https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis.html">https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis.html</a>, прием по UART -> <a href="https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis_7.html">https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis_7.html</a>). Чтобы записать какой нибудь, напечатанный в мониторе последовательного порта, символ в EEPROM, надо сначала получить его по UART. Дождавшись, перед этим, его прихода.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg53lC_oi4UM_buPSyaqxugmgM92DlkYeedbB0qSUkbxHFLVmE9HDMGXbv_vaFovUBCb5NKnA-N1zycs6OhWTQhzNSIM5iQ-Odp3ZljSAZN-0ge_N1b4haZZhsBJYfF8IIpW65QJRQGNI0sY6GRhj0nbj8it0EZGTe30PKFYLFk_EIHqrC0H18h92em=s636" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="380" data-original-width="636" height="265" src="https://blogger.googleusercontent.com/img/a/AVvXsEg53lC_oi4UM_buPSyaqxugmgM92DlkYeedbB0qSUkbxHFLVmE9HDMGXbv_vaFovUBCb5NKnA-N1zycs6OhWTQhzNSIM5iQ-Odp3ZljSAZN-0ge_N1b4haZZhsBJYfF8IIpW65QJRQGNI0sY6GRhj0nbj8it0EZGTe30PKFYLFk_EIHqrC0H18h92em=w444-h265" width="444" /></a></div><div>Получить байт данных для записи в EEPROM лучше в начале цикла, после чего прочитать байт данных из ячейки в переменную для этого а потом записать байт для записи т.к. иначе новое значение в ячейке перезапишет старое и протестировать двустороннюю связь микроконтроллера с микросхемой <span style="text-align: center;">AT24C64 по I2C не получиться.</span></div>Чтобы разрешить работу I2C2 нужно установить бит 0 регистра I2C2->CR1:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjOtAglfYmwMR7knd0donTy_XSnvuVdUevhddUROPwMc95gOHgLTc0nT60AzEhs4rYkxBgmeaelO2y-tYoOy5fQKzEBIrAgFYhLIHdIgEeP1FRRXjeEWN1qAtJj0usNvTJzLOx_-eUFRHUEjxyOihRgnUpCptS6NCBDkeF4NKvwfLkanlT16xXUwAtM=s640" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="432" data-original-width="640" height="267" src="https://blogger.googleusercontent.com/img/a/AVvXsEjOtAglfYmwMR7knd0donTy_XSnvuVdUevhddUROPwMc95gOHgLTc0nT60AzEhs4rYkxBgmeaelO2y-tYoOy5fQKzEBIrAgFYhLIHdIgEeP1FRRXjeEWN1qAtJj0usNvTJzLOx_-eUFRHUEjxyOihRgnUpCptS6NCBDkeF4NKvwfLkanlT16xXUwAtM=w395-h267" width="395" /></a></div>Чтобы разрешить работу чтобы подать сигнал "старт" установить бит 8 этого регистра I2C2->CR1:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhBjLf941ILb1B31woOCbAP9KynaRf43hB-Og2xhn3JN-PmzCG9dPqP50CQ02jxot9tPqgfVOqo7lAx2HKrcX9-ev0CffefzOziI6WunWZq1uf-HhyesAXDZevWHkgvSqPKsIwoTZEBLIMhDCC_5_FiVudyKVGTlxm4zk9VH2L13FQNej8hA2CCmy0t=s640" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="432" data-original-width="640" height="260" src="https://blogger.googleusercontent.com/img/a/AVvXsEhBjLf941ILb1B31woOCbAP9KynaRf43hB-Og2xhn3JN-PmzCG9dPqP50CQ02jxot9tPqgfVOqo7lAx2HKrcX9-ev0CffefzOziI6WunWZq1uf-HhyesAXDZevWHkgvSqPKsIwoTZEBLIMhDCC_5_FiVudyKVGTlxm4zk9VH2L13FQNej8hA2CCmy0t=w385-h260" width="385" /></a></div>Прежде чем послать байт адреса устройства нужно дождаться окончания формирования сигнала "старт", для проверки этого есть бит 0 регистра I2C2->SR1:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi_GH8Tiwc6V1okdTDx2SPKcEW06Ko4RwSAf_-yUwGFg0Xojs24x7dJnIAUEYR_EecpcGZCrCJEKztVqZ_l9NMTZAOvfKqZ2ZeeleB9562wwvwC6WOGCDKhKI5yiab4tkP9Ti-pHfqkmaC8_JaZYm6oUG2dwi208g0hVVgen_kG6V5DsAtE1KhNNODV=s636" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="422" data-original-width="636" height="221" src="https://blogger.googleusercontent.com/img/a/AVvXsEi_GH8Tiwc6V1okdTDx2SPKcEW06Ko4RwSAf_-yUwGFg0Xojs24x7dJnIAUEYR_EecpcGZCrCJEKztVqZ_l9NMTZAOvfKqZ2ZeeleB9562wwvwC6WOGCDKhKI5yiab4tkP9Ti-pHfqkmaC8_JaZYm6oUG2dwi208g0hVVgen_kG6V5DsAtE1KhNNODV=w334-h221" width="334" /></a></div>После того как данный сигнал сформирован нужно прочитать регистр I2C2->SR1:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgAMjsgVagVmbEr0JR_ahZTubc-aPxS6UqNbkrCL78Vp3mq0j5rb3G-HXOdrjPX2CVwdntCzfNudHsYJEdOkPA4U-7kco0_FvasFTVpRSNjuprPLAqlEDAY54zaRtdbrJ7L2ZKKJeOl41nqVBgTcw8wZr_2IBcoqYzYiQxdVpg7rR6caVj0fAxaPcfn=s572" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="364" data-original-width="572" height="234" src="https://blogger.googleusercontent.com/img/a/AVvXsEgAMjsgVagVmbEr0JR_ahZTubc-aPxS6UqNbkrCL78Vp3mq0j5rb3G-HXOdrjPX2CVwdntCzfNudHsYJEdOkPA4U-7kco0_FvasFTVpRSNjuprPLAqlEDAY54zaRtdbrJ7L2ZKKJeOl41nqVBgTcw8wZr_2IBcoqYzYiQxdVpg7rR6caVj0fAxaPcfn=w368-h234" width="368" /></a></div>Теперь можно передать адрес слейва для установки связи с ним. В документации на микросхему показано как формируется её адрес:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg8O0Ru6sqJgE_DztougUhozgKZUe_4f630O_74UTmnWsnvrrAOaOlAJLH8DNg4yAVBVK8JFmtRIv7aYSm6-XKPdxPnGHWo6lWPShygj_CK0LGpnYZeOJ-J6f8iW10W_t55nqyyQK7Zx8jH6Ccb_1DuHBBIRuiWFebFyk9WvDy3ehlc9w-WSkCtONTS=s748" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="436" data-original-width="748" height="187" src="https://blogger.googleusercontent.com/img/a/AVvXsEg8O0Ru6sqJgE_DztougUhozgKZUe_4f630O_74UTmnWsnvrrAOaOlAJLH8DNg4yAVBVK8JFmtRIv7aYSm6-XKPdxPnGHWo6lWPShygj_CK0LGpnYZeOJ-J6f8iW10W_t55nqyyQK7Zx8jH6Ccb_1DuHBBIRuiWFebFyk9WvDy3ehlc9w-WSkCtONTS=s320" width="320" /></a></div>После того как произведен запуск отправки адреса слейва чтобы переходить к следующей части коммуникации микроконтроллера с AT24C64 нужно дождаться окончания отправки этого адреса и приема ответа от слейва. Об этом сигнализирует бит 1 регистра I2C2->SR1:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEipiRO1iafVAbGN80aPmI9C6Pt6OYk6vleAGkQh5w3PxYfslQK9EwEI_suaruLhcafw6-Bbuz2wpCS_O2ALCz3blcStMIY_Mo49pEDXM5x-qeYjGkeevJYIynRmLphILu7Ps1gpXR8CT_03rLCPYzu-3lZTcR8iIPOZP874xhY5tDhrtcwfFEX0Rt7O=s636" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="422" data-original-width="636" height="212" src="https://blogger.googleusercontent.com/img/a/AVvXsEipiRO1iafVAbGN80aPmI9C6Pt6OYk6vleAGkQh5w3PxYfslQK9EwEI_suaruLhcafw6-Bbuz2wpCS_O2ALCz3blcStMIY_Mo49pEDXM5x-qeYjGkeevJYIynRmLphILu7Ps1gpXR8CT_03rLCPYzu-3lZTcR8iIPOZP874xhY5tDhrtcwfFEX0Rt7O=s320" width="320" /></a></div><br /><div>После того как данный бит установится в единицу нужно будет прочитать регистры I2C2->SR1 и I2C2->SR2.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEizCh3jAmzGKj3ugAMS3v0wtqu19wCo7dJZzesLm1nA3_1TV3pmA12o7sZinPvDW9gycaRWFBslNNAXK_D3aXTGlgK3J9vg2xIxLsYf0gUtsyCCGcN9z74WrHok7eEacHRAm6vWh3P5MvchiEUtCZvyyqFbFPTqmfbOJbopg5LWZjpGzHUXUNbtogso=s584" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="362" data-original-width="584" height="198" src="https://blogger.googleusercontent.com/img/a/AVvXsEizCh3jAmzGKj3ugAMS3v0wtqu19wCo7dJZzesLm1nA3_1TV3pmA12o7sZinPvDW9gycaRWFBslNNAXK_D3aXTGlgK3J9vg2xIxLsYf0gUtsyCCGcN9z74WrHok7eEacHRAm6vWh3P5MvchiEUtCZvyyqFbFPTqmfbOJbopg5LWZjpGzHUXUNbtogso=s320" width="320" /></a></div>После этого можно перейти к переходу к нужной ячейке памяти в AT24C64. Для этого нужно передать сначала старшую часть адреса ячейки памяти а потом младшую. Передаются части адресов ячеек также т.е. записью их в регистр данных I2C2->DR. После того как произведен запуск отправки старшей части адреса ячейки памяти, нужно дождаться окончания отправки и передать младшую часть. Часть адреса ячейки памяти, в отличии от адреса слейва, является данными, как и те данные которые хранятся, записываются и читаются в эти ячейки. Поэтому для проверки окончания записи части адреса есть флаг окончания передачи данных по I2C который является битом 2 регистра I2C2->SR1:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjBNEFEEXU0db2HO4Xb-e1SSGXlBFgDDUjM1Tq04m57t9TJsknypCHxKqQ6_1HNeeGxfFqmN_v3fWAfRMPWh4LMd2SsS5dkC3i-pnnAJ5Y5Rax34aJICRkZmt005gTmcy0ZEvoY90fhfNT09mm78sS3aQBpxYm7y932xbfA8x5h9V6-70fMGEZ54p03=s636" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="422" data-original-width="636" height="212" src="https://blogger.googleusercontent.com/img/a/AVvXsEjBNEFEEXU0db2HO4Xb-e1SSGXlBFgDDUjM1Tq04m57t9TJsknypCHxKqQ6_1HNeeGxfFqmN_v3fWAfRMPWh4LMd2SsS5dkC3i-pnnAJ5Y5Rax34aJICRkZmt005gTmcy0ZEvoY90fhfNT09mm78sS3aQBpxYm7y932xbfA8x5h9V6-70fMGEZ54p03=s320" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhGEHN_J-RtXs3Ld-xeMtnO8FQTqi8NNGP6qs3p13DvHwnr7-_WwxUg9CxRdbi7BM1okBt0eI7evDDSt1k7qTWSnW5oZ_LC8wLTjIT3Rd1tFI6hp-s-C-PIRd-f98Tf3ykWYvNW5bZF7aRxTQhBVIK-a3N-CnpgqK3OOtpzLa-1rWl9matOvcXvo8Nh=s538" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="378" data-original-width="538" height="225" src="https://blogger.googleusercontent.com/img/a/AVvXsEhGEHN_J-RtXs3Ld-xeMtnO8FQTqi8NNGP6qs3p13DvHwnr7-_WwxUg9CxRdbi7BM1okBt0eI7evDDSt1k7qTWSnW5oZ_LC8wLTjIT3Rd1tFI6hp-s-C-PIRd-f98Tf3ykWYvNW5bZF7aRxTQhBVIK-a3N-CnpgqK3OOtpzLa-1rWl9matOvcXvo8Nh=s320" width="320" /></a></div>Далее для того чтобы прочитать байт данных из текущей ячейки (адрес которой был передан) нужно снова послать сигнал "старт" после чего адрес устройства но теперь с битом указывающим чтение а не запись.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi6YRAKpjsjy6mENk0-YKfRllMrZ4YJd_B8xQOBQxVSwkyu3_WP733VL4DR1h4WCPTsACyzBUgCW5L2RQlyqQjXgzS8MWL0Ndvt5XGj-Ydo1xRARjmo2cuwK6yL3e9yVPTfHpo8AZ6smZvZu_6qaor_3zSbUzm_QMU6Ajd4mGNgOUsS8ya9CcwyG329=s568" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="380" data-original-width="568" height="214" src="https://blogger.googleusercontent.com/img/a/AVvXsEi6YRAKpjsjy6mENk0-YKfRllMrZ4YJd_B8xQOBQxVSwkyu3_WP733VL4DR1h4WCPTsACyzBUgCW5L2RQlyqQjXgzS8MWL0Ndvt5XGj-Ydo1xRARjmo2cuwK6yL3e9yVPTfHpo8AZ6smZvZu_6qaor_3zSbUzm_QMU6Ajd4mGNgOUsS8ya9CcwyG329=s320" width="320" /></a></div>Потом также дождаться окончания передачи адреса и прочитать статусные регистры. После чего, если прием будет только одного байта данных то, просто дождаться прихода байта и забрать его из регистра данных. Если планируется забрать более одного байта из ячеек следующих после текущей то надо заранее выставить бит 10 первого регистра управления т.к. этот бит отвечает за автоматическй ответ мастера слейву после успешного приема байта данных. Об окончании приема байта данных свидетельствует бит 6 регистра I2C2->SR1:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh_qdDi_z-RcBgdSqt0zPoAVw2w-OA9ohs-aC0WCRJR3YR6AzE89wcUIidQNCN_9_euYmExTr1H4gGKgV6Wq-Q0mNJ7k_53FjDMKj5Ajz7fY4A2u_YTlxGHHfU5dgTfBRq5XBMP8pHHVcfZbWoh65fE4vkAyeV0Fin7zYocSQSts--4priD9gNctWh2=s636" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="422" data-original-width="636" height="212" src="https://blogger.googleusercontent.com/img/a/AVvXsEh_qdDi_z-RcBgdSqt0zPoAVw2w-OA9ohs-aC0WCRJR3YR6AzE89wcUIidQNCN_9_euYmExTr1H4gGKgV6Wq-Q0mNJ7k_53FjDMKj5Ajz7fY4A2u_YTlxGHHfU5dgTfBRq5XBMP8pHHVcfZbWoh65fE4vkAyeV0Fin7zYocSQSts--4priD9gNctWh2=s320" width="320" /></a></div><br /><div><br /><div>Если принимается не один байт то перед приемом последнего надо убрать бит автоматической выдачи ответа. После успешного приема данных нужно сделать сигнал "стоп" установкой бита 9 регистра I2C2->CR1:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgqAYbUTTx1GG0hUEbYjUwvEY56_a6v6vcfJAcIphBRSDVsB_Pw244-rp5p_4JUBvgyFeLlaRmKAv-tPuGm-5unHwNe6LJ8L_HTgSVKAECx4S0lNFAOUW_H8UxMP3n1FMdyO8T9hjXizEHVCausiHxUlMPU4jN6mTmVfosDyvD7Dfm2w4uiyaidIyHa=s640" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="432" data-original-width="640" height="216" src="https://blogger.googleusercontent.com/img/a/AVvXsEgqAYbUTTx1GG0hUEbYjUwvEY56_a6v6vcfJAcIphBRSDVsB_Pw244-rp5p_4JUBvgyFeLlaRmKAv-tPuGm-5unHwNe6LJ8L_HTgSVKAECx4S0lNFAOUW_H8UxMP3n1FMdyO8T9hjXizEHVCausiHxUlMPU4jN6mTmVfosDyvD7Dfm2w4uiyaidIyHa=s320" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh6TA5lPFXvtySLfWsXq-D8cHXhlPsUUCHUMQ9Ln2YrSiNIAwfUO4lxvN5_TysWSe031Cd8SW8D-eoNKSShpxRyycso83WjLL93QwZ-3HXZXgtWqavcWTH0H_jnCyDbge4nK0qKx07hpbLwHHbodNI5C2iFr6PCfI_MN1aBMUZ3zU3_rsMtsgAh2mbt=s572" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="378" data-original-width="572" height="211" src="https://blogger.googleusercontent.com/img/a/AVvXsEh6TA5lPFXvtySLfWsXq-D8cHXhlPsUUCHUMQ9Ln2YrSiNIAwfUO4lxvN5_TysWSe031Cd8SW8D-eoNKSShpxRyycso83WjLL93QwZ-3HXZXgtWqavcWTH0H_jnCyDbge4nK0qKx07hpbLwHHbodNI5C2iFr6PCfI_MN1aBMUZ3zU3_rsMtsgAh2mbt=s320" width="320" /></a></div>После того как байт данных получен его можно вывести на экране компьютера передав по uart через usb-uart переходник. Для этого можно воспользоваться функцией sendSumbol() (написанной над функцией main()). Записать байт данных принятый по uart проще. Для этого проделываются все те же действия как и для чтения до момента передачи сигнала "старт" второй раз. Вместо которого передается ещё один байт который и запишется в текущую ячейку памяти.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiLFY0mxvoHVA8WCSBq6sh-kKXCG7O3kKbb5imV6hDMcwz7TPcReDUeoBkub4S1G_vugqKaMcXyo2mlp4TMBgnat6XsR6cFDwvgTFE63g5B_ss1C5tZaLbzlSxOb0NLdk3VbOZSa-Yp33w39XdXNp6GCslqk21xAGx1jKZIv7SVhiHAQcONMoEagbXv=s434" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="396" data-original-width="434" height="292" src="https://blogger.googleusercontent.com/img/a/AVvXsEiLFY0mxvoHVA8WCSBq6sh-kKXCG7O3kKbb5imV6hDMcwz7TPcReDUeoBkub4S1G_vugqKaMcXyo2mlp4TMBgnat6XsR6cFDwvgTFE63g5B_ss1C5tZaLbzlSxOb0NLdk3VbOZSa-Yp33w39XdXNp6GCslqk21xAGx1jKZIv7SVhiHAQcONMoEagbXv=s320" width="320" /></a></div>Полный код программы можно скопировать из текстового поля:<div><textarea colls="100" readonly="readonly" rows="2200" style="height: 1200px; margin: 0px; width: 650px;">
#include "stm32f10x.h"
char pc_to_rom_dat = '0';
char rom_to_pc_dat = '0';
void sendSymbol(char symbol)
{
while (!(USART1->SR & 0b10000000)) {}
USART1->DR = symbol;
}
int main(void)
{
RCC->APB2ENR |= (1<<3)|(1<<14)|(1<<2);//включить тактирование порта B(3),UART1(14),A(2)
RCC->APB1ENR |= 1<<22;//включить тактирование I2C2
//I2C
GPIOB->CRH |= 0x0000FF00;//PB10,PB11-выходы с открытым стоком,альтернативной функцией,частотой 50Гц
I2C2->CR2 |= 36; // частота шины APB1 36MHz
//CRR = 36 000 000 / (2* 100 000) = 180
I2C2->CCR |= 180;//где 100 000 - частота I2C, 36 000 000 = CR2
I2C2->TRISE = 37;//CR2+1//максимальное время нарастания фронта I2C в тактах
//UART
GPIOA->CRH |= 0b1011<<4;//PA9 - выход 50МГц, push-pull,
GPIOA->CRH &= ~0b0100<<4;//PA9 альтернативная функция
GPIOA->CRH |= 1<<9; //PA10 устанавливаем как вход с подтяжкой
GPIOA->CRH &= ~(1<<8);//PA10 убираем единицу по умолчанию
GPIOA->ODR |= 1<<10;//ставим подтяжку PA10 к плюсу
USART1->BRR = 7500; // скорость 9600 бод
USART1->CR1 |= (1<<2)|(1<<3)|(1<<13);//разблокировать UART1, прием и передачу по нему
while(1)
{
//дождаться прихода байта данных по UART1
while (!(USART1->SR & (1<<5))) {}
pc_to_rom_dat = USART1->DR; //принять байт данных от pc
//получить байт из EEPROM
I2C2->CR1 |= 1; // разрешаем работу
I2C2->CR1 |= 1<<8; // формирование сигнала старт
while (!(I2C2->SR1 & 1)){} // ожидаем окончания формирования сигнала "Старт"
(void) I2C2->SR1;
I2C2->DR = 0b10100000;//адрес устройства где первый бит с лева-мастер1/слейв0,последний read1/write0//подробнее см. даташит на AT24C32
while (!(I2C2->SR1 & (1<<1))){}// ожидаем окончания передачи адреса устройства
(void) I2C2->SR1;
(void) I2C2->SR2;
I2C2->DR = 0; // передаем старшую часть адреса ячейки
while (!(I2C2->SR1 & (1<<2))){}// ожидаем окончания передачи старшей части адреса ячейки //while (!(I2C2->SR1 & I2C_SR1_TXE)){}//7
I2C2->DR = 3;// передаем младшую часть адреса ячейки//можно выбрать любой адрес
while (!(I2C2->SR1 & (1<<2))){}// ожидаем окончания передачи младшей части адреса ячейки//while (!(I2C2->SR1 & I2C_SR1_TXE)){}//7
I2C2->CR1 |= 1<<8;// формирование сигнала повторный старт
while (!(I2C2->SR1 & 1)){}// ждем окончания формирования сигнала "повторный старт"
(void) I2C2->SR1;
I2C2->DR = 0b10100001;//адрес устройства где первый бит слева-мастер1/слейв0,последний read1/write0//подробнее см. даташит на AT24C32
while (!(I2C2->SR1 & (1<<1))){}// ожидаем окончания передачи адреса устройства
(void) I2C2->SR1;
(void) I2C2->SR2;
//I2C2->SR1 |= 1<10 если надо прочитать ещё один байт, если байт последни то надо сбросить данный бит
while (!(I2C2->SR1 & (1<<6))){}// ожидаем окончания приема данных
rom_to_pc_dat = I2C2->DR; // принимаем байт
I2C2->CR1 |= 1<<9; // формирование сигнала "Стоп"
//отчет о чтении из EEPROM
sendSymbol(rom_to_pc_dat);
sendSymbol('-');
sendSymbol('g');
sendSymbol('o');
sendSymbol('t');
sendSymbol(' ');
//записать в EEPROM полученный из пк байт данных
I2C2->CR1 |= 1;// разрешаем работу
I2C2->CR1 |= 1<<8; // формирование сигнала старт
while (!(I2C2->SR1 & 1)){} // ожидаем окончания формирования сигнала "Старт"
(void) I2C2->SR1;//Перед передачей необходимо прочитать регистр SR1, для сброса бита SB.
// передаем адрес ведомого //если не получиться то попробуй перевернутый 0b10000101
I2C2->DR = 0b10100000;//адрес устройства где первый бит с лева-мастер1/слейв0,последний read1/write0//подробнее см. даташит на AT24C32
while (!(I2C2->SR1 & (1<<1))){}// ожидаем окончания передачи адреса устройства
(void) I2C2->SR1;
(void) I2C2->SR2;
I2C2->DR = 0;// передаем старшую часть адреса ячейки
while (!(I2C2->SR1 & (1<<2))){}// ожидаем окончания передачи старшей части адреса ячейки//while (!(I2C2->SR1 & I2C_SR1_TXE)){}//7
I2C2->DR = 3;// передаем младшую часть адреса ячейки//можно выбрать любой адрес
while (!(I2C2->SR1 & (1<<2))){}// ожидаем окончания передачи младшей части адреса ячейки //while (!(I2C2->SR1 & I2C_SR1_TXE)){}//7
I2C2->DR = pc_to_rom_dat;//передаем байт данных в EEPROM
while (!(I2C2->SR1 & (1<<2))){}// ожидаем окончания передачи байта данных в EEPROM //while (!(I2C2->SR1 & I2C_SR1_TXE)){}//7
I2C2->CR1 |= 1<<9;// формирование сигнала "Стоп"
//отчет о записи в EEPROM
sendSymbol(pc_to_rom_dat);
sendSymbol('-');
sendSymbol('s');
sendSymbol('e');
sendSymbol('t');
sendSymbol('\n');
}
}
</textarea><br /><div>Проверить программу можно загрузив её в микроконтроллер, подключив через USB-UART переходник к компьютеру на котором надо запустить например "монитор последовательного порта" (или монитор просто порта (или штуки аналогичного назначения но с другим названием)) среды Arduino IDE (или другой программы с аналогичным функционалом). В этом мониторе надо установить скорость 9600 (если такая скорость уже заранее там не установлена (обычно для установки скорости в Arduino IDE есть выпадающий список справа в низу)). Если отправить один символ по UART (вписав его в верхнюю строку и нажав кнопку "send" или кнопку с другим названием но аналогичной функцией) то он запишется в ячейку памяти EEPROM а в мониторе порта выведется символ который был в этой ячейке до этого. Если, после этого, перезагрузить микроконтроллер (нажав соответствующую кнопку на плате "blue pill" (если конечно используется именно такая плата)) или отключить питание (монитор порта, перед этим (а также перед перезагрузкой), следует выключить) после чего его снова подать (после подачи питания можно включить монитор порта) и послать символ по UART то можно увидеть что в мониторе порта появился символ отосланный ранее. Это свидетельствует о том что он был сохранен в энергонезависимой памяти EEPROM и связь по I2C выполнилась успешно в обе стороны.</div><div>Видео по данной теме:</div><div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/F2l0fmEJ-Po" width="320" youtube-src-id="F2l0fmEJ-Po"></iframe></div><br /><div class="separator" style="clear: both; text-align: center;"><a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a></div></div></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-34050835967897342302022-06-05T01:11:00.000-07:002022-06-05T01:11:10.699-07:00Асинхронное мигание светодиодами на ардуино<p>Задача асинхронного мигания светодиодами, по непонятным причинам, вызывает у некоторых ардуиньщиков и микроконтроллерщиков затруднения а также используется для объяснения преимущества использования операционной системы FreeRTOS. На этом примере как бы объясняется реализация многозадачности которая часто бывает необходима в крупных проектах когда микроконтроллер должен выполнять много разных действий например выводить показания на экран, считывать данные с датчиков и заниматься какими нибудь вычислениями если каждое действие занимает много времени а пользователь не хочет долго ждать результата на экране то нельзя их просто выполнять последовательно друг за другом. Для каждого действия нужно выделить отдельную задачу и некоторое количество времени на выполнение. Допустим светодиодов будет три.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrKa4qCKbAEjLleC0h2dSXIMe7a7wF2PGHYIJWQ3TxEunHKQ6USdgXvxBChNSvUFdYlV1Lpu1wygC8IfsPTRardxYodI1hiMZYN_wIkEHjlX1y503ZZO-N2n-zq7w_85YEuSZiXjaUG9fhD9GOzB_ptI3IC9ZEJip2vkWi8dToFRdU2NZWuyNzj3x7/s588/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="532" data-original-width="588" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrKa4qCKbAEjLleC0h2dSXIMe7a7wF2PGHYIJWQ3TxEunHKQ6USdgXvxBChNSvUFdYlV1Lpu1wygC8IfsPTRardxYodI1hiMZYN_wIkEHjlX1y503ZZO-N2n-zq7w_85YEuSZiXjaUG9fhD9GOzB_ptI3IC9ZEJip2vkWi8dToFRdU2NZWuyNzj3x7/s320/%D1%81%D1%85%D0%B5%D0%BC%D0%B0.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Схема</div><div class="separator" style="clear: both; text-align: center;"><br /></div>Скетч для асинхронного мигания светодиодами может быть например таким:<br /><p><textarea colls="100" readonly="readonly" rows="1700" style="height: 700px; margin: 0px; width: 650px;">
//пины светодиодов
#define LED1 12
#define LED2 13
#define LED3 14
//задержки для мигания светодиодов
#define LED1_DELAY_MS 300
#define LED2_DELAY_MS 1000
#define LED3_DELAY_MS 2000
//время предыдущее и время текущее
unsigned long pre_time;
unsigned long time;
//счетчики мигания светодиодов
int led1_counter = 0;
int led2_counter = 0;
int led3_counter = 0;
//инвертировать состояние пина
void togglePin(int pin){digitalWrite(pin,!digitalRead(pin));}
//задачи
void task1()
{
togglePin(LED1);
}
void task2()
{
togglePin(LED2);
}
void task3()
{
togglePin(LED3);
}
void setup()
{
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
time = millis();
pre_time = time;
}
void loop()
{
time = millis();
if(time != pre_time)//прошла милисекунда
{
pre_time = time;
led1_counter++;
led2_counter++;
led3_counter++;
if(led1_counter >= LED1_DELAY_MS)
{
task1();
led1_counter = 0;
}
if(led2_counter >= LED2_DELAY_MS)
{
task2();
led2_counter = 0;
}
if(led3_counter >= LED3_DELAY_MS)
{
task3();
led3_counter = 0;
}
}
}
</textarea></p><p>Константами LEDx где x-порядковый номер пина к которому подключается светодиод, заданы номера пинов к которым подключаются светодиоды. Константами LEDx_DELAY_MS где x-порядковый номер пина к которому подключается светодиод, задаются длительности полупериодов мигания светодиодов. Переменные unsigned long pre_time и unsigned long time нужны для отсчетов времени. Функция togglePin(int pin) нужна для инвертирования состояния пина. Функции taskx() где x-номер задачи, являются квазиасинхонно вызываемыми задачами. Внутри этих задач помещены инвертирования состояний светодиодов. В функции setup() инициализация пинов и переменных для отсчетов времени. В функции loop() раз в миллисекунду инкрементируются переменные счетчики для для мигания светодиодами. При досчитывани счетчика вызывается соответствующая задача и происходит мигание светодиодом.</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/wvFimx1TcpE" width="320" youtube-src-id="wvFimx1TcpE"></iframe></div><br /><p><br /></p><p></p>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0tag:blogger.com,1999:blog-6395453281173766990.post-88066797977647424912022-05-03T03:42:00.001-07:002022-05-03T03:46:14.166-07:00Металлодетектор<p> Металлодетектор - это прибор для обнаружения металла, данный прибор может применяться для поиска металлических или каких либо других токопроводящих предметов например в земле или в карманах возможных прихватизаторов желающих скомуниздить чужую металлическую собственность. Простейший металлодетектор можно собрать например по схеме:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyIfm2wvRCHYRANxy1-HD5HPoBDH0GfmoxETB-zHn9dAEAmbhxLYoBGrX41qc0vCIJr5I_ZDswZvBG8EE_dltb3dsQTPfd3RLGaxXDge1l0e1L8FhMR6zMhFHaTT0Qv4eDoxih83yeP2g1xLQRRkIk2j2BQNu6bTN6DZ9ozchZIhYmCUPcIfX8iz2u/s468/%D0%A1%D1%85%D0%B5%D0%BC%D0%B0%20%D0%BC%D0%B5%D1%82%D0%B0%D0%BB%D0%BE%D0%B4%D0%B5%D1%82%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B0.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="331" data-original-width="468" height="226" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyIfm2wvRCHYRANxy1-HD5HPoBDH0GfmoxETB-zHn9dAEAmbhxLYoBGrX41qc0vCIJr5I_ZDswZvBG8EE_dltb3dsQTPfd3RLGaxXDge1l0e1L8FhMR6zMhFHaTT0Qv4eDoxih83yeP2g1xLQRRkIk2j2BQNu6bTN6DZ9ozchZIhYmCUPcIfX8iz2u/s320/%D0%A1%D1%85%D0%B5%D0%BC%D0%B0%20%D0%BC%D0%B5%D1%82%D0%B0%D0%BB%D0%BE%D0%B4%D0%B5%D1%82%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B0.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Схема металлодетектора</div><div><br /></div>Катушки L1 и L2 являются поисковыми и представляют собой одну катушку, намотанную в одну сторону с отводом по середине на который подается +5В питания. В каждой половине по 8 витков но можно сделать и другое количество. Слишком малое количество витков делать не стоит т.к. прибор может не заработать. На транзисторе VT1 (которым может быть не только 2SC815 Y но и какой нибудь другой близкий по параметрам напр. КТ315, BC547 и т.д.). На этом транзисторе сделан блокинг генератор который потребляет больше тока когда рядом с поисковой катушкой находиться металл. Т.к. в металле наводятся индукционные токи то данный металл как бы создает нагрузку для генератора что обнаруживает датчик тока на компараторе DA1. Резистор R3 является шунтом. Резистор R4 нужно настроить так чтобы при отсутствии металла вблизи поисковой катушки напряжение на его среднем выводе было чуть больше чем на шунте R3 но как можно ближе к нему. Резистором R1 настраивается оптимальная рабочая точка транзистора. Можно накрутить его так чтобы металлодетектор имел приемлемою чувствительность и потребляемый ток. Крутить его следует перед тем как крутить R4 т.к. R1 влияет на ток протекающий через шунт R3. R4 лучше чтобы был прецезионным т.к. так можно сделать неплохую чувствительность более близким порогом срабатывания или обычным. Увидеть как работает данный детектор можно на видео:<br /><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/8BoDEN8ugsM" width="320" youtube-src-id="8BoDEN8ugsM"></iframe></div><a href="http://electe.blogspot.com/p/blog-page.html" title="Здесь ссылки на статьи этого блога и другие полезные сайты">
<b>КАРТА БЛОГА (содержание)</b>
</a><br /><p><br /></p>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com2tag:blogger.com,1999:blog-6395453281173766990.post-12286733760307924942022-04-02T08:51:00.001-07:002022-04-02T08:51:33.585-07:00Управляемый по wifi автомобиль с камерой на ESP32-CAM<p> Дистанционно управляемое транспортное средство с камерой передающей изображение на экран может иметь много применений а также это весьма интересная штука. Такую штуку можно сделать на основе модуля <span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="background-color: white; font-size: 15.4px;">ESP32-CAM (</span><a href="http://alii.pub/69872w" style="background-color: white; color: red; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 15.4px;">http://alii.pub/69872w</a><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="background-color: white; font-size: 15.4px;">)</span> для чего его нужно дополнить самим транспортным средством и драйверами для его управления через данный модуль. В простейшем случае транспортным средством может быть какая либо колесная платформа поворачивающаяся по танковой схеме. Если исключить возможность заднего хода то электрическая схема всей конструкции может быть очень простой:</p><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC4ojZzndFCyzgZdeEP6U1KOKk8BDw5SPEwfIDyMmAr23_jGdqx9-NOmLk1vwzo2peVRj3RsTmNyHtAuYOt3Vhpcdsy0FLkgcsRX_qOUyGkudOuYg5m6SMUtFkVwBI46xice38wJ7kimB0-yVP8OntB0KyXCNGr6pt3EWGWqOds_kIQPo_cleBEzzt/s808/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D1%81%20%D0%BE%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D1%8B%D0%BC%D0%B8%20%D0%B4%D0%B8%D0%BE%D0%B4%D0%B0%D0%BC%D0%B8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="560" data-original-width="808" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC4ojZzndFCyzgZdeEP6U1KOKk8BDw5SPEwfIDyMmAr23_jGdqx9-NOmLk1vwzo2peVRj3RsTmNyHtAuYOt3Vhpcdsy0FLkgcsRX_qOUyGkudOuYg5m6SMUtFkVwBI46xice38wJ7kimB0-yVP8OntB0KyXCNGr6pt3EWGWqOds_kIQPo_cleBEzzt/s320/%D1%81%D1%85%D0%B5%D0%BC%D0%B0%20%D1%81%20%D0%BE%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D1%8B%D0%BC%D0%B8%20%D0%B4%D0%B8%D0%BE%D0%B4%D0%B0%D0%BC%D0%B8.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Рисунок 1 - Схема радиоуправляемого автомобиля с камерой</div><div><br /></div>В схеме имеется гальваническая развязка силовой и логической части для защиты модуля ESP32-CAM. Используются оптроны 4N33 с током коллектора до 100мА. Это не очень большой ток поэтому моторы должны быть соответственно не очень мощными или оптроны должны быть мощнее в случае использования мощных двигателей. Модуль следует подготовить, об этом есть статья <span color="rgba(0, 0, 0, 0.52)" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif" style="background-color: white; font-size: 14px;"><a href="https://electe.blogspot.com/2022/03/esp32-cam.html">https://electe.blogspot.com/2022/03/esp32-cam.html</a></span>. После того как пароль от wifi вписан нужно найти то ветвление (во вкладке app_httpd.cpp) в котором прописывалось включение светодиода но только теперь добавить строки для управления пинами 12 и 13 например так:<div><div> else if(!strcmp(variable, "left")) </div><div> {</div><div> pinMode(12,OUTPUT);</div><div> digitalWrite(12,HIGH);</div><div> pinMode(13,OUTPUT);</div><div> digitalWrite(13,LOW);</div><div> } </div><div> else if(!strcmp(variable, "right"))</div><div> {</div><div> pinMode(12,OUTPUT);</div><div> digitalWrite(12,LOW);</div><div> pinMode(13,OUTPUT);</div><div> digitalWrite(13,HIGH);</div><div> }</div><div> else if(!strcmp(variable, "forward"))</div><div> {</div><div> pinMode(12,OUTPUT);</div><div> digitalWrite(12,HIGH);</div><div> pinMode(13,OUTPUT);</div><div> digitalWrite(13,HIGH);</div><div> }</div><div> else if(!strcmp(variable, "stop"))</div><div> {</div><div> pinMode(12,OUTPUT);</div><div> digitalWrite(12,LOW);</div><div> pinMode(13,OUTPUT);</div><div> digitalWrite(13,LOW);</div><div> pinMode(12,INPUT);</div><div> pinMode(13,INPUT);</div><div> } </div><div>Теперь чтобы модуль заработал осталось поменять соответствующим образом фронтенд который накодится по вкладке camera_index.h. В коде на этой вкладке есть 2 массива для разных камер. Нужно выбрать массив с камерой которая используется и заменить его содержимое. Для этого можно например скопировать его содержимое в программу для разархивации из формата .gz в .html потом изменить html код, после чего сжать его программой архиватором в формат .gz и содержимое переместить в выбранный массив. html код который будет работать с кодом выше может выглядеть например так:</div></div><div><textarea colls="100" readonly="readonly" rows="1700" style="height: 700px; margin: 0px; width: 650px;">
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ESP32 OV2460</title>
</head>
<body>
<figure>
<img id="stream" src=""><br>
<button id="left">left</button>_____
<button id="right">right</button><br><br>
<button id="forward">forward</button>__
<button id="stop">stop</button>
</figure>
<script>
document.addEventListener('DOMContentLoaded', function (event) {
var baseHost = document.location.origin;
var streamUrl = baseHost + ':81';
document.getElementById('stream').src = ''+streamUrl+'/stream';
document.getElementById('left').addEventListener('click',function(){ sendCommand('left',1) });
document.getElementById('right').addEventListener('click',function(){ sendCommand('right',1) });
document.getElementById('forward').addEventListener('click',function(){ sendCommand('forward',1) });
document.getElementById('stop').addEventListener('click',function(){ sendCommand('stop',1) });
function sendCommand(el,val){fetch(baseHost+'/control?var='+el+'&val='+val);}
})
</script>
</body>
</html>
</textarea></div><div>Это довольно минималистичный веб интерфейс без излишеств, только с изображением для вывода потокового видео и кнопками для управления автомобилем. Основная функция в данном коде -fetch() для отправки get запроса с командами управления моторами. А также тег img для отображения видео. В сжатом виде данный код будет выглядеть так:</div><div><textarea colls="100" readonly="readonly" rows="1700" style="height: 700px; margin: 0px; width: 650px;">
0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x9D, 0x54, 0x5D, 0x6B, 0xDB, 0x30,
0x14, 0x7D, 0x4E, 0x20, 0xFF, 0x41, 0xD3, 0xC3, 0x14, 0x93, 0xAF, 0xA5, 0x1B, 0xA3, 0xAC, 0xB6,
0x07, 0xEB, 0x02, 0x1B, 0x74, 0x74, 0x30, 0xB6, 0xD7, 0xA1, 0x48, 0xD7, 0xB1, 0x98, 0x6C, 0x05,
0xF9, 0x26, 0x21, 0x84, 0xFC, 0xF7, 0x5E, 0xD9, 0x8E, 0x97, 0xB5, 0xA3, 0x94, 0xE8, 0x41, 0xBA,
0x92, 0xCE, 0x39, 0xD7, 0xBA, 0x1F, 0x8E, 0x5F, 0x69, 0xA7, 0x70, 0xBF, 0x06, 0x96, 0x63, 0x61,
0xD3, 0x41, 0x3F, 0x6E, 0x57, 0x46, 0x23, 0xCE, 0x41, 0xEA, 0xD6, 0xAE, 0xF7, 0x05, 0xA0, 0x64,
0x2A, 0x97, 0xBE, 0x02, 0x4C, 0xF8, 0x06, 0xB3, 0xC9, 0x35, 0x7F, 0x72, 0x5F, 0xCA, 0x02, 0x12,
0xBE, 0x35, 0xB0, 0x5B, 0x3B, 0x8F, 0x9C, 0x29, 0x57, 0x22, 0x94, 0x84, 0xDF, 0x19, 0x8D, 0x79,
0xA2, 0x61, 0x6B, 0x14, 0x4C, 0xEA, 0xCD, 0xD8, 0x94, 0x06, 0x8D, 0xB4, 0x93, 0x4A, 0x49, 0x0B,
0xC9, 0xFC, 0x1F, 0x31, 0x34, 0x68, 0x21, 0x5D, 0xFC, 0xF8, 0xFE, 0xF6, 0x8A, 0xDD, 0xFF, 0xBA,
0x7A, 0xF7, 0xFE, 0x4D, 0x3C, 0x6B, 0xCE, 0x06, 0xFD, 0x5E, 0x3C, 0x6B, 0xBF, 0xAD, 0x17, 0x2F,
0x9D, 0xDE, 0xD7, 0x46, 0x66, 0x56, 0x1B, 0x0F, 0xE7, 0x12, 0xA6, 0x58, 0x31, 0xA3, 0x13, 0x5E,
0xA1, 0x07, 0x59, 0x70, 0x56, 0x79, 0x95, 0x70, 0x9E, 0xC6, 0x4B, 0x1F, 0x08, 0x44, 0xDD, 0x20,
0xBA, 0xB2, 0x86, 0x58, 0xC8, 0x90, 0xA7, 0x61, 0x8E, 0x67, 0xCD, 0x71, 0xFA, 0x3B, 0x8C, 0xC7,
0x38, 0x6F, 0x56, 0x39, 0x01, 0xEB, 0xA5, 0x43, 0x06, 0xC1, 0xFF, 0x89, 0x66, 0xCE, 0xEF, 0xA4,
0xD7, 0x3C, 0x6D, 0x8D, 0x33, 0xE9, 0xC7, 0xD0, 0x0A, 0xDD, 0x9A, 0xA7, 0x61, 0xEE, 0x40, 0x6D,
0x12, 0x66, 0xDD, 0xBB, 0x7A, 0x71, 0xA5, 0xBC, 0x59, 0x23, 0x99, 0x94, 0xB5, 0x4D, 0x41, 0x51,
0x9D, 0x4A, 0xAD, 0x17, 0x5B, 0x32, 0xEE, 0x4C, 0x45, 0x51, 0x06, 0x3F, 0x14, 0x9F, 0xEF, 0xBF,
0xDD, 0x36, 0x21, 0xBF, 0x73, 0x52, 0x83, 0x16, 0x63, 0x96, 0x6D, 0x4A, 0x85, 0x86, 0x3C, 0x0D,
0x21, 0x60, 0x23, 0x76, 0x20, 0xB1, 0xAD, 0xF4, 0x6C, 0x29, 0x2B, 0xF8, 0xE2, 0x2A, 0x64, 0x09,
0xEB, 0x14, 0xAD, 0x53, 0x32, 0x80, 0xA7, 0x8E, 0x1E, 0x69, 0xCA, 0x9B, 0x16, 0xDA, 0xC4, 0xF0,
0xA7, 0xB7, 0x84, 0xED, 0x68, 0x23, 0x26, 0x3E, 0x5C, 0xCF, 0x45, 0xC0, 0x74, 0xFC, 0x15, 0xE0,
0xC2, 0x42, 0x30, 0x3F, 0xED, 0xBF, 0xEA, 0xA1, 0x68, 0x88, 0x22, 0x9A, 0x52, 0xF4, 0x89, 0x2B,
0xC4, 0xA8, 0x93, 0x1A, 0x89, 0x59, 0x7B, 0xFB, 0xAC, 0x42, 0xC8, 0x0A, 0xF1, 0x9F, 0x3E, 0x55,
0x59, 0xA3, 0xFE, 0x88, 0xF1, 0xE9, 0x79, 0xC3, 0xE8, 0xC0, 0x2A, 0x28, 0xF5, 0xAD, 0x2B, 0x0A,
0x59, 0x9E, 0x88, 0xE3, 0x79, 0xC4, 0x8E, 0xD1, 0xB3, 0x0E, 0xEA, 0x6C, 0x5E, 0xE4, 0xA1, 0x61,
0xBE, 0xC0, 0x45, 0x5B, 0x01, 0x17, 0x39, 0x39, 0x71, 0x5F, 0xE0, 0x26, 0x14, 0xD0, 0x45, 0x3E,
0x6A, 0xE2, 0x5F, 0x07, 0x5D, 0xC5, 0x9C, 0x83, 0xC0, 0x8E, 0xB7, 0xD2, 0x46, 0x87, 0x0C, 0x50,
0xE5, 0xC3, 0x53, 0x11, 0x50, 0x12, 0x43, 0x8B, 0x7B, 0x67, 0x3F, 0x52, 0x9D, 0x24, 0x62, 0x04,
0x94, 0xD7, 0xD7, 0x04, 0x24, 0x33, 0xC0, 0x6F, 0x8E, 0x83, 0xFE, 0x31, 0xAA, 0x7B, 0xB6, 0x2B,
0x5F, 0xB2, 0xDB, 0xB6, 0xA5, 0x46, 0x0E, 0x3F, 0x9C, 0x07, 0xB9, 0x48, 0x2C, 0x8A, 0x88, 0x04,
0x00, 0x00
</textarea></div><div>Удивительно, но после сжатия размер занимаемой памяти увеличился. Возможно сжатие начнет работать нормально при больших html кодах поэтому тут можно не экономить и сделать большую и красивую веб страницу. Но конечно же в разумных пределах т.е. не стоит например подключать react или другой большой фреймворк. Модуль может этого не потянуть даже со сжатием. В результате после внесения данных поправок в стандартный скетч и загрузки его в автомобиль, получается интересная радиоуправляемая по wifi со смартфона или компьютера машинка, тест драйв которой можно посмотреть на видео:</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/seErT5XclhQ" width="320" youtube-src-id="seErT5XclhQ"></iframe></div><a title="Здесь ссылки на статьи этого блога и другие полезные сайты" href="http://electe.blogspot.com/p/blog-page.html">
<b>КАРТА БЛОГА (содержание)</b>
</a><br /><div><br /></div>Сергейhttp://www.blogger.com/profile/07461894408241531515noreply@blogger.com0