#include "llcc68.h"
#include "llcc68_hal.h"
#include <string.h>
#include "application.h"
#include "main.h"

/** 
 * 模组基本参数定义
 */
typedef struct 
{
	bool    is_tx;             //!< 发送状态
	bool    is_rx;	           //!< 接收状态
	uint8_t rx_buffer[255];    //!< 接收缓存
	uint8_t rx_length;         //!< 接收长度 
	int8_t  rx_rssi;           //!< 接收RSSI强度数值
	uint8_t crystal_capacitor; //!< 无源晶振电容补偿值(影响频率准确度)
}context_e220_t;

/** 
 * 模组默认配置
 */
static context_e220_t context_e220 = {
	.is_tx     = false, 
	.is_rx     = false,	
	.rx_length = 0,
	.crystal_capacitor = 0x1C,
};

/** 
 * 模组PA默认配置
 */
static llcc68_pa_cfg_params_t pa_cfg = {
	.pa_duty_cycle = 0x04,
	.hp_max        = 0x07,
	.device_sel    = 0x00,
	.pa_lut        = 0x01,
};

/** 
 * 模组LoRa调制默认配置
 */
static llcc68_mod_params_lora_t mod_cfg = {
	.sf   = LLCC68_LORA_SF11,      //!< LoRa Spreading Factor
	.bw   = LLCC68_LORA_BW_500,    //!< LoRa Bandwidth
	.cr   = LLCC68_LORA_CR_4_5,    //!< LoRa Coding Rate
	.ldro = true,                  //!< Low DataRate Optimization configuration
};

/** 
 * 模组数据包结构默认配置
 */
static llcc68_pkt_params_lora_t pkt_cfg = {
	.preamble_len_in_symb = 8,                        //!< Preamble length in symbols
	.header_type          = LLCC68_LORA_PKT_EXPLICIT, //!< Header type
	.pld_len_in_bytes     = 255,                      //!< Payload length in bytes
	.crc_is_on            = true,                     //!< CRC activation
	.invert_iq_is_on      = false,                    //!< IQ polarity setup
};

/**
 * @brief  (可选的)模组SPI读写测试
 *
 * @note 如果失败了，请检查电气链接。也可以先短接SPI的 MISO MOSI，先测试自发自收是否正确
 */
static void e220_spi_check(void)
{
		uint8_t temp = 0xA5;
	
		/* 写入一个标记
	     0x06BB 寄存器为可读可写类型
     	 LLCC68_REG_RXTX_PAYLOAD_LEN  */
		llcc68_write_register( &context_e220, 0x06BB, &temp, 1 );

		/* 读回 */
		temp = 0;
		llcc68_read_register( &context_e220, 0x06BB, &temp, 1 );
	
		/* 比较 */
    if( 0xA5 != temp )
    {
        /* 一般是SPI通信异常 请检查接线 */
				while(1);
    }		
}

/**
 * @brief  (可选的)模组晶振启动测试
 */
static void e220_xosc_check(void)
{
		llcc68_errors_mask_t error_info;	
	
		llcc68_clear_device_errors( &context_e220 ); 
	
		llcc68_set_standby( &context_e220 , LLCC68_STANDBY_CFG_XOSC );
		
		llcc68_get_device_errors( &context_e220 , &error_info ); 
	
		if( error_info & LLCC68_ERRORS_XOSC_START )
		{
			  /* 晶振问题请检查模组型号或与销售联系
   			   不要搞混无源、有源晶振模组 */
				while(1);				
		}
}

/**
 * @brief  模组初始化
 */
