构建编译使用HAL库的STM32程序

环境搭建

xmake-io/xmake下载:
https://github.com/xmake-io/xmake/releases/tag/v2.6.9

arm-none-eabi-gcc 下载:
Downloads | GNU Arm Embedded Toolchain Downloads – Arm Developer
https://developer.arm.com/downloads/-/gnu-rm

安装都是一路next下去就可以了。
安装记得勾选添加到环境变量。

xmake入门,构建项目原来可以如此简单
https://zhuanlan.zhihu.com/p/35051214

xmake新增智能代码扫描编译模式,无需手写任何make文件
https://tboox.org/cn/2017/01/07/build-without-makefile/

准备工作

对比MDK开发STM32:

  • MDK开发STM32工程必然有个启动文件(汇编.s),没有启动文件无法设置栈跳转到C语言世界
  • MDK通过UI界面直观地设置内存分配,加载地址、链接地址等,本质也是MDK根据设置生成散列文件(.sct),在GNU那里这种叫链接文件(.ld)链接脚本

如何得到链接文件和启动文件:

  • 自己编写,太麻烦。
  • 安装完arm-none-eabi工具链后 根目录的share\gcc-arm-none-eabi\samples 下有链接文件和启动文件的模板
  • 从ST提供的固件库中copy
  • 通过STM32CubeMx生成STM32工程,选择 Toolchain / IDE的方式为Makefile,生成的工程就带有启动文件和链接文件

为了方便选择STM32CubeMx 的方式生成工程,还可以方便参照生成的makefile来设置编译链接选项

编写xmake的构建文件xmake.lua文件

-- 设置工程名
set_project("stm32-xmake")-- 设置工程版本
set_version("1.0.0")add_rules("mode.debug", "mode.release")-- 自定义工具链
toolchain("arm-none-eabi")-- 标记为独立工具链set_kind("standalone")-- 定义交叉编译工具链地址set_sdkdir("C:\\gcc-arm-none-eabi\\10 2021.10")--set_bindir("C:\\Program Files (x86)\\GNU Arm Embedded Toolchain\\10 2021.10\\bin")
toolchain_end()target("demo")-- 编译为二进制程序set_kind("binary") -- 设置使用的交叉编译工具链set_toolchains("arm-none-eabi")  -- 设置平台set_plat("cross")-- 设置架构set_arch("m3")set_filename("demo.elf")add_defines("USE_HAL_DRIVER","STM32F103xE")-- 添加链接库add_links("c", "m", "nosys", "rdimon");-- 添加启动文件add_files("startup_stm32f103xe.s");-- 源文件和头文件路径local src_path = {"./Core/","./Drivers/STM32F1xx_HAL_Driver/",}-- 特殊头文件目录,不方便递归遍历的local inc_path = {"Drivers/CMSIS/Include","Drivers/CMSIS/Device/ST/STM32F1xx/Include"}-- 排除的文件,不参与编译 模板文件可以直接删除remove_files("./Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_timebase_tim_template.c")remove_files("./Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_timebase_rtc_alarm_template.c")remove_files("./Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_msp_template.c")-- 添加启动文件add_files("startup_stm32f103xe.s");for _, index in ipairs(src_path) do  -- 遍历 src_pathfor _, dir in ipairs(os.dirs(index.."/**")) do  -- 递归搜索子目录add_files(dir.."/*.c"); add_includedirs(dir);endend-- 添加头文件路径for _, inc in ipairs(inc_path) doadd_includedirs(inc);endif is_mode("debug") then add_cflags("-g", "-gdwarf-2")endadd_cflags("-Og","-mcpu=cortex-m3","-mthumb","-Wall","-fdata-sections","-ffunction-sections","-g -gdwarf-2",{force = true})add_asflags("-Og","-mcpu=cortex-m3","-mthumb","-x assembler-with-cpp","-Wall","-fdata-sections", "-ffunction-sections","-g -gdwarf-2",{force = true})add_ldflags("-Og","-mcpu=cortex-m3","-TSTM32F103ZETx_FLASH.ld","-Wl,--gc-sections","--specs=nosys.specs","-u _printf_float",  {force = true})after_build(function(target)cprint("Compile finished!!!")cprint("Next, generate HEX and bin files.")os.exec("arm-none-eabi-objcopy -O ihex ./build/cross/m3/release/demo.elf ./build/demo.hex")os.exec("arm-none-eabi-objcopy -O binary ./build/cross/m3/release/demo.elf ./build/demo.bin")print("Generate hex and bin files ok!!!")print(" ");print("********************储存空间占用情况*****************************")os.exec("arm-none-eabi-size -Ax ./build/cross/m3/release/demo.elf")os.exec("arm-none-eabi-size -Bx ./build/cross/m3/release/demo.elf")os.exec("arm-none-eabi-size -Bd ./build/cross/m3/release/demo.elf")--print("heap-堆, stack-栈, .data-已初始化的变量全局/静态变量, .bss-未初始化的data, .text-代码和常量")--os.run("arm-none-eabi-objdump -D ./build/cross/m3/release/demo.elf > demo.s")end)
  • add_defines( "USE_HAL_DRIVER", "STM32F103xE") 设置宏,类似MDK以下设置
  • 终端根目录执行xmake编译

