Wir schreiben den Arduino-Code für den MSP430 am Beispiel von nRF24_multipro neu, einem Projekt zur Verwaltung von Spielzeug-Multikoptern


Bevor ich anfange, möchte ich sofort reservieren, dass der Code nicht von mir geschrieben und von hier übernommen wurde . Dieses Programm ist in der Arduino IDE geschrieben und ermöglicht in Verbindung mit arduino pro mini und nrf24l01 + die Steuerung von Spielzeug-Multikoptern (mit XN297-Funkchips, nrf24l01-Klonen) von jedem Steuergerät mit PPM-Ausgang. Alle Informationen zu unterstützten Multikoptern finden Sie unter dem obigen Link.
Ich habe mich entschlossen, diesen Code neu zu schreiben, um den Eachine H8 Mini-Copter von Radiolink AT9-Geräten zu steuern. Ich frage nach Details unter Katze.

Der MSP430 wurde gewählt, weil er eine Versorgungsspannung von 3,3 Volt hat, die Versorgungsspannung nrf24l01 ebenfalls 3,3 Volt beträgt und ich den MPS430 irgendwie mag. Im Inneren des Geräts befinden sich 3V3-, OUT- und GND-Kontakte, an die wir uns anschließen werden.

Wenn Sie das Gerät nicht zerlegen möchten, können Sie es an den Coaching-Anschluss anschließen, aber die Spannung daran = Batteriespannung, sodass dem Stromkreis ein Spannungsstabilisator hinzugefügt wird.


Kommen wir zum Code


Öffnen Sie zunächst das Projekt in IDE Energia (Arduino IDE-Klon für MSP430 und andere Chips von TI) und versuchen Sie, es zu kompilieren. Es wird jedoch sofort ein Kompilierungsfehler angezeigt. Dieses Projekt verwendet zusätzliche Bibliotheken und den Zugriff auf Register. Als erstes müssen Sie damit beginnen. Also fahren wir mit einer detaillierten Analyse fort.

Bibliotheken


Wir werden die Datei nRF24_multipro.ino bearbeiten und mit include'ov beginnen. Dieses Projekt verwendet die Atom- und EEPROM-Bibliotheken.

atomar


Wir entfernen die Linie
#include <util/atomic.h>
und in der Funktion void update_ppm () entfernen wir
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
Anstelle von Klammern schreiben wir
__disable_interrupt();
und
__enable_interrupt();

Die Funktion ATOMIC_BLOCK () schaltet Interrupts aus, während der Code in ihrem „Body“ ausgeführt wird, und schaltet je nach Parameter Interrupts ein oder stellt den Wert des Interrupt-Flags in den Zustand zurück, der vor dem Aufruf der Funktion ATOMIC_BLOCK () war.

Eeprom


Der MSP430 verfügt nicht über einen EEPROM-Speicher, es gibt jedoch einen FLASH-Speicher und eine MspFlash-Bibliothek für die Arbeit damit. Deshalb entfernen wir die Linie
#include <EEPROM.h>
und fügen Sie die MspFlash-Bibliothek hinzu (Sketch-> Import Library ... -> MspFlash), und fügen Sie hinzu, in welches Segment des FLASH-Speichers wir Daten schreiben werden
#define flash SEGMENT_D
Ändern Sie in der Funktion void selectProtocol () die Zeilen
else 
	current_protocol = constrain(EEPROM.read(ee_PROTOCOL_ID),0,PROTO_END-1);      
// update eeprom 
EEPROM.update(ee_PROTOCOL_ID, current_protocol);
auf
// update eeprom 
Flash.write(flash+ee_PROTOCOL_ID, & current_protocol,1);
Wir entfernen das Lesen der Protokollkennung vollständig (warum, lesen Sie unten).
In void set_txid (bool erneuern) müssen Sie etwas mehr tun, als zwei Zeilen zu ersetzen. Im FLASH-Speicher können wir nur null Bits. Um den Wert 1 in den FLASH-Speicherbits festzulegen, müssen Sie das gesamte Segment löschen (dann wird der Wert 1 darauf geschrieben). Diese Funktion (set_txid) wird früher als selectProtocol aufgerufen, daher wird das Segment hier gelöscht.
Es war:
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]); 
        }            
    }
}

