FreeRTOS一些常识笔记之快速上手

2023-05-16

一、为啥要用实时多任务操作系统

real-time Operate System 简称有:RTOS,有如下的好处:

  1. 用户无需关心时间信息
    内核负责计时,并由相关的API完成,从而使得用户的应用程序代码结构更简单。
  2. 模块化、可拓展性强
    也正是由于第一点的原因,程序性能不易受底层硬件更改的影响。姐,各个任务是独立的模块,每个模块都有明确的目
    的,降低了代码的耦合性。
  3. 效率高
    内核可以让软件完全由事件驱动,因次,轮询未发生的事件是不浪费时间的。相当于用中断来进行任务切换。
  4. 中断进程更短
    通过把中断的处理推迟到用户创建的任务中,可以使得中断处理程序非常短。

二、核心的C文件和头文件

  • C文件
    在这里插入图片描述
  • 头文件
    在这里插入图片描述

三、两个数据类型和变量的定义方法

TickType_t

  • FreeRTOS配置了一个周期性的时钟中断:Tick Interrupt
  • 每发生一次中断,中断次数累加,这被称为tick count
  • tick count这个变量的类型就是TickType_t
  • TickType_t可以是16位的,也可以是32位的
  • FreeRTOSConfig.h中定义configUSE_16_BIT_TICKS时,TickType_t就是uint16_t
  • 否则TickType_t就是uint32_t
  • 对于32位架构,建议把TickType_t配置为uint32_t

BaseType_t

  • 这是该架构最高效的数据类型
  • 32位架构中,它就是uint32_t
  • 16位架构中,它就是uint16_t
  • 8位架构中,它就是uint8_t
  • BaseType_t通常用作简单的返回值的类型,还有逻辑值,比如pdTRUE/pdFALSE

变量名
每个变量的前缀表示的含义
在这里插入图片描述
函数名
函数名前缀有有2部分:返回值类型、在哪个文件定义。
在这里插入图片描述
宏的名
宏的名字是大小,可以添加小写的前缀。前缀是用来表示:宏在哪个文件中定义
在这里插入图片描述
通用的宏定义如下:
在这里插入图片描述

四、内存管理中的几个API

提及内存管理就必须说一下堆和栈
堆,heap,就是一块空闲的内存,需要提供管理函数

  • malloc:从堆里划出一块空间给程序使用
  • free:用完后,再把它标记为"空闲"的,可以再次使用
    在这里插入图片描述

栈,stack,函数调用时局部变量保存在栈中,当前程序的环境也是保存在栈中

  • 可以从堆中分配一块空间用作栈
    在这里插入图片描述

在FreeRTOS中内存管理的接口函数(API)为:
1、pvPortMalloc 、vPortFree,对应于C库的malloc、free。

void * pvPortMalloc( size_t xWantedSize ); //分配内存,如果分配内存不成功,则返回值为NULL。
void vPortFree( void * pv );//释放内存

2、当前还有多少空闲内存,这函数可以用来优化内存的使用情况。比如当所有内核对象都分配好后,执行此函数返回2000,那么configTOTAL_HEAP_SIZE就可减小2000。

size_t xPortGetFreeHeapSize( void );

3、空闲内存的最小值

size_t xPortGetMinimumEverFreeHeapSize( void );//程序运行过程中,空闲内存容量的最小值。

五、创建任务和删除任务

啥叫任务?
任务就一个函数,但要注意的是
在这里插入图片描述示例

void ATaskFunction( void *pvParameters ) 
{ 
    /* 对于不同的任务,局部变量放在任务的栈里,有各自的副本 */
    int32_t lVariableExample = 0;
    /* 任务函数通常实现为一个无限循环 */
    for( ;; ) 
    { 
        /* 任务的代码 */ 
    }
    
    /* 如果程序从循环中退出,一定要使用vTaskDelete删除自己 * NULL表示删除的是自己 */
    vTaskDelete( NULL ); 
    /* 程序不会执行到这里, 如果执行到这里就出错了 */ 
}

创建任务API

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
                        const char * const pcName, // 任务的名字 
                        const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位 为word,10表示40字节 
                        void * const pvParameters, // 调用任务函数时传入的参数 
                        UBaseType_t uxPriority, // 优先级 
                        TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用 它来操作这个任务

