#include "fmc.h"
#include "bsp_sdram.h"

extern SDRAM_HandleTypeDef hsdram1;
static FMC_SDRAM_CommandTypeDef Command;
static SDRAM_HandleTypeDef *sdramHandle = &hsdram1;

MY_SDRAM_TypeDef my_sdram ={  
	SDRAM_Init,
	SDRAM_Test,
};

static void SDRAM_delay(__IO uint32_t nCount)
{
	__IO uint32_t index = 0;
	for (index = (100000 * nCount); index != 0; index--)
	{
	}
}

void SDRAM_Init()
{
	// MX_FMC_Init();
	SDRAM_Initialization_Sequence(sdramHandle);
}

void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram)
{

	uint32_t temp = 0;
	// SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAM
	SDRAM_Send_Cmd(2, FMC_SDRAM_CMD_CLK_ENABLE, 1, 0); // 时钟配置使能
	HAL_Delay(1);
	// delay_us(500);                                  //至少延时200us
	SDRAM_Send_Cmd(2, FMC_SDRAM_CMD_PALL, 1, 0);			 // 对所有存储区预充电
	SDRAM_Send_Cmd(2, FMC_SDRAM_CMD_AUTOREFRESH_MODE, 8, 0); // 设置自刷新次数
	// 配置模式寄存器,SDRAM的bit0~bit2为指定突发访问的长度，
	// bit3为指定突发访问的类型，bit4~bit6为CAS值，bit7和bit8为运行模式
	// bit9为指定的写突发模式，bit10和bit11位保留位
	temp = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 |		 // 设置突发长度:1(可以是1/2/4/8)
		   SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |		 // 设置突发类型:连续(可以是连续/交错)
		   SDRAM_MODEREG_CAS_LATENCY_3 |				 // 设置CAS值:3(可以是2/3)
		   SDRAM_MODEREG_OPERATING_MODE_STANDARD |		 // 设置操作模式:0,标准模式
		   SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;		 // 设置突发写模式:1,单点访问
	SDRAM_Send_Cmd(2, FMC_SDRAM_CMD_LOAD_MODE, 1, temp); // 设置SDRAM的模式寄存器

	// 刷新频率计数器(以SDCLK频率计数),计算方法:
	// COUNT=SDRAM刷新周期/行数-20=SDRAM刷新周期(us)*SDCLK频率(Mhz)/行数
	// 我们使用的SDRAM刷新周期为64ms,SDCLK=216/2=108Mhz,行数为8192(2^13).
	// 所以,COUNT=64*1000*108/8192-20=823
	//  168 MHz   COUNT=64*1000*84 /8192-20= 3656
	HAL_SDRAM_ProgramRefreshRate(sdramHandle, 823);
}

// 向SDRAM发送命令
// bankx:2,向BANK2上面的SDRAM发送指令  FMC_SDRAM_CMD_TARGET_BANK2  SDCKE1+SDNE1
//       1,向BANK1上面的SDRAM发送指令  FMC_SDRAM_CMD_TARGET_BANK1  SDCKE0+SDNE0
// cmd:指令(0,正常模式/1,时钟配置使能/2,预充电所有存储区/3,自动刷新/4,加载模式寄存器/5,自刷新/6,掉电)
// refresh:自刷新次数
// regval:模式寄存器的定义
// 返回值:0,正常;1,失败.
uint8_t SDRAM_Send_Cmd(uint8_t bankx, uint8_t cmd, uint8_t refresh, uint16_t regval)
{
	uint32_t target_bank = 0;
	FMC_SDRAM_CommandTypeDef Command;

	if (bankx == 1)
		target_bank = FMC_SDRAM_CMD_TARGET_BANK1;
	else if (bankx == 2)
		target_bank = FMC_SDRAM_CMD_TARGET_BANK2;
	Command.CommandMode = cmd;											// 命令
	Command.CommandTarget = target_bank;								// 目标SDRAM存储区域
	Command.AutoRefreshNumber = refresh;								// 自刷新次数
	Command.ModeRegisterDefinition = regval;							// 要写入模式寄存器的值
	if (HAL_SDRAM_SendCommand(sdramHandle, &Command, 0X1000) == HAL_OK) // 向SDRAM发送命令
	{
		return 0;
	}
	else
		return 1;
}