void e220_demo_init( void )
{
	/* 硬件复位 */
	llcc68_reset( &context_e220 );
	
	/* 唤醒 */
	llcc68_wakeup( &context_e220 ); 	
	
	/* 切换工作状态 */
	llcc68_set_standby( &context_e220, LLCC68_STANDBY_CFG_RC );	
		
	/* (可选)基础SPI检查 */
	e220_spi_check(); 
	
	/* 保留寄存器设置 (减少唤醒恢复时间) */
	llcc68_init_retention_list( &context_e220 );
	
	/* 内部电源模式 (DCDC功耗更小)*/
	llcc68_set_reg_mode( &context_e220, LLCC68_REG_MODE_DCDC );		
	
	/* 禁止DIO2切换射频开关 (该评估板硬件没有将E220模组的DIO2与RXEN连接)	*/
	llcc68_set_dio2_as_rf_sw_ctrl( &context_e220, false );	
	
	/* 开启晶振 E220系列为无源晶振 */
	llcc68_set_standby( &context_e220 , LLCC68_STANDBY_CFG_XOSC );
	
	/* 晶振负载电容 */
	llcc68_set_trimming_capacitor_values ( &context_e220 , context_e220.crystal_capacitor , context_e220.crystal_capacitor);  
	
	/* 修正内部状态 */
	llcc68_cal( &context_e220, LLCC68_CAL_ALL );
	
	/* (可选)基础晶振检查 */
	e220_xosc_check();	

	/* 数据包类型 */
	llcc68_set_pkt_type( &context_e220, LLCC68_PKT_TYPE_LORA );
	
	/* 载波频率 (915000000Hz = 915000KHz = 915MHz)*/
	llcc68_set_rf_freq( &context_e220, 915000000 );		
	
	/* 内部PA参数      (请参考llcc68 Datasheet中的 13.1.14 SetPaConfig)*/
	llcc68_set_pa_cfg( &context_e220, &pa_cfg );

	/**
	 * 1级 发射功率 22dBm
	 * (30dBm模组的)软件控制功率仅仅是射频芯片输出功率(1级)，经由功率放大器PA增益后才输出最终功率(2级)
	 * 由于PA本身的非线性特性，一般需要射频功率计或者频谱仪对最终功率(2级)进行标定
	 * @note 915MHz只能粗略给出1级与2级对应关系，仅供参考：
	 * 1级22dBm ≈  2级30dBm    
	 * 1级19dBm ≈  2级27dBm    
	 * 1级17dBm ≈  2级24dBm    
	 * 1级14dBm ≈  2级21dBm    	 	 
	 */
	llcc68_set_tx_params( &context_e220, 22 , LLCC68_RAMP_40_US );  
	
	/* 完成TX/RX后的工作状态 */
	llcc68_set_rx_tx_fallback_mode( &context_e220, LLCC68_FALLBACK_STDBY_RC );
	
	/* 关闭增强接收 (开启会增加接收灵敏度，但功耗会增加) */
	llcc68_cfg_rx_boosted( &context_e220, false );
	
	/* LORA调制参数 (与空中速率、接收灵敏度有关联)
     可以使用计算器	https://www.semtech.com/design-support/lora-calculator */
	llcc68_set_lora_mod_params( &context_e220, &mod_cfg );
	
	/* LORA数据包格式 */
	llcc68_set_lora_pkt_params( &context_e220, &pkt_cfg );
	
	/* LORA同步字 */
	llcc68_set_lora_sync_word( &context_e220, 0x14 );
}

/**
 * @brief 使用显示菜单用户配置参数重新配置模组
 *
 * @param config 菜单配置参数信息
 */
