/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2023. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */
/******************************************************************************
 * @version: V1.0.0 demo
 ******************************************************************************/

/*!
* @file spi_w25x16.c
*
* @brief This file is the library function of spi_w25x16
*
*/

/* ===========================================  Includes  =========================================== */
#include "spi_master_drv.h"
#include "port_hw.h"
#include "gpio_hw.h"
#include "spi_hw.h"
#include "spi_w25x16.h"
#include "Dma_Config.h"

/* ============================================  Define  ============================================ */
#define SPI_INSTANCE        (0U)
#define SPI_IRQ             (SPI0_IRQn)
#define PORT_SPI_SCK        (PORTB)
#define PORT_SPI_CS         (PORTB)
#define PORT_SPI_SIN        (PORTB)
#define PORT_SPI_SOUT       (PORTB)
#define PIN_SPI_SCK         (2U)
#define PIN_SPI_CS          (0U)
#define PIN_SPI_SIN         (3U)
#define PIN_SPI_SOUT        (1U)

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
/*! @brief SPI master configure parameter */
spi_master_config_t spiMasteConfig =
{
    .bitsPerSec = 2000000UL,                    /*! 设置SPI波特率*/
    .whichPcs = SPI_PCS0,                       /*! 设置SPI片选引脚*/
    .pcsPolarity = SPI_ACTIVE_LOW,              /*! 设置SPI片选低有效*/
    .isPcsContinuous = true,                    /*! 设置片选连续模式*/
    .bitcount = 8UL,                            /*! SPI 帧长度,支持 4~32 bits*/
    .spiSrcClk = 60000000UL,                    /*! SPI时钟源频率*/
    .clkPhase = SPI_CLOCK_PHASE_2ND_EDGE,       /*! 设置数据采样相位,第1个边沿采样数据*/
    .clkPolarity = SPI_SCK_ACTIVE_LOW,          /*! 设置SCK工作时极性,工作时SCK为高*/
    .msbFirst = true,                           /*! 选择从最高位开始传输*/
    .transferType = SPI_USING_DMA,              /*! 选择通信类型为DMA方式*/
    .rxDMAChannel = DMA_SPIRX_CHANNEL,          /*! 设置SPI DMA接收通道*/
    .txDMAChannel = DMA_SPITX_CHANNEL,          /*! 设置SPI DMA发送通道*/
    .callback = NULL,                           /*! 注册回调函数 */
    .callbackParam = NULL,                      /*! 回调函数参数 */
};

spi_state_t s_spiMasterState;

uint8_t g_flshTstDataBuf[W25X16_TEST_DAT_LEN];
/* ====================================  Functions declaration  ===================================== */
static void W25X16_EnableWr(void);
static uint8_t W25X16_IsBusy(void);

/* ======================================  Functions define  ======================================== */

/*!
 * @brief SPI模块初始化配置，主机模式
 *
 * @param none
 *
 * @return none
 */
void SPI_Master_Init(void)
{
    GPIO_DRV_SetMuxModeSel(PORT_SPI_SCK, PIN_SPI_SCK, PORT_MUX_ALT3);        /*! SCK */
    GPIO_DRV_SetMuxModeSel(PORT_SPI_SOUT, PIN_SPI_SOUT, PORT_MUX_ALT3);      /*! SOUT */
    GPIO_DRV_SetMuxModeSel(PORT_SPI_SIN, PIN_SPI_SIN, PORT_MUX_ALT3);        /*! SIN */
    GPIO_DRV_SetMuxModeSel(PORT_SPI_CS, PIN_SPI_CS, PORT_MUX_ALT3);          /*! CS */

    SPI_DRV_MasterInit(SPI_INSTANCE, &s_spiMasterState, &spiMasteConfig);    /*! SPI功主机模式初始化 */
    
    SPI_SetPinConfigMode(SPI0, SPI_SOUT_MISO_SIN_MOSI);
}

/**
* @brief    enable write W25X16.
*
* @param[in] void
* @return   void
*
*/
static void W25X16_EnableWr(void)
{
    /* First judge whether the w25X16 is busy, and only when it is free can it be operated */
    while(W25X16_IsBusy());
    g_SpiWrDataBuf[0] = W25X16_CMD_WR_EN;
    
    SPI_DRV_MasterTransfer(SPI_INSTANCE, g_SpiWrDataBuf, g_SpiRdDataBuf, 1);   /*! SPI通信 */
    while (SPI_DRV_MasterGetTransferStatus(SPI_INSTANCE, NULL) == STATUS_BUSY);    /*! 等待传输完成 */
}

