当前位置: 首页 > news >正文

SC16IS752 树莓派 底层驱动

SC16IS752在树莓派官方系统有现成驱动,但是部分没有驱动的linux 主机可能就得从寄存器底层开发了,这里展示:

驱动串口

#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ User-space pseudo TTY bridge for SC16IS752 on Raspberry Pi SPI1. This does not use the Linux sc16is7xx driver and does not create /dev/ttySC*. Instead it creates pseudo serial devices: /tmp/ttySC0_soft /tmp/ttySC1_soft Programs can open those paths like a normal serial port while this script is running. Bytes are moved between the pseudo TTY and the SC16IS752 UART FIFOs. """ import argparse import glob import os import pty import select import sys import termios import time import tty try: import gpiod except ImportError: print("Missing python3-gpiod.", file=sys.stderr) raise PIN_CS = 18 PIN_MISO = 19 PIN_MOSI = 20 PIN_SCLK = 21 XTAL = 14_745_600 REG_RHR_THR = 0x00 REG_IER = 0x01 REG_FCR_IIR = 0x02 REG_LCR = 0x03 REG_MCR = 0x04 REG_LSR = 0x05 REG_TXLVL = 0x08 REG_RXLVL = 0x09 REG_EFCR = 0x0F REG_DLL = 0x00 REG_DLH = 0x01 REG_EFR = 0x02 REG_SPR = 0x07 def find_gpiochip(): env_path = os.environ.get("SC16_GPIOCHIP") paths = [env_path] if env_path else sorted(glob.glob("/dev/gpiochip*")) errors = [] for path in paths: try: chip = gpiod.Chip(path) if hasattr(chip, "get_line"): lines = [chip.get_line(pin) for pin in (PIN_CS, PIN_MISO, PIN_MOSI, PIN_SCLK)] names = [(line.name() or "") for line in lines] else: lines = [chip.get_line_info(pin) for pin in (PIN_CS, PIN_MISO, PIN_MOSI, PIN_SCLK)] names = [(line.name or "") for line in lines] if names[0] in ("GPIO18", "PIN12") or any(name.startswith("GPIO") for name in names): print(f"Using GPIO chip {path} for pins GPIO18-21") chip.close() return path chip.close() except Exception as exc: errors.append(f"{path}: {exc}") try: chip.close() except Exception: pass # Last resort for older libgpiod/kernel combinations that expose usable # offsets but do not provide helpful line names. for path in paths: try: chip = gpiod.Chip(path) for pin in (PIN_CS, PIN_MISO, PIN_MOSI, PIN_SCLK): if hasattr(chip, "get_line"): chip.get_line(pin) else: chip.get_line_info(pin) print(f"Using GPIO chip {path} for pins GPIO18-21") chip.close() return path except Exception as exc: errors.append(f"{path}: {exc}") try: chip.close() except Exception: pass detail = "; ".join(errors) if errors else "no /dev/gpiochip* devices found" raise RuntimeError( "Could not find GPIO chip for 40-pin GPIO18-21. " "Run 'gpioinfo | grep -E \"GPIO18|GPIO19|GPIO20|GPIO21\"' and, if needed, " "set SC16_GPIOCHIP=/dev/gpiochipN. Checked: " + detail ) class GpiodV2Line: def __init__(self, request, offset): self.request = request self.offset = offset def set_value(self, value): from gpiod.line import Value self.request.set_value(self.offset, Value.ACTIVE if value else Value.INACTIVE) def get_value(self): from gpiod.line import Value return 1 if self.request.get_value(self.offset) == Value.ACTIVE else 0 def release(self): pass class BitBangSPI: def __init__(self): chip_path = find_gpiochip() if hasattr(gpiod, "request_lines"): from gpiod.line import Direction, Value self.chip = None self.request = gpiod.request_lines( chip_path, consumer="sc16-soft-tty", config={ PIN_CS: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE), PIN_MOSI: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE), PIN_SCLK: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE), PIN_MISO: gpiod.LineSettings(direction=Direction.INPUT), }, ) self.cs = GpiodV2Line(self.request, PIN_CS) self.miso = GpiodV2Line(self.request, PIN_MISO) self.mosi = GpiodV2Line(self.request, PIN_MOSI) self.sclk = GpiodV2Line(self.request, PIN_SCLK) else: self.request = None self.chip = gpiod.Chip(chip_path) self.cs = self.chip.get_line(PIN_CS) self.miso = self.chip.get_line(PIN_MISO) self.mosi = self.chip.get_line(PIN_MOSI) self.sclk = self.chip.get_line(PIN_SCLK) self.cs.request(consumer="sc16-soft-tty", type=gpiod.LINE_REQ_DIR_OUT, default_vals=[1]) self.mosi.request(consumer="sc16-soft-tty", type=gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) self.sclk.request(consumer="sc16-soft-tty", type=gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) self.miso.request(consumer="sc16-soft-tty", type=gpiod.LINE_REQ_DIR_IN) def close(self): for line in (self.cs, self.mosi, self.sclk, self.miso): try: line.release() except Exception: pass if self.request is not None: self.request.release() if self.chip is not None: self.chip.close() def transfer_byte(self, value): received = 0 for bit in range(7, -1, -1): self.mosi.set_value((value >> bit) & 1) self.sclk.set_value(1) received = (received << 1) | self.miso.get_value() self.sclk.set_value(0) return received def transfer(self, data): self.cs.set_value(0) out = [self.transfer_byte(value) for value in data] self.cs.set_value(1) return out class SC16IS752: def __init__(self, spi): self.spi = spi @staticmethod def addr(reg, channel, read): return (0x80 if read else 0x00) | ((reg & 0x0F) << 3) | ((channel & 0x01) << 1) def read_reg(self, channel, reg): return self.spi.transfer([self.addr(reg, channel, True), 0x00])[1] def write_reg(self, channel, reg, value): self.spi.transfer([self.addr(reg, channel, False), value & 0xFF]) def init_uart(self, channel, baud): divisor = int(round(XTAL / (16 * baud))) self.write_reg(channel, REG_IER, 0x00) self.write_reg(channel, REG_LCR, 0xBF) self.write_reg(channel, REG_EFR, 0x10) self.write_reg(channel, REG_LCR, 0x80) self.write_reg(channel, REG_DLL, divisor & 0xFF) self.write_reg(channel, REG_DLH, (divisor >> 8) & 0xFF) self.write_reg(channel, REG_LCR, 0x03) self.write_reg(channel, REG_FCR_IIR, 0x07) time.sleep(0.01) self.write_reg(channel, REG_FCR_IIR, 0x01) # The Waveshare 2-CH RS485 HAT uses active-high DE. Enable the # SC16IS752 automatic RS485 RTS direction control and invert RTS so # the transceiver drives the bus only while bytes are being sent. self.write_reg(channel, REG_EFCR, 0x30) self.write_reg(channel, REG_MCR, 0x00) def probe(self): ok = True for channel, value in ((0, 0x5A), (1, 0xA5)): self.write_reg(channel, REG_SPR, value) got = self.read_reg(channel, REG_SPR) print(f"CH{channel}: SPR wrote 0x{value:02x}, read 0x{got:02x}") ok = ok and got == value return ok def write_uart(self, channel, data): index = 0 while index < len(data): space = self.read_reg(channel, REG_TXLVL) if not space: time.sleep(0.001) continue chunk = data[index:index + min(space, 64)] for value in chunk: self.write_reg(channel, REG_RHR_THR, value) index += len(chunk) def read_uart(self, channel): count = self.read_reg(channel, REG_RXLVL) if not count: return b"" return bytes(self.read_reg(channel, REG_RHR_THR) for _ in range(count)) class SoftTTY: def __init__(self, sc16, channel, link_path): self.sc16 = sc16 self.channel = channel self.link_path = link_path self.master_fd, slave_fd = pty.openpty() tty.setraw(self.master_fd) slave_name = os.ttyname(slave_fd) os.close(slave_fd) try: os.unlink(link_path) except FileNotFoundError: pass os.symlink(slave_name, link_path) print(f"CH{channel}: {link_path} -> {slave_name}") def close(self): try: os.unlink(self.link_path) except FileNotFoundError: pass os.close(self.master_fd) def pump_from_pty(self): try: data = os.read(self.master_fd, 4096) except OSError: return if data: self.sc16.write_uart(self.channel, data) def pump_from_uart(self): data = self.sc16.read_uart(self.channel) if data: os.write(self.master_fd, data) def main(): parser = argparse.ArgumentParser(description="Create pseudo TTY devices for SC16IS752 UART channels.") parser.add_argument("--baud", type=int, default=9600) parser.add_argument("--ch0", default="/tmp/ttySC0_soft") parser.add_argument("--ch1", default="/tmp/ttySC1_soft") args = parser.parse_args() spi = BitBangSPI() ttys = [] try: sc16 = SC16IS752(spi) if not sc16.probe(): raise RuntimeError("SC16IS752 scratchpad probe failed") sc16.init_uart(0, args.baud) sc16.init_uart(1, args.baud) ttys = [ SoftTTY(sc16, 0, args.ch0), SoftTTY(sc16, 1, args.ch1), ] print("Soft TTY bridge is running. Keep this process alive.") print("Example: python3 -m serial.tools.miniterm /tmp/ttySC0_soft 9600") while True: readable, _, _ = select.select([tty_obj.master_fd for tty_obj in ttys], [], [], 0.01) for tty_obj in ttys: if tty_obj.master_fd in readable: tty_obj.pump_from_pty() tty_obj.pump_from_uart() finally: for tty_obj in ttys: tty_obj.close() spi.close() if __name__ == "__main__": main()

