Chapter 8: Examples with Memory and Storage Media


Introduction

There is no program on this world that doesn’t interact with memory in some way. First, during its execution, it retains the operational data from, uses or alters it, and puts it back into the program memory. Second, it is often necessary to store and handle large amount of data that can be obtained from various sources, whether it is the car engine temperature acquisition data or some bitmap image to be displayed on the GLCD. In this chapter we will focus on the latter problem, i.e. we’ll go through the techniques of manipulating data on the so-called memory storage devices and systems.

8.1 EEPROM Memory

Data used by microcontroller is stored in the RAM memory as long as there is a power supply present. If we need to keep the data for later use, it has to be stored in a permanent memory. An EEPROM (E²PROM), or Electrically-Erasable Programmable Read-Only Memory is a non-volatile storage chip, commonly used with PIC microcontrollers for this purpose. An EEPROM can be programmed and erased multiple times electrically – it may be erased and reprogrammed only a certain number of times, ranging from 100,000 to 1,000,000, but it can be read an unlimited number of times.

8.1.1 Internal EEPROM

Some PIC microcontrollers have internal EEPROM allowing you to store information without any external hardware.

BASIC has a library for working with internal EEPROM which makes writing and reading data very easy. Library function EEPROM_Read reads data from a specified address, while library procedure EEPROM_Write writes data to the specified address.

Note: Be aware that all interrupts will be disabled during execution of EEPROM_Write routine (GIE bit of INTCON register will be cleared). Routine will set this bit on exit. Ensure minimum 20ms delay between successive use of routines EEPROM_Write and EEPROM_Read. Although EEPROM will write the correct value, EEPROM_Read might return undefined result.

In our following example, we will write a sequence of numbers to successive locations in EEPROM. Afterwards, we’ll read these and output to PORTB to verify the process.

program EEPROM_test

dim i as byte
dim j as byte

main:

  TRISB = 0
  for i = 0 to 20
   EEPROM_Write(i, i + 6)
  next i

  Delay_ms(30)

  for i = 0 to 20
     PORTB = EEPROM_Read(i)
     for j = 0 to 200
       Delay_us(500)
     next j
  next i

end.

8.1.2 Serial EEPROM

Occasionally, our needs will exceed the capacity of PIC’s internal EEPROM. When we need to store a larger amount of data obtained by PIC, we have an option of using external serial EEPROM. Serial means that EEPROM uses one of the serial protocols (I2C, SPI, microwire) for communication with microcontroller. In our example, we will work with EEPROM from 24Cxx family which uses two lines and I2C protocol for communication with MCU.

Serial EEPROM connects to microcontroller via SCL and SDA lines. SCL line is a clock for synchronizing the transfer via SDA line, with frequency going up to 1MHz.

I2C communication allows connecting multiple devices on a single line. Therefore, bits A1 and A0 have an option of assigning addresses to certain I2C devices by connecting the pins A1 and A0 to the ground and +5V (one I2C line could be EEPROM on address $A2 and, say, real time clock PCF8583 on address $A0). R/W bit of address byte selects the operation of reading or writing data to memory. More detailed data on I2C communication can be found in the technical documentation of any I2C device.

Our following program sends data to EEPROM at address 2. To verify transfer, we’ll read data via I2C from EEPROM and send its value to PORTD. For more information on I2C Library consult Chapter 5: Built-in and Library Routines.

program EEPROM_test

dim EE_adr as byte
dim EE_data as byte
dim jj as word