/**
* @brief  Determine whether w25x16 is busy
*
* @param[in] void
* @return   0->Not busy. 1->Busy.
*
*/
static uint8_t W25X16_IsBusy(void)
{
    g_SpiWrDataBuf[0] = W25X16_CMD_RD_STS;

    SPI_DRV_MasterTransfer(SPI_INSTANCE, g_SpiWrDataBuf, g_SpiRdDataBuf, 2);   /*! SPI通信 */
    while (SPI_DRV_MasterGetTransferStatus(SPI_INSTANCE, NULL) == STATUS_BUSY);    /*! 等待传输完成 */
    
    return (g_SpiRdDataBuf[1] & W25X16_BUSY_MSK);
}

/**
* @brief    erase W25X16.
*
* @param[in] startAddr:  start Addr
* @param[in] secErNum: sector number
* @return   void
*
*/
void W25X16_ErsSector(uint32_t startAddr, uint8_t secErNum)
{
    uint8_t  ii;
    
    if ((startAddr + secErNum * W25X16_SEC_SIZE) > W25X16_ADDR_MAX){return;}
    
    for (ii = 0; ii < secErNum; ii++)
    {
        /*使能写,在擦除、写入后自动关闭写.*/
        W25X16_EnableWr();
        
        /*装载擦除指令,并发送指令.*/
        g_SpiWrDataBuf[0] = W25X16_CMD_ER_SEC;
        g_SpiWrDataBuf[1] = (startAddr >> 16);
        g_SpiWrDataBuf[2] = (startAddr >>  8);
        g_SpiWrDataBuf[3] = (startAddr >>  0);
        
        SPI_DRV_MasterTransfer(SPI_INSTANCE, g_SpiWrDataBuf, g_SpiRdDataBuf, 4);   /*! SPI通信 */
        while (SPI_DRV_MasterGetTransferStatus(SPI_INSTANCE, NULL) == STATUS_BUSY);    /*! 等待传输完成 */
        
        startAddr += W25X16_SEC_SIZE;
    }
}

/**
* @brief    Write specified length byte data to w25x16
*
* @param[in] startAddr: start Addr
* @param[in] pDataBuf:  data buffer
* @param[in] dataLen:   data len
* @return   void
*
*/
void W25X16_WriteBytes(uint32_t startAddr, uint8_t *pDataBuf, uint16_t dataLen)
{
    uint8_t ii,restLen, wrTimes;
    
    if ((startAddr + (dataLen - 1)) > W25X16_ADDR_MAX){return;}
    
    restLen = (dataLen % 256);  /* Less than 256 bytes in length */
    wrTimes = (dataLen / 256);  /* Write up to 256 bytes at a time */
    
    /* Satisfy 256 bytes of data write */
    for (ii = 0; ii < wrTimes; ii++)
    {
        /* enable write. */
        W25X16_EnableWr();
        
        /* write data. */
        g_SpiWrDataBuf[0] = W25X16_CMD_WR_DAT;
        g_SpiWrDataBuf[1] = (startAddr >> 16);
        g_SpiWrDataBuf[2] = (startAddr >> 8);
        g_SpiWrDataBuf[3] = (startAddr >> 0);
        memcpy(&g_SpiWrDataBuf[4], &pDataBuf[ii * 256], 256); /* A single write up to 256 bytes */

        SPI_DRV_MasterTransfer(SPI_INSTANCE, g_SpiWrDataBuf, g_SpiRdDataBuf, (256 + 4));   /*! SPI通信 */
        while (SPI_DRV_MasterGetTransferStatus(SPI_INSTANCE, NULL) == STATUS_BUSY);    /*! 等待传输完成 */
        
        startAddr += 256;
        OSIF_TimeDelay(1); /* Need to delay a few ms */
    }
    /* Write less than 256 bytes of data */
    if (restLen > 0)
    {
        /* enable write */
        W25X16_EnableWr();
        
        /* write data. */
        g_SpiWrDataBuf[0] = W25X16_CMD_WR_DAT;
        g_SpiWrDataBuf[1] = (startAddr >> 16);
        g_SpiWrDataBuf[2] = (startAddr >> 8);
        g_SpiWrDataBuf[3] = (startAddr >> 0);
        memcpy(&g_SpiWrDataBuf[4], &pDataBuf[wrTimes * 256], restLen);
        
        SPI_DRV_MasterTransfer(SPI_INSTANCE, g_SpiWrDataBuf, g_SpiRdDataBuf, (restLen + 4));   /*! SPI通信 */
        while (SPI_DRV_MasterGetTransferStatus(SPI_INSTANCE, NULL) == STATUS_BUSY);    /*! 等待传输完成 */
        
        OSIF_TimeDelay(1); /* Need to delay a few ms */
    }
}