void e220_demo_menu_config( menu_config_t *config )
{
  int8_t power_select;
	
	/* 切换工作状态 */
	llcc68_set_standby( &context_e220, LLCC68_STANDBY_CFG_RC );	
		
	/* 载波频率 HZ */
	llcc68_set_rf_freq( &context_e220, config->frequency_mhz * 1000000 );		
	
	/* 菜单设定2级功率转换为对应的1级功率 */	
	switch( config->tx_power )
	{
		case 27: 
			power_select = 19; 
			break;
		
		case 24:
			power_select = 17; 
			break;			
		
		case 21:
			power_select = 14; 
			break;	

		default:
			power_select = 22; 
			break;
	}	
	
	/**
	 * 1级 发射功率
	 * (30dBm模组的)软件控制功率仅仅是射频芯片输出功率(1级)，经由功率放大器PA增益后才输出最终功率(2级)
	 * 由于PA本身的非线性特性，一般需要射频功率计或者频谱仪对最终功率(2级)进行标定
	 * @note 915MHz只能粗略给出1级与2级对应关系，仅供参考 ：
	 * 1级22dBm ≈  2级30dBm    
	 * 1级19dBm ≈  2级27dBm    
	 * 1级17dBm ≈  2级24dBm    
	 * 1级14dBm ≈  2级21dBm  
	 */ 
	llcc68_set_tx_params( &context_e220, power_select , LLCC68_RAMP_40_US );  
	
	/* LORA调制参数 (与空中速率、接收灵敏度有关联)
     可以使用计算器	https://www.semtech.com/design-support/lora-calculator */
	mod_cfg.sf = (llcc68_lora_sf_t)config->lora_sf;
	switch( config->lora_bw )
	{
		case 500:
			mod_cfg.bw = LLCC68_LORA_BW_500; 
		  break;
		case 250:
			mod_cfg.bw = LLCC68_LORA_BW_250; 
		  break;
		case 125:
			mod_cfg.bw = LLCC68_LORA_BW_125; 
		  break;		
		default:
			while(1);
	}
	switch( config->lora_cr )
	{
		case 5:
			mod_cfg.cr = LLCC68_LORA_CR_4_5; 
		  break;
		case 6:
			mod_cfg.cr = LLCC68_LORA_CR_4_6; 
		  break;
		case 7:
			mod_cfg.cr = LLCC68_LORA_CR_4_7; 
		  break;		
		case 8:
			mod_cfg.cr = LLCC68_LORA_CR_4_8; 
		  break;			
		default:
			/* 不支持 */
			while(1);
	}	

	llcc68_set_lora_mod_params( &context_e220, &mod_cfg );
}

/**
 * @brief 向模组写入数据并开始发射
 *
 * @param buffer 指向数据缓存
 * @param length 写入长度
 */
void e220_demo_transmit( uint8_t *buffer , uint8_t length )
{
	  /* 发送长度 */
    pkt_cfg.pld_len_in_bytes = length;
    llcc68_set_lora_pkt_params( &context_e220 , &pkt_cfg ); 

    /* 写入内部缓存 */
    llcc68_write_buffer( &context_e220 , 0x00, buffer, length );
	  
    /* 允许发送完成中断，并映射到引脚DIO1上 */
    llcc68_set_dio_irq_params( &context_e220,
                               LLCC68_IRQ_TX_DONE  ,   /* irq_mask */
                               LLCC68_IRQ_TX_DONE  ,   /* dio1_mask */
                               LLCC68_IRQ_NONE,        /* dio2_mask */
                               LLCC68_IRQ_NONE );      /* dio3_mask */
    
    /* 清中断状态 */
    llcc68_clear_irq_status( &context_e220, LLCC68_IRQ_ALL );  	
	
	  /* 模组内部射频开关切换到发送状态 */
		llcc68_rf_switch_tx();
	
	  ///@note 启动发射并不带表发送已完成，要等到发送完成中断产生后才算是完成，请参考 LLCC68_IRQ_TX_DONE
	  /* 开始发送数据 */
    llcc68_set_tx( &context_e220 , 0x00 );
	
	  /* 设置发送标记 */
		context_e220.is_tx = true;
		
		/* (可选) LED TX指示 */
		gpio_led_tx_on();		
}

/**
 * @brief  模组切换到持续接收状态
 */
