微雪树莓派PICO笔记——8-PIO(可编程输入输出接口)
文章目录
- PIO介绍
- 状态机
- PIO指令详解
- PIO语法详解
- 函数详解
- 【MicroPython】PIO有关函数详解
- @asm_pio装饰器
- rp2.StateMachine函数详解
- PIO_ASM
- 例程地址
- 实操
PIO介绍
RP2040的一个特殊的外设
它可以基于RP2040的设备上创建新的或者额外的硬件接口
作用:其本质接近FPGA,也就是现场可编程逻辑门阵列
通过简单的状态机来模拟所以接口,当然前提是满足对应的时钟频率
假设以下场景:
- 场景一
- 你需要使用某些通讯接口,但是现在的外设满足不了你的需求
- 场景二
- 你需要使用某些接口,如单总线协议,VGA,DVI等,但是RP2040没有现存的外设
以上场景中,我们都可以通过PIO自定义创建我们需求的接口,来满足对应的需求
不同于软件模拟,PIO并不会占用内核时间
状态机
因为每个PIO有4个独立的状态机,可以实现超高速且可编程的输入输出
状态机可以理解为一个极简的内核,只能用于处理特定的指令实现实现特定的功能
RP2040有两个PIO块,每个块都有4个状态机和指令寄存器组成
它们可以独立地执行顺序程序来操作GPIO和传输数据
与通用处理器不同,PIO状态机高度专注于IO控制和精确的时钟控制
每个状态机都配备了一个32位PC寄存器,用于指向正在执行的指令地址
两个32位移位寄存器(ISR和OSR)
移位寄存器顾名思义数据能在其中移动
而且此处的移位寄存器可以与对应的先进先出存储器直接传输数据
先进先出存储器都可以英文缩写FIFO代替
两个32位的暂存寄存器,称之为X寄存器和Y寄存器
在接收和传输方向上各有4*32位总线FIFO
- 可重构为8*32位单方向的FIFO,可以通过FIFO可以实现数据的推入或者拉出
一个16位整数,8位小数的小数时钟分频器
- 输入时钟为系统时钟,最低可以将状态机时钟降低到系统时钟的65536分 之一
DMA接口,持续吞吐量高达1字每系统时钟,若系统时钟为100Mhz,每秒最高吞吐量为100M字
IRQ中断表示位,PIO总共有8个中断表示位,可以用于同步状态机或者其他用途
每个状态机都可以进行灵活的GPIO映射,映射方式有四种,分别为
- 输入映射
- 输出映射
- 设置映射
- 侧置映射
详细了解GPIO映射
首先我们需要知道映射寄存器都是32位的
如果让其一位对应着一个GPIO引脚,我们应该用32个GPIO引脚对应
即RP2040只有30个GPIO,我们应当假设其存在32个GPIO,也就是GPIO30和GPIO31,我们也可以戏称之为“消失的引脚”
输入映射
我们可以指定输入GPIO的初始引脚。这个初始引脚在PIO的输入映射中将会被视为引脚0,并将其他引脚按照顺序进行(循环)计数,这里就会遇到“消失的引脚”
- 如果设定GPIO29为初始引脚也就是引脚0,然后依次递增
- 经过GPIO30和GPIO31,引脚3也就是对应GPIO0
输出映射和输入映射接近,但需要设置输出引脚的数量,最高为32
设置和侧置映射与输出映射工作方式相同,但最多映射5个IO,设置和侧置映射引脚是允许重叠的
PIO指令详解
状态机类似于极简的内核,只允许很简单的二进制程序
其采用了接近机器语言的PIO汇编进行编写,其支持9种指令分别为:
- JMP
- WAIT
- IN
- OUT
- PUSH
- PULL
- MOV
- IRQ
- SET
我们编写的PIO ASM指令会被编译器编译成对应的指令再交给PIO的状态机运行
PIO指令编码表
首先我们可以看到一条指令的长度为16位,高3位用于标识这条指令功能或者作用
12-8位则用于标识延迟或者侧置,7-0位则会根据指令的不同有不同的功能
JMP指令的作用是将达到某些条件的情况下将程序跳转到指定地址
- JMP指令有两个参数,分别是condition和 target
- target:允许数值0-31,因为 PIO只有32条指令空间
- condition:
- !X OR !Y: X,Y寄存器为0
- X-- OR Y–: X,Y寄存器减1大于0
- X != Y: X不等于Y
- PIN: 输入引脚为高电平
- !(OSER):输入移位寄存器OSR为0
- JMP指令有两个参数,分别是condition和 target
当程序达成时,程序就会根据target跳转指定地址
当然可以不填写条件,则会无条件跳转到指定地址
WAIT指令的作用为在条件达成前等待
- WAIT指令有三个参数,分别为Polarity极性 ,source源和Index指针
- 极性是指等待目标出现0或者1
- 源指对应等待的目标类型,可以为绝对GPIO,引脚映射后PIN和中断标志IRQ
- 指针对应等待的目标编码
IN指针指令的作用将数据存入ISR寄存器中
- IN指令有两个参数为Source源, Bitcount位数
- 源指对应读取数据的源类型
- 位数对应着读取数据的位数
OUT指令和IN指令功能相反,指令作用是将OSR输出到目标
OUT指令有两个参数destination目标和Bitcount位数
目标对应着数据输出的目标
位数对应着输出位数
PUSH指令作用为将ISR内容推送到RX FIFO中并清空ISR
有两个参数 ,为满 和 阻塞
若为满为1,则ISR达到阈值,才可以进行推送
若阻塞为1且RX FIFO达到阈值,则会将数据推送到ISR寄存器中
否则会等待RX FIFO达到阈值,若为0则RX FIFO达不到阈值时,则不会进行推送
-PULL指令的作用将TX FIFO数据读到OSR寄存器
有两个参数为 空 和 阻塞
若为空为1,则只有TXFIFO达到阈值,OSR才会接收TXFIFO的数据
其中阻塞和PUSH的阻塞一致
MOV指令的作用是将数据从源移动到目标寄存器中
- 有三个参数 目标destination,源sourcr和操作Operation
- 目标将数据写入的寄存器,可以为一下几种!需要注意的是,EXEC解码寄存器,其作用为从外部读取一条指令并执行,理论上是可以执行外部的PIO代码的
- 源为数据来源,可以为一下几种
操作有三种
-IRQ指令的作用为设置或者清空中断标识
有3个参数: 选项 , 中断标志位 和 REL
选项有一下几种
中断标识可以为0-7
若REL存在则中断标识irq_num和状态机编码sm_num进行模4加法运算
若状态机2设置中断标识OX11,最后的中断值则为0X03
SET指令会将数据写入目标地址,通常用于控制设置映射引脚
参数 destination 目标地址
data 数据为写入的数据,这里只能为绝对值
PIO语法详解
以上就是PIO ASM的9个指令,但是如果你看过PIO ASM的程序,你会发现指令后面还会跟着一些小尾巴
比如说方括号带着一个数字,这代表延迟
在正常程序中我们的每一条指令执行所需要时间为1个时钟周期
但往往我们需要等待或者不需要这么快的速度,这时候我们就需要进行这个延迟
现在有两种方法
使用一条无意义指令,这个指令将y寄存器的数值赋给y寄存器,这是一条没有意义的指令,但是他会占用状态机的一个时钟周期,还有个缺点,他会占用PIO的 指令空间,因为状态机的指令空间只能容纳32条指令,所以说不推荐这种用法
使用PIO ASM中的延迟特性
这条指令会将PINS设置为1,之后延迟2个时钟周期,这就是PIO汇编的延迟特性当然延迟时间也是有特性的,最大的延迟时钟为31个时钟周期
侧置Side-set
我们可以在执行任何指令的同时改变被设置为侧置映射引脚,最多5个引脚
需要注意的是,侧置和延迟是共享指令的12-8位,所以说侧置和延迟是无法完美兼容的,但是可以根据你的需求进行设置
延迟的最大数值为31,换算成2进制也就是5位,也就是我们无法通过编码区分延迟和侧置
需要我们需要在程序里注明使用侧置位数
例程
.program spi_tx_fast
.side_set 1loop:
out pins, 1 side 0
jmp loop side 1
该程序会模拟SPI发送数据的过程
其会不断地将OSR寄存器中的数值(依次)通过pins对应的引脚输出
与此同时,侧置引脚会不断地进行翻转
实现了两个时钟周期输出一位数据并输出时钟信号,使我们的程序更小更快
使用侧置功能时,我们还需要声明使用的侧置位数
这里我们使用一位侧置位数,他会占用侧置/延迟字段的高一位
意味着我们的延迟只能占用4位,也就是其数值范围为0-15
正常情况下,程序会从地址为0的指令开始运行,运行到地址为31的指令运行完成后,再从地址0的指令重复开始运行
下面这段程序将会将一个引脚设置为输出并循环输出占空比为50%且周期为4个时钟周期的方波
该程序的PC指针不会在0-31之间往复,所以说PIO引入了程序包装
通过程序包装可以告诉我们的状态机,从哪里结束并且从哪里重新开始
.program squarewave
set pindirs, 1 ; Set pin to output
again:set pins,1 [1]; Drive pin high and delay for one cycleset pins, 0 ; Drive pin lowjmp again ; Set PC to label 'again'
例程
当程序运行到.wrap 时,程序将会返回到.wrap_target并运行
与上一个程序的区别就是不需要jmp指令进行跳转,可以节省一个指令
set pindirs, 1 ; Set pin to output
.wrap_targetset pins, 1 [1] ; Drive pin high and delay for one cycleset pins, 0 [1] ; Drive pin low and delay for one cycle
.wrap
以上大致就是PIO的内容,虽然还有些小细节,但是不妨碍我们使用
函数详解
【MicroPython】PIO有关函数详解
MicroPython引入了一个新的@rp2.Asm_pio装饰器和rp2.PIO类.
- PIO程序的定义和状态机的配置分为2个逻辑部分:
- 程序定义,包括使用了多少引脚,如果它们是in/out引脚。这在@rp2.asm_pio中定义。
- 程序,设置状态机的频率和绑定到哪个引脚。当设置一个状态机来运行特定的程序时,就会设置这些参数。
所有的程序配置(例如autopull)都是在@asm_pio装饰器中完成的,在状态机的构造函数中只需要设置频率和基础引脚。 只设置频率和基础引脚需要在StateMachine构造函数中设置。
@asm_pio装饰器
让其它函数不需要任何代码上的改动,增加额外的功能
@asm_pio(out_init=None,set_init=None,sideset_init=None,in_shiftdir=0,out_shiftdir=0,autopush=False,autopull=False,push_thresh=32,pull_thresh=32,fifo_join=0
)
out_init :输出引脚初始化
set_init :设置引脚初始化
sideset_init :侧置引脚初始化
前三个引脚可以设置为rp2.PIN.OUT_HIGH 输出高电平
PIN.OUT_LOW 输出低电平
需要注意的是MicroPython 是通过侧置引脚初始化来计算使用了多少个侧置引脚,并决定测置和延迟字段的结构
in_shiftdir : 数据输入方向
out_shiftdir:数据输出方向
以上决定了数据移动的方向
如输出情况下,数据向左移动,则OSR的高位先进行输出,若向右移动,则低位先进行输出
autopush :自动推送,若开启,当ISR达到阈值,自动将ISR传输到RX-FIFO。
autopull :自动拉取,若开启,当OSR达到阈值,自动将TX-FIFO传输到OSR。
push_thresh :推送阈值
pull_thresh :拉取阈值
字面意思,结合自动推送和自动拉取使用
fifo_join :fifo组合,指定一个FIFO,将另一个FIFO关闭并加入改FIFO,获取更深位数的FIFO.
如果我们指定TXFIFO,我们就会获得一个8位深度的TXFIFO(32bit*8)
rp2.StateMachine函数详解
MicroPython 使用PIO是以状态机为单位的,所以我们的函数都是围绕状态机的
rp2.StateMachine.init(sm_id ,program, freq=-1, *, in_base=None, out_base=None, set_base=None, jmp_pin=None, sideset_base=None, in_shiftdir=None, out_shiftdir=None, push_thresh=None, pull_thresh=None)
- sm_id:使用状态机ID,0-3为PIO0,4-7为PIO1
- program:PIO运行程序(状态机运行程序)
- freq:状态机运行频率,默认为系统时钟频率,
- 时钟分频器的分配因子计算公式为“系统时钟频率/频率”,所以 可能存在轻微的舍入误差。
- 最小可能的时钟分频器是系统时钟的 65536 分之一:所以在默认系统时钟频率 125MHz下,最小值为1908。
- 要以较慢的频率运行状态机,需要使用“machine.freq()”降低系统时钟速度。
- in_base:用于in()指令的第一个引脚
- out_base:用于out()指令的第一个引脚
- set_base:用于set()指令的第一个引脚
- jmp_pin:用于jmp(pin, …)指令的第一个引脚
- sideset_base:是用于侧置的第一个引脚。
- in_shiftdir:ISR将移动的方向,可为PIO.SHIFT_LEFT或者PIO.SHIFT_RIGHT
- out_shiftdir: OSR 将移动的方向,可为PIO.SHIFT_LEFT或者PIO.SHIFT_RIGHT
- push_thresh:推送阈值
- pull_thresh:拉取阈值
StateMachine.active([value])
- 获取或设置状态机当前是否正在运行。
- 当value不为空时,设置状态机,反之获取运行状态。
StateMachine.restart()
- 重新启动状态机并跳转到程序的开头。
StateMachine.exec(instr)
- 执行单个 PIO 指令。使用
asm_pio_encode
编码 来自给定指令字符串 instr 的指令。
- 执行单个 PIO 指令。使用
StateMachine.get(buf=None, shift=0)
- 从状态机的RX-FIFO中提取一个字。
- 如果FIFO为空,它会阻塞直到数据到达(即状态机推一个字)。
- shift为在返回之前右移位数
- 返回值是“word >> shift”
StateMachine.put(value, shift=0)
- 将一个字推送到状态机的 TX FIFO。
- 如果 FIFO已满,它将阻塞直到有空间(即状态机拉一个字)。
- shift为在返回之前右移位数
- 返回值是“word >> shift”
StateMachine.rx_fifo()
- 返回状态机的 RX FIFO 中的字数。值为0表示 FIFO 为空。
- 用于在调用之前检查数据是否正在等待读取
StateMachine.get()
。
StateMachine.tx_fifo()
- 返回状态机的 RX FIFO 中的字数。值为0表示 FIFO 为空。
- 用于在调用之前检查数据是否正在等待读取
StateMachine.put()
。
StateMachine.irq(handler=None, trigger=0|1, hard=False)
- 返回给定 StateMachine 的 IRQ 对象。
PIO_ASM
JMP (condition) target
- target :跳转地址,允许数值0-31,因为PIO只有32条指令空间。
- condition :
- !X OR !Y:
- X– OR Y–
- X!=Y :
- PIN :
- !(OSRE):
- JMP指令作用是将达到某些条件的情况下将程序跳转到指定地址.
- 当条件达成时,程序就会根据target跳转指定地址
- 当然也可以不填写条件,则会无条件跳转指定地址。
WAIT Polarity Source Index
- Polarity: 等待 0 OR 1
- Source:
- GPIO:绝对GPIO
- PIN:引脚映射后的引脚
- IRQ:中断标志
- Index:
- GPIO_num:对应GPIO源,GPIO数值
- pin_num:对应pin源,pin数值
- IRQ_num:对应IRQ源,指定等待的引脚或者位,这里IRQ_NUM也是支持使用(_rel)
- WAIT指令作用为在条件达成前等待
IN Source,Bitcount
- Source :
- PINS
- X
- Y
- NULL
- ISR
- OSR
- Bitcount :读取位数
- IN指令作用将数据存入ISR寄存器
- Source :
OUT destination,Bitcount
- destination :
- PINS
- X
- Y
- NULL
- PINDIRS
- PC
- ISR
- OSR
- Bitcount :读取位数
- OUT和IN功能相反,指令作用将OSR寄存器输出到目标
- destination :
PUSH (IfFull) (Block/noBloc k)
- IfFull,若为1则只有ISR到达阈值,才能推送.
- Block,若为1且RX FIFO达到阈值,就会进行将数据推送到ISR中,否则会等待RXFIO达到阈值,若为0则RXFIFO达不到阈值,则不会进行推送。
- PUSH指令的作用为将ISR中内容推送到RX FIFO和清空ISR
PULL (IfEmpty) (Block/noBloc k)
- IfEmpty:若为1则只有OSR才会接收TXFIFO的数据。
- Block:1则TXFIFO为空则等待TXFIFO.
- PUSH指令的作用将从TXFIFO数据读出到OSR寄存器。
MOV destinationmov,(Operation ),source
- destination:
- PINS:输出映射
- X:
- Y:
- EXEC:解码寄存器
- PC:PC寄存器(JUM)
- ISR
- EXEC:
- source:
- PINS:
- X
- Y
- NULL:空,用于清零
- STATUS:表示不同状态,如fifo满或者空。
- ISR:
- OSR:
- Operation:
- 00:不改变
- 01:位取反
- 10:位翻转,高低位互换。
- MOV 指令其作用为将数据从源移动目标寄存器。
- destination:
IRQ (option) irq_num (_rel)
- irq_num:0-7 中断标志位
- option
- set(默认):设置
- nowait(默认):不等待清除
- wait:等待清除后,运行
- clear:清除
- _rel:若存在则将irq_num和状态机编码sm_num相加并进行模四运行并将高位置1,若状态机2设置中断标识3,中断值则为0X11
- irq_flag=(sm_id+irq_num) %4 + 0x10
- IRQ指令作用为设置或者清空中断标识
SET destination,data
- destination:目标地址
- PINS :SET映射引脚
- PINDIRS :引脚方向,将第一个映射GPIO1为输出,0为输入
- X : 暂存寄存器,暂存数据
- Y : 暂存寄存器,暂存数据
- data :数据, 0-31
- set指令会将数据写入目标地址.通常用于控制设置映射引脚。
- destination:目标地址
例程地址
- Github仓库
MicroPython源码
实操
原理图
Pico 的GPIO4连接到WS2812B的数据输入引脚
WS2812B是一款单总线驱动的控制电路与发光电路于一体的智能外控LED光源
- 可采用单线输出方式,串接LED使之输出动作同步;
- 数据协议采用单极性归零码
- 因为内部基础控制电路,显示更趋细腻平滑,解决拍摄画面暗条纹问题
- 数据发送速度可达800KbpsWS2812的通讯协议
该协议与之前的协议不太一样,其是通过电平的时长来判断数据为0还是1
如果我们需要发送数据0,需要先拉高电平220ns - 380 ns 紧接着拉低电平580ns-1us
数据1也是相同的原理,
ws2812传输数据结构如表格,且ws2812的数据发送是先进行高位发送,也就是数据是向左移动的
# 先导入需要的库
import time
from machine import Pin
import rp2# WS2812的PIO驱动程序
# 通过asm_pio装饰器初始化pio
# 初始化了侧置引脚,因为这里只有一个初始化信息,说明侧置引脚的数量为1
# 数据输出移动方向为由右向做移动
# 开启自动拉取,当OSR达到阈值,自动将TXFIFO中的数据拉取到OSR中
# 拉取阈值为24位,也就是发送数据的长度
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, Pull_thresh=24)def ws2812():T1 = 2T2 = 5T3 = 3# wrap_target()和wrap()进行了程序包装,让程序在两者间运行wrap_target()label("bitloop")out(x, 1) .side(0) [T3 - 1] # 将OSR的数据读取1位取到X寄存器中,并将侧置引脚设置为0,延迟等待2时钟周期jmp(not_x, "do_zero") .side(1) [T1 - 1] # 如果X中的数值为0则跳转标签“do_zero",反之则顺序执行下一条指令,同时将侧置引脚设置为1,延迟等待1时钟周期jmp("bitloop") .side(1) [T2 - 1] # 如果顺序运行则会跳转标签"bitloop"并同时将侧置引脚设置为1,延迟等待4时钟周期,用于表示数据1label("do_zero") # 如果跳转到标签do_zero,执行一条无意义的指令并将侧置引脚设置为0nop() .side(0) [T2 - 1] # 延迟等待4时钟周期,用于表示数据0,随后返回wrap()wrap()# 主程序
# 创建我们的状态机0使用ws2812程序,时钟频率为8Mhz,基础侧置引脚为4
# 打开状态机
sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(4))
sm.ative(1)# 在循环中,我们不断地改变RGB数值,并通过是sm.put()函数将数组传输给TXFIFO
# TXFIFO会自动的被状态机拉取到OSR寄存器中
# 并指向WS2812驱动程序实现不同颜色的呼吸灯
while True:for i in range(0,max_lum):r = ib = max_lum-irgb=(g<<24) | (r<<16) | (b<<8)sm.put(rgb)time.sleep_ms(10)time.sleep_ms(300)for i in range(0,max_lum):g = ir = max_lum-irgb=(g<<24) | (r<<16) | (b<<8)sm.put(rgb)time.sleep_ms(10)time.sleep_ms(300)for i in range(0,max_lum):b = ig = max_lum-irgb=(g<<24) | (r<<16) | (b<<8)sm.put(rgb)time.sleep_ms(10)time.sleep_ms(300)
微雪PICO教程
微雪树莓派PICO笔记——8-PIO(可编程输入输出接口)相关推荐
- 微雪树莓派PICO笔记——1.基础介绍
文章目录 基础硬件 原理图讲解 电路图重点讲解 点灯实操 基础硬件 长度51mm,宽度21mm,板厚1mm pcb使用了沉金工艺,所以引脚都做了半孔设计,即可焊接2.54mm的排针,也可以直接焊在主板 ...
- 微雪树莓派PICO笔记——3.PWM(脉冲宽度调制)
文章目录 什么是PWM PWM的应用 RP2040 PWM框架图 PWM流程图 PWM内部框架图 [MicroPython]machine.PWM类函数详解 代码实现 什么是PWM 脉冲宽度调制 (P ...
- 微雪树莓派PICO笔记——7. SPI(串行外设接口)
文章目录 SPI简介 硬件连接 通讯协议详解 RP2040 SPI 主要参数 RP2040 SPI 逻辑框图 machine.SPI类函数详解 例程地址 代码示例 代码实现 SPI简介 SPI全称为串 ...
- 微雪树莓派PICO笔记——4. ADC(模拟数字转换器)
文章目录 什么是ADC RP2040 ADC技术参数 ADC大致框架图 [MicroPython]machine.ADC类函数详解 代码实现 如果我们需要使用PWM精准的控制LED的亮度,就需要反馈 ...
- 微雪树莓派PICO笔记——5. UART (异步收发传输器)
文章目录 通讯协议 UART UART详解 RP2040 UART参数 UART流程图 函数详解 例程地址 码代码 通讯协议 MCU如果要说话需要约定一定的规则,这些规则,我们称为通信协议 常见的有U ...
- 微雪树莓派PICO笔记——6. I2C(集成电路总线)
文章目录 简介 协议详解 RP2040 I2C主要参数 函数讲解 内存操作 软件I2C 例程地址 实操 程序讲解 简介 I2C 集成电路总线,一种串行通信总线,使用多主从架构 由飞利浦公司在20世纪8 ...
- 树莓派Pico直流步进电机接口技术及电机运动控制MicroPython+pioasm编程方法
内容目录 一.树莓派Pico直流步进电机接口技术 1.直流步进电机及其驱动电路原理介绍 2.Pico开发板扩展GPIO口与步进电机驱动接口 二.树莓派Pico电机运动控制MicroPython+pio ...
- 树莓派Pico迷你开发板MicroPython多线程编程实践
内容目录: 一.多线程基本知识 二.MicroPython/Python低层多线程API介绍 三.树莓派Pico 开发板MicroPython多线程编程实践举例 3.1 Pico RP2040 MCU ...
- [树莓派]PICO基础使用_微雪OLED显示
简介 Raspberry Pi Pico是具有灵活数字接口的低成本,高性能微控制器板.它集成了Raspberry Pi自己的RP2040微控制器芯片,运行速度高达133 MHz的双核Arm Corte ...
- 首款微控制器级树莓派 Pico,超廉价只需4美元
2021年1月21日,树莓派基金会发布了首款微控制器级产品:Raspberry Pi Pico. 该产品基于全新的 RP2040 芯片构建,售价仅 4 美元,国内标准售价 29.99 元人民币,目前在 ...
最新文章
- exception in initAndListen: 12596 old lock file, terminating
- Java谜题:等于,还是不等于?
- C#动态生成XML并在前台用javascript读取
- 【转】C#自定义控件:WinForm将其它应用程序窗体嵌入自己内部
- 切割日志 python版
- jvm类加载过程_详解JVM类加载
- python读取word文档结构图_python根据文章标题内容自动生成摘分享的实例
- 快手作者视频如何批量下载
- 将bilibili里面的缓存视频保存到电脑
- 女孩,请把第一胎留给丈夫!!!
- LeetCode 3:Longest Substring Without Repeating Charact
- 软件测试-按开发阶段划分
- 全新文案馆头像壁纸小程序源码+带后台的
- 硬盘IDE、SATA、AHCI模式的区别
- Eclipse delete键不能向后删除
- python拼图游戏代码的理解_Python编写的数字拼图游戏(含爬山算法人机对战功能)...
- ASO苹果搜索广告审核不通过的原因
- ssh提交 hadoop集群
- Python-shogun安装问题
- Java新AIO/NIO2:AsynchronousFileChannel以Future方式读