C3 Read a decimal value to the FLASH
Hi,
i'm fairly new to this, so can someone explain me how a decimal value (1..20)
is written to the flash memory ? I have studied and tried the c3_flash_demo_010.spin
and i can write values , but the reading doesn't return the values. I'm sure i'm doing
something wrong with the address or the value , or the bytes to read write
thanx for any eplanation
i'm fairly new to this, so can someone explain me how a decimal value (1..20)
is written to the flash memory ? I have studied and tried the c3_flash_demo_010.spin
and i can write values , but the reading doesn't return the values. I'm sure i'm doing
something wrong with the address or the value , or the bytes to read write
thanx for any eplanation

Comments
You're asking how a decimal value is written to flash memory. You probably don't want the literal answer to your question, it's interesting, but won't help you get things to work. Tell us what you're doing and maybe we can help more.
I don't really have written new code to do this in my project, i'm still messing with the demo to try and figure out how to make it easier to implement.
I don't really get the whole setup in the Flash_Write and Flash_Read , i was hoping to just point to an address and write a hex and read it from the
same place. If only life was that simple ? :P
I cant seem to find any demo that writes a decimal value and reads it back as example
g_sbuffer[ 1 ] := 256 ' Fill the Buffer Flash_Write( 0, @g_sbuffer, 512 ) ' write buffer to flash bytefill ( @g_sbuffer, $00, 512) ' clear buffer out Flash_Read( 0, @g_sbuffer, 512 ) ' read flash into bufferand i try to read the second address :
the value of NP remains empty (=00)
not sure where it goes wrong..
Maybe i am going wrong in displaying the returns to the screen ?
PUB hexadecimalToInteger(characters) | sign '' 10 Stack Longs characters := checkSign(ignoreSpace(characters), @sign) repeat (strsize(characters) <# 8) ifnot(checkDigit(characters, "0", "9")) ifnot(checkDigit(characters, "A", "F") or checkDigit(characters, "a", "f")) quit result += $90_00_00_00 result := ((result <- 4) + (byte[characters++] & $F)) result *= signI make sure that the Flash_Erase has been done in the startup,
then i write a value to the buffer[1] , but so far no real readout
and the readout of 1 byte at address 1 gives me 0C
I'm going to write a new small test program, the whole program would take too much.
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 _pinGroup = 2 _startUpWait = 1 CLOCKS_PER_MILLISECOND = 5000*16 CLOCKS_PER_MICROSECOND = 5*16 ' simple xin*pll / 1_000_000 ' SPI pins SPI_SEL_CLK = 8 ' used to clock the SPI counter that feeds 4-bit value to SPI selection decoder, clocks on rising edge SPI_SEL_CLR = 25 ' used to clear the SPI counter back to 0000b (NULL SPI device), active LOW SPI_MOSI = 9 ' SPI master out serial in to slave SPI_MISO = 10 ' SPI master in serial out from slave, 10 for good C3, change to 7 for BROKEN C3 with YELLOW jumper. SPI_SCK = 11 ' SPI clock from master to all slaves ' SPI channels defines SPI_CHAN_NULL = 0 ' NULL channel, disables all on/off board devices. SPI_CHAN_SRAM0 = 1 ' 32K SRAM Bank 0. SPI_CHAN_SRAM1 = 2 ' 32K SRAM Bank 1. SPI_CHAN_FLASH = 3 ' 1MB FLASH Memory. SPI_CHAN_AD = 4 ' MCP3202 2-Channel 12-bit A/D. SPI_CHAN_SD = 5 ' Micro SD Card. ' interface header SPI selects SPI_CHAN_SPI6 = 6 ' Header Interface SPI6. SPI_CHAN_SPI7 = 7 ' Header Interface SPI7. ' Atmel 2F004/8xxx series basic commands WRITE_ENABLE = $06 WRITE_DISABLE = $04 READ_STATUS = $05 WRITE_STATUS = $01 READ_DATA = $03 SEQ_PROGRAM = $AF BYTE_PROGRAM = $02 BLOCK_ERASE4 = $20 BLOCK_ERASE32 = $52 BLOCK_ERASE64 = $D8 SECTOR_ERASE = $20 CHIP_ERASE = $60 JEDEC_ID = $9F PROTECT_SECTOR = $36 UNPROTECT_SECTOR = $39 ' custom commands we make up, not part of chip spec, just used here, start at code 0xF0 UNPROTECT_CHIP = $F0 ' ASCII codes for ease of parser development ASCII_A = 65 ASCII_B = 66 ASCII_C = 67 ASCII_D = 68 ASCII_E = 69 ASCII_F = 70 ASCII_G = 71 ASCII_H = 72 ASCII_O = 79 ASCII_P = 80 ASCII_Z = 90 ASCII_0 = 48 ASCII_9 = 57 ASCII_LEFT = $C0 ASCII_RIGHT = $C1 ASCII_UP = $C2 ASCII_DOWN = $C3 ASCII_BS = $C8 ' backspace ASCII_DEL = $C9 ' delete ASCII_LF = $0A ' line feed ASCII_CR = $0D ' carriage return ASCII_ESC = $CB ' escape ASCII_HEX = $24 ' $ for hex ASCII_BIN = $25 ' % for binary ASCII_LB = $5B ' [ ASCII_SEMI = $3A ' ; ASCII_EQUALS = $3D ' = ASCII_PERIOD = $2E ' . ASCII_COMMA = $2C ' , ASCII_SHARP = $23 ' # ASCII_NULL = $00 ' null character ASCII_SPACE = $20 ' space ASCII_TAB = $09 ' horizontal tab ' null pointer, null character NULL = 0 OBJ Serial : "Parallax Serial Terminal" VAR byte g_sbuffer[512] PUB demo Serial.Start(115_200) Flash_Open Flash_Erase_Chip(1) bytefill ( @g_sbuffer, $00, 512) g_sbuffer[ 1 ] := 99 Flash_Write( 0, @g_sbuffer, 512 ) bytefill ( @g_sbuffer, $00, 512) repeat Flash_Read( 0, @g_sbuffer, 512 ) Serial.Str(String(" ValueDecimal :")) Serial.Dec(g_sbuffer[ 1 ]) delay_ms(1000) PUB Flash_Open | _index, _spi_data, _flash_start_addr ' use global unprotect to unprotect entire chip at once, write "0" to bits 2,3,4,5 SPI_Select_Channel( SPI_CHAN_FLASH ) _spi_data := SPI_Write_Read( 8, WRITE_ENABLE, $FF ) SPI_Select_Channel( 0 ) Flash_Write_Status ( Flash_Read_Status & %11_0000_11 ) Delay_MS( 10 ) ' // return success always return( 1 ) PUB Flash_Read_Status | _spi_data {{ Reads the FLASH memory status register PARMS: None. RETURNS: status register value }} '// set CS to SPI select channel 3 (FLASH) SPI_Select_Channel( SPI_CHAN_FLASH ) _spi_data := SPI_Write_Read( 8, READ_STATUS, $FF ) ' now read data returned _spi_data := SPI_Write_Read( 8, $FF, $FF ) '// disable SPI interface '// set CS to SPI select channel 0 (null) SPI_Select_Channel( 0 ) ' return results return ( _spi_data ) ' // end Flash_Read_Status ' ///////////////////////////////////////////////////////////////////////////// PUB Flash_Write_Status ( status ) | _spi_data {{ Writes the FLASH memory status register PARMS: status - value to update the status register with RETURNS: None. }} '// set CS to SPI select channel 3 (FLASH) SPI_Select_Channel( SPI_CHAN_FLASH ) _spi_data := SPI_Write_Read( 8, WRITE_STATUS, $FF ) _spi_data := SPI_Write_Read( 8, status, $FF ) '// disable SPI interface '// set CS to SPI select channel 0 (null) SPI_Select_Channel( 0 ) ' return success for now return ( 1 ) ' // end Flash_Write_Status ' ///////////////////////////////////////////////////////////////////////////// PUB Flash_Erase_Chip( wait ) | _spi_data '// set CS to SPI select channel 3 (FLASH) SPI_Select_Channel( SPI_CHAN_FLASH ) _spi_data := SPI_Write_Read( 8, WRITE_ENABLE, $FF ) '// disable SPI interface '// set CS to SPI select channel 0 (null) SPI_Select_Channel( 0 ) '// set CS to SPI select channel 3 (FLASH) SPI_Select_Channel( SPI_CHAN_FLASH ) _spi_data := SPI_Write_Read( 8, CHIP_ERASE, $FF ) '// disable SPI interface '// set CS to SPI select channel 0 (null) SPI_Select_Channel( 0 ) ' test if user requested wait for completion? if (wait) repeat ' erase operation still ongoing? if ((Flash_Read_Status & $01)==0) return (1) '// return success for now return(1) ' ///////////////////////////////////////////////////////////////////////////// PUB Flash_Write( flash_start_addr, buffer_ptr, num_bytes) | _index, _spi_data {{ This function writes num_bytes bytes to the FLASH memory at flash_start_addr from the sent buffer_ptr. It uses the byte/page write function. Although, there is a "sequential" byte write function available on the AT26DF081A the new rev of the Atel chips (AT25xxxx series) coming out in 2011 have replaced the sequential write with another function :(, thus we simply have to check for page boundaries and re-issue another page write every 256 bytes. PARMS: flash_start_addr - starting address in the FLASH memory to write the data at. buffer_ptr - local byte pointer to Prop memory holding data to write to FLASH memory. num_bytes - number of bytes to write to FLASH from source buffer_ptr. }} '// STEP 1: enable writing to the chip again '// enable SPI interface '// set CS to SPI select channel 3 (FLASH) SPI_Select_Channel( SPI_CHAN_FLASH ) _spi_data := SPI_Write_Read( 8, WRITE_ENABLE, $FF ) '// disable SPI interface '// set CS to SPI select channel 0 (null) SPI_Select_Channel( 0 ) '// STEP 2: write buffer to address in flash ////////////////// '// set CS to SPI select channel 3 (FLASH) SPI_Select_Channel( SPI_CHAN_FLASH ) '// initiate byte/page program command _spi_data := SPI_Write_Read( 8, BYTE_PROGRAM, $FF ) '// write starting address byte by byte, MSB first... _spi_data := SPI_Write_Read( 8, flash_start_addr >> 16, $FF ) _spi_data := SPI_Write_Read( 8, flash_start_addr >> 8, $FF ) _spi_data := SPI_Write_Read( 8, flash_start_addr >> 0, $FF ) '// write first byte of _spi_data := SPI_Write_Read( 8, byte [buffer_ptr][ 0 ], $FF ) _index := 1 '// write remaining bytes repeat until (_index => num_bytes ) ' now test if we are on a page boundary, if so finish page, and start new page if ( ((flash_start_addr + _index) // 256) == 0) '// set CS to SPI select channel 0 (null) SPI_Select_Channel( 0 ) ' replace this with polling... Delay_MS(10) '// set CS to SPI select channel 3 (FLASH) SPI_Select_Channel( SPI_CHAN_FLASH ) _spi_data := SPI_Write_Read( 8, WRITE_ENABLE, $FF ) SPI_Select_Channel( 0 ) SPI_Select_Channel( SPI_CHAN_FLASH ) '// re-issue byte/page program command _spi_data := SPI_Write_Read( 8,BYTE_PROGRAM,$FF ) '// write starting address byte by byte, MSB first... _spi_data := SPI_Write_Read( 8, (flash_start_addr + _index) >> 16, $FF ) _spi_data := SPI_Write_Read( 8, (flash_start_addr + _index) >> 8, $FF ) _spi_data := SPI_Write_Read( 8, (flash_start_addr + _index) >> 0, $FF ) ' end if '// write the next byte of data _spi_data := SPI_Write_Read( 8, byte [buffer_ptr][ _index ], $FF ) _index++ ' // end for index '// set CS to SPI select channel 0 (null) SPI_Select_Channel( 0 ) ' write final bytes left in internal buffer, replace this with polling... Delay_MS(10) '// disable SPI interface '// return success return(1) ' // end Flash_Write ' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// PUB Flash_Read( flash_start_addr, buffer_ptr, num_bytes) | _index, _spi_data {{ This function reads num_bytes from the FLASH memory starting at memory location flash_start_addr and stores the data in buffer_ptr. PARMS: flash_start_addr - starting address in FLASH memory to read data from. buffer_ptr - local Prop byte pointer to store the read data at. num_bytes - the number of bytes to read from the FLASH memory. RETURNS: None. }} '// set CS to SPI select channel 3 (FLASH) SPI_Select_Channel( SPI_CHAN_FLASH ) '// initiate read command _spi_data := SPI_Write_Read( 8, READ_DATA, $FF ) '// write starting address byte by byte, MSB first... _spi_data := SPI_Write_Read( 8, flash_start_addr >> 16, $FF ) _spi_data := SPI_Write_Read( 8, flash_start_addr >> 8, $FF ) _spi_data := SPI_Write_Read( 8, flash_start_addr >> 0, $FF ) '// now read data repeat _index from 0 to num_bytes-1 byte [buffer_ptr][_index] := SPI_Write_Read( 8, $00, $FF ) ' // end for index '// disable SPI interface '// set CS to SPI select channel 0 (null) SPI_Select_Channel( 0 ) '// return success return(1) ' // end Flash_Read ' ///////////////////////////////////////////////////////////////////////////// '////////////////////////////////////////////////////////////////////////////// ' FLASH MACROS //////////////////////////////////////////////////////////////// '////////////////////////////////////////////////////////////////////////////// PUB FLASH_ADDR_TO_BLOCK( n ) n := (n >> 12) ' ///////////////////////////////////////////////////////////////////////////// PUB FLASH_BLOCK_TO_ADDR( n ) n := (n << 12) ' ///////////////////////////////////////////////////////////////////////////// ' ///////////////////////////////////////////////////////////////////////////// ' SPI ROUTINES///////// /////////////////////////////////////////////////////// ' ///////////////////////////////////////////////////////////////////////////// PUB SPI_Init {{ This function initializes the SPI IO's, counter, and mux, selects channel 0 and returns. PARMS: none. RETURNS: nothing. }} ' set SPI mux counter IOs up DIRA[SPI_SEL_CLK] := 1 ' set to output OUTA[SPI_SEL_CLK] := 0 DIRA[SPI_SEL_CLR] := 1 ' set to output OUTA[SPI_SEL_CLR] := 0 ' CLR counter OUTA[SPI_SEL_CLR] := 1 ' allow counting ' set up SPI lines OUTA[SPI_MOSI] := 0 ' set to LOW OUTA[SPI_SCK] := 0 ' set to LOW DIRA[SPI_MOSI] := 1 ' set to output DIRA[SPI_MISO] := 0 ' set to input DIRA[SPI_SCK] := 1 ' set to output ' set SPI to NULL channel 0 SPI_Select_Channel( 0 ) ' end SPI_Init ' ///////////////////////////////////////////////////////////////////////////// PUB SPI_Write_Read( num_bits, data_out, bit_mask) | data_in {{ This function writes and reads SPI data a bit at a time (SPI is a circular buffer protocal), the data is in MSB to LSB format and up to 32-bits can be transmitted and received, the final result is bit masked by bit_mask PARMS: num_bits : number of bits to transmit from data_out data_out : source of data to transmit bit_mask : final result of SPI transmission is masked with this to grab the relevant least significant bits RETURNS: data retrieved from SPI transmission }} ' clear result data_in := 0 ' now read the bits in/out repeat num_bits ' drop clock OUTA[SPI_SCK] := 0 ' place next bit on MOSI OUTA[SPI_MOSI] := ((data_out >> (num_bits-- - 1)) & $01) ' now read next bit from MISO data_in := (data_in << 1) + INA[SPI_MISO] ' raise clock OUTA[SPI_SCK] := 1 ' set clock and MOSI to LOW on exit OUTA[SPI_MOSI] := 0 OUTA[SPI_SCK] := 0 ' at this point, the data has been written and read, return result return ( data_in & bit_mask ) ' end SPI_Write_Read ' ///////////////////////////////////////////////////////////////////////////// PUB SPI_Select_Channel( channel ) {{ This function sets the active SPI channel chip select on the SPI mux, this is accomplished by first resetting the SPI counter that feeds the SPI select decoder, then upcounting the requested number of channels. PARMS: channel : channel 0 to 7 to enable where the channels are defined as follows 0 - NULL channel, disables all on/off board devices. 1 - 32K SRAM Bank 0. 2 - 32K SRAM Bank 1. 3 - 1MB FLASH Memory. 4 - MCP3202 2-Channel 12-bit A/D. 5 - Micro SD Card. 6 - Header Interface SPI6. 7 - Header Interface SPI7. RETURNS: nothing. }} ' requesting channel 0? If so, easy reset if (channel == 0) ' clear the 161 OUTA[SPI_SEL_CLR] := 0 ' CLR counter OUTA[SPI_SEL_CLR] := 1 ' allow counting return ' else non-null channel, count up to channel... ' first reset the SPI channel counter ' clear the 161 OUTA[SPI_SEL_CLR] := 0 ' CLR counter OUTA[SPI_SEL_CLR] := 1 ' allow counting ' now increment to requested channel ' clock the 161 OUTA[SPI_SEL_CLK] := 0 repeat channel OUTA[SPI_SEL_CLK] := 1 OUTA[SPI_SEL_CLK] := 0 ' end SPI_Select_Channel ' ///////////////////////////////////////////////////////////////////////////// ' UTILITY ROUTINES //////////////////////////////////////////////////////////// ' ///////////////////////////////////////////////////////////////////////////// PUB StrCpy( dest_str_ptr, source_str_ptr ) | _index {{ mimics C strcpy, copies source -> destination including NULL }} ' test if there is storage if (dest_str_ptr == NULL) return (NULL) _index := 0 repeat while (byte [ source_str_ptr ][_index] <> NULL) ' copy next byte byte [ dest_str_ptr ][_index] := byte [ source_str_ptr ][_index] _index++ ' null terminate byte [ dest_str_ptr ][_index] := NULL ' return number of bytes copied return ( _index ) ' // end StrCpy ' ///////////////////////////////////////////////////////////////////////////// PUB StrUpper(string_ptr) {{ upcases a string }} if (string_ptr <> NULL) repeat while (byte[ string_ptr ] <> NULL) byte[ string_ptr ] := ToUpper( byte[ string_ptr ] ) string_ptr++ ' return string return (string_ptr) ' // end StrUpper ' ///////////////////////////////////////////////////////////////////////////// PUB ToUpper(ch) {{ returns the uppercase of the sent character }} if (ch => $61 and ch =< $7A) return(ch-32) else return(ch) ' // end ToUpper ' ///////////////////////////////////////////////////////////////////////////// PUB IsInSet(ch, set_string) {{ tests if sent character is in string }} repeat while (byte[set_string][0] <> NULL) if (ch == byte[set_string++][0]) 'serial.txstring(string ("found!") ) return( ch ) 'serial.txstring(string ("not found!") ) ' not found return( -1 ) ' // end IsInSet ' ///////////////////////////////////////////////////////////////////////////// PUB IsSpace(ch) {{ tests if sent character is white space, cr, lf, space, tab }} if ( (ch == ASCII_SPACE) or (ch == ASCII_LF) or (ch == ASCII_CR) or (ch == ASCII_TAB)) return (ch) else return(-1) ' // end IsSpace ' ///////////////////////////////////////////////////////////////////////////// PUB IsNull(ch) {{ tests if sent character is null }} if ( (ch == NULL)) return (1) else return(-1) ' // end IsNull ' ///////////////////////////////////////////////////////////////////////////// PUB IsDigit(ch) {{ tests if sent character is a number 0..9, returns integer 0..9 }} if (ch => ASCII_0 and ch =< ASCII_9) return (ch-ASCII_0) else return(-1) ' // end IsDigit ' ///////////////////////////////////////////////////////////////////////////// PUB IsAlpha(ch) {{ tests if sent character is a number a...zA...Z }} ch := ToUpper(ch) if ( (ch => ASCII_A) and (ch =< ASCII_Z)) return (ch) else return(-1) ' end IsAlpha ' ///////////////////////////////////////////////////////////////////////////// PUB IsPunc(ch) {{ tests if sent character is a punctuation symbol [EMAIL="!@#$%^&*()--+={}[]|\;:'",<.>/"]!@#$%^&*()--+={}[]|\;:'",<.>/[/EMAIL]? }} ch := ToUpper(ch) if ( ((ch => 33) and (ch =< 47)) or ((ch => 58) and (ch =< 64)) or ((ch => 91) and (ch =< 96)) or ((ch =>123) and (ch =< 126)) ) return (ch) else return(-1) ' // end IsPunc ' ///////////////////////////////////////////////////////////////////////////// PUB HexToDec(n) {{ converts hex digit to decimal }} if ( (n => "0") and (n =< "9") ) return (n - ASCII_0) elseif ( (n => "A") and (n =< "F") ) return (n - "A" + 10) else return ( 0 ) ' // end HexToDec ' ///////////////////////////////////////////////////////////////////////////// PUB itoa(value, string_ptr) | i {{ converts value into a string and stores in string_ptr with NULL termination }} if value < 0 -value byte [ string_ptr++ ] := "-" i := 1_000_000_000 repeat 10 if value => i byte [ string_ptr++ ] := value / i + "0" value //= i result~~ elseif result or i == 1 byte [ string_ptr++ ] := "0" i /= 10 ' null terminate byte [ string_ptr ] := NULL ' // end itoa ' ///////////////////////////////////////////////////////////////////////////// PUB atoi2(string_ptr, length) | _index, _sum, _ch, _sign {{ this function tries to convert the string to a number, supports binary %, hex $, decimal (default) string_ptr can be to a null terminated string, or the length can be overridden by sending the length shorter than the null terminator, ignores ws Eg. %001010110, $EF, 25 }} ' initialize vars _index := 0 _sum := 0 _sign := 1 ' consume white space repeat while (IsSpace( byte[ string_ptr ][_index] ) <> -1) _index++ ' is there a +/- sign? if (byte [string_ptr][_index] == "+") ' consume it _index++ elseif (byte [string_ptr][_index] == "-") ' consume it _index++ _sign := -1 ' try to determine number base if (byte [string_ptr][_index] == ASCII_HEX) _index++ repeat while ( ( IsDigit(_ch := byte [string_ptr][_index]) <> -1) or ( IsAlpha(_ch := byte [string_ptr][_index]) <> -1) ) _index++ _sum := (_sum << 4) + HexToDec( ToUpper(_ch) ) if (_index => length) return (_sum*_sign) return(_sum*_sign) ' // end if hex number elseif (byte [string_ptr][_index] == ASCII_BIN) repeat while ( IsDigit(_ch := byte [string_ptr][++_index]) <> -1) _sum := (_sum << 1) + (_ch - ASCII_0) if (_index => length) return (_sum*_sign) return(_sum*_sign) ' // end if binary number else ' must be in default base 10, assume that repeat while ( IsDigit(_ch := byte [string_ptr][_index++]) <> -1) _sum := (_sum * 10) + (_ch - ASCII_0) if (_index => length) return (_sum*_sign) return(_sum*_sign) ' else, have no idea of number format! return(0) ' // end atoi2 ' ///////////////////////////////////////////////////////////////////////////// PUB Delay_MS( time ) {{ Delays "time" milliseconds and returns. }} waitcnt (cnt + time*CLOCKS_PER_MILLISECOND) ' // end Delay_MS ' ///////////////////////////////////////////////////////////////////////////// PUB Delay_US( time ) {{ Delays "time" microseconds and returns. }} waitcnt (cnt + time*CLOCKS_PER_MICROSECOND) ' // end Delay_US '//////////////////////////////////////////////////////////////////////////////PUB demo Serial.Start(115_200) Flash_Erase_Chip(1) ' bytefill ( @g_sbuffer, $00, 512) repeat g_index from 0 to 511 g_sbuffer[ g_index ] := (g_index*g_index) // 256 ' g_sbuffer[ 1 ] := 99 Flash_Write( 0, @g_sbuffer, 512 ) bytefill ( @g_sbuffer, $00, 512) Flash_Read( 0, @g_sbuffer, 512 ) repeat g_index from 0 to 511 Serial.Str(String(" Value :")) Serial.Hex(g_sbuffer[ g_index ],8) delay_ms(100)I forgot to add :
As Homer Simpson would say..... DOH !
Ok, so i tried the following :
1) When i delete the entire chip at startup, then add a new value to the address, things work fine
2) When i change that value and want to update it to the flash, i therefor read the 0..512 bytes into
a buffer, i change buffer of the value ( buffer [address]= newvalue) , i erase the whole chip and
i write the whole buffer again. Doesn't work . For some reason i have the feeling that the erasing
doesn't happen, i don't know why since i call the same function (Flash_Erase_Chip(1))
3) i tried to write a $FF to the address , doesn't work either.
Someone knows a method to erase only the address i want to update ?
Are you just looking to learn how to use the flash memory or do you have a specific application in mind?
Attached is an object for reading and writing to either flash or SRAM on the C3. It's functionally the same as the Winbond flash driver already in the Object Exchange. It lets you set up named files that you can read or create (no updating in place). It also lets you read and write individual blocks and erase 4K sectors.