错误解决

arm-none-eabi-gcc 报错 undefined reference to `_exit’解决方案_Pz_mstr的博客-CSDN博客
https://blog.csdn.net/qq_35544379/article/details/104805295

工具链已经设置到环境变量测试已经生效,但在Vscode上的终端还是无法生效的解决办法:
windows 修改环境变量后在 vscode 的终端不生效的解决方法 | 码农家园
https://www.codenong.com/jsf9a5c0fed195/

参考

stm32-xmake: 使用xmake来编译cubemx生成的项目
https://gitee.com/luodeb/stm32-xmake

GNU(gcc-arm-none-eabi)编译stm32代码,重定向printf问题_一个逍遥怪的博客-CSDN博客_gcc printf 重定向
https://blog.csdn.net/qq_42704360/article/details/102853340


使用xmake构建使用标准库的实际项目程序

编写xmake文件

-- 设置工程名
set_project("stm32-xmake")-- 设置工程版本
set_version("1.0.0")add_rules("mode.debug", "mode.release")-- 自定义工具链
toolchain("arm-none-eabi")-- 标记为自定义独立工具链set_kind("standalone")-- 定义交叉编译工具链地址set_sdkdir("C:\\gcc-arm-none-eabi\\10 2021.10")
toolchain_end()target("LED_DRV.elf")-- 编译为二进制程序set_kind("binary") -- 设置使用的交叉编译工具链set_toolchains("arm-none-eabi")  -- 设置平台 表示交叉编译程序set_plat("cross")-- 设置架构set_arch("m3")add_defines("USE_STDPERIPH_DRIVER",  -- 表示使用标准库"STM32F10X_MD"  --  中容量  根据芯片容量选择)-- 添加链接库add_links("c", "m", "nosys", "rdimon");  -- 链接选项添加 :-lc -lm -lnosys -lrdimon -- 添加启动文件add_files("startup_stm32f10x_md.s");add_cflags("-Og","-mcpu=cortex-m3","-mthumb","-Wall","-fdata-sections","-ffunction-sections","-g -gdwarf-2",{force = true})add_asflags("-Og","-mcpu=cortex-m3","-mthumb",--    "-x assembler-with-cpp","-Wall","-fdata-sections", "-ffunction-sections","-g -gdwarf-2",{force = true})add_ldflags("-Og","-mcpu=cortex-m3","-TSTM32F103C8Tx_FLASH.ld",  -- 不同芯片需要修改链接脚本"-Wl,--gc-sections","--specs=nosys.specs","-u _printf_float",  {force = true})-- 源文件和头文件路径local src_path = {"USER/","Common/src","./MqEvent/","Lib/Fwlib","Lib/Fwlib/src","./Lib/CMSIS/","USER/app/src","USER/bsp/src"}-- 头文件路径local inc_path = {"./MqEvent/","./Lib/CMSIS/","Lib/Fwlib/inc","Common/inc","USER/app/inc","USER/bsp/inc"}-- 排除的文件,不参与编译remove_files("Common\\src\\meanFilter.c")remove_files("USER\\bsp\\src\\VirtualCOM.c")for _, dir in ipairs(src_path) do  -- 遍历 src_pathadd_files(dir.."/*.c"); end-- 添加头文件路径for _, inc in ipairs(inc_path) doadd_includedirs(inc);endif is_mode("debug") then add_cflags("-g", "-gdwarf-2")endafter_build(function(target)cprint("Compile finished!!!")cprint("Next, generate hex and bin files.")os.exec("arm-none-eabi-objcopy -O ihex ./build/cross/m3/release/LED_DRV.elf ./build/LED_DRV.hex")os.exec("arm-none-eabi-objcopy -O binary ./build/cross/m3/release/LED_DRV.elf ./build/LED_DRV.bin")print("Generate hex and bin files ok!!!")print(" ");print("********************储存空间占用情况*****************************")os.exec("arm-none-eabi-size -Ax ./build/cross/m3/release/LED_DRV.elf")os.exec("arm-none-eabi-size -Bx ./build/cross/m3/release/LED_DRV.elf")os.exec("arm-none-eabi-size -Bd ./build/cross/m3/release/LED_DRV.elf")--print("heap-堆, stack-栈, .data-已初始化的变量全局/静态变量, .bss-未初始化的data, .text-代码和常量")-- os.run("arm-none-eabi-objdump.exe -D ./build/cross/m3/release/LED_DRV.elf > LED_DRV.s")end)
  • 在终端中切换到工程根目录执行xmake编译,如果使用的是Vscode,可以在编辑器内就可以打开终端,默认就是根路径;如果使用的编辑器无此功能,则可以打开powershell或bash等shell终端编译。
    使用Vscode内打开终端编译,在window下实际上打开的也是powershell:


    如果是想单独打开powershell编译,一个实用技巧就是在当前目录路径输入powershell回车就会打开一个powershell终端并且已经切换到工程根路径。

  • 如果xmake.lua是拷贝另一个工程,编译此工程可能会还是编译上一个工程的程序,这样可能会出问题,这时候可以执行xmake -P .,表示强制使用当前目录下的xmake.lua编译工程。

  • startup_stm32f10x_md.s 从标准库标准库固件包中获取,也可以用STM32CubeMx生成的。

  • STM32F103C8Tx_FLASH.ld 链接脚本使用STM32CubeMx生成的,也可以从标准库固件包中获取。

  • Vscode添加源码头文件路径的技巧:对文件目录树的文件或目录右键选择复制相对路径,就可以快速添加目录。

解决编译错误

1、解决内联汇编错误

C:\Users\opto\AppData\Local\Temp\cc3sKKIq.s:619: Error: registers may not be the same -- `strexb r0,r0,[r1]'
C:\Users\opto\AppData\Local\Temp\cc3sKKIq.s:650: Error: registers may not be the same -- `strexh r0,r0,[r1]'
stack traceback:

参考HAL库头文件Drivers\CMSIS\Core_A\Include\cmsis_gcc.h, 对core_cm3.c做以下修改:

uint32_t __STREXB(uint8_t value, uint8_t *addr)
{uint32_t result=0;//__ASM volatile ("strexb %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );__ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) );return(result);
}uint32_t __STREXH(uint16_t value, uint16_t *addr)
{uint32_t result=0;//__ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );__ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) );return(result);
}

2、解决 sbrkr.c:(.text._sbrk_r+0xc): undefined reference to `_sbrk’ 等问题