main:
    I2C_init(100000)      ' Initialize full master mode
    TRISD = 0             ' PORTD is output
    PORTD = $ff           ' Initialize PORTD
    I2C_Start             ' Issue I2C start signal
    I2C_Wr($a2)           ' Send byte via I2C(command to 24cO2)
    EE_adr  = 2
    I2C_Wr(EE_adr)        ' Send byte(address of EEPROM)
    EE_data = $aa
    I2C_Wr(EE_data)       ' Send data(data that will be written)
    I2C_Stop              ' Issue I2C stop signal

    for jj = 0 to 65500   ' Pause while EEPROM writes data
        nop
    next jj

    I2C_Start             ' Issue I2C start signal
    I2C_Wr($a2)           ' Send byte via I2C
    EE_adr = 2
    I2C_Wr(EE_adr)        ' Send byte(address for EEPROM)
    I2C_Repeated_Start    ' Issue I2C repeated start signal
    I2C_Wr($a3)           ' Send byte(request data from EEPROM)
    EE_data = I2C_Rd(1)   ' Read the data
    I2C_Stop              ' Issue I2C_Stop signal
    PORTD = EE_data       ' Print data on PORTD
noend:                    ' Endless loop
    goto noend
end.

8.2 Flash Memory

Flash memory is a form of EEPROM that allows multiple memory locations to be erased or written in one programming operation. Normal EEPROM only allows one location at a time to be erased or written, meaning that Flash can operate at higher effective speeds when the systems using it read and write to different locations at the same time.

Flash memory stores information on a silicon chip in a way that does not need power to maintain the information in the chip. This means that if you turn off the power to the chip, the information is retained without consuming any power. In addition, Flash offers fast read access times and solid-state shock resistance. These characteristics make it very popular for microcontroller applications and for applications such as storage on battery-powered devices like cell phones.

Many modern PIC microcontrollers utilize Flash memory, usually in addition to normal EEPROM storage chip. Therefore, BASIC provides a library for direct accessing and working with MCU’s Flash. Note: Routines differ for PIC16 and PIC18 families, please refer to Chapter 5: Built-in and Library Routines.

The following code demonstrates use of Flash Memory library routines:

' for PIC18

program flash_pic18_test

const FLASH_ERROR  = $FF
const FLASH_OK     = $AA

dim toRead as byte
dim i as byte
dim toWrite as byte[64]

main:
  TRISB = 0                          ' PORTB is output
  for i = 0 to 63                    ' initialize array
    toWrite[i] = i
  next i
  Flash_Write($0D00, toWrite)        ' write contents of the array to the address 0x0D00
  ' verify write
  PORTB  = 0                         ' turn off PORTB
  toRead = FLASH_ERROR               ' initialize error state

  for i = 0 to 63
      toRead = Flash_Read($0D00+i)   ' read 64 consecutive locations starting from 0x0D00
      if toRead <> toWrite[i] then   ' stop on first error

        PORTB = FLASH_ERROR          ' indicate error
        Delay_ms(500)
      else
        PORTB = FLASH_OK             ' indicate there is no error
      end if
  next i
end.
      	

For PIC16 family, the corresponding code looks like this:

' for PIC16

program flash_pic16_test

const FLASH_ERROR = $FF
const FLASH_OK    = $AA

dim toRead as word
dim i as word

main:
  TRISB = 0                         ' PORTB is output
  for i = 0 to 63
    Flash_Write(i+$0A00, i)         ' write the value of i starting from the address 0x0A00
  next i
  ' verify write
  PORTB  = 0                        ' turn off PORTB
  toRead = FLASH_ERROR              ' initialize error state
  for i = 0 to 63
    toRead = Flash_Read($0A00+i)    ' Read 64 consecutive locations starting from 0x0A00
    if toRead <> i then             ' Stop on first error
      i = i + $0A00                 ' i contains the address of the erroneous location
      PORTB = FLASH_ERROR           ' indicate error
      Delay_ms(500)
    else
      PORTB = FLASH_OK              ' indicate there is no error
    end if
  next i
end.

8.3 Compact Flash

Compact Flash (CF) was originally a type of data storage device, used in portable electronic devices. As a storage device, it typically uses Flash memory in a standardized enclosure. At present, the physical format is used in handheld and laptop computers, digital cameras, and a wide variety of other devices, including desktop computers. Great capacity (8MB ~ 8GB, and more) and excellent access time of typically few microseconds make them very attractive for microcontroller applications.

