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

OpenHarmony(鸿蒙南向开发)——轻量系统STM32F407芯片移植案例

往期知识点记录:

  • 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总
  • 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
  • 持续更新中……

介绍基于STM32F407IGT6芯片在拓维信息 Niobe407 开发板上移植OpenHarmony LiteOS-M轻量系统,提供交通、工业领域开发板解决方案。移植架构采用BoardSoC分离方案,使用arm gcc工具链Newlib C库,实现了lwiplittlefshdf等子系统及组件的适配,开发了配套应用示例代码,支持通过Kconfig图形化配置编译选项。

适配准备

  • 下载 stm32cubemx 图形工具。
  • 准备ubuntu20.04系统环境,安装 arm-none-eabi-gcc 交叉编译工具链。

生成可用工程

通过stm32cubemx工具生成STM32F407IGT6芯片的Makefile工程,在此给出如下配置建议:

  • 系统相关配置采用默认配置。
  • 时钟配置时将SYSCLK选项配置为168MHz,发挥芯片最强性能。
  • 配置USART1用作调试串口,用来打印适配过程中的调试信息。
  • 配置stm32cubemx工程选项时,将Toolchain/IDE选项选为Makefile。

生成的工程目录如下:

├── Core
│   ├── Inc
│   │    ├── main.h
│   │    ├── stm32f4xx_hal_conf.h
│   │    └── stm32f4xx_it.h
│   └── Src
│        ├── main.c                --- 主函数
│        ├── stm32f4xx_hal_msp.c   --- HAL库弱函数配置文件
│        ├── stm32f4xx_it.c        --- 中断回调函数文件
│        └── system_stm32f4xx.c    --- 系统
├── Drivers
│   ├── CMSIS                      --- CMSIS接口
│   └── STM32F4xx_HAL_Driver       --- HAL库驱动
├── Makefile                       --- Makefile编译
├── STM32F407IGTx_FLASH.ld         --- 链接文件
├── startup_stm32f407xx.s          --- 启动文件
└── stm32f407_output.ioc           --- stm32cubemx工程文件

验证生成的工程

将生成的工程拷贝至Ubuntu,进入工程目录下执行make命令编译,确定能够编译成功。

arm-none-eabi-gcc build/main.o build/stm32f4xx_it.o build/stm32f4xx_hal_msp.o build/stm32f4xx_hal_tim.o build/stm32f4xx_hal_tim_ex.o build/stm32f4xx_hal_uart.o build/stm32f4xx_hal_rcc.o build/stm32f4xx_hal_rcc_ex.o build/stm32f4xx_hal_flash.o build/stm32f4xx_hal_flash_ex.o build/stm32f4xx_hal_flash_ramfunc.o build/stm32f4xx_hal_gpio.o build/stm32f4xx_hal_dma_ex.o build/stm32f4xx_hal_dma.o build/stm32f4xx_hal_pwr.o build/stm32f4xx_hal_pwr_ex.o build/stm32f4xx_hal_cortex.o build/stm32f4xx_hal.o build/stm32f4xx_hal_exti.o build/system_stm32f4xx.o build/startup_stm32f407xx.o -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nano.specs -TSTM32F407IGTx_FLASH.ld  -lc -lm -lnosys  -Wl,-Map=build/stm32f407_output.map,--cref -Wl,--gc-sections -o build/stm32f407_output.elf
arm-none-eabi-size build/stm32f407_output.elftext    data     bss     dec     hex filename5000      20    1636    6656    1a00 build/stm32f407_output.elf
arm-none-eabi-objcopy -O ihex build/stm32f407_output.elf build/stm32f407_output.hex
arm-none-eabi-objcopy -O binary -S build/stm32f407_output.elf build/stm32f407_output.bin

编译完成会生成一个.bin文件,为了确认该程序能在开发板中成功运行,需要main函数中的串口初始化之后,通过串口输出一段字符串,运行时若收到打印信息,则开发板启动成功。

printf("hello world!!\r\n");

适配printf输出到串口,只需要重写_write函数即可,参考如下:

#include <stdio.h>int _write(int fd, char *ptr, int len)
{ return HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF); 
}

重新编译代码,将其烧录至开发板中验证。

编译构建

目录规划

芯片适配目录规划为:

device
├── board                                --- 单板厂商目录
│   └── talkweb                          --- 单板厂商名字:拓维信息
│       └── niobe407                     --- 单板名:与产品名一致
└── soc									 --- SoC厂商目录└── st                               --- SoC厂商名称└── stm32f4xx					 --- SoC Series名:stm32f4xx是一个系列,包含该系列soc相关代码

产品样例目录规划为:

vendor
└── talkweb							     --- 开发产品样例厂商目录└── niobe407         			     --- 产品名字:niobe407

获取 OpenHarmony源码 ,根据上述目录规划,创建相应文件夹。

预编译适配

预编译适配内容就是围绕hb set命令的适配,使工程能够通过该命令设置根目录、单板目录、产品目录、单板公司名等环境变量,为后续适配编译做准备。

具体的预编译适配步骤如下:

1.在vendor/talkweb/niobe407目录下新增config.json文件,用于描述这个产品样例所使用的单板、内核等信息,描述信息可参考如下内容:

{"product_name": "niobe407",           --- 用于hb set进行选择时,显示的产品名称"type": "mini",                       --- 构建系统的类型,mini/small/standard"version": "3.0",                     --- 构建系统的版本,1.0/2.0/3.0"device_company": "talkweb",          --- 单板厂商名,用于编译时找到/device/board/talkweb目录"board": "niobe407",                  --- 单板名,用于编译时找到/device/board/talkweb/niobe407目录"kernel_type": "liteos_m",            --- 内核类型,因为OpenHarmony支持多内核,一块单板可能适配了多个内核,所以需要指定某个内核进行编译"kernel_version": "3.0.0",            --- 内核版本,一块单板可能适配了多个linux内核版本,所以需要指定某个具体的内核版本进行编译"subsystems": [ ]                     --- 选择所需要编译构建的子系统
}

2.在//device/board/talkweb/niobe407目录下创建board目录,在创建的目录下新增一个config.gni文件,用于描述该产品的编译配置信息:

# Kernel type, e.g. "linux", "liteos_a", "liteos_m".
kernel_type = "liteos_m"                --- 内核类型,跟config.json中kernel_type对应# Kernel version.
kernel_version = "3.0.0"                --- 内核版本,跟config.json中kernel_version对应

3.验证hb set配置是否正确,输入hb set能够显示如下信息:

4.通过hb env可以查看选择出来的预编译环境变量:

5.hb介绍

