/***********************************************************************
 * Copyright (c)  2017 - 2020, Unicmicro Co.,Ltd .
 * All rights reserved.
 * Filename    : spi0.c
 * Description : spi0 source file
 * Author(s)   : will
 * version     : V1.0
 * Modify date : 2019-11-21
 ***********************************************************************/
#include "spi0.h"
#include "common.h"

void (*spi0func)(void) = { 0 };

/************************************************************************
 * Function   	: SPI0_IRQHandler
 * Description	: SPI0_IRQHandler SPI0中断处理函数
 * Input 				: none
 * Output 			: none
 * Return				: none
 ************************************************************************/
void SPI0_IRQHandler(void)
{
	if(REG_SPI0_SPIIF & (0x01 << 0))		//接收缓存溢出中断标志
	{
		if(spi0func != NULL)
		{
		 ((void(*)())(spi0func))(); 
			REG_SPI0_SPIIF = 0x10;					//清接收缓存溢出中断
		}
	}
	else
	{
		REG_SPI0_SPIIF = 0x18;						//清发送、接收缓存溢出中断
	}
	     						
}

/************************************************************************
 * Function   	: spi0_init
 * Description	: spi0_init SPI0初始化
 * Input 				: 
 *            	  uint8_t work_mode: select spi0 work mode  0,1,2,3   
 *			  	  		uint8_t spi0_clk_div  spi0 baud rate
 *                uint8_t spi0_cs  SPI0_CS0 or SPI0_CS1
 * Output 			: none
 * Return				: none
 ************************************************************************/
void spi0_init(uint8_t work_mode,uint8_t spi0_clk_div,uint8_t spi0_cs)        
{
	REG_SCU_PERICLKEN |= (1<<4);														//SPI0模块时钟使能
	REG_SCU_PERIRESET |= (1<<4);														//SPI0模块正常工作

	REG_SCU_IOCTRLPROTECT = 0xA5A55A5A; 										//清除IO写保护
	if(spi0_cs == SPI0_CS0)
	{
		REG_SCU_PBSEL |= (REG_SCU_PBSEL&(~(7<<28)))|(1<<28);	//PB7复用为SPI0_SCK
		REG_SCU_PASEL |= (REG_SCU_PASEL&(~(7<<4)))|(2<<4);		//PA1复用为SPI0_MOSI
		REG_SCU_PASEL |= (REG_SCU_PASEL&(~(7<<24)))|(3<<24);	//PA6复用为SPI0_CSN0

		
		REG_SCU_IOCTRLPROTECT = 0xFFFFFFFF; 									//开启IO写保护
		
		REG_SPI0_SPICS0 = 0x00;	 															//初始化SPI0主模式控制寄存器0
		
		REG_SPI0_SPICS0 &= ~(1<<2);														//先发送MSB
		REG_SPI0_SPICS0 |= (spi0_clk_div<<3);  								//SPI0主模式下，CS0对应Master模式波特率配置分频PCLK/div
		
		REG_SPI0_SPICS0 |= work_mode;    											//工作模式设置
		
	}
	else																										//若选用CS1，则配置SPICS1寄存器
	{
		REG_SCU_PBSEL |= (REG_SCU_PBSEL&(~(7<<28)))|(1<<28);	//PB7复用为SPI0_SCK
		REG_SCU_PASEL |= (REG_SCU_PASEL&(~(7<<4)))|(2<<4);		//PA1复用为SPI0_MOSI
		REG_SCU_PDSEL |= (REG_SCU_PDSEL&(~(7<<12)))|(6<<12);	//PD3 SPI0_CSN1		
//		REG_SCU_PDSEL |= (REG_SCU_PDSEL&(~(7<<8)))|(2<<8);		//PD2 SPI0_MI1
		
		REG_SCU_IOCTRLPROTECT = 0xFFFFFFFF; 									//开启IO写保护
		
		REG_SPI0_SPICS1 = 0x00;	 															//初始化SPI0主模式控制寄存器1
		
		REG_SPI0_SPICS1 &= ~(1<<2);														//先发送MSB
		REG_SPI0_SPICS1 |= (spi0_clk_div<<3);  								//SPI0主模式下，CS1对应Master模式波特率配置分频PCLK/div
		
		REG_SPI0_SPICS1 |= work_mode;     										//工作模式设置
	}
	
	REG_SPI0_SPICR = 0x00;																	//初始化SPI0配置寄存器
	REG_SPI0_SPICR |= (1<<5);																//主机模式
	REG_SPI0_SPICR |= (1<<1);																//Master模式下SSN输出由软件控制
	
	REG_SPI0_SPIIE = 0x00;																	//默认状态下关闭所有中断功能	
	REG_SPI0_SPIOPCR = 0x0F; 																//开始传输时，先清空内部所有缓存

	REG_SPI0_SPICR |= (1<<0);	 															//使能spi0
	
}

