/* ********************************************
   Modul : NRF24L01

   Bedienung des RF Funkmodules

   Autor : Christian Julius
   ********************************************

    SPI1:

    MOSI    = PA7
    MISO    = PA6
    SCLK    = PA5
    CE      = PD7
    CSN     = PD4
    IRQ     = ---

    APB2 Clock = 1/2 SystemClock = 168 / 2 = 84 Mhz
    Prescaler 64 => Fspi = 1.3 Mhz
*/

#pragma GCC optimize ("O0")

/* -------------- System Include Files ----------------- */
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_spi.h"
#include "stm32f4xx_gpio.h"

/* -------------- Standard Include Files ----------------- */
#include <stdint.h>
/* --------------- Special Include Files / Librarys ------ */
#include "tm_stm32f4_delay.h"

/* -------------- Projekt   Include Files ---------------- */
#include "nrf24l01.h"
#include "config.h"
/* -------------- Typen Deklarationen--------------------- */
/* -------------- Defintionen ---------------------------- */

/* Grundeinstellungen des Moduls */
#define RF_CHANNEL  50

/* Anschluesse der Pins */
#define CSN_PORT       GPIOD
#define CSN_PIN        GPIO_Pin_4
#define CE_PORT        GPIOD
#define CE_PIN         GPIO_Pin_7

/* CE und CSN Pins schalten */
#define CSN_LOW     GPIO_ResetBits(CSN_PORT,CSN_PIN)
#define CSN_HIGH    GPIO_SetBits(CSN_PORT,CSN_PIN)
#define CE_LOW      GPIO_ResetBits(CE_PORT,CE_PIN)
#define CE_HIGH     GPIO_SetBits(CE_PORT,CE_PIN)

/* Viele Definitionen des RF Modules einbinden */
#include "rf_defines.h"

/* -------------- Privat Global Variablen ----------------------- */

volatile uint8_t RF_Payload_size;
volatile uint8_t SPI_initialized = 0;

/* Festgelegte Adressen der Module */
const uint8_t MyAddr[5] = {"RECVR"};
const uint8_t TxAddr[6] = {"SENSR"};

/* -------------- Privat Funktionen Prototypen ----------- */
static void    RF_FlushRX();
static void    RF_FlushTX();
static uint8_t RF_ReadRegSingle(uint8_t);
static void    RF_WriteRegSingle(uint8_t, uint8_t);
static void    RF_WriteRegMulti(uint8_t, const uint8_t*, size_t);
static uint8_t RF_ReadRegSingle(uint8_t);
static void    RF_ReadRegMulti(uint8_t, uint8_t*,size_t);

void RF_SPI_Init() {

    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef SPI_InitStruct;

    // PE5 = Chip Select, PA7 = MOSI, PA6 = MISO (nicht verwendet!), PA5 = SCK
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    /*-------- SCK, MOSI, MISO: Pinpack 1 --------*/
    GPIO_StructInit (&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed  = GPIO_Medium_Speed;
    GPIO_InitStruct.GPIO_OType  = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd   = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* CSN konfigurieren */
    GPIO_StructInit (&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin    = CSN_PIN;
    GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Speed  = GPIO_Medium_Speed;
    GPIO_InitStruct.GPIO_OType  = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd   = GPIO_PuPd_NOPULL;
    GPIO_Init(CSN_PORT, &GPIO_InitStruct);

    /* CE im gleichen Struct konfigurieren*/
    GPIO_InitStruct.GPIO_Pin    = CE_PIN;
    GPIO_Init(CE_PORT, &GPIO_InitStruct);

    /* SPI1 die Alternate Pins zuordnen */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);

    /* Die SPI1 einstellen: Master, 2 Lines, 8 Bit, Mode 0 */
    SPI_StructInit(&SPI_InitStruct);
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode      = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize  = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL      = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA      = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_NSS       = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStruct);

    CE_LOW;            // Setze Pins passiv
    CSN_HIGH;

    SPI_Cmd(SPI1, ENABLE);

    SPI_initialized = 1;

}

