531 lines
16 KiB
C
531 lines
16 KiB
C
/*
|
|
* Eeprom object for Microchip PIC32MX microcontroller.
|
|
*
|
|
* @file eeprom.c
|
|
* @version SVN: \$Id$
|
|
* @author Janez Paternoster
|
|
* @copyright 2004 - 2013 Janez Paternoster
|
|
*
|
|
* This file is part of CANopenNode, an opensource CANopen Stack.
|
|
* Project home page is <http://canopennode.sourceforge.net>.
|
|
* For more information on CANopen see <http://www.can-cia.org/>.
|
|
*
|
|
* CANopenNode is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include "CO_driver.h"
|
|
#include "CO_SDO.h"
|
|
#include "CO_Emergency.h"
|
|
#include "eeprom.h"
|
|
#include "crc16-ccitt.h"
|
|
|
|
|
|
/* Eeprom *********************************************************************/
|
|
#define EE_SS_TRIS() TRISGCLR = 0x0200
|
|
#define EE_SSLow() PORTGCLR = 0x0200
|
|
#define EE_SSHigh() PORTGSET = 0x0200
|
|
#define SPIBUF SPI2ABUF
|
|
#define SPICON SPI2ACON
|
|
#define SPISTAT SPI2ASTAT
|
|
#define SPISTATbits SPI2ASTATbits
|
|
#define SPIBRG SPI2ABRG
|
|
static void EE_SPIwrite(uint8_t *tx, uint8_t *rx, uint8_t len);
|
|
static void EE_writeEnable();
|
|
static void EE_writeByteNoWait(uint8_t data, uint32_t addr);
|
|
static uint8_t EE_readByte(uint32_t addr);
|
|
static void EE_writeBlock(uint8_t *data, uint32_t addr, uint32_t len);
|
|
static void EE_readBlock(uint8_t *data, uint32_t addr, uint32_t len);
|
|
static uint8_t EE_verifyBlock(uint8_t *data, uint32_t addr, uint32_t len);
|
|
static void EE_writeStatus(uint8_t data);
|
|
static uint8_t EE_readStatus();
|
|
#define EE_isWriteInProcess() (EE_readStatus() & 0x01) /* True if write is in process. */
|
|
|
|
|
|
/* Store parameters ***********************************************************/
|
|
static CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg){
|
|
CO_EE_t *ee;
|
|
uint32_t value;
|
|
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
|
|
|
ee = (CO_EE_t*) ODF_arg->object;
|
|
value = CO_getUint32(ODF_arg->data);
|
|
|
|
if(!ODF_arg->reading){
|
|
/* don't change the old value */
|
|
CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U);
|
|
|
|
if(ODF_arg->subIndex == 1){
|
|
if(value == 0x65766173UL){
|
|
EE_MBR_t MBR;
|
|
|
|
/* read the master boot record from the last page in eeprom */
|
|
EE_readBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR));
|
|
/* if EEPROM is not yet initilalized, enable it now */
|
|
if(MBR.OD_EEPROMSize != ee->OD_EEPROMSize)
|
|
ee->OD_EEPROMWriteEnable = CO_true;
|
|
|
|
/* prepare MBR */
|
|
MBR.CRC = crc16_ccitt(ee->OD_ROMAddress, ee->OD_ROMSize, 0);
|
|
MBR.OD_EEPROMSize = ee->OD_EEPROMSize;
|
|
MBR.OD_ROMSize = ee->OD_ROMSize;
|
|
|
|
/* write to eeprom (blocking function) */
|
|
EE_writeStatus(0); /* unprotect data */
|
|
EE_writeBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR));
|
|
EE_writeBlock(ee->OD_ROMAddress, EE_SIZE/2, ee->OD_ROMSize);
|
|
EE_writeStatus(0x88); /* protect data */
|
|
|
|
/* verify data and MBR and status register */
|
|
if( EE_verifyBlock(ee->OD_ROMAddress, EE_SIZE/2, ee->OD_ROMSize) == 1
|
|
&& EE_verifyBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)) == 1
|
|
&& (EE_readStatus()&0x8C) == 0x88){
|
|
/* write successfull */
|
|
return CO_SDO_AB_NONE;
|
|
}
|
|
return CO_SDO_AB_HW;
|
|
}
|
|
else
|
|
return CO_SDO_AB_DATA_TRANSF;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Restore default parameters *************************************************/
|
|
static CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg){
|
|
CO_EE_t *ee;
|
|
uint32_t value;
|
|
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
|
|
|
ee = (CO_EE_t*) ODF_arg->object;
|
|
value = CO_getUint32(ODF_arg->data);
|
|
|
|
if(!ODF_arg->reading){
|
|
/* don't change the old value */
|
|
CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U);
|
|
|
|
if(ODF_arg->subIndex >= 1U){
|
|
if(value == 0x64616F6CUL){
|
|
EE_MBR_t MBR;
|
|
|
|
/* read the master boot record from the last page in eeprom */
|
|
EE_readBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR));
|
|
/* verify MBR for safety */
|
|
if(EE_verifyBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)) == 0)
|
|
return CO_SDO_AB_HW;
|
|
|
|
switch(ODF_arg->subIndex){
|
|
case 0x01: MBR.OD_ROMSize = 0; break; /* clear the ROM */
|
|
/* following don't work, if not enabled in object dictionary */
|
|
case 0x77: MBR.OD_ROMSize = ee->OD_ROMSize; break; /* restore the ROM back */
|
|
case 0x7F: MBR.OD_EEPROMSize = 0; break; /* clear EEPROM */
|
|
default: return 0x06090011; /* Sub-index does not exist. */
|
|
}
|
|
|
|
/* write changed MBR */
|
|
EE_writeStatus(0); /* unprotect data */
|
|
EE_writeBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR));
|
|
EE_writeStatus(0x88); /* protect data */
|
|
|
|
/* verify MBR and status register */
|
|
if(EE_verifyBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)) == 1
|
|
&& (EE_readStatus()&0x8C) == 0x88){
|
|
/* write successfull */
|
|
return CO_SDO_AB_NONE;
|
|
}
|
|
else{
|
|
return CO_SDO_AB_HW;
|
|
}
|
|
}
|
|
else
|
|
return CO_SDO_AB_DATA_TRANSF;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
CO_ReturnError_t CO_EE_init_1(
|
|
CO_EE_t *ee,
|
|
uint8_t *OD_EEPROMAddress,
|
|
uint32_t OD_EEPROMSize,
|
|
uint8_t *OD_ROMAddress,
|
|
uint32_t OD_ROMSize)
|
|
{
|
|
uint32_t rdata;
|
|
|
|
/* Configure SPI port for use with eeprom */
|
|
SPICON = 0; /* Stops and restes the SPI */
|
|
SPISTAT = 0;
|
|
rdata = SPIBUF; /* Clear the receive buffer */
|
|
SPIBRG = 4; /* Clock = FPB / ((4+1) * 2) */
|
|
SPICON = 0x00018120; /* MSSEN = 0 - Master mode slave select enable bit */
|
|
/* ENHBUF(bit16) = 1 - Enhanced buffer enable bit */
|
|
/* Enable SPI, 8-bit mode */
|
|
/* SMP = 0, CKE = 1, CKP = 0 */
|
|
/* MSTEN = 1 - master mode enable bit */
|
|
|
|
/* Set IOs directions for EEPROM SPI */
|
|
EE_SSHigh();
|
|
EE_SS_TRIS();
|
|
|
|
/* verify variables */
|
|
if(OD_ROMSize > (EE_SIZE/2 - EE_PAGE_SIZE)) OD_ROMSize = EE_SIZE/2 - EE_PAGE_SIZE;
|
|
if(OD_EEPROMSize > EE_SIZE/2) OD_EEPROMSize = EE_SIZE/2;
|
|
|
|
/* configure object variables */
|
|
ee->OD_EEPROMAddress = OD_EEPROMAddress;
|
|
ee->OD_EEPROMSize = OD_EEPROMSize;
|
|
ee->OD_ROMAddress = OD_ROMAddress;
|
|
ee->OD_ROMSize = OD_ROMSize;
|
|
ee->OD_EEPROMCurrentIndex = 0;
|
|
ee->OD_EEPROMWriteEnable = CO_false;
|
|
|
|
/* read the master boot record from the last page in eeprom */
|
|
EE_MBR_t MBR;
|
|
EE_readBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR));
|
|
|
|
/* read the CO_OD_EEPROM from EEPROM, first verify, if data are OK */
|
|
if(MBR.OD_EEPROMSize == OD_EEPROMSize && (MBR.OD_ROMSize == OD_ROMSize || MBR.OD_ROMSize == 0)){
|
|
uint32_t firstWordRAM = *((uint32_t*)OD_EEPROMAddress);
|
|
uint32_t firstWordEE, lastWordEE;
|
|
EE_readBlock((uint8_t*)&firstWordEE, 0, 4);
|
|
EE_readBlock((uint8_t*)&lastWordEE, OD_EEPROMSize-4, 4);
|
|
if(firstWordRAM == firstWordEE && firstWordRAM == lastWordEE){
|
|
EE_readBlock(OD_EEPROMAddress, 0, OD_EEPROMSize);
|
|
ee->OD_EEPROMWriteEnable = CO_true;
|
|
}
|
|
else{
|
|
return CO_ERROR_DATA_CORRUPT;
|
|
}
|
|
}
|
|
else{
|
|
return CO_ERROR_DATA_CORRUPT;
|
|
}
|
|
|
|
/* read the CO_OD_ROM from EEPROM and verify CRC */
|
|
if(MBR.OD_ROMSize == OD_ROMSize){
|
|
EE_readBlock(OD_ROMAddress, EE_SIZE/2, OD_ROMSize);
|
|
if(crc16_ccitt(OD_ROMAddress, OD_ROMSize, 0) != MBR.CRC){
|
|
return CO_ERROR_CRC;
|
|
}
|
|
}
|
|
|
|
return CO_ERROR_NO;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
void CO_EE_init_2(
|
|
CO_EE_t *ee,
|
|
CO_ReturnError_t eeStatus,
|
|
CO_SDO_t *SDO,
|
|
CO_EM_t *em)
|
|
{
|
|
CO_OD_configure(SDO, OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)ee, 0, 0U);
|
|
CO_OD_configure(SDO, OD_H1011_REST_PARAM_FUNC, CO_ODF_1011, (void*)ee, 0, 0U);
|
|
if(eeStatus != CO_ERROR_NO){
|
|
CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)eeStatus);
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
void CO_EE_process(CO_EE_t *ee){
|
|
if(ee && ee->OD_EEPROMWriteEnable && !EE_isWriteInProcess()){
|
|
/* verify next word */
|
|
if(++ee->OD_EEPROMCurrentIndex == ee->OD_EEPROMSize) ee->OD_EEPROMCurrentIndex = 0;
|
|
unsigned int i = ee->OD_EEPROMCurrentIndex;
|
|
|
|
/* read eeprom */
|
|
uint8_t RAMdata = ee->OD_EEPROMAddress[i];
|
|
uint8_t EEdata = EE_readByte(i);
|
|
|
|
/* if bytes in EEPROM and in RAM are different, then write to EEPROM */
|
|
if(EEdata != RAMdata)
|
|
EE_writeByteNoWait(RAMdata, i);
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* EEPROM 25LC128 on SPI ******************************************************/
|
|
/******************************************************************************/
|
|
#define EE_CMD_READ (unsigned)0b00000011
|
|
#define EE_CMD_WRITE (unsigned)0b00000010
|
|
#define EE_CMD_WRDI (unsigned)0b00000100
|
|
#define EE_CMD_WREN (unsigned)0b00000110
|
|
#define EE_CMD_RDSR (unsigned)0b00000101
|
|
#define EE_CMD_WRSR (unsigned)0b00000001
|
|
|
|
|
|
/**
|
|
* Function - EE_SPIwrite
|
|
*
|
|
* Write to SPI and at the same time read from SPI.
|
|
*
|
|
* PIC32 used 16bytes long FIFO buffer with SPI. SPI module is initailized in
|
|
* CO_EE_init.
|
|
*
|
|
* @param tx Ponter to transmitting data. If NULL, zeroes will be transmitted.
|
|
* @param rx Ponter to data buffer, where received data wile be stored.
|
|
* If null, Received data will be disregarded.
|
|
* @param len Length of data buffers. Max 16.
|
|
*/
|
|
static void EE_SPIwrite(uint8_t *tx, uint8_t *rx, uint8_t len){
|
|
uint32_t i;
|
|
|
|
/* write bytes into SPI_TXB fifo buffer */
|
|
if(tx) for(i=0; i<len; i++) SPIBUF = tx[i];
|
|
else for(i=0; i<len; i++) SPIBUF = 0;
|
|
|
|
/* read bytes from SPI_RXB fifo buffer */
|
|
for(i=0; i<len; i++){
|
|
uint32_t a;
|
|
while(SPISTATbits.SPIRBE); /* wait if buffer is empty */
|
|
if(rx) rx[i] = SPIBUF;
|
|
else a = SPIBUF;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Function - EE_writeEnable
|
|
*
|
|
* Enable write operation in EEPROM. It is called before every writing to it.
|
|
*/
|
|
static void EE_writeEnable(){
|
|
uint8_t buf = EE_CMD_WREN;
|
|
|
|
EE_SSLow();
|
|
EE_SPIwrite(&buf, 0, 1);
|
|
EE_SSHigh();
|
|
}
|
|
|
|
|
|
/*
|
|
* Write one byte of data to eeprom. It triggers write, but it does not wait for
|
|
* write cycle to complete. Before next write cycle, EE_isWriteInProcess must
|
|
* be checked.
|
|
*
|
|
* @param data Data byte to be written.
|
|
* @param addr Address in eeprom, where data will be written.
|
|
*/
|
|
static void EE_writeByteNoWait(uint8_t data, uint32_t addr){
|
|
uint8_t buf[4];
|
|
|
|
EE_writeEnable();
|
|
|
|
buf[0] = EE_CMD_WRITE;
|
|
buf[1] = (uint8_t) (addr >> 8);
|
|
buf[2] = (uint8_t) addr;
|
|
buf[3] = data;
|
|
|
|
EE_SSLow();
|
|
EE_SPIwrite(buf, 0, 4);
|
|
EE_SSHigh();
|
|
}
|
|
|
|
|
|
/*
|
|
* Read one byte of data from eeprom.
|
|
*
|
|
* @param addr Address in eeprom, where data will be written.
|
|
*
|
|
* @return Data byte read.
|
|
*/
|
|
static uint8_t EE_readByte(uint32_t addr){
|
|
uint8_t bufTx[4];
|
|
uint8_t bufRx[4];
|
|
|
|
bufTx[0] = EE_CMD_READ;
|
|
bufTx[1] = (uint8_t) (addr >> 8);
|
|
bufTx[2] = (uint8_t) addr;
|
|
bufTx[3] = 0;
|
|
|
|
EE_SSLow();
|
|
EE_SPIwrite(bufTx, bufRx, 4);
|
|
EE_SSHigh();
|
|
|
|
return bufRx[3];
|
|
}
|
|
|
|
|
|
/*
|
|
* Write block of data to eeprom. It is blockung function, so it waits, untill
|
|
* all data are written.
|
|
*
|
|
* @param data Pointer to data to be written.
|
|
* @param addr Address in eeprom, where data will be written. *If data are
|
|
* stored accross multiple pages, address must be aligned with page.*
|
|
* @param len Length of the data block.
|
|
*/
|
|
static void EE_writeBlock(uint8_t *data, uint32_t addr, uint32_t len){
|
|
|
|
#if EE_PAGE_SIZE != 64
|
|
#error incompatibility in function
|
|
#endif
|
|
|
|
while(EE_isWriteInProcess());
|
|
|
|
while(len){
|
|
uint8_t buf[3];
|
|
uint32_t i;
|
|
|
|
EE_writeEnable();
|
|
|
|
buf[0] = EE_CMD_WRITE;
|
|
buf[1] = (uint8_t) (addr >> 8);
|
|
buf[2] = (uint8_t) addr;
|
|
|
|
EE_SSLow();
|
|
EE_SPIwrite(buf, 0, 3);
|
|
|
|
for(i=0; i<4; i++){
|
|
if(len > 16){
|
|
EE_SPIwrite(data, 0, 16);
|
|
len -= 16;
|
|
data += 16;
|
|
}
|
|
else{
|
|
EE_SPIwrite(data, 0, len);
|
|
len = 0;
|
|
break;
|
|
}
|
|
}
|
|
EE_SSHigh();
|
|
|
|
/* wait for completion of the write operation */
|
|
i=EE_readStatus();
|
|
while(EE_isWriteInProcess());
|
|
addr += EE_PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Read block of data from eeprom.
|
|
*
|
|
* @param data Pointer to data buffer, where data will be stored.
|
|
* @param addr Address in eeprom, from where data will be read.
|
|
* @param len Length of the data block to be read.
|
|
*/
|
|
static void EE_readBlock(uint8_t *data, uint32_t addr, uint32_t len){
|
|
uint8_t buf[3];
|
|
|
|
buf[0] = EE_CMD_READ;
|
|
buf[1] = (uint8_t) (addr >> 8);
|
|
buf[2] = (uint8_t) addr;
|
|
|
|
EE_SSLow();
|
|
EE_SPIwrite(buf, 0, 3);
|
|
|
|
while(len){
|
|
if(len > 16){
|
|
EE_SPIwrite(0, data, 16);
|
|
len -= 16;
|
|
data += 16;
|
|
}
|
|
else{
|
|
EE_SPIwrite(0, data, len);
|
|
len = 0;
|
|
}
|
|
}
|
|
|
|
EE_SSHigh();
|
|
}
|
|
|
|
|
|
/*
|
|
* Compare block of data with data stored in eeprom.
|
|
*
|
|
* @param data Pointer to data buffer, which will be compared.
|
|
* @param addr Address of data in eeprom, which will be compared.
|
|
* @param len Length of the data block to be compared.
|
|
*
|
|
* @return 0 - comparision failed.
|
|
* @return 1 - data are equal.
|
|
*/
|
|
static uint8_t EE_verifyBlock(uint8_t *data, uint32_t addr, uint32_t len){
|
|
uint8_t buf[16];
|
|
uint8_t equal = 1;
|
|
|
|
buf[0] = EE_CMD_READ;
|
|
buf[1] = (uint8_t) (addr >> 8);
|
|
buf[2] = (uint8_t) addr;
|
|
|
|
EE_SSLow();
|
|
EE_SPIwrite(buf, 0, 3);
|
|
|
|
while(len){
|
|
if(len > 16){
|
|
uint8_t i;
|
|
EE_SPIwrite(0, buf, 16);
|
|
for(i=0; i<16; i++) if(buf[i] != data[i]) equal = 0;
|
|
len -= 16;
|
|
data += 16;
|
|
}
|
|
else{
|
|
uint8_t i;
|
|
EE_SPIwrite(0, buf, len);
|
|
for(i=0; i<len; i++) if(buf[i] != data[i]) equal = 0;
|
|
len = 0;
|
|
}
|
|
}
|
|
|
|
EE_SSHigh();
|
|
return equal;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write eeprom status register.
|
|
*
|
|
* @param data Data byte to be written to status register.
|
|
*/
|
|
static void EE_writeStatus(uint8_t data){
|
|
uint8_t buf[2];
|
|
|
|
EE_writeEnable();
|
|
|
|
EE_SSLow();
|
|
buf[0] = EE_CMD_WRSR;
|
|
buf[1] = data;
|
|
EE_SPIwrite(buf, 0, 2);
|
|
EE_SSHigh();
|
|
|
|
while(EE_isWriteInProcess());
|
|
}
|
|
|
|
|
|
/*
|
|
* Read eeprom status register.
|
|
*
|
|
* @return Data read from status register.
|
|
*/
|
|
static uint8_t EE_readStatus(){
|
|
uint8_t bufTx[2] = {EE_CMD_RDSR, 0};
|
|
uint8_t bufRx[2];
|
|
|
|
EE_SSLow();
|
|
EE_SPIwrite(bufTx, bufRx, 2);
|
|
EE_SSHigh();
|
|
|
|
return bufRx[1];
|
|
}
|