RSS блога
Подписка
Ломаем квадрик: вандализация с элементами реверс-инжиниринга
В последнее время авторы взялись ранее купленные ненужные вещи раздербанивать и смотреть, что там внутрях. Подержу-ка и я эту инициативу — у меня много лет назад купленный баксов за 10 квадрик завалялся, и он мне, как есть, явно абсолютно не нужен. Но может для чего и сгодится — ниже вы найдете что-то наподобие реверс-инжиниринга с кучей библиотек.
Для чего я это все делал — в статью не вошло, и так слишком много получается, не у каждого хватит терпения прочитать.
Вот в таком виде квадрик сохранился — и коробка есть, и запасная батарейка, и запасные винты. Помнится, я его купил, когда еще здоров был, и даже несколько раз игрался. Буквально через месяц после покупки все началось — и первым поехало зрение. Картинка раздвоилась и что к чему — уже было непонятно. И каким из двух мультикоптеров управлять, и как далеко он находится — оставалось только гадать.
При раскурочивании игрушки целью было все-таки понять все протоколы, чтобы потом плату можно было использовать в мирных целях.
Раскручиваем пульт управления. В качестве приемо-передатчика используется микросхема XN297, регистры управления похожи на Nordic Semiconductor nRF24L01 с некоторыми дополнениями, но по формату посылки не совсем совместима. С микроконтроллера название сошлифовано, но мы же не лыком шиты, и чувствуем, что это TG54528.
К сожалению, от этого знания толку нуль. Что она из себя представляет и как ее программировать гугл не знает или не захотел мне рассказать. Придется использовать пульт как есть, протокол мы раскопаем позднее.
Дисплейчик используется некий BAOMEI BM-8025A. К сожалению, издевательств не выдержал и после сборки уже не работал.
Собираем пульт и пытаемся разобраться с управлением квадрика.
Обнаруживаем LDO XC6206P282MR на 2.8 Вольта.
Микроконтроллер STM32F031K4 (ARM®32-bit Cortex®-M0 up to 48 MHz, 4 Kbytes of SRAM, 16 Kbytes Flash), гироскоп с акселерометром MPU6881 (оказался вполне совместим с MPU6150, наверняка у DMP прошивка другая, но так как ее описания все равно нет, то и не заботит) и приемопередатчик — тот же XN297, что и в пульте управления.
Рисуем схему соединений — получается что-то типа такого, остальное нас мало волнует:
Подключаем контрольные точки — потенциальным критикам пайки могу только заметить — попробуйте сделать так с жуткой атаксией.
Точки подпайки — используем провод от дохлых наушников-вкладышей.
Вид снизу:
Для начала подключаемся к SPI и смотрим инициализацию XN297
А теперь, как пульт управления биндится с квадриком — для этого пульт нужно включить с ручкой trottle внизу, поднять ее до упора вверх и снова опустить. В это время нужно посмотреть, что происходит с SPI.
После этого переключаемся на I2C и смотрим инициализацию MPU6881 — собственно, чтобы убедиться в совместимости с MPU6150.
Важно ничего не упустить — микропроцессор залочен, прошивку считать малой кровью не получится. Чтобы разблокировать доступ — нужно стереть содержимое флеш-памяти. Соответственно, потом уже ничего не посмотришь.
Теперь как все это будем программировать? Самое простое — использовать Microsoft Visual Studio Code — бесплатная среда, которая прекрасно работает с Linux, если вам это важно. Ставим плагин PlatformIO и создаем любой проект на базе любого контроллера STM32. Во время создания этого проекта PlatformIO сам натащит из интернета все нужные вам инструменты и библиотеки. К сожалению, STM32F031K4 там отсутствует. Но где наша не пропадала?
В каталоге .platformio/platforms/ststm32/boards создаем файл genericSTM32F031K4.json
со следующим содержимым:
Теперь можно даже с помощью ардуино программировать этот контроллер, но не рекомендую. Сама по себе не особо эффективная среда ардуино будет работать поверх HAL. Не буду высказывать свое мнение, кубологи будут недовольны — но у такой связки простейшая моргалка светодиодом съест больше 50% доступной памяти. Поэтому я дальше буду пользоваться старой, как дерьмо мамонта, библиотекой libopencm3 или вообще напрямую в регистры писать.
Как всегда, начинаем с самого тупого — поморгаем светодиодами. Для этого нужно написать подпрограммы инициализации и выводы в порты ввода-вывода. Лиха беда начало, пишем:
Используем часть этих подпрограмм в основном цикле:
Моргает!
Для дальнейшей отладки нам не помешает последовательный вывод, одним светодиодиком придется пожертвовать, вместо него подключим вывод UART — это будет вывод PA2. Когда все будет готово, эту библиотеку нужно будет прибить насмерть, чтобы место не занимала и не мешала могралкам.
Больше большие куски кода в текст вставлять не буду, все можно загрузить отсюда. Так как неизвестно, что когда отключат и запретят, лежит на майлрушном облаке, его вряд ли тронут. Вроде как гитхаб уже грозился пакость с отключением устроить. Хотя если бы Муське была бы возможность сохранять небольшие файлы — это было бы намного удобно.
Теперь пробуем поиграться с пультом управления, заодно разберемся со структурой пакетов, которые пульт отправляет. Библиотеку, написанную для XN297 найдете в архиве, ссылка выше. Сам тест выглядит так:
Теперь можно очень просто разобраться с посылкой пульта управления, у меня получилось вот так:
Управлять можно моторчиками с помощью PWM или сервами с PPM — в библиотеках все есть.
Для полноты картины добавим в библиотеки функции для работы с MPU6881. Вообще-то он мне не нужен, но уж раз есть — надо иметь возможность пользоваться. Общеизвестно, что определить положение движущегося объекта при помощи только акселерометра достоверно нельзя, нужен еще гироскоп и, если совсем по-хорошему, и магнетометр. И арифметику вспомнить придется. Вы еще помните, что такое кватернионы? Я — нет.
Придется вспомнинать, ну а в библиотеках все приложено, в том числе и фильтр Махони.
Вот простейший метод протестировать эту библиотеку. Магнетометра нет, и поэтому у этого фильтра курс слегка плывет. Проще всего, наверно, это устранить в процессе калибровки.
В этой библиотеке вы также найдете общеизвестный трюк по извлечению обратного квадратного корня из Quake III Arena.
Для чего я этот квадрик расковырял — может, в другой раз расскажу. А пока позвольте на этом откланяться — может, кто-то чего-нибудь полезного найдет в моем рассказике.
Для чего я это все делал — в статью не вошло, и так слишком много получается, не у каждого хватит терпения прочитать.
Вот в таком виде квадрик сохранился — и коробка есть, и запасная батарейка, и запасные винты. Помнится, я его купил, когда еще здоров был, и даже несколько раз игрался. Буквально через месяц после покупки все началось — и первым поехало зрение. Картинка раздвоилась и что к чему — уже было непонятно. И каким из двух мультикоптеров управлять, и как далеко он находится — оставалось только гадать.
Берем картину мироздания и тупо смотрим, что к чему.
При раскурочивании игрушки целью было все-таки понять все протоколы, чтобы потом плату можно было использовать в мирных целях.
Раскручиваем пульт управления. В качестве приемо-передатчика используется микросхема XN297, регистры управления похожи на Nordic Semiconductor nRF24L01 с некоторыми дополнениями, но по формату посылки не совсем совместима. С микроконтроллера название сошлифовано, но мы же не лыком шиты, и чувствуем, что это TG54528.
К сожалению, от этого знания толку нуль. Что она из себя представляет и как ее программировать гугл не знает или не захотел мне рассказать. Придется использовать пульт как есть, протокол мы раскопаем позднее.
Дисплейчик используется некий BAOMEI BM-8025A. К сожалению, издевательств не выдержал и после сборки уже не работал.
Собираем пульт и пытаемся разобраться с управлением квадрика.
Обнаруживаем LDO XC6206P282MR на 2.8 Вольта.
Микроконтроллер STM32F031K4 (ARM®32-bit Cortex®-M0 up to 48 MHz, 4 Kbytes of SRAM, 16 Kbytes Flash), гироскоп с акселерометром MPU6881 (оказался вполне совместим с MPU6150, наверняка у DMP прошивка другая, но так как ее описания все равно нет, то и не заботит) и приемопередатчик — тот же XN297, что и в пульте управления.
Рисуем схему соединений — получается что-то типа такого, остальное нас мало волнует:
Подключаем контрольные точки — потенциальным критикам пайки могу только заметить — попробуйте сделать так с жуткой атаксией.
Точки подпайки — используем провод от дохлых наушников-вкладышей.
Вид снизу:
Для начала подключаемся к SPI и смотрим инициализацию XN297
Инициализация
160us
-----
0x200F/0xE00 *
-----
160us
-----
0x2009/0xE00 *
0x2A26/0xE00 0xA867/0x00 0x35CC/0x00 *
0xAFF/0xE26 *
0x3026/0xE00 0xA867/0x00 0x35CC/0x00 *
0x10FF/0xE26 *
0x390B/0xE00 0xDFC4/0x00 0xA703/0x00 *
0x3EC9/0xE00 0x9AB0/0x00 0x61BB/0x00 0xAB9C/0x00 *
0x1EFF/0xEC9 *
0x3F4C/0xE00 0x846F/0x00 0x9C20/0x00 *
0x1FFF/0xE4C *
0x2607/0xE00 *
0x310F/0xE00 *
А теперь, как пульт управления биндится с квадриком — для этого пульт нужно включить с ручкой trottle внизу, поднять ее до упора вверх и снова опустить. В это время нужно посмотреть, что происходит с SPI.
Bind
0x7FF/0x4040
0x2009/0x4000
0x61FF/0x40AA 0xFFFF/0xE62 0xFFFF/0x00 0xFFFF/0xBC 0xFFFF/0x7F7F 0xFFFF/0x201E 0xFFFF/0x4040 0xFFFF/0x00
0x2740/0x4E00
0xE200/0xE00
0x200F/0xE00
-------
0x2009/0xE00
0x2511/0xE00
0x200F/0xE00
0x7FF/0xE0E
После этого переключаемся на I2C и смотрим инициализацию MPU6881 — собственно, чтобы убедиться в совместимости с MPU6150.
Инициализация MPU6881
write to 0x68 ack data: 0x6B 0x02
write to 0x68 ack data: 0x19 0x01
write to 0x68 ack data: 0x1A 0x01
write to 0x68 ack data: 0x1B 0x18
write to 0x68 ack data: 0x1C 0x18
write to 0x68 ack data: 0x1D 0x00
write to 0x68 ack data: 0x1E 0x0A
write to 0x68 ack data: 0x3B
read to 0x68 ack data: 0xFF 0xD3 0xFF 0x8A 0x08 0xF9 0x09 0xF9 0xFF 0x7F 0x00 0x24 0xFF 0xFE
write to 0x68 ack data: 0x3B
read to 0x68 ack data: 0xFF 0xD3 0xFF 0x8A 0x08 0xF9 0x09 0xF9 0xFF 0x7F 0x00 0x24 0xFF 0xFE
write to 0x68 ack data: 0x3B
read to 0x68 ack data: 0xFF 0xD3 0xFF 0x8A 0x08 0xF9 0x09 0xF9 0xFF 0x7F 0x00 0x24 0xFF 0xFE
Важно ничего не упустить — микропроцессор залочен, прошивку считать малой кровью не получится. Чтобы разблокировать доступ — нужно стереть содержимое флеш-памяти. Соответственно, потом уже ничего не посмотришь.
Теперь как все это будем программировать? Самое простое — использовать Microsoft Visual Studio Code — бесплатная среда, которая прекрасно работает с Linux, если вам это важно. Ставим плагин PlatformIO и создаем любой проект на базе любого контроллера STM32. Во время создания этого проекта PlatformIO сам натащит из интернета все нужные вам инструменты и библиотеки. К сожалению, STM32F031K4 там отсутствует. Но где наша не пропадала?
В каталоге .platformio/platforms/ststm32/boards создаем файл genericSTM32F031K4.json
со следующим содержимым:
genericSTM32F031K4.json
{
"build": {
"cpu": "cortex-m0",
"extra_flags": "-DSTM32F031x4",
"f_cpu": "48000000L",
"mcu": "stm32f031k4",
"product_line": "STM32F031x4",
"variant": "STM32F0xx/F031K4"
},
"debug": {
"jlink_device": "STM32F031K4",
"openocd_target": "stm32f0x",
"svd_path": "STM32F031.svd"
},
"frameworks": [
"arduino",
"cmsis",
"stm32cube",
"libopencm3"
],
"name": "generic STM32F031K4",
"upload": {
"maximum_ram_size": 4096,
"maximum_size": 16384,
"protocol": "stlink",
"protocols": [
"jlink",
"cmsis-dap",
"stlink",
"blackmagic",
"serial"
]
},
"url": "https://www.hotmcu.com/stm32f030f4p6-minimum-systerm-boardcortexm0-p-208.html",
"vendor": "Generic"
}
Теперь можно даже с помощью ардуино программировать этот контроллер, но не рекомендую. Сама по себе не особо эффективная среда ардуино будет работать поверх HAL. Не буду высказывать свое мнение, кубологи будут недовольны — но у такой связки простейшая моргалка светодиодом съест больше 50% доступной памяти. Поэтому я дальше буду пользоваться старой, как дерьмо мамонта, библиотекой libopencm3 или вообще напрямую в регистры писать.
Как всегда, начинаем с самого тупого — поморгаем светодиодами. Для этого нужно написать подпрограммы инициализации и выводы в порты ввода-вывода. Лиха беда начало, пишем:
system.cpp
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
#include <system.h>
volatile uint32_t TimeCounter;
void system_init(void)
{
rcc_clock_setup_in_hsi_out_48mhz(); // set STM32 to clock by 48MHz from HSI oscillator
TimeCounter=0;
systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);
STK_CVR = 0; // clear counter
systick_set_reload(rcc_ahb_frequency / 1000); // Set up timer interrupt
systick_counter_enable();
systick_interrupt_enable();
}
void delay_us(uint16_t del_us)
{
uint32_t cnt = del_us << 2;
do
{
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
} while (--cnt);
}
void delay(uint32_t time)
{
TimeCounter = time;
while(TimeCounter != 0);
}
void sys_tick_handler(void)
{
if (TimeCounter |= 0) TimeCounter--;
}
void init_LEDs(void)
{
// Enable clocks to the GPIO subsystems
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_GPIOA);
// LED OUTPUTS
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO2 | GPIO4 | GPIO12);
gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO0 | GPIO2);
gpio_clear(GPIOA, GPIO2 | GPIO4 | GPIO12);
gpio_clear(GPIOB, GPIO0 | GPIO2);
}
void LedMask(uint8_t mask)
{
if (mask & 0x01) gpio_set(GPIOA, GPIO2); else gpio_clear(GPIOA, GPIO2);
if (mask & 0x02) gpio_set(GPIOA, GPIO4); else gpio_clear(GPIOA, GPIO4);
if (mask & 0x04) gpio_set(GPIOB, GPIO0); else gpio_clear(GPIOB, GPIO0);
if (mask & 0x08) gpio_set(GPIOA, GPIO12); else gpio_clear(GPIOA, GPIO12);
if (mask & 0x10) gpio_set(GPIOB, GPIO2); else gpio_clear(GPIOB, GPIO2);
}
void LedSet(uint8_t mask)
{
if (mask & 0x01) gpio_set(GPIOA, GPIO2);
if (mask & 0x02) gpio_set(GPIOA, GPIO4);
if (mask & 0x04) gpio_set(GPIOB, GPIO0);
if (mask & 0x08) gpio_set(GPIOA, GPIO12);
if (mask & 0x10) gpio_set(GPIOB, GPIO2);
}
void LedClear(uint8_t mask)
{
if (mask & 0x01) gpio_clear(GPIOA, GPIO2);
if (mask & 0x02) gpio_clear(GPIOA, GPIO4);
if (mask & 0x04) gpio_clear(GPIOB, GPIO0);
if (mask & 0x08) gpio_clear(GPIOA, GPIO12);
if (mask & 0x10) gpio_clear(GPIOB, GPIO2);
}
void LedToggle(uint8_t mask)
{
if (mask & 0x01) gpio_toggle(GPIOA, GPIO2);
if (mask & 0x02) gpio_toggle(GPIOA, GPIO4);
if (mask & 0x04) gpio_toggle(GPIOB, GPIO0);
if (mask & 0x08) gpio_toggle(GPIOA, GPIO12);
if (mask & 0x10) gpio_toggle(GPIOB, GPIO2);
}
Используем часть этих подпрограмм в основном цикле:
Blink
#include <system.h>
main(void)
{
system_init();
init_LEDs();
while (1)
{
uint8_t mask = 1;
for(uint8_t i=0; i<5; i++)
{
LedMask(mask);
mask <<=1;
delay(500);
}
}
}
Моргает!
Для дальнейшей отладки нам не помешает последовательный вывод, одним светодиодиком придется пожертвовать, вместо него подключим вывод UART — это будет вывод PA2. Когда все будет готово, эту библиотеку нужно будет прибить насмерть, чтобы место не занимала и не мешала могралкам.
UART.cpp
#include <UART.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <math.h>
void init_USART(void)
{
// only tx at PA2
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_USART1);
// Setup GPIO pins for USART transmit.
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2);
gpio_set_af(GPIOA, GPIO_AF1, GPIO2);
// Setup USART parameters.
usart_set_baudrate(USART1, 115200);
usart_set_databits(USART1, 8);
usart_set_parity(USART1, USART_PARITY_NONE);
usart_set_stopbits(USART1, USART_CR2_STOPBITS_1);
usart_set_mode(USART1, USART_MODE_TX_RX);
usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
usart_enable(USART1);
}
uint8_t num2sym(uint8_t symb)
{
symb &= 0x0F;
if (symb<10) symb+='0';
else symb+='A'-10;
return symb;
}
void tx_byte_UART(uint8_t c)
{
tx_UART('0');
tx_UART('x');
tx_UART(num2sym(c>>4));
tx_UART(num2sym©);
}
void tx_word_UART(uint16_t c)
{
tx_UART('0');
tx_UART('x');
tx_UART(num2sym(c>>12));
tx_UART(num2sym(c>>8));
tx_UART(num2sym(c>>4));
tx_UART(num2sym©);
}
void tx_UART(uint8_t c)
{
while ((USART_ISR(USART1) & USART_ISR_TXE)==0); // TX empty
usart_send(USART1, c);
}
void buff_UART(uint8_t *pointer, uint8_t length)
{
do
{
uint8_t data= *pointer;
tx_UART(num2sym(data>>4));
tx_UART(num2sym(data));
tx_UART(' ');
pointer++;
} while(--length);
tx_UART(0xa);
tx_UART(0xd);
}
void int2s(uint8_t *String, int16_t Data)
{
ldiv_t DivNum;
uint8_t * Pointer;
bool sign = false;
if(Data<0)
{
sign = true;
Data = -Data;
}
Pointer = String;
for(uint8_t i=0; i<5; i++) *Pointer++ = ' ';
*Pointer = 0;
Pointer = String+4;
for(uint8_t i=0; i<4; i++)
{
if (Data<10)
{
*Pointer--= (char)Data+'0';
break;
}
else
{
DivNum = ldiv(Data, 10);
*Pointer-- = (char)DivNum.rem+'0';
Data = DivNum.quot;
}
}
if (sign) *Pointer = '-';
}
void str2UART(uint8_t *pointer)
{
uint8_t maxl=16;
do
{
uint8_t data= *pointer;
if (data==0) break;
tx_UART(data);
pointer++;
} while(--maxl);
}
void int2UART(uint16_t Number)
{
uint8_t tx_buff[16];
int2s(tx_buff, Number);
str2UART(tx_buff);
}
Больше большие куски кода в текст вставлять не буду, все можно загрузить отсюда. Так как неизвестно, что когда отключат и запретят, лежит на майлрушном облаке, его вряд ли тронут. Вроде как гитхаб уже грозился пакость с отключением устроить. Хотя если бы Муське была бы возможность сохранять небольшие файлы — это было бы намного удобно.
Теперь пробуем поиграться с пультом управления, заодно разберемся со структурой пакетов, которые пульт отправляет. Библиотеку, написанную для XN297 найдете в архиве, ссылка выше. Сам тест выглядит так:
Тест XN297
#include <XN297.h>
#include <SPI.h>
#include <UART.h>
#include <system.h>
void CheckVoltage(void)
{
adc_read();
delay(100);
adc_read();
if (adc_read()<CODE_MIN)
{
for(uint8_t j=0; j<6; j++)
{
uint8_t mask = 1;
for(uint8_t i=0; i<5; i++)
{
LedMask(mask);
mask <<=1;
delay(100);
}
}
standby();
}
}
void TestRadio(void)
{
static bool binded = false;
uint16_t out16;
static uint8_t blink_cnt=0;
out16 = transfer_word_SPI(0x07, 0xFF);
if (out16 & 0x40)
{
if (binded) rx_pack();
else
{
bind();
binded = true;
LedClear(0x10);
}
buff_UART(rx_buff,16);
}
else
{
if (!binded)
{
blink_cnt++;
if (blink_cnt==50)
{
blink_cnt=0;
LedToggle(0x10);
}
}
}
delay(2);
}
int main(void)
{
system_init();
init_LEDs();
adc_init();
init_USART();
init_SPI();
init_XN297();
CheckVoltage();
uint8_t mask = 1;
for(uint8_t i=0; i<5; i++)
{
LedMask(mask);
mask <<=1;
delay(500);
}
while (1)
{
TestRadio();
}
}
Теперь можно очень просто разобраться с посылкой пульта управления, у меня получилось вот так:
Управлять можно моторчиками с помощью PWM или сервами с PPM — в библиотеках все есть.
Крутим моторчики и включаем светодиодики с пульта
uint8_t u8map(int16_t x, int16_t in_min, int16_t in_max, int16_t out_min, int16_t out_max)
{
return (x - in_min) *(out_max - out_min) / (in_max - in_min) + out_min;
}
int main(void)
{
system_init();
init_LEDs();
init_PWM();
init_SPI();
init_USART();
init_XN297();
bool binded = false;
while (1)
{
uint16_t out16;
out16 = transfer_word_SPI(0x07, 0xFF);
if (out16 & 0x40)
{
if (binded)
{
rx_pack();
buff_UART(rx_buff,16);
uint8_t status;
status = rx_buff[14];
if(status &0x04) gpio_set(GPIOA, GPIO2); else gpio_clear(GPIOA, GPIO2);
if(status &0x08) gpio_set(GPIOA, GPIO4); else gpio_clear(GPIOA, GPIO4);
if(status &0x10) gpio_set(GPIOA, GPIO12); else gpio_clear(GPIOA, GPIO12);
if(status &0x20) gpio_set(GPIOB, GPIO0); else gpio_clear(GPIOB, GPIO0);
if(status &0x40) gpio_set(GPIOB, GPIO2); else gpio_clear(GPIOB, GPIO2);
status = rx_buff[6];
status = u8map(status, 0, 0xff, 0, 100);
timer_set_oc_value(TIM1, TIM_OC1, status);
status = rx_buff[7];
status = u8map(status, 0x43, 0xbb, 0, 100);
timer_set_oc_value(TIM1, TIM_OC2, status);
status = rx_buff[8];
status = u8map(status, 0x43, 0xbb, 0, 100);
timer_set_oc_value(TIM2, TIM_OC1, status);
status = rx_buff[9];
status = u8map(status, 0x43, 0xbb, 0, 100);
timer_set_oc_value(TIM2, TIM_OC2, status);
}
else
{
bind();
binded = true;
}
}
delay(100);
}
}
Для полноты картины добавим в библиотеки функции для работы с MPU6881. Вообще-то он мне не нужен, но уж раз есть — надо иметь возможность пользоваться. Общеизвестно, что определить положение движущегося объекта при помощи только акселерометра достоверно нельзя, нужен еще гироскоп и, если совсем по-хорошему, и магнетометр. И арифметику вспомнить придется. Вы еще помните, что такое кватернионы? Я — нет.
Придется вспомнинать, ну а в библиотеках все приложено, в том числе и фильтр Махони.
Вот простейший метод протестировать эту библиотеку. Магнетометра нет, и поэтому у этого фильтра курс слегка плывет. Проще всего, наверно, это устранить в процессе калибровки.
Тест IMU
cImu IMU;
uint8_t cout;
void TestImu(void)
{
IMU.readAccelData(); // Read the x/y/z adc values
IMU.readGyroData(); // Read the x/y/z adc values
IMU.Mahony_no_mag_Update();
delay(20);
cout++;
if (cout==50)
{
IMU.quater2euler();
cout=0;
int16_t Angles[3];
Angles[0] = (int16_t)(IMU.roll);
Angles[1] = (int16_t)(IMU.pitch);
Angles[2] = (int16_t)(IMU.yaw);
for(uint i=0; i<3; i++)
{
int2UART(Angles[i]);
tx_UART(' ');
}
tx_UART(0xa);
tx_UART(0xd);
}
}
int main(void)
{
system_init();
init_LEDs();
init_USART();
init_i2c();
IMU.resetMPU9250(); // Reset registers to default in preparation for device calibration
IMU.calibrateMPU9250(IMU.gyroBias, IMU.accelBias); // Calibrate gyro and accelerometers, load biases in bias registers
IMU.initMPU9250();
IMU.deltat = 0.02;
cout=0;
while(1) TestImu();
}
В этой библиотеке вы также найдете общеизвестный трюк по извлечению обратного квадратного корня из Quake III Arena.
Для чего я этот квадрик расковырял — может, в другой раз расскажу. А пока позвольте на этом откланяться — может, кто-то чего-нибудь полезного найдет в моем рассказике.
Самые обсуждаемые обзоры
+27 |
1775
43
|
+22 |
1451
26
|
+14 |
1439
44
|
С одной стороны конечно хороший реверсинг, а с другой — зачем вот это двукратное упоминание про «а зачем это всё было — я вам не скажу», и отмазка про «длинный обзор» тут не актуальна — видали раз в 10 больше, от того-же Кирича.
Так что осталось такое себе кликбейтовское послевкусие. Извини ДонБатон, ничего личного, просто моё мнение.
-как?
-завтра расскажу…
© ничего личного, просто моё мнение
Умейте ждать, и не искать во всём кликбейт или подвох.
Интересный обзор и понятно дело что такой реверс быстро не делается, что по железу, что по софту.
P. S. Дон, спасибо за отличный обзор, с нетерпением буду ждать продолжения.
Например возможность дорабатывать прошивку и купив коптер за 10$, дать ему возможности, которые вам нужны, а не те, что сделали и ленивые китаясы.
Возможность прикрутить нормальную аппу, или вообще управлять с ББ и тд.
А ещё это просто интересно — разобраться как что-то сделано и работает.
У проца мозгов слишком мало
Стм32-103 это ну совсем ни о чем и копеечно
Если хотите дорабатывать прошивку — курите в сторону ardupilot или PX4, оно в исходниках на гитхабе
Это вам говорит человек, сделавший на спор на attiny опрос аналогового ик-приемника и одновременную работу WS-ленты. Тоже все говорили — «мощности проца не хватит, коды не будут распознаваться!», только на деле спорящим не мощности проца не хватает, а знаний.
Проц из обзора — это не топ стм-а, но имя мозги и зная асм и не полагаясь на сгенерированый идехой стартап-код и не копипастя со стэковерфлоу и гита из него можно выжать куда больше, чем выжали китайцы на данный момент. Если не умеете — то нефиг всех ровнять по себе.
А тинька, уважаемый, была дана для примера того, что можно сделать, когда все другие будут кричать, что «невозможно».
При том что я писал про квадр.
И предлагал вместо этого убогого китайского квадра заняться реально полезным делом — реверсинжиниринговать более интересный квадр DJI.
А вы влезли в тему про квадр со своей вшивотозной аттини доказывая что вы крутой квадростроитель. Если вы не про квадр то чего вы мне пишете то?
Напишите сами тогда более продвинутый мозг квадру чтобы мог летать по маршруту используя GPS. Вы я вижу специалист по самостоятельному написанию полетных контроллеров для квадрокоптеров :)
какими инструментами пользоваться? (осциллографа конечно же нет)
Все чипы изготавливаются в китае, но всё что есть на али — со следами лазерной гравировки.
Оригинальный чип имеет горячее тиснение краской.
И ещё — он жутко шумный, использовать без магнитометра нет смысла.
в проекте www.multi-module.org/basics/supported-protocols или www.deviationtx.com/wiki/supported_models посмотри, возможно будет тебе щасте =)
кстати, F0 процы читаются. только уметь надо.
Мне пока не нужно, но вдруг в копилочку знаний попадётся :)
Вы такое сами делали? Правда или это ваша фантазия?
Там есть цитата, на то, на что я комментарий писал.
Цитата, причем, вашего комментария — попробуйте разобрать буковки, тогда может и мой ответ дойдет :D
Попробуйте прочитать буковки о чем идет речь, а потом уж встревать.
Ииии… я жду от вас вами построенный квадрокоптер на аттини :)
12 килобайт! И летал и управлялся, находил цели и стрелял, и сам после убийства пилота возвращался на точку старта и садился.
Для тех кто программировал в СССР на ассемблере или в машинных кодах и этого было МНОГО!
Кстати вся игра с цветной 3Д графикой и генерацией бесконечных вселенных и планет, с симулятором космического истребителя, Элита на пк Синклер Спектрум занимала всего 48 килобайт!
Кстати, из-за картинки Elite грузилась дольше всех намного, учитывая всякие помехи, ещё и не спервого раза. А если вспомнить мигание света в то время, и сохранения на на магнитофон :)
Ах да. В конце-концов она, конечно же, занимала свои 42кб, ибо 6192 — видеопамять.
Автор принимает комиты если есть возможность протестить потом.