/****************************************************************************************
 * KF8A系列单片机	KF8A100GQT_DEMO	开发板演示程序
 * 标    题: SPI主机、从机实验
 * 项目名: 16- SPI_Sample
 * 开发环境：ChipON IDE
 * 版          本     ：V1.0 (2019/07/23)
 * 作 者: 上海芯旺微电子有限公司
 * 功能简述:	测试SPI的主从机模式
 ****************************************************************************************/
#include "includes.h"

s_uint8 		  SSCI_SPI_State;
s_SPI_RAMBuffer   SSCISPI_DateStruct;

uint8 DataBuf_index;
uint8 DataBuf[DATASPI_BUFFER_BYTE_SIZE];  	// 引入校验的接收缓存

#define  SPI_IS_MASTER 	 			SSCISPI_DateStruct.flag.bit0
#define  SPI_SLAVE_ANSWERED  		SSCISPI_DateStruct.flag.bit1

#define  Is_Responses_IN_Return   	SSCISPI_DateStruct.flag.bit7
#define  Is_Command_Read   		   	SSCISPI_DateStruct.flag.bit6
/***********************************************************************************
 * 函数名     	：
 * 函数功能	：有限周期数的延迟
 * 入口参数	：1~255的有效范围
 * 返回         ：无
 ***********************************************************************************
 */
void Delay_Wait_CLK_Sample(unsigned char x)
{
#pragma save
#pragma disable_warning 59
#pragma disable_warning 85   // 59 85 屏蔽参数未使用警告，函数未返回警告，作用于嵌汇编函数功能实现

	__asm
	 ;// 参数使用R0传递
	 ;//MOV R0,#0x05 固定样例
	 DECJZ R0
	 JMP $-1
	__endasm;

#pragma restore
}
/***********************************************************************************
 * 函数名     	：SPI_Slave_ISR_Server   从机应答主机状态处理
 * 函数功能	：从模式的设备应答实现协议控制
 * 入口参数	：无
 * 返回         ：无
 * 该函数的编写应根据协议做状态机，
 **********************************************************************************/