hb是OpenHarmony为了方便开发者进行代码构建编译,提供的python脚本工具,其源码就在//build/lite仓库目录下。在执行hb set命令时,脚本会遍历//vendor/<product_company>/<product_name>目录下的config.json,给出可选产品编译选项。在config.json文件中,product_name表示产品名,device_company和board用于关联出//device/board/<device_company>/目录,匹配该目录下的<any_dir_name>/config.gni文件,其中<any_dir_name>目录名可以是任意名称,但建议将其命名为适配内核名称(如:liteos_m、liteos_a、linux)。hb命令如果匹配到了多个config.gni,会将其中的kernel_type和kernel_version字段与vendor/<device_company>下config.json文件中的字段进行匹配,从而确定参与编译的config.gni文件。

至此,预编译适配完成,但工程还不能执行hb build进行编译,还需要准备好后续的LiteOS-M内核移植。

内核移植

内核移植需要完成LiteOS-M Kconfig适配、gn的编译构建和内核启动最小适配。

Kconfig文件适配

1.在//vendor/talkweb/niobe407目录下创建kernel_configs目录,并创建空文件,命名为debug.config。

2.打开//kernel/liteos_m/Kconfig文件,可以看到在该文件通过orsource命令导入了//device/board//device/soc下多个Kconfig文件,后续需要创建并修改这些文件:

orsource "../../device/board/*/Kconfig.liteos_m.shields"
orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.defconfig.boards"
orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.boards"
orsource "../../device/soc/*/Kconfig.liteos_m.defconfig"
orsource "../../device/soc/*/Kconfig.liteos_m.series"
orsource "../../device/soc/*/Kconfig.liteos_m.soc"

3.在//device/board/talkweb下参考如下目录结构创建相应的Kconfig文件:

.
├── Kconfig.liteos_m.boards
├── Kconfig.liteos_m.defconfig.boards
├── Kconfig.liteos_m.shields
└── niobe407├── Kconfig.liteos_m.board                --- 开发板配置选项├── Kconfig.liteos_m.defconfig.board      --- 开发板默认配置选项└── liteos_m└── config.gni
  1. 修改Kconfig文件内容:
  • //device/board/talkweb/Kconfig.liteos_m.boards文件中添加:
            if SOC_STM32F407orsource "niobe407/Kconfig.liteos_m.board"    --- 可根据SOC定义,加载指定board目录定义endif
  • //device/board/talkweb/Kconfig.liteos_m.defconfig.boards文件中添加:
            orsource "*/Kconfig.liteos_m.defconfig.board"
  • //device/board/talkweb/Kconfig.liteos_m.defconfig.boards文件中添加:
           orsource "shields/Kconfig.liteos_m.shields"
  • //device/board/talkweb/niobe407/Kconfig.liteos_m.board文件中添加:
            menuconfig BOARD_NIOBE407bool "select board niobe407"depends on SOC_STM32F407	 --- niobe407使用的是stm32f407的SoC,只有SoC被选择后,niobe407的配置选项才可见、可以被选择。
  • //device/board/talkweb/niobe407/Kconfig.liteos_m.defconfig.board中添加:
            if BOARD_NIOBE407--- 用于添加BOARD_NIOBE407默认配置endif #BOARD_NIOBE407
  • //device/soc/st下参考如下目录结构创建相应的Kconfig文件,并将stm32cubemx自动生成工程中的Drivers目录拷贝至stm32f4xx/sdk目录下:
        .├── Kconfig.liteos_m.defconfig├── Kconfig.liteos_m.series├── Kconfig.liteos_m.soc└── stm32f4xx├── Kconfig.liteos_m.defconfig.series├── Kconfig.liteos_m.defconfig.stm32f4xx├── Kconfig.liteos_m.series├── Kconfig.liteos_m.soc└── sdk└── Drivers├── CMSIS└── STM32F4xx_HAL_Driver

6.修改Kconfig文件内容:

  • //device/soc/st/Kconfig.liteos_m.defconfig中添加:
    rsource "*/Kconfig.liteos_m.defconfig.series"
  • //device/soc/st/Kconfig.liteos_m.series中添加:
    rsource "*/Kconfig.liteos_m.series"
  • //device/soc/st/Kconfig.liteos_m.soc中添加:
    config SOC_COMPANY_STMICROELECTRONICSboolif SOC_COMPANY_STMICROELECTRONICSconfig SOC_COMPANYdefault "st"rsource "*/Kconfig.liteos_m.soc"endif # SOC_COMPANY_STMICROELECTRONICS
  • //device/soc/st/stm32f4xx/Kconfig.liteos_m.defconfig.series中添加:
    if SOC_SERIES_STM32F4xxrsource "Kconfig.liteos_m.defconfig.stm32f4xx"config SOC_SERIESstringdefault "stm32f4xx"endif
  • //device/soc/st/stm32f4xx/Kconfig.liteos_m.defconfig.stm32f4xx中添加:
    config SOCstringdefault "stm32f4xx"depends on SOC_STM32F4xx
  • //device/soc/st/stm32f4xx/Kconfig.liteos_m.series中添加:
    config SOC_SERIES_STM32F4xxbool "STMicroelectronics STM32F4xx series"select ARCH_ARMselect SOC_COMPANY_STMICROELECTRONICSselect CPU_CORTEX_M4helpEnable support for STMicroelectronics STM32F4xx series
  • //device/soc/st/stm32f4xx/Kconfig.liteos_m.soc中添加:
    choiceprompt "STMicroelectronics STM32F4xx series SoC"depends on SOC_SERIES_STM32F4xxconfig SOC_STM32F407bool "SoC STM32F407"endchoice
  1. kernel/liteos_m目录下执行make menuconfig,使得能够对SoC Series进行选择:

结果将自动保存在$(PRODUCT_PATH)/kernel_configs/debug.config,下次执行make menuconfig时会导出保存的结果。

BUILD.gn文件适配

为了快速熟悉gn的编译和适配,建议先阅读 LiteOS-M内核BUILD.gn编写指南。

(注意,BUILD.gn文件中不要出现tab字符,所有tab用空格代替)

  1. kernel/liteos_m/BUILD.gn 中,可以看到,通过deps指定了BoardSoC的编译入口:
    deps += [ "//device/board/$device_company" ]            --- 对应//device/board/talkweb目录deps += [ "//device/soc/$LOSCFG_SOC_COMPANY" ]          --- 对应//device/soc/st目录
  1. //device/board/talkweb/BUILD.gn中,新增内容如下:
    if (ohos_kernel_type == "liteos_m") {import("//kernel/liteos_m/liteos.gni")module_name = get_path_info(rebase_path("."), "name")module_group(module_name) {modules = [ "niobe407" ]}}

3.在niobe407目录下创建BUILD.gn,为了方便管理,将目录名作为模块名:

    import("//kernel/liteos_m/liteos.gni")module_name = get_path_info(rebase_path("."), "name")module_group(module_name) {modules = [ "liteos_m",]}

