STM32/GD32——FreeRTOS任务管理与相关机制

芯片选型

Ciga Device — GD32F470系列

任务管理

任务处理API

操作

API

动态任务创建

xTaskCreate

任务删除

vTaskDelete

静态任务创建

vTaskCreateStatic

挂起任务

vTaskSuspend

恢复任务

vTaskResume

任务创建

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName,
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask );
  1. TaskFunction_t pxTaskCode: 表示的这个任务执行的函数,函数格式为:
    // typedef void (* TaskFunction_t)( void * );
    void task(void *pvParameters);
  2. void * const pvParameters: 表示任务执行函数的参数,也就是上面函数中的参数部分
  3. const char * const pcName: 表示给这个任务起的名字。
  4. const configSTACK_DEPTH_TYPE usStackDepth: 表示启动任务所需要的栈的大小,单位是byte。通常根据任务的复杂度来进行设置。
  5. UBaseType_t uxPriority: 表示任务的优先级。

以下是关于FreeRTOS任务优先级的几个要点:

  • 数值越大,优先级越高:在FreeRTOS中,任务的优先级数值越大,优先级越高。例如,优先级为1的任务比优先级为0的任务具有更高的优先级。
  • 优先级为0的任务是最低优先级:通常称为IDLE任务或空闲任务。该任务在没有其他任务需要运行时执行,确保系统在空闲时也有任务可以运行。
  • 相同优先级的任务采用时间片轮转调度:当有多个任务具有相同优先级时,FreeRTOS会使用时间片轮转调度算法来平均分配CPU时间。每个任务在一轮时间片内执行一段时间,然后切换到下一个任务。
  • 高优先级任务可以抢占低优先级任务:如果一个高优先级任务就绪并准备好运行,它可以抢占当前正在运行的低优先级任务,从而提供更好的实时性。
  • 优先级反映任务调度顺序:任务的优先级决定了任务调度的顺序。当有多个任务就绪并等待运行时,任务调度器会选择具有最高优先级的就绪任务来执行。

需要注意的是,任务优先级的设置应根据应用的实时需求和任务间的相对重要性进行合理的规划。过多或过少的优先级级别可能导致调度问题或资源竞争。在任务优先级设置时,需要综合考虑系统的响应性、任务的相互影响和资源的使用情况等因素。

  1. TaskHandle_t * const pxCreatedTask: 任务句柄。可以理解为任务的实例。

创建任务的返回值说明:

BaseType_t类型为创建任务的返回值,结果为pdPASS或者pdFAIL(成功或者失败)。

动态任务创建(常用)

  • 此步骤可以省略。因为默认值为1。但是需要了解这个配置。配置FreeRTOS.h中的configSUPPORT_DYNAMIC_ALLOCATION为1.
#ifndef configSUPPORT_DYNAMIC_ALLOCATION
    /* Defaults to 1 for backward compatibility. */
    #define configSUPPORT_DYNAMIC_ALLOCATION    1
#endif
  • 定义任务执行函数。
void task(void *pvParameters) {
    // TODO: 任务的业务逻辑
}
  • 调用任务创建逻辑。
xTaskCreate(task1, "task1", 64, NULL, 2, &task1_handler);
点灯示例 

点亮PE3和PD7的灯,通过两个不同的任务,进行灯的闪烁控制,观察效果。

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"

TaskHandle_t            start_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;

void task1(void *pvParameters) {
    while(1) {
        vTaskDelay(300);
        gpio_bit_set(GPIOE, GPIO_PIN_3);
        vTaskDelay(300);
        gpio_bit_reset(GPIOE, GPIO_PIN_3);
    }
}

void task2(void *pvParameters) {
    while(1) {
        vTaskDelay(1000);
        gpio_bit_set(GPIOD, GPIO_PIN_7);
        vTaskDelay(1000);
        gpio_bit_reset(GPIOD, GPIO_PIN_7);
    }
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

    xTaskCreate(task1, "task1", 64, NULL, 2, &task1_handler);
    xTaskCreate(task2, "task2", 64, NULL, 2, &task2_handler);

    vTaskDelete(start_handler);

    taskEXIT_CRITICAL();
}

