CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

AT25M02 SPI EEPROM Driver

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

AT25M02 SPI EEPROM Driver
PostPosted: Fri Oct 20, 2017 12:14 am     Reply with quote

I am planning to create a driver for this device. What is the nearest SPI drivers included in CCS C compiler to start the development?

I saw from the included drivers folder, the device 9356SPI.

Thanks.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Fri Oct 20, 2017 1:40 am     Reply with quote

Any of the standard SPI drivers.

All of these recent EEPROM's, use a common signalling standard. The only things you have to change are the sizes of numbers fed to them. The nearest is actually the 25640 for example, just requiring one extra address byte added, and tweaks to the maximum size etc...
This is a better fit than the 241025 driver, where there is a bank switching bit added into the command.
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PostPosted: Fri Oct 20, 2017 8:25 pm     Reply with quote

Hi Ttelmah,

I checked the sample source code of 25640 and yes they are similar to AT25M02 though the implementation is I think software SPI.

I will try this one although my final implementation will be using hardware SPI on PIC18F8722.

Thanks.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Oct 21, 2017 12:32 am     Reply with quote

Yes.
I think these examples were written to be 'generic', so usable on just about any PIC without SPI hardware. Done before #USE SPI, gave the option to use hardware or software SPI existed. This makes it much easier. Smile
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PostPosted: Sat Oct 21, 2017 9:25 pm     Reply with quote

Hi Ttelmah,

I ended up writing a HW-based implementation driver for AT25M02. Although I am not quite sure if I done it correctly. I just read the MSSP chapter of PIC18F8722 and the AT25M02 datasheet. I will be testing this on my coming Monday.

I have not use any interrupt #int_ssp coz I think the PIC is a master while the EEPROM is a slave would not need any interrupt handling unless I am wrong with my assumption.

Btw, I am also planning to create a routine for page_write and page_read with a boundary of 256Bytes per EEPROM datasheet. I might also create block_write and block_read with a boundary of 1KB which is a multiple of 256Bytes.

Here are the source files together with the test driver.

AT25M02.h --> EEPROM driver header file
Code:

#if !defined(__AT25M02_H__)
#define __AT25M02_H__


//-------------------------------------------------------------------------
// Includes
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
// Global Constant Macros
//-------------------------------------------------------------------------

#bit SSP2IF = 0xFA4.7               // SPI2 interrupt flag
#bit SSP2IE = 0xFA3.7               // SPI2 interrupt enable

#bit SSP2_TRISB3 = 0xF93.3          // SPI2 EEPROM chip select
#bit SSP2_PORTB3 = 0xF81.3

#bit SSP2STAT_BF = 0xF64.0          // Buffer Full Status bit

#byte SSP2CON1 = 0xF63              // SSP2 Control register
#byte SSP2STAT = 0xF64              // SSP2 Status register
#byte SSP2BUF = 0xF66               // MSSP2 Receive Buffer/Transmit Register


#define SSP2_ENABLE                             (0x20)
#define SSP2_MASTER                             (0x40)


// EEPROM Instruction Set
#define SPI_EEPROM_CMD_WRITE                    (0x02)
#define SPI_EEPROM_CMD_READ                     (0x03)
#define SPI_EEPROM_CMD_WRDI                     (0x04)
#define SPI_EEPROM_CMD_RDSR                     (0x05)
#define SPI_EEPROM_CMD_WREN                     (0x06)


// different byte offset
#define SPI_ADDRESS_OFFSET_0                    (0x00)
#define SPI_ADDRESS_OFFSET_1                    (0x01)
#define SPI_ADDRESS_OFFSET_2                    (0x02)
#define SPI_ADDRESS_OFFSET_3                    (0x03)


//-------------------------------------------------------------------------
// Global Type Definitions
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
// Global Structure Definitions
//-------------------------------------------------------------------------

struct EepromStatRegStruct
{
    unsigned char BSY   :1;
    unsigned char WEL   :1;
    unsigned char BP0   :1;
    unsigned char BP1   :1;
    unsigned char RFU   :3;
    unsigned char WPEN  :1;
};

union EepromStatRegUnion
{
    struct EepromStatRegStruct EepromStatReg;
    unsigned char EepromStatus;
};


//-------------------------------------------------------------------------
// Function Macros
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
// Global Variable Declaration (extern)
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
// Function Prototype
//-------------------------------------------------------------------------
void spi_eeprom_init (void);

union EepromStatRegUnion spi_eeprom_read_status (void);

// send one byte of data and receive one back at the same time
unsigned char spi_eeprom_write (unsigned char Data);

void spi_eeprom_write_enable (void);

void spi_eeprom_write_disable (void);

void spi_eeprom_write_byte (unsigned char Data,
                            unsigned int32 Address);
                           
unsigned char spi_eeprom_read_byte (unsigned int32 Address);


#endif



AT25M02.c --> EEPROM driver source file
Code:

#include "AT25M02.h"

void spi_eeprom_init (void)
{
    SSP2IF = 0;                 // clear interrupt flag
    SSP2IE = 0;                 // disable interrupt
    SSP2CON1 = SSP2_ENABLE;     // enable SPI peripheral
    SSP2STAT = SSP2_MASTER;     // select mode
   
    SSP2_TRISB3 = 0;            // RB3 set to output for EEPROM chip select
    SSP2_PORTB3 = 1;            // EEPROM chip select set to high
    return;
}

union EepromStatRegUnion spi_eeprom_read_status (void)
{
    unsigned char Result;
   
    SSP2_PORTB3 = 0;        // EEPROM chip select set to low
   
    // send the EEPROM read status register
    Result = spi_eeprom_write(SPI_EEPROM_CMD_RDSR);
   
    // get the EEPROM status register
    Result = spi_eeprom_write(0);
    SSP2_PORTB3 = 1;        // EEPROM chip select set to high
   
    return (union EepromStatRegUnion)Result;
}

unsigned char spi_eeprom_write (unsigned char Data)
{
    SSP2BUF = Data;         // write to buffer for TX
    while (!SSP2STAT_BF);   // wait for transfer to complete
    return SSP2BUF;         // read the received value
}

void spi_eeprom_write_enable (void)
{
    unsigned char Result;
   
    SSP2_PORTB3 = 0;        // EEPROM chip select set to low
    Result = spi_eeprom_write(SPI_EEPROM_CMD_WREN);
    SSP2_PORTB3 = 1;        // EEPROM chip select set to high
    return;
}

void spi_eeprom_write_disable (void)
{
    unsigned char Result;
   
    SSP2_PORTB3 = 0;        // EEPROM chip select set to low
    Result = spi_eeprom_write(SPI_EEPROM_CMD_WRDI);
    SSP2_PORTB3 = 1;        // EEPROM chip select set to high
    return;
}

void spi_eeprom_write_byte (unsigned char Data,
                            unsigned int32 Address)
{
    unsigned char Result;
    union EepromStatRegUnion Status;
   
    // enable write operation
    spi_eeprom_write_enable();
    SSP2_PORTB3 = 0;        // EEPROM chip select set to low
   
    // send the EEPROM write command
    Result = spi_eeprom_write(SPI_EEPROM_CMD_WRITE);
   
    // send the EEPROM address
    Result = spi_eeprom_write(make8(Address, SPI_ADDRESS_OFFSET_2));
    Result = spi_eeprom_write(make8(Address, SPI_ADDRESS_OFFSET_1));
    Result = spi_eeprom_write(make8(Address, SPI_ADDRESS_OFFSET_0));
   
    // send the data to write
    Result = spi_eeprom_write(Data);
    SSP2_PORTB3 = 1;        // EEPROM chip select set to high
   
    // wait for completion of previous write operation
    do
    {
        Status = spi_eeprom_read_status();
    } while (Status.EepromStatReg.BSY);
   
    // disable write operation
    spi_eeprom_write_disable();
    return;
}

unsigned char spi_eeprom_read_byte (unsigned int32 Address)
{
    unsigned char Result;
   
    SSP2_PORTB3 = 0;        // EEPROM chip select set to low
   
    // send the EEPROM read command
    Result = spi_eeprom_write(SPI_EEPROM_CMD_READ);
   
    // send the EEPROM address
    Result = spi_eeprom_write(make8(Address, SPI_ADDRESS_OFFSET_2));
    Result = spi_eeprom_write(make8(Address, SPI_ADDRESS_OFFSET_1));
    Result = spi_eeprom_write(make8(Address, SPI_ADDRESS_OFFSET_0));
   
    // get the data from EEPROM
    Result = spi_eeprom_write(0);
   
    SSP2_PORTB3 = 1;        // EEPROM chip select set to high
    return Result;
}



Here's the test driver source file -- AT25M02_test.c
Code:

#include <18F8722.h>
#device ADC=16

#FUSES H4                        // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT                 // No code protection from reading
#FUSES NOWDT                     // No Watch Dog Timer
#FUSES NOBROWNOUT                // No brownout reset
#FUSES NOLVP                     // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT                       // power-up timer

#use delay(clock=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)


#include "AT25M02.h"
#include "AT25M02.c"


void main (void)
{
    unsigned int16 WrData;
    unsigned int16 RdData;
   
    // initialize HW SPI (MSSP) as master
    spi_eeprom_init();
   
    // write one byte to designated address
    WrData = 0xDEAD;
    // write MS byte into EEPROM address
    spi_eeprom_write_byte(make8(WrData, SPI_ADDRESS_OFFSET_1), 0x10);
    // write LS byte into EEPROM address
    spi_eeprom_write_byte(make8(WrData, SPI_ADDRESS_OFFSET_0), 0x11);
   
    // Now Readback one data from the serial eeprom
    // read MS byte from EEPROM address
    RdData = spi_eeprom_read_byte(0x10);
    RdData = make16(RdData, 0x00);
   
    // read LS byte from EEPROM address
    RdData |= (spi_eeprom_read_byte(0x11) & 0x00FF);

    // verify write and read SPI EEPROM (single byte)
    if (RdData != WrData)
    {
        // error: verify failed
        printf("ERROR: Data written mismatch with data read\n");
    }   
   
    printf("Done\n");
    while (TRUE);
    return;
}


Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Oct 21, 2017 11:51 pm     Reply with quote

Seriously, just use #USE SPI.

All you need to do is to remove all the actual data transfers from the existing code, and for each byte replace this with xpi_xfer. It accepts mode 0 or 3.
It'll then work with different chips without having to change settings.

Half your code disappears.
The .h file
Code:

#if !defined(__AT25M02_H__)
#define __AT25M02_H__

#USE SPI(SPI1, MODE=3, STREAM=AT25)

#define EEPROM_CS PIN_B3 //change to suit your hardware

// EEPROM Instruction Set
#define SPI_EEPROM_CMD_WRITE                    (0x02L)
#define SPI_EEPROM_CMD_READ                     (0x03L)
#define SPI_EEPROM_CMD_WRDI                     (0x04L)
#define SPI_EEPROM_CMD_RDSR                     (0x05L)
#define SPI_EEPROM_CMD_WREN                     (0x06L)

//-------------------------------------------------------------------------
// Global Structure Definitions
//-------------------------------------------------------------------------

struct EepromStatRegStruct
{
    unsigned char BSY   :1;
    unsigned char WEL   :1;
    unsigned char BP0   :1;
    unsigned char BP1   :1;
    unsigned char RFU   :3;
    unsigned char WPEN  :1;
};

union EepromStatRegUnion
{
    struct EepromStatRegStruct EepromStatReg;
    unsigned char EepromStatus;
};

#define COMBINE(x) Address=((Address & 0xFFFFFF) | (x<<24))
//Jut include the actual driver
#include <AT25M02.c>

#endif


It is only worth having a separate include without loading the driver, on compilers where you do a link compile.

Then the .c
Code:

void spi_eeprom_init (void)
{
    output_high(EEPROM_CS);
    disable_interrupts(INT_SSP);
}

union EepromStatRegUnion spi_eeprom_read_status (void)
{
    unsigned char Result;
   
    output_low(EEPROM_CS);
   
    // send the EEPROM read status register
    Result = spi_xfer(AT25, SPI_EEPROM_CMD_RDSR,8);
   
    // get the EEPROM status register
    Result = spi_xfer(AT25,0,8);
    output_high(EEPROM_CS);
   
    return (union EepromStatRegUnion)Result;
}

void spi_eeprom_write_enable (void)
{
    unsigned char Result;
   
    output_low(EEPROM_CS);  // EEPROM chip select set to low
    Result = spi_xferAT25,SPI_EEPROM_CMD_WREN,8);
    output_high(EEPROM_CS);;   // EEPROM chip select set to high
}