里面的参数说明如下:
在这里插入图片描述
示例:
任务1代码:

void vTask1( void *pvParameters ) 
{ 
    const char *pcTaskName = "T1 run\r\n";
    volatile uint32_t ul; /* volatile用来避免被优化掉 */ 
    
    /* 任务函数的主体一般都是无限循环 */ 
    for( ;; ) 
    { 
        /* 打印任务1的信息 */
        printf( pcTaskName );
        
         /* 延迟一会(比较简单粗暴) */ 
         for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ ) 
         {
         } 
    } 
}

任务2代码:

void vTask2( void *pvParameters )
{ 
    const char *pcTaskName = "T2 run\r\n"; 
    volatile uint32_t ul; /* volatile用来避免被优化掉 */ 
    
    /* 任务函数的主体一般都是无限循环 */ 
    for( ;; ) 
    { 
        /* 打印任务1的信息 */
        printf( pcTaskName ); 
        
        /* 延迟一会(比较简单粗暴) */ 
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ ) 
        {
        } 
    } 
}

main函数:

int main( void )
{ 
    prvSetupHardware(); 
    
    xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL); 
    xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL); 
    
    /* 启动调度器 */ 
    vTaskStartScheduler(); 
    
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */ 
    return 0; 
}

运行结果:
在这里插入图片描述
注:

  • task 2先运行!
  • 要分析xTaskCreate的代码才能知道原因:更高优先级的、或者后面创建的任务先运行。

删除任务API

void vTaskDelete( TaskHandle_t xTaskToDelete );

参数说明
在这里插入图片描述
举个栗子

  • 自杀: vTaskDelete(NULL)
  • 被杀:别的任务执行 vTaskDelete(pvTaskCode) ,pvTaskCode是自己的句柄
  • 杀人:执行 vTaskDelete(pvTaskCode) ,pvTaskCode是别的任务的句柄

上示例: 大体的意思是:

  1. 创建任务1:任务1的大循环里,创建任务2,然后休眠一段时间
  2. 任务2:打印一句话,然后就删除自己

任务1代码:

void vTask1( void *pvParameters ) 
{ 
    const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL ); 
    BaseType_t ret; 
    
    /* 任务函数的主体一般都是无限循环 */ 
    for( ;; ) 
    { 
        /* 打印任务的信息 */ 
        printf("Task1 is running\r\n"); 
        ret = xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle ); 
        if (ret != pdPASS) 
           printf("Create Task2 Failed\r\n"); 
           
        // 如果不休眠的话, Idle任务无法得到执行 
        // Idel任务会清理任务2使用的内存 
        // 如果不休眠则Idle任务无法执行, 最后内存耗尽 vTaskDelay( xDelay100ms ); 
    }
}

任务2代码

void vTask2( void *pvParameters ) 
{ 
    /* 打印任务的信息 */ 
    printf("Task2 is running and about to delete itself\r\n");
    // 可以直接传入参数NULL, 这里只是为了演示函数用法 
    vTaskDelete(xTask2Handle); 
}

main函数

int main( void ) 
{ 
    prvSetupHardware(); 
    xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
    /* 启动调度器 */ 
    vTaskStartScheduler(); 
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */ 
    return 0;
}

运行结果

在这里插入图片描述

解释:

  • main函数中创建任务1,优先级为1。任务1运行时,它创建任务2,任务2的优先级是2。

  • 任务2的优先级最高,它马上执行。

  • 任务2打印一句话后,就删除了自己。

  • 任务2被删除后,任务1的优先级最高,轮到任务1继续运行,它调用
    vTaskDelay() 进入Block状态

  • 任务1 Block期间,轮到Idle任务执行:它释放任务2的内存(TCB、栈)

  • 时间到后,任务1变为最高优先级的任务继续执行。

  • 如此循环。

六、任务优先级和Tick

任务优先级

  • 高优先级的任务先运行。
  • 优先级的取值范围是:0~(configMAX_PRIORITIES – 1),数值越大优先级越高。

使用uxTaskPriorityGet来获得任务的优先级,
使用参数xTask来指定任务,设置为NULL表示获取自己的优先级。

UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );

使用vTaskPrioritySet 来设置任务的优先级,
使用参数xTask来指定任务,设置为NULL表示设置自己的优先级;
参数uxNewPriority表示新的优先级,取值范围是0~(configMAX_PRIORITIES – 1)。