void GPIO_config() {
    // 1. 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOE);
    // 2. 配置GPIO 输入输出模式
    gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
    // 3. 配置GPIO 模式的操作方式
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);

    // 1. 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOD);
    // 2. 配置GPIO 输入输出模式
    gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
    // 3. 配置GPIO 模式的操作方式
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_7);
}


int main(void)
{
    systick_config();
    GPIO_config();

    xTaskCreate(start_task, "start_task", 128, NULL, 1, &start_handler);
    vTaskStartScheduler();

    while(1) {}
}

静态任务创建(用的不多)

1. 配置FreeRTOS.h中的configSUPPORT_STATIC_ALLOCATION为1.

#i#ifndef configSUPPORT_STATIC_ALLOCATION
    /* Defaults to 0 for backward compatibility. */
    #define configSUPPORT_STATIC_ALLOCATION    1
#endif

2. 实现内存管理函数vApplicationGetIdleTaskMemoryvApplicationGetTimerTaskMemory

StaticTask_t idle_task_tcb;
StackType_t  idle_task_stack[configMINIMAL_STACK_SIZE];

StaticTask_t timer_task_tcb;
StackType_t  timer_task_stack[configTIMER_TASK_STACK_DEPTH];

void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                    StackType_t ** ppxIdleTaskStackBuffer,
                                    uint32_t * pulIdleTaskStackSize )
{
    * ppxIdleTaskTCBBuffer = &idle_task_tcb;
    * ppxIdleTaskStackBuffer = idle_task_stack;
    * pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}


void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
                                     StackType_t ** ppxTimerTaskStackBuffer,
                                     uint32_t * pulTimerTaskStackSize )
{
    * ppxTimerTaskTCBBuffer = &timer_task_tcb;
    * ppxTimerTaskStackBuffer = timer_task_stack;
    * pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}

3. 定义任务执行函数。

void task(void *pvParameters) {
    // TODO: 任务的业务逻辑
}

4. 调用任务创建逻辑

TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,
                               const char * const pcName,
                               const uint32_t ulStackDepth,
                               void * const pvParameters,
                               UBaseType_t uxPriority,
                               StackType_t * const puxStackBuffer,
                               StaticTask_t * const pxTaskBuffer )
  • StackType_t * const puxStackBuffer:任务栈大小。得自己指定,不可更改。
  • StaticTask_t * const pxTaskBuffer:任务控制块,用来存储任务的堆栈空间,任务的状态和优先级等。
  • 返回值为任务的句柄。
点灯示例
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"


StaticTask_t idle_task_tcb;
StackType_t  idle_task_stack[configMINIMAL_STACK_SIZE];

StaticTask_t timer_task_tcb;
StackType_t  timer_task_stack[configTIMER_TASK_STACK_DEPTH];

TaskHandle_t            start_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;

#define TASK_STACK_SIZE   128
StackType_t     task_stack[TASK_STACK_SIZE];
StaticTask_t    task_tcb;

#define TASK1_STACK_SIZE   64
StackType_t     task1_stack[TASK1_STACK_SIZE];
StaticTask_t    task1_tcb;

#define TASK2_STACK_SIZE   64
StackType_t     task2_stack[TASK2_STACK_SIZE];
StaticTask_t    task2_tcb;

void task1(void *pvParameters) {
    while(1) {
        vTaskDelay(300);
        gpio_bit_set(GPIOE, GPIO_PIN_3);
        vTaskDelay(300);
        gpio_bit_reset(GPIOE, GPIO_PIN_3);
    }
}

void task2(void *pvParameters) {
    while(1) {
        vTaskDelay(1000);
        gpio_bit_set(GPIOD, GPIO_PIN_7);
        vTaskDelay(1000);
        gpio_bit_reset(GPIOD, GPIO_PIN_7);
    }
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

		task1_handler = xTaskCreateStatic(task1, "task1", TASK1_STACK_SIZE, NULL, 2, task1_stack, &task1_tcb);
		task2_handler = xTaskCreateStatic(task2, "task2", TASK2_STACK_SIZE, NULL, 2, task2_stack, &task2_tcb);

    vTaskDelete(start_handler);

    taskEXIT_CRITICAL();
}

