'----------------------------------------------------------------------------- ' ' SPIN_FLASH v1.0 ' ' Copyright 2008 by William Henning ' published under the MIT license - basically as long as you give proper ' credit to me (ie "Copyright 2008 William Henning, http://mikronauts.com") ' in the code and documentation for your use of it, and do not remove this notice ' you may use the code at no cost. If you don't want to give credit, you will ' have to pay - contact me at mikronauts.com. ' ' Object for communicating with Winbond W25X80 and compatible SPI flash devices ' ' I wrote this object as I needed more data storage than the on-board eeprom ' offered, and the W25X80 is readily available as an 8 pin DIP device that ' runs at 3.3V and it is trivial to interface with the Propeller. ' ' This code could be a lot more efficient, but it was written with the ' clarity of the implementation and small code size being paramount. ' ' FLASH COMMANDS ' ' I implemented the flash commands that I thought would be useful at this time ' feel free to implement the other commands if you need them. ' '----------------------------------------------------------------------------- CON version = 1_00 ' first release version VAR ' base pin+3 pin+2 pin+1 pin byte SPI_CS, SPI_CLK, SPI_DO, SPI_DI '----------------------------------------------------------------------------- ' ' init(pin) ' ' Sets up the object to communicate with the flash chip through the specified ' four pin group. ' '----------------------------------------------------------------------------- pub init(pin) ' set up the pin indexes SPI_CS := pin+3 SPI_CLK := pin+2 SPI_DO := pin+1 SPI_DI := pin+0 ' set initial state of the output pins outa[SPI_CS]~~ outa[SPI_CLK]~ outa[SPI_DO]~ ' configure the I/O direction register dira[SPI_CS]~~ dira[SPI_CLK]~~ dira[SPI_DO]~~ dira[SPI_DI]~ '----------------------------------------------------------------------------- ' ' CS_LOW ' ' Pull the chip select for the flash low to initiate a command ' '----------------------------------------------------------------------------- pub CS_LOW outa[SPI_CS]:=0 '----------------------------------------------------------------------------- ' ' CS_HI ' ' Pull the chip select for the flash high to end current command ' '----------------------------------------------------------------------------- pub CS_HI outa[SPI_CS] := 1 '----------------------------------------------------------------------------- ' ' CLK ' ' Output a clock cycle by raising clock then dropping it ' '----------------------------------------------------------------------------- pub CLK outa[SPI_CLK] := 1 outa[SPI_CLK] := 0 '----------------------------------------------------------------------------- ' ' putb(val) ' ' write a byte to the SPI port ' '----------------------------------------------------------------------------- pub putb(val)|p repeat p from 7 to 0 outa[SPI_DO] := (val >> p) & 1 CLK '----------------------------------------------------------------------------- ' ' getb ' ' read a byte from the SPI port ' '----------------------------------------------------------------------------- pub getb:a|k a := 0 repeat k from 0 to 7 a := (a << 1) + ina[SPI_DI] CLK '----------------------------------------------------------------------------- ' ' jdec_id ' ' return the JDEC standard information describing flash device ' ' Format of returned 32 bit integer: $00MMTTCC ' ' where highest byte is 00 ' ' MM = manufacturer ID ' ' TT = flash device type ' ' CC = flash capacity, ie $14 = 20 = 20 bits of addresses = 1MB capacity ' '----------------------------------------------------------------------------- pub jdec_id:info|manuf,type,cap CS_LOW putb($9f) manuf := getb type := getb cap := getb CS_HI info := (manuf << 16) | (type << 8) | cap '----------------------------------------------------------------------------- ' ' get_size ' ' returns capacity of flash counted in bytes as a long ' '----------------------------------------------------------------------------- pub get_size:tot tot := 1 << (jdec_id & 255) '----------------------------------------------------------------------------- ' ' get_status ' ' returns the status register of the flash device ' '----------------------------------------------------------------------------- pub get_status:st|v CS_LOW putb(5) v := getb CS_HI st := v '----------------------------------------------------------------------------- ' ' put_status(st) ' ' writes the supplied byte to the status register of the flash chip ' '----------------------------------------------------------------------------- pub put_status(st) CS_LOW putb(1) putb(st) CS_HI '----------------------------------------------------------------------------- ' ' wait ' ' wait until flash is not busy - that is, program or erase operation in progress completes ' '----------------------------------------------------------------------------- pub wait repeat while (get_status & 1) '----------------------------------------------------------------------------- ' ' write_en(en) ' ' enable/disable writes to the flash ' - if the argument is zero, disable writes ' - if the argument is non-zero, enable writes ' '----------------------------------------------------------------------------- pub write_en(en) CS_LOW if en putb(6) else putb(4) CS_HI '----------------------------------------------------------------------------- ' ' put_addr(addr) ' ' private support routine for the commands that follow ' ' outputs a 24 bit address to the SPI flash device ' '----------------------------------------------------------------------------- pri put_addr(addr) putb(addr >> 16) putb(addr >> 8) putb(addr) '----------------------------------------------------------------------------- ' ' read(ptr, addr, len) ' ' read data from the flash device ' ' ptr = pointer to hub memory ' addr = address of data in flash device ' len = number of bytes to read (limited by the buffer size on the propeller) ' '----------------------------------------------------------------------------- pub read(ptr, addr, len)|i CS_LOW putb(3) put_addr(addr) len-- repeat i from 0 to len byte[ptr][i] := getb CS_HI '----------------------------------------------------------------------------- ' ' write(ptr,addr,len) ' ' write data to the flash device ' ' ptr = pointer to hub memory ' addr = address of data in flash device ' len = number of bytes to wrte ** ' ' Writes are very different from reads. You can write to any unprogrammed ' location (contains FF) however you can only write a maximum of 256 bytes ' per call - and that is only if you start at a 256 byte boundary with the ' lowest eight bits of the address being 0; otherwise it will wrap to the ' start of the same page. ' ' after doing a write you should read status before doing another write to ' make sure that the write has completed ' ' There is probably a more elegant, nicer way of breaking up an arbitrary ' write into page writes, I wrote this code around 1am. ' '----------------------------------------------------------------------------- pub write(pt, ad, ln)|prt,lft prt := ad & $ff lft := 256-prt if (prt > 0) if (prt + ln) > 255 write_part(pt, ad, lft) ' large fragment ln -= lft pt += lft ad += lft else write_part(pt, ad, ln) ' small fragment return repeat while ln>256 write_part(pt, ad, 256) pt += 256 ad += 256 ln -= 256 if ln > 0 write_part(pt, ad, ln) pub write_part(ptr, addr, len)|wi write_en(1) CS_LOW putb(2) put_addr(addr) len-- repeat wi from 0 to len putb(byte[ptr][wi]) CS_HI wait '----------------------------------------------------------------------------- ' ' erase(addr,typ) ' ' erase part or all of the flash device ' ' addr = address of the sector (4KB) or block (64KB) in the flash ' ' typ = type of erase to perform ' ' 0 = erase a sector (4KB), lowest 12 bits of addr should be zero ' 1 = erase a block (64KB), lowest 16 bits of addr should be zero ' 99 = erase the whole chip, 'addr' is ignored ' ' after doing an erase you should read status before doing anything else to ' make sure that the erase has completed ' '----------------------------------------------------------------------------- pub erase(addr,typ) wait CS_LOW case typ 0: ' sector erase (4k) putb($20) put_addr(addr&$FFFFFC00) 1: ' block erase - 64k putb($d8) put_addr(addr&$FFFF0000) 99: ' chip erase putb($C7) put_addr(addr) CS_HI