我没有写过 xxx 但是今天需求就是涉及到了 xxx 怎么办
这是我所认为你是否是一名软开人员的基本素质要求,你没接触过但是看看能理解能改改,具备这种素质的,那么你可以说你是一名软开,如果不具备,那么你可能只是一个工具人而已,我将在下文结合自身来进行一次自我检测,看看自己是否有没有这种素质
我的方法
看任何东西时,先看脉络流程,在看具体
看不懂没关系,看不懂可以先猜一个大概
比如 def abs(file) 你就猜这是一个返回绝对路径的东西
再比如 transS2I(s) 可能看不明白,但通过 trans 猜个转译也行,标注出来,然后往下继续看流程看脉络
当你浏览的差不多以后,在一步一步的进行剖析,去印证你猜的结果就好,猜错了改过来。
目录结构
- debian - doc - stcgal - __init__.py - __main__.py - frontend.py - ihex.py - models.py - options.py - protocols.py - utils.py - tests - setup.py - stcgal.py
这里我从一级目录的两个 py 文件看起
steup.py
见名知意,steup 应该和安装有关
// 这里面只有一个函数,提供了name,version,packages,install_requires // 看到这里,我猜测就是类似npm publish的第三方包,然后npm install xxx就可以安装调用 // 不过据我了解,python的安装是pip应该是 pip install stcgal就可以安装使用了 // 具体参数我百度了一下列出 setup( // 包名 name = "stcgal", // 包版本 version = stcgal.__version__, // find_packages包目录,排除了doc和测试包 packages = find_packages(exclude=["doc", "tests"]), // install_requires 安装的前提条件 // pyserial https://pypi.org/project/pyserial/ 串口库版本要>3.0 // tqdm https://pypi.org/project/tqdm/ 为循环显示进度条的库? > 4.0 install_requires = ["pyserial>=3.0", "tqdm>=4.0.0"], // 将“extras”(项目的可选特性)的名称映射到字符串或字符串列表,以指定必须安装哪些其他发行版来支持这些特性。嗯,要必须安装pyusb才行 extras_require = { "usb": ["pyusb>=1.0.0"] }, // 这里我猜这是命令的 名称 在terminal环境中 entry_points = { "console_scripts": [ "stcgal = stcgal.frontend:cli", ], }, // 描述和一些其他的东西 description = "STC MCU ISP flash tool", long_description = long_description, long_description_content_type = "text/markdown", keywords = "stc mcu microcontroller 8051 mcs-51", url = "https://github.com/grigorig/stcgal", author = "Grigori Goronzy", author_email = "greg@kinoho.net", license = "MIT License", platforms = "any", // 分类符, 这样便于 pypi 索引, 由 pypi 固定提供, [https://pypi.python.org/pypi?%3Aaction=list_classifiers](https://pypi.python.org/pypi?%3Aaction=list_classifiers) classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Embedded Systems", "Topic :: Software Development", ], // 测试 test_suite = "tests", // 测试需要的依赖 tests_require = ["PyYAML"], )
然后我们把 endpoint 改了试试,去印证一下我的猜测,改成 stcgal1111 然后执行 python3.7 ./steup.py install
直接安装
stcgal.py
// 闭眼猜这是引包,sys就类似于go的os应该,system import sys // 下面是他自己写的包 import stcgal.frontend if __name__ == "__main__": sys.exit(stcgal.frontend.cli())
__name__
这个写法很奇怪,百度
如果调用放为__main__那么触发 sys.exit(stcgal.frontend.cli())
函数
sys.exit 根据不同的数字判定当前是怎么个退出,是有异常退出呢?是成功退出呢?意外中断呢?以下是翻出来的 API
cli()
// python创建函数方式 def cli(): // 创建了一个格式化参数对象,传入了argparse.RawDescriptionHelpFormatter,和描述 // `RawDescriptionHelpFormatter``RawTextHelpFormatter``ArgumentDefaultsHelpFormatter``MetavarTypeHelpFormatter` 一共有这么些转换方式,不细究 parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description="stcgal {} - an STC MCU ISP flash tool\n".format(stcgal.__version__) + "(C) 2014-2018 Grigori Goronzy and others\nhttps://github.com/grigorig/stcgal") // 下面是加参数项,有详细的描述,翻译一下就好,arg是区分类型的,传入type,default为不传参数的默认值 parser.add_argument("code_image", help="code segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?') parser.add_argument("eeprom_image", help="eeprom segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?') parser.add_argument("-a", "--autoreset", help="cycle power automatically by asserting DTR", action="store_true") parser.add_argument("-r", "--resetcmd", help="shell command for board power-cycling (instead of DTR assertion)", action="store") parser.add_argument("-P", "--protocol", help="protocol version (default: auto)", choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "stc8", "usb15", "auto"], default="auto") parser.add_argument("-p", "--port", help="serial port device", default="/dev/ttyUSB0") parser.add_argument("-b", "--baud", help="transfer baud rate (default: 19200)", type=BaudType(), default=19200) parser.add_argument("-l", "--handshake", help="handshake baud rate (default: 2400)", type=BaudType(), default=2400) parser.add_argument("-o", "--option", help="set option (can be used multiple times, see documentation)", action="append") parser.add_argument("-t", "--trim", help="RC oscillator frequency in kHz (STC15+ series only)", type=float, default=0.0) parser.add_argument("-D", "--debug", help="enable debug output", action="store_true") parser.add_argument("-V", "--version", help="print version info and exit", action="store_true") opts = parser.parse_args() # run programmer gal = StcGal(opts) return gal.run()
如果还是不清楚,中间做了什么,那就直接 print 出来不明白的步骤,看打印信息
比如 parser.parse_args()就是按照用户执行命令输入的参数转换成了对象
继续往下走 StcGal(opts)
Stcgal
// python中类的定义 class StcGal: // 注释 """STC ISP flash tool frontend""" // 构造函数,self = js中的this,opts为调用时传入的参数 def __init__(self, opts): self.opts = opts // 初始化 规则 self.initialize_protocol(opts) // 这里主要就是代理 cli中的-p参数然后分配不同的class给 self.protocol def initialize_protocol(self, opts): """Initialize protocol backend""" if opts.protocol == "stc89": self.protocol = Stc89Protocol(opts.port, opts.handshake, opts.baud) elif opts.protocol == "stc12a": self.protocol = Stc12AProtocol(opts.port, opts.handshake, opts.baud) elif opts.protocol == "stc12b": self.protocol = Stc12BProtocol(opts.port, opts.handshake, opts.baud) elif opts.protocol == "stc12": self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud) elif opts.protocol == "stc15a": self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000)) elif opts.protocol == "stc15": self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000)) elif opts.protocol == "stc8": self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000)) elif opts.protocol == "usb15": self.protocol = StcUsb15Protocol() else: self.protocol = StcAutoProtocol(opts.port, opts.handshake, opts.baud) self.protocol.debug = opts.debug
run()
run 函数分为两段
开始猜吧
try: // 连接 self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd) if isinstance(self.protocol, StcAutoProtocol): if not self.protocol.protocol_name: raise StcProtocolException("cannot detect protocol") base_protocol = self.protocol self.opts.protocol = self.protocol.protocol_name print("Protocol detected: %s" % self.opts.protocol) # recreate self.protocol with proper protocol class self.initialize_protocol(self.opts) else: base_protocol = None // 调用 分配好的规则对象 对应的 初始化函数生成对象 self.protocol.initialize(base_protocol)
第二部分
try: if self.opts.code_image: // 刷程序了开始 self.program_mcu() // 正常流程退出 return 0 // 关闭连接 self.protocol.disconnect() return 0
mcu()
def program_mcu(self): """Execute the standard programming flow.""" code_size = self.protocol.model.code ee_size = self.protocol.model.eeprom print("Loading flash: ", end="") sys.stdout.flush() // 获取文件信息 bindata = self.load_file_auto(self.opts.code_image) # warn if it overflows // 代码量超了 if len(bindata) > code_size: print("WARNING: code_image overflows into eeprom segment!", file=sys.stderr) // 文件大小超了 if len(bindata) > (code_size + ee_size): print("WARNING: code_image truncated!", file=sys.stderr) bindata = bindata[0:code_size + ee_size] # add eeprom data if supplied if self.opts.eeprom_image: print("Loading EEPROM: ", end="") sys.stdout.flush() eedata = self.load_file_auto(self.opts.eeprom_image) if len(eedata) > ee_size: print("WARNING: eeprom_image truncated!", file=sys.stderr) eedata = eedata[0:ee_size] if len(bindata) < code_size: bindata += bytes(code_size - len(bindata)) elif len(bindata) > code_size: print("WARNING: eeprom_image overlaps code_image!", file=sys.stderr) bindata = bindata[0:code_size] bindata += eedata # pad to 512 byte boundary if len(bindata) % 512: bindata += b'\xff' * (512 - len(bindata) % 512) if self.opts.option: self.emit_options(self.opts.option) // 握手检测 self.protocol.handshake() // 清除数据 self.protocol.erase_flash(len(bindata), code_size) // 写数据 self.protocol.program_flash(bindata) self.protocol.program_options() self.protocol.disconnect()
erase_flash(len(bindata), code_size)
def erase_flash(self, erase_size, _): """Erase the MCU's flash memory. 是用块儿擦除命令擦除闪存 """ blks = ((erase_size + 511) // 512) * 2 print("Erasing %d blocks: " % blks, end="") sys.stdout.flush() // 数据包 packet = bytes([0x84, blks, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33]) // 发送数据包 self.write_packet(packet) // 接受返回信息 response = self.read_packet() if response[0] != 0x80: raise StcProtocolException("incorrect magic in erase packet") print("done")
def write_packet(self, packet_data): """Send packet to MCU. 用有效的负载构造数据包发送到MCU """ # frame start and direction magic packet = bytes() packet += self.PACKET_START packet += self.PACKET_HOST # packet length and payload packet += struct.pack(">H", len(packet_data) + 5) packet += packet_data # checksum and end code packet += bytes([sum(packet[2:]) & 0xff]) packet += self.PACKET_END self.dump_packet(packet, receive=False) self.ser.write(packet) self.ser.flush()
这里 ser 是设备信息,之前在 connect 时初始化完毕了
def connect(self, autoreset=False, resetcmd=False): """Connect to MCU and initialize communication. Set up serial port, send sync sequence and get part info. """ self.ser = serial.Serial(port=self.port, parity=self.PARITY) # set baudrate separately to work around a bug with the CH340 driver # on older Linux kernels self.ser.baudrate = self.baud_handshake # fast timeout values to deal with detection errors self.ser.timeout = 0.5 self.ser.interCharTimeout = 0.5 # avoid glitches if there is something in the input buffer self.ser.flushInput() if autoreset: self.reset_device(resetcmd) else: print("Waiting for MCU, please cycle power: ", end="") sys.stdout.flush() # send sync, and wait for MCU response # ignore errors until we see a valid response self.status_packet = None while not self.status_packet: try: self.pulse() self.status_packet = self.get_status_packet() if len(self.status_packet) < 23: raise StcProtocolException("status packet too short") except (StcFramingException, serial.SerialTimeoutException): pass print("done") # conservative timeout values self.ser.timeout = 15.0 self.ser.interCharTimeout = 1.0
program_flash(bindata)
def program_flash(self, data): """Program the MCU's flash memory. 写到MCU,块大小取决于MCU的RAM大小 PROGRAM_BLOCKSIZE """ // 循环data,步进 PROGRAM_BLOCKSIZE for i in range(0, len(data), self.PROGRAM_BLOCKSIZE): // 构造数据包 packet = bytes(3) packet += struct.pack(">H", i) packet += struct.pack(">H", self.PROGRAM_BLOCKSIZE) packet += data[i:i+self.PROGRAM_BLOCKSIZE] while len(packet) < self.PROGRAM_BLOCKSIZE + 7: packet += b"\x00" csum = sum(packet[7:]) & 0xff // 发送数据包 self.write_packet(packet) // 接受返回信息 response = self.read_packet() if len(response) < 1 or response[0] != 0x80: raise StcProtocolException("incorrect magic in write packet") elif len(response) < 2 or response[1] != csum: raise StcProtocolException("verification checksum mismatch") // 每次写完都记录一下当前位置,块内容,数据量 暂时不知道干啥的 self.progress_cb(i, self.PROGRAM_BLOCKSIZE, len(data)) self.progress_cb(len(data), self.PROGRAM_BLOCKSIZE, len(data))
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于