/************************************************************************
 * Function   	: spi0_irq_init
 * Description	: spi0_irq_init spi0中断配置
 * Input 				: 
 *								uint8_t irq_enable 中断开关
 *								uint32_t spi0_intr 中断类型
 *            	  void (*pfunc)() 中断回调函数  
 * Output 			: none
 * Return				: none
 ************************************************************************/
void spi0_irq_init(uint8_t irq_enable,uint32_t spi0_intr,void (*pfunc)())
{
	if(irq_enable == SPI0_IRQ_ENABLE)
	{
		spi0func = pfunc;
		
		NVIC_ClearPendingIRQ(SPI0_IRQn); 
		NVIC_EnableIRQ(SPI0_IRQn);
		
		REG_SPI0_SPIIE |= (1<<spi0_intr);     					//中断使能	
	}
	else
	{
		REG_SPI0_SPIIE &= ~(0xFF<<0);     							//中断关闭
		
		spi0func = NULL;
	}
	
}


/************************************************************************
 * Function   	: spi0_irq_receive_byte
 * Description	: spi0_irq_receive_byte 在中断接收一个byte数据		
 * Input 				: none
 * Output 			: none
* Return				: REG_SPI_SPIRXBUF	接收数据寄存器数据	
 ************************************************************************/
uint8_t spi0_irq_receive_byte(void)
{
	return REG_SPI0_SPIRXBUF;				//返回读取寄存器的值
}



#ifdef full_duplex								//全双工传输
/************************************************************************
 * Function   	: spi0_master_full_duplex_tranfer
 * Description	: spi0_master_full_duplex_tranfer spi0全双工传输
 * Input 				: 
 *								uint8_t send_data
 *							 	uint8_t rec_buff
 *							 	uint32_t length
 * Output 			: none
 * Return				: 正常发送返回	：0
 *			   		 	：异常发送返回 	：1
 ************************************************************************/
uint8_t spi0_master_full_duplex_tranfer(uint8_t *send_data, uint8_t *rec_buff, uint32_t length)
{
	uint32_t time_out = 0xFFFFFFFF;
	while(length--)
	{
		while(!(REG_SPI0_SPIIF & (1<<SPIIF_TNF)))					//等待tx Fifo未满
		{
			if((time_out--) == 0) 
			{
				return 1;
			}
		}

		REG_SPI0_SPITXBUF = *send_data++;										//写入数据寄存器，把要写入的数据写入发送缓存	
		
		time_out = 0xFFFFFFFF;
		
		while(!(REG_SPI0_SPIIF & (1<<SPIIF_RXBF)))				//等待接收缓存非空
		{
			if((time_out--) == 0) 
			{
				return 1;
			}
		}
		
		*rec_buff++ = REG_SPI0_SPIRXBUF;	
	}
	spi0_wait_idle();
	return 0;
}

#else					//半双工传输
/************************************************************************
 * Function   	: spi0_master_half_duplex_send_bytes
 * Description	: spi0_master_half_duplex_send_bytes	发送多个byte数据
 * Input 				: 
 *         				uint8_t* buff: out buffer
 *         				uint32_t length: buffer length
 * Output 			: none
 * Return				: 正常发送返回	：0
 *			   		 	：异常发送返回 	：1
 ************************************************************************/
uint8_t spi0_master_half_duplex_send_bytes(uint8_t *buff, uint32_t length)
{
	uint32_t time_out =0xFFFFFFFF;
	#ifndef irq_mode_enable
	REG_SPI0_SPICR |= (1 << 8);										//打开TXONLY
	#endif
	while(length--)
	{
		while(!(REG_SPI0_SPIIF & (1<<SPIIF_TNF)))		//等待tx Fifo未满
		{
			if((time_out--) == 0)
			{
				return 1;
			}
		}
		REG_SPI0_SPITXBUF = *buff++;
		
	}
	spi0_wait_idle(); 												 	//等待发送空闲

	REG_SPI0_SPICR &= ~(1 << 8);								//关闭TXONLY

	return 0;
}