void e220_demo_receive( void )
{
		/* 接收长度 最大255 */
    pkt_cfg.pld_len_in_bytes = 255;
    llcc68_set_lora_pkt_params( &context_e220 , &pkt_cfg ); 
   
    /* 允许接收完成中断与前导码检测中断，并映射到引脚DIO1上 */
    llcc68_set_dio_irq_params( &context_e220,
                               LLCC68_IRQ_RX_DONE | LLCC68_IRQ_PREAMBLE_DETECTED ,
                               LLCC68_IRQ_RX_DONE | LLCC68_IRQ_PREAMBLE_DETECTED ,
                               LLCC68_IRQ_NONE,
                               LLCC68_IRQ_NONE );    
    /* 清中断状态 */
    llcc68_clear_irq_status( &context_e220, LLCC68_IRQ_ALL );
    
	  /* 模组内部射频开关切换到接收状态 */
		llcc68_rf_switch_rx();	
  
    /* 进入接收状态，等待数据 */
    llcc68_set_rx( &context_e220, 0);		
}

/**
 * @brief  查询是否有接收数据
 *
 * @param   buffer 指向待拷贝数据缓存
 * @param   length 接收数据长度
 * @return  bool 有新接收数据则返回true; 否则返回false。
 */
bool  e220_demo_check_rx_done( uint8_t *buffer, uint8_t * length  , int8_t *rssi)
{
	bool ret = false;
	
	if( context_e220.is_rx == true )
	{
		context_e220.is_rx = false;
		
		memcpy( buffer ,  context_e220.rx_buffer , context_e220.rx_length );
		
		*length = context_e220.rx_length;
		context_e220.rx_length = 0;
		
		*rssi = context_e220.rx_rssi;
		context_e220.rx_rssi = 0;
		
		ret = true;
	}
	
	return ret;
}

/**
 * @brief  模组引脚中断响应
 *
 * @note 与收发函数内的中断配置关联，默认仅实现了发送完成与接收完成
 */
void e220_demo_dio1_interrupt_callback(void)
{
    llcc68_irq_mask_t irq_mask;
    llcc68_rx_buffer_status_t buffer_status;		
		llcc68_pkt_status_lora_t pkt_status;  
	
    /* 读取中断状态 */
    llcc68_get_irq_status( &context_e220 , &irq_mask);	
	
    /* 如果有中断标识 */
    if( irq_mask != LLCC68_IRQ_NONE )
    {
					/* 清除中断标识 */
					llcc68_clear_irq_status( &context_e220, irq_mask );  
       
          //================================================================================
          /* 中断：检查到了前导码 */         
          if( irq_mask & LLCC68_IRQ_PREAMBLE_DETECTED )
          {

          }
     
          //================================================================================
          /* 中断：接收完成 */         
          if( irq_mask & LLCC68_IRQ_RX_DONE )
          {
              llcc68_get_rx_buffer_status( &context_e220, &buffer_status );
              
              if( buffer_status.pld_len_in_bytes != 0 )
              {
                  /* 先获取接收数据长度与缓存偏移位置 */
                  llcc68_get_lora_pkt_status( &context_e220, &pkt_status );
                  
                  /* 将LLCC68内部缓存数据读出 */
                  llcc68_read_buffer( &context_e220 , buffer_status.buffer_start_pointer , context_e220.rx_buffer, buffer_status.pld_len_in_bytes );
                  
									/* 记录接收长度 */
									context_e220.rx_length = buffer_status.pld_len_in_bytes;
								
									/* RSSI */
									context_e220.rx_rssi = pkt_status.rssi_pkt_in_dbm;
								
									/* 标记接收 */
									context_e220.is_rx = true;
								
									/* (可选) LED RX指示 */
									gpio_led_rx_on();										
									
              }

							/* 重新进入接收状态 */
							e220_demo_receive();

          }      
          //================================================================================
          /* 中断：发送完成 */         
          if( irq_mask & LLCC68_IRQ_TX_DONE )
          {          
						  /* 清除发送标记 */
							context_e220.is_tx = false;
						
							/* (可选) LED TX指示 */
							gpio_led_tx_off();						
          }
    }	
}