Es wurde:
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); 
        }  	
	}
}
Hier lesen wir den Wert der Kennungen des Senders und des Protokolls, löschen das Segment, und wenn ein Befehl zum Aktualisieren der Kennung des Senders gegeben wird, schreiben wir einen neuen Wert, andernfalls zeichnen wir den alten auf. Das FLASH-Byte des Speichers mit der Adresse flash + ee_PROTOCOL_ID blieb sauber (0xFF), sodass wir es nicht in der Funktion selectProtocol () lesen, sondern sofort die Protokollkennung dort schreiben.

Register


Lassen Sie uns zuerst die Stifte behandeln. In diesem Projekt wird eine Software-Implementierung von SPI verwendet, in der Makros verwendet werden, um Beine durch Register zu „zucken“.
Schreiben Sie die Definitionen der Stifte neu
#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 ATMEL MKs sind die PORTx-Register im MSP430 PxOUT für den Ausgangsstatus verantwortlich. Für den Eingangsstatus werden die Register PINx und PxIN registriert. Übrigens gibt es in der Energia IDE keine _BV (x) -Funktion. Fügen Sie sie also selbst hinzu:
#define _BV(val) 1<<val
In der Funktion void setup () ändern wir die Pin-Werte der Analogeingänge auf frei mit
randomSeed((analogRead(A4) & 0x1F) | (analogRead(A5) << 5));
zum Beispiel auf
randomSeed((analogRead(A0) & 0x1F) | (analogRead(A1) << 5));
Im MSP430 entsprechen die Analogeingänge A0, A1, A2, ..., A7 den Pins P1_0, P1_1, P1_2, ..., P1_7.
Beim Anschluss Interruptwechsel
attachInterrupt(PPM_pin - 2, ISR_ppm, CHANGE);
auf
attachInterrupt(PPM_pin , ISR_ppm, CHANGE);
In Arduino Uno, Nano, Mini usw. auf mega328 stehen nur 2 Pins (2, 3) zum Verbinden eines Interrupts zur Verfügung. In der Funktion attachInterrupt ist das erste Argument die Interrupt-Nummer, nicht der Pin wie im MSP430. Weitere Informationen zu attachInterrupt () .

Timer


Änderung im void-Setup ()
TCCR1A = 0;  //reset timer1
TCCR1B = 0;
TCCR1B |= (1 << CS11);  //set timer1 to increment every 1 us @ 8MHz, 0.5 us @16MHz
Ein
TACTL = TASSEL_2 + ID_3 + MC_2 + TACLR; //16000000 / 8
Ein Timer wird benötigt, um die Impulsdauer in PPM zu bestimmen. Wir stellen den direkten Zählmodus mit einer Frequenz von 2 MHz (Taktfrequenz von 16 MHz und einem Teiler von 8) ein.
In der void-Funktion ISR_ppm () ändern wir
counterPPM = TCNT1;
TCNT1 = 0;
auf
counterPPM = TAR;
TAR = 0;
Die Register TCNT1 und TAR sind Zeitgeberzähler.

random ()


Ich weiß nicht warum, aber die Funktion random () wird in Energia nicht ohne Argumente aufgerufen. Da wir jedoch eine zufällige Einzelbyte-Nummer benötigen, ersetzen wir nRF24_multipro.ino und Bayang.ino in den Dateien
random() & 0xFF; 
auf
random(0xFF);
und
random() % 0x42;
auf
random(0x41);

???


Und schließlich fügen wir in der Datei MJX.ino in der Funktion void MJX_bind () dem Funktionsaufruf mjx_init2 Klammern hinzu.
Wir stellen das Projekt zusammen und erhalten einen erfolgreichen Abschluss.

Projektquellcode
Geänderter Projektcode

Um ein Programm erfolgreich auf eine andere Plattform zu übertragen, müssen Sie verstehen, was, wie und warum es in diesem Code ausgeführt wird, die Funktionen der verbundenen Bibliotheken, den Zweck der Register verstehen und im Laufe der Arbeit häufig im Kompilierungsprotokoll nach Fehlern suchen.
Wenn Sie etwas Gutes tun, müssen Sie das Projekt in eine andere Entwicklungsumgebung übertragen, das Hardware-SPI verbinden und nur mit Registern arbeiten. Dies ist jedoch eine ganz andere Geschichte.

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


All Articles