受下文启发:
参考:https://www.elektroda.com/rtvforum/topic3931424.html
BK7231 programming via SPI in flash memory mode - Python and Banana Pi
BK7231 is usually programmed via UART - this is allowed by the bootloader uploaded by the manufacturer. In exceptional situations, however, we can accidentally overwrite this bootloader - then we have to use the SPI programming mode to recover the blocked system. Here I will describe how the SPI mode works in BK7231 and I will present the simple code of my own primitive SPI programmer for Beken. The "programmer" will be written in Python and will run on a Raspberry Pi (okay, here on Banana Pi - but it's very similar on Raspberry).
We see the pins: P20, P21, P22, P23 (SCK, CSN, SI and SO). Contrary to appearances, it is not an external memory interface, as in ESP8266, here Beken itself is memory.
Beken is an SPI memory and identifies itself as 00 15 70 1C, so it looks like a bone similar to EN25QH16B - and the EN25QH16B datasheet with read / write operations can brighten the situation a bit here.
解释下: BK7231是博通集成的wifi芯片,内部存储是一个25系列的flash,型号应该是EN25QH16B,通过SPI对这个flash烧写即可完成对BK7231的烧写。
原文作者用python+ Raspberry Pi实现了对BK7231的烧写。
手头有CH347,CH347自带硬件SPI和8口GPIO,应该比Raspberry Pi更专业、更适合。
python程序,读写都成功了,flash用的是W25QH16。
#! /usr/bin/env python
#coding=utf-8
import ctypes
import os
import time
from ctypes import *
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
class spi_config(Structure):
_fields_ = [
("iMode", c_ubyte),
("iClock", c_ubyte),
("iByteOrder", c_ubyte),
("iSpiWriteReadInterval", c_ushort),
("iSpiOutDefaultData",c_ubyte),
("iChipSelect", c_ulong),
("CS1Polarity",c_ubyte),
("CS2Polarity", c_ubyte),
("iIsAutoDeativeCS", c_ushort),
("iActiveDelay", c_ushort),
("iDelayDeactive", c_ulong),
]
class dev_infor(Structure):
_fields_ =[
("iIndex", c_ubyte),
("DeviePath", c_ubyte*255),
("UsbClass", c_ubyte),
("FuncType", c_ubyte),
("DeviceID", c_byte*64),
("Mode", c_ubyte),
("DevHandle", c_ulong),
("BulkOutEndpMaxSize", c_ushort),
("BulkInEndpMaxSize", c_ushort),
("UsbSpeedType", c_ubyte),
("CH347funcType", c_ubyte),
("DataUpEndp", c_ubyte),
("DataDnEndp", c_ubyte),
("ProductString", c_byte*64),
("ManufacturerString", c_byte*64),
("WriteTimeout", c_ulong),
("ReadTimeout", c_ulong),
("FuncDescStr", c_byte*64),
("FirewareVer", c_ubyte),
]
def SPI_Init():
CH347_SPI = spi_config(
iMode = 0x03,
iClock = 0,
iByteOrder = 1,
iSpiWriteReadInterval = 0,
iSpiOutDefaultData = 0,
iChipSelect = 0x80,
CS1Polarity = 0,
CS2Polarity = 0,
iIsAutoDeative = 1,
iActiveDelay = 0,
iDelayDeactive = 0
)
"""
CH347_SPI = spi_config()
CH347_SPI.iMode = 0x03
CH347_SPI.iClock = 0x01
CH347_SPI.iByteOrder = 0x01
CH347_SPI.iSpiWriteReadInterval=0
CH347_SPI.iSpiOutDefaultData = 0xff
CH347_SPI.iChipSelect = 0x80
"""
#CH347.CH347SPI_Init(DevIndex, ctypes.byref(CH347_SPI))
# opendevice
if CH347.CH347OpenDevice(DevIndex) != -1:
print("CH347OpenDevice success")
CH347.CH347SetTimeout(0,0xffff,0xffff)
if CH347.CH347SPI_Init(DevIndex, CH347_SPI)==1:
print("CH347SPI_Init success")
def spi_readId(DevIndex):
cmd_buf = (c_byte * 4)()
len = 4
cmd_buf[0] = 0x9F
cmd_buf[1] = 0xFF
cmd_buf[2] = 0xFF
cmd_buf[3] = 0xFF
CH347.CH347SPI_WriteRead(DevIndex, 0x80, len, cmd_buf)
print("{0:x} {1:x} {2:x} {3:x}".format(cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]))
return cmd_buf[0] & 0xFF
def test_devinfo():
DEVINFO=dev_infor()
if CH347.CH347GetDeviceInfor(DevIndex,ctypes.byref(DEVINFO)) == 1:
print(list(DEVINFO.DeviePath))
print(''.join(chr(b) for b in DEVINFO.DeviePath))
print(DEVINFO.UsbClass)
print(DEVINFO.FuncType)
print(list(DEVINFO.DeviceID))
print(''.join(chr(b) for b in DEVINFO.DeviceID))
print(DEVINFO.Mode)
print(DEVINFO.DevHandle)
print(DEVINFO.BulkOutEndpMaxSize)
print(DEVINFO.BulkInEndpMaxSize)
print(DEVINFO.UsbSpeedType)
print(DEVINFO.CH347funcType)
print(DEVINFO.DataUpEndp)
print(DEVINFO.DataDnEndp)
print(list(DEVINFO.ProductString))
print(list(DEVINFO.ManufacturerString))
print(DEVINFO.FirewareVer)
else:
print("CH347GetDeviceInfor failed")
def spi_write( device_index, chip_select, write_data) :
"""
SPI write data.
Args:
device_index (int): Device number.
chip_select (int): Chip selection control. When bit 7 is 0, chip selection control is ignored.
When bit 7 is 1, chip selection operation is performed.
write_data (bytes): Data to write.
write_step (int, optional): The length of a single block to be read. Default is 512.
Returns:
bool: True if successful, False otherwise.
"""
write_step=1
write_buffer = ctypes.create_string_buffer(write_data)
write_length = len(write_data)
#CH347.CH347SPI_SetChipSelect(0,0xff00,0x00FF,0x00000001,5,5)
result = CH347.CH347SPI_Write(device_index, chip_select, write_length, write_step, write_buffer)
#CH347.CH347SPI_SetChipSelect(0,0xff00,0x0000,0x00000001,5,5)
if result==1:
#print("CH347SPI_Write ok :",write_buffer.raw,write_length)
print("\r\nCH347SPI_Write ok :",end="\r\n")
for x in range(write_length):
print(hex(write_buffer.raw[x]),end=" ")
return result
def spi_transfer(data):
# 发送数据
out_data = ctypes.create_string_buffer(data)
CH347.CH347SPI_WriteRead(0, 0x80, len(out_data), out_data)
#print("write success")
return out_data.raw
def spi_change_cs(iStatus):
"""
Change the chip selection status.
Args:
iStatus (int): Chip selection status. 0 = Cancel the piece to choose, 1 = Set piece selected.
Returns:
bool: True if successful, False otherwise.
"""
result = CH347.CH347SPI_ChangeCS(DevIndex, iStatus)
return result
def spi_read(chip_select: int, write_data: bytes, read_length: int) -> bytes:
"""
SPI read data.
Args:
chip_select (int): Chip selection control. When bit 7 is 0, chip selection control is ignored.
When bit 7 is 1, chip selection operation is performed.
write_data (bytes): Data to write.
read_length (int): Number of bytes to read.
Returns:
bytes: Data read in from the SPI stream if successful, None otherwise.
"""
write_length = len(write_data)
# Create ctypes buffer for write data
write_buffer = ctypes.create_string_buffer(write_data)
# Create ctypes buffer for read data
read_buffer = ctypes.create_string_buffer(read_length)
# Create combined buffer for read and write data
combined_buffer = ctypes.create_string_buffer(write_buffer.raw[:write_length] + read_buffer.raw)
result = CH347.CH347SPI_Read(DevIndex, chip_select, write_length, ctypes.byref(ctypes.c_ulong(read_length)), combined_buffer)
if result:
# Extract the read data from the combined buffer
read_data = combined_buffer[:read_length]
return bytes(read_data)
else:
return None
##################################
# @prief SPI_Closedevice
# //关闭SPI
##################################
def SPI_Closedevice():
CH347.CH347CloseDevice(DevIndex)
#CEN:GPIO02
#
def GPIO_CEN_SET():
#result=CH347.CH347GPIO_Set(DevIndex,0x01,0xff,0x01)
result=CH347.CH347GPIO_Set(DevIndex,0x04,0xff,0x04)
if result==1:
print("Set CEN hight success!")
def GPIO_CEN_CLR():
#result=CH347.CH347GPIO_Set(DevIndex,0x01,0xff,0x00)
result=CH347.CH347GPIO_Set(DevIndex,0x04,0xff,0x00)
if result==1:
print("Set CEN low success!")
def ChipReset():
# set CEN low for 1s
GPIO_CEN_CLR()
time.sleep(1)
GPIO_CEN_SET()
"""
- we initiate SPI in mode 3 (0b11 mode), frequency 30kHz (faster ones did not work for me)
- we set the CEN to a low state
- we wait 1 second
- we set the CEN to a high state
- we send D2 250 times after SPI
- we expect an answer once D2, then 249 times 00
"""
def BK_EnterSPIMode():
print('----------------BK_EnterSPIMode-----------------------\r\n')
ChipReset()
send_buf = bytearray(250)
for x in range(250):
send_buf[x] = 0xD2
read_data = spi_read( 0x80, bytes(send_buf), 250)
#for x in range(250):
# print(hex(read_data[x]),end = " ")
if(read_data[0]==0xD2):
print("BK_EnterSPIMode success\r\n")
else:
print("BK_EnterSPIMode failed\r\n")
#0x9F 0x00 0x00 0x00
a = spi_read( 0x80, b"\x9F\x00\x00\x00", 4)
for x in range(4):
print(hex(a[x]), end = ' ')
if a[0] == 0x00 and a[1] == 0x1c and a[2] == 0x70 and a[3] == 0x15:
print("ID OK")
return 1
print("ID bad")
return 0
def Wait_Busy_Down():
while True:
send_buf = bytearray(2)
send_buf[0] = 0x05
send_buf[1] = 0x00
out_buf = spi_read( 0x80, bytes(send_buf), 2)
if not (out_buf[1] & 0x01):
break
time.sleep(0.01)
def CHIP_ENABLE_Command():
send_buf = bytearray(1)
#send_buf[0] = SPI_CHIP_ENABLE_CMD
send_buf[0] = 0x06
#spi_transfer(bytes(send_buf))
spi_write(0,0x80,bytes(send_buf))
Wait_Busy_Down()
def WriteImage(startaddr,filename, maxSize):
print("WriteImage "+filename)
statinfo = os.stat(filename)
size = statinfo.st_size
size = (size+255)//256*256
#size = maxSize;
count = 0
addr = startaddr
f = open(filename, "rb")
while count < size:
print("count "+str(count) +"/"+str(size))
if 1:
if 0 == (addr & 0xfff):
CHIP_ENABLE_Command()
send_buf = bytearray(4)
send_buf[0] = 0x20
send_buf[1] = (addr & 0xFF0000) >> 16
send_buf[2] = (addr & 0xFF00) >> 8
send_buf[3] = addr & 0xFF
#spi_transfer(bytes(send_buf))
spi_write(0,0x80,bytes(send_buf))
Wait_Busy_Down()
buf = f.read(256)
if buf:
CHIP_ENABLE_Command()
send_buf = bytearray(4+256)
send_buf[0] = 0x02
send_buf[1] = (addr & 0xFF0000) >> 16
send_buf[2] = (addr & 0xFF00) >> 8
send_buf[3] = addr & 0xFF
send_buf[4:4+256] = buf
#spi_transfer(bytes(send_buf))
spi_write(0,0x80,bytes(send_buf))
count += 256
addr += 256
f.close()
return True
def ReadStart(startaddr, filename, readlen):
count = 0
addr = startaddr
f = open(filename, "wb")
size = readlen
size = (size+255)//256*256
print("Reading")
while count < size:
print("count "+str(count) +"/"+str(size))
send_buf = bytearray(4)
send_buf[0] = 0x03
send_buf[1] = (addr & 0xFF0000) >> 16
send_buf[2] = (addr & 0xFF00) >> 8
send_buf[3] = addr & 0xFF
#print("address[")
#print(send_buf,end=']:\r\n')
result = spi_read( 0x80, bytes(send_buf), 256)
count += 256
addr += 256
#part = bytearray(result[4:4+256])
#part = bytearray(result[0:256])
for x in range(256):
print(hex(result[x]), end = '')
print(" ", end = '')
f.write(result)
f.close()
ChipReset()
return True
if __name__ == "__main__":
dll_path = "./ch347dlla64.dll" # Replace with the actual path to the DLL
DevIndex = 0
CH347=windll.LoadLibrary(dll_path)
print("........................................................................")
print(". CH347 BK flash test .")
print("........................................................................")
SPI_Init()
if spi_change_cs(1):
print("SPI CS CHANAGED")
#BK_EnterSPIMode()
"""
read_data = spi_read( 0x80, b"\x90\x00\x00\x00",2)
print(read_data)
print('\r\n---------------- 0x9F end -----------------------\r\n')
read_data = spi_read( 0x80, b"\x4B\x00\x00\x00", 6)
print(read_data)
"""
read_data = spi_read( 0x80, b"\x03\x00\x01\x00", 256)
for x in range(256):
print(hex(read_data[x]), end = ' ')
#print(read_data)
SPI_Closedevice
# this will allow you to write directly bootloader + app
#WriteImage(0,"OpenBK7231T_App_QIO_35a81303.bin", 0x200000)
# if you have an app that was loaded by bkWriter 1.60 with offs 0x11000,
# and you have broke your bootloader, you can take bootloader from OBK build
# and then restore an app
#WriteImage(0,"OpenBK7231T_App_QIO_35a81303.bin", 0x11000)
WriteImage(0,"OpenBK7231T_QIO_1.17.308.bin", 0x1100)
#WriteImage(0x11000,"REST.bin", 0x200000)
# I used this to verify my code and it work
ReadStart(0,"tstReadS.bin", 0x1100)
print("It's finished")
1、程序写的很粗糙,只是跑通了读写。
2、关于CH347,需要有CH347DLLA64.DLL动态库的支持,可以从官网上下。
3、手头没有BK7231,没有去调烧写BK7231。
4、博通集成家的芯片一般都是要SPI烧写,如果BK7231调通了,其他应该也差不多。就怕其他型号芯片进入SPI 烧写步骤还有FLASH型号和bk7231有差异,那样可能就很难搞了。