Advanced Distributed Systems module at HSLU
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
ADIS_Projects/McuLib/src/McuW25Q128.c

477 lines
15 KiB

/*
* Copyright (c) 2022, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "McuW25Q128.h"
#if MCUW28Q128_CONFIG_ENABLED
#include "McuUtility.h"
#include "McuGPIO.h"
#include "McuWait.h"
#include "McuShell.h"
#include "McuLog.h"
#include "McuSPI.h"
#define McuW25_CMD_PAGE_PROGRAM 0x02
#define McuW25_CMD_DATA_READ 0x03
#define McuW25_CMD_READ_STATUS1 0x05
#define McuW25_CMD_WRITE_ENABLE 0x06
#define McuW25_CMD_WRITE_DISABLE 0x04
#define McuW25_CMD_GET_ID 0x9F
#define McuW25_ID0_WINBOND 0xEF /* first byte of ID for Winbond */
#define McuW25_ID1_WINBOND_xQ 0x40 /* second byte for W25Q128JV-IQ-JQ */
#define McuW25_ID2_WINBOND_xQ 0x18 /* third byte for W25Q128JV-IQ-JQ */
#define McuW25_ID1_WINBOND_xM 0x70 /* second byte for W25Q128JV-IM-JM */
#define McuW25_ID2_WINBOND_xM 0x18 /* third byte for W25Q128JV-IM-JM */
#define McuW25_CMD_GET_SERIAL 0x4B
#define McuW25_CMD_SECTOR_ERASE_4K 0x20
#define McuW25_CMD_BLOCK_ERASE_32K 0x52
#define McuW25_CMD_BLOCK_ERASE_64K 0xD8
#define McuW25_CMD_CHIP_ERASE 0xC7
#define SPI_WRITE(write) \
{ \
while(McuSPI_SendByte(write)!=0) {} \
}
#define SPI_WRITE_READ(write, readP) \
{ \
while(McuSPI_SendReceiveByte(write, readP)!=0) {} \
}
#define SPI_WRITE_READ_BLOCK(write, read, size) \
{ \
while(McuSPI_SendReceiveBlock(write, read, size)!=0) {} \
}
uint8_t McuW25_ReadStatus1(uint8_t *status) {
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_READ_STATUS1);
SPI_WRITE_READ(0, status);
McuW25_CONFIG_CS_DISABLE();
return ERR_OK;
}
bool McuW25_isBusy(void) {
uint8_t status;
McuW25_ReadStatus1(&status);
return (status&1);
}
void McuW25_WaitIfBusy(void) {
while(McuW25_isBusy()) {
McuWait_Waitms(1);
}
}
uint8_t McuW25_Read(uint32_t address, uint8_t *buf, size_t bufSize) {
McuW25_WaitIfBusy();
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_DATA_READ);
SPI_WRITE(address>>16);
SPI_WRITE(address>>8);
SPI_WRITE(address);
SPI_WRITE_READ_BLOCK(NULL, buf, bufSize);
McuW25_CONFIG_CS_DISABLE();
return ERR_OK;
}
uint8_t McuW25_EraseAll(void) {
McuW25_WaitIfBusy();
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_WRITE_ENABLE);
McuW25_CONFIG_CS_DISABLE();
McuWait_Waitus(1);
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_CHIP_ERASE);
McuW25_CONFIG_CS_DISABLE();
return ERR_OK;
}
uint8_t McuW25_EraseSector4K(uint32_t address) {
McuW25_WaitIfBusy();
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_WRITE_ENABLE);
McuW25_CONFIG_CS_DISABLE();
McuWait_Waitus(1);
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_SECTOR_ERASE_4K);
SPI_WRITE(address>>16);
SPI_WRITE(address>>8);
SPI_WRITE(address);
McuW25_CONFIG_CS_DISABLE();
return ERR_OK;
}
uint8_t McuW25_EraseBlock32K(uint32_t address) {
McuW25_WaitIfBusy();
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_WRITE_ENABLE);
McuW25_CONFIG_CS_DISABLE();
McuWait_Waitus(1);
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_BLOCK_ERASE_32K);
SPI_WRITE(address>>16);
SPI_WRITE(address>>8);
SPI_WRITE(address);
McuW25_CONFIG_CS_DISABLE();
return ERR_OK;
}
uint8_t McuW25_EraseBlock64K(uint32_t address) {
McuW25_WaitIfBusy();
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_WRITE_ENABLE);
McuW25_CONFIG_CS_DISABLE();
McuWait_Waitus(1);
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_BLOCK_ERASE_64K);
SPI_WRITE(address>>16);
SPI_WRITE(address>>8);
SPI_WRITE(address);
McuW25_CONFIG_CS_DISABLE();
return ERR_OK;
}
/*!
* Program a page with data
* \param address, should be aligned on page (256 bytes) if programming 256 bytes
* \param data pointer to data
* \param dataSize size of data in bytes, max 256
* \return error code, ERR_OK for no error
*/
uint8_t McuW25_ProgramPage(uint32_t address, const uint8_t *data, size_t dataSize) {
McuW25_WaitIfBusy();
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_WRITE_ENABLE);
McuW25_CONFIG_CS_DISABLE();
McuWait_Waitus(1);
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_PAGE_PROGRAM);
SPI_WRITE(address>>16);
SPI_WRITE(address>>8);
SPI_WRITE(address);
#if 0
while(dataSize>0) {
SPI_WRITE(*data);
dataSize--;
data++;
}
#else
SPI_WRITE_READ_BLOCK(data, NULL, dataSize);
#endif
McuW25_CONFIG_CS_DISABLE();
return ERR_OK;
}
uint8_t McuW25_GetCapacity(uint32_t *capacity) {
uint32_t n = 0x100000; /* unknown chips, default to 1 MByte */
uint8_t id[McuW25_ID_BUF_SIZE] = {0,0,0};
uint8_t res;
res = McuW25_ReadID(id, sizeof(id));
if (res!=ERR_OK) {
return ERR_FAILED;
}
if (id[2] >= 16 && id[2] <= 31) {
n = 1ul << id[2];
} else if (id[2] >= 32 && id[2] <= 37) {
n = 1ul << (id[2] - 6);
} else if ((id[0]==0 && id[1]==0 && id[2]==0) || (id[0]==255 && id[1]==255 && id[2]==255)) {
*capacity = 0;
return ERR_FAILED;
}
*capacity = n;
return ERR_OK;
}
uint8_t McuW25_ReadSerialNumber(uint8_t *buf, size_t bufSize) {
int i;
if (bufSize<McuW25_SERIAL_BUF_SIZE) {
return ERR_OVERFLOW; /* buffer not large enough */
}
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_GET_SERIAL);
for(i=0;i<4;i++) {
SPI_WRITE(0); /* 4 dummy bytes */
}
for(i=0; i<McuW25_SERIAL_BUF_SIZE; i++) {
SPI_WRITE_READ(0, &buf[i]);
}
McuW25_CONFIG_CS_DISABLE();
return ERR_OK;
}
uint8_t McuW25_ReadID(uint8_t *buf, size_t bufSize) {
if (bufSize<McuW25_ID_BUF_SIZE) {
return ERR_OVERFLOW; /* buffer not large enough */
}
McuW25_CONFIG_CS_ENABLE();
SPI_WRITE(McuW25_CMD_GET_ID);
/* W25Q128 should report EF 40 18 */
SPI_WRITE_READ(0, &buf[0]);
SPI_WRITE_READ(0, &buf[1]);
SPI_WRITE_READ(0, &buf[2]);
McuW25_CONFIG_CS_DISABLE();
return ERR_OK; /* not expected part */
}
McuW28_Device_e McuW28_GetDeviceType(void) {
uint8_t id[McuW25_ID_BUF_SIZE] = {0,0,0};
uint8_t res;
res = McuW25_ReadID(id, sizeof(id)); /* check ID */
if (res!=ERR_OK) {
return McuW28_DEVICE_UNKNOWN; /* error case */
}
if (id[0]==McuW25_ID0_WINBOND && id[1]==McuW25_ID1_WINBOND_xQ && id[2]==McuW25_ID2_WINBOND_xQ) {
return McuW28_DEVICE_W28Q128JV_IQ_JQ;
} else if (id[0]==McuW25_ID0_WINBOND && id[1]==McuW25_ID1_WINBOND_xM && id[2]==McuW25_ID2_WINBOND_xM) {
return McuW28_DEVICE_W28Q128JV_IM_JM;
}
return McuW28_DEVICE_UNKNOWN; /* error case */
}
static uint8_t McuW25_PrintStatus(McuShell_ConstStdIOType *io) {
uint8_t buf[60];
uint8_t id[McuW25_ID_BUF_SIZE] = {0,0,0};
McuW28_Device_e devID;
uint8_t serial[McuW25_SERIAL_BUF_SIZE] = {0,0,0,0,0,0,0,0};
uint8_t res, status;
uint32_t capacity;
int i;
McuShell_SendStatusStr((const unsigned char*)"McuWW25", (const unsigned char*)"Winbond W25Q128 Flash status\r\n", io->stdOut);
res = McuW25_ReadID(id, sizeof(id));
buf[0] = '\0';
if (res==ERR_OK) {
for(int i=0; i<sizeof(id); i++) {
McuUtility_strcatNum8Hex(buf, sizeof(buf), id[i]);
}
} else {
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"ERROR reading ID, ");
}
devID = McuW28_GetDeviceType();
switch(devID) {
case McuW28_DEVICE_W28Q128JV_IQ_JQ:
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" (Winbond W25Q128JV-IQ/JQ)\r\n");
break;
case McuW28_DEVICE_W28Q128JV_IM_JM:
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" (Winbond W25Q128JV-IM/JM)\r\n");
break;
case McuW28_DEVICE_UNKNOWN:
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" (Unknown)\r\n");
break;
default:
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" (ERROR)\r\n");
break;
}
McuShell_SendStatusStr((const unsigned char*)" ID", buf, io->stdOut);
res = McuW25_GetCapacity(&capacity);
if (res==ERR_OK) {
buf[0] = '\0';
McuUtility_strcatNum32u(buf, sizeof(buf), capacity);
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" bytes\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n");
}
McuShell_SendStatusStr((const unsigned char*)" Capacity", buf, io->stdOut);
res = McuW25_ReadSerialNumber(serial, sizeof(serial)); /* check serial number */
if(res==ERR_OK) {
buf[0] = '\0';
for(i=0; i<sizeof(serial); i++) {
McuUtility_strcatNum8Hex(buf, sizeof(buf), serial[i]);
McuUtility_chcat(buf, sizeof(buf), ' ');
}
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n");
}
McuShell_SendStatusStr((const unsigned char*)" Serial", buf, io->stdOut);
res = McuW25_ReadStatus1(&status);
if(res==ERR_OK) {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x");
McuUtility_strcatNum8Hex(buf, sizeof(buf), status);
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" SEC:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<6)?(unsigned char*)"1": (unsigned char*)"0");
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" TB:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<5)?(unsigned char*)"1": (unsigned char*)"0");
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" BP2:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<4)?(unsigned char*)"1": (unsigned char*)"0");
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" BP1:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<3)?(unsigned char*)"1": (unsigned char*)"0");
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" BP0:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<2)?(unsigned char*)"1": (unsigned char*)"0");
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" WEL:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<1)?(unsigned char*)"1": (unsigned char*)"0");
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" BUSY:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<0)?(unsigned char*)"1": (unsigned char*)"0");
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n");
}
McuShell_SendStatusStr((const unsigned char*)" Status", buf, io->stdOut);
return ERR_OK;
}
static uint8_t ReadBytes(void *hndl, uint32_t address, uint8_t *buf, size_t bufSize) {
(void)hndl; /* not used */
return McuW25_Read(address, buf, bufSize);
}
uint8_t McuW25_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) {
const unsigned char *p;
int32_t val, end;
uint32_t res;
uint8_t data[32];
int i;
if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "McuW25 help")==0) {
McuShell_SendHelpStr((unsigned char*)"McuW25", (const unsigned char*)"Group of Winbond W25Q128 Flash commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (const unsigned char*)"Print help or status information\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" read <start> <end>", (const unsigned char*)"Read memory from <start> to <end> address\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" erase all", (const unsigned char*)"Erase all data on device\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" erase 4k <addr>", (const unsigned char*)"Erase a 4K sector\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" erase 32k <addr>", (const unsigned char*)"Erase a 32K block\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" erase 64k <addr>", (const unsigned char*)"Erase a 64K block\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" write <addr> <data>", (const unsigned char*)"Write to page (max 32 bytes data, separated by spaces)\r\n", io->stdOut);
*handled = TRUE;
return ERR_OK;
} else if (McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, "McuW25 status")==0) {
*handled = TRUE;
return McuW25_PrintStatus(io);
} else if (McuUtility_strncmp((char*)cmd, "McuW25 read ", sizeof("McuW25 read ")-1)==0) {
*handled = TRUE;
p = cmd+sizeof("McuW25 read ")-1;
res = McuUtility_xatoi(&p, &val);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"wrong start address\r\n", io->stdErr);
return ERR_FAILED;
} else {
res = McuUtility_xatoi(&p, &end);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"wrong end address\r\n", io->stdErr);
return ERR_FAILED;
}
res = McuShell_PrintMemory(NULL, val, end, 3, 16, ReadBytes, io);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"memory read failed\r\n", io->stdErr);
return ERR_FAILED;
}
return ERR_OK;
}
} else if (McuUtility_strncmp((char*)cmd, "McuW25 write ", sizeof("McuW25 write ")-1)==0) {
*handled = TRUE;
p = cmd+sizeof("McuW25 write ")-1;
res = McuUtility_xatoi(&p, &val);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"wrong address\r\n", io->stdErr);
return ERR_FAILED;
} else {
for(i=0; i<sizeof(data); i++) {
int32_t v;
res = McuUtility_xatoi(&p, &v);
if (res!=ERR_OK) {
break;
}
data[i] = v;
}
if (i>0) {
res = McuW25_ProgramPage(val, data, i);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"failed programming page\r\n", io->stdErr);
return ERR_FAILED;
}
}
}
return ERR_OK;
} else if (McuUtility_strcmp((char*)cmd, "McuW25 erase all")==0) {
*handled = TRUE;
res = McuW25_EraseAll();
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"failed erasing all memory\r\n", io->stdErr);
return ERR_FAILED;
}
return ERR_OK;
} else if (McuUtility_strncmp((char*)cmd, "McuW25 erase 4k ", sizeof("McuW25 erase 4k ")-1)==0) {
*handled = TRUE;
p = cmd+sizeof("McuW25 erase 4k ")-1;
res = McuUtility_xatoi(&p, &val);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"wrong address\r\n", io->stdErr);
return ERR_FAILED;
} else {
res = McuW25_EraseSector4K(val);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"failed erasing 4k sector\r\n", io->stdErr);
return ERR_FAILED;
}
}
return ERR_OK;
} else if (McuUtility_strncmp((char*)cmd, "McuW25 erase 32k ", sizeof("McuW25 erase 32k ")-1)==0) {
*handled = TRUE;
p = cmd+sizeof("McuW25 erase 32k ")-1;
res = McuUtility_xatoi(&p, &val);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"wrong address\r\n", io->stdErr);
return ERR_FAILED;
} else {
res = McuW25_EraseBlock32K(val);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"failed erasing 32k block\r\n", io->stdErr);
return ERR_FAILED;
}
}
return ERR_OK;
} else if (McuUtility_strncmp((char*)cmd, "McuW25 erase 64k ", sizeof("McuW25 erase 64k ")-1)==0) {
*handled = TRUE;
p = cmd+sizeof("McuW25 erase 64k ")-1;
res = McuUtility_xatoi(&p, &val);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"wrong address\r\n", io->stdErr);
return ERR_FAILED;
} else {
res = McuW25_EraseBlock64K(val);
if (res!=ERR_OK) {
McuShell_SendStr((unsigned char*)"failed erasing 32k block\r\n", io->stdErr);
return ERR_FAILED;
}
}
return ERR_OK;
}
return ERR_OK;
}
void McuW25_Deinit(void) {
McuW25_CONFIG_CS_DISABLE(); /* disable chip select by default */
}
void McuW25_Init(void) {
McuW25_CONFIG_CS_DISABLE(); /* disable chip select by default */
}
#endif /* MCUW28Q128_CONFIG_ENABLED */