forked from btsimonh/hid_download_py
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First (untested) implementaiton of direct SPI flash read & write - e.…
…g. for RPI
- Loading branch information
Showing
6 changed files
with
881 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# BK7231S Flash via SPI | ||
|
||
This is useful if you accidentally overwrite the bootloader. | ||
|
||
This procedure was tested on an RPI3b having enabled SPI in raspi-config. | ||
|
||
Connections: | ||
|
||
``` | ||
CEN - Chip enable (reset) -> GPIO22 | ||
P20 - SCLK -> SCLK | ||
P21 - FLASH CSN -> SPI CE0 | ||
P22 - FLASH SI -> SPI MOSI | ||
P23 - FLASH SO -> SPI MISO | ||
SPI Mode 3 | ||
SPI Rate 50000 | ||
``` | ||
|
||
## Enabling SPI Flash | ||
|
||
set GPIO22 (CEN) to low. | ||
|
||
Wait 1s. | ||
|
||
set GPIO22 (CEN) to high. | ||
|
||
Send 250 'D2' | ||
|
||
Expected response: | ||
|
||
First byte D2, 249 x 00 | ||
|
||
|
||
The MCU is now in SPI flashing mode, and will remain in this state (until power off, or maybe toggle of CEN?). | ||
|
||
Test by sending | ||
|
||
`9F 00 00 00` | ||
-> | ||
`00 15 70 1C` | ||
|
||
This is the ID of an EN25QH16B - data sheet here: https://datasheetspdf.com/datasheet/EN25QH16B.html | ||
|
||
|
||
## Reading the flash | ||
|
||
data may be read in chunks of 256 bytes using cmd 03: | ||
|
||
`03 (addr>>16 & 0xFF) (addr>>8 & 0xFF) (addr & 0xFF) <256 x 00>` | ||
|
||
The data read in this way is from the raw flash. Executable partitions on the BK7231 may/will be both packaged and encrypted. | ||
|
||
|
||
## writing the flash | ||
|
||
Writing consists of erasing sectors and then writing data. | ||
|
||
As it is writing the faw flash, executable partitions must be encrypted and packaged (32->34 bytes with CRC). | ||
|
||
From the Tuya SDK, the firmware file tagged '_QIO_' is suitable. e.g. to replace the bootloader, flash the first 0xf000 bytes from that firmware file. | ||
|
||
Each command must raise CSN at the end to take effect, so all commands must be sent separately over SPI. | ||
|
||
A sector is 0x1000 bytes, writing is max 256 bytes per page. | ||
|
||
Procedure: | ||
|
||
Enable write (must be done before each erase or write): | ||
|
||
`06` | ||
|
||
Erase sector: | ||
|
||
`20 (addr>>16 & 0xFF) (addr>>8 & 0xFF) (addr & 0xFF)` | ||
|
||
Wait for Erase to complete: | ||
|
||
`05 00` | ||
|
||
Repeat until bit 0 of the second byte returns clear. | ||
|
||
Enable write (must be done before each erase or write): | ||
|
||
`06` | ||
|
||
Write data: | ||
|
||
`03 (addr>>16 & 0xFF) (addr>>8 & 0xFF) (addr & 0xFF) <up to 256 bytes of data>` | ||
|
||
Note that there are 4 writes to each erase. If you call erase with a write addr not on a 0x1000 boundary, it will erase the sector starting on the relevant 0x1000 boundary. | ||
|
||
|
||
|
||
## Additional notes: | ||
|
||
At first, I attempted this from Espruino on ESP32. That was not successful, and I'm still not sure why. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import time | ||
from .ExternDownloadFormatSPI import CHIP_EXTERN_Reset, CHIP_EXTERN_End | ||
from ..hid_commands import RESET_ENABLE_PIN, WRITE_COMMAND_CMD, SOFT_SPI, HARD_SPI, DOWNLOAD_STATE, VPP_VCC_POWER_UPDATE | ||
|
||
FT_MSG_SIZE_FLASH = 0x40 | ||
|
||
def CHIP_BK3435_Reset(spi_downloader, DownFormat): | ||
spi_downloader.ChipReset() | ||
for _ in range(150): | ||
send_buf[0] = 0xD2 | ||
reply = spi_downloader.spi.xfer2(send_buf) | ||
|
||
time.sleep(0.1) | ||
|
||
return CHIP_EXTERN_Reset(hid_downloader) | ||
|
||
|
||
def CHIP_BK3435_Start(*arg): | ||
return True | ||
|
||
def CHIP_BK3435_End(hid_downloader): | ||
return CHIP_EXTERN_End(hid_downloader) | ||
|
||
def ChangeInterface(hidDev, DownFormat): | ||
send_buf = bytearray(5) | ||
send_buf[0] = VPP_VCC_POWER_UPDATE | ||
send_buf[1] = DOWNLOAD_STATE | ||
send_buf[2] = HARD_SPI | ||
send_buf[3] = 100 | ||
send_buf[4] = DownFormat | ||
hidDev.WriteHid(send_buf) | ||
|
||
out_buf = hidDev.ReadHid(1) | ||
if out_buf[0] != 0xEE: | ||
print("Vpp打开失败!") | ||
return False | ||
|
||
return True | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import time | ||
import struct | ||
from ..hid_commands import * | ||
|
||
FT_MSG_SIZE_FLASH = 0x40 | ||
|
||
SPI_CHIP_ERASE_CMD = 0xc7 | ||
SPI_CHIP_ENABLE_CMD = 0x06 | ||
SPI_READ_PAGE_CMD = 0x03 | ||
SPI_WRITE_PAGE_CMD = 0x02 | ||
SPI_SECTRO_ERASE_CMD = 0x20 | ||
SPI_SECUR_SECTOR_ERASE = 0x44 | ||
SPI_ID_READ_CMD = 0x9F | ||
SPI_STATU_WR_LOW_CMD = 0x01 | ||
SPI_STATU_WR_HIG_CMD = 0x31 | ||
SPI_READ_REG = 0x05 | ||
|
||
def Wait_Busy_Down(spi): | ||
while True: | ||
send_buf = bytearray(2) | ||
send_buf[0] = SPI_READ_REG | ||
send_buf[1] = 0x00 | ||
out_buf = spi.xfer2(send_buf) | ||
if not (out_buf[0] & 0x01): | ||
break | ||
time.sleep(0.1) | ||
|
||
def Read_ID(spi): | ||
send_buf = bytearray(4) | ||
send_buf[0] = SPI_ID_READ_CMD | ||
send_buf[1] = 0x00 | ||
send_buf[2] = 0x00 | ||
send_buf[3] = 0x00 | ||
out_buf = spi.xfer2(send_buf) | ||
flash_id = out_buf[1:4] | ||
flash_id.append(0) | ||
flash_id = bytes(flash_id) | ||
flash_id = struct.unpack('<I', flash_id[0:4])[0] | ||
return flash_id | ||
|
||
def CHIP_ENABLE_Command(spi): | ||
send_buf = bytearray(1) | ||
send_buf[0] = SPI_CHIP_ENABLE_CMD | ||
spi.xfer(send_buf) | ||
Wait_Busy_Down(spi) | ||
|
||
def Statu_Write(hidDev, cmd, cmdHead): | ||
send_buf = bytearray(FT_MSG_SIZE_FLASH) | ||
send_buf[0] = cmd | ||
send_buf[1:len(cmdHead)+1] = cmdHead | ||
spi.xfer(send_buf) | ||
Wait_Busy_Down(spi) | ||
|
||
def CHIP_EXTERN_Reset(spi_downloader, *args): | ||
spi = spi_downloader.spi | ||
RunStatu = True | ||
|
||
Id = Read_ID(spi) | ||
# print("flash_id: {:X}".format(Id)) | ||
|
||
if Id == 0x001340C8 or Id == 0x001640C8: | ||
send_buf = bytearray(1) | ||
CHIP_ENABLE_Command(spi) | ||
send_buf[0] = 0 | ||
Statu_Write(spi, SPI_STATU_WR_LOW_CMD, send_buf) | ||
send_buf[0] = 0 | ||
Statu_Write(spi, SPI_STATU_WR_HIG_CMD, send_buf) | ||
elif Id == 0x001440E0 or Id == 0x001423C2: | ||
send_buf = bytearray(2) | ||
CHIP_ENABLE_Command(spi) | ||
send_buf[0] = 0x00 | ||
send_buf[1] = 0x08 | ||
Statu_Write(spi, SPI_STATU_WR_LOW_CMD, send_buf) | ||
elif Id == 0x001340E0 or Id == 0x001323C2: | ||
send_buf = bytearray(2) | ||
CHIP_ENABLE_Command(spi) | ||
send_buf[0] = 0x00 | ||
send_buf[1] = 0x00 | ||
Statu_Write(spi, SPI_STATU_WR_LOW_CMD, send_buf) | ||
elif Id == 0x00FFFFFF or Id == 0x0: | ||
RunStatu = False | ||
else: | ||
send_buf = bytearray(1) | ||
CHIP_ENABLE_Command(spi) | ||
send_buf[0] = 0 | ||
Statu_Write(spi, SPI_STATU_WR_LOW_CMD, send_buf) | ||
|
||
if RunStatu: | ||
time.sleep(0.5) | ||
|
||
return RunStatu | ||
|
||
def CHIP_EXTERN_Erase(spi_downloader): | ||
spi = spi_downloader.spi | ||
CHIP_ENABLE_Command(spi) | ||
send_buf = bytearray(1) | ||
send_buf[0] = SPI_CHIP_ERASE_CMD | ||
spi.xfer(send_buf) | ||
Wait_Busy_Down(spi) | ||
return True | ||
|
||
def CHIP_EXTERN_End(hid_downloader): | ||
spi = spi_downloader.spi | ||
RunStatu = True | ||
|
||
Id = Read_ID(spi) | ||
|
||
if Id == 0x001340C8 or Id == 0x001640C8: | ||
send_buf = bytearray(1) | ||
CHIP_ENABLE_Command(spi) | ||
send_buf[0] = 0x3c | ||
Statu_Write(spi, SPI_STATU_WR_LOW_CMD, send_buf) | ||
send_buf[0] = 0x00 | ||
Statu_Write(spi, SPI_STATU_WR_HIG_CMD, send_buf) | ||
elif Id == 0x001440E0 or Id == 0x001423C2: | ||
send_buf = bytearray(2) | ||
CHIP_ENABLE_Command(hidDev) | ||
send_buf[0] = 0x3c | ||
send_buf[1] = 0x08 | ||
Statu_Write(spi, SPI_STATU_WR_LOW_CMD, send_buf) | ||
elif Id == 0x001340E0 or Id == 0x001323C2: | ||
send_buf = bytearray(2) | ||
CHIP_ENABLE_Command(spi) | ||
send_buf[0] = 0x3c | ||
send_buf[1] = 0x00 | ||
Statu_Write(spi, SPI_STATU_WR_LOW_CMD, send_buf) | ||
elif Id == 0x00FFFFFF or Id == 0x0: | ||
RunStatu = False | ||
else: | ||
send_buf = bytearray(1) | ||
CHIP_ENABLE_Command(hidDev) | ||
send_buf[0] = 0 | ||
Statu_Write(spi, SPI_STATU_WR_LOW_CMD, send_buf) | ||
|
||
if RunStatu: | ||
time.sleep(0.5) | ||
|
||
return RunStatu |
Oops, something went wrong.