0
Interfacing with Flash memory
Posted by Chris
on
Wednesday, November 04, 2009
Ok, here it is. Some Oshonsoft PIC Basic code for reading and writing data to a Flash memory device.
This example writes data through the Flash internal buffer(s) and reads/writes data one entire page (256 bytes) at a time. Because of the limitations of the USB implementation, the PIC keeps a mirror copy of the Flash buffer in memory and when called for, sends a group of 8 bytes to the USB host device.
This code example works with a PIC and Flash memory connected according to the schematic from this post, with the Flash memory connected to pins PORTA.0-3
Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x3e
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x05 '0x83
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40
'SPI setup
Define SPI_CS_REG = PORTA
Define SPI_CS_BIT = 1
Define SPI_SCK_REG = PORTA
Define SPI_SCK_BIT = 2
Define SPI_SDI_REG = PORTA
Define SPI_SDI_BIT = 3
Define SPI_SDO_REG = PORTA
Define SPI_SDO_BIT = 0
SPIPrepare
'USB setup
UsbSetVendorId 0x099
UsbSetProductId 0x004
UsbSetVersionNumber 0x1001
UsbSetManufacturerString "ScoreSure"
UsbSetProductString "ScoreSure Template Device"
UsbSetSerialNumberString "0123456789"
UsbOnIoInGosub usbonioin
UsbOnIoOutGosub usbonioout
UsbOnFtInGosub usbonftin
UsbOnFtOutGosub usbonftout
AllDigital
'general declarations
Dim i As Byte
Dim j As Byte
Dim state As Byte
Dim iusb As Byte
'memory access variables
Dim addri As Byte
Dim addrj As Byte
Dim addrk As Byte
Dim ai As Byte
Dim bi As Byte
Dim ci As Byte
Dim useusb As Bit
Dim pageaddr As Word
Dim byteaddr As Byte
Dim bytecount As Word
Dim picbuffer(256) As Byte
Dim picbufferpointer As Byte
Dim flashstatus As Byte
'initialisation
init:
useusb = 1
picbufferpointer = 0
'startup
start:
If useusb = 1 Then UsbStart
'main loop
loop:
If useusb = 1 Then
UsbService
Else
Select Case state
EndSelect
Endif
Goto loop
End
usbonftout:
'feature reports.
Return
usbonftin:
'don't do anything on feature.
Return
usbonioout:
'received data from host into PIC
'Here we put a single command byte into buffer(7) to tell the PIC
'what we want it to do.
Select Case UsbIoBuffer(7)
'---------200+ are memory control commands --------
'-----------------------------------------------------------
Case 200 'set page address 0-2048 (byte 2= MSB, byte 1=LSB)
'-----------------------------------------------------------
iusb = UsbIoBuffer(2)
pageaddr = iusb * 256
iusb = UsbIoBuffer(1)
pageaddr = pageaddr + iusb
'-----------------------------------------------------------
Case 201 'set byte address within a page (1 page=256 bytes)
'-----------------------------------------------------------
byteaddr = UsbIoBuffer(2)
'---------------------------------------------------------
Case 202 'load flash memory page into flash memory buffer
'---------------------------------------------------------
Gosub flashmemorytoflashbuffer
'--------------------------------------------------------------
Case 203 'load flash memory buffer (256 bytes) into PIC buffer
'--------------------------------------------------------------
Gosub flashbuffertopicbuffer
'-------------------------------------------
Case 204 'write PIC buffer to memory buffer
'-------------------------------------------
Gosub picbuffertoflashbuffer
'-------------------------------------------
Case 205 'flash buffer to flash page memory
'-------------------------------------------
Gosub flashbuffertoflashmemory
'---------------------------------------
Case 206 'set picbuffer pointer (0-255)
'---------------------------------------
picbufferpointer = UsbIoBuffer(6)
'------------------------------------------------
Case 207 'load 4 bytes from host into pic buffer
'------------------------------------------------
For iusb = 0 To 3
picbuffer(picbufferpointer) = UsbIoBuffer(iusb)
picbufferpointer = picbufferpointer + 1
Next iusb
'---------------------------
Case 208 'start stream read
'---------------------------
streamread = 1
SPICSOn
SPISend 0xe8 'stream read (legacy mode)
SPISend pageaddr.HB
SPISend pageaddr.LB
SPISend byteaddr
SPISend 0x00 'four don't care bytes
SPISend 0x00
SPISend 0x00
SPISend 0x00
'-------------------------
Case 209 'end stream read
'-------------------------
SPICSOff
'--------------------------
Case 210 'empty PIC buffer
'--------------------------
Gosub emptypicbuffer
picbufferpointer = 0
'-------------------------------------------------
Case 240 'clear entire memory (use with caution!)
'-------------------------------------------------
'-------------------------------------------
Case 253 'flash memory status register read
'-------------------------------------------
Gosub getflashstatus
Gosub emptypicbuffer
picbuffer(0) = flashstatus
'-------------------------------------------------------------------
Case 254 'blow the fuse on the flash memory to set to 256-byte mode
'-------------------------------------------------------------------
SPICSOn
SPISend 0x3d
SPISend 0x2a
SPISend 0x80
SPISend 0xa6
SPICSOff
EndSelect
Return
usbonioin:
'send data from PIC to host
'if you use a loop in here, garbage gets sent back!
If streamread = 1 Then
'load the data from flash memory (direct access, legacy mode)
'straight into the usbiobuffer
SPIReceive UsbIoBuffer(0)
SPIReceive UsbIoBuffer(1)
SPIReceive UsbIoBuffer(2)
SPIReceive UsbIoBuffer(3)
SPIReceive UsbIoBuffer(4)
SPIReceive UsbIoBuffer(5)
SPIReceive UsbIoBuffer(6)
SPIReceive UsbIoBuffer(7)
Else
iusb = picbufferpointer
UsbIoBuffer(0) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(1) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(2) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(3) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(4) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(5) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(6) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(7) = picbuffer(iusb)
Endif
Return
setflashdeepsleep:
'when we're not using the flash memory, set it to deep sleep which causes it to
'use minimum amount of power (it's basically on super-standby) opcode B9H
SPICSOn
SPISend 0xb9
SPICSOff
Return
setflashwakeup:
'when we've put the flash memory into deep sleep, it ignores all commands sent to
'it until it's been woken up by sending opcode ABH
SPICSOn
SPISend 0x0ab
SPICSOff
Return
flashmemorytoflashbuffer:
SPICSOn
SPISend 0x53 'transfer memory page to buffer1 command
SPISend pageaddr.HB 'send the page address to read data
SPISend pageaddr.LB 'from into buffer number one
SPISend 0x00 'don't care 8-bits
SPICSOff
Return
flashbuffertoflashmemory:
SPICSOn
SPISend 0x83 'transfer buffer1 to flash page (with built-in erase) command
SPISend pageaddr.HB 'send the page address to read data
SPISend pageaddr.LB 'from into buffer number one
SPISend 0x00 'don't care 8-bits
SPICSOff
Return
flashbuffertopicbuffer:
SPICSOn
SPISend 0xd4 'buffer1 read command
SPISend 0x00 'start off with 16
SPISend 0x00 'don't care bits
SPISend 0x00 'then buffer byte to start reading from (always zero)
For picbufferpointer = 0 To 255
SPIReceive picbuffer(picbufferpointer)
Next picbufferpointer
picbufferpointer = 0
SPICSOff
Return
picbuffertoflashbuffer:
SPICSOn
SPISend 0x84 'buffer1 write command
SPISend 0x00 'start with 16
SPISend 0x00 'don't care bits
SPISend 0x00 'then buffer byte to start reading to (always zero)
For picbufferpointer = 0 To 255
SPISend picbuffer(picbufferpointer)
Next picbufferpointer
picbufferpointer = 0
SPICSOff
Return
emptypicbuffer:
For i = 0 To 255
picbuffer(i) = 0
Next i
Return
getflashstatus:
SPICSOn
SPISend 0xd7 'status register command
SPIReceive flashstatus
SPICSOff
Return
The first thing to do, once connected and the USB device recognised, is to set the page size to 256 bytes. This is done by sending the byte command (from PC to PIC)
number 254. Before doing this, we checked the Flash memory status register:
Send (from PC to PIC) byte command 253. This is done by clearing the entire USB buffer - setting all values to zero - on the PC then setting USB byte(7) to 253 using the VB command:
(see Oshonsoft PIC18 USB section for details about communicating with your PIC-based USB device from your PC and the HIDTerminal Activex control which encapsulates USB functionality.
After the byte command 253 has been sent to the USB device, the internal PIC memory buffer should be blank (all zeros) except for byte(0) which contains the Flash Status Register value. We read this back to the PC by issuing the VB command:
Displaying the values for data(0-7) on screen (we use a few text boxes, but you could use Debug.Print or any other method to view the values) we see that the Flash Status Register value is 156 or, in binary:
Checking the AT45DB041D datasheet we can see that the last bit of the status register (bit zero) tells us whether each page size is 264 (default) or 256.
In this case, we can see - because it's a brand new device - bit zero is cleared, which signifies that a page size is set to the default of size 264 bytes.
We issued the "change page size" command from PC to PIC (which in turn sent the byte sequence 0x3D, 0x2A, 0x80, 0xA6 to the Flash memory) to blow the fuse holding the page size value - effectively turning it to a 256-byte page size. This is a one-time operation and cannot be undone.
After repeating the "read Flash Status Register" routine as outlined above, we now got a status value of 157. This tells us that bit zero of the Flash Status Register is now set, which - according to the datasheet - signifies that the Flash unit is now working with a 256-byte page size. Just the result we were after!
Using a combination of send and read commands, we are now able to read and write data to and from the Flash memory device.
Result!
This example writes data through the Flash internal buffer(s) and reads/writes data one entire page (256 bytes) at a time. Because of the limitations of the USB implementation, the PIC keeps a mirror copy of the Flash buffer in memory and when called for, sends a group of 8 bytes to the USB host device.
This code example works with a PIC and Flash memory connected according to the schematic from this post, with the Flash memory connected to pins PORTA.0-3
Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x3e
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x05 '0x83
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40
'SPI setup
Define SPI_CS_REG = PORTA
Define SPI_CS_BIT = 1
Define SPI_SCK_REG = PORTA
Define SPI_SCK_BIT = 2
Define SPI_SDI_REG = PORTA
Define SPI_SDI_BIT = 3
Define SPI_SDO_REG = PORTA
Define SPI_SDO_BIT = 0
SPIPrepare
'USB setup
UsbSetVendorId 0x099
UsbSetProductId 0x004
UsbSetVersionNumber 0x1001
UsbSetManufacturerString "ScoreSure"
UsbSetProductString "ScoreSure Template Device"
UsbSetSerialNumberString "0123456789"
UsbOnIoInGosub usbonioin
UsbOnIoOutGosub usbonioout
UsbOnFtInGosub usbonftin
UsbOnFtOutGosub usbonftout
AllDigital
'general declarations
Dim i As Byte
Dim j As Byte
Dim state As Byte
Dim iusb As Byte
'memory access variables
Dim addri As Byte
Dim addrj As Byte
Dim addrk As Byte
Dim ai As Byte
Dim bi As Byte
Dim ci As Byte
Dim useusb As Bit
Dim pageaddr As Word
Dim byteaddr As Byte
Dim bytecount As Word
Dim picbuffer(256) As Byte
Dim picbufferpointer As Byte
Dim flashstatus As Byte
'initialisation
init:
useusb = 1
picbufferpointer = 0
'startup
start:
If useusb = 1 Then UsbStart
'main loop
loop:
If useusb = 1 Then
UsbService
Else
Select Case state
EndSelect
Endif
Goto loop
End
usbonftout:
'feature reports.
Return
usbonftin:
'don't do anything on feature.
Return
usbonioout:
'received data from host into PIC
'Here we put a single command byte into buffer(7) to tell the PIC
'what we want it to do.
Select Case UsbIoBuffer(7)
'---------200+ are memory control commands --------
'-----------------------------------------------------------
Case 200 'set page address 0-2048 (byte 2= MSB, byte 1=LSB)
'-----------------------------------------------------------
iusb = UsbIoBuffer(2)
pageaddr = iusb * 256
iusb = UsbIoBuffer(1)
pageaddr = pageaddr + iusb
'-----------------------------------------------------------
Case 201 'set byte address within a page (1 page=256 bytes)
'-----------------------------------------------------------
byteaddr = UsbIoBuffer(2)
'---------------------------------------------------------
Case 202 'load flash memory page into flash memory buffer
'---------------------------------------------------------
Gosub flashmemorytoflashbuffer
'--------------------------------------------------------------
Case 203 'load flash memory buffer (256 bytes) into PIC buffer
'--------------------------------------------------------------
Gosub flashbuffertopicbuffer
'-------------------------------------------
Case 204 'write PIC buffer to memory buffer
'-------------------------------------------
Gosub picbuffertoflashbuffer
'-------------------------------------------
Case 205 'flash buffer to flash page memory
'-------------------------------------------
Gosub flashbuffertoflashmemory
'---------------------------------------
Case 206 'set picbuffer pointer (0-255)
'---------------------------------------
picbufferpointer = UsbIoBuffer(6)
'------------------------------------------------
Case 207 'load 4 bytes from host into pic buffer
'------------------------------------------------
For iusb = 0 To 3
picbuffer(picbufferpointer) = UsbIoBuffer(iusb)
picbufferpointer = picbufferpointer + 1
Next iusb
'---------------------------
Case 208 'start stream read
'---------------------------
streamread = 1
SPICSOn
SPISend 0xe8 'stream read (legacy mode)
SPISend pageaddr.HB
SPISend pageaddr.LB
SPISend byteaddr
SPISend 0x00 'four don't care bytes
SPISend 0x00
SPISend 0x00
SPISend 0x00
'-------------------------
Case 209 'end stream read
'-------------------------
SPICSOff
'--------------------------
Case 210 'empty PIC buffer
'--------------------------
Gosub emptypicbuffer
picbufferpointer = 0
'-------------------------------------------------
Case 240 'clear entire memory (use with caution!)
'-------------------------------------------------
'-------------------------------------------
Case 253 'flash memory status register read
'-------------------------------------------
Gosub getflashstatus
Gosub emptypicbuffer
picbuffer(0) = flashstatus
'-------------------------------------------------------------------
Case 254 'blow the fuse on the flash memory to set to 256-byte mode
'-------------------------------------------------------------------
SPICSOn
SPISend 0x3d
SPISend 0x2a
SPISend 0x80
SPISend 0xa6
SPICSOff
EndSelect
Return
usbonioin:
'send data from PIC to host
'if you use a loop in here, garbage gets sent back!
If streamread = 1 Then
'load the data from flash memory (direct access, legacy mode)
'straight into the usbiobuffer
SPIReceive UsbIoBuffer(0)
SPIReceive UsbIoBuffer(1)
SPIReceive UsbIoBuffer(2)
SPIReceive UsbIoBuffer(3)
SPIReceive UsbIoBuffer(4)
SPIReceive UsbIoBuffer(5)
SPIReceive UsbIoBuffer(6)
SPIReceive UsbIoBuffer(7)
Else
iusb = picbufferpointer
UsbIoBuffer(0) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(1) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(2) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(3) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(4) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(5) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(6) = picbuffer(iusb)
iusb = iusb + 1
UsbIoBuffer(7) = picbuffer(iusb)
Endif
Return
setflashdeepsleep:
'when we're not using the flash memory, set it to deep sleep which causes it to
'use minimum amount of power (it's basically on super-standby) opcode B9H
SPICSOn
SPISend 0xb9
SPICSOff
Return
setflashwakeup:
'when we've put the flash memory into deep sleep, it ignores all commands sent to
'it until it's been woken up by sending opcode ABH
SPICSOn
SPISend 0x0ab
SPICSOff
Return
flashmemorytoflashbuffer:
SPICSOn
SPISend 0x53 'transfer memory page to buffer1 command
SPISend pageaddr.HB 'send the page address to read data
SPISend pageaddr.LB 'from into buffer number one
SPISend 0x00 'don't care 8-bits
SPICSOff
Return
flashbuffertoflashmemory:
SPICSOn
SPISend 0x83 'transfer buffer1 to flash page (with built-in erase) command
SPISend pageaddr.HB 'send the page address to read data
SPISend pageaddr.LB 'from into buffer number one
SPISend 0x00 'don't care 8-bits
SPICSOff
Return
flashbuffertopicbuffer:
SPICSOn
SPISend 0xd4 'buffer1 read command
SPISend 0x00 'start off with 16
SPISend 0x00 'don't care bits
SPISend 0x00 'then buffer byte to start reading from (always zero)
For picbufferpointer = 0 To 255
SPIReceive picbuffer(picbufferpointer)
Next picbufferpointer
picbufferpointer = 0
SPICSOff
Return
picbuffertoflashbuffer:
SPICSOn
SPISend 0x84 'buffer1 write command
SPISend 0x00 'start with 16
SPISend 0x00 'don't care bits
SPISend 0x00 'then buffer byte to start reading to (always zero)
For picbufferpointer = 0 To 255
SPISend picbuffer(picbufferpointer)
Next picbufferpointer
picbufferpointer = 0
SPICSOff
Return
emptypicbuffer:
For i = 0 To 255
picbuffer(i) = 0
Next i
Return
getflashstatus:
SPICSOn
SPISend 0xd7 'status register command
SPIReceive flashstatus
SPICSOff
Return
The first thing to do, once connected and the USB device recognised, is to set the page size to 256 bytes. This is done by sending the byte command (from PC to PIC)
number 254. Before doing this, we checked the Flash memory status register:
Send (from PC to PIC) byte command 253. This is done by clearing the entire USB buffer - setting all values to zero - on the PC then setting USB byte(7) to 253 using the VB command:
data(0)=0
data(1)=0
data(2)=0
data(3)=0
data(4)=0
data(5)=0
data(6)=0
data(7)=253
HIDTerminal1.HIDSendReport(data(0), data(1), data(2), data(3), data(4), data(5), data(6), data(7))
data(1)=0
data(2)=0
data(3)=0
data(4)=0
data(5)=0
data(6)=0
data(7)=253
HIDTerminal1.HIDSendReport(data(0), data(1), data(2), data(3), data(4), data(5), data(6), data(7))
(see Oshonsoft PIC18 USB section for details about communicating with your PIC-based USB device from your PC and the HIDTerminal Activex control which encapsulates USB functionality.
After the byte command 253 has been sent to the USB device, the internal PIC memory buffer should be blank (all zeros) except for byte(0) which contains the Flash Status Register value. We read this back to the PC by issuing the VB command:
HIDTerminal1.HIDReadReport(data(0), data(1), data(2), data(3), data(4), data(5), data(6), data(7))
Displaying the values for data(0-7) on screen (we use a few text boxes, but you could use Debug.Print or any other method to view the values) we see that the Flash Status Register value is 156 or, in binary:
10011100
Checking the AT45DB041D datasheet we can see that the last bit of the status register (bit zero) tells us whether each page size is 264 (default) or 256.
In this case, we can see - because it's a brand new device - bit zero is cleared, which signifies that a page size is set to the default of size 264 bytes.
We issued the "change page size" command from PC to PIC (which in turn sent the byte sequence 0x3D, 0x2A, 0x80, 0xA6 to the Flash memory) to blow the fuse holding the page size value - effectively turning it to a 256-byte page size. This is a one-time operation and cannot be undone.
After repeating the "read Flash Status Register" routine as outlined above, we now got a status value of 157. This tells us that bit zero of the Flash Status Register is now set, which - according to the datasheet - signifies that the Flash unit is now working with a 256-byte page size. Just the result we were after!
Using a combination of send and read commands, we are now able to read and write data to and from the Flash memory device.
Result!
Post a Comment