Commit 72050604 authored by Martin Budden's avatar Martin Budden Committed by GitHub
Browse files

Merge pull request #283 from martinbudden/inav_nrf24

Support for Crazepony MINI flight controller and NRF24L01 transceiver
parents c2dcfea3 7af74cbf
......@@ -376,8 +376,11 @@ COMMON_SRC = \
drivers/buf_writer.c \
drivers/bus_i2c_soft.c \
drivers/bus_spi.c \
drivers/bus_spi_soft.c \
drivers/gps_i2cnav.c \
drivers/gyro_sync.c \
drivers/rx_nrf24l01.c \
drivers/rx_xn297.c \
drivers/pwm_mapping.c \
drivers/pwm_output.c \
drivers/pwm_rx.c \
......@@ -403,6 +406,12 @@ COMMON_SRC = \
io/statusindicator.c \
rx/ibus.c \
rx/msp.c \
rx/nrf24.c \
rx/nrf24_cx10.c \
rx/nrf24_syma.c \
rx/nrf24_v202.c \
rx/nrf24_h8_3d.c \
rx/nrf24_ref.c \
rx/pwm.c \
rx/rx.c \
rx/sbus.c \
......
......@@ -439,3 +439,17 @@ void sensorCalibrationSolveForScale(sensorCalibrationState_t * state, float resu
result[i] = sqrtf(beta[i]);
}
}
uint16_t crc16_ccitt(uint16_t crc, unsigned char a)
{
crc ^= a << 8;
for (int ii = 0; ii < 8; ++ii) {
if (crc & 0x8000) {
crc = (crc << 1) ^ 0x1021;
} else {
crc = crc << 1;
}
}
return crc;
}
......@@ -168,3 +168,4 @@ float acos_approx(float x);
#endif
void arraySubInt32(int32_t *dest, int32_t *array1, int32_t *array2, int count);
uint16_t crc16_ccitt(uint16_t crc, unsigned char a);
......@@ -35,6 +35,7 @@
#include "drivers/gpio.h"
#include "drivers/timer.h"
#include "drivers/pwm_rx.h"
#include "drivers/rx_nrf24l01.h"
#include "drivers/serial.h"
#include "sensors/sensors.h"
......@@ -55,6 +56,7 @@
#include "io/gps.h"
#include "rx/rx.h"
#include "rx/nrf24.h"
#include "blackbox/blackbox_io.h"
......@@ -81,6 +83,13 @@
void useRcControlsConfig(modeActivationCondition_t *modeActivationConditions, escAndServoConfig_t *escAndServoConfigToUse, pidProfile_t *pidProfileToUse);
#ifndef DEFAULT_RX_FEATURE
#define DEFAULT_RX_FEATURE FEATURE_RX_PARALLEL_PWM
#endif
#ifndef NRF24_DEFAULT_PROTOCOL
#define NRF24_DEFAULT_PROTOCOL 0
#endif
#if !defined(FLASH_SIZE)
#error "Flash size not defined for target. (specify in KB)"
#endif
......@@ -465,6 +474,7 @@ static void resetConf(void)
resetTelemetryConfig(&masterConfig.telemetryConfig);
masterConfig.rxConfig.serialrx_provider = 0;
masterConfig.rxConfig.nrf24rx_protocol = NRF24_DEFAULT_PROTOCOL;
masterConfig.rxConfig.spektrum_sat_bind = 0;
masterConfig.rxConfig.midrc = 1500;
masterConfig.rxConfig.mincheck = 1100;
......@@ -792,33 +802,48 @@ void activateConfig(void)
#endif
}
void validateAndFixConfig(void)
static void validateAndFixConfig(void)
{
if (!(featureConfigured(FEATURE_RX_PARALLEL_PWM) || featureConfigured(FEATURE_RX_PPM) || featureConfigured(FEATURE_RX_SERIAL) || featureConfigured(FEATURE_RX_MSP))) {
featureSet(FEATURE_RX_PARALLEL_PWM); // Consider changing the default to PPM
}
if (!(featureConfigured(FEATURE_RX_PARALLEL_PWM) || featureConfigured(FEATURE_RX_PPM) || featureConfigured(FEATURE_RX_SERIAL) || featureConfigured(FEATURE_RX_MSP) || featureConfigured(FEATURE_RX_NRF24))) {
featureSet(DEFAULT_RX_FEATURE);
}
if (featureConfigured(FEATURE_RX_PPM)) {
featureClear(FEATURE_RX_PARALLEL_PWM);
}
if (featureConfigured(FEATURE_RX_PPM)) {
featureClear(FEATURE_RX_SERIAL | FEATURE_RX_PARALLEL_PWM | FEATURE_RX_MSP | FEATURE_RX_NRF24);
}
if (featureConfigured(FEATURE_RX_MSP)) {
featureClear(FEATURE_RX_SERIAL);
featureClear(FEATURE_RX_PARALLEL_PWM);
featureClear(FEATURE_RX_PPM);
}
if (featureConfigured(FEATURE_RX_MSP)) {
featureClear(FEATURE_RX_SERIAL | FEATURE_RX_PARALLEL_PWM | FEATURE_RX_PPM | FEATURE_RX_NRF24);
}
if (featureConfigured(FEATURE_RX_SERIAL)) {
featureClear(FEATURE_RX_PARALLEL_PWM);
featureClear(FEATURE_RX_PPM);
}
if (featureConfigured(FEATURE_RX_SERIAL)) {
featureClear(FEATURE_RX_PARALLEL_PWM | FEATURE_RX_MSP | FEATURE_RX_PPM | FEATURE_RX_NRF24);
}
if (featureConfigured(FEATURE_RX_NRF24)) {
featureClear(FEATURE_RX_SERIAL | FEATURE_RX_PARALLEL_PWM | FEATURE_RX_PPM | FEATURE_RX_MSP);
}
#if defined(NAV)
// Ensure sane values of navConfig settings
validateNavConfig(&masterConfig.navConfig);
#endif
if (featureConfigured(FEATURE_SOFTSPI)) {
featureClear(FEATURE_RX_PPM | FEATURE_RX_PARALLEL_PWM | FEATURE_SOFTSERIAL | FEATURE_VBAT);
#if defined(STM32F10X)
featureClear(FEATURE_LED_STRIP);
// rssi adc needs the same ports
featureClear(FEATURE_RSSI_ADC);
// current meter needs the same ports
if (masterConfig.batteryConfig.currentMeterType == CURRENT_SENSOR_ADC) {
featureClear(FEATURE_CURRENT_METER);
}
#endif
}
if (featureConfigured(FEATURE_RX_PARALLEL_PWM)) {
featureClear(FEATURE_RX_SERIAL | FEATURE_RX_MSP | FEATURE_RX_PPM | FEATURE_RX_NRF24);
#if defined(STM32F10X)
// rssi adc needs the same ports
featureClear(FEATURE_RSSI_ADC);
......
......@@ -43,7 +43,9 @@ typedef enum {
FEATURE_DISPLAY = 1 << 17,
FEATURE_ONESHOT125 = 1 << 18,
FEATURE_BLACKBOX = 1 << 19,
FEATURE_CHANNEL_FORWARDING = 1 << 20
FEATURE_CHANNEL_FORWARDING = 1 << 20,
FEATURE_RX_NRF24 = 1 << 21,
FEATURE_SOFTSPI = 1 << 22,
} features_e;
void handleOneshotFeatureChangeOnRestart(void);
......
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include <platform.h>
#include "build_config.h"
#ifdef USE_SOFTSPI
#include "gpio.h"
#include "bus_spi_soft.h"
void softSpiInit(const softSPIDevice_t *dev)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// SCK as output
GPIO_InitStructure.GPIO_Pin = dev->sck_pin;
GPIO_Init(dev->sck_gpio, &GPIO_InitStructure);
// MOSI as output
GPIO_InitStructure.GPIO_Pin = dev->mosi_pin;
GPIO_Init(dev->mosi_gpio, &GPIO_InitStructure);
// MISO as input
GPIO_InitStructure.GPIO_Pin = dev->miso_pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(dev->miso_gpio, &GPIO_InitStructure);
#ifdef SOFTSPI_NSS_PIN
// NSS as output
GPIO_InitStructure.GPIO_Pin = dev->nss_pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(dev->nss_gpio, &GPIO_InitStructure);
#endif
}
uint8_t softSpiTransferByte(const softSPIDevice_t *dev, uint8_t byte)
{
for(int ii = 0; ii < 8; ++ii) {
if (byte & 0x80) {
GPIO_SetBits(dev->mosi_gpio, dev->mosi_pin);
} else {
GPIO_ResetBits(dev->mosi_gpio, dev->mosi_pin);
}
GPIO_SetBits(dev->sck_gpio, dev->sck_pin);
byte <<= 1;
if (GPIO_ReadInputDataBit(dev->miso_gpio, dev->miso_pin) == 1) {
byte |= 1;
}
GPIO_ResetBits(dev->sck_gpio, dev->sck_pin);
}
return byte;
}
#endif
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
typedef struct softSPIDevice_s {
GPIO_TypeDef* sck_gpio;
uint16_t sck_pin;
GPIO_TypeDef* mosi_gpio;
uint16_t mosi_pin;
GPIO_TypeDef* miso_gpio;
uint16_t miso_pin;
#ifdef SOFTSPI_NSS_PIN
GPIO_TypeDef* nss_gpio;
uint16_t nss_pin;
#endif
} softSPIDevice_t;
void softSpiInit(const softSPIDevice_t *dev);
uint8_t softSpiTransferByte(const softSPIDevice_t *dev, uint8_t data);
......@@ -84,21 +84,20 @@ pwmIOConfiguration_t *pwmGetOutputConfiguration(void){
pwmIOConfiguration_t *pwmInit(drv_pwm_config_t *init)
{
int i = 0;
const uint16_t *setup;
#ifndef SKIP_RX_PWM_PPM
int channelIndex = 0;
#endif
memset(&pwmIOConfiguration, 0, sizeof(pwmIOConfiguration));
// this is pretty hacky shit, but it will do for now. array of 4 config maps, [ multiPWM multiPPM airPWM airPPM ]
int i = 0;
if (init->airplane)
i = 2; // switch to air hardware config
if (init->usePPM || init->useSerialRx)
i++; // next index is for PPM
setup = hardwareMaps[i];
const uint16_t *setup = hardwareMaps[i];
for (i = 0; i < USABLE_TIMER_CHANNEL_COUNT && setup[i] != 0xFFFF; i++) {
uint8_t timerIndex = setup[i] & 0x00FF;
......@@ -265,6 +264,7 @@ pwmIOConfiguration_t *pwmInit(drv_pwm_config_t *init)
#endif
if (type == MAP_TO_PPM_INPUT) {
#ifndef SKIP_RX_PWM_PPM
#ifdef CC3D_PPM1
if (init->useOneshot || isMotorBrushed(init->motorPwmRate)) {
ppmAvoidPWMTimerClash(timerHardwarePtr, TIM4);
......@@ -278,11 +278,14 @@ pwmIOConfiguration_t *pwmInit(drv_pwm_config_t *init)
ppmInConfig(timerHardwarePtr);
pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_PPM;
pwmIOConfiguration.ppmInputCount++;
#endif
} else if (type == MAP_TO_PWM_INPUT) {
#ifndef SKIP_RX_PWM_PPM
pwmInConfig(timerHardwarePtr, channelIndex);
pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_PWM;
pwmIOConfiguration.pwmInputCount++;
channelIndex++;
#endif
} else if (type == MAP_TO_MOTOR_OUTPUT) {
#if defined(CC3D) && !defined(CC3D_PPM1)
......
......@@ -22,6 +22,9 @@
#include "platform.h"
#include "build_config.h"
#ifndef SKIP_RX_PWM_PPM
#include "debug.h"
#include "common/utils.h"
......@@ -424,3 +427,4 @@ uint16_t pwmRead(uint8_t channel)
{
return captures[channel];
}
#endif
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
// This file is copied with modifications from project Deviation,
// see http://deviationtx.com
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <platform.h>
#ifdef USE_RX_NRF24
#include "system.h"
#include "gpio.h"
#include "rx_nrf24l01.h"
#ifdef UNIT_TEST
#define NRF24_CE_HI() {}
#define NRF24_CE_LO() {}
void NRF24L01_SpiInit(void) {}
#else
#include "bus_spi.h"
#include "bus_spi_soft.h"
#define DISABLE_NRF24() {GPIO_SetBits(NRF24_CSN_GPIO, NRF24_CSN_PIN);}
#define ENABLE_NRF24() {GPIO_ResetBits(NRF24_CSN_GPIO, NRF24_CSN_PIN);}
#define NRF24_CE_HI() {GPIO_SetBits(NRF24_CE_GPIO, NRF24_CE_PIN);}
#define NRF24_CE_LO() {GPIO_ResetBits(NRF24_CE_GPIO, NRF24_CE_PIN);}
#ifdef USE_NRF24_SOFTSPI
static const softSPIDevice_t softSPIDevice = {
.sck_gpio = NRF24_SCK_GPIO,
.mosi_gpio = NRF24_MOSI_GPIO,
.miso_gpio = NRF24_MISO_GPIO,
.sck_pin = NRF24_SCK_PIN,
.mosi_pin = NRF24_MOSI_PIN,
.miso_pin = NRF24_MISO_PIN,
#ifdef SOFTSPI_NSS_PIN
.nss_pin = NRF24_CSN_PIN,
.nss_gpio = NRF24_CSN_GPIO
#endif
};
#endif
#ifdef USE_NRF24_SOFTSPI
static bool useSoftSPI = false;
#endif
void NRF24L01_SpiInit(nfr24l01_spi_type_e spiType)
{
static bool hardwareInitialised = false;
if (hardwareInitialised) {
return;
}
if (spiType == NFR24L01_SOFTSPI) {
#ifdef USE_NRF24_SOFTSPI
useSoftSPI = true;
softSpiInit(&softSPIDevice);
#endif
}
// Note: Nordic Semiconductor uses 'CSN', STM uses 'NSS'
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
#if defined(STM32F10X)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
#endif
#ifdef STM32F303xC
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
#endif
#ifndef SOFTSPI_NSS_PIN
// CSN as output
RCC_AHBPeriphClockCmd(NRF24_CSN_GPIO_CLK_PERIPHERAL, ENABLE);
GPIO_InitStructure.GPIO_Pin = NRF24_CSN_PIN;
GPIO_Init(NRF24_CSN_GPIO, &GPIO_InitStructure);
#endif
// CE as OUTPUT
RCC_AHBPeriphClockCmd(NRF24_CE_GPIO_CLK_PERIPHERAL, ENABLE);
GPIO_InitStructure.GPIO_Pin = NRF24_CE_PIN;
GPIO_Init(NRF24_CE_GPIO, &GPIO_InitStructure);
DISABLE_NRF24();
NRF24_CE_LO();
#ifdef NRF24_SPI_INSTANCE
spiSetDivisor(NRF24_SPI_INSTANCE, SPI_9MHZ_CLOCK_DIVIDER);
#endif
hardwareInitialised = true;
}
uint8_t nrf24TransferByte(uint8_t data)
{
#ifdef USE_NRF24_SOFTSPI
if (useSoftSPI) {
return softSpiTransferByte(&softSPIDevice, data);
} else
#endif
{
#ifdef NRF24_SPI_INSTANCE
return spiTransferByte(NRF24_SPI_INSTANCE, data);
#else
return 0;
#endif
}
}
// Instruction Mnemonics
// nRF24L01: Table 16. Command set for the nRF24L01 SPI. Product Specification, p46
// nRF24L01+: Table 20. Command set for the nRF24L01+ SPI. Product Specification, p51
#define R_REGISTER 0x00
#define W_REGISTER 0x20
#define REGISTER_MASK 0x1F
#define ACTIVATE 0x50
#define R_RX_PL_WID 0x60
#define R_RX_PAYLOAD 0x61
#define W_TX_PAYLOAD 0xA0
#define W_ACK_PAYLOAD 0xA8
#define FLUSH_TX 0xE1
#define FLUSH_RX 0xE2
#define REUSE_TX_PL 0xE3
#define NOP 0xFF
uint8_t NRF24L01_WriteReg(uint8_t reg, uint8_t data)
{
ENABLE_NRF24();
nrf24TransferByte(W_REGISTER | (REGISTER_MASK & reg));
nrf24TransferByte(data);
DISABLE_NRF24();
return true;
}
uint8_t NRF24L01_WriteRegisterMulti(uint8_t reg, const uint8_t *data, uint8_t length)
{
ENABLE_NRF24();
const uint8_t ret = nrf24TransferByte(W_REGISTER | ( REGISTER_MASK & reg));
for (uint8_t i = 0; i < length; i++) {
nrf24TransferByte(data[i]);
}
DISABLE_NRF24();
return ret;
}
/*
* Transfer the payload to the nRF24L01 TX FIFO
* Packets in the TX FIFO are transmitted when the
* nRF24L01 next enters TX mode
*/
uint8_t NRF24L01_WritePayload(const uint8_t *data, uint8_t length)
{
ENABLE_NRF24();
const uint8_t ret = nrf24TransferByte(W_TX_PAYLOAD);
for (uint8_t i = 0; i < length; i++) {
nrf24TransferByte(data[i]);
}
DISABLE_NRF24();
return ret;
}
uint8_t NRF24L01_ReadReg(uint8_t reg)
{
ENABLE_NRF24();
nrf24TransferByte(R_REGISTER | (REGISTER_MASK & reg));
const uint8_t ret = nrf24TransferByte(NOP);
DISABLE_NRF24();
return ret;
}
uint8_t NRF24L01_ReadRegisterMulti(uint8_t reg, uint8_t *data, uint8_t length)
{
ENABLE_NRF24();
const uint8_t ret = nrf24TransferByte(R_REGISTER | (REGISTER_MASK & reg));
for (uint8_t i = 0; i < length; i++) {
data[i] = nrf24TransferByte(NOP);
}
DISABLE_NRF24();
return ret;
}
/*
* Read a packet from the nRF24L01 RX FIFO.
*/
uint8_t NRF24L01_ReadPayload(uint8_t *data, uint8_t length)
{
ENABLE_NRF24();
const uint8_t ret = nrf24TransferByte(R_RX_PAYLOAD);
for (uint8_t i = 0; i < length; i++) {
data[i] = nrf24TransferByte(NOP);
}
DISABLE_NRF24();
return ret;
}
/*
* Empty the transmit FIFO buffer.
*/
void NRF24L01_FlushTx()
{
ENABLE_NRF24();
nrf24TransferByte(FLUSH_TX);
DISABLE_NRF24();
}
/*
* Empty the receive FIFO buffer.
*/
void NRF24L01_FlushRx()
{
ENABLE_NRF24();
nrf24TransferByte(FLUSH_RX);
DISABLE_NRF24();
}
#endif // UNIT_TEST
// standby configuration, used to simplify switching between RX, TX, and Standby modes
static uint8_t standbyConfig;
void NRF24L01_Initialize(uint8_t baseConfig)
{
standbyConfig = BV(NRF24L01_00_CONFIG_PWR_UP) | baseConfig;
NRF24_CE_LO();
// nRF24L01+ needs 100 milliseconds settling time from PowerOnReset to PowerDown mode
static const uint32_t settlingTimeUs = 100000;
const uint32_t currentTimeUs = micros();
if (currentTimeUs < settlingTimeUs) {
delayMicroseconds(settlingTimeUs - currentTimeUs);
}
// now in PowerDown mode
NRF24L01_WriteReg(NRF24L01_00_CONFIG, standbyConfig); // set PWR_UP to enter Standby mode
// nRF24L01+ needs 4500 microseconds from PowerDown mode to Standby mode, for crystal oscillator startup
delayMicroseconds(4500);
// now in Standby mode
}
/*
* Enter standby mode
*/
void NRF24L01_SetStandbyMode(void)
{
// set CE low and clear the PRIM_RX bit to enter standby mode
NRF24_CE_LO();
NRF24L01_WriteReg(NRF24L01_00_CONFIG, standbyConfig);
}
/*
* Enter receive mode
*/
void NRF24L01_SetRxMode(void)
{
NRF24_CE_LO(); // drop into standby mode
// set the PRIM_RX bit
NRF24L01_WriteReg(NRF24L01_00_CONFIG, standbyConfig | BV(NRF24L01_00_CONFIG_PRIM_RX));
NRF24L01_ClearAllInterrupts();
// finally set CE high to start enter RX mode
NRF24_CE_HI();
// nRF24L01+ will now transition from Standby mode to RX mode after 130 microseconds settling time
}
/*
* Enter transmit mode. Anything in the transmit FIFO will be transmitted.
*/
void NRF24L01_SetTxMode(void)
{
// Ensure in standby mode, since can only enter TX mode from standby mode
NRF24L01_SetStandbyMode();
NRF24L01_ClearAllInterrupts();