PYQT5:基于QsciScintilla的代码编辑器分析9--STC51烧写协议及实现

STC51单片机烧写软件《stcflash.py》在第2章已经有简单的介绍,看看作者大神laborer的版权声明:

# stcflash  Copyright (C) 2013  laborer (laborer@126.com)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

这里提到这是一个免费的软件,只要遵守《GNU GPL》开源的精神,我们就可以发行,修改该软件。说实话,这些我都不太懂。反正本着学习的精神拿来用,并做一些修改就是了。

1.烧写hex文件流程

烧写流程在源码中注释得很清楚,函数名也说明了自己的功能,所以直接看源码:

    def  MyProgram(self, prog, code, erase_eeprom=None):   
        #下面的语句是打印调试信息到 《test.log》文件,有需要就取消注释
        #logging.basicConfig(filename="test.log", filemode="w", 
        #                            format="%(asctime)s %(name)s:%(levelname)s:%(message)s", 
        #                            datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
        #0.判断是否发送下载指令
        if self.downloadWay == '2':
            prog.sendISPcmd(self.baudArray[self.baudIndex])
        elif self.downloadWay == '3':
            prog.sendISPcmd(self.baudArray[self.baudIndex], self.DIYString)
        #1.开始检测芯片
        self.SetTipString( '正在检测 MCU,请给芯片上电。'   )
        ok=prog.detect()        
        if not ok:
            self.SetTipString("检测芯片失败. 请重试...")
            return
        self.SetTipString("检测芯片完成. done")    
        self.Myprint_info(prog) #打印检测芯片的结果:1 】晶振频率;2】芯片型号;3】ROMM
        
        if prog.protocol is None:
            self.SetTipString("目标芯片无法识别. 终止下载。")
            return       
        if code is None:
            self.SetTipString("下载文件内容为空。 终止下载。")
            return
        prog.comfirm_MCU_model() 
        #2.建立更高波特率       
        self.SetTipString("尝试更高波特率...")
        prog.handshake()        
        self.SetTipString("波特率为 %d " % prog.baudrate)
        
        #3.擦除芯片
        prog.prepare_for_erase()
        self.SetTipString("开始擦除芯片......" )
        prog.erase()
        self.SetTipString("擦除芯片成功。")
        
        #4.开始写入程序数据
        self.SetTipString("开始写入,程序数据大小为%d bytes... " %  len(code))
        oldbar = 0
        for progress in prog.flash(code):
            bar = progress
            self.SetTipString("#%d -  %d bytes 已完成..." %( oldbar,bar ))
            oldbar = bar       
        self.SetTipString("全部写入完毕,运行程序...") 

        prog.unknown_packet_3()
        #5.结束整个流程
        prog.terminate()

prog就是把软件《stcflash.py》封装起来的一个类。

2.STC51烧写协议介绍

  PC命令        说明            		MCU回应					 	对应源码函数			
	7F    引导MCU进入ISP并测量时钟   	50    MCU选项信息			detect()
	50    设置MCU型号等           	8F    应答					comfirm_MCU_model()
	8F/01 新波特率测试            	8F/01 测试应答				handshake() 
	8E    正式修改波特率           	84    修改波特率应答			包含在handshake()的末尾,STC15,STC8没有用到此指令
	80/05准备擦除					80/05 应答					prepare_for_erase()
	84/03 擦除芯片         			00/03    应答				prog.erase()
	00/02 下载程序               	00/02    应答校验和			flash(code)
	69    型号等                		8D    应答(部分芯片需要)		unknown_packet_3()
	8D    设置选项                	50    应答选项				本软件不提供此功能
	82/FF    退出                   运行程序					terminate()

3.指令帧格式

3.1检测芯片指令0x7F

这个指令就是一个单独的字节,二进制就是0b0111 1111,对应电平就是开始一个低电平,后面7个高电平,这个方便MCU计算上位机的波特率。

