/***********************************************************************
 * Copyright (c)  2017 - 2022, Unicmicro Co.,Ltd .
 * All rights reserved.
 * Filename    : spi0_master.c
 * Description : spi0 master source file
 * Author(s)   : Mason
 * version     : V1.0
 * Modify date : 2022-08-04
 ***********************************************************************/
#include "spi0_master.h"

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

/************************************************************************
 * Function     : SPI0_IRQHandler
 * Description  : SPI0_IRQHandler
 * Input        : 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_wait_idle
 * Description  : spi0_wait_idle 等待SPI0空闲
 * Input        : none
 * Return       : none
 ************************************************************************/
static void spi0_wait_idle(void)
{
	while(!(REG_SPI0_SPIIF & (1<<SPIIF_IDLE)));					        //等待发送完成
}

/************************************************************************
 * Function     : spi0_init
 * Description  : spi0_init
 * 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
 * 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_PESEL = (REG_SCU_PESEL&(~(0xF<<4)))|(0x3<<4);	        //PE1复用为SPI0_SCK
		REG_SCU_PESEL = (REG_SCU_PESEL&(~(0xF<<12)))|(0x2<<12);	        //PE3复用为SPI0_MOSI
		REG_SCU_PFSEL = (REG_SCU_PFSEL&(~(0xF<<24)))|(0xB<<24);	        //PF6复用为SPI0_MISO
        REG_SCU_PADIE1 |= (1<<14);                                      //PF6输入使能
		REG_SCU_PFSEL = (REG_SCU_PFSEL&(~(0xF<<12)))|(0x9<<12);	        //PF3复用为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_PFSEL = (REG_SCU_PFSEL&(~(0xF<<4)))|(0x8<<4);	        //PF1复用为SPI0_SCK
		REG_SCU_PFSEL = (REG_SCU_PFSEL&(~(0xF<<16)))|(0xB<<16);	        //PF4复用为SPI0_MOSI
		REG_SCU_PGSEL = (REG_SCU_PGSEL&(~(0xF<<8)))|(0xB<<8);	        //PG2复用为SPI0_MI1	
        REG_SCU_PADIE1 |= (1<<18);                                      //PG2输入使能
		REG_SCU_PGSEL = (REG_SCU_PGSEL&(~(0xF<<16)))|(0xA<<16);	        //PG4复用为SPI0_CSN1
		
		
		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_SPICR |= (3<<3);                                           //该配置项用于4分频及4分频以下的场景
                           
	REG_SPI0_SPIIE = 0x00;	                                            //默认状态下关闭所有中断功能	
	REG_SPI0_SPIOPCR = 0x0F;                                            //开始传输时，先清空内部所有缓存
                                        
	REG_SPI0_SPICR |= (1<<0);                                           //使能spi0
	
}

/************************************************************************
 * Function     : spi0_irq_init
 * Description  : spi0 irq init
 * Input        : uint8_t irq_enable: irq enable or disable
 *                uint32_t spi0_intr：spi0 interrupt type
 *                void (*pfunc)()：spi0 interrupt callback function
 * 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  : receive a byte in interrupt
 * Input        : none
 * Return       : spi rx buff
 ************************************************************************/
uint8_t spi0_irq_receive_byte(void)
{
	return REG_SPI0_SPIRXBUF;		                                    //返回读取寄存器的值
}



#ifdef FULL_DUPLEX_MODE				                                    //全双工传输
/************************************************************************
 * Function     : spi0_master_full_duplex_tranfer
 * Description  : spi0_master_full_duplex_tranfer spi0全双工传输
 * Input        : uint8_t *send_data：tx data buff
 *                uint8_t *rec_buff：rx data buff
 *                uint32_t length：data length
 * Return       : 正常发送返回	：0
 *                异常发送返回 	：1
 ************************************************************************/
uint8_t spi0_master_full_duplex_tranfer(uint8_t *send_data, uint8_t *rec_buff, uint32_t length)
{
	while(length--)
	{
		
		REG_SPI0_SPITXBUF = *send_data++;			                    //写入数据
        while(!(REG_SPI0_SPIIF & (1<<SPIIF_TXBE)));                     //1发送缓存无数据，等待发送缓存有数据
		
        while(!(REG_SPI0_SPIIF & (1<<SPIIF_RXBF)));	                    //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
 * Input        : uint8_t *buff：tx data buff
 *                uint32_t length：data length
 * Return       : 正常发送返回	：0
 *                异常发送返回 	：1
 ************************************************************************/
uint8_t spi0_master_half_duplex_send_bytes(uint8_t *buff, uint32_t length)
{
   
#ifndef SPI_RX_INT_MODE
	REG_SPI0_SPICR |= (1 << 8);							                //打开TXONLY
#endif
    
	while(length--)
	{
		REG_SPI0_SPITXBUF = *buff++;
		while(!(REG_SPI0_SPIIF & (1<<SPIIF_TXBE)));                     //1发送缓存无数据，等待发送缓存有数据

        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 *buff：rx data buff
 *                uint32_t length：data length
 * Return       : 正常发送返回	：0
 *                异常发送返回 	：1
 ************************************************************************/
uint8_t spi0_master_half_duplex_receive_bytes(uint8_t *buff,uint32_t length)
{

	while(length--)
	{
		REG_SPI0_SPITXBUF = 0xFF;										
		while(!(REG_SPI0_SPIIF & (1<<SPIIF_TXBE)));                     //1发送缓存无数据，等待发送缓存有数据

		while(!(REG_SPI0_SPIIF & (1<<SPIIF_RXBF)));	                    //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：enable or disable
 *                uint8_t cs_select：spi0 cs0 or spi0 cs1
 * 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片选拉高停止工作
	}
}