void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );

Tick

对于相同优先级的任务的话,它们“轮流”执行。怎么轮流?你执行一会,我执行一会。那么这个一会就是使用Tick定义的

vTaskDelay(2); // 等待2个Tick,假设configTICK_RATE_HZ=100, Tick周期时10ms, 等待20ms 

// 还可以使用pdMS_TO_TICKS宏把ms转换为tick 
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms

七、任务的几种状态

  1. 阻塞状态(Blocked)
  2. 暂停状态(Suspended)
  3. 就绪状态(Ready)

任务转换图
在这里插入图片描述

七、两个Delay函数

  • vTaskDelay:至少等待指定个数的Tick Interrupt才能变为就绪状态
  • vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪态
void vTaskDelay( const TickType_t xTicksToDelay ); /* xTicksToDelay: 等待多少给 Tick */ 
/* pxPreviousWakeTime: 上一次被唤醒的时间 
 * xTimeIncrement: 要阻塞到(pxPreviousWakeTime + xTimeIncrement) 
 * 单位都是Tick Count 
 */ 
 BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement );

详细介绍:

  • 使用vTaskDelay(n)时,进入、退出vTaskDelay的时间间隔至少是n个Tick中断
  • 使用xTaskDelayUntil(&Pre, n)时,前后两次退出xTaskDelayUntil的时间至少是n个Tick中断
    1、退出xTaskDelayUntil时任务就进入的就绪状态,一般都能得到执行机会
    2、所以可以使用xTaskDelayUntil来让任务周期性地运行

在这里插入图片描述
示例:
本程序会创建2个任务:

  • Task1:
    1、高优先级
    2、设置变量flag为1,然后调用 vTaskDelay(xDelay50ms); 或vTaskDelayUntil(&xLastWakeTime, xDelay50ms);

  • Task2:
    1、低优先级
    2、设置变量flag为0

main函数

int main( void )
{
    prvSetupHardware();
    
    /* Task1的优先级更高, Task1先执行 */ 
    xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL ); 
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL ); 

    /* 启动调度器 */ 
    vTaskStartScheduler(); 

    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */ 
    return 0; 
}

Task1的代码中使用条件开关来选择Delay函数,把 #if 1 改为 #if 0 就可以使用 vTaskDelayUntil

void vTask1( void *pvParameters ) 
{ 
    const TickType_t xDelay50ms = pdMS_TO_TICKS( 50UL ); 
    TickType_t xLastWakeTime; int i; 
    
    /* 获得当前的Tick Count */ 
    xLastWakeTime = xTaskGetTickCount(); 
    for( ;; ) 
    { 
        flag = 1;
        
        /* 故意加入多个循环,让程序运行时间长一点 */ 
        for (i = 0; i <5; i++)
            printf( "Task 1 is running\r\n" );
##if 1 
    vTaskDelay(xDelay50ms);
##else 
    vTaskDelayUntil(&xLastWakeTime, xDelay50ms); 
##endif 
    }
}

Task2的代码

void vTask2( void *pvParameters ) 
{ 
    for( ;; )
    { 
        flag = 0; 
        printf( "Task 2 is running\r\n" ); 
    } 
}

使用Keil的逻辑分析观察flag变量的bit波形,如下:

  • flag为1时表示Task1在运行,flag为0时表示Task2在运行,也就是Task1处于阻塞状态
  • vTaskDelay:指定的是阻塞的时间
  • vTaskDelayUntil:指定的是任务执行的间隔、周期
    在这里插入图片描述
    在这里插入图片描述

八、空闲任务和钩子函数

空闲任务
为什么要有空闲函数呢?

因为一个良好的程序,它的任务都是事件驱动的:平时大部分时间处于阻塞状态。有可能我们自己创建的所有任务都无法执行,但是调度器必须能找到一个可以运行的任务:所以,我们要提供空闲任务。

在使用 vTaskStartScheduler() 函数来创建、启动调度器时,这个函数内部会创建空闲任务:

  • 空闲任务优先级为0:它不能阻碍用户任务运行
  • 空闲任务要么处于就绪态,要么处于运行态,永远不会阻塞