void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                    StackType_t ** ppxIdleTaskStackBuffer,
                                    uint32_t * pulIdleTaskStackSize )
{
    * ppxIdleTaskTCBBuffer = &idle_task_tcb;
    * ppxIdleTaskStackBuffer = idle_task_stack;
    * pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}


void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
                                     StackType_t ** ppxTimerTaskStackBuffer,
                                     uint32_t * pulTimerTaskStackSize )
{
    * ppxTimerTaskTCBBuffer = &timer_task_tcb;
    * ppxTimerTaskStackBuffer = timer_task_stack;
    * pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}

void GPIO_config() {
    // 1. 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOE);
    // 2. 配置GPIO 输入输出模式
    gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
    // 3. 配置GPIO 模式的操作方式
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);

    // 1. 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOD);
    // 2. 配置GPIO 输入输出模式
    gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
    // 3. 配置GPIO 模式的操作方式
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_7);
}


int main(void)
{
    systick_config();
    GPIO_config();

		start_handler = xTaskCreateStatic(start_task, "start_task", TASK_STACK_SIZE, NULL, 2, task_stack, &task_tcb);
    vTaskStartScheduler();

    while(1) {}
}

动态任务与静态任务的区别

在FreeRTOS中,任务可以使用静态分配方式或动态分配方式创建。这两种方式在任务创建和内存管理方面存在一些区别。

静态任务:

  1. 静态任务是在编译时分配内存的任务。
  2. 在创建静态任务时,需要提前为任务分配足够的内存空间。
  3. 静态任务的内存分配是固定的,任务的内存大小在编译时确定,并在运行时保持不变。
  4. 静态任务使用 xTaskCreateStatic() 函数创建。

动态任务:

  1. 动态任务是在运行时分配内存的任务。
  2. 在创建动态任务时,不需要提前为任务分配内存空间,而是在运行时使用动态内存分配函数进行分配。
  3. 动态任务的内存分配是动态的,任务的内存大小可以根据需要进行调整。
  4. 动态任务使用 xTaskCreate() 函数创建。

区别:

  1. 静态任务的内存分配是在编译时完成,而动态任务的内存分配是在运行时完成。
  2. 静态任务需要手动为任务分配内存空间,而动态任务会自动进行内存分配和释放。
  3. 静态任务的内存大小在编译时确定,不能在运行时改变;而动态任务的内存大小可以在运行时进行动态调整。
  4. 静态任务对内存的使用是固定的,不会有内存碎片的问题;而动态任务的内存使用可能存在碎片化的风险。

选择静态任务还是动态任务取决于具体的应用需求和系统约束。

静态任务在一些资源有限的系统中更常用,可以避免动态内存分配的开销和内存碎片问题。

而动态任务可以在运行时根据需要动态分配内存,灵活性更高。

通常采用动态任务创建更多。

任务优先级

任务的优先级等级是在FreeRTOSConfig.h中定义的,configMAX_PRIORITIES定义了最大任务优先级值,默认值为5。那么优先级取值为0到4。数值越大优先级越高。

我们采用日志打印的方式进行验证,开启两个任务,分别打印日志,开启任务时设置不同优先级进行测试,以下是示例代码。

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "usart0.h"

TaskHandle_t            start_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;

void task1(void *pvParameters) {
    while(1) {
				printf("task1\r\n");
        vTaskDelay(1000);
    }
}

void task2(void *pvParameters) {
    while(1) {
				printf("task2\r\n");
        vTaskDelay(1000);
    }
}

void Usart0_recv(uint8_t *data, uint32_t len) {
		printf("recv: %s\r\n", data);
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

    xTaskCreate(task1, "task1", 64, NULL, 2, &task1_handler);
    xTaskCreate(task2, "task2", 64, NULL, 3, &task2_handler);

    vTaskDelete(start_handler);

    taskEXIT_CRITICAL();
}