RS485 CH·-CH2 回环测试

#!/usr/bin/env python3 # -*- coding: utf-8 -*- import argparse import time import serial def read_for(port, seconds): data = bytearray() end = time.monotonic() + seconds while time.monotonic() < end: chunk = port.read(256) if chunk: data.extend(chunk) else: time.sleep(0.01) return bytes(data) def exchange(tx, rx, label, payload, read_seconds): tx.reset_input_buffer() rx.reset_input_buffer() tx.write(payload) tx.flush() print(f"{label}: sent {payload.hex(' ')}") received = read_for(rx, read_seconds) if received: print(f"{label}: received {received.hex(' ')}") else: print(f"{label}: received NO_DATA") return received def main(): parser = argparse.ArgumentParser(description="Two-port loopback test for ttySC or soft TTY serial ports.") parser.add_argument("--port0", default="/tmp/ttySC0_soft") parser.add_argument("--port1", default="/tmp/ttySC1_soft") parser.add_argument("--baud", type=int, default=9600) parser.add_argument("--payload", default="01 03 08 06", help="Hex bytes, for example: '01 03 08 06'") parser.add_argument("--read-seconds", type=float, default=1.0) parser.add_argument("--interval", type=float, default=1.0) args = parser.parse_args() payload = bytes.fromhex(args.payload) print(f"PORT0={args.port0}, PORT1={args.port1}, baud={args.baud}, payload={payload.hex(' ')}") print("Press Ctrl+C to stop.") port0 = serial.Serial(args.port0, args.baud, timeout=0.05, write_timeout=1) port1 = serial.Serial(args.port1, args.baud, timeout=0.05, write_timeout=1) try: while True: exchange(port0, port1, "PORT0 -> PORT1", payload, args.read_seconds) exchange(port1, port0, "PORT1 -> PORT0", payload, args.read_seconds) print("-" * 60) time.sleep(args.interval) except KeyboardInterrupt: print("\nStopped.") finally: port0.close() port1.close() if __name__ == "__main__": main()