3.2 其它指令的格式

协议帧的构成如下:

HeadSignLengthcommandDataChecksumTrail

各个部分的详细说明:

名称长度功能
Head2 Byte帧头(0x46,0x49)
Sign1 Byte方向标识:【0x6A:PC发出】【0x68:MCU应答】
Length2 Byte除去帧头,其它数据的总字节数
command1 Byte第2节中的命令
Data最大160Byte数据
Checksum2 Byte校验和
Trail1Byte帧尾(0x16)

下面举例子说明,以STC15的准备擦除指令0x05为例,PC发出的指令如下:

[序号]0  1  2  3  4  5  6  7  8  9  10 11 12
[数据]46 B9 6A 00 0B 05 00 00 5A A5 01 79 16

为了说明方便,假设数据存于一个Python列表list[]中,于是有Head = list[0:1]=[0x46,0xB9], Sign = list[2]=[0x6A], Length = list[3:4]=[0x00,0x0B], command = list[5]=[0x05], data = list[6:9]=[0x00,0x00,0x5A,0xA5], Checksum=list[10:11]=[0x01,0x79], Trail=list[12]=[0x16].

3.3 调试时的一些原始数据

下面给出我调试时监控串口的一些原始数据,供读者参考:

一、检测	
WRITE gth: 1, Data: 7F 	
READ  gth: 0, Data: 	
WRITE gth: 1, Data: 7F 	
READ  gth: 0, Data: 	
WRITE  1, Data: 7F 	
READ  gth: 1, Data:  [序号][数据]
0  1  2  3  4  5  6  7  8  9  10 11 12[13 14 15]16 17 18 19 20 21[22 23]24[25 26]27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
46 B9 68 00 34 50 90 05 6B 93 F7 BB 9F 01 51 CF B0 FD 26 D8 00 00 72 53 00 F4 08 05 06 70 93 02 18 1F 22 23 21 00 F4 F0 04 D5 FF 6F 7F FF FF 16 03 23 60 13 4E 16(15F2K60S2)
46 B9 68 00 34 50 8E C6 71 99 F7 BB 9F 00 A8 70 50 FD 26 E2 00 00 72 54 00 F2 A4 05 06 70 99 02 14 19 1B 1D 22 80 14 10 04 E8 EA 72 BF FF FF 18 06 08 00 12 D0 16(15W104)
46 B9 68 00 34 50 8E C6 71 99 F7 BB 9F 01 51 5A 80 FD 26 DD 00 00 72 54 00 F2 A4 05 06 70 99 02 14 19 1B 1D 22 80 14 10 04 E8 EA 72 7F FF FF 18 06 08 00 12 4F 16(15W104,22.1184M)

46 B9 68 00 30 50[01 51 43]10 6C 03 01 FF FF 8F BF FF 26 E1 F7 73 73 55 00 F6 44 0A 85 E3 6C 8D 08 20 20 20 01 00 00 80 05 38 18 12 07 70 FF 0F EC 16(8F2K32S2,22.1184) 
46 B9 68 00 30 50[00 A8 CB]B8 6D 01 02 FF FF 8F BF FF 26 E1 F7 73 73 55 00 F6 44 0A 85 E3 6C 8D 08 20 20 20 01 00 00 80 05 38 18 12 07 70 FF 11 72 16(8F2K32S2,11.0592)  	
46 B9 68 00 30 50[01 51 6D]40 66 02 01 FF FF 8B BF FF 26 CF F7 FE[73 55]00[F6 28]0A 86 2E 63 86 08 20 20 20 01 00 00 FE 05 42 19 09 17 70 FF 10 64 16(8F8K64S4A12,22.1184) 	
	
46 B9 68 00 34 50 90 05 6B 93 F7 BB 9F 00 A8 87 C0 FD 26 D3 00 00 72 53 00 F4 08 05 06 70 93 02 18 1F 22 23 21 00 F4 F0 04 D5 FF 6D BF FF FF 16 03 23 60 13 A5 16 	