int main(void)
{
    systick_config();
    Usart0_init();

	printf("start\r\n");
    xTaskCreate(start_task, "start_task", 128, NULL, 1, &start_handler);
    vTaskStartScheduler();

    while(1) {}
}

任务操作

任务挂起

// 挂起任务
vTaskSuspend(xTaskHandle);

任务恢复

// 恢复任务
vTaskResume(xTaskHandle);

任务删除

BaseType_t xTaskDelete(TaskHandle_t xTaskToDelete);

其中,xTaskToDelete 是要删除的任务的句柄(TaskHandle_t 类型)。可以将任务的句柄传递给 xTaskDelete() 函数,以删除指定的任务。

任务删除的几个要点如下:

  1. 当前任务的删除:如果在任务的执行过程中调用 xTaskDelete(NULL),表示删除当前任务。当前任务将被立即删除,并且不会继续执行后续代码
  2. 删除其他任务:如果要删除除当前任务之外的任务,需要传递相应任务的句柄给 xTaskDelete() 函数。这样,指定的任务将被删除。
  3. 任务删除的影响:任务删除后,其占用的资源(如堆栈、任务控制块等)会被释放,其他任务可以继续执行。删除任务时需要注意任务间的同步和资源释放,以避免产生悬空指针或资源泄漏等问题。
  4. 返回值:xTaskDelete() 函数的返回值是 BaseType_t 类型,表示任务删除成功与否。如果任务删除成功,返回值为 pdPASS。如果任务删除失败,返回值为 errTASK_NOT_DELETED。
  5. 需要确保配置了如下宏:#define INCLUDE_vTaskDelete 1

需要注意的是,在任务删除之前,需要确保不再需要该任务的执行,并且合理处理任务间的同步和资源释放。不正确地删除任务可能会导致未定义行为和系统不稳定性。

代码示例

通过按键来操作任务的操作,点击按钮挂起任务,恢复任务。

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "usart0.h"

TaskHandle_t            start_handler;
TaskHandle_t            task_key_handler;
TaskHandle_t            task1_handler;
TaskHandle_t            task2_handler;

void task1(void *pvParameters) {
    while(1) {
        printf("task1\r\n");
        vTaskDelay(1000);
    }
}

void task2(void *pvParameters) {
    while(1) {
        printf("task2\r\n");
        vTaskDelay(1000);
    }
}

void task_key(void *pvParameters) {
    uint32_t flag = 0;
    FlagStatus pre_state = RESET;
    BaseType_t result;
    while(1) {
        FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
        if(SET == state && pre_state == RESET) {
            // 当前高电平, 上一次为低电平,按下
            pre_state = state;

            if(flag == 0) {
                // 挂起
                vTaskSuspend(task1_handler);
            } else if(flag == 1) {
                // 恢复
                vTaskResume(task1_handler);
            }
            flag++;
            if(flag > 1) flag = 0;
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;
        }
        vTaskDelay(20);
    }
}

void Usart0_recv(uint8_t *data, uint32_t len) {
    printf("recv: %s\r\n", data);
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

    xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
    xTaskCreate(task1, "task1", 64, NULL, 2, &task1_handler);
    xTaskCreate(task2, "task2", 64, NULL, 3, &task2_handler);

    vTaskDelete(start_handler);

    taskEXIT_CRITICAL();
}

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}



int main(void)
{
    //NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    GPIO_config();
    Usart0_init();

    printf("start\r\n");
    xTaskCreate(start_task, "start_task", 128, NULL, 1, &start_handler);
    vTaskStartScheduler();

    while(1) {}
}

任务相关机制

任务控制块

任务调度机制

临界区

内存管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/465549.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vulhub中GIT-SHELL 沙盒绕过漏洞复现(CVE-2017-8386)

