GD32F30x Keil 环境下在 FreeRTOS 任务中使用浮点运算报 HardFault 异常的问题(二)

2023-05-16

文章目录

    • 1 问题描述
      • 1. 1 环境
      • 1. 2 问题
    • 2 参考资料
    • 3 来龙去脉
      • 3.1 定位问题
      • 3.2 xPortPendSVHandler
      • 3.3 EXC_RETURN
      • 3.4 寄存器
      • 3.5 探索真像
        • 3.5.1 浮点任务切换到空闲任务
        • 3.5.2 空闲任务切换到浮点任务
    • 4 解决办法
    • 5 总结

示例工程代码库地址如下:

  • Gitee
  • Git

1 问题描述

1. 1 环境

类别版本
系统WIN10
KeilKeil MDK 5.15.0
开发板星空派GD32F303开发板
GD32F30x 固件库V2.1.2
GD32F30x Keil 5 支持包V2.1.0
FreeRTOSV10.4.3-LTS-Patch-2

1. 2 问题

书接上回《FreeRTOS 任务中使用浮点运算报 HardFault 异常的问题(一)》
上回只描述了问题的表象,并巧合的使用编译优化暂时解决了问题;
本回则彻底深入探究其背后的来龙去脉。

2 参考资料

  • FreeRTOS - STM32F2 hard fault

  • FreeRTOS - Float and double cause hardfault handler on STM32F417
    幸运的在官网社区中检索到以上文章,其讨论的问题跟我极其相似,
    内心顿时燃起希望之火,文章问答中大意是说:

    /* xPortPendSVHandler 不能按照如下方式调用,
     * 当发生 PendSV 中断时需要直接调用 xPortPendSVHandler 
     * 经过验证确实如此,但是内心的疑惑并没有解除
    */
    void PendSV_Handler(void)
    {
        xPortPendSVHandler();
    }
    

    但至于为什么不能按照以上方式调用,具体解说只有如下一段话:

    When you use floating point instructions a bit gets set in one of the control registers, which in turn changes the stack frame when you enter interrupts. I would have to study the code output by the compiler to see exactly why you were getting the symptoms you were, but it is likely you actually have a problem in all cases but were only noticing when the stack frame was different and/or when floating point registers corruptions were causing faults.

    大意就是:当使用浮点指令时,控制寄存器中的一个位会被设置,进入中断时会根据该位改变堆栈帧,我必须去研究编译器输出的代码,以确切的了解为什么会出现该问题。

  • 使用Cortex-M4的FPU需要知道这个知识点!
    浅显易懂的介绍了 FPU 相关知识点,主要了解 lazy stacking 特性

  • The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors
    Contex-M4 全英文官方权威指南

3 来龙去脉

建议往下看之前,先了解 lazy stacking 特性。
接下来将始终围绕如下代码调用导致 HardFault 来进行解说:

void PendSV_Handler(void)
{
    xPortPendSVHandler();
}

整个解说过程按照倒叙,即从问题反推原因。

3.1 定位问题

在线调试参考文档:

  • 手把手教你查找stm32 HardFault_Handler调试及问题方法
  • 【STM32】Fault 类异常_记一次STM32中HardFault问题的调试解决

启动在线调试,全速运行,当进入 HardFault 断点后,查看异常原因
在这里插入图片描述
发现 HardFault 异常是上访造成的(FORCED 位),而真实的错误原因是由 IACCVIOL 引起的,查看官方权威指南,其描述如下:

  1. Violation to memory access protection, which is defined by MPU setup. For example, user application (unprivileged) trying to access privileged-only region. Stacked PC might able to locate the code that has caused the problem.
  2. Branch to non-executable regions, which could be
    caused by
    – simple coding error.
    – use of incorrect instruction for exception return
    – corruption of stack, which can affect stacked LR which is used for normal function returns, or corruption of stack frame which contains return address.
    – Invalid entry in exception vector table. For example, loading of an executable image for traditional ARM processor core into the memory, or exception happen
    before vector table in SRAM is set up.

大意如下:

  1. 违反了 MPU 定义的内存访问保护。例如,用户应用程序(非特权)试图访问特权区域。可通过 PC 堆栈找到问题的代码。
  2. 分支到了不可执行区域,这可能由以下原因引起
    - 简单的编码错误
    - 不正确的异常返回值 EXC_RETURN
    - 栈的损坏,它会影响用于正常函数返回的堆栈 LR,或者包含返回地址的栈帧的损坏
    - 无效的异常向量表条目,例如,将传统 ARM 处理器内核的可执行映像加载到内存中,或者在 SRAM 中的向量表建立之前发生异常。