void SPI_Slave_ISR_Server(void)
{
	uint8 RevDate;

		SSCI1OV=0;// 节省判断的强制清溢出可能，根据应答设计保证不溢出即可，评估系统时可以判断溢出标志识别接收处理
		RevDate=SSCI1BUFR;
		P9LR1=!P9LR1;   // 调试时观察响应及时性，程序进入代码时间内，主机不要启动时钟，否则应答会反应不过来

		//:::: SPI使用的数据流类似串口，需要自行定制协议，根据协议做状态机应用
		//:::: 以下为自定义的协议代码示例，根据需要自行完成协议解析即可。

		//##########################################################
		//###############     协议接收时                 #################
		//##########################################################
		if(!Is_Responses_IN_Return)
		{
			// 识别开头，根据需要设计
			if(SSCISPI_DateStruct.assistant==0)
			{
				if(RevDate!=0x68)  // 设计的固定起始格式
				{
					return;
				}
				else
				{
					SSCI1BUFR=RevDate;// 样例 观察的 返回
					SSCISPI_DateStruct.head=RevDate;  // 根据选择保存
					SSCISPI_DateStruct.assistant++;
				}
				SSCISPI_DateStruct.checkvalue=SSCISPI_DateStruct.head;
				// 启动接收超时监控点
			}
			// 识别命令
			else if(SSCISPI_DateStruct.assistant==1)
			{
				SSCI1BUFR=RevDate;// 样例 观察的 返回
				SSCISPI_DateStruct.Command=RevDate;   //  命令编码或资源地址，这里为资源地址
				SSCISPI_DateStruct.assistant++;
				SSCISPI_DateStruct.checkvalue+=SSCISPI_DateStruct.Command;

				if(SSCISPI_DateStruct.Command&0x80)      //先去看主控模式中的相关控制命令
				{ // 读取模式
					Is_Command_Read=1;
					SSCISPI_DateStruct.Command=SSCISPI_DateStruct.Command&0x7F;
				}
				else
				{ // 写入模式
					Is_Command_Read=0;
				}
			}
			// 识别数据长度
			else if(SSCISPI_DateStruct.assistant==2)
			{
//				P4LR7=!P4LR7;

				SSCISPI_DateStruct.length=RevDate;
				SSCISPI_DateStruct.checkvalue+=SSCISPI_DateStruct.length;

				// 设计存在后续一个校验值的 应答  或 数据后的应答
				if(Is_Command_Read)
				{
					SSCI1BUFR=SSCISPI_DateStruct.checkvalue;  // 同步返回校验和
					SSCISPI_DateStruct.assistant=3; // 数据传递偏移
				}
				else
				{
					// 写数据阶段
					SSCISPI_DateStruct.assistant=4;
					DataBuf_index=0;
				}
			}
			// 读取的命令校验和，样例设计同步时回传，也可以设计读取的第一内容为校验值验证，主机通过后才读取后续数据，协议自行实现状态转移
			else if(SSCISPI_DateStruct.assistant==3)
			{
				if(RevDate==SSCISPI_DateStruct.checkvalue)							  //
				{// 读取第一个数据的准备
					SSCISPI_DateStruct.checkvalue=SSCISPI_DateStruct.buffer[SSCISPI_DateStruct.Command++];
					SSCI1BUFR=SSCISPI_DateStruct.checkvalue;  //

					Is_Responses_IN_Return=1;
				}
				else
				{ // 回到接收命令状态
					SSCI1BUFR=0x01;  //  即使主机读取也会识别从机未应答，数据最后校验和会否决的可靠设计
					SSCISPI_DateStruct.assistant=0;
				}
			}
			// 长度下写命令
			else if(SSCISPI_DateStruct.assistant==4)
			{
				if(DataBuf_index==SSCISPI_DateStruct.length)
				{// 写数据接收完毕，最后一个是校验和
					Is_Responses_IN_Return=0; 			// 命令完成回到接收机状态
					SSCISPI_DateStruct.assistant=0; 	// 接收状态，回到接收起始头

					if(RevDate==SSCISPI_DateStruct.checkvalue)
					{// 接收验证正确下的写入 或自定义设计逻辑
						for(RevDate=0;RevDate<SSCISPI_DateStruct.length;RevDate++)
						{
							SSCISPI_DateStruct.buffer[SSCISPI_DateStruct.Command+RevDate]=DataBuf[RevDate];
						}
					}
					SPI_SLAVE_ANSWERED=1;
				}
				else if(DataBuf_index<SSCISPI_DateStruct.length)
				{// 过程接收，进行缓存，因为不确定接收是否正确
					SSCISPI_DateStruct.checkvalue+=RevDate;
					DataBuf[DataBuf_index++]=RevDate; // 读取到缓存
					SSCI1BUFR=SSCISPI_DateStruct.checkvalue;  // 实时返回校验和
				}
				else
				{// 额外校验模式
					SSCI1BUFR=SSCISPI_DateStruct.checkvalue;
					// 支持无设计的头读取，0x00读取下也返回
				}
			}
		}
		//##########################################################
		//###############    协议应答读取继续准备数据            ############
		//##########################################################
		else
		{  // Is_Responses_IN_Return ==1   true
			if(SSCISPI_DateStruct.Command	<	SSCISPI_DateStruct.length)
			{// 继续的数据返回
				DataBuf_index=SSCISPI_DateStruct.buffer[SSCISPI_DateStruct.Command++];
				SSCISPI_DateStruct.checkvalue+=DataBuf_index;
				SSCI1BUFR=DataBuf_index;
			}
			else
			{// 读完成  或超  返回校验和	// 切换回命令接收状态
				SSCI1BUFR=SSCISPI_DateStruct.checkvalue;
				Is_Responses_IN_Return=0;
				SSCISPI_DateStruct.assistant=0;

				SPI_SLAVE_ANSWERED=1;
				SPI_SLAVE_ANSWERED=0;
			}
			// 考虑读取终止,并完成命令的格式头接收 ,设计主机读取时数据为 0x00，特定值代表指令
			if(RevDate==0x68)
			{
				Is_Responses_IN_Return=0;
				// 帧头接收
				SSCISPI_DateStruct.head=RevDate;
				SSCISPI_DateStruct.checkvalue=RevDate;
				SSCISPI_DateStruct.assistant=1;
			}
		}
		//##########################################################
		//###############           处理完毕                    #################
		//##########################################################
}

/***********************************************************************************
 * 函数名     	：SPI_Master_ISR_Simulate
 * 函数功能	：主控模式的设备实现协议控制
 * 入口参数	：无
 * 返回         ：无
 * 该函数的编写应根据协议做状态机，
 **********************************************************************************/
