Reescrevemos o código do arduino para o MSP430 usando o exemplo do nRF24_multipro, um projeto para gerenciar multicopter de brinquedo


Antes de começar, gostaria de fazer imediatamente uma reserva de que o código não foi escrito por mim e retirado daqui . Este programa foi escrito no IDE do Arduino e em conjunto com o arduino pro mini e o nrf24l01 + permite controlar multicopters de brinquedo (com chips de rádio XN297, clones nrf24l01) de qualquer equipamento de controle com saída PPM. Todas as informações sobre multicopter suportados podem ser encontradas no link acima.
Decidi reescrever esse código para controlar o mini helicóptero Eachine H8 do equipamento Radiolink AT9. Peço detalhes sob gato.

O MSP430 foi escolhido porque possui uma tensão de alimentação de 3,3 volts, a tensão de alimentação nrf24l01 também é de 3,3 volts e, de alguma forma, gosto do MPS430. Dentro do equipamento existem contatos 3V3, OUT, GND, aos quais nos conectaremos.

Se não houver desejo de desmontar o equipamento, você poderá conectar ao conector de treinamento, mas a tensão nele = tensão da bateria, para que um estabilizador de tensão seja adicionado ao circuito.


Vamos ao código


Primeiro, abra o projeto no IDE Energia (clone do Arduino IDE para MSP430 e outros chips da TI) e tente compilá-lo, mas imediatamente observe um erro de compilação. Este projeto usa bibliotecas adicionais e acesso a registros, e a primeira coisa que você precisa para começar com eles. Então, procedemos a uma análise detalhada.

Bibliotecas


Vamos editar o arquivo nRF24_multipro.ino e começar com include'ov. Este projeto usa as bibliotecas atômica e EEPROM.

atômico


Nós removemos a linha
#include <util/atomic.h>
e na função void update_ppm () removemos
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
Em vez de aparelho, escrevemos
__disable_interrupt();
e
__enable_interrupt();

A função ATOMIC_BLOCK () desativa as interrupções enquanto o código em seu "corpo" é executado e, dependendo do parâmetro, ativa as interrupções ou restaura o valor do sinalizador de interrupção para o estado anterior à chamada da função ATOMIC_BLOCK ().

Eeprom


O MSP430 não possui memória EEPROM, mas há memória FLASH e há uma biblioteca MspFlash para trabalhar com ele. Portanto, removemos a linha
#include <EEPROM.h>
e adicione a biblioteca MspFlash (Esboço-> Importar Biblioteca ... -> MspFlash), adicione a qual segmento da memória FLASH iremos gravar dados
#define flash SEGMENT_D
Na função void selectProtocol (), altere as linhas
else 
	current_protocol = constrain(EEPROM.read(ee_PROTOCOL_ID),0,PROTO_END-1);      
// update eeprom 
EEPROM.update(ee_PROTOCOL_ID, current_protocol);
em
// update eeprom 
Flash.write(flash+ee_PROTOCOL_ID, & current_protocol,1);
Removemos completamente a leitura do identificador de protocolo (leia abaixo).
No void set_txid (renovação booleana), você precisa fazer um pouco mais do que substituir duas linhas. Na memória FLASH, podemos apenas zero bits. Para definir o valor de 1 nos bits da memória FLASH, é necessário apagar o segmento inteiro (os valores 1 serão gravados nele). Esta função (set_txid) é chamada antes do selectProtocol, portanto, vamos apagar o segmento aqui.
Foi:
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]); 
        }            
    }
}

Tornou-se:
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); 
        }  	
	}
}
Aqui lemos o valor dos identificadores do transmissor e protocolo, apagamos o segmento e, se um comando é dado para atualizar o identificador do transmissor, escrevemos um novo valor, caso contrário registramos o antigo. O byte FLASH da memória com o endereço flash + ee_PROTOCOL_ID permaneceu limpo (0xFF), portanto, não o lemos na função selectProtocol (), mas escrevemos imediatamente o identificador de protocolo.

Registros


Primeiro, vamos lidar com os pinos. Neste projeto, é usada uma implementação de software do SPI, na qual macros são usadas para "torcer" as pernas através de registros.
Reescreva as definições de pinos
#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

Nos ATMEL MKs, os registros PORTx no MSP430 PxOUT são responsáveis ​​pelo status da saída. Para o status de entrada, os registros PINx e PxIN, respectivamente. A propósito, não há nenhuma função _BV (x) no IDE Energia, portanto, adicione-o você mesmo:
#define _BV(val) 1<<val
Na função void setup (), alteramos os valores dos pinos das entradas analógicas para liberar com
randomSeed((analogRead(A4) & 0x1F) | (analogRead(A5) << 5));
por exemplo em
randomSeed((analogRead(A0) & 0x1F) | (analogRead(A1) << 5));
No MSP430, as entradas analógicas A0, A1, A2, ..., A7 correspondem aos pinos P1_0, P1_1, P1_2, ..., P1_7.
Ao conectar a mudança de interrupção
attachInterrupt(PPM_pin - 2, ISR_ppm, CHANGE);
em
attachInterrupt(PPM_pin , ISR_ppm, CHANGE);
No Arduino Uno, Nano, Mini, etc. no mega328, apenas 2 pinos (2, 3) estão disponíveis para conectar uma interrupção e, na função attachInterrupt, o primeiro argumento é o número da interrupção, não o pino, como no MSP430. Mais sobre attachInterrupt () .

Temporizador


Alteração na configuração de cancelamento ()
TCCR1A = 0;  //reset timer1
TCCR1B = 0;
TCCR1B |= (1 << CS11);  //set timer1 to increment every 1 us @ 8MHz, 0.5 us @16MHz
Ativado
TACTL = TASSEL_2 + ID_3 + MC_2 + TACLR; //16000000 / 8
É necessário um cronômetro para determinar a duração do pulso no PPM. Nós o configuramos no modo de contagem direta com uma frequência de 2 MHz (frequência de relógio de 16 MHz e um divisor de 8).
Na função void ISR_ppm (), mudamos
counterPPM = TCNT1;
TCNT1 = 0;
em
counterPPM = TAR;
TAR = 0;
Os registradores TCNT1 e TAR são contadores temporizados.

aleatório ()


Não sei por que, mas a função random () não é chamada sem argumentos na Energia, mas como precisamos de um número aleatório de byte único, substituímos nRF24_multipro.ino e Bayang.ino nos arquivos
random() & 0xFF; 
em
random(0xFF);
e
random() % 0x42;
em
random(0x41);

???


E, finalmente, no arquivo MJX.ino, na função void MJX_bind (), adicionamos colchetes à chamada de função mjx_init2;
Compilamos o projeto e obtemos uma conclusão bem-sucedida.

Código-fonte do projeto
Código do projeto alterado

Para transferir com sucesso um programa para outra plataforma, é necessário entender o que, como e por que ele é executado neste código, entender os recursos das bibliotecas conectadas, o objetivo dos registros e, no decorrer do trabalho, procurar frequentemente no log de compilação para encontrar erros.
Bem, se você faz algo de bom, precisa transferir o projeto para outro ambiente de desenvolvimento, conectar o SPI do hardware e trabalhar apenas com registros, mas essa é uma história completamente diferente.

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


All Articles