Підключення STM32 до апаратури радіоуправління

Знадобилося мені реалізувати дистанційне керування кількома двигунами постійного струму.

У магазинах доступні готові комплекти радіоуправління для різних літаючих іграшок і «не іграшок», і з'явилося бажання використовувати саме таке управління.

Сигнали на виході приймача такого комплекту - це імпульси для керування сервомашинками,

і завдання зводиться до того, щоб виміряти тривалість імульсу 0,8.. 2,3 мс в кожному з шести каналів, витративши якомога менше ресурсів контролера.

Далі описано як реалізовано вимірювання тривалості імпульсів з шести каналів, використовуючи особливості периферії STM32 мікроконтролерів.

Залізо, на якому проводилася налагоджування - апаратура радіоуправління HobbyKing HK-T6A і контролер STM32F100C8T.

На виході прийомника маємо такі сигнали:

Очевидне рішення - запустити 6 таймерів і кожен канал вимірювати окремим таймером.

По передньому фронту імпульсу скидаємо таймер, починаємо відлік. По задньому фронту дивимося скільки таймер нарахував з моменту скидання, це і буде тривалість імпульсу.

Для скорочення кількості задіяних таймерів спробуємо завести кілька каналів на один таймер, але так, щоб було якомога менше програмної обробки.

Будемо використовувати режим таймера для роботи з Hall sensor.

Безколекторні двигуни іноді як датчик положення ротора мають три датчики холу, встановлених зі зміщенням. Зображення нижче дає наочне зображення як це відбувається.

Для обробки датчика холу безколекторного електродвигуна деякі таймери в STM32 можуть бути налаштовані таким чином, що:

- по передньому фронту на будь-якому з трьох входів CC1, CC2, CC3 таймер скидається,

- по задньому фронту, на будь-якому з цих входів, в регістрі capture compare CCMR1 приєднується значення лічильника таймера - за фактом тривалість імпульсу.

Таким чином, в одному регістрі послідовно маємо значення тривалості імпульсів за трьома каналами.

Програмно можна якось розділити ці значення, але простіше залишити тільки два канали ch2 і ch3, значення від них будуть долучатися в CCR2 і CCR3 регістрах, а скидатися таймер буде по передньому фронту на будь-якому з цих каналів.

Пін, на який призначено вхід ch1, налаштуємо на вихід, щоб не ловити перешкоди на невикористаному вході.

На малюнку нижче: червона стрілочка показує шлях сигналу скидання таймера, зелені стрілочки - шлях сигналу вирівнювання значення таймера.

У підсумку для читання 6 каналів будемо використовувати 3 таймери, і без додаткових програмних хитрощів, в регістрах CCR таймерів мати необхідні значення.

Невеликий сюрприз.

Коли це все вже було реалізовано, але я в черговий раз забув розпинування виходу прийомника, то при пошуку в інтернеті натрапив на цікаву картинку.

Взято отсюда.

Повивчавши інтернет, зробив для себе відкриття.

Виявилося, що вхід, який використовується для харчування і прив'язки приймача до передавача, на виході має сигнал, що об'єднує всі 6 виходів.

Тільки вимірювати треба не тривалість імпульсів, а час між імпульсами.

Називається він PPM і широко використовується для підключення апаратури радіоуправління до комп'ютера як джойстик для симулятора (через адаптер, зрозуміло).

Є різні версії адаптерів, і навіть на основі недорого USBasp программатора.

При наявності PPM виходу завдання спростилося: вимірювати час між шістьма послідовними імпульсами, початком до вимірювання буде досить тривала пауза.

По передньому фронту імпульсу зберігаємо значення таймера і відразу скидаємо таймер.

Коли імпульси не надходять, відбувається переривання з переповнення таймера, в цей момент готуємося до прийому імпульсів, налаштовуємо DMA так, щоб скласти 7 значень в масив.

Перший імпульс синхронізує процес вимірювання, значення таймера в цей момент нас не цікавить, наступні 6 значень будуть зберігати час між імпульсами.

При кожному наступному імпульсі значення таймера відкриється, таймер скидається, сповільнене значення по DMA зберігається в масив.

Виводимо всі результати вимірювання першим і другим способом в UART, бачимо, що працює, на відхилення рукояток реагує.

При відхиленні рукояток значення змінюються в діапазоні 3200.. 5700 одиниць

Тільки ось значення вимірені першим і другим способом відрізняються, але логічний аналізатор те ж саме показує, так що це особливості апаратури.

Граблі, на які вдалося наступити в процесі розробки.

Поведінка програми виглядає так - при включенні харчування програма працює, але при роботі з зневадником вилітає в HardFault_Handler. Програматор з'єднано за SWD лінією (SWDIO, SWIO, GND).

Виявилося:

- При ресеті программатором не очищається регістр статусу таймера TIM1- > SR, там залишаються прапорці запиту на переривання,

- при ініціалізації таймера дозволяється переривання від TIM1,

- переривання для TIM1 і TIM15 спільне, і коли звертаємося до структури htim15, то вона ще не визначена, тому відлітаємо в HardFault_Handler.

Ось так виглядає процедура переривання

void TIM1_BRK_TIM15_IRQHandler(void)

{

/* USER CODE BEGIN TIM1_BRK_TIM15_IRQn 0 */

/* USER CODE END TIM1_BRK_TIM15_IRQn 0 */

HAL_TIM_IRQHandler(&htim1);

HAL_TIM_IRQHandler(&htim15);

/* USER CODE BEGIN TIM1_BRK_TIM15_IRQn 1 */

/* USER CODE END TIM1_BRK_TIM15_IRQn 1 */

}

Весь цей код згенеровано Cub-ом автоматично, тому довелося перед всією ініціалізацією додати рядок htim15.Instance = TIM15.

Програми:

1. Проект для Cube

2. Дані 6 каналів + PPM збережені з USB логічного аналізатора Logic-U