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 其它指令的格式
协议帧的构成如下:
Head | Sign | Length | command | Data | Checksum | Trail |
---|
各个部分的详细说明:
名称 | 长度 | 功能 |
---|---|---|
Head | 2 Byte | 帧头(0x46,0x49) |
Sign | 1 Byte | 方向标识:【0x6A:PC发出】【0x68:MCU应答】 |
Length | 2 Byte | 除去帧头,其它数据的总字节数 |
command | 1 Byte | 第2节中的命令 |
Data | 最大160Byte | 数据 |
Checksum | 2 Byte | 校验和 |
Trail | 1Byte | 帧尾(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.2,0x49对应'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系列的协议分析