我们使用nRF24_multipro(用于管理玩具多旋翼飞机的项目)的示例重写MSP430的arduino代码。


在开始之前,我想立即保留一下代码不是我写的,而是从这里获取的该程序是用Arduino IDE编写的,与arduino pro mini和nrf24l01 +结合使用,使您可以从任何具有PPM输出的控制设备控制玩具多旋翼飞机(带有XN297无线电芯片,nrf24l01克隆)。有关受支持的多旋翼机的所有信息,可以在上面的链接中找到。
我决定重写此代码,以从Radiolink AT9设备控制Everyine H8小型直升机。我要求猫下提供详细信息。

之所以选择MSP430,是因为它的电源电压为3.3伏,电源电压nrf24l01也为3.3伏,我某种程度上喜欢MPS430。设备内部有3V3,OUT,GND触点,我们将连接这些触点。

如果不想拆卸设备,则可以连接至教练连接器,但是其上的电压=电池电压,因此在电路中添加了稳压器。


让我们开始讨论代码


首先,在IDE Energia(用于MSP430和TI的其他芯片的Arduino IDE克隆)中打开项目,并尝试对其进行编译,但立即会看到编译错误。该项目使用其他库和对寄存器的访问,并且首先需要从它们开始。因此,我们进行详细分析。

图书馆


我们将编辑nRF24_multipro.ino文件,并从include'ov开始。该项目使用原子库和EEPROM库。

原子的


我们删除线
#include <util/atomic.h>
并在函数void update_ppm()中删除
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
我们写括号而不是大括号
__disable_interrupt();
__enable_interrupt();

ATOMIC_BLOCK()函数在执行其“主体”中的代码时关闭中断,并根据参数打开中断或将中断标志的值恢复到调用ATOMIC_BLOCK()函数之前的状态。

埃普罗姆


MSP430没有EEPROM存储器,但是有FLASH存储器,并且有一个MspFlash库可供使用。因此,我们删除了该行
#include <EEPROM.h>
并添加MspFlash库(Sketch->导入库...-> 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(布尔更新)中,您需要做的不仅仅是替换两行。在闪存中,我们只能将零位。要在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 + ee_PROTOCOL_ID的存储器的FLASH字节保持干净(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 MK中,MSP430 PxOUT中的PORTx寄存器负责输出状态。对于输入状态,分别为PINx和PxIN寄存器。顺便说一下,Energia IDE中没有_BV(x)函数,因此请自己添加:
#define _BV(val) 1<<val
在功能void setup()中,我们将模拟输入的引脚值更改为free
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);
在mega328的Arduino Uno,Nano,Mini等中,只有2个引脚(2、3)可用于连接中断,在attachInterrupt函数中,第一个参数是中断号,而不是MSP430中的引脚。有关attachInterrupt()的更多信息

计时器


更改空隙设置()
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 MHz(时钟频率为16 MHz,分频器为8)的直接计数模式。
在void函数ISR_ppm()中,我们进行更改
counterPPM = TCNT1;
TCNT1 = 0;

counterPPM = TAR;
TAR = 0;
寄存器TCNT1和TAR是定时器计数器。

随机()


我不知道为什么,但是Energia中不是不带参数地调用random()函数,但是由于我们需要一个随机的单字节数字,因此我们在文件中替换了nRF24_multipro.ino和Bayang.ino
random() & 0xFF; 
random(0xFF);
random() % 0x42;
random(0x41);

???


最后,在MJX.ino文件的void MJX_bind()函数中,我们在mjx_init2函数调用中添加了括号;
我们编译该项目并获得成功的结论。

项目源代码
更改后的项目代码

为了成功地将程序转移到另一个平台,您需要了解在此代码中执行什么,如何以及为什么执行它,了解所连接的库的功能,寄存器的用途,并且在工作过程中经常查看编译日志以查找错误。
好吧,如果您做得很好,则需要将项目转移到另一个开发环境,连接硬件SPI并仅与寄存器一起使用,但这是完全不同的事情。

Source: https://habr.com/ru/post/zh-CN392609/


All Articles