Перед началом, хотелось бы сразу оговориться, что код написан не мной и взят отсюда. Данная программа написана в среде Arduino IDE и в связке с arduino pro mini и nrf24l01+ позволяет управлять игрушечными мультикоптерами (с радиочипами XN297, клонами nrf24l01) с любых аппаратур управления, на которых есть PPM выход. Всю информацию о поддерживаемых мультикоптерах можно найти по ссылке выше.
Я решил переписать данный код для управления коптером Eachine H8 mini с аппаратуры Radiolink AT9. За подробностями прошу под кат.
MSP430 был выбран потому, что у него напряжение питания 3.3 вольта, напряжение питания nrf24l01 также 3.3 вольта, да и MPS430 мне как-то больше нравятся. Внутри аппаратуры располагаются контакты 3V3, OUT, GND, к которым мы и будем подключаться.
Если нет желания разбирать аппаратуру, то можно подключится к тренерскому разъему, но напряжение на нем = напряжению аккумулятора, поэтому в схему добавляется стабилизатор напряжения.
Приступим к коду
Первым делом откроем проект в IDE Energia (клон Arduino IDE для MSP430 и других чипов от TI) и попытаемся его скомпилировать, но сразу же увидим ошибку компиляции. Данный проект использует дополнительные библиотеки и обращение к регистрам, и первым делом нужно начинать с них. Итак, приступим к детальному анализу.
Библиотеки
Править будем файл nRF24_multipro.ino и начнем с include’ов. В данном проекте используются библиотеки atomic и EEPROM.
atomic
Убираем строку
#include <util/atomic.h>ATOMIC_BLOCK(ATOMIC_RESTORESTATE)__disable_interrupt();__enable_interrupt();Функция ATOMIC_BLOCK() выключает прерывания, пока исполняется код в его «теле» и в зависимости от параметра, включает прерывания или восстанавливает значение флага прерывания в состояние, которое было до вызова функции ATOMIC_BLOCK().
EEPROM
В MSP430 нет EEPROM памяти, но есть FLASH память и для работы с ней есть библиотека MspFlash. Поэтому убираем строку
#include <EEPROM.h>#define flash SEGMENT_Delse 
	current_protocol = constrain(EEPROM.read(ee_PROTOCOL_ID),0,PROTO_END-1);      