/**
* @brief   Read specified length byte data from w25x16
*
* @param[in] startAddr: start addr
* @param[in] pDataBuf:  data buffer
* @param[in] dataLen:   data len
* @return   void
*
*/
void W25X16_ReadBytes(uint32_t startAddr, uint8_t *pDataBuf, uint16_t dataLen)
{
    while(W25X16_IsBusy());
    g_SpiWrDataBuf[0] = W25X16_CMD_RD_DAT;
    g_SpiWrDataBuf[1] = (startAddr >> 16);
    g_SpiWrDataBuf[2] = (startAddr >>  8);
    g_SpiWrDataBuf[3] = (startAddr >>  0);
    
    SPI_DRV_MasterTransfer(SPI_INSTANCE, g_SpiWrDataBuf, g_SpiRdDataBuf, (dataLen + 4));   /*! SPI通信 */
    while (SPI_DRV_MasterGetTransferStatus(SPI_INSTANCE, NULL) == STATUS_BUSY);    /*! 等待传输完成 */

    memcpy(pDataBuf, &g_SpiRdDataBuf[4], dataLen);
    
    OSIF_TimeDelay(1);  /* Need to delay a few ms */
}

/**
* @prototype W25X16_ReadDataTest(void)
*
* @param[in] void
* @return	 void
*
* @brief  	 ??W25X16????.
*/
void W25X16_ReadDataTest(void)
{
    memset(g_SpiRdDataBuf, 0x00, W25X16_DATA_BUF_LEN);
    memset(g_flshTstDataBuf, 0x00, W25X16_TEST_DAT_LEN);
    
    W25X16_ReadBytes(W25X16_WRRD_DAT_ADDR, g_flshTstDataBuf, W25X16_TEST_DAT_LEN);

    printf("read: ");
    for(uint16_t ii = 0; ii < W25X16_TEST_DAT_LEN; ii++)
    {
        printf("%d ", g_flshTstDataBuf[ii]);
    }
}

/**
* @prototype W25X16_WriteDataTest(void)
*
* @param[in] void
* @return    void
*
* @brief    ??W25X16???.
*/
void W25X16_WriteDataTest(void)
{
    W25X16_ErsSector(W25X16_WRRD_DAT_ADDR, 1);
    
    memset(g_SpiWrDataBuf, 0x00, W25X16_DATA_BUF_LEN);
    memset(g_flshTstDataBuf, 0x00, W25X16_TEST_DAT_LEN);
    
    printf("write: ");
    for (uint16_t ii = 0; ii < W25X16_TEST_DAT_LEN; ii++)
    {
        g_flshTstDataBuf[ii] = ii;
        printf("%d ", g_flshTstDataBuf[ii]);
    }
    printf("\r\n");

    W25X16_WriteBytes(W25X16_WRRD_DAT_ADDR, g_flshTstDataBuf, W25X16_TEST_DAT_LEN);
}

/**
* @prototype W25X16_ReadDeviceID(void)
*
* @param[in] void
* @return    void
*
* @brief    ??W25X16???.
*/
void W25X16_ReadDeviceID(void)
{
    uint16_t Device_ID = 0x0;
    
    memset(g_SpiWrDataBuf, 0x00, W25X16_DATA_BUF_LEN);
    memset(g_flshTstDataBuf, 0x00, W25X16_TEST_DAT_LEN);

    /* enable write. */
    W25X16_EnableWr();
    
    /* write data. */
    g_SpiWrDataBuf[0] = W25X16_CMD_DEVICEID;
    g_SpiWrDataBuf[1] = 0x00;
    g_SpiWrDataBuf[2] = 0x00;
    g_SpiWrDataBuf[3] = 0x00;

    SPI_DRV_MasterTransfer(SPI_INSTANCE, g_SpiWrDataBuf, g_SpiRdDataBuf, (2 + 4));   /*! SPI通信 */
    while (SPI_DRV_MasterGetTransferStatus(SPI_INSTANCE, NULL) == STATUS_BUSY);    /*! 等待传输完成 */
    
    OSIF_TimeDelay(1); /* Need to delay a few ms */
    
    Device_ID = ((g_SpiRdDataBuf[4] << 8) | g_SpiRdDataBuf[5]);

    printf("0x%x\r\n", Device_ID);

}

/*!
* @brief  Communication test of W25x16
*
* @param[in] none
* @return none
*/
void W25X16_Test(void)
{
    W25X16_ReadDataTest();
    OSIF_TimeDelay(100);
    W25X16_WriteDataTest();
    OSIF_TimeDelay(100);
}
/* =============================================  EOF  ============================================== */

