EEPROM-programming on 9S12-controllers
10-15-2008
10:31 AM
4,507 次查看
I have stumbled over a strange problem regarding EEPROM-programming on several different controllers in the 9S12-family. I have tested this on the following parts, and all behave in the same way: MC9S12DT128MPVE (chipset 1L59W), MC9S12D32VFUE (chipset 0M89C) and MC9S12XDT256CAL (chipset 0L15Y). I am using the same routines for EEPROM-handling on all parts.
The routine I am using to write a 4 byte page to the EEPROM starts by using the Sector-Modify command (0x60) to erase the page and program the first two bytes, then I wait for CBEIF (Command Buffer Empty Flag), continue with a Program command (0x20) to program the second two bytes and finally wait for both CBEIF and CCIF (Command Complete Flag).
This works (and has worked for a long time) fine, until I yesterday stumbled over something very strange. If the second two bytes of a page (any page anywhere in the EEPROM) is programmed to 10 4f, those two bytes remains erased (ff ff) and the 10 4f appears as the two first bytes in the next page to program.
This happens only with the exact bytes 0x10 and 0x4f and when programming to the second two bytes of a EEPROM page. For example, if I want to program 0 0 10 4f to adress 810 and 0 0 0 0 to address 820 the result will be 0 0 ff ff at 810 and 10 4f 0 0 at address 820.
I have fould out that if I wait also for the CCIF-bit after the Sector-Modify command (before using the Program command to write the 10 4f), it works ok. But should this be needed, because I am continuing the programming process, shouldn't it be enough to wait just for the CBEIF-bit? And why does it work with any other byte combination, but not with 10 4f?
Sten
8 回复数
10-17-2008
05:23 AM
1,626 次查看
Lundin:
yes, I will also now modify all my programs to also wait for CCIF. My routines were based on the ones generated by some older version of Processor Expert, but I have noticed that since v. 4.7, they have changed the PE-routines to also wait for CCIF. The behavior is not dependent of the number of zeroes, it is only the exact combination 10 4F that fails, e.g. 20 4F (with the same number of of 0-bits) and 00 00 works fine.
kef:
did you try to a erased sector or did the sector contain something. If I write 10 4f to a sector already containing xx xx ff ff (to get xx xx 10 4f) it also works for me, but if I first fill the sector with some other contents (eg. 00 00 00 00) and then do a sector-modify and program sequence to get 00 00 10 4f, it fails.
Here is an extract from my code:
Code:
void main(void) {.... prog_eeprom_WORD(0x0c00, 0); /* clear 0x0c00..0x0c07 */ prog_eeprom_WORD(0x0c02, 0); prog_eeprom_WORD(0x0c04, 0); prog_eeprom_WORD(0x0c06, 0); prog_eeprom_WORD(0x0c02, 0x104f); /* try to write 0x104f to 0x0c02 (will be 0xffff) */ prog_eeprom_WORD(0x0c06, 0x1234); /* try to write something to 0x0c06 (succeeds but 0x0c04 will be 0x104f) */......} /* Note that low_level_WriteWORD does NOT erase nor verify the EEPROM for erased state */ /* the timer interrupt must be disabled during this time (parameters in EEPROM are not accessable during this operation) */void low_level_WriteWORD(WORD addr, WORD data16) { BYTE iflag; if (*(volatile WORD *) addr == data16) return; /* no need to program */ if (ESTAT_CBEIF == 0) { /* Command Buffer not empty— */ int_err |= 2; /* flag an OnChip EEPROM internal error */ return; }// keep the interrupts enabled, to prevent SCI OverRuns, just disable the Modulus Counter Interrupt iflag = MCCTL_MCZI – 1 : 0; MCCTL_MCZI = 0; /* Disable Modulus Counter Interrupt */ ESTAT = 0x30; /* writing a 1 to the error flags PVIOL and ACCERR resets them */ *(volatile WORD *) addr = data16; /* Array address and program data */ ECMD = WORD_Program; /* WORD program command */ ESTAT_CBEIF = 1; /* Clear flag command buffer empty */ if (ESTAT_PVIOL || ESTAT_ACCERR) { int_err |= 4; /* flag an OnChip EEPROM internal error */ goto ww_exit; } while (ESTAT_CBEIF == 0); /* Wait to buffer empty */ while (ESTAT_CCIF == 0); /* Wait for command completition */ww_exit: if (iflag) MCCTL_MCZI = 1; /* Reenable Modulus Counter Interrupt */ return;} /* the timer interrupt must be disabled during this time (parameters in EEPROM are not accessable during this operation) */void low_level_WriteSector(WORD sect_addr, DWORD data32) { BYTE iflag; if (*(volatile DWORD *) sect_addr == data32) return; /* no need to program */ if (ESTAT_CBEIF == 0) { /* Command Buffer not empty˜ */ int_err |= 2; /* flag an OnChip EEPROM internal error */ return; }// keep the interrupts enabled, to prevent SCI OverRuns, just disable the Modulus Counter Interrupt iflag = MCCTL_MCZI ™ 1 : 0; MCCTL_MCZI = 0; /* Disable Modulus Counter Interrupt */ ESTAT = 0x30; /* writing a 1 to the error flags PVIOL and ACCERR resets them */ *(volatile WORD *) sect_addr = (WORD)(data32 >> 16); /* Array address and program data - higher part */ ECMD = Sector_Modify; /* Sector modify command */ ESTAT_CBEIF = 1; /* Clear flag command buffer empty */ if (ESTAT_PVIOL || ESTAT_ACCERR) { int_err |= 4; /* flag an OnChip EEPROM internal error */ goto ws_exit; } while (ESTAT_CBEIF == 0); /* Wait to buffer empty */ low_level_WriteWORD(sect_addr + 2, (WORD) data32); /* Write lower part */ws_exit: if (iflag) MCCTL_MCZI = 1; /* Reenable Modulus Counter Interrupt */ return;} /* program one word in the internal EEPROM */ /* - if the bytes fall into different sectors */ /* - program the word as two bytes */ /* - else if the word is aligned and erased */ /* - just program that word */ /* else */ /* - read the 4-byte sector it belongs to */ /* - erase the sector */ /* - program back the modified sector */void prog_eeprom_WORD(WORD Addr, WORD Data) { union { BYTE b[4]; WORD w[2]; DWORD l; } backup; BYTE idx = Addr & 0x0003; if (*(volatile WORD *) Addr == Data) return; /* no need to program */ if (Addr < EEPROM_START || Addr > EEPROM_END) { /* Is given address out of EEPROM area array */ *(WORD *) Addr = Data; return; } if (idx == 3) { // the word spans over two segments prog_eeprom_BYTE(Addr, Data >> 8); prog_eeprom_BYTE(Addr + 1, Data & 0xff); } else if ((Addr & 1) == 0 && *(volatile WORD *) Addr == 0xFFFF) low_level_WriteWORD(Addr, Data); /* Write new content directly if word aligned and already erased */ else { /* Not erased or not word aligned */ backup.l = *(volatile DWORD *) (Addr & 0xFFFC); /* Load sector to variable backup (from sector-aligned address) */ backup.b[idx] = Data >> 8; /* Store data to variable backup */ backup.b[idx + 1] = Data & 0xff; low_level_WriteSector(Addr & 0xFFFC, backup.l); /* Erase sector and write new content */ }}
10-17-2008
07:19 AM
1,626 次查看
Hm, works for me on 1L59W. With RAM remapped to 0x2000 and EEPROM to 0x800.
Both step by step in BDM mode and at full speed in normal mode. Of course I removed
calls to not defined prog_eeprom_BYTE().
BTW IMO this is a bad piece of code
Code:
ESTAT_CBEIF = 1; /* Clear flag command buffer empty */ if (ESTAT_PVIOL || ESTAT_ACCERR) { ... }
Clearing CBEIF as you do above, you also may write ones to PVIOL and ACCERR bits!
Every time PVIOL or ACCERR, or both are set, you clear them doing ESTAT_CBEIF=1.
You should use ESTAT = CBEIF_MASK to clear just CBEIF.
10-17-2008
10:45 AM
1,626 次查看
Found it! My fault!
The first condition in the low_level_WriteWORD()-routine (if (*(volatile WORD *) addr == data16) return is not allowed in that stage! It tries to check if the contents of the EEPROM already is correct and skip the programming in that case, but because this routine is called from low_level_WriteSector which just has started a SectorModify-command, the EEPROM is busy and unreadable!
It seems that a busy EEPROM returns 104f when read, and that will cause the low_level_WriteWORD()-routine to be skipped when trying to program 104f.
kef:
ESTAT_CBEIF = 1; compiles to BSET _ESTAT,#128. Isn't that as safe as ESTAT = ESTAT_CBEIF_MASK (which compiles to LDAB #128, STAB _ESTAT)?
Sten
10-17-2008
01:11 PM
1,626 次查看
Glad you found it!
ESTAT_CBEIF = 1 and BSET _ESTAT,#128 and LDAB #128 STAB _ESTAT DO CLEAR NOT ONLY CBEIF flag, but also other writeable flags (ACCERR, PVIOL). It doesn't make sense to check if ACCERR or PVIOL are set after ESTAT_CBEIF = 1. You should use
ESTAT = ESTAT_CBEIF_MASK
or
ESTAT &= ESTAT_CBEIF_MASK
or
BCLR ESTAT, #127 // 127 = ~0x80
These 3 varianst will clear just CBEIF
10-16-2008
02:59 PM
1,626 次查看
I always wait for CCIF after erase, as advised by Freescale. See the example flowchart for an eeprom programming algorithm in the manual.
Edit: The reason why your partcular example isn't working might be because it contains a lot of binary zeroes. To me, it makes sense that zeroes takes longer time to program than ones, since the cell is set to FF after reset. Bit far-fetched maybe... But try the value 00h.
Message Edited by Lundin on 2008-10-16 05:02 PM
Edit: The reason why your partcular example isn't working might be because it contains a lot of binary zeroes. To me, it makes sense that zeroes takes longer time to program than ones, since the cell is set to FF after reset. Bit far-fetched maybe... But try the value 00h.
Message Edited by Lundin on 2008-10-16 05:02 PM
10-15-2008
03:53 PM
1,626 次查看
J2MEJediMaster
Specialist I
That sounds really odd. I have no idea why that is happening. Please file a service request on this.
---Tom
---Tom