GIT-SHELL 沙盒绕过&#xff08;CVE-2017-8386&#xff09;导致任意文件读取、可能的任意命令执行漏洞。 测试环境 为了不和docker母机的ssh端口冲突&#xff0c;将容器的ssh端口设置成3322。本目录下我生成了一个id_rsa&#xff0c;这是ssh的私钥&#xff0c;连接的时候请指…

固态硬盘有缓存和没缓存有什么区别

固态硬盘&#xff08;SSD&#xff09;已经成为现代计算机的重要组成部分&#xff0c;它们提供了比传统机械硬盘更快的读写速度&#xff0c;从而显著提升了操作系统的运行速度和应用程序的加载效率。 其中&#xff0c;缓存&#xff08;Cache&#xff09;是固态硬盘中一个重要的…

【SpringCloud】使用Seata实现分布式事务

目录 一、Seata 框架的需求背景二、Seata 事务模式与架构2.1 Seata 组成2.2 Seata 事务模式 三、Seata 实战演示3.1 部署 Seata Server3.1.1 下载 Seata Server3.1.2 更改 Seata Server 配置3.1.3 创建 Seata Server 所需的数据库、数据库表3.1.4 启动 Seata Server 3.2 Seata …

ROS2从入门到精通1-1:详解ROS2话题通信机制与自定义消息

目录 0 专栏介绍1 话题通信模型2 话题模型实现(C)3 话题模型实现(Python)4 自定义消息 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 &#x1f680;详情&a…

【最新版源码】快递平台独立版小程序源码|带cps推广营销流量主+前端

源码介绍&#xff1a; 快递代发快递代寄寄件小程序可以对接易达云洋一级总代 快递小程序&#xff0c;接入云洋/易达物流接口&#xff0c;支持选择快递公司&#xff0c;三通一达&#xff0c;极兔&#xff0c;德邦等&#xff0c;功能成熟 如何收益: 1.对接第三方平台成本大约4…

CoAP计算机协议,应用于物联网

什么是CoAP协议&#xff1f; CoAP&#xff08;Constrained Application Protocol&#xff0c;受限应用协议&#xff09;是一种专为物联网&#xff08;IoT&#xff09;设备和资源受限网络设计的应用层协议。它的诞生也是由于物联网设备大多都是资源限制型的&#xff0c;比如 CP…

【GPT-SOVITS-02】GPT模块解析

说明&#xff1a;该系列文章从本人知乎账号迁入&#xff0c;主要原因是知乎图片附件过于模糊。 知乎专栏地址&#xff1a; 语音生成专栏 系列文章地址&#xff1a; 【GPT-SOVITS-01】源码梳理 【GPT-SOVITS-02】GPT模块解析 【GPT-SOVITS-03】SOVITS 模块-生成模型解析 【G…

Java之SpringBoot基础夯实——八股文【2024面试题案例代码】

1、什么是 Spring Boot&#xff1f; Spring Boot 是一个开源的Java开发框架&#xff0c;由Pivotal团队开发&#xff0c;其核心目标是简化新Spring应用的初始搭建和开发流程。它以Spring框架为基础&#xff0c;通过自动配置和约定优于配置的原则&#xff0c;极大程度地减少了手…

HarmonyOS(鸿蒙)ArkUI组件

方舟开发框架&#xff08;简称ArkUI&#xff09;为HarmonyOS应用的UI开发提供了完整的基础设施&#xff0c;包括简洁的UI语法、丰富的UI功能&#xff08;组件、布局、动画以及交互事件&#xff09;&#xff0c;以及实时界面预览工具等&#xff0c;可以支持开发者进行可视化界面…

嵌入式学习之Linux系统编程篇笔记——系统编程初探

配套视频学习链接&#xff1a;https://www.bilibili.com/video/BV1zV411e7Cy?p2&vd_sourced488bc722b90657aaa06a1e8647eddfc 目录 Linux系统编程的基本认识 什么是Linux系统编程? 什么是系统编程 系统编程的作用 怎么学习Linux系统编程? Linux系统编程基本程序框…

马斯克大模型Grok-1已开源,目前为止最大的开源大语言模型