查看 Call Stack 窗口,如下图:
在这里插入图片描述
发现是在空闲任务函数中触发了 HardFault,
根据断点一步一步调试直到 HardFault 异常,程序运行大致如下:

系统初始化 -> SVC 中断启动第一个任务 -> 运行浮点任务(优先级高于空闲任务) 
-> PendSV 中断切换任务 -> 运行空闲任务 -> PendSV 中断切换任务 
-> 切换不成功进入 HardFault 异常

一旦浮点任务中不使用 FPU,整个系统运行正常,
而空闲任务是 FreeRTOS 定义的任务,没有任何浮点运算。

猜想如下:

  • 可能从浮点任务切换到非浮点任务正常,从非浮点任务切换到浮点任务则不正常;
  • 可能仅第一次任务切换正常,第二次即以后的任务切换都不正常。

根据以上种种迹象表明,初步可以断定问题出在任务切换处理过程中

3.2 xPortPendSVHandler

任务切换处理函数 xPortPendSVHandler 解读如下:

__asm void xPortPendSVHandler( void ){
	extern uxCriticalNesting;
	extern pxCurrentTCB;
	extern vTaskSwitchContext;
	
	/* 指定堆栈八字节对齐 */
	PRESERVE8
	
	/* 读取 psp 的值到 r0 */
	mrs r0, psp
	/* 指令同步隔离。
	 * 清洗流水线,保证它前面的指令都执行完毕之后才执行它后面的指令
    */
	isb
	/* 获取当前任务控制块,即获取任务栈顶 */
	ldr	r3, =pxCurrentTCB
	ldr	r2, [r3]
	/* FPU 处理,检测 r14(LR) 即 EXC_RETURN 的 Bit[4] 是否为 0,
	 * 若为 0 表明线程使用了 FPU,当对 S16 - S31 进行处理后,
	 * 内核会自动对 S0 - S15 和 FPSCR 寄存器值进行入栈操作
	 * (需要了解 lazy stacking 特性,才能理解此处过程)
	 */
	tst r14, #0x10 //按位与运算,运算结果为 1,Z = 0,运算结果为 0,Z = 1
	it eq //如果 Z == 1,则执行下面语句
	vstmdbeq r0!, {s16-s31} // 把 s16-s31 入栈
	/* 保存内核余下未自动保存的寄存器 */
	stmdb r0!, {r4-r11, r14}
	/* 保存当前任务栈顶,把栈顶指针入栈 */
	str r0, [r2]
	stmdb sp!, {r3}
	/* 屏蔽 FreeRTOS 能控制的所有中断 */
	mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
	msr basepri, r0
	/* 数据同步隔离。
	 * 仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令 
	*/
	dsb
	isb
	/* 执行任务上下文切换 */
	bl vTaskSwitchContext
	/* 使能已屏蔽的中断 */
	mov r0, #0
	msr basepri, r0
	/* 恢复任务控制块指向的栈顶 */
	ldmia sp!, {r3}
	/* 获取当前栈顶 */
	ldr r1, [r3]
	ldr r0, [r1]
	/* 出栈*/
	ldmia r0!, {r4-r11, r14}
	/* FPU 相关寄存器出栈*/
	tst r14, #0x10
	it eq
	vldmiaeq r0!, {s16-s31}
	/* 更新 PSP 指针 */
	msr psp, r0
	isb
	/* 跳转到 r14 地址处 */
	bx r14
}

整个处理逻辑如下:
当前任务的寄存器值入栈 -> 切换任务上下文 -> 新任务的寄存器值出栈 -> 跳转到新任务

3.3 EXC_RETURN

当处理器进入异常处理程序或中断服务程序 (ISR) 时,链接寄存器 (LR) 的值更新为 EXC_RETURN 的值。当使用 BX、POP 或内存加载指令(LDR 或 LDM)将该值加载到程序计数器(PC)时,该值用于触发异常返回机制。具体位说明描述如下:
在这里插入图片描述
在这里插入图片描述

3.4 寄存器

R0 - R3、R12、LR 和 PSR 被称为调用者保存寄存器,即进入异常中断前这些寄存器已自动入栈保存

官方权威指南有如下描述:

In order to allow a C function to be used as an exception handler, the exception
mechanism needs to save R0 to R3, R12, LR, and PSR at exception entrance automatically, and restore them at exception exit under the control of the processor’s hardware.In this way when returned to the interrupted program, all the registers would have the same value as when the interrupt entry sequence started. In addition, since the value of the return address (PC) is not stored in LR as in normal C function calls (the exception mechanism puts an EXC_RETURN code in LR at exception entry, which is used in exception return), the value of the return address also needs to be saved by the exception sequence. So in total eight registers need to be saved during the exception handling sequence on the Cortex-M3 or Cortex-M4 processors without a floating point unit.

谷歌翻译如下:

为了允许将 C 函数用作异常处理程序,异常机制需要在异常入口处自动保存R0到R3、R12、LR、PSR,并在处理器硬件的控制下在异常出口处恢复它们。这样当返回到被中断程序时,所有的寄存器都一样 值作为中断进入序列开始时的值。 此外,由于返回地址 (PC) 的值不像普通 C 函数调用那样存储在 LR 中(异常机制在异常入口处将 EXC_RETURN 代码放在 LR 中,用于异常返回),所以 返回地址也需要通过异常序列保存。 因此,在没有浮点单元的 Cortex-M3 或 Cortex-M4 处理器上,在异常处理序列期间总共需要保存八个寄存器。

没有使用 FPU 时的自动入栈处理如下图:
在这里插入图片描述
使用了 FPU 时自动入栈处理如下图:
在这里插入图片描述
寄存器说明如下:

寄存器说明
R0 - R3、R12通用寄存器
LR(R14)Link Register 链接寄存器,用于在调用函数或子例程时保存返回地址,另外在异常处理过程中,保存 EXC_RETURN 异常返回值
Return Address记录函数返回地址,即程序计数器 PC 值
xPSRProgram Status Register 程序状态寄存器
S0 - S15单精度浮点通用寄存器 (“S” for single precision)
FPSCRFloating point status and control register 浮点状态和控制寄存器
FPCARFloating point context address register 浮点内容地址寄存器

3.5 探索真像

3.5.1 浮点任务切换到空闲任务

首先在浮点任务运行时发生 PendSV 中断,进入 PendSV_Handler,寄存器值如下:
在这里插入图片描述
R14(LR) = 0xFFFFFFED,Bit4 = 0,表明使用了 FPU,符合预期。
进入 xPortPendSVHandler,执行到 tst r14, #0x10 语句,寄存器值如下:
在这里插入图片描述
R14(LR) = 0x08000767,恰好 Bit4 = 0,表明使用了 FPU 指令,执行 vstmdbeq r0!, {s16-s31} 语句,入栈 FPU 相关寄存器值,继而触发内核对 S0 - S15、FPSCR 寄存器的入栈,紧接着把 R14(LR) = 0x8000767 的值入栈保存,最终把浮点任务的所有寄存器入栈。

0x08000767 是 xPortPendSVHandler 函数的返回地址(0x08000766 + 1,Bit0 为 1 表明是 thumb 状态,16位指令),如下图:
在这里插入图片描述
紧接着从空闲任务的堆栈中恢复相关寄存器值,此时 R14(LR) = 0xFFFFFFFD,最后执行 bx r14 语句(xPortPendSVHandler 根本不会返回到 PendSV_Handler),触发 PendSV 异常返回机制,任务切换成功,开始执行空闲任务。

至于首次运行空闲任务时 R14(LR) = 0xFFFFFFFD 的设置代码如下:

/* Constants required to set up the initial stack. */
#define portINITIAL_XPSR                      ( 0x01000000 )
#define portINITIAL_EXC_RETURN                ( 0xfffffffd )

/*
 * See header file for description.
 */
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
                                     TaskFunction_t pxCode,
                                     void * pvParameters )
{
    /* Simulate the stack frame as it would be created by a context switch
     * interrupt. */

    /* Offset added to account for the way the MCU uses the stack on entry/exit
     * of interrupts, and to ensure alignment. */
    pxTopOfStack--;

    *pxTopOfStack = portINITIAL_XPSR;                                    /* xPSR */
    pxTopOfStack--;
    *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) prvTaskExitError;                    /* LR */

    /* Save code space by skipping register initialisation. */
    pxTopOfStack -= 5;                            /* R12, R3, R2 and R1. */
    *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */

    /* A save method is being used that requires each task to maintain its
     * own exec return value. */
    pxTopOfStack--;
    *pxTopOfStack = portINITIAL_EXC_RETURN;

    pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */

    return pxTopOfStack;
}