uint8 SPI_Master_ISR_Simulate(void)
{
	static uint8 commid=1;
	uint8 i;
	// 如果配置SS的控制，进行拉低的使能
	// todo
	//#######################读取命令样例
	SSCISPI_DateStruct.head=0x68;
	SSCISPI_DateStruct.Command=0x80;  // bit7 1读 0写
	SSCISPI_DateStruct.checkvalue=SSCISPI_DateStruct.head+SSCISPI_DateStruct.Command;
	SSCISPI_DateStruct.length=0x03;
	SSCISPI_DateStruct.checkvalue+=SSCISPI_DateStruct.length;

	//~~~~~~~~~~~~~~ 格式化命令下发
	SPI_SendData(SSCISPI_DateStruct.head);	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
	SPI_SendData(SSCISPI_DateStruct.Command);	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
	SPI_SendData(SSCISPI_DateStruct.length);	Delay_Wait_CLK_Sample(50); // 给予从机响应时间



	if(SPI_SendAndReceive_Data(SSCISPI_DateStruct.checkvalue)!=SSCISPI_DateStruct.checkvalue)   // 样例设计的从机先返回校验和
	{// 从机接收异常

		// 如果配置SS的控制，进行置高的失能
		// todo
		return (0);
	}
	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
	// :如果设计读取的首内容为校验和，应  if(SPI_SendAndReceive_Data(0x00)!=SSCISPI_DateStruct.checkvalue ) {容错处理}

	// ~~~~~~~~~~~~~~ 长度下数据
	SSCISPI_DateStruct.checkvalue=0;
	for(i=0;i<SSCISPI_DateStruct.length;i++)
	{
		SSCISPI_DateStruct.buffer[i]=SPI_ReceiveData();	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
		SSCISPI_DateStruct.checkvalue+=SSCISPI_DateStruct.buffer[i];
	}
	// 设计返回校验和验证
	if(SPI_SendAndReceive_Data(0x00)!=SSCISPI_DateStruct.checkvalue)
	{
		// 如果配置SS的控制，进行置高的失能
		// todo
		return (0);
	}
	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
	//######################## 写入样例demo
	SSCISPI_DateStruct.Command=0x00;  // bit7 1读 0写

	// 格式化命令
		SSCISPI_DateStruct.checkvalue=SSCISPI_DateStruct.head;
	SPI_SendData(SSCISPI_DateStruct.head);	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
		SSCISPI_DateStruct.checkvalue+=SSCISPI_DateStruct.Command;
	SPI_SendData(SSCISPI_DateStruct.Command);	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
		SSCISPI_DateStruct.checkvalue+=SSCISPI_DateStruct.length;
	SPI_SendData(SSCISPI_DateStruct.length);	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
	// 长度下数据
	for(i=0;i<SSCISPI_DateStruct.length;i++)
	{
		SSCISPI_DateStruct.checkvalue+=SSCISPI_DateStruct.buffer[i];
		SPI_SendData(SSCISPI_DateStruct.buffer[i]);	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
	}
	// 校验和,样例实时校验不一定觉得从机执行，除非协议设计额外读取校验和值【前面校验正确，最后的校验和从机接收错误不执行命令】
	if(SPI_SendAndReceive_Data(SSCISPI_DateStruct.checkvalue)!=SSCISPI_DateStruct.checkvalue)
	{
		// 如果配置SS的控制，进行置高的失能
		// todo

		return (0);
	}
	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
	// 设计写模式，样例从机实时返回校验值，支持最后多返回一个校验，即 这里判断完整性较佳
	 if(SPI_SendAndReceive_Data(0x00)!=SSCISPI_DateStruct.checkvalue)
	 {
			// 如果配置SS的控制，进行置高的失能
			// todo

			return (0);
	 }
	Delay_Wait_CLK_Sample(50); // 给予从机响应时间
	//########################
	// 如果配置SS的控制，进行置高的失能
	// todo

	return (1);
}


//***********************************************************************************
//主函数
//***********************************************************************************



void delay_ms(uint16 msset)
{ // 具体根据时钟进行调整参数，这里仅样例示例延时
	volatile uint8 i;
	while(msset--)
	{
		i=200;
		while(i--)
		{
			_CWDT_();
		}
	}
}


void main()
{
	SCLKCTL=0x78;
	//SPI初始化
	SPI_IS_MASTER=1;    //0：从机配置，1：主机配置
	// 主从模式配置
	if(!SPI_IS_MASTER)
	{//从机配置
		SSCISPI_DateStruct.buffer[0]='A';
		SSCISPI_DateStruct.buffer[1]='B';
		SSCISPI_DateStruct.buffer[2]='C';
		SPI_Slave_Mode_Configuration();
	}
	else
	{//主机配置
		SPI_Master_Mode_Configuration();
		delay_ms(10);  // 建议延时使从机充分就位
	}

	//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
	AIE=1;PUIE=1;
	TR91=0x00;P9LR1=0x00;	// 样例指示引脚
	TR93=0x00;P9LR3=0x00;	// 样例指示引脚
	while(1)
	{// 注：调试时第二次循环指向上一行代码，此为正常现象，是因为嵌汇编的XX指令在KF工具中没有行号，被属于需上一行代码的尾指令。
		_CWDT_();  // 周期性清理，如果代码执行时间超过看门狗的周期，应该在代码中插入清狗指令。

		//从机样例 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
		if(!SPI_IS_MASTER)
		{
			if(SPI_SLAVE_ANSWERED)
			{
				SPI_SLAVE_ANSWERED=0;
				P9LR1=!P9LR1;  // 样例观察引脚
			}
		}
		//主机样例 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
		if(SPI_IS_MASTER)
		{
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			if(SPI_Master_ISR_Simulate()) // 从机基于中断接收响应主机，主机调用收发函数完成协议传输，具体协议根据产品自行设计
			{
				P9LR3=!P9LR3; // 样例观察引脚
			}
			else
			{
				P9LR1=!P9LR1; // 样例观察引脚
			}
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		}
		//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
		delay_ms(1000);

	}
}
//中断函数0:0X04入口地址
void int_fun0() __interrupt (0)
{
	// 从机
	if(SSCI1IE && SSCI1IF)
	{
		SSCI1IF=0;
		SPI_Slave_ISR_Server();// 如果需要 方法使用标记   如 SSCI_SPI_State.bit1=1;
	}
}


//中断函数1:0x14入口地址
void int_fun1() __interrupt (1)
{

}