/* ------ SPI Low Level: Ein 8 Bit Datenwort senden und holen ------- */
uint8_t __attribute__((optimize(0))) SPI_SendByte(uint8_t data)
{
    // Warte bis frei...
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));      // Byte senden...
    SPI_I2S_SendData(SPI1, data);                               // Warte bis Empfang fertig...
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));     // Warte bis SPI komplett fertig...
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));       // Wert abholen...
    return SPI_I2S_ReceiveData(SPI1);
}

/* ----------------------- Basic Level Routinen -------------------------------------- */

/* Schreibt ein Kommando in das Modul ein */
static void RF_FlushTX() {
    CSN_LOW;
    SPI_SendByte(NRF24L01_CMD_FLUSH_TX);
    CSN_HIGH;
}

static void RF_FlushRX() {
    CSN_LOW;
    SPI_SendByte(NRF24L01_CMD_FLUSH_RX);
    CSN_HIGH;
    // Status Register rücksetzen
    RF_WriteRegSingle(NRF24L01_REG_STATUS,0xf0);

}


/* NRF24L01 Register mit 1 Byte beschreiben
   Eingabe: Register, neuer Wert
*/
static void RF_WriteRegSingle(uint8_t nrf_reg, uint8_t value)
{
    CSN_LOW;
    SPI_SendByte(NRF24L01_CMD_WRITE(nrf_reg));
    SPI_SendByte(value);
    CSN_HIGH;
}

/* NRF24L01 Register mit 1-n Bytes beschreiben, n < 32
   Eingabe: Register, Zeiger auf Daten, Länge der Daten
 */
static  void RF_WriteRegMulti(uint8_t nrf_reg, const uint8_t* data, size_t len)
{
    CSN_LOW;
    SPI_SendByte(NRF24L01_CMD_WRITE(nrf_reg));
    for (uint8_t i = 0; i < len; i++)
        SPI_SendByte(*(data++));

    CSN_HIGH;
}

/* NRF24L01 Register: 1 Byte auslesen */
static  uint8_t RF_ReadRegSingle(uint8_t nrf_reg)
{
    CSN_LOW;
    SPI_SendByte(NRF24L01_CMD_READ(nrf_reg));
    uint8_t val = SPI_SendByte(0x00);
    CSN_HIGH;
    return val;
}

/* NRF24L01 Register: Mehrere Bytes auslesen */
static void RF_ReadRegMulti(uint8_t nrf_reg, uint8_t *data, size_t len)
{
    CSN_LOW;

    SPI_SendByte(NRF24L01_CMD_READ(nrf_reg));
    for (uint8_t i = 0; i < len; i++)
        *(data++) = SPI_SendByte(0x00);

    CSN_HIGH;

}

/* ----------------------- Top Level Routinen -------------------------------------- */

/* Ist das Funkmodul ansprechbar ?
   Dazu wird das Feature Register beschrieben
   Geprüft: OK!
*/
uint8_t RF_Available()
{
    /* Register sichern */
    uint8_t backup = RF_ReadRegSingle(NRF24L01_REG_FEATURE);
    RF_WriteRegSingle(NRF24L01_REG_FEATURE,0x07);

    /* Rest Write */
    uint8_t val = RF_ReadRegSingle(NRF24L01_REG_FEATURE);
    RF_WriteRegSingle(NRF24L01_REG_FEATURE,backup);

    return
     (val == 0x07) ? SUCCESS:ERROR;
}


/* Sind Daten da ? */
uint8_t RF_DataReady() {
    uint8_t val;

    /* Status auslesen */
    val = RF_ReadRegSingle(NRF24L01_REG_STATUS);

    // RX_DR Bit gesetzt?
    if (val & (1 << NRF24L01_RX_DR))
        return SUCCESS;

    return ERROR;
}

