关于 STCGAL 的剖析

本贴最后更新于 1314 天前,其中的信息可能已经时移世改

我没有写过 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 直接安装

图片.png

stcgal.py

// 闭眼猜这是引包sys就类似于go的os应该system
import sys
// 下面是他自己写的包
import stcgal.frontend

if __name__ == "__main__":
    sys.exit(stcgal.frontend.cli())

__name__ 这个写法很奇怪,百度

图片.png

如果调用放为__main__那么触发 sys.exit(stcgal.frontend.cli()) 函数

sys.exit 根据不同的数字判定当前是怎么个退出,是有异常退出呢?是成功退出呢?意外中断呢?以下是翻出来的 API

图片.png

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 出来不明白的步骤,看打印信息

图片.png

比如 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))
  • STM32

    STM32 系列基于专为要求高性能、低成本、低功耗的嵌入式应用专门设计的 ARM Cortex®-M0,M0+,M3, M4 和 M7 内核。按内核架构分为不同产品: 主流产品(STM32F0、STM32F1、STM32F3)、超低功耗产品(STM32L0、STM32L1、STM32L4、STM32L4+)、高性能产品(STM32F2、STM32F4、STM32F7、STM32H7)

    5 引用 • 2 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...