4.将stm32cubemx生成的示例工程Core目录下的文件、startup_stm32f407xx.s启动文件和STM32F407IGTx_FLASH.ld链接文件拷贝至//device/board/talkweb/niobe407/liteos_m/目录下,并在该目录下创建BUILD.gn,添加如下内容:

import("//kernel/liteos_m/liteos.gni")
module_name = get_path_info(rebase_path("."), "name")
kernel_module(module_name) {sources = ["startup_stm32f407xx.s","Src/main.c","Src/stm32f4xx_hal_msp.c","Src/stm32f4xx_it.c","Src/system_stm32f4xx.c",]include_dirs = [ "Inc",]
}config("public") {ldflags = ["-Wl,-T" + rebase_path("STM32F407IGTx_FLASH.ld"),"-Wl,-u_printf_float",]libs = ["c","m","nosys",]
}

5.在make menuconfig中配置(Top) → Compat → Choose libc implementation,选择newlibc。

6.由于_write函数会与kernel的文件操作函数重名,会导致编译失败。后续会换一种方法来适配printf函数,此处我们先将main.c文件中对_write函数的重写删除,将printf函数改用如下方式进行串口打印测试。

uint8_t test[]={"hello niobe407!!\r\n"};
int len = strlen(test);
HAL_UART_Transmit(&huart1, (uint8_t *)test, len, 0xFFFF);

7.同理//device/soc/st/BUILD.gn也是一样,按照目录结构层层依赖包含,最终在//device/soc/st/stm32f4xx/sdk/BUILD.gn中通过kernel_module模板中指定需要参与编译的文件及编译参数,参考如下:

import("//kernel/liteos_m/liteos.gni")
module_name = "stm32f4xx_sdk"
kernel_module(module_name) {asmflags = board_asmflagssources = ["Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c","Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c","Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c","Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c","Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c","Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c","Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c","Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c","Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c",]
}
#指定全局头文件搜索路径
config("public") {include_dirs = ["Drivers/STM32F4xx_HAL_Driver/Inc","Drivers/CMSIS/Device/ST/STM32F4xx/Include",]
}

config.gni文件适配

在预编译阶段,在//device/board/talkweb/niobe407/liteos_m目录下创建了一个config.gni文件,它其实就是gn脚本的头文件,可以理解为工程构建的全局配置文件。主要配置了CPU型号、交叉编译工具链及全局编译、链接参数等重要信息:

# Kernel type, e.g. "linux", "liteos_a", "liteos_m".
kernel_type = "liteos_m"# Kernel version.
kernel_version = "3.0.0"# Board CPU type, e.g. "cortex-a7", "riscv32".
board_cpu = "cortex-m4"# Board arch, e.g.  "armv7-a", "rv32imac".
board_arch = ""# Toolchain name used for system compiling.
# E.g. gcc-arm-none-eabi, arm-linux-harmonyeabi-gcc, ohos-clang,  riscv32-unknown-elf.
# Note: The default toolchain is "ohos-clang". It's not mandatory if you use the default toolchain.
board_toolchain = "arm-none-eabi-gcc"use_board_toolchain = true# The toolchain path installed, it's not mandatory if you have added toolchain path to your ~/.bashrc.
board_toolchain_path = ""# Compiler prefix.
board_toolchain_prefix = "arm-none-eabi-"# Compiler type, "gcc" or "clang".
board_toolchain_type = "gcc"#Debug compiler optimization level options
board_opt_flags = ["-mcpu=cortex-m4","-mthumb","-mfpu=fpv4-sp-d16","-mfloat-abi=hard",
]# Board related common compile flags.
board_cflags = ["-Og","-Wall","-fdata-sections","-ffunction-sections","-DSTM32F407xx",
]
board_cflags += board_opt_flagsboard_asmflags = ["-Og","-Wall","-fdata-sections","-ffunction-sections",
]
board_asmflags += board_opt_flagsboard_cxx_flags = board_cflagsboard_ld_flags = board_opt_flags# Board related headfiles search path.
board_include_dirs = [ "//utils/native/lite/include" ]# Board adapter dir for OHOS components.
board_adapter_dir = ""

如上所示,比较难理解的就是board_opt_flags、board_cflags、board_asmflags等几个参数配置。可以参考如下描述,从stm32cubemx生成的工程中的Makefile文件中提取出来:

board_opt_flags : 编译器相关选项,一般为芯片架构、浮点类型、编译调试优化等级等选项。
board_asmflags  :汇编编译选项,与Makefile中的ASFLAGS变量对应。
board_cflags    :C代码编译选项,与Makefile中的CFLAGS变量对应。
board_cxx_flags :C++代码编译选项,与Makefile中的CXXFLAGS变量对应。
board_ld_flags  :链接选项,与Makefile中的LDFLAGS变量对应。

内核子系统适配

在//vendor/talkweb/niobe407/config.json文件中添加内核子系统及相关配置,如下所示:

{"product_name": "niobe407","type": "mini","version": "3.0","device_company": "talkweb","board": "niobe407","kernel_type": "liteos_m","kernel_version": "3.0.0","subsystems": [ {"subsystem": "kernel","components": [{"component": "liteos_m"}]}],"product_adapter_dir": "","third_party_dir": "//third_party"
}

target_config.h文件适配

在//kernel/liteos_m/kernel/include/los_config.h文件中,有包含一个名为target_config.h的头文件,如果没有这个头文件,则会编译出错。

该头文件的作用主要是定义一些与soc芯片相关的宏定义,可以创建一个空头文件,再配合编译报错提示信息来确定需要定义哪些宏。经验证,Cortex-M4的核适配只需定义LOSCFG_BASE_CORE_TICK_RESPONSE_MAX宏并包含stm32f4xx.h头文件即可将kernel编译通过。

若前期不知如何配置,可以参考虚拟机qemu示例中//device/qemu/arm_mps2_an386/liteos_m/board/target_config.h的配置。

#ifndef _TARGET_CONFIG_H
#define _TARGET_CONFIG_H#define LOSCFG_BASE_CORE_TICK_RESPONSE_MAX                  0xFFFFFFUL
#include "stm32f4xx.h"			//包含了stm32f4平台大量的宏定义#endif

其中宏定义LOSCFG_BASE_CORE_TICK_RESPONSE_MAX是直接参考的//device/qemu/arm_mps2_an386/liteos_m/board/target_config.h文件中的配置,//device/qemu/arm_mps2_an386cortex-m4的虚拟机工程,后续适配可以直接参考,在此不做深入讲解。

内核启动适配

至此,已经可以成功将kernel子系统编译通过,并且在out目录下生成OHOS_Image.bin文件。将生成的OHOS_Image.bin文件烧录至开发板,验证板子能否正常启动运行,如果能成功打印出main函数中串口输出的正确的打印信息,则可以开始进行内核启动适配。

1.为liteos_m分配内存,适配内存分配函数

在文件//kernel/liteos_m/kernel/src/mm/los_memory.c中,OsMemSystemInit函数通过LOS_MemInit进行了内存初始化。可以看到几个比较关键的宏需要我们指定,我们将其添加到target_config.h中:

extern unsigned int __los_heap_addr_start__;
extern unsigned int __los_heap_addr_end__;
#define LOSCFG_SYS_EXTERNAL_HEAP 1
#define LOSCFG_SYS_HEAP_ADDR ((void *)&__los_heap_addr_start__)
#define LOSCFG_SYS_HEAP_SIZE (((unsigned long)&__los_heap_addr_end__) - ((unsigned long)&__los_heap_addr_start__))

其中,__los_heap_addr_start__与__los_heap_addr_end__变量在STM32F407IGTx_FLASH.ld链接文件中被定义, 将_user_heap_stack花括号内内容修改为:

._user_heap_stack :
{. = ALIGN(0x40);__los_heap_addr_start__ = .;__los_heap_addr_end__ = ORIGIN(RAM) + LENGTH(RAM);
} >RAM

除此之外,我们还需要适配内存分配函数,由于内核中已经对_malloc_r等内存分配函数进行了实现,在此我们采用包装函数的方式来适配,用内核中的内存分配函数替换标准库中的内存分配函数即可,在//device/board/talkweb/niobe407/liteos_m/config.gni中board_ld_flags链接参数变量修改为:

board_ld_flags = ["-Wl,--wrap=_calloc_r","-Wl,--wrap=_malloc_r","-Wl,--wrap=_realloc_r","-Wl,--wrap=_reallocf_r","-Wl,--wrap=_free_r","-Wl,--wrap=_memalign_r","-Wl,--wrap=_malloc_usable_size_r",
]
board_ld_flags += board_opt_flags

2.适配printf打印

为了方便后续调试,第一步需要先适配printf函数。而printf的函数适配可大可小,在此只做简单适配,具体实现可以参考其它各开发板源码。

在main.c同级目录下创建dprintf.c文件,文件内容如下:

#include <stdarg.h>
#include "los_interrupt.h"
#include <stdio.h>extern UART_HandleTypeDef huart1;INT32 UartPutc(INT32 ch, VOID *file)
{char RL = '\r';if (ch =='\n') {HAL_UART_Transmit(&huart1, &RL, 1, 0xFFFF);}return HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
}static void dputs(char const *s, int (*pFputc)(int n, FILE *cookie), void *cookie)
{unsigned int intSave;intSave = LOS_IntLock();while (*s) {pFputc(*s++, cookie);}LOS_IntRestore(intSave);
}int printf(char const  *fmt, ...)
{char buf[1024] = { 0 };va_list ap;va_start(ap, fmt);int len = vsnprintf_s(buf, sizeof(buf), 1024 - 1, fmt, ap);va_end(ap);if (len > 0) {dputs(buf, UartPutc, 0);} else {dputs("printf error!\n", UartPutc, 0);}return len;
}

在串口初始化之后使用printf函数打印,测试是否适配成功。

3.调用LOS_KernelInit初始化内核,进入任务调度。

在main函数中串口初始化之后,调用LOS_KernelInit进行初始化,创建任务示例,进入任务调度。

#include "los_task.h"UINT32 ret;
ret = LOS_KernelInit();  //初始化内核
if (ret == LOS_OK) {TaskSample();  //示例任务函数,在此函数中创建线程任务LOS_Start();   //开始任务调度,程序执行将阻塞在此,由内核接管调度
}

其中TaskSample()函数内容如下:

VOID TaskSampleEntry2(VOID)
{while (1) {printf("TaskSampleEntry2 running...\n");(VOID)LOS_TaskDelay(2000); /* 2000 millisecond */}
}VOID TaskSampleEntry1(VOID)
{while (1) {printf("TaskSampleEntry1 running...\n");(VOID)LOS_TaskDelay(2000); /* 2000 millisecond */}
}
VOID TaskSample(VOID)
{UINT32 uwRet;UINT32 taskID1;UINT32 taskID2;TSK_INIT_PARAM_S stTask = {0};stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskSampleEntry1;stTask.uwStackSize = 0x1000;stTask.pcName = "TaskSampleEntry1";stTask.usTaskPrio = 6; /* Os task priority is 6 */uwRet = LOS_TaskCreate(&taskID1, &stTask);if (uwRet != LOS_OK) {printf("Task1 create failed\n");}stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskSampleEntry2;stTask.uwStackSize = 0x1000;stTask.pcName = "TaskSampleEntry2";stTask.usTaskPrio = 7; /* Os task priority is 7 */uwRet = LOS_TaskCreate(&taskID2, &stTask);if (uwRet != LOS_OK) {printf("Task2 create failed\n");}
}

适配完内核启动后,可以通过调试串口看到如下打印信息:

后续还需要对整个基础内核进行详细适配验证。

内核基础功能适配

内核基础功能适配项包括:中断管理任务管理内存管理内核通信机制时间管理软件定时器,可以参考对应链接中的编程实例进行内核基础功能验证。在验证的过程中发现问题,针对相应问题进行具体的适配。

从上一节中打印信息输出时间间隔可以看出,LOS_TaskDelay函数的延时时间不准确,我们可以在target_config.h中定义如下宏进行内核时钟适配:

#define OS_SYS_CLOCK                                        168000000
#define LOSCFG_BASE_CORE_TICK_PER_SECOND                    (1000UL)

其它内核基础功能的适配方法大多也是围绕于target_config.h中的宏定义,需要大家配合//kernel/liteos_m下源码,自行尝试摸索,在此不做进一步讲解。

littlefs文件系统移植适配

Niobe407开发板外挂了16MB的SPI-FLASH,Niobe407基于该Flash进行了littlefs适配。

内核已经对littlefs进行了适配,我们只需要开启Kconfig中的配置,然后适配Littlefs如下接口:

  int32_t LittlefsRead(const struct lfs_config *cfg, lfs_block_t block,lfs_off_t off, void *buffer, lfs_size_t size){W25x_BufferRead(buffer, cfg->context + cfg->block_size * block + off, size);return LFS_ERR_OK;}int32_t LittlefsProg(const struct lfs_config *cfg, lfs_block_t block,lfs_off_t off, const void *buffer, lfs_size_t size){W25x_BufferWrite((uint8_t *)buffer,cfg->context + cfg->block_size * block + off,size);return LFS_ERR_OK;}int32_t LittlefsErase(const struct lfs_config *cfg, lfs_block_t block){W25x_SectorErase(cfg->context + cfg->block_size * block);return LFS_ERR_OK;}int32_t LittlefsSync(const struct lfs_config *cfg){return LFS_ERR_OK;}