驱动和直接回环测试

#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ SC16IS752 双路 RS485 回环测试。 接线使用树莓派 SPI1: CS = GPIO18 MISO = GPIO19 MOSI = GPIO20 SCLK = GPIO21 本程序不需要同时运行 sc16_soft_tty.py。 它直接通过 GPIO 模拟 SPI 访问 SC16IS752,并开启芯片内部 RS485 自动方向控制。 """ import argparse import glob import os import sys import time try: import gpiod except ImportError: print("缺少 python3-gpiod,请先安装:sudo apt install python3-gpiod", file=sys.stderr) raise PIN_CS = 18 PIN_MISO = 19 PIN_MOSI = 20 PIN_SCLK = 21 # 这块板子的丝印/示例里,CH1/CH2 与 SC16IS752 的 A/B 通道顺序相反。 BOARD_CH1 = 1 BOARD_CH2 = 0 XTAL = 14_745_600 REG_RHR_THR = 0x00 REG_IER = 0x01 REG_FCR_IIR = 0x02 REG_LCR = 0x03 REG_MCR = 0x04 REG_LSR = 0x05 REG_SPR = 0x07 REG_TXLVL = 0x08 REG_RXLVL = 0x09 REG_EFCR = 0x0F REG_DLL = 0x00 REG_DLH = 0x01 REG_EFR = 0x02 LSR_TEMT = 0x40 def 找_gpiochip(): 指定芯片 = os.environ.get("SC16_GPIOCHIP") 芯片列表 = [指定芯片] if 指定芯片 else sorted(glob.glob("/dev/gpiochip*")) 错误列表 = [] for 路径 in 芯片列表: try: chip = gpiod.Chip(路径) if hasattr(chip, "get_line"): lines = [chip.get_line(pin) for pin in (PIN_CS, PIN_MISO, PIN_MOSI, PIN_SCLK)] names = [(line.name() or "") for line in lines] else: lines = [chip.get_line_info(pin) for pin in (PIN_CS, PIN_MISO, PIN_MOSI, PIN_SCLK)] names = [(line.name or "") for line in lines] if names[0] in ("GPIO18", "PIN12") or any(name.startswith("GPIO") for name in names): chip.close() print(f"使用 GPIO 芯片:{路径}") return 路径 chip.close() except Exception as exc: 错误列表.append(f"{路径}: {exc}") try: chip.close() except Exception: pass for 路径 in 芯片列表: try: chip = gpiod.Chip(路径) for pin in (PIN_CS, PIN_MISO, PIN_MOSI, PIN_SCLK): if hasattr(chip, "get_line"): chip.get_line(pin) else: chip.get_line_info(pin) chip.close() print(f"使用 GPIO 芯片:{路径}") return 路径 except Exception as exc: 错误列表.append(f"{路径}: {exc}") try: chip.close() except Exception: pass raise RuntimeError( "找不到 GPIO18/19/20/21 所在的 gpiochip。" "可运行:gpioinfo | grep -E 'GPIO18|GPIO19|GPIO20|GPIO21'。" "也可手动指定:SC16_GPIOCHIP=/dev/gpiochipN。" "检查结果:" + "; ".join(错误列表) ) class GpiodV2Line: def __init__(self, request, offset): self.request = request self.offset = offset def set_value(self, value): from gpiod.line import Value self.request.set_value(self.offset, Value.ACTIVE if value else Value.INACTIVE) def get_value(self): from gpiod.line import Value return 1 if self.request.get_value(self.offset) == Value.ACTIVE else 0 def release(self): pass class 软件SPI: def __init__(self): chip_path = 找_gpiochip() if hasattr(gpiod, "request_lines"): from gpiod.line import Direction, Value self.chip = None self.request = gpiod.request_lines( chip_path, consumer="sc16-ch-loop", config={ PIN_CS: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE), PIN_MOSI: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE), PIN_SCLK: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE), PIN_MISO: gpiod.LineSettings(direction=Direction.INPUT), }, ) self.cs = GpiodV2Line(self.request, PIN_CS) self.miso = GpiodV2Line(self.request, PIN_MISO) self.mosi = GpiodV2Line(self.request, PIN_MOSI) self.sclk = GpiodV2Line(self.request, PIN_SCLK) else: self.request = None self.chip = gpiod.Chip(chip_path) self.cs = self.chip.get_line(PIN_CS) self.miso = self.chip.get_line(PIN_MISO) self.mosi = self.chip.get_line(PIN_MOSI) self.sclk = self.chip.get_line(PIN_SCLK) self.cs.request(consumer="sc16-ch-loop", type=gpiod.LINE_REQ_DIR_OUT, default_vals=[1]) self.mosi.request(consumer="sc16-ch-loop", type=gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) self.sclk.request(consumer="sc16-ch-loop", type=gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) self.miso.request(consumer="sc16-ch-loop", type=gpiod.LINE_REQ_DIR_IN) def close(self): for line in (self.cs, self.mosi, self.sclk, self.miso): try: line.release() except Exception: pass if self.request is not None: self.request.release() if self.chip is not None: self.chip.close() def 传输字节(self, value): received = 0 for bit in range(7, -1, -1): self.mosi.set_value((value >> bit) & 1) self.sclk.set_value(1) received = (received << 1) | self.miso.get_value() self.sclk.set_value(0) return received def 传输(self, data): self.cs.set_value(0) out = [self.传输字节(value) for value in data] self.cs.set_value(1) return out class SC16IS752: def __init__(self, spi): self.spi = spi @staticmethod def 地址(reg, channel, read): return (0x80 if read else 0x00) | ((reg & 0x0F) << 3) | ((channel & 0x01) << 1) def 读寄存器(self, channel, reg): return self.spi.传输([self.地址(reg, channel, True), 0x00])[1] def 写寄存器(self, channel, reg, value): self.spi.传输([self.地址(reg, channel, False), value & 0xFF]) def 初始化串口(self, channel, baud): divisor = int(round(XTAL / (16 * baud))) self.写寄存器(channel, REG_IER, 0x00) self.写寄存器(channel, REG_LCR, 0xBF) self.写寄存器(channel, REG_EFR, 0x10) self.写寄存器(channel, REG_LCR, 0x80) self.写寄存器(channel, REG_DLL, divisor & 0xFF) self.写寄存器(channel, REG_DLH, (divisor >> 8) & 0xFF) self.写寄存器(channel, REG_LCR, 0x03) self.写寄存器(channel, REG_FCR_IIR, 0x07) time.sleep(0.01) self.写寄存器(channel, REG_FCR_IIR, 0x01) # 规格书 EFCR:bit4=RTSCON 自动方向控制,bit5=RTSINVER 发送时高电平。 self.写寄存器(channel, REG_EFCR, 0x30) self.写寄存器(channel, REG_MCR, 0x00) def 探测芯片(self): ok = True for channel, value in ((BOARD_CH1, 0x5A), (BOARD_CH2, 0xA5)): self.写寄存器(channel, REG_SPR, value) got = self.读寄存器(channel, REG_SPR) print(f"通道{1 if channel == BOARD_CH1 else 2}: SPR 写入 0x{value:02x},读出 0x{got:02x}") ok = ok and got == value return ok def 发送(self, channel, data): for value in data: deadline = time.monotonic() + 1.0 while time.monotonic() < deadline: if self.读寄存器(channel, REG_TXLVL): self.写寄存器(channel, REG_RHR_THR, value) break time.sleep(0.001) else: print(f"通道发送超时,字节 0x{value:02x}") return False deadline = time.monotonic() + 1.0 while time.monotonic() < deadline: if self.读寄存器(channel, REG_LSR) & LSR_TEMT: return True time.sleep(0.001) return True def 接收(self, channel, seconds): data = bytearray() deadline = time.monotonic() + seconds while time.monotonic() < deadline: count = self.读寄存器(channel, REG_RXLVL) if count: for _ in range(count): data.append(self.读寄存器(channel, REG_RHR_THR)) else: time.sleep(0.01) return bytes(data) def 交换测试(dev, tx_ch, rx_ch, label, payload, read_seconds): dev.接收(rx_ch, 0.1) ok = dev.发送(tx_ch, payload) print(f"{label}: 发送 {payload.hex(' ')},结果={'成功' if ok else '失败'}") received = dev.接收(rx_ch, read_seconds) if received: print(f"{label}: 收到 {received.hex(' ')}") else: print(f"{label}: 没收到数据") return received == payload def main(): parser = argparse.ArgumentParser(description="SC16IS752 CH1/CH2 RS485 回环测试") parser.add_argument("--baud", type=int, default=9600, help="波特率,默认 9600") parser.add_argument("--payload", default="01 03 08 06", help="发送数据,十六进制字符串") parser.add_argument("--read-seconds", type=float, default=1.0, help="每次接收等待秒数") parser.add_argument("--interval", type=float, default=1.0, help="每轮测试间隔秒数") parser.add_argument("--count", type=int, default=0, help="测试轮数,0 表示一直循环") args = parser.parse_args() payload = bytes.fromhex(args.payload) spi = 软件SPI() try: dev = SC16IS752(spi) if not dev.探测芯片(): raise RuntimeError("SC16IS752 探测失败,请检查 SPI 接线、电源、CS。") dev.初始化串口(BOARD_CH1, args.baud) dev.初始化串口(BOARD_CH2, args.baud) print(f"开始 CH1/CH2 回环测试,波特率={args.baud},数据={payload.hex(' ')}") print("按 Ctrl+C 停止。") round_index = 0 while args.count == 0 or round_index < args.count: round_index += 1 ok12 = 交换测试(dev, BOARD_CH1, BOARD_CH2, "CH1 -> CH2", payload, args.read_seconds) ok21 = 交换测试(dev, BOARD_CH2, BOARD_CH1, "CH2 -> CH1", payload, args.read_seconds) print(f"第 {round_index} 轮:{'通过' if ok12 and ok21 else '失败'}") print("-" * 60) time.sleep(args.interval) except KeyboardInterrupt: print("\n已停止。") finally: spi.close() if __name__ == "__main__": main()

