未验证 提交 20067853 编写于 作者: W Wayne 提交者: GitHub

[bsp/nuvoton] Upload missing files. (#6052)

* [bsp/nuvoton] Upload missing files.
Co-authored-by: NWayne Lin <wclin@nuvoton.com>
上级 c16eaf55
/**************************************************************************//**
* @file nu_trng.h
* @version V3.00
* @brief TRNG driver header file
*
* @copyright SPDX-License-Identifier: Apache-2.0
* @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
******************************************************************************/
#ifndef __NU_TRNG_H__
#define __NU_TRNG_H__
#ifdef __cplusplus
extern "C"
{
#endif
/** @addtogroup Standard_Driver Standard Driver
@{
*/
/** @addtogroup TRNG_Driver TRNG Driver
@{
*/
/** @addtogroup M460_TRNG_EXPORTED_MACROS TRNG Exported Macros
@{
*/
/*----------------------------------------------------------------------------------------------*/
/* Macros */
/*----------------------------------------------------------------------------------------------*/
/**
* @brief Let TRNG engine know the currrent PCLK frequency. The CLKPSC is the peripheral
* clock frequency range for the selected value , the CLKPSC setting must be higher
* than or equal to the actual peripheral clock frequency (for correct random generation).
* @param clkpsc 0: PCLK is 80~100 MHz
* 1: PCLK is 60~80 MHz
* 2: PCLK is 50~60 MHz
* 3: PCLK is 40~50 MHz
* 4: PCLK is 30~40 MHz
* 5: PCLK is 25~30 MHz
* 6: PCLK is 20~25 MHz
* 7: PCLK is 15~20 MHz
* 8: PCLK is 12~15 MHz
* 9: PCLK is 9~12 MHz
*
* @return None
* \hideinitializer
*/
#define TRNG_SET_CLKP(clkpsc) do { TRNG->CTL = (TRNG->CTL&~TRNG_CTL_CLKP_Msk)|((clkpsc & 0xf)<<TRNG_CTL_CLKP_Pos); } while(0);
/*@}*/ /* end of group M460_TRNG_EXPORTED_MACROS */
/** @addtogroup TRNG_EXPORTED_FUNCTIONS TRNG Exported Functions
@{
*/
/*---------------------------------------------------------------------------------------------------------*/
/* Functions */
/*---------------------------------------------------------------------------------------------------------*/
int32_t TRNG_Open(void);
int32_t TRNG_GenWord(uint32_t *u32RndNum);
int32_t TRNG_GenBignum(uint8_t u8BigNum[], int32_t i32Len);
int32_t TRNG_GenBignumHex(char cBigNumHex[], int32_t i32Len);
/*@}*/ /* end of group TRNG_EXPORTED_FUNCTIONS */
/*@}*/ /* end of group TRNG_Driver */
/*@}*/ /* end of group Standard_Driver */
#ifdef __cplusplus
}
#endif
#endif /* __NU_TRNG_H__ */
/**************************************************************************//**
* @file trng.c
* @version V3.00
* @brief M460 series TRNG driver source file
*
* @copyright SPDX-License-Identifier: Apache-2.0
* @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include "NuMicro.h"
/** @addtogroup Standard_Driver Standard Driver
@{
*/
/** @addtogroup TRNG_Driver TRNG Driver
@{
*/
/** @addtogroup TRNG_EXPORTED_FUNCTIONS TRNG Exported Functions
@{
*/
/**
* @brief Initialize TRNG hardware.
* @return TRNG hardware enable success or failed.
* @retval 0 Success
* @retval -1 Time-out. TRNG hardware may not be enabled.
*/
int32_t TRNG_Open(void)
{
uint32_t u32TimeOutCount = SystemCoreClock; /* 1 second time-out */
SYS->IPRST1 |= SYS_IPRST1_TRNGRST_Msk;
SYS->IPRST1 ^= SYS_IPRST1_TRNGRST_Msk;
TRNG->CTL |= TRNG_CTL_TRNGEN_Msk;
TRNG->ACT |= TRNG_ACT_ACT_Msk;
/* Waiting for ready */
while ((TRNG->CTL & TRNG_CTL_READY_Msk) == 0)
{
if (--u32TimeOutCount == 0) return -1; /* Time-out error */
}
return 0;
}
/**
* @brief Generate a 32-bits random number word.
* @param[out] u32RndNum The output 32-bits word random number.
*
* @return Success or time-out.
* @retval 0 Success
* @retval -1 Time-out. TRNG hardware may not be enabled.
*/
int32_t TRNG_GenWord(uint32_t *u32RndNum)
{
uint32_t i, u32Reg, timeout;
*u32RndNum = 0;
u32Reg = TRNG->CTL;
for (i = 0; i < 4; i++)
{
TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg;
/* TRNG should generate one byte per 125*8 us */
for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--)
{
if (TRNG->CTL & TRNG_CTL_DVIF_Msk)
break;
}
if (timeout == 0)
return -1;
*u32RndNum |= ((TRNG->DATA & 0xff) << i * 8);
}
return 0;
}
/**
* @brief Generate a big number in binary format.
* @param[out] u8BigNum The output big number.
* @param[in] i32Len Request bit length of the output big number. It must be multiple of 8.
*
* @return Success or time-out.
* @retval 0 Success
* @retval -1 Time-out. TRNG hardware may not be enabled.
*/
int32_t TRNG_GenBignum(uint8_t u8BigNum[], int32_t i32Len)
{
uint32_t i, u32Reg, timeout;
u32Reg = TRNG->CTL;
for (i = 0; i < i32Len / 8; i++)
{
TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg;
/* TRNG should generate one byte per 125*8 us */
for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--)
{
if (TRNG->CTL & TRNG_CTL_DVIF_Msk)
break;
}
if (timeout == 0)
return -1;
u8BigNum[i] = (TRNG->DATA & 0xff);
}
return 0;
}
/**
* @brief Generate a big number in hex format.
* @param[out] cBigNumHex The output hex format big number.
* @param[in] i32Len Request bit length of the output big number. It must be multiple of 8.
*
* @return Success or time-out.
* @retval 0 Success
* @retval -1 Time-out. TRNG hardware may not be enabled.
*/
int32_t TRNG_GenBignumHex(char cBigNumHex[], int32_t i32Len)
{
uint32_t i, idx, u32Reg, timeout;
uint32_t data;
u32Reg = TRNG->CTL;
idx = 0;
for (i = 0; i < i32Len / 8; i++)
{
TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg;
/* TRNG should generate one byte per 125*8 us */
for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--)
{
if (TRNG->CTL & TRNG_CTL_DVIF_Msk)
break;
}
if (timeout == 0)
return -1;
data = (TRNG->DATA & 0xff);
if (data >= 0xA0)
cBigNumHex[idx++] = ((data >> 4) & 0xf) - 10 + 'A';
else
cBigNumHex[idx++] = ((data >> 4) & 0xf) + '0';
data &= 0xf;
if (data >= 0xA)
cBigNumHex[idx++] = data - 10 + 'A';
else
cBigNumHex[idx++] = data + '0';
}
cBigNumHex[idx] = 0;
return 0;
}
/*@}*/ /* end of group TRNG_EXPORTED_FUNCTIONS */
/*@}*/ /* end of group TRNG_Driver */
/*@}*/ /* end of group Standard_Driver */
/**************************************************************************//**
*
* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-4-21 Wayne First version
*
******************************************************************************/
#include "rtconfig.h"
#if defined(BSP_USING_SDH)
#include <rtdevice.h>
#include <drivers/mmcsd_core.h>
#include <drivers/sdio.h>
#include "NuMicro.h"
#define LOG_TAG "drv.sdh"
#undef DBG_ENABLE
#define DBG_SECTION_NAME LOG_TAG
#define DBG_LEVEL LOG_LVL_ASSERT
#define DBG_COLOR
#include <rtdbg.h>
#define SDH_ALIGN_LEN 4
#define SDH_BUFF_SIZE 512
#define SDH_BLOCK_SIZE 512
enum
{
SDH_START = -1,
#if defined(BSP_USING_SDH0)
SDH0_IDX,
#endif
#if defined(BSP_USING_SDH1)
SDH1_IDX,
#endif
SDH_CNT
};
struct nu_sdh
{
struct rt_mmcsd_host *host;
char *name;
SDH_T *base;
IRQn_Type irqn;
uint32_t rstidx;
uint32_t modid;
uint8_t *cachebuf;
uint32_t u32CmdResp0;
uint32_t u32CmdResp1;
uint32_t u32CurClk;
rt_tick_t LastNotice;
};
typedef struct nu_sdh *nu_sdh_t;
/* Private variables ------------------------------------------------------------*/
static struct nu_sdh nu_sdh_arr [] =
{
#if defined(BSP_USING_SDH0)
{
.name = "sdh0",
.base = SDH0,
.irqn = SDH0_IRQn,
.rstidx = SDH0_RST,
.modid = SDH0_MODULE,
.cachebuf = RT_NULL,
},
#endif
#if defined(BSP_USING_SDH1)
{
.name = "sdh1",
.base = SDH1,
.irqn = SDH1_IRQn,
.rstidx = SDH1_RST,
.modid = SDH1_MODULE,
.cachebuf = RT_NULL,
},
#endif
}; /* struct nu_sdh nu_sdh_arr [] */
#define SDH_SetClock SDH_Set_clock
static int SDH_SetBusWidth(SDH_T *sdh, uint32_t bw)
{
if (bw == 4)
{
sdh->CTL |= SDH_CTL_DBW_Msk;
}
else if (bw == 1)
{
sdh->CTL &= ~SDH_CTL_DBW_Msk;
}
else
{
goto exit_SDH_SetBusWidth;
}
return 0;
exit_SDH_SetBusWidth:
return -1;
}
static int SDH_GetBusStatus(SDH_T *sdh, uint32_t mask)
{
int cnt = 0x100000;
while (cnt-- > 0)
{
sdh->CTL |= SDH_CTL_CLK8OEN_Msk;
while (sdh->CTL & SDH_CTL_CLK8OEN_Msk) { }
if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk))
break;
}
return (cnt == 0) ? -1 : 0 ;
}
static int SDH_GetCD(SDH_T *sdh)
{
int i32CD = 0;
if ((sdh->GCTL & SDH_GCTL_SDEN_Msk) == SDH_GCTL_SDEN_Msk)
{
if ((sdh->INTEN & SDH_INTEN_CDSRC_Msk) == SDH_INTEN_CDSRC_Msk) /* Card detect pin from GPIO */
{
i32CD = (sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) ? 0 : 1;
}
else /* Card detect pin from DAT3 mode */
{
__IO uint32_t i;
sdh->CTL |= SDH_CTL_CLKKEEP_Msk;
for (i = 0ul; i < 5000ul; i++) { }
i32CD = ((sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) == SDH_INTSTS_CDSTS_Msk) ? 1 : 0;
sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk;
}
}
return i32CD;
}
static void SDH_Enable(SDH_T *sdh)
{
/* Reset sdh and its DMA engine at first. */
sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk;
while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { }
sdh->DMACTL = SDH_DMACTL_DMAEN_Msk;
sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag
sdh->GCTL = SDH_GCTL_GCTLRST_Msk;
while ((sdh->GCTL & SDH_GCTL_GCTLRST_Msk) == SDH_GCTL_GCTLRST_Msk) { }// clear all interrupt flag
sdh->GINTSTS = SDH_GINTSTS_DTAIF_Msk;
sdh->GCTL = SDH_GCTL_SDEN_Msk;
sdh->CTL |= SDH_CTL_CTLRST_Msk;
while ((sdh->CTL & SDH_CTL_CTLRST_Msk) == SDH_CTL_CTLRST_Msk) { }
sdh->INTSTS = 0xFFFFFFFF; // clear all interrupt flag
sdh->INTEN |= SDH_INTEN_CDSRC_Msk;
sdh->INTEN |= SDH_INTEN_CDIEN_Msk;
}
/**
* @brief This function get command responding.
* @param sdh SDH instance
* @param cmd rt_mmcsd_cmd
* @retval none
*/
static void nu_sdh_sendcmd_done(SDH_T *sdh, struct rt_mmcsd_cmd *cmd)
{
if (resp_type(cmd) == RESP_R2)
{
uint8_t *c = (uint8_t *)&sdh->FB[0];
int i, j, tmp[5];
for (i = 0, j = 0; j < 5; i += 4, j++)
{
tmp[j] = (*(c + i) << 24) | (*(c + i + 1) << 16) | (*(c + i + 2) << 8) | (*(c + i + 3));
}
for (i = 0; i < 4; i++)
{
cmd->resp[i] = ((tmp[i] & 0x00ffffff) << 8) |
((tmp[i + 1] & 0xff000000) >> 24);
}
}
else
{
cmd->resp[0] = (sdh->RESP0 << 8) | (sdh->RESP1 & 0xff);
cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0;
}
}
/**
* @brief This function wait data-sending/receiving.
* @param sdh SDH instance
* @param data rt_mmcsd_data
* @retval error code
*/
static int nu_sdh_xfer_data(SDH_T *sdh, struct rt_mmcsd_data *data)
{
while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk)) { }
SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk);
if (data->flags & DATA_DIR_WRITE)
{
sdh->CTL |= SDH_CTL_CLKKEEP_Msk;
while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk)) { }
sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk;
}
return 0;
}
/**
* @brief This function send command and wait its response.
* @param host rt_mmcsd_host
* @param cmd rt_mmcsd_cmd
* @param data rt_mmcsd_data
* @retval error code
*/
static int nu_sdh_sendcmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd, struct rt_mmcsd_data *data)
{
int ret;
nu_sdh_t NuSdh = (nu_sdh_t)host->private_data;
SDH_T *sdh = NuSdh->base;
volatile uint32_t ctl = 0, tout = 0;
switch (host->io_cfg.bus_width)
{
case MMCSD_BUS_WIDTH_1:
ctl &= ~SDH_CTL_DBW_Msk;
break;
case MMCSD_BUS_WIDTH_4:
ctl |= SDH_CTL_DBW_Msk;
break;
case MMCSD_BUS_WIDTH_8:
default:
return -1;
}
/* Reset sdh and its DMA engine at first. */
sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk;
while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { }
sdh->DMACTL = SDH_DMACTL_DMAEN_Msk;
sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag
if (resp_type(cmd) != RESP_NONE)
{
if (resp_type(cmd) == RESP_R2)
{
ctl |= SDH_CTL_R2EN_Msk;
}
else
{
ctl |= SDH_CTL_RIEN_Msk;
}
tout = 0xFFFF;
}
/* Set SDNWR and BLK_CNT to 1 */
ctl |= ((9 << SDH_CTL_SDNWR_Pos) | (1 << SDH_CTL_BLKCNT_Pos));
ctl |= ((cmd->cmd_code << SDH_CTL_CMDCODE_Pos) | SDH_CTL_COEN_Msk);
/* Set Transfer mode regarding to data flag */
if (data != RT_NULL)
{
sdh->BLEN = data->blksize - 1;
if (data->blksize <= 0x200)
{
if (data->blks < 256)
{
ctl = (ctl & ~SDH_CTL_BLKCNT_Msk) | (data->blks << SDH_CTL_BLKCNT_Pos);
}
else
{
LOG_E("SD Max block transfer is 255!!");
}
}
if (data->flags & DATA_DIR_READ)
{
tout = 0xFFFFFF;
ctl |= SDH_CTL_DIEN_Msk; // Data-in
sdh->DMASA = (uint32_t)data->buf; // Read from dest
}
else if (data->flags & DATA_DIR_WRITE)
{
ctl |= SDH_CTL_DOEN_Msk; // Data-out
sdh->DMASA = (uint32_t)data->buf; // Write to dest
}
}
else if (resp_type(cmd) == RESP_R1B)
{
}
/* Clear response-timeout flag first for safty and reset new timeout value. */
SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk);
sdh->TOUT = tout;
/* Set argument and start a transaction. */
sdh->CMDARG = cmd->arg;
sdh->CTL = ctl;
/* Wait a command done. */
while ((sdh->CTL & (SDH_CTL_COEN_Msk)) == SDH_CTL_COEN_Msk) { }
if (resp_type(cmd) != RESP_NONE)
{
if (resp_type(cmd) == RESP_R2)
{
/* Wait to receive a response R2 from SD card and store the response data into DMC's Flash buffer (exclude CRC7). */
while (sdh->CTL & SDH_CTL_R2EN_Msk)
{
/* When get a Response timeout, break the polling. */
if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk))
{
ret = __LINE__;
goto exit_nu_sdh_sendcmd;
}
}
}
else
{
/* Wait to receive a response from SD card. */
while ((sdh->CTL & SDH_CTL_RIEN_Msk))
{
/* When get a Response timeout, break the polling. */
if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk))
{
ret = __LINE__;
goto exit_nu_sdh_sendcmd;
}
}
/* TOFIX: ISSUE: Sometimes, SDH's RIEN is cleared automatically by controller after host sending CMD5 to SD card. */
/* Workaround: To check previous cmd's response with CMD's. */
if (cmd->cmd_code == 5)
{
if ((NuSdh->u32CmdResp0 == sdh->RESP0) && (NuSdh->u32CmdResp1 == sdh->RESP1))
{
LOG_E("False CMD5-RESP issue occured.\n");
ret = __LINE__;
goto exit_nu_sdh_sendcmd;
}
}
NuSdh->u32CmdResp0 = sdh->RESP0;
NuSdh->u32CmdResp1 = sdh->RESP1;
}
/* Get response from FB or register */
nu_sdh_sendcmd_done(sdh, cmd);
}
if (data != RT_NULL)
{
/* Wait data processing done */
nu_sdh_xfer_data(sdh, data);
ret = SDH_GetBusStatus(sdh, 0);
if (ret)
{
LOG_E("ERROR: Busy %d\n", ret);
ret = __LINE__;
goto exit_nu_sdh_sendcmd;
}
}
/* Handle CRC flag */
if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk)) // Fault
{
uint32_t u32INTSTS = sdh->INTSTS;
SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk);
ret = __LINE__;
if ((resp_type(cmd) != RESP_R3) && (u32INTSTS & SDH_INTSTS_CRC7_Msk) == 0) //CRC7, Ignore R3
{
LOG_E("CRC7 error! (resp_type=%d)", resp_type(cmd));
goto exit_nu_sdh_sendcmd;
}
if ((u32INTSTS & SDH_INTSTS_CRC16_Msk) == 0) //CRC16
{
LOG_E("CRC16 error! (resp_type=%d)", resp_type(cmd));
goto exit_nu_sdh_sendcmd;
}
}
return 0;
exit_nu_sdh_sendcmd:
LOG_D("[%s %d] cmdid=%d error line=%d", __func__, __LINE__, cmd->cmd_code, ret);
cmd->resp[0] = cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0;
sdh->TOUT = 0;
SDH_Enable(sdh);
return -ret;
}
/**
* @brief This function send request.
* @param host rt_mmcsd_host
* @param req request
* @retval None
*/
static void nu_sdh_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
{
nu_sdh_t NuSdh;
SDH_T *sdh;
RT_ASSERT(host);
RT_ASSERT(req);
NuSdh = (nu_sdh_t)host->private_data;
sdh = NuSdh->base;
if (!SDH_GetCD(sdh)) // card is not present
{
LOG_E("Card is not present");
req->cmd->err = -RT_EIO;
goto exit_nu_sdh_request;
}
if (req->cmd != RT_NULL)
{
struct rt_mmcsd_cmd *cmd = req->cmd;
struct rt_mmcsd_data *data = req->data;
LOG_D("[%s%s%s%s%s]REQ: CMD:%d ARG:0x%08x RESP_TYPE:%d rw:%c addr:%08x, blks:%d, blksize:%d datalen:%d",
(host->card == RT_NULL) ? "Unknown" : "",
(host->card) && (host->card->card_type == CARD_TYPE_MMC) ? "MMC" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SD) ? "SD" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SDIO) ? "SDIO" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SDIO_COMBO) ? "SDIO_COMBO" : "",
cmd->cmd_code,
cmd->arg,
resp_type(cmd),
data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-',
data ? data->buf : 0,
data ? data->blks : 0,
data ? data->blksize : 0,
data ? data->blks * data->blksize : 0);
if (data != RT_NULL)
{
rt_uint32_t size;
rt_int32_t IsNonaligned = 0;
rt_uint32_t *org_data_buf = data->buf;
size = data->blksize * data->blks;
RT_ASSERT(org_data_buf);
IsNonaligned = (((rt_uint32_t)data->buf & (SDH_ALIGN_LEN - 1)) > 0) ? 1 : 0;
if (IsNonaligned)
{
/* Allocate memory temp buffer on demand. */
RT_ASSERT(size <= SDH_BUFF_SIZE);
if (NuSdh->cachebuf == RT_NULL)
{
NuSdh->cachebuf = rt_malloc_align(SDH_BUFF_SIZE, SDH_ALIGN_LEN);
RT_ASSERT(NuSdh->cachebuf);
}
data->buf = (rt_uint32_t *)NuSdh->cachebuf;
if (data->flags & DATA_DIR_WRITE)
{
LOG_D("Un-aligned, prepare into cache buf(%d)", size);
rt_memcpy(data->buf, org_data_buf, size);
}
}
cmd->err = nu_sdh_sendcmd(host, cmd, data);
if (!cmd->err && IsNonaligned)
{
if (data->flags & DATA_DIR_READ)
{
LOG_D("Un-aligned, restore from cache buf(%d)", size);
rt_memcpy(org_data_buf, data->buf, size);
}
}
data->buf = org_data_buf;
LOG_HEX("data.dest", 16, (void *)data->buf, size);
}
else
{
cmd->err = nu_sdh_sendcmd(host, cmd, NULL);
}
if (resp_type(cmd) != RESP_NONE)
LOG_HEX("cmd->resp", 16, (void *)&cmd->resp[0], 16);
}
if (req->stop != RT_NULL)
{
struct rt_mmcsd_cmd *stop = req->stop;
stop->err = nu_sdh_sendcmd(host, stop, NULL);
if (resp_type(stop) != RESP_NONE)
LOG_HEX("stop->resp", 16, (void *)&stop->resp[0], 16);
}
exit_nu_sdh_request:
mmcsd_req_complete(host);
}
/**
* @brief This function config.
* @param host rt_mmcsd_host
* @param io_cfg rt_mmcsd_io_cfg
* @retval None
*/
static void nu_sdh_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
{
nu_sdh_t NuSdh;
rt_uint32_t clk;
SDH_T *sdh;
RT_ASSERT(host);
RT_ASSERT(io_cfg);
NuSdh = (nu_sdh_t)host->private_data;
sdh = NuSdh->base;
clk = io_cfg->clock;
LOG_D("[%s]clk:%d width(%d):%s%s%s power:%s%s%s",
NuSdh->name,
clk,
io_cfg->bus_width,
(io_cfg->bus_width) == MMCSD_BUS_WIDTH_8 ? "8" : "",
(io_cfg->bus_width) == MMCSD_BUS_WIDTH_4 ? "4" : "",
(io_cfg->bus_width) == MMCSD_BUS_WIDTH_1 ? "1" : "",
io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : "");
/* Clock */
if (clk > host->freq_max)
clk = host->freq_max;
if (clk < host->freq_min)
clk = host->freq_min;
LOG_D("[%s] ExceptedFreq: %d kHz", NuSdh->name, clk / 1000);
if (NuSdh->u32CurClk != (clk / 1000))
{
SDH_SetClock(sdh, clk / 1000);
NuSdh->u32CurClk = (clk / 1000);
}
switch (io_cfg->power_mode)
{
case MMCSD_POWER_UP:
if (clk <= 400000)
{
/* power ON 74 clock */
sdh->CTL |= SDH_CTL_CLK74OEN_Msk;
while ((sdh->CTL & SDH_CTL_CLK74OEN_Msk) == SDH_CTL_CLK74OEN_Msk)
{
}
}
break;
case MMCSD_POWER_ON:
break;
case MMCSD_POWER_OFF:
break;
default:
break;
}
/* Bus width */
switch ((io_cfg->bus_width))
{
case MMCSD_BUS_WIDTH_1:
SDH_SetBusWidth(sdh, 1);
break;
case MMCSD_BUS_WIDTH_4:
SDH_SetBusWidth(sdh, 4);
break;
case MMCSD_BUS_WIDTH_8:
default:
break;
}
}
/**
* @brief This function detect sdcard.
* @param host rt_mmcsd_host
* @retval card detection status
*/
static rt_int32_t nu_sdh_card_detect(struct rt_mmcsd_host *host)
{
nu_sdh_t NuSdh;
RT_ASSERT(host);
NuSdh = (nu_sdh_t)host->private_data;
SDH_T *sdh = NuSdh->base;
LOG_D("try to detect device");
return SDH_GetCD(sdh);
}
static void nu_sdh_isr(nu_sdh_t NuSdh)
{
SDH_T *sdh = NuSdh->base;
uint32_t isr = sdh->INTSTS;
/* card detected */
if (isr & SDH_INTSTS_CDIF_Msk)
{
rt_tick_t cur_tick = rt_tick_get();
rt_tick_t diff_tick;
/* ready to change */
if (cur_tick >= NuSdh->LastNotice)
diff_tick = (cur_tick - NuSdh->LastNotice);
else
diff_tick = ((rt_tick_t) -1) - NuSdh->LastNotice + cur_tick;
if (!NuSdh->LastNotice || (diff_tick > (RT_TICK_PER_SECOND / 5))) // Debounce 200ms
{
NuSdh->LastNotice = cur_tick;
mmcsd_change(NuSdh->host);
}
/* Clear CDIF interrupt flag */
SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CDIF_Msk);
}
}
#if defined(BSP_USING_SDH0)
void SDH0_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
nu_sdh_isr(&nu_sdh_arr[SDH0_IDX]);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
#if defined(BSP_USING_SDH1)
void SDH1_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
nu_sdh_isr(&nu_sdh_arr[SDH1_IDX]);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
/**
* @brief This function update sdh interrupt.
* @param host rt_mmcsd_host
* @param enable
* @retval None
*/
void nu_sdh_irq_update(struct rt_mmcsd_host *host, rt_int32_t enable)
{
nu_sdh_t NuSdh = (nu_sdh_t)host->private_data;
SDH_T *sdh = NuSdh->base;
if (enable)
{
LOG_D("Enable %s irq", NuSdh->name);
SDH_ENABLE_INT(sdh, SDH_INTSTS_CDIF_Msk);
}
else
{
LOG_D("Disable %s irq", NuSdh->name);
SDH_DISABLE_INT(sdh, SDH_INTSTS_CDIF_Msk);
}
}
static const struct rt_mmcsd_host_ops ops =
{
nu_sdh_request,
nu_sdh_iocfg,
nu_sdh_card_detect,
nu_sdh_irq_update,
};
/**
* @brief This function create mmcsd host.
* @param sdh nu_sdh_t
* @retval nuvton
*/
void nu_sdh_host_init(nu_sdh_t sdh)
{
struct rt_mmcsd_host *host = mmcsd_alloc_host();
RT_ASSERT(host);
/* set host default attributes */
host->ops = &ops;
host->freq_min = 300 * 1000;
host->freq_max = 48 * 1000 * 1000;
host->valid_ocr = VDD_26_27 | VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD_33_34;
host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED;
host->max_seg_size = SDH_BUFF_SIZE;
host->max_dma_segs = 1;
host->max_blk_size = SDH_BLOCK_SIZE;
host->max_blk_count = (SDH_BUFF_SIZE / SDH_BLOCK_SIZE);
/* link up host and sdio */
host->private_data = sdh;
sdh->host = host;
/* Enable DMA engine at first. */
SDH_Enable(sdh->base);
/* Install ISR. */
NVIC_EnableIRQ(sdh->irqn);
/* ready to change */
//mmcsd_change(host);
}
static int rt_hw_sdh_init(void)
{
int i;
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
CLK_EnableModuleClock(nu_sdh_arr[i].modid);
SYS_ResetModule(nu_sdh_arr[i].rstidx);
nu_sdh_host_init(&nu_sdh_arr[i]);
}
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_sdh_init);
void nu_sd_attach(void)
{
int i;
/* ready to change */
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
if (nu_sdh_arr[i].host)
mmcsd_change(nu_sdh_arr[i].host);
}
}
MSH_CMD_EXPORT(nu_sd_attach, attach card);
void nu_sd_regdump(void)
{
int i;
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
if (nu_sdh_arr[i].host)
LOG_HEX("sdh_reg", 16, (void *)nu_sdh_arr[i].base, sizeof(SDH_T));
}
}
MSH_CMD_EXPORT(nu_sd_regdump, dump sdh registers);
#endif
/**************************************************************************//**
*
* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-4-21 Wayne First version
*
******************************************************************************/
#include "rtconfig.h"
#if defined(BSP_USING_SDH)
#include <rtdevice.h>
#include <drivers/mmcsd_core.h>
#include <drivers/sdio.h>
#include "NuMicro.h"
#define LOG_TAG "drv.sdh"
#undef DBG_ENABLE
#define DBG_SECTION_NAME LOG_TAG
#define DBG_LEVEL LOG_LVL_ASSERT
#define DBG_COLOR
#include <rtdbg.h>
#define SDH_ALIGN_LEN 4
#define SDH_BUFF_SIZE 512
#define SDH_BLOCK_SIZE 512
enum
{
SDH_START = -1,
#if defined(BSP_USING_SDH0)
SDH0_IDX,
#endif
#if defined(BSP_USING_SDH1)
SDH1_IDX,
#endif
SDH_CNT
};
struct nu_sdh
{
struct rt_mmcsd_host *host;
char *name;
SDH_T *base;
IRQn_Type irqn;
uint32_t rstidx;
uint32_t modid;
uint8_t *cachebuf;
uint32_t u32CmdResp0;
uint32_t u32CmdResp1;
uint32_t u32CurClk;
rt_tick_t LastNotice;
};
typedef struct nu_sdh *nu_sdh_t;
/* Private variables ------------------------------------------------------------*/
static struct nu_sdh nu_sdh_arr [] =
{
#if defined(BSP_USING_SDH0)
{
.name = "sdh0",
.base = SDH0,
.irqn = SDH0_IRQn,
.rstidx = SDH0_RST,
.modid = SDH0_MODULE,
.cachebuf = RT_NULL,
},
#endif
#if defined(BSP_USING_SDH1)
{
.name = "sdh1",
.base = SDH1,
.irqn = SDH1_IRQn,
.rstidx = SDH1_RST,
.modid = SDH1_MODULE,
.cachebuf = RT_NULL,
},
#endif
}; /* struct nu_sdh nu_sdh_arr [] */
#define SDH_SetClock SDH_Set_clock
static int SDH_SetBusWidth(SDH_T *sdh, uint32_t bw)
{
if (bw == 4)
{
sdh->CTL |= SDH_CTL_DBW_Msk;
}
else if (bw == 1)
{
sdh->CTL &= ~SDH_CTL_DBW_Msk;
}
else
{
goto exit_SDH_SetBusWidth;
}
return 0;
exit_SDH_SetBusWidth:
return -1;
}
static int SDH_GetBusStatus(SDH_T *sdh, uint32_t mask)
{
int cnt = 0x100000;
while (cnt-- > 0)
{
sdh->CTL |= SDH_CTL_CLK8OEN_Msk;
while (sdh->CTL & SDH_CTL_CLK8OEN_Msk) { }
if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk))
break;
}
return (cnt == 0) ? -1 : 0 ;
}
static int SDH_GetCD(SDH_T *sdh)
{
int i32CD = 0;
if ((sdh->GCTL & SDH_GCTL_SDEN_Msk) == SDH_GCTL_SDEN_Msk)
{
if ((sdh->INTEN & SDH_INTEN_CDSRC_Msk) == SDH_INTEN_CDSRC_Msk) /* Card detect pin from GPIO */
{
i32CD = (sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) ? 0 : 1;
}
else /* Card detect pin from DAT3 mode */
{
__IO uint32_t i;
sdh->CTL |= SDH_CTL_CLKKEEP_Msk;
for (i = 0ul; i < 5000ul; i++) { }
i32CD = ((sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) == SDH_INTSTS_CDSTS_Msk) ? 1 : 0;
sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk;
}
}
return i32CD;
}
static void SDH_Enable(SDH_T *sdh)
{
/* Reset sdh and its DMA engine at first. */
sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk;
while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { }
sdh->DMACTL = SDH_DMACTL_DMAEN_Msk;
sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag
sdh->GCTL = SDH_GCTL_GCTLRST_Msk;
while ((sdh->GCTL & SDH_GCTL_GCTLRST_Msk) == SDH_GCTL_GCTLRST_Msk) { }// clear all interrupt flag
sdh->GINTSTS = SDH_GINTSTS_DTAIF_Msk;
sdh->GCTL = SDH_GCTL_SDEN_Msk;
sdh->CTL |= SDH_CTL_CTLRST_Msk;
while ((sdh->CTL & SDH_CTL_CTLRST_Msk) == SDH_CTL_CTLRST_Msk) { }
sdh->INTSTS = 0xFFFFFFFF; // clear all interrupt flag
sdh->INTEN |= SDH_INTEN_CDSRC_Msk;
sdh->INTEN |= SDH_INTEN_CDIEN_Msk;
}
/**
* @brief This function get command responding.
* @param sdh SDH instance
* @param cmd rt_mmcsd_cmd
* @retval none
*/
static void nu_sdh_sendcmd_done(SDH_T *sdh, struct rt_mmcsd_cmd *cmd)
{
if (resp_type(cmd) == RESP_R2)
{
uint8_t *c = (uint8_t *)&sdh->FB[0];
int i, j, tmp[5];
for (i = 0, j = 0; j < 5; i += 4, j++)
{
tmp[j] = (*(c + i) << 24) | (*(c + i + 1) << 16) | (*(c + i + 2) << 8) | (*(c + i + 3));
}
for (i = 0; i < 4; i++)
{
cmd->resp[i] = ((tmp[i] & 0x00ffffff) << 8) |
((tmp[i + 1] & 0xff000000) >> 24);
}
}
else
{
cmd->resp[0] = (sdh->RESP0 << 8) | (sdh->RESP1 & 0xff);
cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0;
}
}
/**
* @brief This function wait data-sending/receiving.
* @param sdh SDH instance
* @param data rt_mmcsd_data
* @retval error code
*/
static int nu_sdh_xfer_data(SDH_T *sdh, struct rt_mmcsd_data *data)
{
while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk)) { }
SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk);
if (data->flags & DATA_DIR_WRITE)
{
sdh->CTL |= SDH_CTL_CLKKEEP_Msk;
while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk)) { }
sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk;
}
return 0;
}
/**
* @brief This function send command and wait its response.
* @param host rt_mmcsd_host
* @param cmd rt_mmcsd_cmd
* @param data rt_mmcsd_data
* @retval error code
*/
static int nu_sdh_sendcmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd, struct rt_mmcsd_data *data)
{
int ret;
nu_sdh_t NuSdh = (nu_sdh_t)host->private_data;
SDH_T *sdh = NuSdh->base;
volatile uint32_t ctl = 0, tout = 0;
switch (host->io_cfg.bus_width)
{
case MMCSD_BUS_WIDTH_1:
ctl &= ~SDH_CTL_DBW_Msk;
break;
case MMCSD_BUS_WIDTH_4:
ctl |= SDH_CTL_DBW_Msk;
break;
case MMCSD_BUS_WIDTH_8:
default:
return -1;
}
/* Reset sdh and its DMA engine at first. */
sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk;
while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { }
sdh->DMACTL = SDH_DMACTL_DMAEN_Msk;
sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag
if (resp_type(cmd) != RESP_NONE)
{
if (resp_type(cmd) == RESP_R2)
{
ctl |= SDH_CTL_R2EN_Msk;
}
else
{
ctl |= SDH_CTL_RIEN_Msk;
}
tout = 0xFFFF;
}
/* Set SDNWR and BLK_CNT to 1 */
ctl |= ((9 << SDH_CTL_SDNWR_Pos) | (1 << SDH_CTL_BLKCNT_Pos));
ctl |= ((cmd->cmd_code << SDH_CTL_CMDCODE_Pos) | SDH_CTL_COEN_Msk);
/* Set Transfer mode regarding to data flag */
if (data != RT_NULL)
{
sdh->BLEN = data->blksize - 1;
if (data->blksize <= 0x200)
{
if (data->blks < 256)
{
ctl = (ctl & ~SDH_CTL_BLKCNT_Msk) | (data->blks << SDH_CTL_BLKCNT_Pos);
}
else
{
LOG_E("SD Max block transfer is 255!!");
}
}
if (data->flags & DATA_DIR_READ)
{
tout = 0xFFFFFF;
ctl |= SDH_CTL_DIEN_Msk; // Data-in
sdh->DMASA = (uint32_t)data->buf; // Read from dest
}
else if (data->flags & DATA_DIR_WRITE)
{
ctl |= SDH_CTL_DOEN_Msk; // Data-out
sdh->DMASA = (uint32_t)data->buf; // Write to dest
}
}
else if (resp_type(cmd) == RESP_R1B)
{
}
/* Clear response-timeout flag first for safty and reset new timeout value. */
SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk);
sdh->TOUT = tout;
/* Set argument and start a transaction. */
sdh->CMDARG = cmd->arg;
sdh->CTL = ctl;
/* Wait a command done. */
while ((sdh->CTL & (SDH_CTL_COEN_Msk)) == SDH_CTL_COEN_Msk) { }
if (resp_type(cmd) != RESP_NONE)
{
if (resp_type(cmd) == RESP_R2)
{
/* Wait to receive a response R2 from SD card and store the response data into DMC's Flash buffer (exclude CRC7). */
while (sdh->CTL & SDH_CTL_R2EN_Msk)
{
/* When get a Response timeout, break the polling. */
if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk))
{
ret = __LINE__;
goto exit_nu_sdh_sendcmd;
}
}
}
else
{
/* Wait to receive a response from SD card. */
while ((sdh->CTL & SDH_CTL_RIEN_Msk))
{
/* When get a Response timeout, break the polling. */
if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk))
{
ret = __LINE__;
goto exit_nu_sdh_sendcmd;
}
}
/* TOFIX: ISSUE: Sometimes, SDH's RIEN is cleared automatically by controller after host sending CMD5 to SD card. */
/* Workaround: To check previous cmd's response with CMD's. */
if (cmd->cmd_code == 5)
{
if ((NuSdh->u32CmdResp0 == sdh->RESP0) && (NuSdh->u32CmdResp1 == sdh->RESP1))
{
LOG_E("False CMD5-RESP issue occured.\n");
ret = __LINE__;
goto exit_nu_sdh_sendcmd;
}
}
NuSdh->u32CmdResp0 = sdh->RESP0;
NuSdh->u32CmdResp1 = sdh->RESP1;
}
/* Get response from FB or register */
nu_sdh_sendcmd_done(sdh, cmd);
}
if (data != RT_NULL)
{
/* Wait data processing done */
nu_sdh_xfer_data(sdh, data);
ret = SDH_GetBusStatus(sdh, 0);
if (ret)
{
LOG_E("ERROR: Busy %d\n", ret);
ret = __LINE__;
goto exit_nu_sdh_sendcmd;
}
}
/* Handle CRC flag */
if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk)) // Fault
{
uint32_t u32INTSTS = sdh->INTSTS;
SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk);
ret = __LINE__;
if ((resp_type(cmd) != RESP_R3) && (u32INTSTS & SDH_INTSTS_CRC7_Msk) == 0) //CRC7, Ignore R3
{
LOG_E("CRC7 error! (resp_type=%d)", resp_type(cmd));
goto exit_nu_sdh_sendcmd;
}
if ((u32INTSTS & SDH_INTSTS_CRC16_Msk) == 0) //CRC16
{
LOG_E("CRC16 error! (resp_type=%d)", resp_type(cmd));
goto exit_nu_sdh_sendcmd;
}
}
return 0;
exit_nu_sdh_sendcmd:
LOG_D("[%s %d] cmdid=%d error line=%d", __func__, __LINE__, cmd->cmd_code, ret);
cmd->resp[0] = cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0;
sdh->TOUT = 0;
SDH_Enable(sdh);
return -ret;
}
/**
* @brief This function send request.
* @param host rt_mmcsd_host
* @param req request
* @retval None
*/
static void nu_sdh_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
{
nu_sdh_t NuSdh;
SDH_T *sdh;
RT_ASSERT(host);
RT_ASSERT(req);
NuSdh = (nu_sdh_t)host->private_data;
sdh = NuSdh->base;
if (!SDH_GetCD(sdh)) // card is not present
{
LOG_E("Card is not present");
req->cmd->err = -RT_EIO;
goto exit_nu_sdh_request;
}
if (req->cmd != RT_NULL)
{
struct rt_mmcsd_cmd *cmd = req->cmd;
struct rt_mmcsd_data *data = req->data;
LOG_D("[%s%s%s%s%s]REQ: CMD:%d ARG:0x%08x RESP_TYPE:%d rw:%c addr:%08x, blks:%d, blksize:%d datalen:%d",
(host->card == RT_NULL) ? "Unknown" : "",
(host->card) && (host->card->card_type == CARD_TYPE_MMC) ? "MMC" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SD) ? "SD" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SDIO) ? "SDIO" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SDIO_COMBO) ? "SDIO_COMBO" : "",
cmd->cmd_code,
cmd->arg,
resp_type(cmd),
data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-',
data ? data->buf : 0,
data ? data->blks : 0,
data ? data->blksize : 0,
data ? data->blks * data->blksize : 0);
if (data != RT_NULL)
{
rt_uint32_t size;
rt_int32_t IsNonaligned = 0;
rt_uint32_t *org_data_buf = data->buf;
size = data->blksize * data->blks;
RT_ASSERT(org_data_buf);
IsNonaligned = (((rt_uint32_t)data->buf & (SDH_ALIGN_LEN - 1)) > 0) ? 1 : 0;
if (IsNonaligned)
{
/* Allocate memory temp buffer on demand. */
RT_ASSERT(size <= SDH_BUFF_SIZE);
if (NuSdh->cachebuf == RT_NULL)
{
NuSdh->cachebuf = rt_malloc_align(SDH_BUFF_SIZE, SDH_ALIGN_LEN);
RT_ASSERT(NuSdh->cachebuf);
}
data->buf = (rt_uint32_t *)NuSdh->cachebuf;
if (data->flags & DATA_DIR_WRITE)
{
LOG_D("Un-aligned, prepare into cache buf(%d)", size);
rt_memcpy(data->buf, org_data_buf, size);
}
}
cmd->err = nu_sdh_sendcmd(host, cmd, data);
if (!cmd->err && IsNonaligned)
{
if (data->flags & DATA_DIR_READ)
{
LOG_D("Un-aligned, restore from cache buf(%d)", size);
rt_memcpy(org_data_buf, data->buf, size);
}
}
data->buf = org_data_buf;
LOG_HEX("data.dest", 16, (void *)data->buf, size);
}
else
{
cmd->err = nu_sdh_sendcmd(host, cmd, NULL);
}
if (resp_type(cmd) != RESP_NONE)
LOG_HEX("cmd->resp", 16, (void *)&cmd->resp[0], 16);
}
if (req->stop != RT_NULL)
{
struct rt_mmcsd_cmd *stop = req->stop;
stop->err = nu_sdh_sendcmd(host, stop, NULL);
if (resp_type(stop) != RESP_NONE)
LOG_HEX("stop->resp", 16, (void *)&stop->resp[0], 16);
}
exit_nu_sdh_request:
mmcsd_req_complete(host);
}
/**
* @brief This function config.
* @param host rt_mmcsd_host
* @param io_cfg rt_mmcsd_io_cfg
* @retval None
*/
static void nu_sdh_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
{
nu_sdh_t NuSdh;
rt_uint32_t clk;
SDH_T *sdh;
RT_ASSERT(host);
RT_ASSERT(io_cfg);
NuSdh = (nu_sdh_t)host->private_data;
sdh = NuSdh->base;
clk = io_cfg->clock;
LOG_D("[%s]clk:%d width(%d):%s%s%s power:%s%s%s",
NuSdh->name,
clk,
io_cfg->bus_width,
(io_cfg->bus_width) == MMCSD_BUS_WIDTH_8 ? "8" : "",
(io_cfg->bus_width) == MMCSD_BUS_WIDTH_4 ? "4" : "",
(io_cfg->bus_width) == MMCSD_BUS_WIDTH_1 ? "1" : "",
io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : "");
/* Clock */
if (clk > host->freq_max)
clk = host->freq_max;
if (clk < host->freq_min)
clk = host->freq_min;
LOG_D("[%s] ExceptedFreq: %d kHz", NuSdh->name, clk / 1000);
if (NuSdh->u32CurClk != (clk / 1000))
{
SDH_SetClock(sdh, clk / 1000);
NuSdh->u32CurClk = (clk / 1000);
}
switch (io_cfg->power_mode)
{
case MMCSD_POWER_UP:
if (clk <= 400000)
{
/* power ON 74 clock */
sdh->CTL |= SDH_CTL_CLK74OEN_Msk;
while ((sdh->CTL & SDH_CTL_CLK74OEN_Msk) == SDH_CTL_CLK74OEN_Msk)
{
}
}
break;
case MMCSD_POWER_ON:
break;
case MMCSD_POWER_OFF:
break;
default:
break;
}
/* Bus width */
switch ((io_cfg->bus_width))
{
case MMCSD_BUS_WIDTH_1:
SDH_SetBusWidth(sdh, 1);
break;
case MMCSD_BUS_WIDTH_4:
SDH_SetBusWidth(sdh, 4);
break;
case MMCSD_BUS_WIDTH_8:
default:
break;
}
}
/**
* @brief This function detect sdcard.
* @param host rt_mmcsd_host
* @retval card detection status
*/
static rt_int32_t nu_sdh_card_detect(struct rt_mmcsd_host *host)
{
nu_sdh_t NuSdh;
RT_ASSERT(host);
NuSdh = (nu_sdh_t)host->private_data;
SDH_T *sdh = NuSdh->base;
LOG_D("try to detect device");
return SDH_GetCD(sdh);
}
static void nu_sdh_isr(nu_sdh_t NuSdh)
{
SDH_T *sdh = NuSdh->base;
uint32_t isr = sdh->INTSTS;
/* card detected */
if (isr & SDH_INTSTS_CDIF_Msk)
{
rt_tick_t cur_tick = rt_tick_get();
rt_tick_t diff_tick;
/* ready to change */
if (cur_tick >= NuSdh->LastNotice)
diff_tick = (cur_tick - NuSdh->LastNotice);
else
diff_tick = ((rt_tick_t) -1) - NuSdh->LastNotice + cur_tick;
if (!NuSdh->LastNotice || (diff_tick > (RT_TICK_PER_SECOND / 5))) // Debounce 200ms
{
NuSdh->LastNotice = cur_tick;
mmcsd_change(NuSdh->host);
}
/* Clear CDIF interrupt flag */
SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CDIF_Msk);
}
}
#if defined(BSP_USING_SDH0)
void SDH0_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
nu_sdh_isr(&nu_sdh_arr[SDH0_IDX]);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
#if defined(BSP_USING_SDH1)
void SDH1_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
nu_sdh_isr(&nu_sdh_arr[SDH1_IDX]);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
/**
* @brief This function update sdh interrupt.
* @param host rt_mmcsd_host
* @param enable
* @retval None
*/
void nu_sdh_irq_update(struct rt_mmcsd_host *host, rt_int32_t enable)
{
nu_sdh_t NuSdh = (nu_sdh_t)host->private_data;
SDH_T *sdh = NuSdh->base;
if (enable)
{
LOG_D("Enable %s irq", NuSdh->name);
SDH_ENABLE_INT(sdh, SDH_INTSTS_CDIF_Msk);
}
else
{
LOG_D("Disable %s irq", NuSdh->name);
SDH_DISABLE_INT(sdh, SDH_INTSTS_CDIF_Msk);
}
}
static const struct rt_mmcsd_host_ops ops =
{
nu_sdh_request,
nu_sdh_iocfg,
nu_sdh_card_detect,
nu_sdh_irq_update,
};
/**
* @brief This function create mmcsd host.
* @param sdh nu_sdh_t
* @retval nuvton
*/
void nu_sdh_host_init(nu_sdh_t sdh)
{
struct rt_mmcsd_host *host = mmcsd_alloc_host();
RT_ASSERT(host);
/* set host default attributes */
host->ops = &ops;
host->freq_min = 300 * 1000;
host->freq_max = 48 * 1000 * 1000;
host->valid_ocr = VDD_26_27 | VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD_33_34;
host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED;
host->max_seg_size = SDH_BUFF_SIZE;
host->max_dma_segs = 1;
host->max_blk_size = SDH_BLOCK_SIZE;
host->max_blk_count = (SDH_BUFF_SIZE / SDH_BLOCK_SIZE);
/* link up host and sdio */
host->private_data = sdh;
sdh->host = host;
/* Enable DMA engine at first. */
SDH_Enable(sdh->base);
/* Install ISR. */
NVIC_EnableIRQ(sdh->irqn);
/* ready to change */
//mmcsd_change(host);
}
static int rt_hw_sdh_init(void)
{
int i;
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
CLK_EnableModuleClock(nu_sdh_arr[i].modid);
SYS_ResetModule(nu_sdh_arr[i].rstidx);
nu_sdh_host_init(&nu_sdh_arr[i]);
}
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_sdh_init);
void nu_sd_attach(void)
{
int i;
/* ready to change */
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
if (nu_sdh_arr[i].host)
mmcsd_change(nu_sdh_arr[i].host);
}
}
MSH_CMD_EXPORT(nu_sd_attach, attach card);
void nu_sd_regdump(void)
{
int i;
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
if (nu_sdh_arr[i].host)
LOG_HEX("sdh_reg", 16, (void *)nu_sdh_arr[i].base, sizeof(SDH_T));
}
}
MSH_CMD_EXPORT(nu_sd_regdump, dump sdh registers);
#endif
Import('RTT_ROOT')
from building import *
cwd = GetCurrentDir()
group = []
src = Split("""
lcd_fsa506.c
""")
CPPPATH = [cwd]
if GetDepend('NU_PKG_USING_FSA506_EBI'):
src += Glob('fsa506_ebi.c')
if GetDepend('NU_PKG_USING_FSA506'):
group = DefineGroup('nu_pkgs_fsa506', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
/**************************************************************************//**
*
* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-2-23 Wayne First version
*
******************************************************************************/
#include <rtconfig.h>
#if defined(NU_PKG_USING_FSA506_EBI)
#include <lcd_fsa506.h>
#include "drv_pdma.h"
#define FSA506_ADDR_CMD 0x0
#define FSA506_ADDR_DATA 0x0
#if defined(FSA506_EBI_16BIT)
#define fsa506_reg_write(RegAddr) (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_CMD))) = (RegAddr))
#define fsa506_read_data() (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))))
#define fsa506_write_data(Data) (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))) = (Data))
#else
#define fsa506_reg_write(RegAddr) (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_CMD))) = (RegAddr))
#define fsa506_read_data() (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))))
#define fsa506_write_data(Data) (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))) = (Data))
#endif
static rt_uint32_t s_u32AccessBase = 0;
void fsa506_send_cmd(rt_uint8_t cmd)
{
CLR_RS;
fsa506_reg_write(cmd);
SET_RS;
}
void fsa506_send_cmd_parameter(rt_uint8_t data)
{
fsa506_write_data(data);
}
void fsa506_send_cmd_done(void)
{
CLR_RS;
fsa506_reg_write(0x80);
SET_RS;
}
void fsa506_write_reg(rt_uint8_t cmd, rt_uint8_t data)
{
fsa506_send_cmd(cmd);
fsa506_send_cmd_parameter(data);
fsa506_send_cmd_done();
}
void fsa506_send_pixel_data(rt_uint16_t color)
{
#if 1
// for LV_COLOR_16_SWAP
//BGR, B is high byte
fsa506_write_data(color & 0xffu);
fsa506_write_data((color >> 8) & 0xffu);
#else
//RGB, R is high byte
fsa506_write_data((color >> 8) & 0xffu);
fsa506_write_data(color & 0xffu);
#endif
}
void fsa506_send_pixels(rt_uint16_t *pixels, int len)
{
int count = len / sizeof(rt_uint16_t);
if (count < 512)
{
// CPU feed
int i = 0;
while (i < count)
{
fsa506_send_pixel_data(pixels[i]);
i++;
}
}
else
{
// PDMA-M2M feed
// Must enable LV_COLOR_16_SWAP definition in LVGL.
nu_pdma_mempush((void *)(s_u32AccessBase + (FSA506_ADDR_DATA)), (void *)pixels, 8, len);
}
}
void fsa506_set_column(uint16_t StartCol, uint16_t EndCol)
{
fsa506_write_reg(0x0, (StartCol >> 8) & 0xFF);
fsa506_write_reg(0x1, StartCol & 0xFF);
fsa506_write_reg(0x2, (EndCol >> 8) & 0xFF);
fsa506_write_reg(0x3, EndCol & 0xFF);
}
void fsa506_set_page(uint16_t StartPage, uint16_t EndPage)
{
fsa506_write_reg(0x4, (StartPage >> 8) & 0xFF);
fsa506_write_reg(0x5, StartPage & 0xFF);
fsa506_write_reg(0x6, (EndPage >> 8) & 0xFF);
fsa506_write_reg(0x7, EndPage & 0xFF);
}
void fsa506_lcd_get_pixel(char *color, int x, int y)
{
//Not supported.
return;
}
rt_err_t rt_hw_lcd_fsa506_ebi_init(rt_uint32_t fsa506_base)
{
s_u32AccessBase = fsa506_base;
return RT_EOK;
}
#endif /* if defined(NU_PKG_USING_FSA506_EBI) */
/**************************************************************************//**
*
* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-2-23 Wayne First version
*
******************************************************************************/
#include <rtconfig.h>
#if defined(NU_PKG_USING_FSA506)
#include <rtdevice.h>
#include <lcd_fsa506.h>
#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
#if !defined(NU_PKG_FSA506_LINE_BUFFER_NUMBER)
#define NU_PKG_FSA506_LINE_BUFFER_NUMBER YSIZE_PHYS
#endif
#endif
#define fsa506_delay_ms(ms) rt_thread_mdelay(ms)
static void fsa506_fillscreen(rt_uint16_t color);
static struct rt_device_graphic_info g_FSA506Info =
{
.bits_per_pixel = 16,
.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565,
.framebuffer = RT_NULL,
.width = XSIZE_PHYS,
.pitch = XSIZE_PHYS * 2,
.height = YSIZE_PHYS
};
static rt_err_t fsa506_pin_init(void)
{
rt_pin_mode(BOARD_USING_FSA506_PIN_DC, PIN_MODE_OUTPUT);
rt_pin_mode(BOARD_USING_FSA506_PIN_RESET, PIN_MODE_OUTPUT);
rt_pin_mode(BOARD_USING_FSA506_PIN_BACKLIGHT, PIN_MODE_OUTPUT);
rt_pin_mode(BOARD_USING_FSA506_PIN_DISPLAY, PIN_MODE_OUTPUT);
CLR_RS;
CLR_RST;
SET_BACKLIGHT_OFF;
SET_DISP_OFF;
return RT_EOK;
}
static rt_err_t fsa506_lcd_init(rt_device_t dev)
{
/* Hardware reset */
CLR_RST;
fsa506_delay_ms(100); // Delay 100ms
SET_RST;
fsa506_delay_ms(100); // Delay 100ms
fsa506_write_reg(0x40, 0x12); // Underspece
fsa506_write_reg(0x41, 0x05); // Underspece
fsa506_write_reg(0x42, 0x06); // Underspece
/* Set the panel X size */
fsa506_write_reg(0x08, (uint8_t)(XSIZE_PHYS >> 8)); //Set the panel X size H[1.0]
fsa506_write_reg(0x09, (uint8_t)(XSIZE_PHYS)); //Set the panel X size L[7:0]
/* Memory write start address */
fsa506_write_reg(0x0a, 0x00); //[17:16] bits of memory write start address
fsa506_write_reg(0x0b, 0x00); //[15:8] bits of memory write start address
fsa506_write_reg(0x0c, 0x00); //[7:0] bits of memory write start address
/* Clock & format */
fsa506_write_reg(0x10, 0x0D); //[0-1] : 20MHz, [2]: Parallel panel, [3]: Normal operation
fsa506_write_reg(0x11, 0x05); //[3-5]: RGB, [0-2]BGR
/* For TFT output timing adjust */
fsa506_write_reg(0x12, 0x00); //Hsync start position H-Byte
fsa506_write_reg(0x13, 0x00); //Hsync start position L-Byte
fsa506_write_reg(0x14, (uint8_t)(41 >> 8)); //Hsync pulse width H-Byte
fsa506_write_reg(0x15, (uint8_t)(41)); //Hsync pulse width L-Byte
fsa506_write_reg(0x16, (uint8_t)(43 >> 8)); //DE pulse start position H-Byte
fsa506_write_reg(0x17, (uint8_t)(43)); //DE pulse start position L-Byte
fsa506_write_reg(0x18, (uint8_t)(XSIZE_PHYS >> 8)); //DE pulse width H-Byte
fsa506_write_reg(0x19, (uint8_t)(XSIZE_PHYS)); //DE pulse width L-Byte
fsa506_write_reg(0x1a, (uint8_t)(525 >> 8)); //Hsync total clocks H-Byte
fsa506_write_reg(0x1b, (uint8_t)(525)); //Hsync total clocks H-Byte
fsa506_write_reg(0x1c, 0x00); //Vsync start position H-Byte
fsa506_write_reg(0x1d, 0x00); //Vsync start position L-Byte
fsa506_write_reg(0x1e, (uint8_t)(10 >> 8)); //Vsync pulse width H-Byte
fsa506_write_reg(0x1f, (uint8_t)(10)); //Vsync pulse width L-Byte
fsa506_write_reg(0x20, (uint8_t)(12 >> 8)); //Vertical DE pulse start position H-Byte
fsa506_write_reg(0x21, (uint8_t)(12)); //Vertical DE pulse start position L-Byte
fsa506_write_reg(0x22, (uint8_t)(YSIZE_PHYS >> 8)); //Vertical Active width H-Byte
fsa506_write_reg(0x23, (uint8_t)(YSIZE_PHYS)); //Vertical Active width H-Byte
fsa506_write_reg(0x24, (uint8_t)(286 >> 8)); //Vertical total width H-Byte
fsa506_write_reg(0x25, (uint8_t)(286)); //Vertical total width L-Byte
fsa506_write_reg(0x26, 0x00); //Memory read start address
fsa506_write_reg(0x27, 0x00); //Memory read start address
fsa506_write_reg(0x28, 0x00); //Memory read start address
fsa506_write_reg(0x29, 0x01); //[0] Load output timing related setting (H sync., V sync. and DE) to take effect
//[7:4] Reserved
//[3] Output pin X_DCON level control
//[2] Output clock inversion 0: Normal 1: Inverse
//[1:0] Image rotate
// 00: 0? 01: 90? 10: 270?11: 180?
fsa506_write_reg(0x2d, (1 << 2) | 0x08);
/* Set the Horizontal offset */
fsa506_write_reg(0x30, 0x00); //_H byte H-Offset[3:0]
fsa506_write_reg(0x31, 0x00); //_L byte H-Offset[7:0]
fsa506_write_reg(0x32, 0x00); //_H byte V-Offset[3:0]
fsa506_write_reg(0x33, 0x00); //_L byte V-Offset[7:0]
fsa506_write_reg(0x34, (uint8_t)(XSIZE_PHYS >> 8)); //H byte H-def[3:0]
fsa506_write_reg(0x35, (uint8_t)(XSIZE_PHYS)); //_L byte H-def[7:0]
fsa506_write_reg(0x36, (uint8_t)((2 * YSIZE_PHYS) >> 8)); //[3:0] MSB of image vertical physical resolution in memory
fsa506_write_reg(0x37, (uint8_t)(2 * YSIZE_PHYS)); //[7:0] LSB of image vertical physical resolution in memory
fsa506_fillscreen(0);
SET_DISP_ON;
SET_BACKLIGHT_ON;
return RT_EOK;
}
#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
static void fsa506_fillrect(uint16_t *pixels, struct rt_device_rect_info *pRectInfo)
{
fsa506_set_column(pRectInfo->x, pRectInfo->x + pRectInfo->width - 1);
fsa506_set_page(pRectInfo->y, pRectInfo->y + pRectInfo->height - 1);
fsa506_send_cmd(0xC1);
fsa506_send_pixels(pixels, pRectInfo->height * pRectInfo->width * 2);
fsa506_send_cmd_done();
}
#endif
static void fsa506_fillscreen(rt_uint16_t color)
{
#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
struct rt_device_rect_info rectinfo;
int filled_line_num = 0;
while (filled_line_num < YSIZE_PHYS)
{
int pixel_count;
rectinfo.x = 0;
rectinfo.y = filled_line_num;
rectinfo.width = XSIZE_PHYS;
rectinfo.height = (NU_PKG_FSA506_LINE_BUFFER_NUMBER < YSIZE_PHYS) ? NU_PKG_FSA506_LINE_BUFFER_NUMBER : YSIZE_PHYS;
pixel_count = XSIZE_PHYS * NU_PKG_FSA506_LINE_BUFFER_NUMBER;
rt_uint16_t *pu16ShadowBuf = (rt_uint16_t *)g_FSA506Info.framebuffer;
while (pixel_count > 0)
{
*pu16ShadowBuf++ = color;
pixel_count--;
}
fsa506_fillrect((uint16_t *)g_FSA506Info.framebuffer, &rectinfo);
filled_line_num += NU_PKG_FSA506_LINE_BUFFER_NUMBER;
}
#else
fsa506_set_column(0, (XSIZE_PHYS - 1));
fsa506_set_page(0, (YSIZE_PHYS - 1));
fsa506_send_cmd(0xC1);
for (int i = 0; i < (XSIZE_PHYS * YSIZE_PHYS); i++)
fsa506_send_pixel_data(color);
fsa506_send_cmd_done();
#endif
}
static void fsa506_lcd_set_pixel(const char *color, int x, int y)
{
fsa506_set_column(x, x);
fsa506_set_page(y, y);
fsa506_send_cmd(0xC1);
fsa506_send_pixel_data(*(uint16_t *)color);
fsa506_send_cmd_done();
}
static void fsa506_lcd_draw_hline(const char *pixel, int x1, int x2, int y)
{
fsa506_set_column(x1, x2);
fsa506_set_page(y, y);
fsa506_send_cmd(0xC1);
for (; x1 < x2; x1++)
fsa506_send_pixel_data(*(uint16_t *)pixel);
fsa506_send_cmd_done();
}
static void fsa506_lcd_draw_vline(const char *pixel, int x, int y1, int y2)
{
fsa506_set_column(x, x);
fsa506_set_page(y1, y2);
fsa506_send_cmd(0xC1);
for (; y1 < y2; y1++)
fsa506_send_pixel_data(*(uint16_t *)pixel);
fsa506_send_cmd_done();
}
static void fsa506_lcd_blit_line(const char *pixels, int x, int y, rt_size_t size)
{
rt_uint16_t *ptr = (rt_uint16_t *)pixels;
fsa506_set_column(x, x + size);
fsa506_set_page(y, y);
fsa506_send_cmd(0xC1);
while (size--)
fsa506_send_pixel_data(*ptr++);
fsa506_send_cmd_done();
}
static rt_err_t fsa506_lcd_open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
static rt_err_t fsa506_lcd_close(rt_device_t dev)
{
return RT_EOK;
}
static rt_err_t fsa506_lcd_control(rt_device_t dev, int cmd, void *args)
{
switch (cmd)
{
case RTGRAPHIC_CTRL_GET_INFO:
{
struct rt_device_graphic_info *info;
info = (struct rt_device_graphic_info *) args;
RT_ASSERT(info != RT_NULL);
rt_memcpy(args, (void *)&g_FSA506Info, sizeof(struct rt_device_graphic_info));
}
break;
case RTGRAPHIC_CTRL_RECT_UPDATE:
{
#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
struct rt_device_rect_info *psRectInfo = (struct rt_device_rect_info *)args;
rt_uint16_t *pixels = (rt_uint16_t *)g_FSA506Info.framebuffer;
RT_ASSERT(args);
fsa506_fillrect(pixels, psRectInfo);
#else
/* nothong to be done */
#endif
}
break;
default:
break;
}
return RT_EOK;
}
static struct rt_device lcd_device;
static struct rt_device_graphic_ops fsa506_ops =
{
fsa506_lcd_set_pixel,
fsa506_lcd_get_pixel,
fsa506_lcd_draw_hline,
fsa506_lcd_draw_vline,
fsa506_lcd_blit_line
};
int rt_hw_lcd_fsa506_init(void)
{
fsa506_pin_init();
/* register lcd device */
lcd_device.type = RT_Device_Class_Graphic;
lcd_device.init = fsa506_lcd_init;
lcd_device.open = fsa506_lcd_open;
lcd_device.close = fsa506_lcd_close;
lcd_device.control = fsa506_lcd_control;
lcd_device.read = RT_NULL;
lcd_device.write = RT_NULL;
lcd_device.user_data = &fsa506_ops;
#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
g_FSA506Info.framebuffer = rt_malloc_align((g_FSA506Info.pitch * NU_PKG_FSA506_LINE_BUFFER_NUMBER) + 32, 32);
RT_ASSERT(g_FSA506Info.framebuffer != RT_NULL);
g_FSA506Info.smem_len = g_FSA506Info.pitch * NU_PKG_FSA506_LINE_BUFFER_NUMBER;
#endif
/* register graphic device driver */
rt_device_register(&lcd_device, "lcd", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
return 0;
}
#ifdef RT_USING_FINSH
#define LINE_LEN 32
static void lcd_test(int argc, char *argv[])
{
uint16_t pixels[LINE_LEN];
uint16_t color;
int x, y, i;
x = y = 100;
fsa506_lcd_init(NULL);
color = 0x0; //Black, RGB
rt_kprintf("Brush 0x%X on screen.\n", color);
fsa506_fillscreen(color);
fsa506_lcd_get_pixel((char *)&color, x, y);
rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
color = 0xffff; //White, RGB
rt_kprintf("Brush 0x%X on screen.\n", color);
fsa506_fillscreen(color);
fsa506_lcd_get_pixel((char *)&color, x, y);
rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
color = 0x1f; //Blue, RGB
rt_kprintf("Brush 0x%X on screen.\n", color);
fsa506_fillscreen(color);
fsa506_lcd_get_pixel((char *)&color, x, y);
rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
color = 0x07e0; //Green, RGB
rt_kprintf("Brush 0x%X on screen.\n", color);
fsa506_fillscreen(color);
fsa506_lcd_get_pixel((char *)&color, x, y);
rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
color = 0xf800; //Red, RGB
rt_kprintf("Brush 0x%X on screen.\n", color);
fsa506_fillscreen(color);
fsa506_lcd_get_pixel((char *)&color, x, y);
rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
color = 0xffff; //White, RGB
rt_kprintf("lcd draw hline, pixel: 0x%X, x1: %d, x2: %d, y: %d\n", color, x, x + 20, y);
fsa506_lcd_draw_hline((const char *)&color, x, x + 20, y);
color = 0xffff; //White, RGB
rt_kprintf("lcd draw vline, pixel: 0x%X, x: %d, y: %d\n", color, y, y + 20);
fsa506_lcd_draw_vline((const char *)&color, x, y, y + 20);
for (i = 0; i < LINE_LEN; i++)
pixels[i] = 20 + i * 5;
x = y = 50;
rt_kprintf("lcd blit line, start: x: %d, y: %d\n", x, y);
fsa506_lcd_blit_line((const char *)&pixels[0], x, y, LINE_LEN);
x = y = 200;
color = 0x07E0; //Green, RGB
rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
fsa506_lcd_set_pixel((const char *)&color, x, y);
color = 0x0;
fsa506_lcd_get_pixel((char *)&color, x, y);
rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
x = y = 200;
color = 0x1f; //Blue, RGB
rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
fsa506_lcd_set_pixel((const char *)&color, x, y);
color = 0x0;
fsa506_lcd_get_pixel((char *)&color, x, y);
rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
x = y = 200;
color = 0xf800; //Red, RGB
rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
fsa506_lcd_set_pixel((const char *)&color, x, y);
color = 0x0;
fsa506_lcd_get_pixel((char *)&color, x, y);
rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
}
MSH_CMD_EXPORT(lcd_test, test lcd display);
#endif
#endif /* if defined(NU_PKG_USING_FSA506) */
/**************************************************************************//**
*
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-2-23 Wayne First version
*
******************************************************************************/
#ifndef __LCD_FSA506_H__
#define __LCD_FSA506_H__
#include <rtthread.h>
#include <rtdevice.h>
#define SET_RS rt_pin_write(BOARD_USING_FSA506_PIN_DC, 1)
#define CLR_RS rt_pin_write(BOARD_USING_FSA506_PIN_DC, 0)
#define SET_RST rt_pin_write(BOARD_USING_FSA506_PIN_RESET, 1)
#define CLR_RST rt_pin_write(BOARD_USING_FSA506_PIN_RESET, 0)
#define SET_BACKLIGHT_ON rt_pin_write(BOARD_USING_FSA506_PIN_BACKLIGHT, 1)
#define SET_BACKLIGHT_OFF rt_pin_write(BOARD_USING_FSA506_PIN_BACKLIGHT, 0)
#define SET_DISP_ON rt_pin_write(BOARD_USING_FSA506_PIN_DISPLAY, 1)
#define SET_DISP_OFF rt_pin_write(BOARD_USING_FSA506_PIN_DISPLAY, 0)
//
// Physical display size
//
//#if defined(NU_PKG_FSA506_HORIZONTAL)
#define XSIZE_PHYS 480
#define YSIZE_PHYS 272
//#else
// #define XSIZE_PHYS 272
// #define YSIZE_PHYS 480
//#endif
int rt_hw_lcd_fsa506_init(void);
void fsa506_send_cmd(rt_uint8_t cmd);
void fsa506_send_cmd_parameter(rt_uint8_t data);
void fsa506_send_cmd_done(void);
void fsa506_write_reg(rt_uint8_t cmd, rt_uint8_t data);
void fsa506_set_column(rt_uint16_t StartCol, rt_uint16_t EndCol);
void fsa506_set_page(rt_uint16_t StartPage, rt_uint16_t EndPage);
void fsa506_send_pixel_data(rt_uint16_t color);
void fsa506_lcd_get_pixel(char *color, int x, int y);
void fsa506_send_pixels(rt_uint16_t *pixels, int len);
#if defined(NU_PKG_USING_FSA506_EBI)
rt_err_t rt_hw_lcd_fsa506_ebi_init(rt_uint32_t ebi_base);
#endif
#endif /* __LCD_FSA506_H__ */
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-04-11 Wayne First version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
#define DBG_TAG "ft5446"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "ft5446.h"
static struct rt_i2c_client ft5446_client;
static void ft5446_touch_up(void *buf, rt_int8_t id);
static rt_err_t ft5446_write_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t value)
{
struct rt_i2c_msg msgs;
rt_uint8_t buf[2];
buf[0] = reg;
buf[1] = value;
msgs.addr = dev->client_addr;
msgs.flags = RT_I2C_WR;
msgs.buf = buf;
msgs.len = sizeof(buf);
if (rt_i2c_transfer(dev->bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
static rt_err_t ft5446_read_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t len)
{
struct rt_i2c_msg msgs[2];
msgs[0].addr = dev->client_addr;
msgs[0].flags = RT_I2C_WR;
msgs[0].buf = &reg;
msgs[0].len = FT_REGITER_LEN;
msgs[1].addr = dev->client_addr;
msgs[1].flags = RT_I2C_RD;
msgs[1].buf = data;
msgs[1].len = len;
if (rt_i2c_transfer(dev->bus, msgs, 2) == 2)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
static rt_int16_t pre_x[FT_MAX_TOUCH];
static rt_int16_t pre_y[FT_MAX_TOUCH];
static rt_int16_t pre_w[FT_MAX_TOUCH];
static rt_uint8_t s_tp_dowm[FT_MAX_TOUCH];
static void ft5446_touch_up(void *buf, rt_int8_t id)
{
struct rt_touch_data *read_data = (struct rt_touch_data *)buf;
if (s_tp_dowm[id] == 1)
{
s_tp_dowm[id] = 0;
read_data[id].event = RT_TOUCH_EVENT_UP;
}
else
{
read_data[id].event = RT_TOUCH_EVENT_NONE;
}
read_data[id].timestamp = rt_touch_get_ts();
read_data[id].width = pre_w[id];
read_data[id].x_coordinate = pre_x[id];
read_data[id].y_coordinate = pre_y[id];
read_data[id].track_id = id;
pre_x[id] = -1; /* last point is none */
pre_y[id] = -1;
pre_w[id] = -1;
//LOG_I("%s (%d)\n", __func__, id);
}
static void ft5446_touch_down(void *buf, rt_int8_t id, rt_int16_t x, rt_int16_t y, rt_int16_t w)
{
struct rt_touch_data *read_data = (struct rt_touch_data *)buf;
if (s_tp_dowm[id] == 1)
{
read_data[id].event = RT_TOUCH_EVENT_MOVE;
}
else
{
read_data[id].event = RT_TOUCH_EVENT_DOWN;
s_tp_dowm[id] = 1;
}
read_data[id].timestamp = rt_touch_get_ts();
read_data[id].width = w;
read_data[id].x_coordinate = x;
read_data[id].y_coordinate = y;
read_data[id].track_id = id;
pre_x[id] = x; /* save last point */
pre_y[id] = y;
pre_w[id] = w;
//LOG_I("%s (%d %d %d %d)\n", __func__, id, x, y, w );
}
static int8_t pre_id[FT_MAX_TOUCH];
static S_FT_REGMAP sFtRegMap;
static rt_uint8_t pre_touch = 0;
static rt_size_t ft5446_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num)
{
int i;
rt_err_t error = 0;
rt_int32_t touch_event, touchid;
RT_ASSERT(touch);
RT_ASSERT(buf);
RT_ASSERT(read_num != 0);
RT_ASSERT(read_num <= FT_MAX_TOUCH);
error = ft5446_read_reg(&ft5446_client, 0, (rt_uint8_t *)&sFtRegMap, sizeof(sFtRegMap));
if (error)
{
LOG_E("get touch data failed, err:%d\n", error);
goto exit_read_point;
}
if (sFtRegMap.u8TDStatus > FT_MAX_TOUCH)
{
LOG_E("FW report max point:%d > panel info. max:%d\n", sFtRegMap.u8TDStatus, FT_MAX_TOUCH);
goto exit_read_point;
}
if (pre_touch > sFtRegMap.u8TDStatus) /* point up */
{
for (i = 0; i < FT_MAX_TOUCH; i++)
{
rt_uint8_t j;
for (j = 0; j < sFtRegMap.u8TDStatus; j++) /* this time touch num */
{
touchid = sFtRegMap.m_sTP[j].u8TouchID;
if (touchid >= 0x0f)
continue;
if (pre_id[i] == touchid) /* this id is not free */
break;
}
if ((j == sFtRegMap.u8TDStatus) && (pre_id[i] != -1)) /* free this node */
{
// LOG_I("free %d tid=%d\n", i, pre_id[i]);
ft5446_touch_up(buf, pre_id[i]);
pre_id[i] = -1;
}
}
}
for (i = 0; i < sFtRegMap.u8TDStatus; i++)
{
touch_event = sFtRegMap.m_sTP[i].u8EvtFlag;
touchid = sFtRegMap.m_sTP[i].u8TouchID;
//LOG_I("(%d/%d) %d %d\n", i, sFtRegMap.u8TDStatus, touchid, touch_event );
if (touchid >= 0x0f)
continue;
pre_id[i] = touchid;
if ((touch_event == FT_EVENTFLAG_PRESS_DOWN) || (touch_event == FT_EVENTFLAG_CONTACT))
{
rt_uint16_t x, y, w;
x = ((uint16_t)sFtRegMap.m_sTP[i].u8X_11_8 << 8) | sFtRegMap.m_sTP[i].u8X_7_0;
y = ((uint16_t)sFtRegMap.m_sTP[i].u8Y_11_8 << 8) | sFtRegMap.m_sTP[i].u8Y_7_0;
w = sFtRegMap.m_sTP[i].m_u8Weight;
//LOG_I("[%d] (%d %d %d %d)\n", touch_event, touchid, x, y, w );
if (x >= touch->info.range_x || y >= touch->info.range_y)
{
LOG_E("invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n",
0, x, touch->info.range_x,
0, y, touch->info.range_y);
continue;
}
ft5446_touch_down(buf, touchid, x, y, w);
}
else
{
// Up
ft5446_touch_up(buf, touchid);
}
} // for (i = 0; i < sFtRegMap.u8TDStatus; i++)
pre_touch = sFtRegMap.u8TDStatus;
return read_num;
exit_read_point:
pre_touch = 0;
return 0;
}
static rt_err_t ft5446_control(struct rt_touch_device *touch, int cmd, void *arg)
{
switch (cmd)
{
case RT_TOUCH_CTRL_GET_INFO:
{
struct rt_touch_info *info = (struct rt_touch_info *)arg;
RT_ASSERT(arg);
rt_memcpy(info, &touch->info, sizeof(struct rt_touch_info));
break;
}
case RT_TOUCH_CTRL_GET_ID:
break;
case RT_TOUCH_CTRL_SET_X_RANGE:
break;
case RT_TOUCH_CTRL_SET_Y_RANGE:
break;
case RT_TOUCH_CTRL_SET_X_TO_Y:
break;
case RT_TOUCH_CTRL_SET_MODE:
break;
default:
break;
}
return RT_EOK;
}
static struct rt_touch_ops ft5446_touch_ops =
{
.touch_readpoint = ft5446_read_point,
.touch_control = ft5446_control,
};
static void ft5446_init(struct rt_i2c_client *dev)
{
ft5446_write_reg(dev, 0x0, 0);
}
int rt_hw_ft5446_init(const char *name, struct rt_touch_config *cfg)
{
struct rt_touch_device *touch_device = RT_NULL;
rt_uint32_t bus_speed = 400000;
touch_device = (struct rt_touch_device *)rt_malloc(sizeof(struct rt_touch_device));
if (touch_device == RT_NULL)
{
LOG_E("touch device malloc fail");
return -RT_ERROR;
}
rt_memset((void *)touch_device, 0, sizeof(struct rt_touch_device));
/* hw init*/
rt_pin_mode(*(rt_uint8_t *)cfg->user_data, PIN_MODE_OUTPUT);
rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_LOW);
rt_thread_delay(5);
rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_HIGH);
rt_thread_delay(200);
rt_pin_mode(cfg->irq_pin.pin, cfg->irq_pin.mode);
ft5446_client.bus = (struct rt_i2c_bus_device *)rt_device_find(cfg->dev_name);
if (ft5446_client.bus == RT_NULL)
{
LOG_E("Can't find %s device", cfg->dev_name);
return -RT_ERROR;
}
if (rt_device_open((rt_device_t)ft5446_client.bus, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
LOG_E("open %s device failed", cfg->dev_name);
return -RT_ERROR;
}
if (rt_device_control((rt_device_t)ft5446_client.bus, RT_I2C_DEV_CTRL_CLK, &bus_speed) != RT_EOK)
{
LOG_E("control %s device failed", cfg->dev_name);
return -RT_ERROR;
}
ft5446_client.client_addr = FT5446_ADDRESS;
ft5446_init(&ft5446_client);
rt_memset(&pre_x[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t));
rt_memset(&pre_y[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t));
rt_memset(&pre_w[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t));
rt_memset(&s_tp_dowm[0], 0, FT_MAX_TOUCH * sizeof(int16_t));
rt_memset(&pre_id[0], 0xff, FT_MAX_TOUCH * sizeof(int8_t));
/* register touch device */
touch_device->info.type = RT_TOUCH_TYPE_CAPACITANCE;
touch_device->info.vendor = RT_TOUCH_VENDOR_FT;
touch_device->info.range_x = BSP_LCD_WIDTH;
touch_device->info.range_y = BSP_LCD_HEIGHT;
touch_device->info.point_num = FT_MAX_TOUCH;
rt_memcpy(&touch_device->config, cfg, sizeof(struct rt_touch_config));
touch_device->ops = &ft5446_touch_ops;
rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL);
LOG_I("touch device ft5446 init success");
return RT_EOK;
}
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-04-11 Wayne the first version
*/
#ifndef __FT5446_H__
#define __FT5446_H__
#include "touch.h"
#define FT_REGITER_LEN 1
#define FT_MAX_TOUCH 5
#define FT5446_ADDRESS 0x38
#pragma anon_unions
typedef struct
{
//03H
union
{
uint8_t m_u8XH;
struct
{
uint8_t u8X_11_8: 4;
uint8_t : 2;
uint8_t u8EvtFlag: 2;
#define FT_EVENTFLAG_PRESS_DOWN 0x0
#define FT_EVENTFLAG_LIFT_UP 0x1
#define FT_EVENTFLAG_CONTACT 0x2
#define FT_EVENTFLAG_NONE 0x3
};
};
//04H
union
{
uint8_t m_u8XL;
struct
{
uint8_t u8X_7_0;
};
};
//05H
union
{
uint8_t m_u8YH;
struct
{
uint8_t u8Y_11_8: 4;
uint8_t u8TouchID: 4; /* Touch ID of Touch Point, this value is 0x0F when the ID is invalid */
};
};
//06H
union
{
uint8_t m_u8YL;
struct
{
uint8_t u8Y_7_0;
};
};
//07H
uint8_t m_u8Weight; /* Touch pressure value */
//08H
union
{
uint8_t m_u8Misc;
struct
{
uint8_t : 4;
uint8_t u8TouchArea: 4; /* Touch area value */
};
};
} S_FT_TP;
#pragma pack(push)
#pragma pack(4)
typedef struct
{
union
{
uint8_t m_u8ModeSwitch;
#define FT_DEVICE_MODE_WORKING 0x0
#define FT_DEVICE_MODE_TEST 0x4
struct
{
uint8_t : 4;
uint8_t u8DevMode: 3;
uint8_t : 1;
};
};
uint8_t m_u8Guesture;
#define FT_GESTURE_ID_MOVE_UP 0x10
#define FT_GESTURE_ID_MOVE_RIGHT 0x14
#define FT_GESTURE_ID_MOVE_DOWN 0x18
#define FT_GESTURE_ID_MOVE_LEFT 0x1C
#define FT_GESTURE_ID_MOVE_IN 0x48
#define FT_GESTURE_ID_MOVE_OUT 0x49
#define FT_GESTURE_ID_MOVE_NONE 0x00
union
{
uint8_t m_u8Status;
struct
{
uint8_t u8TDStatus: 4;
uint8_t : 4;
};
};
S_FT_TP m_sTP[FT_MAX_TOUCH];
} S_FT_REGMAP;
#pragma pack(pop)
int rt_hw_ft5446_init(const char *name, struct rt_touch_config *cfg);
#endif /* __FT5446_H__ */
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-04-11 Wayne First version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
#define DBG_TAG "st1663i"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "st1663i.h"
static struct rt_i2c_client st1663i_client;
static void st1663i_touch_up(void *buf, rt_int8_t id);
static rt_err_t st1663i_write_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t value)
{
struct rt_i2c_msg msgs;
rt_uint8_t buf[2];
buf[0] = reg;
buf[1] = value;
msgs.addr = dev->client_addr;
msgs.flags = RT_I2C_WR;
msgs.buf = buf;
msgs.len = sizeof(buf);
if (rt_i2c_transfer(dev->bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
static rt_err_t st1663i_read_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t len)
{
struct rt_i2c_msg msgs[2];
msgs[0].addr = dev->client_addr;
msgs[0].flags = RT_I2C_WR;
msgs[0].buf = &reg;
msgs[0].len = ST_REGITER_LEN;
msgs[1].addr = dev->client_addr;
msgs[1].flags = RT_I2C_RD;
msgs[1].buf = data;
msgs[1].len = len;
if (rt_i2c_transfer(dev->bus, msgs, 2) == 2)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
static rt_int16_t pre_x[ST_MAX_TOUCH];
static rt_int16_t pre_y[ST_MAX_TOUCH];
static rt_int16_t pre_w[ST_MAX_TOUCH];
static rt_uint8_t s_tp_dowm[ST_MAX_TOUCH];
static void st1663i_touch_up(void *buf, rt_int8_t id)
{
struct rt_touch_data *read_data = (struct rt_touch_data *)buf;
if (s_tp_dowm[id] == 1)
{
s_tp_dowm[id] = 0;
read_data[id].event = RT_TOUCH_EVENT_UP;
}
else
{
read_data[id].event = RT_TOUCH_EVENT_NONE;
}
read_data[id].timestamp = rt_touch_get_ts();
read_data[id].width = pre_w[id];
read_data[id].x_coordinate = pre_x[id];
read_data[id].y_coordinate = pre_y[id];
read_data[id].track_id = id;
pre_x[id] = -1; /* last point is none */
pre_y[id] = -1;
pre_w[id] = -1;
//LOG_I("%s (%d)\n", __func__, id);
}
static void st1663i_touch_down(void *buf, rt_int8_t id, rt_int16_t x, rt_int16_t y, rt_int16_t w)
{
struct rt_touch_data *read_data = (struct rt_touch_data *)buf;
if (s_tp_dowm[id] == 1)
{
read_data[id].event = RT_TOUCH_EVENT_MOVE;
}
else
{
read_data[id].event = RT_TOUCH_EVENT_DOWN;
s_tp_dowm[id] = 1;
}
read_data[id].timestamp = rt_touch_get_ts();
read_data[id].width = w;
read_data[id].x_coordinate = x;
read_data[id].y_coordinate = y;
read_data[id].track_id = id;
pre_x[id] = x; /* save last point */
pre_y[id] = y;
pre_w[id] = w;
//LOG_I("%s (%d %d %d %d)\n", __func__, id, x, y, w );
}
static int8_t pre_id[ST_MAX_TOUCH];
static S_ST_REGMAP sStRegMap;
static rt_uint8_t pre_touch = 0;
static rt_size_t st1663i_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num)
{
int i;
rt_err_t error = 0;
rt_int32_t touch_event, touchid;
RT_ASSERT(touch);
RT_ASSERT(buf);
RT_ASSERT(read_num != 0);
RT_ASSERT(read_num <= ST_MAX_TOUCH);
error = st1663i_read_reg(&st1663i_client, 0x10, (rt_uint8_t *)&sStRegMap, sizeof(sStRegMap));
if (error)
{
LOG_E("get touch data failed, err:%d\n", error);
goto exit_read_point;
}
if (sStRegMap.u8Fingers > ST_MAX_TOUCH)
{
LOG_E("FW report max point:%d > panel info. max:%d\n", sStRegMap.u8Fingers, ST_MAX_TOUCH);
goto exit_read_point;
}
if (pre_touch > sStRegMap.u8Fingers) /* point up */
{
for (i = 0; i < ST_MAX_TOUCH; i++)
{
rt_uint8_t j;
for (j = 0; j < sStRegMap.u8Fingers; j++) /* this time touch num */
{
touchid = i;
if (pre_id[i] == touchid) /* this id is not free */
break;
}
if ((j == sStRegMap.u8Fingers) && (pre_id[i] != -1)) /* free this node */
{
// LOG_I("free %d tid=%d\n", i, pre_id[i]);
st1663i_touch_up(buf, pre_id[i]);
pre_id[i] = -1;
}
}
}
for (i = 0; i < sStRegMap.u8Fingers; i++)
{
touch_event = sStRegMap.m_sTP[i].u8Valid;
touchid = i;
//LOG_I("(%d/%d) %d %d\n", i, sStRegMap.u8Fingers, touchid, touch_event);
pre_id[i] = touchid;
if (touch_event)
{
rt_uint16_t x, y, w;
x = ((uint16_t)sStRegMap.m_sTP[i].u8X0_H << 8) | sStRegMap.m_sTP[i].m_u8X0_L;
y = ((uint16_t)sStRegMap.m_sTP[i].u8Y0_H << 8) | sStRegMap.m_sTP[i].m_u8Y0_L;
w = sStRegMap.m_sTP[i].m_u8Z;
//LOG_I("[%d] (%d %d %d %d)\n", touch_event, touchid, x, y, w);
if (x >= touch->info.range_x || y >= touch->info.range_y)
{
LOG_E("invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n",
0, x, touch->info.range_x,
0, y, touch->info.range_y);
continue;
}
st1663i_touch_down(buf, touchid, x, y, w);
}
else
{
// Up
st1663i_touch_up(buf, touchid);
}
} // for (i = 0; i < sStRegMap.u8TDStatus; i++)
pre_touch = sStRegMap.u8Fingers;
return read_num;
exit_read_point:
pre_touch = 0;
return 0;
}
static rt_err_t st1663i_control(struct rt_touch_device *touch, int cmd, void *arg)
{
switch (cmd)
{
case RT_TOUCH_CTRL_GET_INFO:
{
struct rt_touch_info *info = (struct rt_touch_info *)arg;
RT_ASSERT(arg);
rt_memcpy(info, &touch->info, sizeof(struct rt_touch_info));
break;
}
case RT_TOUCH_CTRL_GET_ID:
break;
case RT_TOUCH_CTRL_SET_X_RANGE:
break;
case RT_TOUCH_CTRL_SET_Y_RANGE:
break;
case RT_TOUCH_CTRL_SET_X_TO_Y:
break;
case RT_TOUCH_CTRL_SET_MODE:
break;
default:
break;
}
return RT_EOK;
}
static struct rt_touch_ops st1663i_touch_ops =
{
.touch_readpoint = st1663i_read_point,
.touch_control = st1663i_control,
};
static void st1663i_init(struct rt_i2c_client *dev)
{
st1663i_write_reg(dev, 0x0, 0);
}
int rt_hw_st1663i_init(const char *name, struct rt_touch_config *cfg)
{
struct rt_touch_device *touch_device = RT_NULL;
rt_uint32_t bus_speed = 400000;
touch_device = (struct rt_touch_device *)rt_malloc(sizeof(struct rt_touch_device));
if (touch_device == RT_NULL)
{
LOG_E("touch device malloc fail");
return -RT_ERROR;
}
rt_memset((void *)touch_device, 0, sizeof(struct rt_touch_device));
/* hw init*/
rt_pin_mode(*(rt_uint8_t *)cfg->user_data, PIN_MODE_OUTPUT);
rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_LOW);
rt_thread_delay(5);
rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_HIGH);
rt_thread_delay(200);
rt_pin_mode(cfg->irq_pin.pin, cfg->irq_pin.mode);
st1663i_client.bus = (struct rt_i2c_bus_device *)rt_device_find(cfg->dev_name);
if (st1663i_client.bus == RT_NULL)
{
LOG_E("Can't find %s device", cfg->dev_name);
return -RT_ERROR;
}
if (rt_device_open((rt_device_t)st1663i_client.bus, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
LOG_E("open %s device failed", cfg->dev_name);
return -RT_ERROR;
}
if (rt_device_control((rt_device_t)st1663i_client.bus, RT_I2C_DEV_CTRL_CLK, &bus_speed) != RT_EOK)
{
LOG_E("control %s device failed", cfg->dev_name);
return -RT_ERROR;
}
st1663i_client.client_addr = ST1663I_ADDRESS;
st1663i_init(&st1663i_client);
rt_memset(&pre_x[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t));
rt_memset(&pre_y[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t));
rt_memset(&pre_w[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t));
rt_memset(&s_tp_dowm[0], 0, ST_MAX_TOUCH * sizeof(int16_t));
rt_memset(&pre_id[0], 0xff, ST_MAX_TOUCH * sizeof(int8_t));
/* register touch device */
touch_device->info.type = RT_TOUCH_TYPE_CAPACITANCE;
touch_device->info.vendor = RT_TOUCH_VENDOR_UNKNOWN;
touch_device->info.range_x = BSP_LCD_WIDTH;
touch_device->info.range_y = BSP_LCD_HEIGHT;
touch_device->info.point_num = ST_MAX_TOUCH;
rt_memcpy(&touch_device->config, cfg, sizeof(struct rt_touch_config));
touch_device->ops = &st1663i_touch_ops;
rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL);
LOG_I("touch device st1663i init success");
return RT_EOK;
}
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-04-11 Wayne the first version
*/
#ifndef __ST1663I_H__
#define __ST1663I_H__
#include "touch.h"
#define ST_REGITER_LEN 1
#define ST_MAX_TOUCH 5
#define ST1663I_ADDRESS 0x55
#pragma anon_unions
typedef struct
{
//012H*n+0 (n=0, 1, ...,4)
union
{
uint8_t m_u8XY0_H;
struct
{
uint8_t u8Y0_H: 3;
uint8_t : 1;
uint8_t u8X0_H: 3;
uint8_t u8Valid: 1;
};
};
//012H*n+1 (n=0, 1, ...,4)
uint8_t m_u8X0_L;
//012H*n+2 (n=0, 1, ...,4)
uint8_t m_u8Y0_L;
//012H*n+3 (n=0, 1, ...,4)
uint8_t m_u8Z;
} S_ST_TP;
#pragma pack(push)
#pragma pack(4)
typedef struct
{
union
{
uint8_t m_u8TouchInfo;
struct
{
uint8_t u8Fingers: 4;
uint8_t : 4;
};
};
uint8_t m_u8Keys;
S_ST_TP m_sTP[ST_MAX_TOUCH];
} S_ST_REGMAP;
#pragma pack(pop)
int rt_hw_st1663i_init(const char *name, struct rt_touch_config *cfg);
#endif /* __ST1663I_H__ */
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-12-17 Wayne The first version
*/
/**
* @file lv_gpu_n9h30_ge2d.c
*
*/
/*********************
* INCLUDES
*********************/
#include <lvgl.h>
#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)
#include "lv_gpu_n9h30_ge2d.h"
#include "nu_2d.h"
#include "mmu.h"
/*********************
* DEFINES
*********************/
#if LV_COLOR_16_SWAP
#error "Can't use GE2D with LV_COLOR_16_SWAP 1"
#endif
#if !((LV_COLOR_DEPTH == 16) || (LV_COLOR_DEPTH == 32))
/*Can't use GPU with other formats*/
#error "Can't use GPU with other formats"
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_draw_n9h30_ge2d_blend_fill(lv_color_t *dest_buf, lv_coord_t dest_stride, const lv_area_t *fill_area,
lv_color_t color);
static void lv_draw_n9h30_ge2d_blend_map(lv_color_t *dest_buf, const lv_area_t *dest_area, lv_coord_t dest_stride,
const lv_color_t *src_buf, lv_coord_t src_stride, lv_opa_t opa);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Turn on the peripheral and set output color mode, this only needs to be done once
*/
void lv_draw_n9h30_ge2d_ctx_init(lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx)
{
lv_draw_sw_init_ctx(drv, draw_ctx);
lv_draw_n9h30_ge2d_ctx_t *ge2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
ge2d_draw_ctx->blend = lv_draw_n9h30_ge2d_blend;
ge2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_n9h30_ge2d_wait_cb;
}
void lv_draw_n9h30_ge2d_ctx_deinit(lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx)
{
LV_UNUSED(drv);
LV_UNUSED(draw_ctx);
}
void lv_draw_n9h30_ge2d_blend(lv_draw_ctx_t *draw_ctx, const lv_draw_sw_blend_dsc_t *dsc)
{
lv_area_t blend_area;
int32_t blend_area_w;
bool done = false;
if (!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;
blend_area_w = lv_area_get_width(&blend_area);
if ((lv_area_get_size(&blend_area) > 7200) &&
(((blend_area_w * (LV_COLOR_DEPTH / 2)) & 0x3) == 0) &&
(dsc->mask_buf == NULL) && (dsc->blend_mode == LV_BLEND_MODE_NORMAL))
{
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
lv_color_t *dest_buf = draw_ctx->buf;
dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1);
const lv_color_t *src_buf = dsc->src_buf;
if (src_buf)
{
lv_coord_t src_stride;
src_stride = lv_area_get_width(dsc->blend_area);
src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1);
lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_draw_n9h30_ge2d_blend_map(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa);
done = true;
}
else if (dsc->opa >= LV_OPA_MAX)
{
lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_draw_n9h30_ge2d_blend_fill(dest_buf, dest_stride, &blend_area, dsc->color);
done = true;
}
}
if (!done) lv_draw_sw_blend_basic(draw_ctx, dsc);
}
static void lv_draw_n9h30_ge2d_blend_fill(lv_color_t *dest_buf, lv_coord_t dest_stride, const lv_area_t *fill_area,
lv_color_t color)
{
int32_t fill_area_w = lv_area_get_width(fill_area);
int32_t fill_area_h = lv_area_get_height(fill_area);
lv_color_t *start_buf = dest_buf - (fill_area->y1 * dest_stride) - fill_area->x1;
//rt_kprintf("[blend_fill %d %08x] %dx%d %d %d\n", lv_area_get_size(fill_area), dest_buf, fill_area_w, fill_area_h, fill_area->x1, fill_area->y1 );
if (IS_CACHEABLE_VRAM(dest_buf))
mmu_clean_invalidated_dcache((uint32_t)dest_buf, sizeof(lv_color_t) * (dest_stride * fill_area_h + fill_area_w));
/*Hardware filling*/
// Enter GE2D ->
ge2dInit(sizeof(lv_color_t) * 8, dest_stride, fill_area->y2, (void *)start_buf);
ge2dClip_SetClip(fill_area->x1, fill_area->y1, fill_area->x2, fill_area->y2);
if (sizeof(lv_color_t) == 4)
ge2dFill_Solid(fill_area->x1, fill_area->y1, fill_area_w, fill_area_h, color.full);
else if (sizeof(lv_color_t) == 2)
ge2dFill_Solid_RGB565(fill_area->x1, fill_area->y1, fill_area_w, fill_area_h, color.full);
ge2dClip_SetClip(-1, 0, 0, 0);
// -> Leave GE2D
}
static void lv_draw_n9h30_ge2d_blend_map(lv_color_t *dest_buf, const lv_area_t *dest_area, lv_coord_t dest_stride,
const lv_color_t *src_buf, lv_coord_t src_stride, lv_opa_t opa)
{
/*Simple copy*/
int32_t dest_x = dest_area->x1;
int32_t dest_y = dest_area->y1;
int32_t dest_w = lv_area_get_width(dest_area);
int32_t dest_h = lv_area_get_height(dest_area);
const lv_color_t *dest_start_buf = dest_buf - (dest_area->y1 * dest_stride) - dest_area->x1;
//rt_kprintf("[blend_map %d %08x -> %08x] (x:%d y:%d, %dx%d) <stride src:%d dst:%d>\n", lv_area_get_size(dest_area), src_buf, dest_buf, dest_x, dest_y, dest_w, dest_h, src_stride, dest_stride);
// Enter GE2D ->
ge2dInit(sizeof(lv_color_t) * 8, dest_stride, dest_area->y2, (void *)dest_start_buf);
if (opa >= LV_OPA_MAX)
{
ge2dBitblt_SetAlphaMode(0, 0, 0);
ge2dBitblt_SetDrawMode(0, 0, 0);
}
else
{
ge2dBitblt_SetAlphaMode(1, opa, opa);
}
// flush
mmu_clean_dcache((uint32_t)src_buf, sizeof(lv_color_t) * (src_stride * dest_h + dest_w));
ge2dSpriteBlt_Screen(dest_x, dest_y, dest_w, dest_h, (void *)src_buf);
// -> Leave GE2D
}
void lv_gpu_n9h30_ge2d_wait_cb(lv_draw_ctx_t *draw_ctx)
{
lv_draw_sw_wait_for_finish(draw_ctx);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif // #if (LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0))
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-3-29 Wayne The first version
*/
#ifndef LV_GPU_N9H_GE2D_H
#define LV_GPU_N9H_GE2D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_color.h"
#include "../../hal/lv_hal_disp.h"
#include "../sw/lv_draw_sw.h"
#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_disp_drv_t;
typedef lv_draw_sw_ctx_t lv_draw_n9h30_ge2d_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Turn on the peripheral and set output color mode, this only needs to be done once
*/
void lv_draw_n9h30_ge2d_init(void);
void lv_draw_n9h30_ge2d_ctx_init(struct _lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx);
void lv_draw_n9h30_ge2d_ctx_deinit(struct _lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx);
void lv_draw_n9h30_ge2d_blend(lv_draw_ctx_t *draw_ctx, const lv_draw_sw_blend_dsc_t *dsc);
void lv_gpu_n9h30_ge2d_wait_cb(lv_draw_ctx_t *draw_ctx);
/**********************
* MACROS
**********************/
#endif /*#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GPU_N9H_GE2D_H*/
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册