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

от автора

Перед началом, хотелось бы сразу оговориться, что код написан не мной и взят отсюда. Данная программа написана в среде 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>

и в функции 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().

Таймер

Меняем в 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;
Компилируем проект и получаем успешный вывод.

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

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

ссылка на оригинал статьи https://geektimes.ru/post/273948/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *