|
|
View previous topic :: View next topic |
Author |
Message |
newguy
Joined: 24 Jun 2004 Posts: 1908
|
Hardware SPI <-> 25LC320 EEPROM question |
Posted: Wed Sep 13, 2006 1:54 pm |
|
|
A long time ago I tried using the built in h/w SPI module to communicate with an EEPROM and eventually gave up. I ended up bit-banging the comms, and was able to get everything to work.
NOTE: My hardware is working. I used the '25320.c' driver to verify this.
Now I'm coming back to the h/w SPI module and I'm trying to get it to work.....and can't. This is just one of those things that I want to make work not because I need it but because I want it.
My almost complete code listing follows. I say almost complete because I'm not including the lcd driver file as it is not relevant.
Code: | #include <18F4680.h>
#device adc=8
#FUSES WDT
#FUSES WDT64 //Watch Dog Timer uses 1:64 Postscale
#FUSES HS //High speed Osc (> 4mhz)
#FUSES NOPROTECT //Code not protected from reading
#FUSES BROWNOUT //Reset when brownout detected
#FUSES BORV43 //Brownout reset at 4.3V
#FUSES PUT //Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES NOIESO //Internal External Switch Over mode disabled
#FUSES NOFCMEN //Fail-safe clock monitor disabled
#FUSES NOPBADEN //PORTB pins are configured as digital I/O on RESET
#FUSES BBSIZ4K //4K words Boot Block size
#FUSES NOWRTC //configuration not registers write protected
#FUSES NOWRTB //Boot block not write protected
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES NOCPB //No Boot Block code protection
#FUSES LPT1OSC //Timer1 configured for low-power operation
#FUSES MCLR //Master Clear pin enabled
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#use delay(clock=20000000,RESTART_WDT)
int8 i, j;
// Microchip 25LC320 SPI EEPROM
// /CS: pin A5
// MISO: pin C5
// MOSI: pin C4
// SCK: pin C3
#define CHIP_SELECT PIN_A5
#define WREN 0x06 // write enable
#define RDSR 0x05 // read status register
#define WRSR 0x01 // write status register
#define READ_EE 0x03 // read instruction
#define WRITE_EE 0x02 // write instruction
void enable_eeprom_write(void) {
// send the WREN (write enable) command to EEPROM
output_low(CHIP_SELECT);
spi_write(WREN);
output_high(CHIP_SELECT);
}
int1 check_eeprom_status(void) {
int8 data;
// checks the status of the WIP (write in progress) status bit
// returns 1 if ready, 0 if not
output_low(CHIP_SELECT);
spi_write(RDSR);
data = spi_read(0);
output_high(CHIP_SELECT);
printf(lcd_putc,"\nStatus = %u",data);
return (!bit_test(data, 0));
}
void init_eeprom(void) {
// initializes the block protect bits in the eeprom's status register
enable_eeprom_write();
output_low(CHIP_SELECT);
spi_write(WRSR);
spi_write(0x80); // sets bank protect bits to 0, and enables external WP line
output_high(CHIP_SELECT);
}
void write_byte_to_eeprom(int16 address, int8 data) {
int1 eeprom_ready;
// check status and keep looping until it is ready
eeprom_ready = check_eeprom_status();
while(!eeprom_ready) {
restart_wdt();
eeprom_ready = check_eeprom_status();
}
// eeprom no longer busy, so enable writes
enable_eeprom_write();
output_low(CHIP_SELECT);
spi_write(WRITE_EE);
spi_write(address >> 8); // address MSB
spi_write(address); // address LSB
spi_write(data);
output_high(CHIP_SELECT);
}
int8 read_data_from_eeprom(int16 address) {
int8 data;
int1 eeprom_ready;
// check status and keep looping until it is ready
eeprom_ready = check_eeprom_status();
while(!eeprom_ready) {
restart_wdt();
eeprom_ready = check_eeprom_status();
}
// eeprom no longer busy
output_low(CHIP_SELECT);
spi_write(READ_EE);
spi_write(address >> 8); // address MSB
spi_write(address); // address LSB
data = spi_read(0);
output_high(CHIP_SELECT);
return (data);
}
void main() {
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF|ADC_TAD_MUL_0);
setup_psp(PSP_DISABLED);
setup_spi(SPI_MASTER|SPI_H_TO_L|SPI_CLK_DIV_16);
setup_wdt(WDT_ON);
setup_timer_0(RTCC_OFF);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
setup_oscillator(False);
set_tris_e(0x00);
lcd_init();
lcd_putc("\fReady...");
set_tris_a(0x00); // all out
output_high(PIN_A5); // deselect EEPROM
delay_ms(250);
init_eeprom();
lcd_putc("\fInit successful");
restart_wdt();
write_byte_to_eeprom(0, 0xcc);
restart_wdt();
write_byte_to_eeprom(12, 0xab);
lcd_putc("\fData written");
while (TRUE) {
restart_wdt();
for (i = 0; i < 20; i++) {
delay_ms(500);
j = read_ext_eeprom(i);
printf(lcd_putc,"\f%x from %u",j,i);
restart_wdt();
}
}
} |
Here's the problem....The code hangs in the while (!eeprom_ready) loop because it reads 0xff from the EEPROM. Again, using the routines in the 25320.c driver from CCS, everything works.
What am I missing? Do I have to manually set the CLK and MOSI lines either high or low before I attempt communications with the chip? Do I have to manually set the port direction bits (I thought the MSSP module took control of them)? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Sep 13, 2006 2:19 pm |
|
|
Here is a hardware SPI driver for the 25LC640. This should help
you to write a driver for the 25LC320.
The test program fills up the EEPROM with random numbers and then
reads back the data and compares it to the original data. Any errors
found are reported. The dots show the progress in writing or reading.
Example of good output:
Code: |
writing................................
reading................................
done
|
Note: The \WP and \HOLD pins are both connected to +5v.
Here is the test program that calls the driver:
Code: | #include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#define SPI_MODE_0_0 0x4000
#define SPI_MODE_0_1 0x0000
#define SPI_MODE_1_0 0x0010
#define SPI_MODE_1_1 0x4010
/*
// If your hardware SPI pins are different than the
// ones used on the 16F877, then define them here
// and un-comment this section.
#define EEPROM_SELECT PIN_C2
#define EEPROM_CLK PIN_C3
#define EEPROM_DI PIN_C5
#define EEPROM_DO PIN_C4
*/
#include <25LC640_Hardware_SPI.c>
#include <STDLIB.H>
//========================
void main()
{
int8 data;
int8 wrote;
int16 addr;
int16 errors = 0;
init_ext_eeprom();
// Fill eeprom with random data.
printf("\n\r");
printf("writing");
srand(0x55);
for(addr = 0; addr < EEPROM_SIZE; addr++)
{
write_ext_eeprom(addr, (int8)rand());
if((int8)addr == 0)
putc('.');
}
// Read the eeprom and check for errors.
printf("\n\r");
printf("reading");
srand(0x55);
for(addr = 0; addr < EEPROM_SIZE; addr++)
{
data = read_ext_eeprom(addr);
wrote = (int8)rand();
if(data != wrote)
{
printf("%lx: read %x, should be %x\n\r", addr, data, wrote);
errors++;
if(errors >= 10)
break;
}
if((int8)addr == 0)
putc('.');
}
printf("\n\r");
printf("done\n\r");
while(1);
} |
Here is the hardware SPI driver for the 25LC640:
Code: |
#ifndef EEPROM_SELECT
#define EEPROM_SELECT PIN_C2
#define EEPROM_CLK PIN_C3
#define EEPROM_DI PIN_C5
#define EEPROM_DO PIN_C4
#endif
#define EEPROM_ADDRESS long int
#define EEPROM_SIZE 8192
void init_ext_eeprom()
{
output_high(EEPROM_SELECT);
setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_16 );
}
//--------------------------------
int1 ext_eeprom_ready(void)
{
int8 data;
output_low(EEPROM_SELECT);
spi_write(0x05);
data = spi_read(0);
output_high(EEPROM_SELECT);
return(!bit_test(data, 0));
}
//--------------------------------
void write_ext_eeprom(EEPROM_ADDRESS address, BYTE data)
{
while(!ext_eeprom_ready());
output_low(EEPROM_SELECT);
spi_write(0x06);
output_high(EEPROM_SELECT);
output_low(EEPROM_SELECT);
spi_write(0x02);
spi_write(address >> 8);
spi_write(address);
spi_write(data);
output_high(EEPROM_SELECT);
}
//--------------------------------
BYTE read_ext_eeprom(EEPROM_ADDRESS address)
{
int8 data;
while(!ext_eeprom_ready());
output_low(EEPROM_SELECT);
spi_write(0x03);
spi_write(address >> 8);
spi_write(address);
data = spi_read(0);
output_high(EEPROM_SELECT);
return(data);
} |
|
|
|
newguy
Joined: 24 Jun 2004 Posts: 1908
|
|
Posted: Wed Sep 13, 2006 2:57 pm |
|
|
Solved. Thanks PCM.
The issue is my prototyping board, an ME Labs LAB-X1. It has a socket for an 8 pin DIP SPI EEPROM. It has the MOSI and MISO lines reversed.
I pulled the EEPROM out of the socket and placed it in a separate protoboard and it works just fine now that pin C5 -> EEPROM's SI and pin C4 <- EEPROM's SO.
Geez. All this time wasted for something so simple and so obvious. Reinforced lesson: never trust that a commercial product is wired correctly. |
|
|
|
|
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
|