/* Liest die Daten aus RX FIFO aus */
void RF_GetData(uint8_t *target) {

    CSN_LOW;
    SPI_SendByte(NRF24L01_CMD_R_RX_PAYLOAD);
    for (uint8_t i = 0; i < RF_Payload_size; i++)
        *(target++) = SPI_SendByte(0x00);
    CSN_HIGH;

    /* Bits im Status sicherheitshalber rücksetzen */
    RF_FlushRX();

}

// RX Mode einschalten, 8 Bit CRC
void RF_PowerUpRx() {

    CE_LOW;
    RF_WriteRegSingle(NRF24L01_REG_CONFIG, (1 << NRF24L01_PRIM_RX) | (1 << NRF24L01_PWR_UP) | (1 << NRF24L01_EN_CRC) | (0 << NRF24L01_CRCO));
    /* Alle Buffer leer machen */
    RF_FlushRX();
    RF_FlushTX();
    CE_HIGH;
}

/* Modul abschalten */
void RF_PowerDown() {


}

/* Feste Einstellung des Funkmoduls für die Anwendung
   Auto ACK (Enhanced ShockBurst)
   Empfänger Adresse: RECVR
   Sender Adresse   : SENSR
   Sende Power      : Maximum
   Payload Size     : Anwendung
   Kanal            : 100
*/
void RF_ConfigureAsRX(uint8_t payload_size) {

    if (SPI_initialized == 0)
        RF_SPI_Init();          // Hardware einstellen

    CE_LOW;

    RF_Payload_size = payload_size;

    /* Sendekanal setzen */
    RF_WriteRegSingle(NRF24L01_REG_RF_CH,RF_CHANNEL);

    /* No dyn Payload, No Payload with ACK */
    RF_WriteRegSingle(NRF24L01_REG_FEATURE,0x00);

    /* Datenrate auf 250kBits/s setzen, Power = Max */
    RF_WriteRegSingle(NRF24L01_REG_RF_SETUP, (1 << NRF24L01_RF_DR_LOW) | (0 << NRF24L01_RF_DR_HIGH) | 0x06);

    /* Erlaubte Pipe 0 setzen, alle anderen aus */
    RF_WriteRegSingle(NRF24L01_REG_EN_RXADDR, (1 << NRF24L01_ERX_P0));

    /* Adressen setzen */
    RF_WriteRegSingle(NRF24L01_REG_SETUP_AW,0x03);          // 11 = 5 Bytes Adresse
    RF_WriteRegMulti(NRF24L01_REG_RX_ADDR_P0,MyAddr,0x05);  // RX Pipe 0
    RF_WriteRegMulti(NRF24L01_REG_TX_ADDR,TxAddr,0x05);     // TX Pipe fuer Auto ACK

    /* Enhanced Shockburst: Auto ACK nur für Pipe 0 und 1 setzen, alle anderen aus */
    RF_WriteRegSingle(NRF24L01_REG_EN_AA, ( 1 << NRF24L01_ENAA_P0));

    /* Retransmission 15 und Delay 4000us (nicht erforderlich) */
    RF_WriteRegSingle(NRF24L01_REG_SETUP_RETR,0xff);

    /* Payload Size für die Pipes setzen */
    RF_WriteRegSingle(NRF24L01_REG_RX_PW_P0,payload_size);  // P0: Receiver Pipe
    RF_WriteRegSingle(NRF24L01_REG_RX_PW_P1,0);             // P1: not used
    RF_WriteRegSingle(NRF24L01_REG_RX_PW_P2,0);             // P2: not used
    RF_WriteRegSingle(NRF24L01_REG_RX_PW_P3,0);             // P3: not used
    RF_WriteRegSingle(NRF24L01_REG_RX_PW_P4,0);             // P4: not used
    RF_WriteRegSingle(NRF24L01_REG_RX_PW_P5,0);             // P5: not used

}

