当前位置:网站首页>OpenOCD-JTAG调试
OpenOCD-JTAG调试
2020-11-09 16:56:00 【whoisliang】
目录
title: OpenOCD-JTAG调试
tags: ARM
date: 2018-10-13 23:36:28
---
Todo
- [ ] JTAG 调试linux内核
- [ ] linux下使用OpenOCD调试
- [x] win下使用OpenOCD调试
概述
-
学习文档 韦东山 Eclipse,OpenOCD,OpenJTAGv3.1嵌入式开发教程版本5.pdf
-
硬件连接: PC>JTAG调试器>CPU
-
软件控制:IDE(KEIL/ADS/)> GDB(指令)> OpenOCD(实际命令)> JTAG调试器> 单板
- JTAG控制CPU功能:
- 当CPU的地址信号ADDR=xxx,停止CPU-硬件断点
- 当CPU的数据信号DATA=xxx,停止CPU--软件断点
- 重新运行CPU
- 读取R0,..寄存器
- 控制外设,内存
-
百问网的
OpenJTAG.exe
这个GUI实际是封装了openocd.exe
命令行- 设置Workdir到程序代码目录
- 点击
telnet
,或者直接cmd输入telnet 127.0.0.1 4444
- 使用help查看帮助或者查看`Eclipse,OpenOCD,OpenJTAGv3.1嵌入式开发教程版本5.pdf
断点
-
硬件断点:一个程序只能打两个断点(ARM7),可以调试ROM,NOR
设置CPU里面的JTAG比较器,使硬件断点 Addr=A,当CPU发出A地址时停止
CPU是如何运行?CPU需要取指令,也就是需要发出地址信号去取得指令,JTAG就检测到这个地址
-
软件断点,可以有无数个软件断点.前提是断点的地址可写,所以无法调试NOR,或者ROM上的程序,具体可以看下面的led.c的示例
- 设置CPU里面的JTAG比较强,使其值=一个特殊值
- 替换掉希望断点位置(A)的值=这个特殊值,做好备份
- 当CPU读取到这个特殊值,程序断点
- 重新运行时,恢复这个(A)位置的指令
快速使用
常用命令
-
halt 停止cpu
-
reg 查看寄存器
-
mdw 0 //memory display word 查看内存
-
mww 0 0x12345678 //memory write word
-
load_image leds.bin 0 //下载程序到0地址,然后可以使用mdw读取0,看看是不是程序的bin
-
resume 0 //指定地址运行,如果不指定地址,则恢复运行
-
reset 复位目标板子
-
reset halt
-
-
step 0 //执行第一句话,并halt
-
step //单步执行
-
-
bp设置断点
-
bp 0x6c 4 hw 在0x6c的地址位置设置断点,硬件断点
-
rpb 0x6c 取消断点
测试led的断点
-
//led.c
-
void wait(volatile unsigned long dly)
-
{
-
for(; dly > 0; dly--);
-
}
-
-
int main(void)
-
{
-
unsigned long i = 0;
-
-
GPFCON = GPF4_out|GPF5_out|GPF6_out;
-
-
while(1){
-
wait(30000);-------------------尝试在这里断点,就能观察灯的变化
-
GPFDAT = (~(i<<4));
-
if(++i == 8)
-
i = 0;
-
}
-
-
return 0;
-
}
-
-
-
//反汇编摘要
-
00000044 <main>:
-
44: e52de004 str lr, [sp, #-4]!
-
48: e24dd004 sub sp, sp, #4 ; 0x4
-
4c: e3a03000 mov r3, #0 ; 0x0
-
50: e58d3000 str r3, [sp]
-
54: e3a03456 mov r3, #1442840576 ; 0x56000000
-
58: e2833050 add r3, r3, #80 ; 0x50
-
5c: e3a02c15 mov r2, #5376 ; 0x1500
-
60: e5832000 str r2, [r3]
-
64: e3a00c75 mov r0, #29952 ; 0x7500
-
68: e2800030 add r0, r0, #48 ; 0x30
-
6c: ebffffe9 bl 18 <wait>---------------------------尝试在这里断点
-
70: e3a02456 mov r2, #1442840576 ; 0x56000000
-
74: e2822054 add r2, r2, #84 ; 0x54
-
78: e59d3000 ldr r3, [sp]
-
7c: e1a03203 mov r3, r3, lsl #4
-
80: e1e03003 mvn r3, r3
-
84: e5823000 str r3, [r2]
-
88: e59d3000 ldr r3, [sp]
-
8c: e2833001 add r3, r3, #1 ; 0x1
-
90: e58d3000 str r3, [sp]
-
94: e3530008 cmp r3, #8 ; 0x8
-
98: 1afffff1 bne 64 <main+0x20>
-
9c: e3a03000 mov r3, #0 ; 0x0
-
a0: e58d3000 str r3, [sp]
-
a4: eaffffee b 64 <main+0x20>
-
Disassembly of section .debug_line:
使用openocd命令来调试断点
-
halt
-
load_image leds.bin 0 //下载程序
-
resume 0 //指定地址运行,如果不指定地址,则恢复运行
-
halt
-
bp 0x6c 4 hw //在0x6c的地址位置设置断点,硬件断点
-
-
resume //反复这个断点,就能观察灯的变化了
测试软件断点是否改变ram值
-
//读取原来的值
-
> mdw 0x6c
-
0x0000006c: ebffffe9
-
//设置软件断点
-
> bp 0x6c 4
-
breakpoint set at 0x0000006c
-
//读回这个ram值,发现改变了
-
> mdw 0x6c
-
0x0000006c: deeedeee
-
//删除这个断点
-
> rbp 0x6c
-
//读回来
-
> mdw 0x6c
-
0x0000006c: ebffffe9
NAND调试(进阶)
程序概述:
- 链接地址在
0x30000,0000
,运行的时候应该位于0x3000,0000
- 直接烧写程序到内存中,也就是程序运行在0地址.并没有在加载地址
- 程序功能:main中点灯,但是直接烧录到内部ram,跑飞了
- 程序的bug在于,所有的代码都应该是位置无关的否则需要搬运代码
-
.text
-
.global _start
-
_start:
-
@函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
-
ldr sp, =4096 @设置堆栈
-
bl disable_watch_dog @关WATCH DOG
-
bl memsetup @初始化SDRAM
-
bl nand_init @初始化NAND Flash
-
-
@将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中
-
@nand_read_ll函数需要3个参数:
-
ldr r0, =0x30000000 @1. 目标地址=0x30000000,这是SDRAM的起始地址
-
mov r1, #0 @2. 源地址 = 0
-
mov r2, #4096 @3. 复制长度= 2048(bytes),
-
bl nand_read @调用C函数nand_read
-
-
ldr sp, =0x34000000 @设置栈
-
ldr lr, =halt_loop @设置返回地址
-
ldr pc, =main @b指令和bl指令只能前后跳转32M的范围
-
@,所以这里使用向pc赋值的方法进行跳转
-
halt_loop:
-
b halt_loop
bug原因:mem_cfg_val是在栈,是位置无关的,但是他的初始值是去在链接地址取的,也就是0x3000000
后面
-
void memsetup()
-
{
-
unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON
-
0x00000700, //BANKCON0
-
0x00000700, //BANKCON1
-
0x00000700, //BANKCON2
-
0x00000700, //BANKCON3
-
0x00000700, //BANKCON4
-
0x00000700, //BANKCON5
-
0x00018005, //BANKCON6
-
0x00018005, //BANKCON7
-
0x008C07A3, //REFRESH
-
0x000000B1, //BANKSIZE
-
0x00000030, //MRSRB6
-
0x00000030, //MRSRB7
-
};
-
-
}
调试开始
-
>reset halt
-
> load_image nand.bin 0
-
1520 bytes written at address 0x00000000
-
downloaded 1520 bytes in 0.063003s (23.560 KiB/s)
执行第一句话step 0
也就是执行mov sp, #4096 ; 0x1000
,可以使用reg
看到sp=0x1000
step
执行跳转,可以发现pc=0x38,对应汇编
-
30000000 <_start>:
-
30000000: e3a0da01 mov sp, #4096 ; 0x1000
-
30000004: eb00000b bl 30000038 <disable_watch_dog>
然后一步一步step
,并使用poll
查看当前pc值等,使用mdw
查看该设置的内存
跳转到memsetup
,pc=0x08,反汇编先入栈,可以发现sp=4096-5*4=4076=0xFEC
可以使用 step 0,然后设置 bp 0x50 4 hw 断点 直接跳到想到的位置
然后在汇编代码,发现问题了
-
// ip=300005bc
-
30000050: e1a0400c mov r4, ip
-
30000054: e8b4000f ldmia r4!, {r0, r1, r2, r3}
-
//从r4中指向的内存给r0~r3
-
-
//也就是说从 300005bc读取一些数据,但是这个时候300005bc(sdram)并没有被初始化且进行代码搬运,所以这里的数据肯定有问题
-
-
//这里的其实就是那个局部数组的值 mem_cfg_val
-
300005bc <.rodata>:
-
300005bc: 22011110 andcs r1, r1, #4 ; 0x4
-
300005c0: 00000700 andeq r0, r0, r0, lsl #14
-
300005c4: 00000700 andeq r0, r0, r0, lsl #14
-
300005c8: 00000700 andeq r0, r0, r0, lsl #14
-
300005cc: 00000700 andeq r0, r0, r0, lsl #14
-
300005d0: 00000700 andeq r0, r0, r0, lsl #14
-
300005d4: 00000700 andeq r0, r0, r0, lsl #14
-
300005d8: 00018005 andeq r8, r1, r5
-
300005dc: 00018005 andeq r8, r1, r5
-
300005e0: 008c07a3 addeq r0, ip, r3, lsr #15
-
300005e4: 000000b1 streqh r0, [r0], -r1
-
300005e8: 00000030 andeq r0, r0, r0, lsr r0
-
300005ec: 00000030 andeq r0, r0, r0, lsr r0
-
Disassembly of section .comment:
OpenOCD
全称是(Open On-Chip Debugger)
使用前都需要打开OpenOCD,连接上开发板,然后打开telent,或者使用命令telnet 127.0.0.1 4444
启动OpenOCD
专家模式:对应比较自由高级的配置,我们一般直接用普通模式即可
- Interface
对应
OpenOCD\0.4.0\interface`中的选项 Target
对应OpenOCD\0.4.0\ target
和OpenOCD\0.4.0\board
启用telnet
Win7默认没有打开这个功能,需要在程序和功能>打开或关闭 windows 功能> TelentClient
打开
OpenOCD命令
这些命令都在telnet中运行,官方命令索引在这里,PDF文档OpenOCD User's Guide.pdf
-
记住的命令
-
reset halt
-
resume
-
step
-
load_image
-
-
目标板状态处理命令(Target state handling)
-
poll 查询目标板当前状态
-
halt 中断目标板的运行
-
resume [address] 恢复目标板的运行,如果指定了 address,则从 address 处开始运行
-
step [address] 单步执行,如果指定了 address,则从 address 处开始执行一条指令
-
reset 复位目标板
-
-
断点命令
-
bp <addr> <length> [hw] 在地址 addr 处设置断点,指令长度为 length, hw 表示硬件断点
-
rbp <addr> 删除地址 addr 处的断点 内存访问指令(Memory access commands)
-
-
内存访问指令(Memory access commands)
-
mdw ['phys'] <addr> [count] 显示从(物理)地址 addr 开始的 count(缺省是 1)个字(4 字节)
-
mdh ['phys'] <addr> [count] 显示从(物理)地址 addr 开始的 count(缺省是 1)个半字(2 字节)
-
mdb ['phys'] <addr> [count] 显示从(物理)地址 addr 开始的 count(缺省是 1)个字节
-
mww ['phys'] <addr> <value> 向(物理)地址 addr 写入一个字,值为 value
-
mwh ['phys'] <addr> <value> 向(物理)地址 addr 写入一个半字,值为 value
-
mwb ['phys'] <addr> <value> 向(物理)地址 addr 写入一个字节,值为 value
-
-
内存装载命令,注意:下载程序之前先使用“ halt” 命令暂停单板,才能下载代码;如果使用“ poll” 命令发现单板的 MMU 或 D-cache 已经使能,则需要使用“ arm920t cp15 2 0” 、“ step”两条命令禁止 MMU 和 D-cache。
-
load_image <file> <address> [‘bin’|‘ihex’|‘elf’]
-
======将文件<file>载入地址为 address 的内存,格式有‘bin’、 ‘ihex’、 ‘elf’
-
dump_image <file> <address> <size>
-
======将内存从地址 address 开始的 size 字节数据读出,保存到文件<file>中
-
verify_image <file> <address> [‘bin’|‘ihex’|‘elf’]
-
======将文件<file>与内存 address 开始的数据进行比较,格式有‘bin’、 ‘ihex’、 ‘elf’
-
-
CPU 架构相关命令(Architecture Specific Commands)
-
reg 打印寄存器的值
-
arm7_9 fast_memory_access ['enable'|'disable']
-
=======使能或禁止“快速的内存访问”
-
arm mcr cpnum op1 CRn op2 CRm value 修改协处理器的寄存器
-
=======比如: arm mcr 15 0 1 0 0 0 关闭 MMU
-
arm mrc cpnum op1 CRn op2 CRm 读出协处理器的寄存器
-
=======比如: arm mcr 15 0 1 0 0 读出 cp15 协处理器的寄存器 1
-
arm920t cp15 regnum [value] 修改或读取 cp15 协处理器的寄存器
-
=======比如 arm920t cp15 2 0 关闭 MMU
-
-
其他命令
script <file> 执行 file 文件中的命令
OpenOCD烧录程序
-
load_image <file> <address> [‘bin’|‘ihex’|‘elf’]
-
======将文件<file>载入地址为 address 的内存,格式有‘bin’、 ‘ihex’、 ‘elf’
-
====load_image led.bin 0
SDRAM初始化
OpenOCD可以快速读写SDRAM,前提是需要程序先初始化好SDRAM
,这样之后可以烧录u-boot到sdram,然后通过u-boot烧录其他大程序.这里需要一段初始化sdram的位置无关的代码init.bin
-
//从 Nand Flash 启动
-
load_image init/init.bin 0x0
-
resume 0x0
-
//NOR 启动
-
load_image init/init.bin 0x40000000
-
resume 0x40000000
烧录u-boot,之后打开串口,就能看到跑起来了
-
halt
-
load_image u-boot/u-boot.bin 0x33f80000
-
resume 0x33f80000
uboot烧录其他程序(led/uboot)
- u-boot 的命令把所有的数字当作 16 进制,所以不管是否在数字前加前缀“ 0x”,这个数
字都是 16 进制的。 - 擦除 Flash 的长度、烧写的数据长度,这些数值都是根据要烧写的
文件的长度确定的。 u-boot.bin 的长度是 178704 字节,即 0x2BA10 字节,使用的长
度都是 0x30000,一是为了与 Flash 的可擦除长度相配(16K 的整数倍),二是方便。
GDB
- linux下 arm-linux-gdb,win下arm-elf-gdb
-
arm-elf-gdb nand_elf
-
target remote 127.0.0.1:3333
-
load
GDB命令
启动/退出 | |
---|---|
gdb [FILE] arm-elf-gdb [FILE] arm-linux-gdb [FILE] | 启动 gdb,调试 FILE(也可以先不指定文件) |
quit | 退出 gdb |
target remote ip:port | 远程连接 |
文件操作 | |
file | 载入文件 FILE,注意:不会下载到单板上 |
load [FILE] | 把文件下载到单板上,如果不指定 FILE,则下载之前指定 过的(比如 file 命令指定的,或是 gdb 运行时指定的文件) |
查看源程序 | |
list | 列出某个函数 |
list | 以当前源文件的某行为中间显示一段源程序 |
list | 接着前一次继续显示 |
break * | 在某个地址上设置断点,比如 break *0x84 |
list - | 显示前一次之前的源程序 |
list list | 显示指定文件的一段程序 |
info source | 查看当前源程序 |
info stack | 查看堆栈信息 |
info args | 查看当前的参数 |
断点操作 | |
break | 在函数入口设置断点 |
break | 在当前源文件的某一行上设置断点 |
break | 在指定源文件的某一行上设置断点 |
info br | 查看断点 |
delete | 删除断点 |
diable | 禁止断点 |
enable | 使能断点 |
监视点(watch)操作 | |
watch | 当指定变量被写时,程序被停止 |
rwatch | 当指定变量被读时,程序被停止 |
数据操作 | |
print < EXPRESSION > | 查看数据 |
set varible=value | 设置变量 |
x /NFU ADDR | 检查内存值 ① N 代表重复数 ② F 代表输出格式 x : 16 进制整数格式 d : 有符号十进制整数格式 u : 无符号十进制整数格式 f : 浮点数格式 ③ U 代表输出格式: b :字节(byte) h :双字节数值 w :四字节数值 g :八字节数值 比如“ x /4ub 0x0”将会显示 0 地址开始到 4 个字节 |
执行程序 | |
step next nexti | 都是单步执行: step 会跟踪进入一个函数, next 指令则不会进入函数 nexti 执行一条汇编指令 |
continue | 继续执行程序,加载程序后也可以用来启动程序 |
帮助 | |
help [command] | 列出帮助信息,或是列出某个命令的帮助信 |
其他命令 | |
monitor <command …> | 调用 gdb 服务器软件的命令,比如:“ monitor mdw 0x0” 就是调用 openocd 本身的命令“ mdw 0x0” |
使用条件
-
代码已经重定位,处于它的链接地址.为什么?【在源文件设置断点,其实是在链接地址设置断点,是根据链接地址去修改内存(软断点)】
-
链接脚本必须是按照固定格式
text,data,bss
分开 -
被调试的程序中含有debug信息,也就是编译elf时有
-g
选项-
%.o:%.c
-
arm-linux-gcc -Wall -c -g -O2 -o $@ $<
-
-
%.o:%.S
-
arm-linux-gcc -Wall -c -g -O2 -o $@ $<
-
-
FAQ: 无法调试重定位的代码,那么代码搬运与sdram初始化怎么办? 使用opencod来执行,也就是再弄一个程序初始化sdram,使用openocd烧录
使用步骤
-
打开openocd,打开telent
-
如果是需要使用sdram的程序,则先在OpenOCD中先下载初始化sdram的程序
-
> load_image init/init.bin 0
-
> resume 0
-
> halt
-
target state: halted
-
target halted in ARM state due to debug-request, current mode: Supervisor
-
cpsr: 0x200000d3 pc: 0x000000b8
-
MMU: disabled, D-Cache: disabled, I-Cache: enabled
-
-
// 测试一下 sdram可用
-
> mdw 0x30000000
-
0x30000000: ea000017
-
> mww 0x30000000 0x12345678
-
> mdw 0x30000000
-
0x30000000: 12345678
-
-
打开cmd,输入
arn-elf-gdb leds_elf
启动gdb,指定程序 -
连接到OpenOCD
target remote 127.0.0.1:3333
-
下载程序
load
直接使用命令脚本 gdb.init : arm-elf-gdb -x gdb.init leds_elf
-
target remote localhost:3333
-
monitor halt
-
monitor arm920t cp15 2 0
-
monitor step
-
load
-
break main
-
continue
注意 gdb运行之后没有断点之后停不了了,需要在telnet使用halt
,然后GDB
界面才能继续输入
常用命令
-
si 执行一条指令
-
braek main.c:21 //在main.c的21行打断点
-
c 或者 continue 继续运行
-
-
print i //查看变量值
Eclipes
Eclipse 是 gdb(包括 arm-elf-gdb, arm-linux-gdb)的图形化前台,使用 Eclipse 进行调试实质
上是使用 gdb 进行调试。
使用 gdb 进行调试时, gdb 会用到程序的链接地址。比如在 main 函数打断点, gdb 会根
据 main 函数的链接地址找到内存上对应的指令,修改这条指令为一条特殊的指令。当程序执
行到这条特殊的指令时,就会停止下来。[也就是软件断点]
使用条件
- 程序应该位于它的链接地址上
- 如果用到SDRAM,先初始化SDRAM,然后下载程序到链接地址
简单工程
-
点击图标
Workbench
-
新建一个C工程
File -> New -> C Project
,选择“ Makefile project->Empty Projects”、“ Other Toolchain”
-
导入文件在
File -> Import
中的General>File System
-
工程设置
-
在“ Project” 菜单里,点击去掉“ Build Automatically”前面的标
记,表示不自动编译工程 -
在“ Project” 菜单里,点击
clean
,去除Start a build immediately
-
-
编译,其实在目录下直接
make
也是可以的了,已经安装后工具链了- 使用
Project
中的build all
和build project
都可以了 - 使用
clean
也行了 - 注意,make是不一样的,一个是arm-linux,另一个是arm-elf
- 使用
-
调试配置
-
参考下面uboot的图配,非uboot不需要配置source选项,命令行也不需要第一个路径配置
-
去除debug中的
stop on startup at main
-
project> debug config
选择Zylin Native
,new或者双击都可以,出现配置 -
Main> C/C++ Application
选择调试的elfleds.elf
-
Debugger> Debugger
选择EmbeddedGDB
,下面的main选择arm-elf-gdb
或者是C:\Program Files\yagarto\bin\arm-elf-gdb.exe
-
GDB command file 可以选择,也可以不选
,其实就是提前运行的命令-
target remote localhost:3333
-
monitor halt
-
monitor arm mcr 15 0 1 0 0 0
-
monitor step 0
-
load
-
break main
-
continue
但是虽然这里设置了断点,貌似有点问题,依然需要在
command
输入-
load
-
break main
-
continue
-
-
注意
如果有时候没有看到debug窗口,右上角点一下debug视图,然后F5试试
有时候使用clean,需要看下是不是有debug存在着,需要关掉
u-boot工程
调试网上下载的 u-boot 时,需要定义 CONFIG_SKIP_LOWLEVEL_INIT,它表示
“跳过底层的初始始化”,就是不要初始化存储控制器,不要再次复制 u-boot 本身到 SDRAM
中。对于韦东山的的 u-boot,已经增加的自动识别代码,无需定义这个宏。
-
import相关文件
-
设置命令如下,这个是为了建立路径对应
-
set substitute-path /work/eclipse_projects/u-boot/u-boot-1.1.6_OpenJTAG E:/Eeclipse_projects/u-boot/u-boot-1.1.6_OpenJTAG
-
load
-
break start_armboot
-
c
然后再source中删除原来的default,添加如下(这个实际是上面的命令在生效)
-
\work\projects\OpenPDA\u-boot-1.1.6_OpenJTAG
-
E:\Eeclipse_projects\u-boot\u-boot-1.1.6_OpenJTAG\
-
STM32烧写程序
-
halt
-
flash probe 0
-
flash write_image erase STM3210B.bin 0x08000000
-
verify_image STM3210B.bin 0x08000000
转载于:https://www.cnblogs.com/zongzi10010/p/9784797.html
版权声明
本文为[whoisliang]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/2963604/blog/4710174
边栏推荐
- 5分钟GET我使用Github 5 年总结的这些骚操作!
- 深入分析商淘多用户商城系统如何从搜索着手打造盈利点
- (3) ASP.NET Core3.1 Ocelot certification
- 零基础小白python入门——深入Python中的文件操作
- Exhibition cloud technology interpretation | in the face of emergencies, how does app do a good job in crash analysis and performance monitoring?
- Mobile security reinforcement helps app achieve comprehensive and effective security protection
- The worst hacker in history: stealing $1 billion of bitcoin without spending it for seven years, and finally being seized by the Department of justice
- 在Python中创建文字云或标签云
- 布客·ApacheCN 编程/后端/大数据/人工智能学习资源 2020.11
- 标梵IPFS矿机app软件开发软件 IPFSApp开发方法详解
猜你喜欢
高质量的缺陷分析:让自己少写 bug
I do digital transformation in traditional industries (1)
Do you think it's easy to learn programming? In fact, it's hard! Do you think it's hard to learn programming? In fact, it's very simple!
Full stack technology experience tells you: how much does it cost to develop a mall small program?
The latest version of pycharm 2020.3: pair programming, intelligent text proofreading and downloading experience
在Python中创建文字云或标签云
How to download and install autocad2020 in Chinese
解决微信小程序使用switchTab跳转后页面不刷新的问题
Explain git in detail
5 minutes get I use GitHub's 5-year summary of these operations!
随机推荐
Source code analysis of serilog -- implementation of sink
磁阻式随机存储器MRAM基本原理
Analysis of h264nalu head
In depth analysis of the multi-user shopping mall system from search to create a profit point
The selection of wire displacement encoder needs the guidance of precise electronics
【云小课】版本管理发展史之Git+——代码托管
布客·ApacheCN 编程/后端/大数据/人工智能学习资源 2020.11
A certification and authorization solution based on. Net core - hulutong 1.0 open source
脑机接口先驱炮轰马斯克:“他走的是一条死胡同,说的话我一个字都不同意”
Colleague notes - small program entry point
同事笔记-小程序入坑点
从一次需求改良漫谈php文件分片上传
Service registration and discovery of go micro integration Nacos
High quality defect analysis: let yourself write fewer bugs
Gesture switch background, let live with goods more immersive
QML Repeater
CentOS查看CPU核心数及cpuinfo解析
揭秘在召唤师峡谷中移动路径选择逻辑?
Installation and testing of Flink
5分钟GET我使用Github 5 年总结的这些骚操作!