Vuelva a escribir el código arduino para el MSP430 usando el ejemplo de nRF24_multipro, un proyecto para administrar multicopter de juguete


Antes de comenzar, me gustaría hacer una reserva inmediata de que el código no fue escrito por mí y tomado de aquí . Este programa está escrito en el Arduino IDE y, junto con arduino pro mini y nrf24l01 +, le permite controlar multicopters de juguete (con chips de radio XN297, clones nrf24l01) desde cualquier equipo de control que tenga una salida PPM. Toda la información sobre multicopter compatible se puede encontrar en el enlace de arriba.
Decidí reescribir este código para controlar el mini helicóptero Eachine H8 desde el equipo Radiolink AT9. Pido detalles bajo cat.

El MSP430 fue elegido porque tiene un voltaje de suministro de 3.3 voltios, el voltaje de suministro nrf24l01 también es de 3.3 voltios, y de alguna manera me gusta el MPS430. Dentro del equipo hay contactos 3V3, OUT, GND, a los que nos conectaremos.

Si no desea desarmar el equipo, puede conectarse al conector de coaching, pero el voltaje en él = voltaje de la batería, por lo que se agrega un estabilizador de voltaje al circuito.


Vayamos al código


En primer lugar, abra el proyecto en IDE Energia (clon Arduino IDE para MSP430 y otros chips de TI) e intente compilarlo, pero inmediatamente ve un error de compilación. Este proyecto utiliza bibliotecas adicionales y acceso a registros, y lo primero que necesita para comenzar con ellos. Entonces, procedemos a un análisis detallado.

Bibliotecas


Editaremos el archivo nRF24_multipro.ino y comenzaremos con include'ov. Este proyecto utiliza las bibliotecas atómicas y EEPROM.

atómico


Quitamos la linea
#include <util/atomic.h>
y en la función void update_ppm () eliminamos
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
En lugar de llaves escribimos
__disable_interrupt();
y
__enable_interrupt();

La función ATOMIC_BLOCK () desactiva las interrupciones mientras se ejecuta el código en su "cuerpo" y, según el parámetro, activa las interrupciones o restaura el valor del indicador de interrupción al estado anterior a la llamada a la función ATOMIC_BLOCK ().

Eeprom


El MSP430 no tiene memoria EEPROM, pero hay memoria FLASH y hay una biblioteca MspFlash para trabajar con ella. Por lo tanto, eliminamos la línea.
#include <EEPROM.h>
y agregue la biblioteca MspFlash (Sketch-> Import Library ... -> MspFlash), agregue a qué segmento de la memoria FLASH escribiremos datos
#define flash SEGMENT_D
En la función void selectProtocol (), cambie las líneas
else 
	current_protocol = constrain(EEPROM.read(ee_PROTOCOL_ID),0,PROTO_END-1);      
// update eeprom 
EEPROM.update(ee_PROTOCOL_ID, current_protocol);
en
// update eeprom 
Flash.write(flash+ee_PROTOCOL_ID, & current_protocol,1);
Eliminamos por completo la lectura del identificador de protocolo (por qué, lea a continuación).
En void set_txid (boolnew), debe hacer un poco más que reemplazar dos líneas. En la memoria FLASH solo podemos cero bits. Para establecer el valor de 1 en los bits de memoria FLASH, debe borrar todo el segmento (luego se escribirán los valores 1). Esta función (set_txid) se llama antes que selectProtocol, por lo que borraremos el segmento aquí.
Fue:
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]); 
        }            
    }
}

Se convirtió en:
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); 
        }  	
	}
}
Aquí leemos el valor de los identificadores del transmisor y el protocolo, borramos el segmento, y si se da un comando para actualizar el identificador del transmisor, escribimos un nuevo valor, de lo contrario, registramos el antiguo. El byte FLASH de la memoria con la dirección flash + ee_PROTOCOL_ID permaneció limpio (0xFF), por lo que no lo leemos en la función selectProtocol (), pero inmediatamente escribimos el identificador de protocolo allí.

Registros


Primero, tratemos con los alfileres. En este proyecto, se utiliza una implementación de software de SPI, en la que las macros se utilizan para "mover" las patas a través de los registros.
Reescribe las definiciones de pines
#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

En ATMEL MK, los registros PORTx en el MSP430 PxOUT son responsables del estado de salida. Para el estado de entrada, los registros PINx y PxIN, respectivamente. Por cierto, no hay una función _BV (x) en el IDE de Energia, así que agréguela usted mismo:
#define _BV(val) 1<<val
En la función void setup () cambiamos los valores de pin de las entradas analógicas para liberarlas con
randomSeed((analogRead(A4) & 0x1F) | (analogRead(A5) << 5));
por ejemplo en
randomSeed((analogRead(A0) & 0x1F) | (analogRead(A1) << 5));
En el MSP430, las entradas analógicas A0, A1, A2, ..., A7 corresponden a los pines P1_0, P1_1, P1_2, ..., P1_7.
Al conectar el cambio de interrupción
attachInterrupt(PPM_pin - 2, ISR_ppm, CHANGE);
en
attachInterrupt(PPM_pin , ISR_ppm, CHANGE);
En Arduino Uno, Nano, Mini, etc. en mega328, solo 2 pines (2, 3) están disponibles para conectar una interrupción, y en la función attachInterrupt, el primer argumento es el número de interrupción, no el pin como en el MSP430. Más sobre attachInterrupt () .

Temporizador


Cambio en la configuración nula ()
TCCR1A = 0;  //reset timer1
TCCR1B = 0;
TCCR1B |= (1 << CS11);  //set timer1 to increment every 1 us @ 8MHz, 0.5 us @16MHz
En
TACTL = TASSEL_2 + ID_3 + MC_2 + TACLR; //16000000 / 8
Se necesita un temporizador para determinar la duración del pulso en PPM. Lo configuramos en modo de conteo directo con una frecuencia de 2 MHz (frecuencia de reloj de 16 MHz y un divisor de 8).
En la función nula ISR_ppm () cambiamos
counterPPM = TCNT1;
TCNT1 = 0;
en
counterPPM = TAR;
TAR = 0;
Los registros TCNT1 y TAR son contadores de temporizador.

aleatorio ()


No sé por qué, pero la función random () no se llama sin argumentos en Energia, pero como necesitamos un número aleatorio de un solo byte, reemplazamos nRF24_multipro.ino y Bayang.ino en los archivos
random() & 0xFF; 
en
random(0xFF);
y
random() % 0x42;
en
random(0x41);

???


Y finalmente, en el archivo MJX.ino, en la función void MJX_bind (), agregamos corchetes a la llamada a la función mjx_init2;
Compilamos el proyecto y obtenemos una conclusión exitosa.

Código fuente del proyecto
Código de proyecto modificado

Para transferir con éxito un programa a otra plataforma, debe comprender qué, cómo y por qué se ejecuta en este código, comprender las características de las bibliotecas conectadas, el propósito de los registros y, en el curso del trabajo, a menudo buscar en el registro de compilación para encontrar errores.
Bueno, si hace algo bueno, necesita transferir el proyecto a otro entorno de desarrollo, conectar el SPI de hardware y trabajar solo con registros, pero esta es una historia completamente diferente.

Source: https://habr.com/ru/post/es392609/


All Articles