/************************************************************************
 * Function   	: spi0_master_half_duplex_receive_bytes	
 * Description	: spi0_master_half_duplex_receive_bytes 	接收多个byte数据
 * Input 				: 
 *								uint8_t byte   
 * Output 			: none
 * Return				: 正常发送返回	：0
 *			   		 	：异常发送返回 	：1
 ************************************************************************/
uint8_t spi0_master_half_duplex_receive_bytes(uint8_t *buff,uint32_t length)
{
	uint32_t time_out = 0xFFFFFFFF;
	while(length--)
	{
		while(!(REG_SPI0_SPIIF & (1<<SPIIF_TNF)))			//等待tx Fifo未满
		{
			if((time_out--) == 0) 
			{
				return 1;
			}
		}

		REG_SPI0_SPITXBUF = 0xFF;										//写入数据寄存器，把数据写入发送缓存	
		
		time_out = 0xFFFFFFFF;
		while(!(REG_SPI0_SPIIF & (1<<SPIIF_RXBF)))	//等待接收缓存非空
		{
			if((time_out--) == 0) 
			{
				return 1;
			}
		}
		*buff++ = REG_SPI0_SPIRXBUF;
		
		spi0_wait_idle(); 												 //等待发送空闲
	}

		return 0;

}


#endif



/************************************************************************
 * Function   	: spi0_cs_enable
 * Description	: spi0_cs_enable CS片选设置
 * Input 				: 
 *								uint8_t cs_enable
 * 								uint8_t cs_select
 * Output 			: none
 * Return				: none
 ************************************************************************/
void spi0_cs_enable(uint8_t cs_enable, uint8_t cs_select)
{
	if(cs_enable == SPI0_CS_ENABLE)
	{
		if(cs_select == SPI0_CS0)
		{
			REG_SPI0_SPICS0 |= (1<<6); 	//CS0片选拉低开始工作
		}
		else
		{
			REG_SPI0_SPICS1 |= (1<<6);	//CS1片选拉低开始工作
		}
	}
	else
	{
		REG_SPI0_SPICS0 &= ~(1<<6); 	//CS0片选拉高停止工作
		REG_SPI0_SPICS1 &= ~(1<<6);		//CS1片选拉高停止工作
	}
}


/************************************************************************
 * Function   	: spi0_wait_idle
 * Description	: spi0_wait_idle 等待SPI0空闲
 * Input 				: none
 * Output 			: none
 * Return				: none
 ************************************************************************/
static void spi0_wait_idle(void)
{
	while(!(REG_SPI0_SPIIF & (1<<SPIIF_IDLE)));					//等待发送完成
}



/************************************************************************
 * Function   	: spi0_command
 * Description	: spi0_command spi0 command
 * Input 				: write_length - write bufffer length
 *								write_buf - write buffer
 *								read_length - read bufffer length
 *								read_buf - read buffer
 *								cs - chip select   
 * Output 			: none
 * Return				: 正常发送返回	：0
 *			   		 	：异常发送返回 	：1
 ************************************************************************/
uint8_t spi0_command(uint32_t write_length, uint8_t *write_buf,
									   uint32_t read_length, uint8_t *read_buf,
									   uint8_t cs)
{
	uint8_t ret = 0;
	
	spi0_init(WORK_MODE_3,SPI_CLK_DIV_32,cs);
	delay(100);	//等待	
	spi0_cs_enable(SPI0_CS_ENABLE,cs);  //拉低片选开始工作

	
	ret = spi0_master_half_duplex_send_bytes(write_buf, write_length); //半双工发送
	if (ret)
	{
		goto exit;
	}

	ret = spi0_master_half_duplex_receive_bytes(read_buf, read_length); //半双工接收
	if (ret)
	{
		goto exit;
	}

exit:
	spi0_cs_enable(SPI0_CS_DISABLE,cs);//拉高片选停止工作	
	return ret;
}