// update eeprom 
EEPROM.update(ee_PROTOCOL_ID, current_protocol);// update eeprom 
Flash.write(flash+ee_PROTOCOL_ID, & current_protocol,1);В фунции void set_txid(bool renew) нужно сделать немного больше, чем заменить две строки. В FLASH памяти мы можем только обнулять биты. Для выставления в битах FLASH памяти значения 1, нужно стереть весь сегмент (тогда в него запишутся значения 1). Данная функция (set_txid) вызывается раньше чем selectProtocol, поэтому стирать сегмент мы будем тут.
Было:
void set_txid(bool renew)
{
    uint8_t i;
    for(i=0; i<4; i++)
        transmitterID[i] = EEPROM.read(ee_TXID0+i);
    if(renew || (transmitterID[0]==0xFF && transmitterID[1]==0x0FF)) {
        for(i=0; i<4; i++) {
            transmitterID[i] = random() & 0xFF;
            EEPROM.update(ee_TXID0+i, transmitterID[i]); 
        }            
    }
}Стало:
void set_txid(bool renew)
{
    uint8_t i;
    unsigned char p;
    for(i=0; i<4; i++) {
        Flash.read(flash+ee_TXID0+i,&p,1);
        transmitterID[i] =p;
    }
	Flash.read(flash+ee_PROTOCOL_ID,&p,1);
    current_protocol = constrain(p,0,PROTO_END-1);
	Flash.erase(flash);
    if(renew || (transmitterID[0]==0xFF && transmitterID[1]==0x0FF)) {
        for(i=0; i<4; i++) {
            transmitterID[i] = random(0xff) & 0xFF;
            p = transmitterID[i];
            Flash.write(flash+ee_TXID0+i, &p,1); 
        }            
    }else{
        for(i=0; i<4; i++) {
            p = transmitterID[i];
            Flash.write(flash+ee_TXID0+i, &p,1); 
        }  	
	}
}Регистры
Для начала разберемся с пинами. В данном проекте используется программная реализация SPI, при котором используются макросы для «дергания» ногами через регистры.
#define PPM_pin   2  // PPM in
//SPI Comm.pins with nRF24L01
#define MOSI_pin  3  // MOSI - D3
#define SCK_pin   4  // SCK  - D4
#define CE_pin    5  // CE   - D5
#define MISO_pin  A0 // MISO - A0
#define CS_pin    A1 // CS   - A1
#define ledPin    13 // LED  - D13#define PPM_pin   P1_5  // PPM in
//SPI Comm.pins with nRF24L01
#define MOSI_pin  P2_0  // MOSI 
#define SCK_pin   P2_1  // SCK  
#define CE_pin    P2_2  // CE 
#define MISO_pin  P2_3 // MISO 
#define CS_pin    P2_4 // CS   
#define ledPin    P1_4 // LED  #define MOSI_on PORTD |= _BV(3)  // PD3
#define MOSI_off PORTD &= ~_BV(3)// PD3
#define SCK_on PORTD |= _BV(4)   // PD4
#define SCK_off PORTD &= ~_BV(4) // PD4
#define CE_on PORTD |= _BV(5)    // PD5
#define CE_off PORTD &= ~_BV(5)  // PD5
#define CS_on PORTC |= _BV(1)    // PC1
#define CS_off PORTC &= ~_BV(1)  // PC1
// SPI input
#define  MISO_on (PINC & _BV(0)) // PC0#define MOSI_on P2OUT |= _BV(0)// P2_0
#define MOSI_off P2OUT &= ~_BV(0)// P2_0
#define SCK_on P2OUT |= _BV(1)// P2_1
#define SCK_off P2OUT &= ~_BV(1)// P2_1
#define CE_on P2OUT |= _BV(2)// P2_2
#define CE_off P2OUT &= ~_BV(2)// P2_2
#define CS_on P2OUT |= _BV(4)// P2_4
#define CS_off P2OUT &= ~_BV(4) // P2_4
// SPI input
#define  MISO_on (P2IN & _BV(3)) // P2_3В МК от ATMEL за состояние выхода отвечают регистры PORTx в MSP430 PxOUT. За состояние входа- регистры PINx и PxIN соответственно. Кстати, функции _BV(x) в IDE Energia нет, поэтому добавим ее сами:
#define _BV(val) 1<<valrandomSeed((analogRead(A4) & 0x1F) | (analogRead(A5) << 5));randomSeed((analogRead(A0) & 0x1F) | (analogRead(A1) << 5));При подключении прерывания меняем
attachInterrupt(PPM_pin - 2, ISR_ppm, CHANGE);attachInterrupt(PPM_pin , ISR_ppm, CHANGE);Таймер
Меняем в void setup()
TCCR1A = 0;  //reset timer1
TCCR1B = 0;
TCCR1B |= (1 << CS11);  //set timer1 to increment every 1 us @ 8MHz, 0.5 us @16MHzTACTL = TASSEL_2 + ID_3 + MC_2 + TACLR; //16000000 / 8В функции void ISR_ppm() меняем
counterPPM = TCNT1;
TCNT1 = 0;counterPPM = TAR;
TAR = 0;random()
Не знаю почему, но функция random() без аргументов в Energia не вызывается, но так как нам необходимо случайное однобайтное число, то заменяем в файлах nRF24_multipro.ino и Bayang.ino
random() & 0xFF; random(0xFF);random() % 0x42;random(0x41);???
Ну и напоследок в файле MJX.ino в функции void MJX_bind() добавляем скобочки к вызову функции mjx_init2;
Компилируем проект и получаем успешный вывод.
Исходный код проекта
Измененный код проекта
Для успешного переноса программы на другую платформу необходимо понять что, как и зачем выполняется в данном коде, разобраться в особенностях подключенных библиотек, назначении регистров и в ходе работы чаще заглядывать в лог компиляции, для поиска ошибок.
Ну а если делать совмсем по хорошему, то надо переносить проект в другую среду разработки, подключать аппаратный SPI и работать только с регистрами, но это совсем другая история.
Комментарии (10)
 - g0dlike07.04.2016 15:11- Спасибо, что рассказали про такой замечательный проект. 
 На выхоных подключу Taranis к Syma X5C - winKING07.04.2016 20:04- Да не за что. Отпишитесь потом о результатах и впечатлениях. Если не получится с первого раза, обращайтесь, там есть подводные камни=)  - g0dlike16.04.2016 16:02- Результаты: все получилось. Помог автору прошивки починить протокол SymaX, если что, можно почитать на последних 3-4 страницах http://www.rcgroups.com/forums/showthread.php?t=2367400 (ник godlike) 
 Впечатления: я никогда и не думал, что китайский квадрокоптер может так плавно упраляться:)
 Дополнение: Теперь можно покупать китайские квадрокоптеры в комплектации BNF(без трансмиттера) — а это неплохая экономия)
 
 
 - Neolith07.04.2016 16:29- Не совсем понял для чего это нужно, что бы управлять коптером с не родного пульта?  - winKING07.04.2016 16:31- Именно, оригинальные пульты бывают очень неудобными. Да и после знакомства с более менее нормально аппаратурой, на игрушечные возвращаться не захочется.  - Neolith07.04.2016 16:35+1- Профессиональные пульты не тискал, может и есть в этом резон. Мне для 15-ти долларового Floureon H101 покупать про пульт религия не позволяет. Ж)  - winKING07.04.2016 20:02- У меня не про аппа, но для моих целей очень даже ничего, тем более за такую цену (сейчас она стоит от $93). Для одного мелколета, конечно покупать аппу смысла нет, но если таких штук 5 и все со своими пультами, то уже стоит задуматься. 
 
 
 
 
           
 
Houston
Добрый день, очень интересный проект.
Но мне не удалось найти список поддерживаемых коптеров по указанной ссылке. Просвятите, пожалуйста.
winKING
В файле README.md указаны отклонения стиков для инициализации протокола и соостветствующие ему модели коптеров:
Rudder right + Elevator down = HiSky RXs, HFP80, HCP80/100, FBL70/80/90/100, FF120, HMX120, WLToys v933/944/955
Rudder right + Elevator up = Syma X5C (older model), X2…
Rudder right + Aileron right = MJX X600
Rudder right + Aileron left = EAchine H8 mini 3D, JJRC H20/H22
Elevator down + Aileron left = Syma X5C-1/X11/X11C/X12
Elevator down + Aileron right = Attop YD-822/YD-829/YD-829C…
Elevator up + Aileron right = EAchine H8© mini, BayangToys X6/X7/X9, JJRC JJ850, Floureon H101…
Elevator up + Aileron left = EAchine H7
Elevator up = WLToys V202/252/272, JXD 385/388, JJRC H6C, Yizhan Tarantula X6…
Elevator down = EAchine CG023/CG031/3D X4
Aileron left = Cheerson CX-10 green pcb
Aileron right = Cheerson CX-10 blue pcb & some newer red pcb, CX-10A, CX-10C, CX11, CX12, Floureon FX10, JJRC DHD D1
Houston
Спасибо!