以上函数被调用过程如下:
vTaskStartScheduler -> xTaskCreate( prvIdleTask)-> prvInitialiseNewTask -> pxPortInitialiseStack

3.5.2 空闲任务切换到浮点任务

待从空闲任务运行过程中进入 PendSV 中断,如下图:
在这里插入图片描述
R14(LR) = 0xFFFFFFFD,Bit4 = 1,空闲任务没有使用 FPU,符合预期。
进入 xPortPendSVHandler,执行到 tst r14, #0x10 语句,寄存器值如下:
在这里插入图片描述
R14(LR) = 0x08000767,Bit4 = 0,执行 vstmdbeq r0!, {s16-s31} 语句,入栈 FPU 相关寄存器值(但此处的操作是由于 R14(LR) 此时已不是 EXC_RETURN,导致误判入栈了 FPU 寄存器值,其 tst r14, #0x10 语句判断时的值应该是进入 xPortPendSVHandler 函数前的 0xFFFFFFFD,该值表明没有使用 FPU,而不是该函数的 Return Address),紧接着把 R14(LR) = 0x8000767 的值入栈保存,最终把空闲任务的所有寄存器入栈。

紧接着从浮点任务的堆栈中恢复相关寄存器值,R14(LR) = 0x8000767,最后执行 bx r14 语句,从结果看是从 xPortPendSVHandler 函数返回到了 PendSV_Handler 函数,如下图:
在这里插入图片描述
当执行 POP {r4,pc} 语句时,由于是从空闲任务进入的 PendSV 中断,那么 PUSH {r4,lr} 语句中的 lr = 0xFFFFFFFD,Bit4 = 1,表明没有使用 FPU,而实际上切换到浮点任务时,希望 Bit4 = 0,即 lr = 0xFFFFFFED,所以即使 xPortPendSVHandler 函数内部结尾处恰好因为其 Return Address 地址 lr = 0x08000767,Bit4 = 0 对 S16 - S31 进行了出栈处理,但最终 PendSV 异常返回时却因为 Bit4 = 1,而不会对 S0 - S15、FPSCR 寄存器进行出栈,导致 FPU 相关寄存器值恢复不完整,才会出现本节开头描述的使用了浮点运算就会进入 HardFault 异常的问题。

因此,在从空闲任务切换到浮点任务过程中,如下 PendSV 中断处理实现:

void PendSV_Handler(void)
{
    xPortPendSVHandler();
}

将导致寄存器相关操作出现如下问题:

  • 空闲任务没有使用 FPU 却入栈了 S16 - S31,也错把 xPortPendSVHandler 函数 Return Address 当成 EXC_RETURN 进行了入栈保存;
  • 浮点任务巧合的出栈了 S16 - S31,但在退出 PendSV 异常中断时,EXC_RETURN 的值是从空闲任务进入 PendSV 中断的 0xFFFFFFFD,而实际应该是 0xFFFFFFED,导致 S0 - S15、FPSCR 没有出栈,即浮点相关寄存器出栈不完全。

4 解决办法

以下方法,都是保证发生 PendSV 中断时,直接调用 xPortPendSVHandler。
操作时取其中一种方法即可。

  • 《FreeRTOS 任务中使用浮点运算报 HardFault 异常的问题(一)》
    按照以上博文中的处理方法启用编译优化,即不使用编译优化 Level 0 等级,
    启用编译优化生成的最终代码丢弃了 PendSV_Handler 函数,发生中断直接调用 xPortPendSVHandler 函数,反汇编也能看出端倪。

    使用编译优化 Level 0 等级反汇编代码如下:
    在这里插入图片描述
    使用编译优化 Level 1 - 3 等级反汇编代码如下:
    在这里插入图片描述
    但该方法有局限性,当 PendSV_Handler 函数内部除了调用 xPortPendSVHandler 函数之外还有其它代码,会因编译无法优化而失效。

  • FreeRTOSConfig.h 文件中增加如下宏定义

    #define vPortSVCHandler 		SVC_Handler
    #define xPortPendSVHandler 		PendSV_Handler
    #define xPortSysTickHandler 	SysTick_Handler
    

    并删除 gd32f30x_it.c 文件中调用 FreeRTOS 内核 vPortSVCHandler、xPortPendSVHandler、xPortSysTickHandler 的代码,以及屏蔽函数 SVC_Handler、PendSV_Handler、SysTick_Handler 的定义。

  • startup_gd32f30x_hd.s 启动文件中进行如下替换

    原函数名新函数名
    SVC_HandlervPortSVCHandler
    PendSV_HandlerxPortPendSVHandler
    SysTick_HandlerxPortSysTickHandler

    并删除 gd32f30x_it.c 文件中调用 FreeRTOS 内核 vPortSVCHandler、xPortPendSVHandler、xPortSysTickHandler 的代码。

5 总结

总算拨云见日,理清了来龙去脉,
一路过来,面对问题刨根问底的心态让我坚持了下来,
而坚持就会有收获,不说别的至少解决了心中疑惑。

一切没有解决的问题都是大问题,一切解决了的问题都是小问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GD32F30x Keil 环境下在 FreeRTOS 任务中使用浮点运算报 HardFault 异常的问题(二) 的相关文章

  • PuTTY在远程连接服务器之后,经常会断线提示“Software caused connection abort”,而且经常在很短的时间内就失去连接

    解决方案如下 xff1a 第一步 xff1a 设置服务器 1 修改服务器中 etc ssh sshd config 文件 xff0c 将LoginGraceTime的值设为0 xff0c TCPKeepAlive 设为yes 2 servi
  • Go语言循环语句

    Go语言循环语句 资料参考至菜鸟教程 在不少实际问题中有许多具有规律性的重复操作 xff0c 因此在程序中就需要重复执行某些语句 以下为大多编程语言循环程序的流程图 xff1a Go语言提供了以下几种类型循环处理语句 xff1a 循环类型描
  • Lua 协同程序(coroutine)

    Lua 协同程序 coroutine 参考文章 xff1a 菜鸟教程 https zhuanlan zhihu com p 480357405 https zhuanlan zhihu com p 76249973 Lua 协同程序 cor
  • Lua 变量

    Lua 变量 参考至菜鸟教程 变量在使用前 xff0c 需要在代码中进行声明 xff0c 即创建该变量 变量需要标识类型是因为编译程序执行代码之前需要知道如何给语句变量开辟存储区 xff0c 用于存储变量的值 Lua变量有三种类型 xff1
  • Lua 函数 - 可变参数

    Lua 函数 可变参数 参考至菜鸟教程 Lua函数可以接收可变数目的参数 xff0c 和C语言类似 xff0c 在函数参数列表中使用三点 表示函数有可变的参数 span class token keyword function span s
  • Lua 运算符 - 较为特殊部分

    Lua 运算符 较为特殊部分 参考至菜鸟教程 算术运算符 操作符描述实例 乘幂A 2 输出结果 100 负号 A 输出结果 10 整除运算符 gt 61 lua5 3 5 2 输出结果 2 在 lua 中 xff0c 用作除法运算 xff0
  • python(9):python循环打印进度条

    1 while 循环 Python的while循环可以打印进度条 xff0c 可以使用tqdm这个库来实现 tqdm是一个用于在Python中添加进度条的库 xff0c 它可以很容易地集成到while循环中 下面是一个简单的示例 xff0c
  • 平衡车直立PID调节总结

    苦战一周 xff0c 终于使平衡小车站了起来 xff0c PID无疑是我从学习板子至今遇到最困难的东西了 xff0c 并不是说它原理有多么复杂 xff0c 只是想让小车的效果更佳 xff0c 调参的过程无疑是漫长而艰难的 连续调了俩天的参数
  • Lua 字符串

    Lua 字符串 参考至菜鸟教程 字符串或串 String 是由数字 字母 下划线组成的一串字符 Lua 语言中字符串可以使用以下三种方式来表示 xff1a 单引号间的一串字符 双引号间的一串字符 与 间的一串字符 以上三种方式的字符串实例如
  • Lua 迭代器

    Lua 迭代器 参考文章 xff1a 菜鸟教程 https cloud tencent com developer article 2203215 迭代器 xff08 iterator xff09 是一种对象 xff0c 它能够用来遍历标准
  • Lua table(表)

    Lua table 表 参考至菜鸟教程 Lua table 使用关联型数组 xff0c 你可以用任意类型的值来作数组的索引 xff0c 但这个值不能是 nil Lua table 是不固定大小的 xff0c 你可以根据自己需要进行扩容 Lu
  • Lua 模块与包

    Lua 模块与包 参考至菜鸟教程 模块类似于一个封装库 xff0c 从 Lua 5 1 开始 xff0c Lua 加入了标准的模块管理机制 xff0c 可以把一些公用的代码放在一个文件里 xff0c 以 API 接口的形式在其他地方调用 x
  • Lua 文件I/O

    Lua 文件I O 参考至菜鸟教程 Lua I O 库用于读取和处理文件 分为简单模式 xff08 和C一样 xff09 完全模式 简单模式 xff08 simple model xff09 拥有一个当前输入文件和一个当前输出文件 xff0
  • Lua 错误处理

    Lua 错误处理 参考至菜鸟教程 程序运行中错误处理是必要的 xff0c 在我们进行文件操作 xff0c 数据转移及web service 调用过程中都会出现不可预期的错误 如果不注重错误信息的处理 xff0c 就会造成信息泄露 xff0c
  • Lua 调试(Debug)

    Lua 调试 Debug 参考至菜鸟教程 Lua 提供了 debug 库用于提供创建我们自定义调试器的功能 Lua 本身并未有内置的调试器 xff0c 但很多开发者共享了他们的 Lua 调试器代码 Lua 中 debug 库包含以下函数 x
  • Lua 垃圾回收

    Lua 垃圾回收 参考至菜鸟教程 Lua 采用了自动内存管理 这意味着你不用操心新创建的对象需要的内存如何分配出来 xff0c 也不用考虑在对象不再被使用后怎样释放它们所占用的内存 Lua运行了一个垃圾收集器来收集所有死对象 xff08 即
  • Lua 面向对象(详解)

    Lua 面向对象 xff08 详解 xff09 参考文章 xff1a https blog csdn net linxinfa article details 103254828 https zhuanlan zhihu com p 115
  • Lua实现矩阵的加减乘除

    Lua实现矩阵的加减乘除 参考文章 xff1a https blog csdn net qq 54180412 article details 122943327 https www bilibili com video BV1434y18
  • ubuntu系统配置大恒相机驱动并读取ros话题

    文章目录 0 说明1 安装大恒相机sdk1 1 下载1 2 安装sdk 用于配置ip和调试相机参数 1 电脑网卡配置 网卡固定ip 2 查看相机图像以及配置相机参数 2 安装ros驱动包 注 xff1a 大恒相机官方没ros驱动 2 0 正
  • C++类对象与Lua之间的交互

    C 43 43 类对象与Lua之间的交互 C语言与Lua进行交互 xff0c 我们可以相对轻易的做到 xff0c 但在实际应用中我们更加偏向于使用C 43 43 与Lua进行交互 xff0c 面向对象编程 关于C语言与Lua之间的调用交互实

随机推荐

  • C++与Lua交互实例 -- 矩阵的加减乘除(版本一)

    C 43 43 与Lua交互实例 矩阵的加减乘除 xff08 版本一 xff09 关于lua中封装的类模板以及相关知识可参考以下链接 xff1a https ufgnix0802 blog csdn net article details
  • C++与Lua交互实例 -- 矩阵的加减乘除(版本二)

    C 43 43 与Lua交互实例 矩阵的加减乘除 xff08 版本二 xff09 TIPS xff1a 关于使用矩阵的加减乘除测试C 43 43 与Lua的交互以及下面没讲述到的知识点可以阅读第一版 xff1a https blog csd
  • Windows下LuaBridge2.8的环境配置及简单应用

    Windows下LuaBridge2 8的环境配置及简单应用 LuaBridge2 8下载链接 xff1a https github com vinniefalco LuaBridge tags 关于Lua的环境配置可参考以下链接 xff0
  • Lua 开发过程中常见坑

    Lua 开发过程中常见坑 Lua next span class token keyword return span G span class token punctuation span span class token function
  • 私网与公网地址转换

    私网与公网地址转换 NAT概述NAT功能静态NAT动态NATEASYIP xff08 多个内网地址对一个接口 xff09 PAT端口多路复用 NAT概述 NAT xff08 Network Address Translation xff0c
  • VWmare安装CentOS7及连接Xshell超详细过程(图文)

    VWmare安装CentOS7及连接Xshell超详细过程 xff08 图文 xff09 前言一 准备工作二 安装虚拟机过程 1 选择文件 xff0c 新建虚拟机 2 选择配置类型 3 自定义硬件配置 4 进入系统安装界面 二 连接Xshe
  • rpm与yum

    rpm与yum 前言一 应用程序与命令系统的关系二 典型应用程序的目录结构三 常见的软件封装类型四 rpm 1 概述 2 命令概述 3 查询rpm软件包信息 查询已安装的rpm软件信息 查询未安装的rpm软件包文件中的信息 安装 升级 卸载
  • Linux用户与权限管理

    Linux用户与权限管理 前言一 管理用户账号 1 用户账号概述 用户标识UID xff08 User IDentity xff0c 用户标识号 xff09 用户账号文件 2 用户账号管理 添加用户账号 xff08 useradd xff0
  • yum源仓库

    yum源仓库 前言一 yum介绍一 yum源的提供方式 1 配置本地仓库 2 配置ftp源 三 yum命令 1 yum常用的操作 2 搜索软件包命令 3 安装升级 4 软件卸载 5 yun history命令 总结 前言 yum相对与rpm
  • C++常见问题的总结

    1 C语言跟C 43 43 的关系 xff1a xff08 1 xff09 C语言跟C 43 43 的本质区别 xff1a 1 xff09 c更倾向于面向过程 xff0c c 43 43 是面向过程 43 面向对象 43 泛型编程 2 xf
  • Nginx Rewrite

    Nginx Rewrite 前言一 nginx rewrite概述 1 概述 2 跳转场景 3 跳转实现 4 rewrite实际场景 nginx跳转需要的实现方式 rewrite放在server if location 段中 对域名或参数字
  • Dockerfile概念简介

    Dockerfile概念简介 前言一 dockerfile概念二 Docker镜像的创建 1 基于现有镜像创建 2 基于本地模板创建 3 基于dockerfile创建 dockerfile结构 xff08 四部分 xff09 构建镜像命令
  • 【云原生之k8s】k8s基础详解

    云原生之k8s k8s基础详解 前言一 kubernetes介绍 1 kubernetes简介 2 应用部署方式的演变 二 kubernetes组件 1 kubernetes架构 2 master组件 apiserver controlle
  • 【云原生之k8s】kubeadm搭建k8s集群

    云原生之k8s kubeadm搭建k8s集群 前言一 集群介绍 1 集群搭建方法 2 集群架构 二 集群部署 1 环境部署 所有节点 xff0c 关闭防火墙规则 xff0c 关闭selinux xff0c 关闭swap交换 修改主机名 xf
  • 【云原生之k8s】k8s管理工具kubectl详解

    云原生之k8s k8s管理工具kubectl详解 前言一 陈述式管理 1 陈述式资源管理方法 2 k8s相关信息查看 查看版本信息 查看节点信息 查看资源对象简写 查看集群信息 配置kubectl自动补全 查看日志 基本信息查看1 查看ma
  • 关于结构体指针与STM32外设的笔记

    96 define RCC RCC TypeDef RCC BASE xff09 96 逐步分解这句代码的含义 RCC TypeDef RCC BAS 其中 RCC BAS定义为 define RCC BASE AHBPERIPH BASE
  • visual studio与visual c++ 6.0的区别

    xfeff xfeff Visual Studio支持多种语言 xff0c Visual C 43 43 6 0 只支持C和C 43 43 Visual C 43 43 6 0 是Visual Studio 6 0的一个组成部分 xff0c
  • GD32F303 移植 FreeRTOS

    文章目录 1 准备工作1 1 软件版本1 2 源码下载1 3 基础工程 3 FreeRTOS 移植3 1 复制需要的内核文件3 2 添加文件到 Keil 工程3 3 添加 FreeRTOSConfig h 内核配置文件3 4 配置任务调度相
  • FreeRTOS 之 heap_4 踩坑之路

    参考博文连接 xff1a FreeRTOS系列 heap 4 c 内存管理分析FreeRTOS Heap 1 2 3 4 5 比较 示例工程代码库地址如下 xff1a GiteeGit 1 问题描述 博主在使用 heap 4 的 pvPor
  • GD32F30x Keil 环境下在 FreeRTOS 任务中使用浮点运算报 HardFault 异常的问题(二)

    文章目录 1 问题描述1 1 环境1 2 问题 2 参考资料3 来龙去脉3 1 定位问题3 2 xPortPendSVHandler3 3 EXC RETURN3 4 寄存器3 5 探索真像3 5 1 浮点任务切换到空闲任务3 5 2 空闲