PYTHON+CH347读写25系列flash

受下文启发:  

参考: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上位机

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有差异,那样可能就很难搞了。

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值