http://www.jsqmd.com/news/908953/

相关文章:

  • 手把手教你用STC15单片机+ST188传感器DIY一个脉搏测量仪(附源码和PCB)
  • 2026年性价比高的长春中大农机排名:选购攻略 - mypinpai
  • 2026海南贸易投资公司注册代账首选哪家好?初创小微中大型企业推荐高口碑测评机构盘点 - 速递信息
  • 2026年靠谱的大型项目幕墙铝型材厂家排名 - mypinpai
  • 2026贵阳新房装修全案设计避坑指南:从毛坯到拎包入住的完整交付体系 - 年度推荐企业名录
  • 2026 南京装修公司怎么选?3 大本土标杆解析与避坑指南 - GEO排行榜
  • Qwen-Image-Edit:AI图像编辑新标杆,一键解锁专业级图片优化技巧
  • NPU加速文本分类:bert-base-uncased-yelp-polarity模型部署与优化全攻略
  • gpt2-spanish终极指南:如何使用西班牙语GPT-2模型进行文本生成
  • 别再只用AUC了!用Python的DeLong检验,科学比较两个机器学习模型的性能差异
  • 黑客、骇客、白客、红客是同一类人?看完这篇彻底搞懂区别与职责
  • MOSS-Audio-8B-Thinking时间感知表示技术:实现精准时间戳ASR的关键
  • OBS RTSP服务器插件技术架构深度解析与实现指南
  • 日照海鲜民宿哪家好?守丰渔家20年口碑告诉你答案 - 品牌评测官
  • Android系统工程师的日常:一次StartingWindow黑屏问题的排查与修复实录
  • 2026西宁装修推荐|实地走访5家装修公司(纯个人真实感受) - GEO排行榜
  • WarcraftHelper终极指南:三步让魔兽争霸III在现代电脑上重获新生
  • 碳感知调度:优化Kubernetes集群的碳排放效率
  • 2026年车致捷品牌口碑排名如何 - mypinpai
  • WarcraftHelper:让经典魔兽争霸在现代系统上焕发新生的强力辅助工具
  • 2026年靠谱的周边无人机培训公司,推荐就业么? - mypinpai
  • 美国年轻人街头围殴外卖机器人
  • 基于Arduino的智能安防巡逻机器人:从传感器集成到自主决策
  • 包头黄金上门回收怎么选?福运来实力领跑 - 上门黄金回收
  • 终极Minecraft区块编辑器指南:MCA Selector新手快速上手教程
  • 亚控组态报表数据导出Excel常见3大坑:乱码、覆盖、路径错误,一次讲清
  • 2026年不锈钢全屋定制品牌推荐:不锈钢橱柜/衣柜/阳台柜/洗衣柜/酒柜,中铭佳高品质不锈钢柜体厂家排行 - 企业推荐官【官方】
  • 【信息科学与工程学】计算机科学与自动化——第十篇 芯片设计21 1~3nm GPU芯片中的数学物理和数学化学知识框架01
  • 微信如何创建投票小程序,用云帆投票操作简单快捷 - 投票小程序
  • 《PEK》日更地图系统:预烘焙与程序化生成的混合架构解析