void spi_eeprom_write_disable (void)
{
    unsigned char Result;
   
    output_low(EEPROM_CS);  // EEPROM chip select set to low
    Result = spi_xferAT25,SPI_EEPROM_CMD_WRDI,8);
    output_high(EEPROM_CS);;   // EEPROM chip select set to high
}

void spi_eeprom_write_byte (unsigned char Data,
                            unsigned int32 Address)
{
    unsigned char Result;
    union EepromStatRegUnion Status;
   
    // enable write operation
    spi_eeprom_write_enable();
    output_low(EEPROM_CS);        // EEPROM chip select set to low
    COMBINE(SPI_EEPROM_CMD_WRITE);
   
    // send to the EEPROM
    Result = spi_xfer(AT25,Address,32);
   
    // send the data to write
    Result = spi_xfer(AT25,Data,8);
    output_high(EEPROM_CS);        // EEPROM chip select set to high
   
    // wait for completion of previous write operation
    do
    {
        Status = spi_eeprom_read_status();
    } while (Status.EepromStatReg.BSY);
   
    // disable write operation
    spi_eeprom_write_disable();
    return;
}

unsigned char spi_eeprom_read_byte (unsigned int32 Address)
{
    unsigned char Result;
 
    output_low(EEPROM_CS);      // EEPROM chip select set to low
 
    COMBINE(SPI_EEPROM_CMD_READ);

    // send the EEPROM address
    Result = spi_xfer(AT25,Address,32);

    // get the data from EEPROM
    Result = spi_xfer(AT25,0,8);

    output_high(EEPROM_CS);        // EEPROM chip select set to high
    return Result;
}


Then just include the .h in your main.

Now, I've typed this in untried on a computer without the compiler, so probably some typing errors, but this should be very close.

The compiler should optimise the 24bit shift into a single byte move. All I do is clock the address and command out as a single 32bit transfer.

I've also added an 'L' to the defined instructions so they will be shifted as long values to ensure this works.
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PostPosted: Sun Oct 22, 2017 1:15 am     Reply with quote

Hi Ttelmah,

I noticed the #use spi() has MODE=3 set. Why is so? Although AT25M02 supports both mode 0 and 3.

Now on using spi_xfer, I noticed you use the code snippet as:
Code:

Result = spi_xfer(AT25,Address,32);


What interests me is spi_xfer transferring 32-bits to SPI bus? Is the MSB of this 32-bit the EEPROM command while the LSB is the LSB of the Address as illustrated below or the other way around ?
Code:

-----------------------------------------------------------------------
|  EEPROM Cmd (8bit)  |  Addr[23..16]  |  Addr[15..8]  |  Addr[7..0]  |
-----------------------------------------------------------------------


Basically, I want to know how spi_xfer() works.

Thanks.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sun Oct 22, 2017 1:42 am     Reply with quote

I remember some other EEPROM's proving more reliable with Mode3, than Mode0. On the basis of this I went with Mode3.

The manual does tell you about spi_xfer. It can be programmed in the #USE to transfer a specific number of bits, but defaults to allowing up to 32. When setup like this, you can then specify how many bits to send with the xfer.

SPI by default always transfers stuff MSB/MSb first. So if you look at the data sheet for the chip it shows 32bits being clocked to it, comprising the command, and then 24bits of address. I decided to just do this as one transfer. It'd actually be fractionally simpler to define the commands as 32bit values, so 0x02000000 for example, but the compiler is 'smart' here and if you specify a constant value like this and then rotate it three bytes, it only takes the low one and transfers it directly into the high byte of the result, so 'nothing lost'. Smile

It may well be wrong, as I said, not at a computer with the compiler at present, so typed it directly from your code, but it should be very close. Smile
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PostPosted: Wed Oct 25, 2017 5:16 pm     Reply with quote

Hi Ttelmah,

Already tested the code and it works perfectly. Thank you for that clever suggestions.
ece_jm



Joined: 13 Feb 2018
Posts: 1

View user's profile Send private message

PostPosted: Tue Feb 13, 2018 6:23 pm     Reply with quote

Thanks for this. I am working on the same chips PIC18F8722 and AT25M02 memory chip. What is the final test code that worked?
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PostPosted: Sat Mar 03, 2018 2:23 am     Reply with quote

The project was continued by my colleague and I do not know her progress while I was transferred to another project.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group