Всем привет и рад познакомиться ) Добрался я, наконец-то, до написания статей, а точнее до первых попыток. Опыта в этом нет, так что, если что пишите пожелания и замечания в комментарии. Как мы и условились с Aveal, я буду делать обзоры на разнообразные датчики из такого вот набора – ссылка. А начну с одного из самых первых, KY-002 (KY-001 уже был описан ранее), просто потому что он первый из оставшихся по номенклатуре. И модуль KY-002 это небольшая платка с установленным на ней датчиком вибрации.
На плате установлен SW-18015, и, как и для большинства модулей из этого набора, плата максимально проста:
Модуль содержит всего лишь три элемента:
- датчик вибрации
- резистор на 10 КОм
- штыревой разъем с шагом 2.54 мм на 3 контакта
Нарисовал принципиальную схему KY-002:
Сам же датчик состоит из цилиндрического металлического корпуса, внутри которого расположена металлическая пружина, а в центре пружины – стержень:
Два контакта датчика соединены с пружиной и с этим стержнем. При ударе, вибрации или другом механическом воздействии пружина физически контактирует со стержнем, что приводит к замыканию контактов датчика.
Возвращаемся к схеме. Здесь на выходе Output по умолчанию будет напряжение питания, потому что выход Output подтянут резистором к Power. А при замыкании пружины со стержнем Output окажется замкнут с землей (Ground). То есть датчик вибрации практически идентичен кнопке, которая нажимается при механическом ударе и т. п. Как я уже говорил, схема максимально проста )
Давайте подытожим, что будет на выходе Output модуля KY-002:
- Без воздействия на датчик – высокий уровень, то есть напряжение, подаваемое на вывод Power.
- При воздействии на датчик будет импульс или импульсы низкого уровня (зависит от степени и типа воздействия).
Я подключил датчик, стал стучать по нему и сохранил осциллограмму:
Наверно больше и нечего про этот датчик вибрации рассказать, разве что основные характеристики:
- при разомкнутых пружине и стержне сопротивление датчика более 10 МОм (МегаОм)
- при замкнутых – менее 30 мОм (миллиОм)
- количество срабатываний, которое датчик может пережить - более 100000
- максимальное напряжение – 12 В, превышать не рекомендую )
Теперь перейдем к программной части. Я сделал два проекта – самый базовый, который может помочь проверить работоспособность датчика, и чуть посложнее, чтобы было поинтереснее.
Проект 1. Базовое подключение датчика вибрации к STM32.
Использую STM32CubeIDE – потому что она бесплатная и плюс нравится мне. Стараюсь всегда обновлять до последней версии, но это не сильно повлияет, если у вас не самая актуальная версия. Плата у меня будет такая:
Контроллер STM32F401CCU6, вроде как плата называется Black Pill, короче обычный Aliexpress-девайс. Подключаю так:
Из возможностей STM32 понадобится самый минимум – один порт на вход (для считывания сигнала с датчика - PB0) и один порт на выход (для управления светодиодом). На этой плате светодиод подключен к PC13. Настраиваю в STM32CubeMX:
После генерации кода получаем готовый проект для CubeIDE. Хотя работа с датчиком очень проста, я оформил работу с ним в отдельных файлах, просто я так привык. За основу взял шаблон из примера про ПИД-регулятор. Структура проекта такая:
Не буду описывать как добавляются файлы в проект, и как прописываются пути к файлам в настройках проекта, потому что наверно все это проделывают регулярно и так. Но если что-то не получится, пишите в комментарии, я на связи почти всегда. Проект я назвал KY002_Base, чтобы потом не запутаться.
В файле vibration_sensor.c у меня три переменные:
static GPIO_TypeDef* port; static uint32_t pin; static uint8_t isInitialized = 0;
Первые две для того, чтобы в них сохранить порт и пин, к которым подключен выход с модуля. И третья переменная на всякий случай, чтобы не забыть поставить в первые две переменные правильные значения. Для этого используется функция VIBRO_Init()
:
void VIBRO_Init(GPIO_TypeDef* gpioPort, uint32_t gpioPin) { port = gpioPort; pin = gpioPin; isInitialized = 1; }
Кроме нее в файле еще только одна функция:
int8_t VIBRO_Read() { if (isInitialized == 1) { return HAL_GPIO_ReadPin(port, pin); } else { return -1; } }
Она нужна для считывания сигнала с датчика. В main()
добавляем инициализацию:
/* USER CODE BEGIN 2 */ VIBRO_Init(GPIOB, GPIO_PIN_0); /* USER CODE END 2 */
Это позволит менять порт только в одном месте при подключении датчика к другой ножке микроконтроллера. Я так делаю всегда, это мне кажется наиболее удобным. В while(1)
добавил опрос датчика и включение светодиода:
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ int8_t vibrationData = VIBRO_Read(); if (vibrationData >= 0) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, vibrationData); } } /* USER CODE END 3 */
В момент механического воздействия на датчик светодиод будет загораться, что очень удобно для визуального наблюдения. Если забыли вызвать VIBRO_Init()
, то функция VIBRO_Read()
вернет значение -1, можно добавить на этот случай какие-то действия, показывающие, что возникла ошибка. Здесь я этого не делал, потому что в проекте всего две лишь функции, все очень просто.
Если прошить STM32 и постучать по датчику, то можно увидеть мигающий диод, так и было задумано. Теперь займемся вторым проектом, он сложнее, но не сильно.
Проект 2. Оценка степени воздействия на датчик вибрации.
Этот проект я назвал KY002_Timer, и для теста воздействий я придумал такую штуку… Берем любой таймер и настраиваем так, чтобы генерировалось прерывание каждые 100 мкс (можно взять и другое значение). Далее в течение более длительного времени (у меня 1 секунда = 100 мкс * 10000) опрашиваем датчик каждые 100 мкс, если датчик замкнут (то есть на него есть воздействие), то инкрементируется переменная. После одной секунды проверяем значение этой переменной. Чем больше значение – тем больше на датчик было воздействий.
Все подключено и настроено точно также как и в первом примере. Только добавлен таймер, я взял TIM9:
При таком предделителе получаем частоту таймера 1 МГц (частота шины 84 МГц, деленная на (83 + 1)), значит один отсчет таймера – 1 мкс. Нужен период в 100 мкс, поэтому в Counter Period я поставил 100.
В функции main()
оставляю только инициализацию и запуск таймера:
/* USER CODE BEGIN 2 */ VIBRO_Init(GPIOB, GPIO_PIN_0); HAL_TIM_Base_Start_IT(&htim9); /* USER CODE END 2 */
Также здесь объявлены несколько вспомогательных переменных:
/* USER CODE BEGIN PD */ #define VIBRO_MEASURE_PERIOD (10000) /* USER CODE END PD */ /* USER CODE BEGIN PV */ static float vibroRatio = 0; static uint16_t vibroCounter = 0; static uint16_t timerCounter = 0; /* USER CODE END PV */
Значение VIBRO_MEASURE_PERIOD
задает период таймера (у меня 1 секунда). Прерывание таймера вызывается каждые 100 мкс, тогда за 1 секунду оно будет вызвано 10000 раз. Это число здесь и задается.
Добавляю в main.c callback, который вызывается при переполнении таймера (каждые 100 мкс):
/* USER CODE BEGIN 4 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == htim9.Instance) { timerCounter++; int8_t vibrationData = VIBRO_Read(); if (vibrationData == 0) { vibroCounter++; } if (timerCounter == VIBRO_MEASURE_PERIOD) { vibroRatio = (float)vibroCounter / (float)timerCounter * 100; timerCounter = 0; vibroCounter = 0; } } } /* USER CODE END 4 */
В callback я написал то, что описано выше. Посмотрим по строкам, сначала проверяем, что прерывание вызвано нужным таймером:
if (htim->Instance == htim9.Instance)
Следующей строкой увеличиваю переменную-счетчик timerCounter
:
timerCounter++;
Эта переменная будет считать, сколько раз сработало прерывание таймера. Если ее значение станет равным VIBRO_MEASURE_PERIOD
, значит прошла 1 секунда, можно проверить значения с датчика. После инкрементирования timerCounter
проверяю сигнал с датчика. И если на выходе датчика низкий уровень (значит пружина замкнута со стержнем), то увеличиваю другую переменную (vibroCounter
):
int8_t vibrationData = VIBRO_Read(); if (vibrationData == 0) { vibroCounter++; }
Далее проверка, прошла ли 1 секунда:
if (timerCounter == VIBRO_MEASURE_PERIOD) { vibroRatio = (float)vibroCounter / (float)timerCounter * 100; timerCounter = 0; vibroCounter = 0; }
Получается, что vibroRatio
показывает степень воздействия на датчик в процентах. timerCounter
увеличивается каждый раз при попадании в прерывание, а vibroCounter
только при воздействии на датчик. Если разделить второе на первое, узнаем, какую долю времени на датчик было воздействие. Умножаем на 100 и получаем значение в процентах.
У меня есть небольшой стенд, на котором находится двигатель с вращающимся валом. На статичной платформе я установил датчик, так чтобы при вращении вала происходил небольшой контакт с этой платформой, достаточный для срабатывания датчика. Поэтому чем выше скорость вращения двигателя, тем большее количество раз происходит контакт с платформой. Значит при большей скорости значение vibroRatio
должно быть больше.
Для разных скоростей вращения я получил такие результаты:
Так что все получилось правильно. На этом заканчиваю свою статью и выкладываю оба проекта: KY002_Base, KY002_Timer.