├── README.md └── bk7231_spi_flasher.py /README.md: -------------------------------------------------------------------------------- 1 | # BK7231 SPI Flasher 2 | This is a simple SPI programmer for BK7231T chips. By default, everyone is using UART bootloader to program BK7231T, but in some rare cases one might overwrite Beken bootloader and thus brick the BK. The only way to unbrick it, is to use SPI flashing mode. 3 | 4 | This tool is able to read and write whole flash content of BK7231T (maybe also other chips?) in the SPI mode, by using SPI access pins. 5 | 6 | Tested only on Banana Pi, but should also work on Raspberry Pi. 7 | 8 | Detailed writeup and guide: 9 | https://www.elektroda.com/rtvforum/topic3931424.html 10 | 11 | -------------------------------------------------------------------------------- /bk7231_spi_flasher.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import sys 4 | import os 5 | import argparse 6 | import os 7 | import sys 8 | import platform 9 | import spidev 10 | import time 11 | import RPi.GPIO as GPIO 12 | 13 | def ChipReset(): 14 | # set CEN low for 1s 15 | GPIO.setup(CENGPIO, GPIO.OUT) 16 | GPIO.output(CENGPIO, GPIO.LOW) 17 | time.sleep(1) 18 | GPIO.output(CENGPIO, GPIO.HIGH) 19 | 20 | def BK_EnterSPIMode(spi): 21 | ChipReset() 22 | print("Send 250 'D2'") 23 | send_buf = bytearray(250) 24 | for x in range(250): 25 | send_buf[x] = 0xD2 26 | a = spi.xfer2(send_buf) 27 | 28 | 29 | for x in range(250): 30 | print(hex(a[x]), end = '') 31 | print(" ", end = '') 32 | 33 | time.sleep(0.1) 34 | 35 | print("Test by sending get ID") 36 | cmd_id = bytearray(4) 37 | cmd_id[0] = 0x9F 38 | cmd_id[1] = 0x0 39 | cmd_id[2] = 0x0 40 | cmd_id[3] = 0x0 41 | 42 | a = spi.xfer2(cmd_id) 43 | 44 | for x in range(4): 45 | print(hex(a[x]), end = '') 46 | print(" ", end = '') 47 | 48 | if a[0] == 0x00 and a[1] == 0x1c and a[2] == 0x70 and a[3] == 0x15: 49 | print("ID OK") 50 | return 1 51 | 52 | print("ID bad") 53 | return 0 54 | 55 | SPI_CHIP_ERASE_CMD = 0xc7 56 | SPI_CHIP_ENABLE_CMD = 0x06 57 | SPI_READ_PAGE_CMD = 0x03 58 | SPI_WRITE_PAGE_CMD = 0x02 59 | SPI_SECTRO_ERASE_CMD = 0x20 60 | SPI_SECUR_SECTOR_ERASE = 0x44 61 | SPI_ID_READ_CMD = 0x9F 62 | SPI_STATU_WR_LOW_CMD = 0x01 63 | SPI_STATU_WR_HIG_CMD = 0x31 64 | SPI_READ_REG = 0x05 65 | 66 | def Wait_Busy_Down(): 67 | while True: 68 | send_buf = bytearray(2) 69 | send_buf[0] = SPI_READ_REG 70 | send_buf[1] = 0x00 71 | out_buf = spi.xfer2(send_buf) 72 | if not (out_buf[1] & 0x01): 73 | break 74 | time.sleep(0.01) 75 | 76 | def CHIP_ENABLE_Command(): 77 | send_buf = bytearray(1) 78 | send_buf[0] = SPI_CHIP_ENABLE_CMD 79 | spi.xfer(send_buf) 80 | Wait_Busy_Down() 81 | 82 | def WriteImage(startaddr,filename, maxSize): 83 | print("WriteImage "+filename) 84 | statinfo = os.stat(filename) 85 | # size = statinfo.st_size 86 | # size = (size+255)//256*256 87 | size = maxSize; 88 | 89 | count = 0 90 | addr = startaddr 91 | f = open(filename, "rb") 92 | 93 | while count < size: 94 | print("count "+str(count) +"/"+str(size)) 95 | if 1: 96 | if 0 == (addr & 0xfff): 97 | CHIP_ENABLE_Command() 98 | send_buf = bytearray(4) 99 | send_buf[0] = 0x20 100 | send_buf[1] = (addr & 0xFF0000) >> 16 101 | send_buf[2] = (addr & 0xFF00) >> 8 102 | send_buf[3] = addr & 0xFF 103 | spi.xfer(send_buf) 104 | Wait_Busy_Down() 105 | 106 | buf = f.read(256) 107 | if buf: 108 | CHIP_ENABLE_Command() 109 | send_buf = bytearray(4+256) 110 | send_buf[0] = 0x02 111 | send_buf[1] = (addr & 0xFF0000) >> 16 112 | send_buf[2] = (addr & 0xFF00) >> 8 113 | send_buf[3] = addr & 0xFF 114 | send_buf[4:4+256] = buf 115 | spi.xfer(send_buf) 116 | count += 256 117 | addr += 256 118 | 119 | f.close() 120 | 121 | return True 122 | 123 | def ReadStart(startaddr, filename, readlen): 124 | count = 0 125 | addr = startaddr 126 | f = open(filename, "wb") 127 | size = readlen 128 | size = (size+255)//256*256 129 | print("Reading") 130 | 131 | while count < size: 132 | print("count "+str(count) +"/"+str(size)) 133 | send_buf = bytearray(4+256) 134 | send_buf[0] = 0x03 135 | send_buf[1] = (addr & 0xFF0000) >> 16 136 | send_buf[2] = (addr & 0xFF00) >> 8 137 | send_buf[3] = addr & 0xFF 138 | result = spi.xfer2(send_buf) 139 | count += 256 140 | addr += 256 141 | part = bytearray(result[4:4+256]) 142 | for x in range(256): 143 | print(hex(part[x]), end = '') 144 | print(" ", end = '') 145 | f.write(part) 146 | 147 | f.close() 148 | 149 | ChipReset() 150 | return True 151 | 152 | # Adjust it for your pin 153 | CENGPIO = GPIO.PB+21 154 | # also adjust it 155 | GPIO.setmode(GPIO.RAW) 156 | 157 | spi = spidev.SpiDev() 158 | spi.open(0, 0) 159 | # SPI mode 3 160 | spi.mode = 0b11 161 | # it will fail to read ID with higher speeds (at least it fails for me) 162 | spi.max_speed_hz = 30000 163 | 164 | if BK_EnterSPIMode(spi) == 0: 165 | print("Failed to read flash id") 166 | exit(); 167 | 168 | # this will allow you to write directly bootloader + app 169 | #WriteImage(0,"OpenBK7231T_App_QIO_35a81303.bin", 0x200000) 170 | # if you have an app that was loaded by bkWriter 1.60 with offs 0x11000, 171 | # and you have broke your bootloader, you can take bootloader from OBK build 172 | # and then restore an app 173 | WriteImage(0,"OpenBK7231T_App_QIO_35a81303.bin", 0x11000) 174 | WriteImage(0x11000,"REST.bin", 0x200000) 175 | # I used this to verify my code and it work 176 | #ReadStart(0,"tstReadS.bin", 0x1100) 177 | 178 | 179 | 180 | --------------------------------------------------------------------------------