空闲任务的优先级为0,这意为着一旦某个用户的任务变为就绪态,那么空闲任务马上被切换出去,让这个用户任务运行。在这种情况下,我们说用户任务"抢占"(pre-empt)了空闲任务,这是由调度器实现的。
要注意的是:如果使用 vTaskDelete() 来删除任务,就要确保空闲任务有机会执行,否则就无法释放被删除任务的内存。

钩子函数

钩子函数在空闲任务中添加,空闲任务每执行一次,钩子函数就会被调用一次,那么钩子函数能干些什么事呢?

  • 执行一些低优先级的、后台的、需要连续执行的函数
  • 测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都停止了,所以测量空闲任务占据的时间,就可以算出处理器占用率。
  • 让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当然可以进入省电模式 了。

钩子函数使用过程中应该注意:

  • 不能导致空闲任务进入阻塞状态、暂停状态
  • 如果你会使用 vTaskDelete()来删除任务,那么钩子函数要非常高效地执行。如果空闲任务移植 卡在钩子函数里的话,它就无法释放内存。

如果想使用钩子函数

  • 在FreeRTOSConfig.h中,把configUSE_MALLOC_FAILED_HOOK定义为1
  • 提供vApplicationMallocFailedHook函数
  • pvPortMalloc失败时,才会调用此函数
    在这里插入图片描述

九、调度算法

因为只能一个任务处在运行状态中,所以需要调度来实现不同任务进入运行状态。

调度算法的行为主要体现为:

  • 高优先级的任务先运行
  • 同优先级的就绪态任务如何被选中

从3个角度统一理解多种调度算法:
在这里插入图片描述
在这里插入图片描述
上表解释:

  • A:可抢占+时间片轮转+空闲任务让步
  • B:可抢占+时间片轮转+空闲任务不让步
  • C:可抢占+非时间片轮转+空闲任务让步
  • D:可抢占+非时间片轮转+空闲任务不让步
  • E:合作调度

示例:

注:

  • 任务1优先级0
  • 任务2优先级0
  • 任务3优先级2(最高优先级)

是否抢占对比
在 FreeRTOSConfig.h 中,定义这样的宏,对比逻辑分析仪的效果:

// 实验1:抢占
#define configUSE_PREEMPTION 1 
#define configUSE_TIME_SLICING 1 
#define configIDLE_SHOULD_YIELD 1 
// 实验2:不抢占 
#define configUSE_PREEMPTION 0 
#define configUSE_TIME_SLICING 1 
#define configIDLE_SHOULD_YIELD 1
  • 抢占时:高优先级任务就绪时,就可以马上执行
  • 不抢占时:优先级失去意义了,既然不能抢占就只能协商了,图中任务1一直在运行(一点都没有协商精神),其他任务都无法执行。即使任务3的 vTaskDelay 已经超时、即使它的优先级更高,都没办法执行。
    在这里插入图片描述
    在这里插入图片描述
    是否时间片轮转对比
    在 FreeRTOSConfig.h 中,定义这样的宏,对比逻辑分析仪的效果:
// 实验1:时间片轮转 
#define configUSE_PREEMPTION 1 
#define configUSE_TIME_SLICING 1 
#define configIDLE_SHOULD_YIELD 1 
// 实验2:时间片不轮转 
#define configUSE_PREEMPTION 1 
#define configUSE_TIME_SLICING 0 
#define configIDLE_SHOULD_YIELD 1
  • 时间片轮转:在Tick中断中会引起任务切换
  • 时间片不轮转:高优先级任务就绪时会引起任务切换,高优先级任务不再运行时也会引起任务切换。可以看到任务3就绪后可以马上执行,它运行完毕后导致任务切换。其他时间没有任务切换, 可以看到任务1、任务2都运行了很长时间。
    在这里插入图片描述
    在这里插入图片描述
    空闲任务是否让步对比
    在 FreeRTOSConfig.h 中,定义这样的宏,对比逻辑分析仪的效果:
// 实验1:空闲任务让步 
#define configUSE_PREEMPTION 1 
#define configUSE_TIME_SLICING 1 
#define configIDLE_SHOULD_YIELD 1 
// 实验2:空闲任务不让步 
#define configUSE_PREEMPTION 1 
#define configUSE_TIME_SLICING 1 
#define configIDLE_SHOULD_YIELD 0
  • 让步时:在空闲任务的每个循环中,会主动让出处理器,从图中可以看到flagIdelTaskrun的波形很小
  • 不让步时:空闲任务跟任务1、任务2同等待遇,它们的波形宽度是差不多的
    在这里插入图片描述
    在这里插入图片描述

