Rewriting the arduino code for MSP430 using the example of nRF24_multipro, a project for managing toy multicopters


    Before the start, I would like to immediately say that the code was not written by me and was taken from here . This program is written in the Arduino IDE and in conjunction with the arduino pro mini and nrf24l01 + allows you to control toy multicopters (with XN297 radio chips, nrf24l01 clones) from any control devices that have a PPM output. All information about supported multicopters can be found at the link above.
    I decided to rewrite this code to control the Eachine H8 mini copter from the Radiolink AT9 hardware. For details, I ask under the cat.

    MSP430 was chosen because it has a power supply of 3.3 volts, the supply voltage of nrf24l01 is also 3.3 volts, and I somehow like the MPS430 more. Inside the equipment there are contacts 3V3, OUT, GND, to which we will connect.

    If there is no desire to disassemble the equipment, then you can connect to the coaching connector, but the voltage on it = battery voltage, so a voltage regulator is added to the circuit.


    Let's get down to the code


    First we will open the project in IDE Energia (Arduino IDE clone for MSP430 and other chips from TI) and try to compile it, but we immediately see a compilation error. This project uses additional libraries and access to registers, and the first thing you need to start with them. So, we proceed to a detailed analysis.

    Libraries


    We will edit the nRF24_multipro.ino file and start with include. This project uses the atomic and EEPROM libraries.

    atomic


    Remove the string
    #include<util/atomic.h>
    and in the void update_ppm () function we remove
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    Instead of curly brackets we write
    __disable_interrupt();
    and
    __enable_interrupt();

    The ATOMIC_BLOCK () function turns off interrupts while the code is being executed in its “body” and, depending on the parameter, enables interrupts or restores the value of the interrupt flag to the state it was before the ATOMIC_BLOCK () function was called.

    Eeprom


    There is no EEPROM memory in MSP430, but there is a FLASH memory and there is a MspFlash library for working with it. Therefore, remove the string
    #include<EEPROM.h>
    and add the MspFlash library (Sketch-> Import Library ... -> MspFlash), add to which segment of the FLASH memory we will write data
    #define flash SEGMENT_D
    In the function void selectProtocol () we change strings
    else 
    	current_protocol = constrain(EEPROM.read(ee_PROTOCOL_ID),0,PROTO_END-1);      
    // update eeprom 
    EEPROM.update(ee_PROTOCOL_ID, current_protocol);
    on
    // update eeprom 
    Flash.write(flash+ee_PROTOCOL_ID, & current_protocol,1);
    We remove completely reading the protocol identifier (why, read below).
    In the void set_txid (bool renew) function, you need to do a little more than replace two lines. In FLASH memory, we can only reset bits. To set the value 1 in FLASH memory, you need to erase the entire segment (then the value 1 will be written into it). This function (set_txid) is called earlier than selectProtocol, so we will delete the segment here.
    It was:
    voidset_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]); 
            }            
        }
    }

    It became:
    voidset_txid(bool renew){
        uint8_t i;
        unsignedchar 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); 
            }  	
    	}
    }
    Here we read the value of the transmitter identifiers and the protocol, erase the segment, and if a command is given to update the transmitter identifier, we write the new value, otherwise we write the old one. The FLASH memory byte with the flash + ee_PROTOCOL_ID address remained clean (0xFF), so we don’t read it in the selectProtocol () function, and immediately write the protocol identifier there.

    Registers


    First, let's deal with the pins. This project uses a software implementation of SPI, which uses macros to “jerk” with its legs through the registers.
    Rewrite pin definitions
    с
    #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

    In MK from ATMEL, the PORTx registers in MSP430 PxOUT are responsible for the output status. For the state of the input-registers PINx and PxIN, respectively. By the way, there is no _BV (x) function in IDE Energia, therefore we add it ourselves:
    #define _BV(val) 1<<val
    In the void setup () function, we change the values ​​of the pins of the analog inputs to the free ones with
    randomSeed((analogRead(A4) & 0x1F) | (analogRead(A5) << 5));
    for example on
    randomSeed((analogRead(A0) & 0x1F) | (analogRead(A1) << 5));
    In MSP430, the analog inputs A0, A1, A2, ..., A7 correspond to the P1_0, P1_1, P1_2, ..., P1_7 pins.
    When connecting interrupt change
    attachInterrupt(PPM_pin - 2, ISR_ppm, CHANGE);
    on
    attachInterrupt(PPM_pin , ISR_ppm, CHANGE);
    In Arduino Uno, Nano, Mini, and others, on mega328 only 2 pins are available for connecting interrupts (2, 3) and in the attachInterrupt function, the first argument is the interrupt number, and not a pin like MSP430. Learn more about attachInterrupt () .

    Timer


    Change in void setup ()
    TCCR1A = 0;  //reset timer1
    TCCR1B = 0;
    TCCR1B |= (1 << CS11);  //set timer1 to increment every 1 us @ 8MHz, 0.5 us @16MHz
    On
    TACTL = TASSEL_2 + ID_3 + MC_2 + TACLR; //16000000 / 8
    The timer is needed to determine the pulse duration in the PPM. We set it to direct counting with a frequency of 2 MHz (16 MHz clock frequency and a divisor by 8).
    In the function void ISR_ppm () we change
    counterPPM = TCNT1;
    TCNT1 = 0;
    on
    counterPPM = TAR;
    TAR = 0;
    Registers TCNT1 and TAR timer counters.

    random ()


    I don’t know why, but the random () function is not called in Energia, but since we need a random single-byte number, we replace nRF24_multipro.ino and Bayang.ino in the files
    random() & 0xFF; 
    on
    random(0xFF);
    and
    random() % 0x42;
    on
    random(0x41);

    ???


    And finally, in the MJX.ino file in the void MJX_bind () function, we add parentheses to the function call mjx_init2;
    We compile the project and get a successful conclusion.

    Project Source Code
    Modified Project Code

    In order to successfully transfer the program to another platform, you need to understand what, how and why it is performed in this code, understand the features of the connected libraries, assignment of registers and look more often into the compilation log for errors in the course of work.
    Well, if you do it together for good, then you need to transfer the project to another development environment, connect hardware SPI and work only with registers, but this is a completely different story.

    Also popular now: