This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

AT25SL641 does not perform Write operation

Hello Renesas Community,

I am having an issue with my AT25SL641 flash memory chip in that it works completely except for the Page Program command (0x02). I am using a Maxim Integrated MAX32660 MCU and trying to write and read data from the AT25SL641 chip. I follow these steps:

  1. Setup my MCU as Master and begin SPI. 
  2. Send Write Enable Command 
  3. Send Page Program Command with a specific address  and a few bytes of data
  4. Send Read Command with same address 

When I read my RX data from the Read Command, it is filled with all 0xFF bytes, as if the memory has not been written. I have used an oscilloscope to confirm that the timings and signals are correct. I can see all three commands, can see that chip select is pulled low and return to high, and can individually see where each bit is to know all of them have been sent by the MCU.

Solutions tried:

  • I have tested a handful of memory address, including 0x000000, 0xABCD00, 0xABCDEF, 0x7FFF00, and more. Always the same result.
  • The logic level of MAX32660 is at 3.3 volts, and I have used a commercial logic level shifter to get the 3.3V signal down to 1.7V, where the AT25SL641 is supposed to be operated at. This does not change my results. Additionally, I am powering the AT25SL641 with a 1.8v signal the entire time.
  • As recommended in the datasheet, I have put a 10k Ohm pull-up resister on the Chip Select line, and this does not change my result.
  • I have put pull-ups on both the Write Protect and Hold pins, as referenced in the datasheet, and this seems to not change anything.

I have included my main.c file and an image of what my tx data and rx data look like. If anyone has any suggestions please let me know. Nothing has been able to fix this!

/*******************************
 * @file    main.c
 * @brief   Peripheral code
*******************************/

/***** MAXIM SDK Includes *****/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <stdlib.h>
#include "mxc_device.h"
#include "mxc_delay.h"
#include "mxc_sys.h"
#include "mxc_errors.h"
#include "mxc_pins.h"
#include "led.h"
#include "board.h"
#include "gpio.h"
#include "nvic_table.h"
#include "i2c_regs.h"
#include "i2c.h"
#include "tmr.h"
#include "flc.h"
#include "flc_regs.h"
#include "gcr_regs.h"
#include "icc.h"
#include "spi.h"
#include "spimss.h"


/***** Custom Includes *****/
#include "Wire.h"
#include "millis.h"
#include "MXC4005XC_I2C.h"


/***** Definitions *****/
#define I2C_MASTER        MXC_I2C1
#define I2C_FREQ          100000
#define I2C_FILLER        0xAA

#define SPI_MASTER        MXC_SPI0
#define SPI_FREQ          100000
#define SPI_IRQ           SPI0_IRQn

#define DATA_LEN 32


// eLED being the external transmitting LED
#define MXC_GPIO_PORT_OUT     MXC_GPIO0
#define eLED_PIN              MXC_GPIO_PIN_4
mxc_gpio_cfg_t eLED;

// iLED being the LED on the MCU maki roll
#define iLED_PIN              MXC_GPIO_PIN_5
mxc_gpio_cfg_t iLED;


// typedef enum{
//     FAILED,
//     PASSED
// }test_t;


/***** Globals *****/
uint8_t config[DATA_LEN];      // Placeholder for BLE messaging
uint8_t rxdata[DATA_LEN];      // To get response from BLE
uint8_t txdata[DATA_LEN];      // To send to SPI

uint8_t readout[DATA_LEN];

volatile uint32_t isr_cnt;
volatile uint32_t isr_flags;
int error;
bool startMsgRx = false;       // To break loops in app()
uint32_t StartTime;
int ADDRESS;
bool accel_flag, BLE_flag, arduino_flag;



/***** Function Declarations *****/
void delay(int milliseconds);
void writeLEDLow();
void writeLEDHigh();
void writeI_LEDLow();
void writeI_LEDHigh();
static void sendBLEMessage(uint8_t *message, uint8_t sizeOfMessage);

// Primary Functions
void setup(void);
void Setup_SPI(void);

// Helper Functions
void Send_SPI_Command(uint8_t hex_com);
void Write_Enable(void);
void Write_Page(void);
void Read_Page(void);
void Chip_Erase(void);

void Setup_IRQs(void);
short Float32toFloat16(float input);
void Download_Data(uint32_t address);
void Send_To_Arduino(void);
void Setup_LEDs(void);



enum BLE_COMMAND {
    GET_DATA_FROM_NODE_NUM =      0x29,

    UPDATE_TEMP_CHAR_DATA =       0x40,
    UPDATE_LIGHT_CHAR_DATA =      0x41,
    UPDATE_ACCEL_CHAR_DATA =      0x42,
    UPDATE_PPG_CHAR_DATA =        0x43,

    ON_OFF_LED_STATE =            0x14,

    SYNCH_START_MSG =             0xBB,
    RESTART_LOOP =                0xDD,
    DOWNLOAD_MSG =                0xCC,
};


/**************************************************/
/*** Defining Hex Address for Fiber Peripherals ***/
#define ACCEL_SENSOR            0x15
#define BLE_SENSOR              0x30
#define LIGHT_SENSOR            0x39     // TSL2584TSV
#define MCU_READOUT_SENSOR      0x50
#define ARDUINO                 0x55     // Slave address of Arduino



/*************************** Main ***************************/
int main(void)
{
    setup();       // Setup peripherals and I2C
    Setup_SPI();   // Setup SPI peripheral
    NVIC_EnableIRQ(SPI_IRQ);

    Write_Enable(); 
    Write_Page();
    Read_Page();

    while (true)
    {
        printf("main completed, looping forever...\n");
        delay(10000);
    }
}


/*************************** Setup ***************************/
void setup(void)
{
    printf("\n\n\n************************* SPI TEST *************************\n");

    printf("\nStarting setup...\n");
    int error;

    // Initializing timer and interrupts
    MXC_NVIC_SetVector(TMR2_IRQn, ContinuousTimerHandler);
    NVIC_EnableIRQ(TMR2_IRQn);
    ContinuousTimer();
    delay(1000);

    // Setup LEDs and set to High on start.
    Setup_LEDs();
    writeI_LEDHigh();
    writeLEDHigh();
    delay(1000);


    /**********************************************************************/
    /***** Check I2C Connections *****/
    // error = Wire_begin();
    // if (error != E_NO_ERROR)
    //     while (1)
    //     {
    //         printf("Failed to initiate I2C controller port.\n");
    //         writeI_LEDLow();
    //         delay(1000);
    //         NVIC_SystemReset(); // Reset MCU
    //     }
    // printf("I2C initiated.\n");

    /***** Check SPI Connections *****/
    error = SPI_Begin();
    if (error != E_NO_ERROR)
        while (1)
        {
            printf("Failed to initiate SPI: %d\n", error);
            writeI_LEDLow();
            delay(1000);
            NVIC_SystemReset(); // Reset MCU
        }
    printf("SPI initiated.\n");
    /**********************************************************************/
}


void Setup_SPI(void)
{
    // Configure the peripheral
    error = MXC_SPI_Init(SPI_MASTER, 1, 0, 1, 0, SPI_FREQ);
    if (error != E_NO_ERROR)
    {
        printf("\nSPI Master Init Error: %d\n", error); return;
    }

    error = MXC_SPI_SetDataSize(SPI_MASTER, 8); // number of bits
    if (error != E_NO_ERROR)
    {
        printf("\nSPI SET DATASIZE ERROR: %d\n", error); return;
    }

    error = MXC_SPI_SetWidth(SPI_MASTER, SPI_WIDTH_STANDARD);
    if (error != E_NO_ERROR)
    {
        printf("\nSPI SET WIDTH ERROR: %d\n", error); return;
    }

    NVIC_EnableIRQ(SPI_IRQ);

    printf("SPI Setup complete.\n\n");
}


void Send_SPI_Command(uint8_t hex_com)
{
    memset(&rxdata, 0x0, DATA_LEN * sizeof(uint8_t));   // Rx Start Byte
    memset(&txdata, 0x0, DATA_LEN * sizeof(uint8_t));

    txdata[0] = hex_com;

    mxc_spi_req_t req;
    req.spi = SPI_MASTER;
    req.txData = (uint8_t *)txdata;
    req.rxData = (uint8_t *)rxdata;
    req.txLen = DATA_LEN;
    req.rxLen = DATA_LEN;
    req.ssIdx = 0;
    req.ssDeassert = 1;
    req.txCnt = 0;
    req.rxCnt = 0;
    req.completeCB = (spi_complete_cb_t)SPI_Callback;

    error = MXC_SPI_MasterTransaction(&req);

    if (error != E_NO_ERROR)
        printf("Error: %d\n", error);

    printf("SPI Command 0x%X, rxdata:\n", hex_com);
    for (int i = 0; i < DATA_LEN; i++)
    {
        printf("%X ", rxdata[i]);
    }
    printf("\n\n");

    delay(100);
}


void Write_Page(void)
{
    memset(&rxdata, 0x0, DATA_LEN * sizeof(uint8_t));
    memset(&txdata, 0x0, DATA_LEN * sizeof(uint8_t));
    txdata[0] = 0x02;                                   // Write command
    txdata[1] = 0xAB; // address bytes
    txdata[2] = 0xCD;
    txdata[3] = 0x0F;

    txdata[4] = 0x11; // bytes to be written
    txdata[5] = 0x22;
    txdata[6] = 0x33;
    txdata[7] = 0x44;
    txdata[8] = 0x55;
    txdata[9] = 0x66;
    txdata[10] = 0x77;
    txdata[11] = 0x88;
    txdata[12] = 0x99;
    txdata[13] = 0xAA;
    txdata[14] = 0xBB;
    txdata[15] = 0xCC;
    txdata[16] = 0xDD;
    txdata[17] = 0xEE;
    txdata[18] = 0xFF;

    printf("write page txdata:\n");
    for (int i = 0; i < DATA_LEN; i++)
    {
        printf("%X ", txdata[i]);
    }
    printf("\n");

    mxc_spi_req_t req;
    req.spi = SPI_MASTER;
    req.txData = (uint8_t *)txdata;
    req.rxData = (uint8_t *)rxdata;
    req.txLen = DATA_LEN;
    req.rxLen = DATA_LEN;
    req.ssIdx = 0;
    req.ssDeassert = 1;
    req.txCnt = 0;
    req.rxCnt = 0;
    req.completeCB = (spi_complete_cb_t)SPI_Callback;

    error = MXC_SPI_MasterTransaction(&req);

    if (error != E_NO_ERROR)
        printf("Error: %d\n", error);

    printf("write page rxdata:\n");
    for (int i = 0; i < DATA_LEN; i++) { printf("%X ", rxdata[i]); }
    printf("\n\n");
}


void Write_Enable(void)
{
    memset(&rxdata, 0x0, DATA_LEN * sizeof(uint8_t));   // Rx Start Byte
    memset(&txdata, 0x0, DATA_LEN * sizeof(uint8_t));
    txdata[0] = 0x06;                                   // Write Enable command for PmodSF3

    mxc_spi_req_t req;
    req.spi = SPI_MASTER;
    req.txData = (uint8_t *)txdata;
    req.rxData = (uint8_t *)rxdata;
    req.txLen = DATA_LEN;
    req.rxLen = DATA_LEN;
    req.ssIdx = 0;
    req.ssDeassert = 1;
    req.txCnt = 0;
    req.rxCnt = 0;
    req.completeCB = (spi_complete_cb_t)SPI_Callback;

    error = MXC_SPI_MasterTransaction(&req);

    if (error != E_NO_ERROR)
        printf("Write Enable Error: %d\n", error);

    printf("Write Enable command sent.\n\n");
}


void Read_Page(void)
{
    memset(&rxdata, 0x0, DATA_LEN * sizeof(uint8_t));
    memset(&txdata, 0x0, DATA_LEN * sizeof(uint8_t));

    txdata[0] = 0x03;
    txdata[1] = 0xAB; // address
    txdata[2] = 0xCD;
    txdata[3] = 0x0F;

    // txdata[0] = 0x9F; // Read Manufactor ID, 0x1F

    printf("read page txdata:\n");
    for (int i = 0; i < DATA_LEN; i++)
    {
        printf("%X ", txdata[i]);
    }
    printf("\n");

    mxc_spi_req_t req;
    req.spi = SPI_MASTER;
    req.txData = (uint8_t *)txdata;
    req.rxData = (uint8_t *)rxdata;
    req.txLen = DATA_LEN;
    req.rxLen = DATA_LEN;
    req.ssIdx = 0;
    req.ssDeassert = 1;
    req.txCnt = 0;
    req.rxCnt = 0;
    req.completeCB = (spi_complete_cb_t)SPI_Callback;

    error = MXC_SPI_MasterTransaction(&req);

    if (error != E_NO_ERROR) printf("Error: %d\n", error);

    printf("rxdata:\n");
    for (int i = 0; i < DATA_LEN; i++)
    {
        printf("%X ", rxdata[i]);
    } printf("\n\n");
}


void Chip_Erase(void)
{
    memset(&rxdata, 0x0, DATA_LEN * sizeof(uint8_t));   // Rx Start Byte
    memset(&txdata, 0x0, DATA_LEN * sizeof(uint8_t));
    txdata[0] = 0x60;                                   // Erase chip command for Renesas chip

    mxc_spi_req_t req;
    req.spi = SPI_MASTER;
    req.txData = (uint8_t *)txdata;
    req.rxData = (uint8_t *)rxdata;
    req.txLen = DATA_LEN;
    req.rxLen = DATA_LEN;
    req.ssIdx = 0;
    req.ssDeassert = 1;
    req.txCnt = 0;
    req.rxCnt = 0;
    req.completeCB = (spi_complete_cb_t)SPI_Callback;

    error = MXC_SPI_MasterTransaction(&req);
}


void Send_To_Arduino(void)
{
    delay(1000); // Wait two seconds as a buffer

    uint32_t address = 0x12000;
    uint8_t buffer;

    while (address < 0x40000)
    {
        MXC_FLC_Read(address, &buffer, 1);

        Wire_beginTransmission(ARDUINO);
        Wire_write(&buffer, 1);
        Wire_endTransmission();
        address += 1;
    
        delay(1);
    }
}


unsigned short x1_, y1_, z1_, x2_, y2_, z2_;
uint32_t xbits, ybits, zbits;

// int Write_Data(uint32_t address)
// {
//     int err = 0;

//     // Get two readings with delay based on sampling rate
//     x1_ = Float32toFloat16( (float)MXC4005XC_readX_Axis() );
//     y1_ = Float32toFloat16( (float)MXC4005XC_readY_Axis() );
//     z1_ = Float32toFloat16( (float)MXC4005XC_readZ_Axis() );
//     delay(20);
//     x2_ = Float32toFloat16( (float)MXC4005XC_readX_Axis() );
//     y2_ = Float32toFloat16( (float)MXC4005XC_readY_Axis() );
//     z2_ = Float32toFloat16( (float)MXC4005XC_readZ_Axis() );

//     // Copy two 16 bit values to one 32 bit value
//     xbits = (x1_ << 16) | (x2_ & 0xFFFF);
//     ybits = (y1_ << 16) | (y2_ & 0xFFFF);
//     zbits = (z1_ << 16) | (z2_ & 0xFFFF);

//     err = MXC_FLC_Write32(address, xbits);   // X Axis Write
//     if (err!=0) { return err; }

//     address += 4;
//     err = MXC_FLC_Write32(address, ybits);   // Y Axis Write
//     if (err!=0) { return err; }

//     address += 4;
//     err = MXC_FLC_Write32(address, zbits);   // Z Axis Write
//     if (err!=0) { return err; }

//     return err;
// }


void Download_Data(uint32_t address)
{
    while (address < 0x40000)
    {
        memset(&config, I2C_FILLER, sizeof(config));
        config[0] = UPDATE_ACCEL_CHAR_DATA;

        uint8_t buffer;
        for (int j = 1; j < 31; j++)  // Iterate over buffer size
        {
            MXC_FLC_Read(address, &buffer, 1);
            config[j] = buffer;
            address += 1;
        }

        sendBLEMessage(&config[0], sizeof(config));

        // Requires a delay of 100 for some reason or else DA14531 doesn't pick up the data properly
        delay(150);
    }
}


/**********************************************************************
 * Converts a float (32 bits) into memory space of short type (int16)
**********************************************************************/
short Float32toFloat16(float input)
{
    // Copy Float bits into Int32
    uint32_t input_bits;
    memcpy(&input_bits, &input, sizeof(input));
    
    // StackOverflow Magic to convert Float32 to Float16 using fast operations
    unsigned short fltInt16;
    fltInt16 = (input_bits >> 31) << 5;
    unsigned short tmp = (input_bits >> 23) & 0xff;
    tmp = (tmp - 0x70) & ((unsigned int)((int)(0x70 - tmp) >> 4) >> 27);
    fltInt16 = (fltInt16 | tmp) << 10;
    fltInt16 |= (input_bits >> 13) & 0x3ff;

    return fltInt16;
}


/*******************************************************************************
 * Message pointer to an array
 * Will continue to re-send messages with 100ms delay until BLE ACK's properly
*******************************************************************************/
static void sendBLEMessage(uint8_t *message, uint8_t sizeOfMessage)
{
    Wire_beginTransmission(BLE_SENSOR);
    Wire_write(message, sizeOfMessage);
    int error = Wire_endTransmission();
    if (error != 0) 
        while(1)
        {
            Wire_beginTransmission(BLE_SENSOR);
            Wire_write(message, sizeOfMessage);
            error = Wire_endTransmission();
            delay(50);
        }
}

void FLC0_IRQHandler(void)
{
    uint32_t temp;
    isr_cnt++;
    temp = MXC_FLC0->intr;

    if (temp & MXC_F_FLC_INTR_DONE)
    {
        MXC_FLC0->intr &= ~MXC_F_FLC_INTR_DONE;
    }

    if (temp & MXC_F_FLC_INTR_ACCESS_FAIL)
    {
        MXC_FLC0->intr &= ~MXC_F_FLC_INTR_ACCESS_FAIL;
    }

    isr_flags = temp;
}

void Setup_IRQs(void)
{
    /**********************************************************************
     * All functions modifying flash contents are set to execute out of RAM
     * with the (section(".flashprog")) attribute.  Therefore,
     * 
     * If:
     * - An FLC function is in the middle of execution (from RAM)
     * ... and...
     * - An interrupt triggers an ISR which executes from Flash
     * 
     * ... Then a hard fault will be triggered.  
     * 
     * FLC functions should be:
     *  1) Executed from a critical code block (interrupts disabled)
     *    or
     *  2) ISRs should be set to execute out of RAM with NVIC_SetRAM()
     * 
     * This example demonstrates method #1.  Any code modifying
     * flash is executed from a critical block, and the FLC
     * interrupts will trigger afterwards.
    **********************************************************************/

    // NVIC_SetRAM(); // Execute ISRs out of SRAM (for use with #2 above)
    MXC_NVIC_SetVector(FLC_IRQn, FLC0_IRQHandler); // Assign ISR
    NVIC_EnableIRQ(FLC_IRQn); // Enable interrupt

    __enable_irq();

    // Clear and enable flash programming interrupts
    MXC_FLC_EnableInt(MXC_F_FLC_INTR_DONE_IE | MXC_F_FLC_INTR_ACCESS_FAIL_IE);
    isr_flags = 0;
    isr_cnt = 0;
}



/*********************** LED and Timer Helper Functions ***********************/

void delay(int milliseconds)
{
    MXC_Delay(milliseconds * 1000);
    return;
}

void writeLEDLow()
{
    MXC_GPIO_OutClr(eLED.port, eLED.mask);
}

void writeLEDHigh()
{
    MXC_GPIO_OutSet(eLED.port, eLED.mask);
}

void writeI_LEDLow()
{
    MXC_GPIO_OutClr(iLED.port, iLED.mask);
}

void writeI_LEDHigh()
{
    MXC_GPIO_OutSet(iLED.port, iLED.mask);
}

void Setup_LEDs()
{
    /**********************************/
    /* Setup external LED output pin. */
    eLED.port = MXC_GPIO_PORT_OUT;
    eLED.mask = eLED_PIN;
    eLED.pad  = MXC_GPIO_PAD_NONE;
    eLED.func = MXC_GPIO_FUNC_OUT;
    MXC_GPIO_Config(&eLED);
    writeLEDLow();

    /* Setup internal LED output pin. */
    iLED.port = MXC_GPIO_PORT_OUT;
    iLED.mask = iLED_PIN;
    iLED.pad  = MXC_GPIO_PAD_NONE;
    iLED.func = MXC_GPIO_FUNC_OUT;
    MXC_GPIO_Config(&iLED);
    writeI_LEDLow();
    /**********************************/
}




[locked by: Cindy H at 13:41 (GMT 0) on 9 Nov 2023]