&#x1f989; AI新闻 &#x1f680; 马斯克大模型Grok-1已开源&#xff0c;目前为止最大的开源大语言模型 摘要&#xff1a;马斯克上一周就在x上预告将开源自己的大模型&#xff0c;等了一周&#xff0c;就在刚刚&#xff0c;马斯克的大模型 Grok-1 开源了&#xff0c;Grok-…

【Canvas与艺术】砂落字现

【注意】 本作代码需要在服务器端执行&#xff0c;不可用浏览器直接打开运行。 如何安装服务器端请参考&#xff1a;https://www.cnblogs.com/heyang78/p/3339235.html 【原理】 雨粒子落下时&#xff0c;如果当前点不是黑点&#xff0c;则化身为金字的一个像素点。 【效果…

USB - USB Gadget on Linux

February, 2012. Embedded Linux Conference 2012. Agenda Introduction to USB USB Gadget API Existing Gadgets Design your own Gadget Demo Conclusio About the Author Software engineer at Adeneo Embedded Linux, Android Main activities: – BSP adaptation – Driv…

PXVDI企业级PVE免费桌面虚拟化部署教程ProxmoxVE

什么是PXVDI&#xff1f; PXVDI是一款基于Proxmox VE为底层的可商用的免费云桌面套件。对熟悉PVE的人来说&#xff0c;这点非常的点赞。首先是PVE是免费的&#xff0c;其次PVE的免费云桌面方案也极为少数。 根据官方提出的价格清单&#xff0c;免费版和商业版在功能上主要的区…

使用CURL命令确定Access-Control-Allow-Origin问题

一、问题描述 有前端小伙伴反馈ajax请求遇到跨域问题&#xff0c;也让后端小伙伴设置了跨域允许&#xff0c;但诡异的事情是在前端小伙伴的微信开发者工具中Network headers中看到了两行&#xff1a;Access-Control-Allow-Origin&#xff0c;其中居然出现了&#xff1a;“Acce…

51单片机—DS18B20温度传感器

目录 一.元件介绍及原理 二&#xff0c;应用&#xff1a;DS18B20读取温度 一.元件介绍及原理 1.元件 2.内部介绍 本次元件使用的是单总线 以下为单总线的介绍 时序结构 操作流程 本次需要使用的是SKIP ROM 跳过&#xff0c; CONVERT T温度变化&#xff0c;READ SCRATCHPAD…

Linux:系统初始化,内核优化,性能优化(2)

优化ssh协议 Linux&#xff1a;ssh配置_ssh配置文件-CSDN博客https://blog.csdn.net/w14768855/article/details/131520745?ops_request_misc%257B%2522request%255Fid%2522%253A%2522171068202516800197044705%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fb…

redis 常见的异常

目录 一、缓存穿透 1、概念 解决方案 &#xff08;1&#xff09;布隆过滤器 (2)、缓存空对象 二、缓存雪崩 1、概念 解决方案 &#xff08;1&#xff09;redis高可用 &#xff08;2&#xff09;限流降级 &#xff08;3&#xff09;数据预热 一、缓存穿透 1、概念 缓…

JavaWeb后端——分层解耦 IOC DI

分层/三层架构概述 三层架构&#xff1a;Controller、Service、Dao 解耦/IOC&DI概述 分层解耦 容器称为&#xff1a;IOC容器/Spring容器 IOC 容器中创建&#xff0c;管理的对象&#xff0c;称为&#xff1a;bean 对象 IOC&DI入门 实现 IOC&DI 需要的注解&#…

【MySQL】 MySQL的内置函数——日期函数、字符串函数、数学函数、聚合函数、其他函数

文章目录 MySQL1. 日期函数1.1 查看时间1.2 对时间进行计算 2. 字符串函数2.1 字符串查找2.2 字符串修改显示 3. 数学函数4. 聚合函数5. 其他函数 MySQL 1. 日期函数 在MySQL中&#xff0c;提供了多种时间函数供我们使用&#xff0c;其中包括用于查看时间的函数和计算日期的函数…