--- 模块功能:W25Q32/BH25Q32驱动代码 -- @module SPIFlash -- @author openLuat -- @license MIT -- @copyright openLuat -- @release 2018.8.26 module(..., package.seeall) local READ_ID = 0x90 local WRITE_ENABLE = 0x06 local READ_STATUS = 0x05 local FLASH_READ = 0x03 local ERASE_SECTOR_4K = 0x20 local BLOCK_ERASE_64K = 0xD8 local ERASE_CHIP = 0x60 local PAGE_PROGRAM = 0x02 local gpio_set = pio.pin.setval local mt = { __index = {} } local function address24bit(addr) return pack.pack('>I', addr):sub(2, 4) end --- 创建spi flash驱动实例 -- @param id spi id -- @param timeout 等待flash busy状态解除的最长超时时间,默认60000ms -- @return 返回spi flash驱动实例 -- @usage w25q32 = spiFlash.setup(spi.SPI_1, pio.P0_10) function setup(id, timeout) if not spi.setup(id, 0, 0, 8, 13000000, 1) then log.error('BH25Q32.setup', 'spi setup failed') end local o = { id = id, timeout = timeout or 60000, -- ms pagesize = 256, } setmetatable(o, mt) return o end --- 释放spi flash驱动实例 -- @param -- @return -- @usage w25q32:close() function mt.__index:close() spi.close(self.id) end --- 读取flash id -- @return manufactureID, deviceID 返回两个参数 厂商id,设备id function mt.__index:readFlashID() log.info("send",pack.pack('bAA', READ_ID, address24bit(0), '\255\255'):toHex()) local r = spi.send_recv(self.id, pack.pack('bAA', READ_ID, address24bit(0), '\255\255')) if r then return string.byte(r, 5, 6) else return false end end --- 读取flash状态寄存器 STATUS S0-S7 -- @return 返回flash状态寄存器bit0-bit7的值 function mt.__index:readStatus() local r = spi.send_recv(self.id, string.char(READ_STATUS, 0xff)) return r and string.byte(r, 2) end --- 查询flash是否处于busy状态 -- @return true - 繁忙 false - 空闲 function mt.__index:busy() return self:readStatus() % 2 == 1 end --- 等待flash空闲 -- @param timeout 超时时间 单位ms -- function mt.__index:waitNotBusy(timeout) local status local step = 50 local count = 0 timeout = timeout or self.timeout while true do if not self:busy() then break end sys.wait(step) count = count + 1 if count > timeout / step then log.error('BH25Q32.waitNotBusy', 'timeout') return false end end return true end --- 向spi flash 传输数据 -- @param send_data 要发送的数据 -- @param read_size 可选参数,需要读取的数据长度,如果是写指令,不填参数或者填0 -- @return nil - 失败 true - 写指令发送成功 string - 返回读取到的数据 function mt.__index:transfer(send_data, read_size) if not self:waitNotBusy() then return end if type(send_data) == 'number' then send_data = string.char(send_data) end if not read_size then read_size = 0 end if read_size == 0 then spi.send_recv(self.id, string.char(WRITE_ENABLE)) end local r = spi.send_recv(self.id, send_data .. string.rep('\255', read_size)) if read_size > 0 then return string.sub(r, send_data:len() + 1, -1) end return true end --- 读取spi flash 指定地址的数据 -- @param addr flash地址 -- @param len 长度 -- @return nil - 读取失败 string - 读取到的数据 -- @usage w25q32 = spiFlash.setup(spi.SPI_1, pio.P0_10); w25q32:read(0x1000, 6); function mt.__index:read(addr, len) return self:transfer(string.char(FLASH_READ) .. address24bit(addr), len) end --- 按擦除sector(4K)的方式擦除指定地址的数据 -- @param addr 擦除的起始地址,会自动做4K对齐 -- @return true - 成功 false - 失败 function mt.__index:erase4K(addr) return self:transfer(string.char(ERASE_SECTOR_4K) .. address24bit(addr - addr % 0x1000)) and self:waitNotBusy() end --- 按擦除block 64K的方式擦除指定地址的数据 -- @param addr 擦除的起始地址,会自动做64K对齐 -- @return true - 成功 false - 失败 function mt.__index:erase64K(addr) return self:transfer(string.char(BLOCK_ERASE_64K) .. address24bit(addr - addr % 0x10000)) and self:waitNotBusy() end --- 擦除整个flash芯片的数据 -- @param 无 -- @return true - 成功 false - 失败 function mt.__index:eraseChip() return self:transfer(ERASE_CHIP) and self:waitNotBusy() end --- 向spi flash指定地址写入数据 -- @param address 写入的地址 -- @param data 数据 -- @return number - 成功写入的数据长度 function mt.__index:write(address, data) local ending_address = address + data:len() local wrote_len = 0 local bytes_to_write = 0 while address < ending_address do bytes_to_write = self.pagesize - (address % self.pagesize) if wrote_len + bytes_to_write > data:len() then bytes_to_write = data:len() - wrote_len end if not self:transfer(pack.pack('bAA', PAGE_PROGRAM, address24bit(address), data:sub(wrote_len + 1, wrote_len + bytes_to_write))) then break end address = address + bytes_to_write wrote_len = wrote_len + bytes_to_write end return wrote_len end