十、同步互斥与通信

关于RTOS的其他内容后续更新,已是凌晨1点,先睡会

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

FreeRTOS一些常识笔记之快速上手 的相关文章

  • FreeRTOS系列

    1 多任务系统 1 1 前后台系统 单片机裸机开发时 一般都是在main函数里面用while 1 做一个大循环来完成所有的处理 循环中调用相应的函数完成所需的处理 有时也需要在中断中完成一些处理 相对于多任务系统而言 这就是单人单任务系统也
  • 详解FreeRTOS中的软件定时器

    软件定时器用于让某个任务定时执行 或者周期性执行 比如设定某个时间后执行某个函数 或者每隔一段时间执行某个函数 由软件定时器执行的函数称为软件定时器的回调函数 参考资料 Mastering the FreeRTOS Real Time Ke
  • FreeRTOS config开始的宏

    FreeRTOSConfig h系统配置文件中可以自定义 FreeRTOS h中定义默认值 configAPPLICATION ALLOCATED HEAP 默认情况下FreeRTOS的堆内存是由编译器来分配的 将宏configAPPLIC
  • 【FreeRTOS 信号量】互斥信号量

    互斥信号量与二值信号量类似 但是互斥信号量可以解决二值信号量出现的优先级翻转问题 解决办法就是优先级继承 普通互斥信号量创建及运行 参阅安富莱电子demo 互斥信号量句柄 static SemaphoreHandle t xMutex NU
  • FreeRTOS 软件定时器的使用

    FreeRTOS中加入了软件定时器这个功能组件 是一个可选的 不属于freeRTOS内核的功能 由定时器服务任务 其实就是一个定时器任务 来提供 软件定时器是当设定一个定时时间 当达到设定的时间之后就会执行指定的功能函数 而这个功能函数就叫
  • FreeRTOS-内核控制函数

    FreeRTOS 内核控制函数 FreeRTOS中有一些内核函数 一般来说这些内核函数在应用层不会使用 但是内核控制函数是理解FreeRTOS中断的基础 接下来我们逐一分析这些内核函数 taskYIELD 该函数的作用是进行任务切换 这是一
  • FreeRTOS软件定时器创建、复位、开始和停止(备忘)

    目录 一 简介 1 1 开发环境 1 2 摘要 二 STM32CubeIDE配置 三 创建定时器 3 1 头文件声明 3 2 工程文件定义 3 3 创建定时器 3 4 开启 复位 和关闭定时器 四 定时器回调函数 一 简介 1 1 开发环境
  • FreeRTOS学习笔记 6 - 互斥量

    目录 1 创建 2 获取 3 释放 4 测试 FreeRTOS不支持调度方式的设置 所以下面2个宏定义可以随意设置值 define RTOS IPC FLAG FIFO 0x00 define RTOS IPC FLAG PRIO 0x01
  • FreeRTOS学习笔记(3、信号量、互斥量的使用)

    FreeRTOS学习笔记 3 信号量 互斥量的使用 前言 往期学习笔记链接 学习工程 信号量 semaphore 两种信号量的对比 信号量的使用 1 创建信号量 2 give 3 take 4 删除信号量 使用计数型信号量实现同步功能 使用
  • FreeRTOS学习(八) 延时函数

    声明及感谢 跟随正点原子资料学习 在此作为学习的记录和总结 环境 keil stm32f103 FreeRTOS延时函数有两个 分别是 vTaskDelay vTaskDelayUntil 1 vTaskDelay 任务相对延时 函数原型
  • STM32移植FreeRTOS的Tips

    转自 http bbs armfly com read php tid 7140 1 在FreeRTOS的demo文件夹中拷贝对应的FreeRTOSConfig h文件后 需要加入一行 define configUSE MUTEXES 1
  • 【FreeRTOS】队列的使用

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • FreeRTOS打印任务对CPU的占有率

    1 配置RTOS 1 打开RTOS Config Parameter 找到Run Time And Task States gathering related definitions 使能GENERATE RUN TIME STATS US
  • 基于HAL库的FREERTOS----------一.任务

    FreeROTS 就是一个免费的 RTOS 类系统 这里要注意 RTOS 不是指某一个确定的系统 而是指一类系统 比如 UCOS FreeRTOS RTX RT Thread 等这些都是 RTOS 类操作系统 FreeRTOS 是 RTOS
  • Arduino IDE将FreeRTOS用于STM32

    介绍 适用于STM32F103C8的FreeRTOS STM32F103C是一种能够使用FreeRTOS的ARM Cortex M3处理器 我们直接在Arduino IDE中开始使用STM32F103C8的FreeRTOS 我们也可以使用K
  • FreeRTOS笔记(十)中断

    中断 当CPU在执行某一事件A时 发生另外一个更重要紧急的事件B请求CPU去处理 产生了中断 于是CPU暂时中断当前正在执行的事件A任务而对对事件B进行处理 CPU处理完事件B后再返回之前中断的位置继续执行原来的事件A 这一过程统称为中断
  • FreeRTOS学习笔记(8)---- 软件定时器

    使用FreeRTOS软件定时器需要在文件FreeRTOSConfig h先做如下配置 1 configUSE TIMERS 使能软件定时器 2 configTIMER TASK PRIORITY 定时器任务优先级 3 configTIMER
  • FreeRTOS死机原因

    1 中断回调函数中没有使用中断级API xxFromISR 函数 xSemaphoreGiveFromISR uart busy HighterTask 正确 xSemaphoreGive uart busy 错误 2 比configMAX
  • FreeRTOS轻量级同步--任务通知

    1 简介 在FreeRTOS的配置参数中的configUSE TASK NOTIFICATIONS宏打开 一般RTOS会默认打开 如图1所示 图1 notify宏开关 RTOS在创建任务时 会创建一个32位的通知值ulNotifiedVal
  • FreeRTOS 配置TICK_RATE_HZ

    我使用的是带有 5 4 版 FreeRTOS 的 MSP430f5438 我有一个有趣的问题 我无法弄清楚 基本上 当我将 configTICK RATE HZ 设置为不同的值时 LED 闪烁得更快或更慢 它应该保持相同的速率 我将 con

