|
|
View previous topic :: View next topic |
Author |
Message |
ralph79
Joined: 29 Aug 2007 Posts: 87
|
PIC12f529T39A read flash memory |
Posted: Thu Oct 12, 2017 10:15 am |
|
|
Hi,
I'm having a problem trying to write/read flash memory on PIC12F529T39A, so I'm trying to follow the datasheet instructions but no success. I tried some functions for similar pic's (like pic12f519), from this forum, but again without luck.
The code i have is this:
Code: |
#include <12F529T39A.h>
#use delay(internal=4000000)
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOPROTECT //Code protected from reads
#FUSES NOPROTECTDF
#byte EEADR=getenv("SFR:EEADR")
#byte EECON=getenv("SFR:EECON")
#byte EEDATA=getenv("SFR:EEDATA") //EE registers
#bit RD=getenv("BIT:RD")
#bit WR=getenv("BIT:WR")
#bit FREE=getenv("BIT:FREE")
#bit WREN=getenv("BIT:WREN") //EE control bits
#define port_b_dir 0b001011
//The 'flash data memory' on this chip behaves like a sort of hybrid between
//flash program memory, and EEPROM. The erase size if just 8bytes (so not
//a terribly large 'page', and the control only has a simple 'unlock' sequence
//Implements four routines:
// write_byte_flash(address, value) - writes 'value' to 'address'
// read_byte_flash(address) - returns the byte at 'address'
// erase_row_flash(address) - erases the eight byte row containing 'address'
// write_as_eeprom(address, value) - this is the complex one
//it reads the byte at 'address' and if bits only have to turn 'off' (1->0)
//simply writes the byte. If however any bit has to turn 'on', this cannot
//be done without erasing the page. In this case it reads the whole page
//into a RAM buffer, erases the page, changes the one byte needed, and
//writes the page back. Allows the data flash to be 'written' as if it was
//EEPROM (hence the name), and will not erase if it is not required to do so.
void write_byte_flash(unsigned int8 address, unsigned int8 value)
{
//write a single byte to an address. This does not erase, so a bit cannot
//be set to '1' by this routine.
EEADR=address; //select address
EEDATA=value; //data to write
WREN=TRUE; //trigger a write
WR=TRUE;
}
unsigned int8 read_byte_flash(unsigned int8 address)
{
//read a single byte from the flash
unsigned int8 temp;
EEADR=address; //select address
RD=TRUE; //trigger a read
temp=EEDATA;
return temp;
}
void erase_row_flash(unsigned int8 address)
{
//The erase uses the top three bits of the specified address as the page to erase
EEADR=address; //select address
FREE=TRUE;
WREN=TRUE;
WR=TRUE; //trigger the erase
}
//
void write_as_eeprom(unsigned int8 address, unsigned int8 value)
{
//this allows a single byte to be written to any address and automatically
//erases if necessary.
unsigned int8 low3; //low 3 bits of address
//unsigned int8 buffer[8];
unsigned int8 temp;
low3=address&7; //index into the buffer
//Now I need to determine if the row has to be erased.
temp=read_byte_flash(address);
//Now if writing the byte would only set bits to zero, an erase is not needed
if ((value & temp) == value)
{
//Just write
write_byte_flash(address,value);
return;
}
otherwise we need to erase the row.
First read the row.
for (temp=0;temp<8;temp++)
{
buffer[temp]=read_byte_flash((address&0x38)+temp);
}
//Now update the byte to change
buffer[low3]=value;
//erase the row
erase_row_flash(address);
//and write back all eight bytes
for (temp=0;temp<8;temp++)
{
write_byte_flash((address&0x38)+temp, buffer[temp]);
}
}
void main()
{
int8 temp;
set_tris_b(port_b_dir);
setup_wdt(PIN_CHANGE_FROM_SLEEP);
write_as_eeprom(0,0xAA);
write_as_eeprom(5,0xAA);
write_as_eeprom(15,0xAA);
write_as_eeprom(25,0xAA);
write_as_eeprom(35,0xAA);
while (TRUE)
{
output_high(PIN_B4);
delay_ms(200);
output_low(PIN_B4);
delay_ms(200);
}
}
|
As you can see i'm writing 1 byte in 5 different positions of flash memory. When i try to read the flash memory using MPLAB X IDE or IPE i only see 0xFF, and this way i see that i can't really write in flash.
Do you have any thoughts on how to access to read and write the flash?
This code was compiled with the CCS 5.070. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Oct 12, 2017 8:33 pm |
|
|
I don't have vs. 5.070 or any current version of the PCB compiler so I
can't check if the compiler is working properly.
But the first thing I would do is look at the .LST file and make sure
that CCS has all the register addresses and bit positions correct.
Consult the PIC data sheet to see the correct addresses.
Code: |
#byte EEADR=getenv("SFR:EEADR")
#byte EECON=getenv("SFR:EECON")
#byte EEDATA=getenv("SFR:EEDATA") //EE registers
#bit RD=getenv("BIT:RD")
#bit WR=getenv("BIT:WR")
#bit FREE=getenv("BIT:FREE")
#bit WREN=getenv("BIT:WREN") //EE control bits |
The 2nd thing is, I wouldn't trust MPLAB or the IPE to show me the results.
I would setup a serial port connection and use printf to show the results
on a serial terminal. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Fri Oct 13, 2017 2:21 am |
|
|
I'd also start testing in parts. Put something into an address in the memory at compile time #ROM 0x600=xxxx
Then test your read routine, and see if this reads OK.
You then 'know' you have a working read, so have a starting point to test the writes.
Your code as posted won't compile (remarks missing off lines 83 & 84, and 'buffer' is remmed out).
0xFF is the 'I am erased' state, so I'd be then looking at the write routine.
A quick check with the bugs above corrected, shows the correct code being generated. The compiler is using the bit sets required, and on the correct bits.
The CCS write_eeprom routine does access the registers for this area of memory. However they have omitted an option to erase. So you can substitute their write_eeprom routine for the write_byte_flash routine. I notice that they do perform two NOP's after setting the WR bit. Most chips require this so it is worth trying. Their read_eeprom routine also seems to correctly encode the read_byte_flash, with them again waiting one clock cycle after initiating the read.
Now I can't get the current MPLAB-X to actually accept simulating on the chip. It gives odd errors. So can't try, but:
Code: |
#include <12F529T39A.h>
#use delay(internal=4000000)
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOPROTECT //Code protected from reads
#FUSES NOPROTECTDF
#FUSES NOWDT
#byte EEADR=getenv("SFR:EEADR")
#byte EECON=getenv("SFR:EECON")
#byte EEDATA=getenv("SFR:EEDATA") //EE registers
#bit RD=getenv("BIT:RD")
#bit WR=getenv("BIT:WR")
#bit FREE=getenv("BIT:FREE")
#bit WREN=getenv("BIT:WREN") //EE control bits
#define port_b_dir 0b001011
//The 'flash data memory' on this chip behaves like a sort of hybrid between
//flash program memory, and EEPROM. The erase size if just 8bytes (so not
//a terribly large 'page', and the control only has a simple 'unlock' sequence
//Implements four routines:
// write_byte_flash(address, value) - writes 'value' to 'address'
// read_byte_flash(address) - returns the byte at 'address'
//substituted the CCS routines for these. Slightly smaller.
// erase_row_flash(address) - erases the eight byte row containing 'address'
// write_as_eeprom(address, value) - this is the complex one
//it reads the byte at 'address' and if bits only have to turn 'off' (1->0)
//simply writes the byte. If however any bit has to turn 'on', this cannot
//be done without erasing the page. In this case it reads the whole page
//into a RAM buffer, erases the page, changes the one byte needed, and
//writes the page back. Allows the data flash to be 'written' as if it was
//EEPROM (hence the name), and will not erase if it is not required to do so.
void erase_row_flash(unsigned int8 address)
{
//The erase uses the top three bits of the specified address as the page to erase
EEADR=address; //select address
FREE=TRUE;
WREN=TRUE;
WR=TRUE; //trigger the erase
}
//
void write_as_eeprom(unsigned int8 address, unsigned int8 value)
{
//this allows a single byte to be written to any address and automatically
//erases if necessary.
unsigned int8 low3; //low 3 bits of address
unsigned int8 buffer[8];
unsigned int8 temp;
low3=address&7; //index into the buffer
//Now I need to determine if the row has to be erased.
temp=read_eeprom(address);
//Now if writing the byte would only set bits to zero, an erase is not needed
if ((value & temp) == value)
{
//Just write
write_eeprom(address,value);
return;
}
//otherwise we need to erase the row.
//First read the row.
for (temp=0;temp<8;temp++)
{
buffer[temp]=read_eeprom((address&0x38)+temp);
}
//Now update the byte to change
buffer[low3]=value;
//erase the row
erase_row_flash(address);
//and write back all eight bytes
for (temp=0;temp<8;temp++)
{
write_eeprom((address&0x38)+temp, buffer[temp]);
}
}
void main()
{
int8 temp;
set_tris_b(port_b_dir);
setup_wdt(PIN_CHANGE_FROM_SLEEP);
write_as_eeprom(0,0xAA);
write_as_eeprom(5,0xAA);
write_as_eeprom(15,0xAA);
write_as_eeprom(25,0xAA);
write_as_eeprom(35,0xAA);
temp=read_eeprom(0); //test if a byte has been saved.
while (TRUE)
{
output_high(PIN_B4);
delay_ms(200);
output_low(PIN_B4);
delay_ms(200);
}
}
|
Looks to be generating sensible assembler for all the functions, with your compiler version.
If you have got the debugger running, test the value in 'temp', to see if it is working. |
|
|
ralph79
Joined: 29 Aug 2007 Posts: 87
|
|
Posted: Fri Oct 13, 2017 8:25 am |
|
|
Hi, thanks for your answer.
So i've been testing around with the code you showed and i found out that it doesn't really work the way its intended.
Code: |
void main()
{
set_tris_b(port_b_dir);
setup_wdt(PIN_CHANGE_FROM_SLEEP);
while (TRUE)
{
if(!input(PIN_B3))
{
write_as_eeprom(0x15,0xEA);
//write_byte_flash(0x15,0xEA);
delay_ms(500);
}
if(0xEA == read_eeprom(0x15))
{
output_high(PIN_B4);
delay_ms(500);
output_low(PIN_B4);
delay_ms(500);
}
else
{
output_high(PIN_B4);
delay_ms(100);
output_low(PIN_B4);
delay_ms(100);
}
}
} |
So i made a LED blink (PIN_B4) with the frequency depending in the condition "if(0xEA == read_eeprom(0x15))", for debug.
Whenever PIN_B3 gets value 0, the PIN_B4 will toggle faster and it actually does.
I tried with write_as_eeprom and write_byte_flash, but everytime i remove the power and insert again the flash gets erased. The only change made to the program was the main function, any thoughts why this is happening? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Fri Oct 13, 2017 9:27 am |
|
|
Try something silly.
Try setting the PROTECTDF fuse and have another go.
I have seen a few PIC's where fuses are reversed from the data sheet...
Now it is unusual to have the 'protection off' state being the erased 'default'. Normally protection defaults to 'on' on erased chips and has to be turned off. Just wonder if this bit is actually reversed and nobody has noticed!.... |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Fri Oct 13, 2017 11:25 am |
|
|
Just been doing a bit more reading.
It is interesting to look at the PIC16F526 which is another chip with the flash data memory. This specifically says:
Quote: |
2: For reads, erases and writes to the Flash
data memory, there is no need to insert a
NOP into the user code as is done on midrange
devices. The instruction immediately
following the “BSF EECON,WR/RD” will be
fetched and executed properly
|
Now, the PIC12F529T39A, doesn't say this, though the code included leaves these NOP's out. I'm wondering if it does need these?.
So try with:
Code: |
void write_byte_flash(unsigned int8 address, unsigned int8 value)
{
//write a single byte to an address. This does not erase, so a bit cannot
//be set to '1' by this routine.
EEADR=address; //select address
EEDATA=value; //data to write
WREN=TRUE; //trigger a write
WR=TRUE;
delay_cycles(1);
}
unsigned int8 read_byte_flash(unsigned int8 address)
{
//read a single byte from the flash
unsigned int8 temp;
EEADR=address; //select address
RD=TRUE; //trigger a read
delay_cycles(1);
temp=EEDATA;
return temp;
}
void erase_row_flash(unsigned int8 address)
{
//The erase uses the top three bits of the specified address as the page to erase
EEADR=address; //select address
FREE=TRUE;
WREN=TRUE;
WR=TRUE; //trigger the erase
delay_cycles(1);
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Fri Oct 13, 2017 2:28 pm |
|
|
Just had a reply back from CCS. They have got an erase_eeprom function in the code (not documented in the header...). They have sent me a little demo, which I have modified to match your original code:
Code: |
//
void write_as_eeprom(unsigned int8 address, unsigned int8 value)
{
//this allows a single byte to be written to any address and automatically
//erases if necessary.
unsigned int8 temp; //single temp variable
unsigned int8 buffer[8];
unsigned int8 low3; //low part of address
//Now I need to determine if the row has to be erased.
temp=read_eeprom(address);
//Now if writing the byte would only set bits to zero, an erase is not needed
if ((value & temp) == value)
{
//Just write
write_eeprom(address,value);
return;
}
low3=address&7; //index into the buffer - no point in doing this earlier
address &=0xF8; //and the high bits
//otherwise we need to erase the row.
//First read the row.
for (temp=0;temp<8;temp++)
{
buffer[temp]=read_eeprom(address+temp);
}
//Now update the byte to change
buffer[low3]=value;
//erase the row
erase_eeprom(address);
//and write back all eight bytes
for (temp=0;temp<8;temp++)
{
write_eeprom(address+temp, buffer[temp]);
}
}
|
As you can see I've just slightly optimised things which will save a little space and a few machine cycles.
See if this works.
If it doesn't, it has to be the fuse, or an issue with your chip/hardware (what voltage are you running? - though the data sheet lists Vmin as the minimum voltage for an erase, I have seen several PICs where an erase doesn't work near the bottom of the supply range...). |
|
|
ralph79
Joined: 29 Aug 2007 Posts: 87
|
|
Posted: Mon Oct 16, 2017 9:03 am |
|
|
Thanks for the answers but i had no sucess even with that function. I'm running at +/-3V.
I changed the fuses as you suggested but the result was worse, in run time it stopped work (not only when i removed the power).
So i tried some things with XC8 and looking to the .lst file, i was able to make it work in ccs, without erasing after reset.
The code i made to read was:
Code: | #asm
MOVLW 21
MOVLB 1
MOVWF 6
BSF 1,0
MOVF 5,w
MOVLB 0
MOVWF test
#endasm
|
And it's working with SQTP. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Mon Oct 16, 2017 10:47 am |
|
|
That's the same as 'test=read_eeprom(33);' (depending on what bank 'test' is in). |
|
|
|
|
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
|