error: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-sbrkr.o): in function `_sbrk_r':
sbrkr.c:(.text._sbrk_r+0xc): undefined reference to `_sbrk'
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-writer.o): in function `_write_r':
writer.c:(.text._write_r+0x14): undefined reference to `_write'
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-closer.o): in function `_close_r':
closer.c:(.text._close_r+0xc): undefined reference to `_close'
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-fstatr.o): in function `_fstat_r':
fstatr.c:(.text._fstat_r+0xe): undefined reference to `_fstat'
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-isattyr.o): in function `_isatty_r':
isattyr.c:(.text._isatty_r+0xc): undefined reference to `_isatty'
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-lseekr.o): in function `_lseek_r':
lseekr.c:(.text._lseek_r+0x14): undefined reference to `_lseek'
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-readr.o): in function `_read_r':
readr.c:(.text._read_r+0x14): undefined reference to `_read'
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-abort.o): in function `abort':
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-signalr.o): in function `_kill_r':
signalr.c:(.text._kill_r+0xe): undefined reference to `_kill'
c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/gcc-arm-none-eabi/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7-m/nofp\libc.a(lib_a-signalr.o): in function `_getpid_r':
signalr.c:(.text._getpid_r+0x0): undefined reference to `_getpid'
collect2.exe: error: ld returned 1 exit status
  • 解决:加上链接选项:--specs=nosys.specs

STM32标准库修改Flash起始地址

修改中断向量

  • 修改文件:Lib\CMSIS\system_stm32f10x.c
128 #define VECT_TAB_OFFSET  0xB000 /*!< Vector Table base offset field. 264 #ifdef VECT_TAB_SRAM
265  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
266 #else
267  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
268 #endif
  • VECT_TAB_OFFSET 从0x0修改为0xB000
  • 也可以调用NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0xB000); 设置

修改链接地址

修改链接地址也就是修改链接文件

/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx)      : ORIGIN = 0x800B000, LENGTH = 64K
}
  • 将链接地址从0x8000000改为0x800B000

修改了地址后要执行xmake clean清除先前地址编译的obj文件,因为当前obj文件是未修改前的地址生成的obj,直接执行xmake地址还是原来的地址,所以要先清除后再执行xmake编译。
然后可以通过生成反汇编文件查看程序链接地址修改是否正常

反汇编:

Disassembly of section .isr_vector:0800b000 <.isr_vector>:800b000:  20005000  andcs   r5, r0, r0800b004:  0800c541  stmdaeq r0, {r0, r6, r8, sl, lr, pc}800b008:  0800d4d1  stmdaeq r0, {r0, r4, r6, r7, sl, ip, lr, pc}800b00c:    0800d4d3  stmdaeq r0, {r0, r1, r4, r6, r7, sl, ip, lr, pc}

可以看到中断向量地址已经从0800b000 开始,说明设置成功。

printf实现

1、重定向printf
usart_printf.c:

#include "stm32f1xx_hal.h"
#include "usart.h"int _write(int fd, char *buf, int size)
{for (int i = 0; i < size; i++){while ((USART1->SR & 0X40) == 0); /* wait finised */USART1->DR = (uint8_t)buf[i]; /* send data */}return size;
}

2、自己编写printf

void myprintf(const char *format, ...)
{char  str[256] = {0};va_list   v_args;va_start(v_args, format);(void)vsnprintf((char       *)&str[0],(size_t      ) sizeof(str),(char const *) format,v_args);va_end(v_args);usart1Write((char *)str, strlen(str));
}

重定向串口发现bin程序尺寸已经涨了58KB,后经过搜索解决:

  • 加入链接选项--specs=nano.specs,编译器会使用newlib_nano库, newlib_nano应该是类似MDK micro Lib的一个东西,编译完bin文件尺寸已经降到40KB。

测试

除了构建APP工程,还构建了Bootloader工程,测试从Bootloader程序跳转到APP程序都没问题。

总结

  • 使用这种方式开发适用于所有Cortex-M的芯片,不必受限于某个芯片的专用IDE,如STM32CubeIDE似乎只能用于STM32?STM32CubeIDE似乎也不太好用。
  • 所用涉及的软件全为开源免费的软件。
  • 不用编写修改Makefile,构建项目容易。
  • 可以使用任意编辑器,可以是Vscode、source insight,甚至是记事本。推荐使用Vscode,Vscode内就可以打开终端执行编译,用其他编辑器可能就要另外打开powershell或bash之类的进行编译了。

使用arm-none-eabi-gcc的缺点:

  • 由于MDK armcc工具针对性做了优化,编译出来的代码尺寸相对较小;arm-none-eabi-编译出来的代码尺寸相比armcc编译的偏大的。

使用xmake配合arm-none-eabi-gcc构建stm32工程相关推荐

  1. VisualGDB+Visual Studio 2019+CubeMX构建STM32工程问题记录(持续更新)

    CubeMX Toolchain IDE中没有 Other Toolchains(GPDSC)选项. cubemx在6.6.0以上的时候去掉了这个工具链选项.最后的版本是6.5.0. 使用ST-Lin ...

  2. ARM架构Docker镜像构建-基础知识

    介绍ARM版本的Docker镜像的构建,包括ARM机器上Docker的安装,在ARM机器上构建镜像,及在amd64机器上使用buildx交叉构建arm版本镜像. 前言 现在很多地方都对服务的国产化适配 ...

  3. Windows下使用MinGw和gcc构建第一个C程序、g++构建第一个C++程序

    gcc与g++都gnu的编译器:gcc是c语言的编译器:g++是c++的编译器:gdb 是调试工具. 看着有些面生:都是Linux的东西: MinGw 是 Minimal GNU on Windows ...

  4. ARM汇编与GCC汇编

    ARM汇编与GCC汇编 /** 您忘记正确设置程序集.要解决此问题,请在文件开头发出这些指令: */ .syntax unified .cpu cortex-m3 .fpu softvfp .thum ...

  5. Centos上使用Jenkins配合Gradle进行Android APK构建和分发

    Centos上使用Jenkins配合Gradle进行Android APK构建和分发 很多时候,测试人员和后台人员需要我们将各个环境APK包发给他们进行测试和调试,但是呢,我们不是时时都能响应他们的需 ...

  6. buntu linux下建立stm32开发环境: GCC安装以及工程Makefile建立

    之前在e络盟的意法半导体掏了一个STM32开发板挺好的,却不想在window下开发,也不想用那么占内存的IAR MDK等软件,所以决定在ubuntu下建立该开发环境,像之前avr linux一样,找了 ...

  7. Ubuntu下使用GCC开发STM32的环境的搭建

    注:从ubuntu linux下建立stm32开发环境: GCC安装以及工程Makefile建立转载. 1.STM 32 GCC 安装 stm32 属于arm cortex-m系列thumb指令集,所 ...

  8. Ubuntu下使用VS Code构建CMake工程

    1.下载Visual Studio Code 编译器 可以去Ubuntu自带的应用商店下载,或者使用你命令行下载 2.设置中文显示 直接下载的是英文版本,需要设置成中文显示 先去VS Code自带的商 ...

  9. 使用vscode + gcc进行 STM32 单片机开发(三)DMA读写SD卡,移植FATFS文件系统

    背景 在本系列的前两篇文章( 使用vscode + gcc进行 STM32 单片机开发(一)编译及调试 使用vscode + gcc进行 STM32 单片机开发(二)gcc环境 移植rtthread) ...

最新文章

  1. google nexus 5 刷机 卡刷 救砖教程
  2. xman的思维导图快捷键_这个良心好用的思维导图软件,居然不用氪金充钱
  3. 滥用static_沉思滥用:“强力使用,破坏滥用”
  4. tukey检测_回到数据分析的未来:Tukey真空度的整洁实现
  5. 汇编语言ax=0c58ch,第4章89C5汇编语言程序设计.ppt
  6. 高通计划通过多层级骁龙5G移动平台 加速5G商业化
  7. Node.js 14 发布,改进了诊断功能
  8. 《Django实战系列》
  9. java实现二叉树遍历
  10. matplotlib—matplotlib绘图中出现□的解决办法
  11. python中列表的嵌套是指列表的元素是另一个列表_Python实现嵌套列表去重方法示例...
  12. paip.log4j 日志系统 参数以及最佳实践
  13. 数字电路基础知识——CMOS门电路 (与非门、或非、非门、OD门、传输门、三态门)
  14. 阿里云CTO王坚当选院士,高手的人生都是如此雷同:生命的信仰
  15. 理解复数域上的向量空间
  16. 基于MATLAB的语音去噪处理系统
  17. C语言怎么才能让末尾没有多余的空格_C语言干货分享
  18. 术语FXO和FXS的含义是什么?
  19. 有声读物探索高速公路和英语的小路
  20. 仿乐享微信源码分享,微信管家升级版最新版本

热门文章

  1. 【2021-09-15】封装、继承、多态(作业)
  2. python单词表首字母排序_Python 程序按字母顺序对单词进行排序
  3. Spark的深入浅出
  4. 在飞书群里设置机器人提醒 github 代码更新
  5. RedisUtils和 RedisConfig代码封装如下
  6. 趣题:奇怪的自然数集划分
  7. 故宫珍宝馆完成二期改陈 珍贵红珊瑚盆景揭开面纱
  8. 源码大公开!Python爬取豆瓣电影Top250源代码,赶紧收藏!
  9. FTP文件服务器的搭建
  10. 问题关于json 字符串中带有反斜杠的问题