1 Introduction Almost every embedded system needs a non-volatile memory to store the application code; flash device is the most common one nowadays because it can be erased and programmed electronically. Most embedded development IDE comes with a flash programming utility to facilitate the programming of the application code into a flash device. However, if your application requires in-field updates of the code or non-volatile data storage, you’ll need a flash driver yourself for the upper layer application code to call to reprogram your code or write data into the flash.
While most flash devices are similar, they vary in structure and commands. Reading data from flash is easy, but writing data and erasing memory are done through a series of commands. Therefore, the key code is a function of writing one byte completely. Once you have that function working correctly, you can write as many bytes as you want. Some flash devices support multiple writes in one command. Here is an example of flash driver for Atmel AT49BV040B. That flash is a 512k x 8, parallel interface device. A typical hardware interface is shown in Figure 1.
2 Hardware Interface Figure 1 AT49BV040B flash interfacing with a DSP chip
3 Programming Reading data from flash is straightforward usually, while writing to flash requires specific attention. As long as you know how to write one byte into flash, you can write any number of data. So let’s focus on how to write one byte to flash and make sure it is written completely. This function shall include two parts; one is to write the byte into a memory, the other is to check when the write is completed. To detect the completion of a byte write, the device has two methods, data polling and toggle bit. We used the toggle bit method in the example shown below.
/******************************************************************************************* * Function: SendByte * * Desc: This function writes a byte to an address in flash and wait for completion. * * Params: * long lAddr - address to write to * char value - value to write * * Returns: true if data is written to the address successfully, false otherwise * * Notes: * 1 - None * *******************************************************************************************/ bool SendByte( const long lAddr, const char value ) { bool result; // set the address long *pFlashAddr = (long *)(FLASH_BASE_ADDR + lAddr);
//write commands *pFlashAddr = value;
// make sure the write was successful result = PollToggleBit(lAddr, value);
return result; }
/******************************************************************************************* * Function: PollToggleBit * * Desc: This function polls the toggle bit I/O6 to see when the operation is complete * * Params: * long lOffset - address offset * * Returns: true if data is written to the address successfully, false otherwise * * Notes: * 1 - None * *******************************************************************************************/ bool PollToggleBit( const long lOffset ) { char val1, val2; // 2 consecutive values read from flash char toggleBit; // used to see if I/O6 is toggling long timeout = FLASH_POLL_TIMEOUT; // timeout value
while(timeout--) { // read the same location 2 times ReadByte(lOffset, &val1); ReadByte(lOffset, &val2);
// check to see if bit 6 is toggling toggleBit = val1 ^ val2;
// if bit 6 stops toggling and the data matches what it should be // the write or erase is complete if( !(toggleBit & 0x40) ) break; }
// check to see if there was a timeout return (timeout > 0); }
With the above function, now we can write data and erase the chip. Note that each of those actions requires a series of commands, as shown in Table 1.
Table 1 AT49BV040B Command Table

FLASH_BASE_ADDR is a base address macro determined by the chip select signal.
3.1 Write Data /******************************************************************************************* * Function: WriteByte * * Desc: This function write one byte to an address in flash. * * Params: * long lAddr - address to write to * long value - value to write * * Returns: true if data is written to the address successfully, false otherwise * * Notes: None * Addr Data Addr Data Addr Data Addr Data * 555 AA AAA 55 555 A0 Addr Data *******************************************************************************************/ bool WriteByte( const long ulAddr, const char value ) { //write commands if (SendByte(0x555, 0xaa)) { if (SendByte(0xaaa, 0x55)) { if (SendByte(0x555, 0xa0)) { if (SendByte(lAddr, value)) { return true; } } } }
return false; }
The write wave form (wrting data 0xA to address 0x0) is shown in Figure 1.

3.2 Chip Erase /******************************************************************************************* * Function: Flash_EraseChip * * Desc: This function sends an "erase all" command to the flash * * Params: none * * Returns: true - if erase is successful, false otherwise * * Notes: Erase command: * Addr Data Addr Data Addr Data Addr Data Addr Data Addr Data * 555 AA AAA 55 555 80 555 AA AAA 55 555 10 * *******************************************************************************************/ bool Flash_EraseChip(void) { bool result; // flag to indicate error
//write commands result = SendByte(0x555, 0xaa); if (result) { result = SendByte(0xaaa, 0x55); if (result) { result = SendByte(0x555, 0x80); if (result) { result = SendByte(0x555, 0xaa); if (result) { result = SendByte(0xaaa, 0x55); if (result) { result = SendByte(0x555, 0x10); } } } } }
return result; }
3.3 Sector Erase /******************************************************************************************* * Function: Flash_EraseSector * * Desc: This function sends an "erase sector" command to the flash * * Params: long nSector - the sector number to be erased * * Returns: true - if erase is successful, false otherwise * * Notes: Erase command: * Addr Data Addr Data Addr Data Addr Data Addr Data Addr Data * 555 AA AAA 55 555 80 555 AA AAA 55 addr 30 * where: addr - any address within the sector *******************************************************************************************/ bool Flash_EraseSector(long nSector) { bool result; // flag to indicate error long lStartAddr, lEndAddr;
//get the start address of the sector Flash_GetSectorStartEnd(nSector, &lStartAddr, &lEndAddr);
//write commands result = SendByte(0x555, 0xaa); if (result) { result = SendByte(0xaaa, 0x55); if (result) { result = SendByte(0x555, 0x80); if (result) { result = SendByte(0x555, 0xaa); if (result) { result = SendByte(0xaaa, 0x55); if (result) { result = SendByte(lStartAddr, 0x30); } } } } }
return result; } 3.4 Read Data /******************************************************************************************* * Function: ReadByte * * Desc: This function reads one byte from an address in flash. * * Params: * long lAddr - the address to read from * char pValue - pointer to store the value read from flash * * Returns: void * * Notes: None * *******************************************************************************************/ void ReadByte( const long lAddr, char *pValue ) { // set flash address to where we want to read long *pFlashAddr = (long *)(FLASH_BASE_ADDR + lAddr);
// read the value *pValue = *pFlashAddr; }
The read wave form (reading data from address 0 to 9) is shown in Figure 2.

|