/*
 * true's NRF24L01 C library
 * STM32L1xx interface driver
 * basically: it's better than nothing
 */


#include "true_nRF24l01_stm32l1xx.h"


/* hardware init */
void nRF24_spi_init(uint16_t speed) {
	SPI_InitTypeDef spi;

	spi.SPI_Mode = SPI_Mode_Master;
	spi.SPI_BaudRatePrescaler = speed;
	spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	spi.SPI_CPOL = SPI_CPOL_Low;
	spi.SPI_CPHA = SPI_CPHA_1Edge;
	spi.SPI_CRCPolynomial = 7;
	spi.SPI_DataSize = SPI_DataSize_8b;
	spi.SPI_FirstBit = SPI_FirstBit_MSB;
	spi.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set;

	SPI_Init(_TRNRF_SPI_PORT, &spi);
	SPI_Cmd(_TRNRF_SPI_PORT, ENABLE);
}

void nRF24_interrupt_init()
{
	EXTI_InitTypeDef exti;
	NVIC_InitTypeDef nvic;

	// make sure SYSCFG clock is running
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	// set up the IRQ pin to actually attach to an interrupt
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource11); 	// IRQ pin, also see lonelywolf_nRF24L01.h

	exti.EXTI_Line = EXTI_Line11; // EXTI will be on line 11
	exti.EXTI_LineCmd = ENABLE; // EXTI enabled
	exti.EXTI_Mode = EXTI_Mode_Interrupt; // Generate IRQ
	exti.EXTI_Trigger = EXTI_Trigger_Falling; // IRQ on signal falling
	EXTI_Init(&exti);

	// and then enable this interrupt at the lowest priority
	nvic.NVIC_IRQChannel = EXTI15_10_IRQn;
	nvic.NVIC_IRQChannelCmd = ENABLE;
	nvic.NVIC_IRQChannelPreemptionPriority = 0x0F;
	nvic.NVIC_IRQChannelSubPriority = 0x0F;
	NVIC_Init(&nvic);
}


/* driver init */
void nRF24_driver_init()
{
	GPIO_InitTypeDef gpio;

#if _TRNRF_SPI_PORT_USE == 1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); 	// SPI pins
#elif _TRNRF_SPI_PORT_USE == 2
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
#endif

	// set up other pin clocks
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); 	// CE/IRQ pins
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); 	// CS pin

	// Configure SPI pins GPIO
	gpio.GPIO_Speed = GPIO_Speed_10MHz;
	gpio.GPIO_OType = GPIO_OType_PP;
	gpio.GPIO_Mode = GPIO_Mode_AF;
	gpio.GPIO_Pin = _TRNRF_SPI_SCK_PIN | _TRNRF_SPI_MISO_PIN | _TRNRF_SPI_MOSI_PIN;
	GPIO_Init(_TRNRF_SPI_GPIO_PORT, &gpio);

	// configure SPI pin AF
	GPIO_PinAFConfig(_TRNRF_SPI_GPIO_PORT, _TRNRF_SPI_SCK_PINSRC, _TRNRF_SPI_GPIO_AF);
	GPIO_PinAFConfig(_TRNRF_SPI_GPIO_PORT, _TRNRF_SPI_MISO_PINSRC, _TRNRF_SPI_GPIO_AF);
	GPIO_PinAFConfig(_TRNRF_SPI_GPIO_PORT, _TRNRF_SPI_MOSI_PINSRC, _TRNRF_SPI_GPIO_AF);

	// configure CS pin as output with push-pull
	gpio.GPIO_Mode = GPIO_Mode_OUT;
	gpio.GPIO_Pin = _TRNRF_SPI_CS_PIN;
	GPIO_Init(_TRNRF_SPI_CS_PORT, &gpio);

	// configure CE pin as output with push-pull
	gpio.GPIO_Pin = _TRNRF_nRF24_CE_PIN;
	GPIO_Init(_TRNRF_nRF24_CE_PORT, &gpio);

	// configure IRQ pin as input with weak pull-Up
	gpio.GPIO_Mode = GPIO_Mode_IN;
	gpio.GPIO_PuPd = GPIO_PuPd_UP;
	gpio.GPIO_Pin = _TRNRF_nRF24_IRQ_PIN;
	GPIO_Init(_TRNRF_nRF24_IRQ_PORT, &gpio);

	// set SPI and CE pins to startup values
	_TRNRF_CSN_H();
	_TRNRF_CE_L();

	// start up SPI and IRQ interrupt
	nRF24_spi_init(_TRNRF_SPI_BAUD);
	nRF24_interrupt_init();
}


/**
 * Communicates with the nRF24L01 (or anything else, really) over SPI.
 *
 * Since this command can be used for bulk transfer, it does NOT do anything
 * with the SPI CS pin. Please check CS before and after using this function.
 *
 * @param 	data 	byte to send on SPI bus
 *
 * @return 			data returned on SPI bus
 */
uint8_t nRF24_spi_rw(uint8_t data) {
	// Wait while DR register is not empty
	while (SPI_I2S_GetFlagStatus(_TRNRF_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET);
	// Send byte to SPI
	SPI_I2S_SendData(_TRNRF_SPI_PORT, data);
	// Wait to receive byte
	while (SPI_I2S_GetFlagStatus(_TRNRF_SPI_PORT, SPI_I2S_FLAG_RXNE) == RESET);
	// Read byte from SPI bus
	return SPI_I2S_ReceiveData(_TRNRF_SPI_PORT);
}


/* irq handler */
void EXTI15_10_IRQHandler(void) {
	uint8_t status = 0;

	if (EXTI_GetITStatus(EXTI_Line11) != RESET) {
		status = nRF24_interrupt();

		// do anything custom here if you like

		EXTI_ClearITPendingBit(EXTI_Line11);
	}

	// fixes compiler warning for unused status
	status = status | 0x00;
}