Flash memory devices are non-volatile and solid state, and thus are more robust than disk drives, consuming only about 5% of the power required by small disk drives. They operate at 3.3 volts or 5 volts, and can be swapped from system to system. CF cards are able to cope with extremely rapid changes in temperature – industrial versions of flash memory cards can operate at a range of -45°C to +85°C.

BASIC includes a library for accessing and handling data on Compact Flash card. In CF card, data is divided into sectors, one sector usually comprising 512 bytes (few older models have sectors of 256B). Read and write operations are not performed directly, but successively through 512B buffer. These routines are intented for use with CF that have FAT16 and FAT32 file system. Note: routines for file handling (CF_File_Write_Init, CF_File_Write_Byte, CF_File_Write_Complete) can only be used with FAT16 file system, and only with PIC18 family!

File accessing routines can write file. File names must be exactly 8 characters long and written in uppercase. User must ensure different names for each file, as CF routines will not check for possible match. Before write operation, make sure you don't overwrite boot or FAT sector as it could make your card on PC or digital cam unreadable. Drive mapping tools, such as Winhex, can be of a great assistance.

Here’s an example for using Compact Flash card from BASIC. A set of files is written on CF card. This can be checked later by plugging the CF card on a PC or a digital camera. Observe the way the file is being written:

program CompactFlash_File
' for PIC18

dim i1 as word
dim index as byte
dim fname as char[9]
dim ext as char[4]

sub procedure Init
 TRISC = 0                           ' PORTC is output. We'll use it only to signal
                                     '   end of our program.
 CF_Init_Port(PORTB, PORTD)          ' Initialize ports
do
   nop
loop until CF_DETECT(PORTB) = true   ' Wait until CF card is inserted
Delay_ms(50)                         ' Wait for a while until the card is stabilized
end sub                              '   i.e. its power supply is stable and CF card
                                     '   controller is on

main:
  ext = "txt"                        ' File extensions will be "txt"
  index = 0                          ' Index of file to be written
  while index < 5
     PORTC = 0
     Init
     PORTC = index
     CF_File_Write_Init(PORTB, PORTD)            ' Initialization for writing to new file
     i1 = 0
     while i1 < 50000
       CF_File_Write_Byte(PORTB,PORTD,48+index)  ' Writes 50000 bytes to file
       inc(i1)
     wend
     fname = "RILEPROX"                          ' Must be 8 character long in upper case
     fname[8] = 48 + index                       ' Ensure that files have different name
     CF_File_Write_Complete(PORTB,PORTD, fname, ext)  ' Close the file
     Inc(index)
   wend
   PORTC = $FF

end.
      

If you do not wish to use your CF card in PCs and digicams but ruther as a simple storage device for your PIC MCU only, you can then ignore the entire FAT system and store data directly to CF memory sectors:

program cf_test

dim i as word


main:
  TRISC = 0                                  ' PORTC is output
  CF_Init_Port(PORTB,PORTD)                  ' Initialize ports

  do
     nop
  loop until CF_Detect(PORTB) = true         ' Wait until CF card is inserted


  Delay_ms(500)
  CF_Write_Init(PORTB, PORTD, 590, 1)        ' Initialize write at sector address 590
                                             '   of  1 sector (512 bytes)
  for i = 0 to 511                           ' Write 512 bytes to sector (590)
    CF_Write_Byte(PORTB, PORTD, i + 11)
  next i
  PORTC = $FF
  Delay_ms(1000)
  CF_Read_Init(PORTB, PORTD, 590, 1)          ' Initialize write at sector address 590
                                              '   of  1 sector (512 bytes)
  for i = 0 to 511                            ' Read 512 bytes from sector (590)
       PORTC = CF_Read_Byte(PORTB, PORTD)     '    and display it on PORTC
       Delay_ms(1000)
  next i
end.