// 在指定地址(WriteAddr+Bank5_SDRAM_ADDR)开始,连续写入n个字节.
// pBuffer:字节指针
// WriteAddr:要写入的地址
// n:要写入的字节数
void FMC_SDRAM_WriteBuffer(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t n)
{
	for (; n != 0; n--)
	{
		*(volatile unsigned char *)(Bank4_SDRAM_ADDR + WriteAddr) = *pBuffer;
		WriteAddr++;
		pBuffer++;
	}
}

// 在指定地址((WriteAddr+Bank5_SDRAM_ADDR))开始,连续读出n个字节.
// pBuffer:字节指针
// ReadAddr:要读出的起始地址
// n:要写入的字节数
void FMC_SDRAM_ReadBuffer(uint8_t *pBuffer, uint32_t ReadAddr, uint32_t n)
{
	for (; n != 0; n--)
	{
		*pBuffer++ = *(volatile unsigned char *)(Bank4_SDRAM_ADDR + ReadAddr);
		ReadAddr++;
	}
}

uint8_t SDRAM_Test(uint32_t count)
{
	/*写入数据计数器*/
	uint32_t counter = 0;

	uint8_t ubWritedata_8b = 0, ubReaddata_8b = 0;
	uint16_t uhWritedata_16b = 0, uhReaddata_16b = 0;

	/*按8位格式读写数据，并校验*/
	for (counter = 0x00; counter < count; counter++)
	{
		*(__IO uint8_t *)(Bank4_SDRAM_ADDR + counter) = (uint8_t)0x0;
	}

	/* 向整个SDRAM写入数据  8位 */
	for (counter = 0; counter < count; counter++)
	{
		*(__IO uint8_t *)(Bank4_SDRAM_ADDR + counter) = (uint8_t)(ubWritedata_8b + counter);
	}

	/* 读取 SDRAM 数据并检测*/
	for (counter = 0; counter < count; counter++)
	{
		ubReaddata_8b = *(__IO uint8_t *)(Bank4_SDRAM_ADDR + counter); // 从该地址读出数据

		if (ubReaddata_8b != (uint8_t)(ubWritedata_8b + counter)) // 检测数据，若不相等，跳出函数,返回检测失败结果。
		{
			// SDRAM_ERROR("8位数据读写错误！");
			return 0;
		}
	}

	/*按16位格式读写数据，并检测*/

	/* 把SDRAM数据全部重置为0 */
	for (counter = 0x00; counter < count / 2; counter++)
	{
		*(__IO uint16_t *)(Bank4_SDRAM_ADDR + 2 * counter) = (uint16_t)0x00;
	}

	/* 向整个SDRAM写入数据  16位 */
	for (counter = 0; counter < count / 2; counter++)
	{
		*(__IO uint16_t *)(Bank4_SDRAM_ADDR + 2 * counter) = (uint16_t)(uhWritedata_16b + counter);
	}

	/* 读取 SDRAM 数据并检测*/
	for (counter = 0; counter < count / 2; counter++)
	{
		uhReaddata_16b = *(__IO uint16_t *)(Bank4_SDRAM_ADDR + 2 * counter); // 从该地址读出数据

		if (uhReaddata_16b != (uint16_t)(uhWritedata_16b + counter)) // 检测数据，若不相等，跳出函数,返回检测失败结果。
		{
			// SDRAM_ERROR("16位数据读写错误！");

			return 0;
		}
	}

	/*检测正常，return 1 */
	return 1;
}