[13:15]=  01 51 CF  内部晶振=0x0151CF * 256=22.1184M   0x00A870*256 =11.068M  
[22:23]=0x72,0x53 表示MCU版本号,0x72意思是7.20x49对应'R',合起来就是7.2R
[24]=0x00,配置ALE引脚,看门狗,时钟倍速等等
[25:26]=0xF4,0x08  --- mcu Model(15F2K60S2)     [25:26]=0xF2,0xA4  --- mcu Model(15W104) 
以下两条指令忽略	
WRITE  34, Data: 46 B9 6A 00 20 00 0B 00 C0 80 C0 FF C0 00 80 80 80 FF 80 00 40 80 40 FF 40 00 00 80 00 00 00 0A 12 16 	

WRITE gth: 1, Data: FE 	
READ  gth: 0, Data: 	
WRITE gth: 1, Data: FE 	
READ  gth: 1, Data: 46 B9 68 00 20 00 0B 0D 38 12 D8 18 7D 1A 53 25 61 30 65 35 15 4A 91 5F 54 53 6A 73 A4 00 00 07 8B 16 
	
WRITE gth: 34, Data: 46 B9 6A 00 20 00 0C 6D 40 6E 40 6F 40 70 40 71 40 72 40 6D 40 6E 40 6F 40 70 40 71 40 72 40 08 D0 16 	
		
WRITE gth: 1, Data: FE 	
READ  gth: 1, Data: 46  B9 68 00 20 00 0C 47 C6 47 F3 48 20 48 5C 48 7F 48 B1 47 C1 47 F3 48 20 48 52 48 7A 48 AC 0A A1 16 
	
二、尝试更高波特率0x01
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		6E 40 	FF D0 	40 6F 	81 04 26 16 (22.1184MHz)
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		6E 40 	FF D0 	40 28 	81 03 DF 16 (18.432MHz)	

WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		6E 40 	FF D0 	80 6E 	81 04 65 16 (11.0592MHz)	
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		6F 40 	FF D0 	C0 67 	81 04 9F 16 (05.5296MHz)
以上为15F2K60S2																				  
WRITE  th: 16, Data: 46 B9 6A 00 0E 01 		73 40 	FD BF 	FC 9F 	81 05 04 16 
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		72 40 	FD C0 	FC A0 	81 05 05 16 (11.0592MHz)15F104	
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		72 40 	FD C1 	FC A1 	81 05 07 16	(22.1184MHz)15F104 波特率38400
  ============以下为STC8F2K32S2====================
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		00 00 	FF CC 	01 6B 	81 03 31 16 (22.1184MHz)
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		00 00 	FF CC 	01 6A 	81 03 30 16	(11.0592MHz)
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		00 00 	FF CC 	01 06 	81 02 CC 16 (05.5296MHz)
WRITE gth: 16, Data: 46 B9 6A 00 0E 01 		00 00 	FF CC 	01 28 	81 02 EE 16 (18.432MHz) 	

READ  gth: 1, Data: 46 B9 68 00 07 	
READ  gth: 4, Data: 01 00 70 16 	
ERIAL_d Rate: 115200       ERIAL_pBits: 1, Parity: Even, DataBits: 8	
三、准备指令 0x05
WRITE gth: 13, Data: 46 B9 6A 00 0B 05 00 00 5A A5 01 79 16 
  ============以下为STC8F2K32S2====================	   		
WRITE gth: 13, Data: 46 B9 6A 00 0B 05 00 00 5A A5 01 79 16 
READ  gth: 1, Data: 46 B9 68 00 07 05 00 74 16 
四、擦除指令 0x03	
WRITE gth: 13, Data: 46 B9 6A 00 0B 03 00 00 5A A5 01 77 16 
WRITE gth: 13, Data: 46 B9 6A 00 0B 03 00 00 5A A5 01 77 16	   (STC8F2K32S2)	   		

READ  gth: 1, Data: 46 B9 68 00 0E 03 F4 08 01 78 07 E8 2C 03 09 16 
READ  gth: 1, Data: 46 B9 68 00 0E 03 F6 44 43 D3 01 21 9A 03 85 16   (STC8F2K32S2)
五、写入程序数据 0x02	
WRITE gth: 141, Data: 46 B9 6A 00 8B 02 00 00 5A A5 02 00 2E E4 FF FE E4 FD FC 0D BD 00 01 0C BC 01 F8 BD F4 F5 0F BF 00 01 0E EF 64 64 4E 70 E7 22 E4 F5 90 12 00 03 75 90 FF 12 00 03 80 F2 78 7F E4 F6 D8 FD 75 81 	
WRITE gth: 141, Data: 46 B9 6A 00 8B 22 00 00 5A A5 02 00 2E E4 FF FE E4 FD FC 0D BD 00 01 0C BC 01 F8 BD F4 F5 0F BF 00 01 0E EF 64 64 4E 70 E7 22 E4 F5 90 12 00 03 75 90 FF 12 00 03 80 F2 78 7F E4 F6 D8 FD 75 81 	

READ  gth: 0, Data:   46 B9 6A 00 8D 00 00 00 00 00 00 80	
READ  gth: 1, Data: 46 B9 68 00 08 02 54 00 C6 16 	
WRITE gth: 141, Data: 46 B9 6A 00 8B 02 00 80 5A A5 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 	

READ  gth: 1, Data: 46 B9 68 00 08 02 54 00 C6 16 	
WRITE gth: 141, Data: 46 B9 6A 00 8B 02 01 00 5A A5 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 	

READ  gth: 1, Data: 46 B9 68 00 08 02 54 00 C6 16	
WRITE gth: 141, Data: 46 B9 6A 00 8B 02 01 80 5A A5 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 	

READ  gth: 1, Data: 46 B9 68 00 08 02 54 00 C6 16
六、写入配置 0x04	  (可忽略)
WRITE gth: 77, Data: 46 B9 6A 00 4B 04 00 00 5A A5 FF FF FF 00 FF FF 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 01 FF 51 FF 43 FF 10 FF FD 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 	
	
READ  gth: 1, Data: 46 B9 68 00 08 04 54 00 C8 16 
七、启动芯片	
WRITE gth: 9, Data: 46 B9 6A 00 07 FF 01 70 16 	
CLOSE t Closed	

说了这么多,不知道有没有成功的把读者绕晕,反正我自己说得昏昏沉沉;让我想起调试这部分程序时,也是昏昏沉沉,所幸最后调试成功了。

4.《stcflash.py》源码阅读重点

4.1 detect()

这个函数就是以较低的波特率(一般是1200或者2400,本程序采用2400)尝试发送一个字节0x7F,不停的发送,直到单片机有数据返回,然后对返回的数据分析。分析数据的关键就是要知道每个字节的含义。
返回的数据中,在程序中用到的就是晶振频率,MCU型号代码,固件版本。
STC12的返回数据有16个字节是8次测量0x7F的高电平宽度数据,把这8个数据取平均后,代入公式 f = 脉宽 x 波特率 x 12 / 7,就可以得到晶振频率。比如在波特率1200下,获得一串测量数据为16 05 16 05 16 05 16 05 16 05 16 05 16 05 16 05,就是0x1605 = 5637(十进制),代入公式得 f = 5637 x 1200 x 12 / 7 = 11,596,114 Hz 。
STC15的返回数据中直接给出晶振频率,只要把该数 放大256倍就是晶振频率。比如上面3.3节的原始数据有一个是[13:15]= 01 51 CF ,那么可以得到 晶振频率=0x0151CF * 256=22.1184MHz。
STC8的晶振频率也是直接给出的,只是数据位置和STC15不同,可以从原始数据中看出来。
晶振频率数据在接下来的“新波特率测试”指令要用到,用来确定更高速的波特率。

    def detect(self):
        #1. 不停发送 0x7F,等待应答
        for i in range(500):
            try:
                self.__conn_write([0x7F, 0x7F])
                cmd, dat = self.recv(0.03, [0x68])
                break
            except IOError:
                pass
        else:            
            return False            #raise IOError()
        #2. 计算MCU 的晶振频率
        if self.protocol in PROTOSET_15F2K: # dat[7:9] *0x100 Hz,/ 1000000 =MHz
            self.fosc = (dat[7]*0x1000000 +dat[8]*0x10000+dat[9]*0x100) /1000000
            
            logging.info("dat[7]:%02X dat[8]:%02X dat[9]%02X" % (dat[7], dat[8], dat[9]))
            logging.info("self.fosc:%04f" % (self.fosc))
        elif self.protocol in PROTOSET_8F2K: # dat[0:2] *0x100 Hz,/ 1000000 =MHz
            self.fosc = (dat[0]*0x1000000 +dat[1]*0x10000+dat[2]*0x100) /1000000
        else:
            self.fosc = (float(sum(dat[0:16:2]) * 256 + sum(dat[1:16:2])) / 8
                         * self.conn.baudrate / 580974)
        #3. 分析MCU 型号,固件版本,ROM容量
        self.info = dat[16:]
        self.version = "%d.%d%c" % (self.info[0] >> 4,
                                    self.info[0] & 0x0F,
                                    self.info[1])
        self.model = self.info[3:5]

        self.name, self.romsize = self.__model_database(self.model)

        logging.info("Model ID: %02X %02X" % tuple(self.model))
        logging.info("Model name: %s" % self.name)
        logging.info("ROM size: %s" % self.romsize)
        #4. 假如事先未知型号,现在确定,
        if self.protocol is None:
            try:
                self.protocol = {0xF0: PROTOCOL_89,       #STC89/90C5xRC
                                 0xF1: PROTOCOL_89,       #STC89/90C5xRD+
                                 0xF2: PROTOCOL_12Cx052,  #STC12Cx052
                                 0xD1: PROTOCOL_12C5A,    #STC12C5Ax
                                 0xD2: PROTOCOL_12C5A,    #STC10Fx
                                 0xE1: PROTOCOL_12C52,    #STC12C52x
                                 0xE2: PROTOCOL_12C5A,    #STC11Fx
                                 0xE6: PROTOCOL_12C52,    #STC12C56x
                                 0X72:PROTOCOL_12Cx052,   #stc12Cx052
                                 0X74:PROTOCOL_15F2K,   #stc15F2K
                                 0X76:PROTOCOL_8F2K,   #stc15F2K
                                 }[self.model[0]]
            except KeyError:
                pass
        #5. 根据MCU型号,确定校验和的是1 Byte还是 2Byte;还有串口的奇偶模式
        if self.protocol in PROTOSET_PARITY or self.protocol in PROTOSET_15F2K \
                        or self.protocol in PROTOCOL_8F2K:
            self.chkmode = 2
            self.conn.parity = serial.PARITY_EVEN
        else:
            self.chkmode = 1
            self.conn.parity = serial.PARITY_NONE

        if self.protocol is not None:
            del self.info[-self.chkmode:]

            logging.info("Protocol ID: %s" % self.protocol)
            logging.info("Checksum mode: %d" % self.chkmode)
            logging.info("UART Parity: %s"
                         % {serial.PARITY_NONE: "NONE",
                            serial.PARITY_EVEN: "EVEN",
                            }[self.conn.parity])

        for i in range(0, len(self.info), 16):
            logging.info("Info string [%d]: %s"
                         % (i // 16,
                            " ".join(["%02X" % j for j in self.info[i:i+16]])))
        return True

4.2 handshake()

这个函数是以刚才发送0x7F的波特率通信的。用于指导单片机切换到目标波特率。原先作者所支持的单片机型号,是可以适应任意晶振频率的;我增加的STC15,STC8两种型号就只支持晶振频率为:5.5296M,11.0592M,18.432M,22.1184M 。因为我没有找到规律,只能监控串口,生搬硬套。具体请看代码。
握手成功后使用新的波特率,一直到结束。

    def handshake(self):
        baud0 = self.conn.baudrate

        if self.protocol in PROTOSET_15F2K or self.protocol in PROTOSET_8F2K:
            for baud in [115200, 57600, 38400, 28800, 19200,
                         14400, 9600, 4800, 2400, 1200]:
                t = self.fosc * 1000000 / baud / 2
                if self.protocol in PROTOSET_15F2K:
                    if self.fosc < 11.2 and self.fosc > 11:
                        baudstr = [0x6E, 0x40, 0xff,0xd0,    0x80,0x6E, 0x81]
                    elif self.fosc > 18.3 and self.fosc < 18.5:
                        baudstr = [0x6E, 0x40, 0xff,0xd0,   0x40,0x28, 0x81]
                    elif self.fosc > 5.4 and self.fosc < 5.6:
                        baudstr = [0x6f, 0x40, 0xff,0xd0,   0xC0,0x67, 0x81]
                    else:           #elif self.fosc < 22.5 and self.fosc > 22:     默认为22.1184MHz
                        baudstr = [0x6E, 0x40, 0xff,0xd0,   0x40,0x6f, 0x81]
                elif self.protocol in PROTOSET_8F2K:
                    if self.fosc < 11.2 and self.fosc > 11:
                        baudstr = [0x00, 0x00, 0xff,0xCC,   0x01,0x6A, 0x81]
                    elif self.fosc > 18.3 and self.fosc < 18.5:
                        baudstr = [0x00, 0x00, 0xff,0xCC,   0x01,0x28, 0x81]
                    elif self.fosc > 5.4 and self.fosc < 5.6:
                        baudstr = [0x00, 0x00, 0xff,0xCC,   0x01,0x06, 0x81]
                    else:               #elif self.fosc < 22.5 and self.fosc > 22:  默认为22.1184MHz
                        baudstr = [0x00, 0x00, 0xff,0xCC,   0x01,0x6B, 0x81]
                logging.info("Test baudrate %d (accuracy %0.4f) using config %s"
                             % (baud,
                                abs(round(t) - t) / t,
                                " ".join(["%02X" % i for i in baudstr])))
                self.send(0x01, baudstr )
                try:
                    cmd, dat = self.recv()
                    break
                except Exception:
                    logging.info("Cannot use baudrate %d" % baud)
                    time.sleep(0.2)
                    self.conn.flushInput()
                finally:
                    self.__conn_baudrate(baud0, False)
                    
            logging.info("Change baudrate to %d" % baud)
            self.__conn_baudrate(baud)
            self.baudrate = baud            
        else:
            for baud in [115200, 57600, 38400, 28800, 19200,
                         14400, 9600, 4800, 2400, 1200]:
                t = self.fosc * 1000000 / baud / 32
                if self.protocol not in PROTOSET_89:
                    t *= 2

                if abs(round(t) - t) / t > 0.03:
                    continue

                if self.protocol in PROTOSET_89:
                    tcfg = 0x10000 - int(t + 0.5)
                else:
                    if t > 0xFF:
                        continue
                    tcfg = 0xC000 + 0x100 - int(t + 0.5)

                baudstr = [tcfg >> 8,
                           tcfg & 0xFF,
                           0xFF - (tcfg >> 8),
                           min((256 - (tcfg & 0xFF)) * 2, 0xFE),
                           int(baud0 / 60)]

                logging.info("Test baudrate %d (accuracy %0.4f) using config %s"
                             % (baud,
                                abs(round(t) - t) / t,
                                " ".join(["%02X" % i for i in baudstr])))

                if self.protocol in PROTOSET_89:
                    freqlist = (40, 20, 10, 5)
                else:
                    freqlist = (30, 24, 20, 12, 6, 3, 2, 1)

                for twait in range(0, len(freqlist)):
                    if self.fosc > freqlist[twait]:
                        break

                logging.info("Waiting time config %02X" % (0x80 + twait))

                self.send(0x8F, baudstr + [0x80 + twait])

                try:
                    self.__conn_baudrate(baud)
                    cmd, dat = self.recv()
                    break
                except Exception:
                    logging.info("Cannot use baudrate %d" % baud)

                    time.sleep(0.2)
                    self.conn.flushInput()
                finally:
                    self.__conn_baudrate(baud0, False)

            else:                
                self.parent.SetTipString("IOError in stcflash.py handshake()")#raise IOError()

            logging.info("Change baudrate to %d" % baud)

            self.send(0x8E, baudstr)
            self.__conn_baudrate(baud)
            self.baudrate = baud

            cmd, dat = self.recv()

4.3 sendISPcmd()

在单片机运行时发送ISP命令,是要单片机程序配合的。这个函数有个特别说明的地方:发送命令后,不要有等待,立刻进入detect()函数。0.1秒的等待都不行。

    def sendISPcmd(self, baud=115200, cmdstr='5a 3a 6c'):   
        #1. 发送ISP命令的波特率是单片机运行时的波特率
        baud0 = self.conn.baudrate
        self.conn.parity = serial.PARITY_NONE        
        self.__conn_baudrate(baud)
        
        dat = []
        hexList=cmdstr.split(' ')
        for hex0 in hexList:
            dat += binascii.a2b_hex(hex0)
        self.__conn_write(dat)    
        #2.恢复原来波特率  ,即发送检测命令 0x7F 时的波特率 
        if self.protocol in PROTOSET_PARITY or self.protocol in PROTOSET_15F2K \
                    or self.protocol in PROTOSET_8F2K:
            self.conn.parity = serial.PARITY_EVEN
        self.__conn_baudrate(baud0,  flush=False)   #默认flush=True 将会产生0.2秒的延时
                
    def __conn_baudrate(self, baud, flush=True):
        logging.debug("baud: %d" % baud)
        #   这里特别注意,在芯片工作时发送下载指令,flush 必须为 False,因为不能有延时。
        if flush:       #               我调试了大半天才找到原因
            self.conn.flush()
            time.sleep(0.2)

        self.conn.baudrate = baud

单片机的串口接收程序要对命令进行处理才有效,以默认的命令为例:

#define RS_ISP  IAP_CONTR |= 0x60           // 热启动,准备下载程序
void UART1_Isr() __interrupt UART1_VECTOR
{
    if (TI) {
        TI = 0;
        if (txdData.index < txdData.len){
           SBUF = txdData.buf[txdData.index];
           txdData.index++;
        }else {
            txdData.busy=false;
            txdData.len = 0;
            txdData.index = 0;
        }
    }else if (RI){
		RI = 0;
		if(rxdData.len < RXD_LENGTH){
            rxdData.buf[rxdData.len] = SBUF;
            rxdData.len++;
            if(rxdData.len==3){	//接收到下载指令,进入bootload
                if((rxdData.buf[0]==0x5a)&&
                   (rxdData.buf[1]==0x3a)&&
                   (rxdData.buf[2]==0x6c))
                    RS_ISP;
            }
		}else{//   避免溢出处理
		    rxdData.len = 0;
		}
    }
}

参考文章:
1.STC12C系列的协议分析

posted @ 2022-12-26 12:28  汉塘阿德  阅读(21)  评论(0编辑  收藏  举报  来源