随机推荐

  • 一张图搞懂数据结构体系——数据结构脑图

    基本概念 xff1a 数据 xff1a 数据是信息的载体 xff0c 是计算机程序加工的原料 数据元素 xff1a 是数据的基本单位 xff0c 也称元素 结点 数据结构 xff1a 是数据之间的相互关系 xff0c 是数据的组织形式 xf
  • 【入门篇】ESP8266直连智能音箱(天猫精灵)控制智能灯

    本系列博客学习由非官方人员 刘一周 潜心所力所写 xff0c 仅仅做个人技术交流分享 xff0c 不做任何商业用途 如有不对之处 xff0c 请留言 xff0c 本人及时更改 本系列博客内容是通过乐鑫ESP8266直连天猫精灵 xff0c
  • 如何进行CAN总线高效测试?

    CAN总线自BOSCH公司发明以来 xff0c 在汽车通信网络中的应用得到了广泛认可 随着汽车电子技术的发展 xff0c 车上的电子模块越来越多 xff0c 汽车内部的CAN总线节点也随之增多 一般汽车内部CAN节点少则10个 xff0c
  • keil5 中文注释

    keil5 中文注释 点击 Edit gt configuration 如下图 xff1a 在Editor界面中选择Encoding xff0c 如下图 xff0c 这三个均可显示中文注释 xff0c 推荐第一个 xff0c 保持Keil代
  • 【CMake学习】list使用

    添加链接描述 一 介绍 cmake的list命令即对列表的一系列操作 xff0c cmake中的列表变量是用分号 分隔的一组字符串 xff0c 创建列表可以使用set命令 xff08 参考set命令 xff09 xff0c 例如 xff1a
  • 【ros+movros安装与飞控连接测试,pixhawk2.4.8】

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 ros安装二 mavros总结 前言 记一下装ros和movros的过程顺便把一些重要的链接保存一些 提示 xff1a
  • 使用docker发布.net应用

    步骤 xff1a 创建 NET应用样例 创建包含生成 NET镜像所需引导的Dockerfile 构建一个镜像并基于此创建一个容器 设置容器数据卷和网络设置 使用Docker Compose编排容器 使用容器构建开发坏境 创建镜像 先决条件
  • python计算机视觉--全景图像拼接

    目录 一 RANSAC算法 1 1 RANSAC算法简介 1 2 算法基本思想和流程 1 3 RANSAC求解单应性矩阵 二 图像映射与全景拼接 2 1 简介 2 2 计算第二张图像与第一张图像之间的变换关系 2 3 将第二张图像叠加到第一
  • px4自带教程offboard下的gazebo多无人机编队仿真

    px4自带教程offboard下的gazebo多无人机编队仿真 主要教程参考这篇文章 xff0c offboard代码也源自下面链接 xff0c 增加了其他文件的配置细节 xff0c 链接如下 xff1a https blog csdn n
  • rotors_simulator与sitl_gazebo冲突导致报错“gzserver....”

    rotors simulator与sitl gazebo冲突导致报错 gzserver 创建时间2021 04 14 报错图片详见2021 04 14屏幕截图 总是出现gzserver symbol lookup error home zy
  • 安装ROS、gazebo、PX4基础细节及offboard控制

    新手参考教程安装ROS gazebo PX4基础细节及offboard控制 1 安装ROS 参考教程 2 安装PX4 参考教程 注 xff1a 1 在编译px4 Firmware前会经过安装步骤 xff0c 安装需要去github上git
  • 无人机模型记录

    今天看了这篇知乎 xff0c 收获非常大 xff0c 实现了一个非常基础的无人机动力学以及运动学模型 xff0c 包括公式推导等 xff0c 也解决了困扰我很久的问题 xff0c 在此基础上就可以加入控制算法 xff0c 设置轨迹等 htt
  • VMware安装Ubuntu20.04.5常见问题及解决方案

    文章目录 使用Xftp连接ubuntu系统ubuntu上安装指定版本nodejsubuntu设置连网ubuntu安装Java8ubuntu安装 deb格式软件ubuntu卸载 deb格式软件ubuntu中electron框架安装的缓存在如下
  • 无人机控制输入、PID控制

    无人机控制输入 PID控制 最近思路比较乱 xff0c 看到很多东西 xff0c 因为有各种控制 xff0c 需要在这里记录总结 控制输入 结合以下两个链接理解虚拟控制输入U1 U2 U3 U4 1 https blog csdn net
  • FreeRTOS学习(3)——任务创建和删除(静态)

    本代码是基于正点原子的STM32Mini板子 xff0c 结合其FreeRTOS课程进行学习 实验一 xff1a 设计4个任务 xff1a start task task1 task2 task3 start task任务 xff1a 用来
  • 华三交换机配置定时重启任务

    组网及说明 1 配置需求或说明 1 1 适用产品系列 本案例适用于如S7006 S7503E S7506E S7606 S10510 S10508等S7000 S7500E S10500系列 xff0c 且软件版本是V7的交换机 1 2 配
  • RTK差分通讯链路---Ntrip DTU(支持千寻位置,CORS站、自建站)

    在之前的博客中提到RTK差分通讯链路 电台 RTK技术的关键在于其获取了载波相位的观测量 xff0c 通过架设基准站和移动站 xff0c 利用电台的通讯方式 xff0c 使得移动站通过差分方式消除观测数据误差实现高精度 还有一种通讯方式 x
  • 北斗/GPS如何处理定位漂移?

    漂移是北斗 GPS导航时需要处理的问题之一 xff0c 漂移主要有两个方面 xff0c 第一 xff0c 速度过快 xff0c 以至于北斗 GPS的响应时间短于当前运行速度 xff0c 出现漂移 xff1b 第二 xff0c 在高大建筑密集
  • Jetson nx批量复制

    Jetson NX 批量克隆教程 文章目录 Jetson NX 批量克隆教程一 批量克隆是什么 xff1f 二 克隆步骤1 准备材料2 备份镜像3 克隆新SD卡 解压失败问题参考 一 批量克隆是什么 xff1f 辛辛苦苦在Jetson Xa
  • FreeRTOS一些常识笔记之快速上手

    一 为啥要用实时多任务操作系统 real time Operate System 简称有 xff1a RTOS xff0c 有如下的好处 用户无需关心时间信息 内核负责计时 xff0c 并由相关的API完成 xff0c 从而使得用户的应用程