W25x_BufferRead等函数是spi-flash读写操作的接口,不同型号的spi-flash其实现也不同,Niobe407的SPI-Flash操作具体实现可参考//device/board/talkweb/niobe407/liteos_m/drivers/spi_flash/src/w25qxx.c

由于SPI已经hdf化了,而littlefs依赖于spi驱动,为了方便对文件系统进行配置,可以将littlefs的配置加入至.hcs文件中,具体参考://device/board/talkweb/niobe407/liteos_m/hdf_config/hdf_littlefs.hcs文件

misc {littlefs_config {match_attr = "littlefs_config";mount_points = ["/talkweb"];partitions = [0x800000];block_size = [4096];block_count = [256];}
}

板级驱动移植

驱动适配相关文件放置在//drivers/adapter/platform中,对应有gpioi2cpwmspiuartwatchdog,都是通过HDF机制加载,本章节以pwm为例进行说明。

PWM驱动适配

在HDF框架中,PWM的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDF DeviceManager的服务管理能力,但需要为每个设备单独配置设备节点。

  • 接口说明
1. pwm open初始化函数:DevHandle PwmOpen(uint32_t num);参数说明: num:     PWM设备编号。return:  获取成功返回PWM设备句柄,失败返回NULL。
2. pwm close去初始化函数:void PwmClose(DevHandle handle);参数说明:handle:   pwm设备句柄。return:    无。
3. 设置PWM设备参数:int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config);参数说明:handle:   pwm设备句柄。*config    参数指针。return:    返回0表示设置成功,返回负数表示失败。
  • PWM HDF HCS配置文件解析

device_info.hcs文件位于//device/board/talkweb/niobe407/liteos_m/hdf_config/device_info.hcs,以下示例为使用TIM2、TIM3和TIM7定时器输出PWM信号:

device_pwm1 :: device {pwm1 :: deviceNode {policy = 2;priority = 100;moduleName = "ST_HDF_PLATFORM_PWM";serviceName = "HDF_PLATFORM_PWM_1";deviceMatchAttr = "config_pwm1";}
}
device_pwm2 :: device {pwm2 :: deviceNode {policy = 2;priority = 100;moduleName = "ST_HDF_PLATFORM_PWM";serviceName = "HDF_PLATFORM_PWM_2";deviceMatchAttr = "config_pwm2";}
}
device_pwm7 :: device {pwm7 :: deviceNode {policy = 2;priority = 100;moduleName = "ST_HDF_PLATFORM_PWM";serviceName = "HDF_PLATFORM_PWM_7";deviceMatchAttr = "config_pwm7";}
}

hdf.hcs文件位于//device/board/talkweb/niobe407/liteos_m/hdf_config/hdf.hcs,在此文件中配置TIM定时器具体信息:

--- 注意:tim2-tim7、tim12-tim14时钟频率为84M,TIM1、TIM8~TIM11为168M,tim6和tim7不能输出pwm。
--- tim1~tim5、tim8有4个channel,tim9、tim12有2个channel,tim10、tim11、tim13、tim14只有1个channel。pwm_config {pwm1_config {match_attr = "config_pwm1";pwmTim = 1; 		--- 定时器ID tim2(0:tim1,1:tim2,...,tim6和tim7不可用)pwmCh = 3; 			--- 对应channel数(0:ch1、1:ch2、2:ch3、3:ch4)prescaler = 4199; 	--- 预分频数,例如tim2时钟为84M,(84M/(4199+1))=20khz,则以20khz为基准。}       pwm2_config {match_attr = "config_pwm2";pwmTim = 2;pwmCh = 0;prescaler = 8399;} pwm3_config {match_attr = "config_pwm7";pwmTim = 7;pwmCh = 0;prescaler = 8399;}
}

hdf pwm适配代码请参考://drivers/adapter/platform/pwm/pwm_stm32f4xx.c

hdf pwm使用示例可请参考://device/board/talkweb/niobe407/applications/206_hdf_pwm

子系统适配

OpenHarmony子系统适配一般包含两部分:

  • config.json中增加对应子系统和部件,这样编译系统会将该部件纳入编译目标中。
  • 针对该部件的HAL层接口进行硬件适配,或者可选的软件功能适配。

LWIP部件适配

LiteOS-M kernel通过Kconfig配置可以使lwip参与编译,并可以在kernel组件中指定lwip编译适配的目录。如下:

{"subsystem": "kernel","components": [{"component": "liteos_m","features": ["ohos_kernel_liteos_m_lwip_path = \"//device/board/talkweb/niobe407/liteos_m/lwip_adapter\"" --- 指定适配路径]}]
}

在指定的编译适配目录中,通过#include_next "lwip/lwipopts.h"的方式入侵修改lwip三方库中头文件配置,关于有线以太网LWIP适配部分,后续会补充详细适配步骤,在此先不做深入讲解。

启动恢复子系统适配

启动恢复子系统适配bootstrap_litesyspara_lite两个组件。请在//vendor/talkweb/niobe407/config.json中新增对应的配置选项。

{"subsystem": "startup","components": [{"component": "bootstrap_lite","features": []},{"component": "syspara_lite","features": []}]
}

适配bootstrap_lite部件时,需要在链接文件//device/board/talkweb/niobe407/liteos_m/STM32F407IGTx_FLASH.ld中手动新增如下段:

__zinitcall_bsp_start = .;
KEEP (*(.zinitcall.bsp0.init))
KEEP (*(.zinitcall.bsp1.init))
KEEP (*(.zinitcall.bsp2.init))
KEEP (*(.zinitcall.bsp3.init))
KEEP (*(.zinitcall.bsp4.init))
__zinitcall_bsp_end = .;
__zinitcall_device_start = .;
KEEP (*(.zinitcall.device0.init))
KEEP (*(.zinitcall.device1.init))
KEEP (*(.zinitcall.device2.init))
KEEP (*(.zinitcall.device3.init))
KEEP (*(.zinitcall.device4.init))
__zinitcall_device_end = .;
__zinitcall_core_start = .;
KEEP (*(.zinitcall.core0.init))
KEEP (*(.zinitcall.core1.init))
KEEP (*(.zinitcall.core2.init))
KEEP (*(.zinitcall.core3.init))
KEEP (*(.zinitcall.core4.init))
__zinitcall_core_end = .;
__zinitcall_sys_service_start = .;
KEEP (*(.zinitcall.sys.service0.init))
KEEP (*(.zinitcall.sys.service1.init))
KEEP (*(.zinitcall.sys.service2.init))
KEEP (*(.zinitcall.sys.service3.init))
KEEP (*(.zinitcall.sys.service4.init))
__zinitcall_sys_service_end = .;
__zinitcall_sys_feature_start = .;
KEEP (*(.zinitcall.sys.feature0.init))
KEEP (*(.zinitcall.sys.feature1.init))
KEEP (*(.zinitcall.sys.feature2.init))
KEEP (*(.zinitcall.sys.feature3.init))
KEEP (*(.zinitcall.sys.feature4.init))
__zinitcall_sys_feature_end = .;
__zinitcall_run_start = .;
KEEP (*(.zinitcall.run0.init))
KEEP (*(.zinitcall.run1.init))
KEEP (*(.zinitcall.run2.init))
KEEP (*(.zinitcall.run3.init))
KEEP (*(.zinitcall.run4.init))
__zinitcall_run_end = .;
__zinitcall_app_service_start = .;
KEEP (*(.zinitcall.app.service0.init))
KEEP (*(.zinitcall.app.service1.init))
KEEP (*(.zinitcall.app.service2.init))
KEEP (*(.zinitcall.app.service3.init))
KEEP (*(.zinitcall.app.service4.init))
__zinitcall_app_service_end = .;
__zinitcall_app_feature_start = .;
KEEP (*(.zinitcall.app.feature0.init))
KEEP (*(.zinitcall.app.feature1.init))
KEEP (*(.zinitcall.app.feature2.init))
KEEP (*(.zinitcall.app.feature3.init))
KEEP (*(.zinitcall.app.feature4.init))
__zinitcall_app_feature_end = .;
__zinitcall_test_start = .;
KEEP (*(.zinitcall.test0.init))
KEEP (*(.zinitcall.test1.init))
KEEP (*(.zinitcall.test2.init))
KEEP (*(.zinitcall.test3.init))
KEEP (*(.zinitcall.test4.init))
__zinitcall_test_end = .;
__zinitcall_exit_start = .;
KEEP (*(.zinitcall.exit0.init))
KEEP (*(.zinitcall.exit1.init))
KEEP (*(.zinitcall.exit2.init))
KEEP (*(.zinitcall.exit3.init))
KEEP (*(.zinitcall.exit4.init))
__zinitcall_exit_end = .;

需要新增上述段是因为bootstrap_init提供的对外接口,见//utils/native/lite/include/ohos_init.h文件,采用的是灌段的形式,最终会保存到上述链接段中。主要的服务自动初始化宏如下表格所示:

接口名描述
SYS_SERVICE_INIT(func)标识核心系统服务的初始化启动入口
SYS_FEATURE_INIT(func)标识核心系统功能的初始化启动入口
APP_SERVICE_INIT(func)标识应用层服务的初始化启动入口
APP_FEATURE_INIT(func)标识应用层功能的初始化启动入口

通过上面加载的组件编译出来的lib文件需要手动加入强制链接。

如在 //vendor/talkweb/niobe407/config.json 中配置了bootstrap_lite 部件

    {"subsystem": "startup","components": [{"component": "bootstrap_lite"},...]},

bootstrap_lite部件会编译//base/startup/bootstrap_lite/services/source/bootstrap_service.c,该文件中,通过SYS_SERVICE_INIT将Init函数符号灌段到__zinitcall_sys_service_start和__zinitcall_sys_service_end中,由于Init函数是没有显式调用它,所以需要将它强制链接到最终的镜像。如下:

static void Init(void)
{static Bootstrap bootstrap;bootstrap.GetName = GetName;bootstrap.Initialize = Initialize;bootstrap.MessageHandle = MessageHandle;bootstrap.GetTaskConfig = GetTaskConfig;bootstrap.flag = FALSE;SAMGR_GetInstance()->RegisterService((Service *)&bootstrap);
}
SYS_SERVICE_INIT(Init);   --- 通过SYS启动即SYS_INIT启动就需要强制链接生成的lib

在//base/startup/bootstrap_lite/services/source/BUILD.gn文件中,描述了在//out/niobe407/niobe407/libs 生成 libbootstrap.a,如下:

static_library("bootstrap") {sources = ["bootstrap_service.c","system_init.c",]...

适配syspara_lite部件时,系统参数会最终写到文件中进行持久化保存。在轻量系统中,文件操作相关接口有POSIX接口与HalFiles接口这两套实现。

因为对接内核的文件系统,采用POSIX相关的接口,所以features字段中需要增加enable_ohos_startup_syspara_lite_use_posix_file_api = true。

如果对接HalFiles相关的接口实现的,则无须修改。

DFX子系统适配

进行DFX子系统适配需要添加hilog_lite和hievent_lite部件,直接在config.json文件配置即可。

{"subsystem": "hiviewdfx","components": [{"component": "hilog_lite","features": []},{"component": "hievent_lite","features": []}]
}

配置完成之后,需要注册日志输出实现函数,并加入编译。

bool HilogProc_Impl(const HiLogContent *hilogContent, uint32_t len)
{char tempOutStr[LOG_FMT_MAX_LEN];tempOutStr[0] = 0,tempOutStr[1] = 0;if (LogContentFmt(tempOutStr, sizeof(tempOutStr), hilogContent) > 0) {printf(tempOutStr);}return true;
}HiviewRegisterHilogProc(HilogProc_Impl);

系统服务管理子系统适配

进行系统服务管理子系统适配需要添加samgr_lite部件,直接在config.json配置即可。

{"subsystem": "systemabilitymgr","components": [{"component": "samgr_lite","features": []}]
}

在轻量系统中,samgr_lite配置的共享任务栈大小默认为2048。在适配时可以在features中,通过config_ohos_systemabilitymgr_samgr_lite_shared_task_size重新设置共享任务栈大小。

"config_ohos_systemabilitymgr_samgr_lite_shared_task_size = 4096"

安全子系统适配

进行安全子系统适配需要添加huks组件,直接在config.json配置即可。

{"subsystem": "security","components": [{"component": "huks","features": ["huks_use_lite_storage = true","huks_use_hardware_root_key = true","huks_config_file = \"hks_config_lite.h\"","huks_key_store_path = \"storage\""]}]
}

huks部件适配时,huks_key_store_path配置选项用于指定存放秘钥路径,huks_config_file为配置头文件名称。

公共基础库子系统适配

公共基础库子系统适配添加了kv_storefileos_dump组件,直接在config.json配置即可。

{"subsystem": "utils","components": [{"component": "file","features": []},{"component": "kv_store","features": ["enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = false"]},{"component": "os_dump","features": []}]
},

与适配syspara_lite部件类似,适配kv_store部件时,键值对会写到文件中。在轻量系统中,文件操作相关接口有POSIX接口与HalFiles接口这两套实现。因为对接内核的文件系统,采用POSIX相关的接口,所以features需要增加enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true。如果对接HalFiles相关的接口实现的,则无须修改。

HDF子系统适配

与启动恢复子系统适配类似,我们需要在链接文件//device/board/talkweb/niobe407/liteos_m/STM32F407IGTx_FLASH.ld中手动新增如下段:

_hdf_drivers_start = .;
KEEP(*(.hdf.driver))
_hdf_drivers_end = .;

然后,在kernel初始化完成后调用DeviceManagerStart函数,执行完成后,才能调用hdf接口控制外设。

#include "devmgr_service_start.h"   --- 注意需要包含该头文件#ifdef LOSCFG_DRIVERS_HDFDeviceManagerStart();
#endif

devmgr_service_start.h头文件所在路径为: //drivers/framework/core/common/include/manager,为保证编译时能找到该头文件,需要将其加入到include_dirs中:

XTS兼容性测评子系统适配

产品兼容性规范

产品兼容性规范文档请参考 产品兼容性SIG介绍。

添加XTS子系统

XTS测试参考资料见 xts参考资料,进行XTS子系统适配需要添加xts_actsxts_tools组件,直接在config.json配置即可,配置如下:

{"subsystem": "xts","components": [{"component": "xts_acts","features": []},{"component": "xts_tools","features": []}]
}

我们可以在xts_acts组件的features数组中指定如下属性:

  • config_ohos_xts_acts_utils_lite_kv_store_data_path 配置挂载文件系统根目录的名字。
  • enable_ohos_test_xts_acts_use_thirdparty_lwip 表示如果使用thirdparty/lwip目录下的源码编译,则设置为true,否则设置为false
编译XTS

在配置config.json后,使用hb build是不会去编译xts的,只有在debug版本编译时才会参与编译,并且需要我们强制链接需要进行测试的套件静态库。

在我们//device/board/talkweb/liteos_m下包含kernel_module的BUILD.gn 脚本中添加如下内容:

config("public") {if (build_xts) {lib_dirs = [ "$root_out_dir/libs" ]ldflags += ["-Wl,--whole-archive",     --- 开启whole-archive特性,可以把在其后面出现的静态库包含的函数和变量输出到动态库"-lbootstrap","-lbroadcast","-lhctest",#公共基础库# "-lmodule_ActsUtilsFileTest",# "-lmodule_ActsKvStoreTest",#DFX"-lmodule_ActsDfxFuncTest","-lmodule_ActsHieventLiteTest",#启动恢复# "-lmodule_ActsBootstrapTest",# "-lmodule_ActsParameterTest",#分布式任务调度# "-lmodule_ActsSamgrTest","-Wl,--no-whole-archive",  --- 关掉whole-archive这个特性]}
}

由于Niobe407开发板内存有限,xts测试时需要分套件测试。执行如下编译命令,即可生成包含xts测试的固件。

hb build -f -b debug --gn-args build_xts=true

此外,我们还需要修改//vendor/talkweb/niobe407/hals/utils/sys_param/hal_sys_param.c文件,将这些字符串定义正确。

static const char OHOS_DEVICE_TYPE[] = {"Evaluation Board"};
static const char OHOS_DISPLAY_VERSION[] = {"OpenHarmony 3.1"};
static const char OHOS_MANUFACTURE[] = {"Talkweb"};
static const char OHOS_BRAND[] = {"Talkweb"};
static const char OHOS_MARKET_NAME[] = {"Niobe"};
static const char OHOS_PRODUCT_SERIES[] = {"Niobe"};
static const char OHOS_PRODUCT_MODEL[] = {"Niobe407"};
static const char OHOS_SOFTWARE_MODEL[] = {"1.0.0"};
static const char OHOS_HARDWARE_MODEL[] = {"2.0.0"};
static const char OHOS_HARDWARE_PROFILE[] = {"RAM:192K,ROM:1M,ETH:true"};
static const char OHOS_BOOTLOADER_VERSION[] = {"twboot-v2022.03"};
static const char OHOS_ABI_LIST[] = {"armm4_hard_fpv4-sp-d16-liteos"};
static const char OHOS_SERIAL[] = {"1234567890"};  // provided by OEM.

验证XTS

编译完成后,将固件烧录至开发板,xts全部跑完会有显示xx Tests xx Failures xx Ignored等信息,以下以公共基础库测试为例:

../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:590:testKvStoreClearCache002:PASS
../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:625:testKvStoreCacheSize001:PASS
../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:653:testKvStoreCacheSize002:PASS
../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:681:testKvStoreCacheSize003:PASS
../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:709:testKvStoreMaxSize001:PASS
../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:737:testKvStoreMaxSize002:PASS
../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:765:testKvStoreMaxSize003:PASS
../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:793:testKvStoreMaxSize004:PASS
+-------------------------------------------+-----------------------
32 Tests 0 Failures 0 Ignored 
OK
All the test suites finished!

粉丝推荐

经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?

为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

在这里插入图片描述

《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

鸿蒙开发面试真题(含参考答案):

在这里插入图片描述

《OpenHarmony源码解析》:

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

图片

OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN/733GH/overview

图片
在这里插入图片描述

相关文章:

OpenHarmony(鸿蒙南向开发)——轻量系统STM32F407芯片移植案例

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 介绍基于STM32F407IGT6芯片在拓维信息 Niobe407 开发板上移植OpenH…...

简单易懂的springboot整合Camunda 7工作流入门教程

简单易懂的Spring Boot整合Camunda7入门教程 因为关于Spring Boot结合Camunda7的教程在网上比较少&#xff0c;而且很多都写得有点乱&#xff0c;很多概念写得太散乱&#xff0c;讲解不清晰&#xff0c;导致看不懂&#xff0c;本人通过研究学习之后就写出了这篇教学文档。 介…...

LabVIEW提高开发效率技巧----点阵图(XY Graph)

在LabVIEW开发中&#xff0c;点阵图&#xff08;XY Graph&#xff09; 是一种强大的工具&#xff0c;尤其适用于需要实时展示大量数据的场景。通过使用点阵图&#xff0c;开发人员能够将实时数据可视化&#xff0c;帮助用户更直观地分析数据变化。 1. 点阵图的优势 点阵图&…...

C++-匿名空间

匿名命名空间&#xff08;anonymous namespace&#xff09;是 C 中的一种特性&#xff0c;用于将符号&#xff08;如变量、函数或类&#xff09;限制在定义它们的源文件的作用域内。这意味着在该源文件外部&#xff0c;这些符号不可见&#xff0c;从而避免了命名冲突。 1. 定义…...

jdk的安装和环境变量配置

1.将从官网下载好的jdk放在自己想要放的位置&#xff0c;这里的位置是&#xff1a;E:\develop 2.新建一个文件夹用来放安装的jdk&#xff0c;将jdk安装的此目录&#xff0c;这里的位置是&#xff1a;E:\develop\jdk17 3.jdk安装好之后&#xff0c;点击jdk17目录&#xff0c;点…...

继承、Lambda、Objective-C和Swift

继承 东风系列导弹是镇国神器。东风41不是突然就造出来的&#xff0c;之前有很多种东风xx导弹&#xff0c;每种导弹都有自己的独特之处&#xff0c;相同之处都具备导弹基本特点。很多工厂有量产磨具的生产线&#xff0c;盖房子就图纸&#xff0c;建筑设计建设都有参考&#xff…...

设置服务器走本地代理

勾选&#xff1a; 然后&#xff1a; git clone https://github.com/rofl0r/proxychains-ng.git./configure --prefix/home/wangguisen/usr --sysconfdir/home/wangguisen/etcmakemake install# 在最后配置成本地代理地址 vim /home/wangguisen/etc/proxychains.confsocks4 17…...

刷题 -哈希

面试面试经典 150 题 - 哈希 383. 赎金信 - 一个哈希表搞定 class Solution { public:bool canConstruct(string ransomNote, string magazine) {int hash[26] {0};for (auto& ch : magazine) {hash[ch - a];}for (auto& ch : ransomNote) {if (--hash[ch - a] < …...

React响应式修改数组和对象

在React中&#xff0c;响应式地修改数组数据是一个常见的需求&#xff0c;它涉及到状态&#xff08;state&#xff09;的管理和更新。React的状态是不可变的&#xff0c;这意味着你不能直接修改状态对象中的数组元素&#xff0c;而是需要创建一个新的数组来更新状态。下面将详细…...

cerbot https证书免费自动续期

1. 简介 cerbot是一个开源的数字证书生成平台&#xff0c;简单好用。下面我们来看下安装配置和使用。 cerbot有很多种安装方式&#xff0c;有源码安装、第三方发布版本、pip安装和snap安装&#xff0c;官方推荐使用snap安装。 2. snap安装 snap官方安装文档&#xff0c;Sna…...

嵌入式硬件设计

嵌入式硬件设计是指针对嵌入式系统&#xff08;一种专用的计算机系统&#xff0c;通常嵌入到其他设备中&#xff09;进行的硬件设计工作。嵌入式系统广泛应用于消费电子、工业控制、医疗设备、汽车电子、航空航天等领域。以下是嵌入式硬件设计的主要内容和步骤&#xff1a; 1.…...

2024.09.24 校招 实习 内推 面经

&#x1f6f0;️ &#xff1a;neituijunsir 交* 流*裙 &#xff0c;内推/实习/校招汇总表格 1、校招 | 毫末智行2025届校园招聘正式启动 &#xff08;内推&#xff09; 校招 | 毫末智行2025届校园招聘正式启动 &#xff08;内推&#xff09; 2、校招 | 圭目机器人 2025 校…...

GIT安装及集成到IDEA中操作步骤

最近深感GIT使用技能太差&#xff0c;我只会些皮毛&#xff0c;还是得看官网&#xff0c;总结一下常用的操作方法吧。 GIT环境配置到IDEA中安装 一、GIt的基本的安装 这个不在这里赘述了&#xff0c;自己装一个git吧 二、给IDEA指定本地GIT的安装路径 1、下图这个是我本地的…...

Java使用线程池创建线程

一、线程前言 首先我们知道&#xff0c;线程的概念如果不知道可以去看这一篇Java中的线程&#xff0c;我们这篇主要讲述的是Java怎么使用线程池创建线程&#xff0c;首先我们要对线程池有点概念&#xff0c;其实顾名思义&#xff0c;线程池就是有喝多线程的一个池子类似于&…...

mysql UDF提权(实战案例)

作者&#xff1a;程序那点事儿 日期&#xff1a;2024/09/29 16:10 什么是UDF? 全称 User Define Function &#xff08;用户自定义函数&#xff09;UDF提权&#xff0c;就是通过自定义函数&#xff0c;实现执行系统的命令。 dll&#xff08;windows&#xff0c;dll文件是c语…...

【瑞昱RTL8763E】刷屏

1 显示界面填充 用户创建的各个界面在 rtk_gui group 中。各界面中 icon[]表对界面进行描述&#xff0c;表中的每个元素代表一 个显示元素&#xff0c;可以是背景、小图标、字符等&#xff0c;UI_WidgetTypeDef 结构体含义如下&#xff1a; typedef struct _UI_WidgetTypeDef …...

【黑马点评】使用RabbitMQ实现消息队列——3.使用Jmeter压力测试,导入批量token,测试异步秒杀下单

3 批量获取用户token&#xff0c;使用jmeter压力测试 3 批量获取用户token&#xff0c;使用jmeter压力测试3.1 需求3.2 实现3.2.1 环境配置3.2.2 修改登录接口UserController和实现类3.2.3 测试类 3.3 使用jmeter进行测试3.4 测试结果3.5 将用户登录逻辑修改回去 3 批量获取用户…...

第 21 章 一条记录的多幅面孔——事务的隔离级别与 MVCC

21.1 事前准备 CREATE TABLE hero ( number INT, NAME VARCHAR ( 100 ), country VARCHAR ( 100 ), PRIMARY KEY ( number ) ) ENGINE INNODB CHARSET utf8;INSERT INTO hero VALUES ( 1, 刘备, 蜀 );21.2 事务隔离级别 在保证事务隔离性的前提下&#xff0c;使用不同的隔…...

javaScript操作dom的事件(3个案例+代码+效果图)

目录 1.焦点事件 案例:登录表单的验证 1.代码 2.效果 3.解释 2.鼠标事件 案例:单击鼠标使小球跳跃 1.代码 2.效果 3.解释 3.键盘事件 案例:使用左右键控制小球左右移动 1.代码 2.效果 ​编辑 3.解释 1.焦点事件 focus 当获得焦点时出发(不会冒泡)blur 当失去焦点时出发(不会…...

国庆期间的问题,如何在老家访问杭州办公室的网络呢

背景&#xff1a;国庆期间的问题&#xff0c;如何在老家访问杭州办公室的网络呢 实现方案&#xff1a;异地组网 实现语言&#xff1a;Java 环境&#xff1a;三个网络&#xff0c;一台拥有公网IP的服务器、一台杭州本地机房内服务器、你老家所在网络中的一台电脑&#xff08;…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解

文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一&#xff1a;HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二&#xff1a;Floyd 快慢指针法&#xff08;…...

Qt的学习(一)

1.什么是Qt Qt特指用来进行桌面应用开发&#xff08;电脑上写的程序&#xff09;涉及到的一套技术Qt无法开发网页前端&#xff0c;也不能开发移动应用。 客户端开发的重要任务&#xff1a;编写和用户交互的界面。一般来说和用户交互的界面&#xff0c;有两种典型风格&…...