- PVSM.RU - https://www.pvsm.ru -

Переписываем код arduino для MSP430 на примере nRF24_multipro, проекта для управления игрушечными мультикоптерами

Переписываем код arduino для MSP430 на примере nRF24_multipro, проекта для управления игрушечными мультикоптерами - 1

Перед началом, хотелось бы сразу оговориться, что код написан не мной и взят отсюда [1]. Данная программа написана в среде Arduino IDE и в связке с arduino pro mini и nrf24l01+ позволяет управлять игрушечными мультикоптерами (с радиочипами XN297, клонами nrf24l01) с любых аппаратур управления, на которых есть PPM выход. Всю информацию о поддерживаемых мультикоптерах можно найти по ссылке выше.
Я решил переписать данный код для управления коптером Eachine H8 mini с аппаратуры Radiolink AT9. За подробностями прошу под кат.

MSP430 был выбран потому, что у него напряжение питания 3.3 вольта, напряжение питания nrf24l01 также 3.3 вольта, да и MPS430 мне как-то больше нравятся. Внутри аппаратуры располагаются контакты 3V3, OUT, GND, к которым мы и будем подключаться.

Переписываем код arduino для MSP430 на примере nRF24_multipro, проекта для управления игрушечными мультикоптерами - 2

Если нет желания разбирать аппаратуру, то можно подключится к тренерскому разъему, но напряжение на нем = напряжению аккумулятора, поэтому в схему добавляется стабилизатор напряжения.

Переписываем код arduino для MSP430 на примере nRF24_multipro, проекта для управления игрушечными мультикоптерами - 3

Приступим к коду

Первым делом откроем проект в IDE Energia (клон Arduino IDE для MSP430 и других чипов от TI) и попытаемся его скомпилировать, но сразу же увидим ошибку компиляции. Данный проект использует дополнительные библиотеки и обращение к регистрам, и первым делом нужно начинать с них. Итак, приступим к детальному анализу.

Библиотеки

Править будем файл nRF24_multipro.ino и начнем с include’ов. В данном проекте используются библиотеки atomic и EEPROM.

atomic

Убираем строку

#include <util/atomic.h>

и в функции void update_ppm() убираем

ATOMIC_BLOCK(ATOMIC_RESTORESTATE)

Вместо фигурных скобок пишем

__disable_interrupt();

и

__enable_interrupt();

Функция ATOMIC_BLOCK() выключает прерывания, пока исполняется код в его «теле» и в зависимости от параметра, включает прерывания или восстанавливает значение флага прерывания в состояние, которое было до вызова функции ATOMIC_BLOCK().

EEPROM

В MSP430 нет EEPROM памяти, но есть FLASH память и для работы с ней есть библиотека MspFlash. Поэтому убираем строку

#include <EEPROM.h>

и добавляем библиотеку MspFlash (Sketch->Import Library…->MspFlash), добавляем в какой сегмент FLASH памяти мы будем записывать данные

#define flash SEGMENT_D

В функции void selectProtocol() меняем строки

else 
	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); 
        }  	
	}
}

Тут мы считываем значение идентификаторов передатчика и протокола, стираем сегмент, и если дана команда на обновление идентификатора передатчика, записываем новое значение, иначе записываем старое. Байт FLASH памяти с адресом flash+ee_PROTOCOL_ID остался чистым (0xFF), поэтому в функции selectProtocol() мы его и не читаем, а сразу записываем туда идентификатор протокола.

Регистры

Для начала разберемся с пинами. В данном проекте используется программная реализация 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<<val

В функции void setup() меняем значения пинов аналоговых входов на свободные с

randomSeed((analogRead(A4) & 0x1F) | (analogRead(A5) << 5));

например на

randomSeed((analogRead(A0) & 0x1F) | (analogRead(A1) << 5));

В MSP430 аналоговые входы A0, A1, A2, …, A7 соответствуют пинам P1_0, P1_1,P1_2, ..., P1_7.
При подключении прерывания меняем

attachInterrupt(PPM_pin - 2, ISR_ppm, CHANGE);

на

attachInterrupt(PPM_pin , ISR_ppm, CHANGE);

В Arduino Uno, Nano, Mini, и др. на mega328 для подключения прерывания доступны только 2 пина (2, 3) и в функции attachInterrupt первым аргументом является номер прерывания, а не пин как у MSP430. Более подробно про attachInterrupt() [2].

Таймер

Меняем в void setup()

TCCR1A = 0;  //reset timer1
TCCR1B = 0;
TCCR1B |= (1 << CS11);  //set timer1 to increment every 1 us @ 8MHz, 0.5 us @16MHz

На

TACTL = TASSEL_2 + ID_3 + MC_2 + TACLR; //16000000 / 8

Таймер нужен для определения длительности импульсов в PPM. Устанавливаем его в режим прямого счета с частотой 2МГц (тактовая частота 16МГц и делитель на 8).
В функции void ISR_ppm() меняем

counterPPM = TCNT1;
TCNT1 = 0;

на

counterPPM = TAR;
TAR = 0;

Регистры TCNT1 и TAR счетчики таймера.

random()

Не знаю почему, но функция random() без аргументов в Energia не вызывается, но так как нам необходимо случайное однобайтное число, то заменяем в файлах nRF24_multipro.ino и Bayang.ino

random() & 0xFF; 

на

random(0xFF);

и

random() % 0x42;

на

random(0x41);

???

Ну и напоследок в файле MJX.ino в функции void MJX_bind() добавляем скобочки к вызову функции mjx_init2;
Компилируем проект и получаем успешный вывод.

Исходный код проекта [1]
Измененный код проекта [3]

Для успешного переноса программы на другую платформу необходимо понять что, как и зачем выполняется в данном коде, разобраться в особенностях подключенных библиотек, назначении регистров и в ходе работы чаще заглядывать в лог компиляции, для поиска ошибок.
Ну а если делать совмсем по хорошему, то надо переносить проект в другую среду разработки, подключать аппаратный SPI и работать только с регистрами, но это совсем другая история.

Автор: winKING

Источник [4]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/msp430/117271

Ссылки в тексте:

[1] отсюда: https://github.com/goebish/nrf24_multipro

[2] attachInterrupt(): https://www.arduino.cc/en/Reference/AttachInterrupt

[3] Измененный код проекта: https://github.com/winkingsl/nRF24_multipro_msp430

[4] Источник: https://geektimes.ru/post/273948/