FreeRTOS之heap4

2023-05-16

操作系统离不开内存管理。FreeRTOS提供了5种内存管理方法。实现在portable\MemMang里heap1到heap5。每种管理方案策略不同。我采用的是比较有代表性的heap4管理方案。该模式定义了ucHeap全局数组充当堆内存池。然后通过链表管理未分配的内存块空间。首先所有的堆和栈内存空间申请都来着ucHeap的数组。然后没分配一块内存就到可用链表里找到第一个合适的自由块分配给使用者。如果该块大于申请大小。分配后就有一个剩余空间,如果剩余空间小于管理用的结构体占用空间,那么剩余空间不用再构建新的空闲块,因为他连管理用的结构体都无法放下。如果剩余空间大于关联用的结构体空间,那么剩余空间就还有可用加载,在分配地址返回前把要分配的块截止大小为申请的大小,把剩余的空间构造一个新的自由块加入自由跨链表。如果使用者调用了释放内存方法,那么先给传入的要释放内存地址前移内存管理块的大小,即得到当前释放内存块的管理结构体地址。然后按地址大小找到空闲列表对应位置插入释放块。如果释放块和前后地址接的上那么就和前后进行块合并操作。整体就是这样一种操作。

要理解FreeRTOS的内核调度,要先理解内核的内存分配方案。结构图如下(引用的别人的图):
在这里插入图片描述

heap_4.c实现如下

//堆内存管理的实现,该模式可以申请和释放内存。定义了ucHeap全局数组充当堆内存池。
//实际分配的该数组的空间。采用链表管理分配,将多个自由内存块链接起来。
//定义了#define pvPortMallocStack    pvPortMalloc,所有内核栈也是从这里申请内存

/*动态内存分配是C语言编程中的一个概念,而不是FreeRTOS或者多任务里面的特有的概念。
FreeRTOS需要涉及到动态内存分配是因为内核对象是动态构造的。
在通用的C语言编程环境里,我们可以使用标准库中的malloc()和free()来进行动态内存分配的操作,
但这些函数对于实时应用是不合适的,原因有如下几点:
1.在嵌入式系统中他们不总是可用的,有的环境不支持或者没有实现
2.他们的底层实现可能需要较多的资源,调用时开销大
3.他们很少情况下是线程安全的
4.他们的调用耗时是不确定的(not deterministic),每次调用耗时都可能不一样(标准只要求他们的执行尽可能的快速完成并返回)
5.可能会造成内存碎片问题
6.他们会使连接器的配置变的复杂
7.会使得代码很难调试

提供下面方法给内核申请和释放内存
void *pvPortMalloc( size_t xWantedSize )
void vPortFree( void *pv )
*/

#include <stdlib.h>

/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
 * all the API functions to use the MPU wrappers.  That should only be done when
 * task.h is included from an application file. */
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE

#include "FreeRTOS.h"
#include "task.h"

#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
//这个模式就是动态管理内存,所以不允许关闭动态内存
#if (configSUPPORT_DYNAMIC_ALLOCATION == 0)
    #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif

//最小可用做内存分配的堆大小
//一个内存块分配给需求之后剩余大小如果小于对结构体大小就不能当做空闲块。因为分配内存要用堆结构体记录分配信息
//剩余空间都小于结构体大小了就不能当新的空闲块
//所以最小堆大小就是堆结构体打印左一1位,即堆结构体大小加八个比特
#define heapMINIMUM_BLOCK_SIZE    ((size_t) ( xHeapStructSize << 1))

/*假设8bit 字节!*/
#define heapBITS_PER_BYTE         ((size_t) 8)

/*如果配置了运用程序自己定义堆数组就使用外部的堆。
	ucHeap的存放位置是在编译期间,由链接器(linker)决定的,
	如果ucHeap被放到了访问速度较慢的外部SRAM,则任务的执行速度将受到不利影响。
	这个时候可以由开发者自己定义ucHeap内存池数组*/
#if (configAPPLICATION_ALLOCATED_HEAP == 1)
		//引入自定义堆
    extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
		//堆就是一个u8类型大数组
    PRIVILEGED_DATA static uint8_t ucHeap[configTOTAL_HEAP_SIZE ];
#endif

//用于内存管理的链表结构,通过该链表把空闲内块链接起来管理
typedef struct A_BLOCK_LINK
{
	 //下一个空闲块
   struct A_BLOCK_LINK * pxNextFreeBlock;
	 //空闲块的大小
   size_t xBlockSize;
} BlockLink_t;

/*-----------------------------------------------------------*/

//把要释放的内存块放入自由内存块链表。按地址找到插入位置。如果能和前后正好接上就做块合并处理
static void prvInsertBlockIntoFreeList(BlockLink_t * pxBlockToInsert) PRIVILEGED_FUNCTION;

//初始化堆,第一个内存分配时候自动调用
static void prvHeapInit(void) PRIVILEGED_FUNCTION;

/*-----------------------------------------------------------*/

//堆结构体的大小。每次分配要用堆结构体记录分配的一块信息。记录的结构体本身要占空间
static const size_t xHeapStructSize = (sizeof(BlockLink_t) + ((size_t) (portBYTE_ALIGNMENT - 1))) & ~((size_t) portBYTE_ALIGNMENT_MASK);

/*创建内存块开始和结束节点,通过xStart和pxEnd遍历找到合适内存分配*/
PRIVILEGED_DATA static BlockLink_t xStart, * pxEnd = NULL;

/* 跟踪分配和释放内存的调用次数以及
  * 剩余的空闲字节数,但没有说明碎片。 */
PRIVILEGED_DATA static size_t xFreeBytesRemaining = 0U;
PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = 0U;
PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = 0;
PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = 0;

//已经分配的比特数
PRIVILEGED_DATA static size_t xBlockAllocatedBit = 0;

/*-----------------------------------------------------------*/
//申请内存
//xWantedSize:想要申请的大小
void * pvPortMalloc(size_t xWantedSize)
{
    BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink;
    void * pvReturn = NULL;
    //挂起所有任务
    vTaskSuspendAll();
    {
        /* 如果这是对 malloc 的第一次调用,那么堆将需要
          * 初始化设置空闲块列表。 */
        if( pxEnd == NULL )
        {
					  //第一次申请内存就初始化堆
            prvHeapInit();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        //检查申请分配的内存是否具有合法性和检查申请的内存是否过大
        if((xWantedSize & xBlockAllocatedBit ) == 0)
        {
            //分配的数必须大于0
            if((xWantedSize > 0) && ((xWantedSize + xHeapStructSize)> xWantedSize))
            {
							  //申请的大小要加上堆记录结构体大小
                xWantedSize += xHeapStructSize;

                /*确保块对齐*/
                if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0x00)
                {
                    /*需要字节对齐。检查溢出。*/
                    if((xWantedSize + (portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK )))
                            > xWantedSize )
                    {
                        xWantedSize += (portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK));
                        configASSERT((xWantedSize & portBYTE_ALIGNMENT_MASK) == 0 );
                    }
                    else
                    {
                        xWantedSize = 0;
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                xWantedSize = 0;
            }
						//申请大小小于剩余的内存大小
            if((xWantedSize > 0) && (xWantedSize <= xFreeBytesRemaining) )
            {
                /*从开始(最低地址)块遍历列表,直到
                  * 找到一个足够大小的。 */
							  //前一个块,从开始块找
                pxPreviousBlock = &xStart;
							  //当前块,从开始取下一个空闲块
                pxBlock = xStart.pxNextFreeBlock;
								//一直找到大小大于申请大小的块或者找到最后块
                while((pxBlock->xBlockSize < xWantedSize) && (pxBlock->pxNextFreeBlock != NULL))
                {
										//前一个块等于当前块
                    pxPreviousBlock = pxBlock;
									  //当前块往下移一个
                    pxBlock = pxBlock->pxNextFreeBlock;
                }

                //如果当前块不等于结束就代表找到了符合的内存块了
                if( pxBlock != pxEnd )
                {
                    //返回当前块地址加上一个堆结构大小的地址
                    pvReturn = (void *) (((uint8_t *) pxPreviousBlock->pxNextFreeBlock) + xHeapStructSize);

                    //前一个块执行当前块的下一个块
                    pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;

                    //当前块分配给要的空间后如果剩余大小大于堆最小大小。剩下的空间就是一个可用的块
									  //构造一个新的空闲块
                    if((pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE)
                    {
                        //新空闲块执行当前块分配给想要空间大小后的地址
                        pxNewBlockLink = (void *) ((( uint8_t *) pxBlock) + xWantedSize);
                        configASSERT((((size_t) pxNewBlockLink) & portBYTE_ALIGNMENT_MASK ) == 0);

                        //新空闲块大小为当前块大小减去要申请的大小
                        pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
											  //当前块已经把剩下空间构成新块了,他本身大小变为申请的大小了
                        pxBlock->xBlockSize = xWantedSize;

                       //把新的空闲块插入空闲块列表
                        prvInsertBlockIntoFreeList(pxNewBlockLink);
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
										//空闲内存大小减去申请的大小
                    xFreeBytesRemaining -= pxBlock->xBlockSize;

                    if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
                    {
                        xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }

                    /* The block is being returned - it is allocated and owned
                     * by the application and has no "next" block. */
                    pxBlock->xBlockSize |= xBlockAllocatedBit;
										//当前块已经分配出去了,所以下一个自由块执行空指针
                    pxBlock->pxNextFreeBlock = NULL;
										//成功分配内存的数量加1
                    xNumberOfSuccessfulAllocations++;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
				//跟踪内存分配
        traceMALLOC(pvReturn,xWantedSize);
    }
		//恢复挂起的任务
    (void) xTaskResumeAll();
    //配置了分配内存失败钩子的处理
    #if (configUSE_MALLOC_FAILED_HOOK == 1)
        {
						//分配失败之后调用钩子函数
            if(pvReturn == NULL)
            {
                extern void vApplicationMallocFailedHook(void);
                vApplicationMallocFailedHook();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    #endif 
    configASSERT(((( size_t) pvReturn) & (size_t) portBYTE_ALIGNMENT_MASK ) == 0);
		//返回内存地址
    return pvReturn;
}
/*-----------------------------------------------------------*/
//释放分配的内存
//要释放的内存首地址
void vPortFree(void * pv)
{
    uint8_t * puc = (uint8_t *)pv;
	  //堆分配块指针
    BlockLink_t * pxLink;

    if(pv != NULL)
    {
        //由于申请内存返回地址加了堆结构体的大小。所以这里减去堆结构体大小就得到要释放内存的结构体
        puc -= xHeapStructSize;

        //转换为内存结构体指针
        pxLink = (void *)puc;

        //断言用的
        configASSERT((pxLink->xBlockSize & xBlockAllocatedBit) != 0);
        configASSERT(pxLink->pxNextFreeBlock == NULL);

        if((pxLink->xBlockSize & xBlockAllocatedBit ) != 0)
        {
					  //是使用的内存块的下一个自由块为空指针
					  //防止是用户程序传错了要释放的地址
            if(pxLink->pxNextFreeBlock == NULL)
            {
                /* 该块正在返回到堆中 - 它不再是分配 */
                pxLink->xBlockSize &= ~xBlockAllocatedBit;
								//挂起所有任务
                vTaskSuspendAll();
                {
                    //空闲内存大小加上释放块的大小
                    xFreeBytesRemaining += pxLink->xBlockSize;
									  //跟踪位点
                    traceFREE( pv, pxLink->xBlockSize );
									  //把要释放的内存块加入空闲内存列表
                    prvInsertBlockIntoFreeList(((BlockLink_t *) pxLink));
									  //成功释放内存次数加1
                    xNumberOfSuccessfulFrees++;
                }
								//恢复所有挂起的任务
                (void) xTaskResumeAll();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
}
/*-----------------------------------------------------------*/
//得到自由堆大小
size_t xPortGetFreeHeapSize(void)
{
    return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/

size_t xPortGetMinimumEverFreeHeapSize(void)
{
    return xMinimumEverFreeBytesRemaining;
}
/*-----------------------------------------------------------*/

void vPortInitialiseBlocks(void)
{
    /*这只是为了保持链接器安静。*/
}
/*-----------------------------------------------------------*/
//用来第一次申请内存时候初始化堆
//第一步:起始地址做字节对齐,保存pucAlignedHeap 可用空间大小为xTotalHeapSize 
//第二步:计算首尾 ,这里需要注意的是链表的尾部指针是保存到该地址尾部的
//第三部:完成链表的初始化,记录内存块信息
static void prvHeapInit(void) 
{
    BlockLink_t * pxFirstFreeBlock;
    uint8_t * pucAlignedHeap;
    size_t uxAddress;
    size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;

    //起始地址做字节对齐处理
	  //执行堆数组开始位置
    uxAddress = (size_t)ucHeap;
    //开始地址做8位对齐
    if((uxAddress & portBYTE_ALIGNMENT_MASK) != 0)
    {
        uxAddress+=(portBYTE_ALIGNMENT-1);
        uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK);
			  //减去对齐舍弃的字节
        xTotalHeapSize -= uxAddress - (size_t) ucHeap;
    }
    //对齐后可以用的起始地址
    pucAlignedHeap = (uint8_t *)uxAddress;

    //开始的下一个自由块执行对齐的堆首地址
    xStart.pxNextFreeBlock = (void *) pucAlignedHeap;
		//块大小设置0
    xStart.xBlockSize = (size_t) 0;

    //按对齐的堆的首地址和堆的总大小计算堆的结束位置
    uxAddress = ((size_t)pucAlignedHeap ) + xTotalHeapSize;
    uxAddress -= xHeapStructSize;
    uxAddress &= ~((size_t) portBYTE_ALIGNMENT_MASK);
		//设置计算地址到结束地址,结束块大小为0,结束块的下一个自由块执行空指针
    pxEnd = (void *) uxAddress;
    pxEnd->xBlockSize = 0;
    pxEnd->pxNextFreeBlock = NULL;

    //得到第一个自由块。开始地址是对齐堆的首地址
    pxFirstFreeBlock = (void *) pucAlignedHeap;
		//第一个自由块大小就是堆结束地址减堆对齐的首地址
    pxFirstFreeBlock->xBlockSize = uxAddress - (size_t)pxFirstFreeBlock;
		//第一个自由块执行结束块
    pxFirstFreeBlock->pxNextFreeBlock = pxEnd;

    //记录最小的空闲内存块大小,即开始堆的总大小
    xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
		//剩余内存堆大小
    xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;

    //已经申请的大小
    xBlockAllocatedBit = ((size_t) 1) << ((sizeof(size_t) * heapBITS_PER_BYTE ) - 1);
}
/*-----------------------------------------------------------*/
//插入块到空闲列表
static void prvInsertBlockIntoFreeList(BlockLink_t * pxBlockToInsert) 
{
	  //遍历用的游标
    BlockLink_t * pxIterator;
    uint8_t * puc;

    //从开始块一直找到第一个地址大于要插入块地址的内存块
    for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
    {
        /* 这里没什么可做的,只是迭代到正确的位置。 */
    }

    //找到的块地址转换为u8
    puc = (uint8_t *) pxIterator;
    //如果找到块的首地址加上块大小等于要插入块的首地址
    if((puc + pxIterator->xBlockSize) == (uint8_t *)pxBlockToInsert)
    {
			  //就把要插入的块合并到找到的块
        pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
			  //要插入的块指向找到的块。即两个块合并了
        pxBlockToInsert = pxIterator;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    //要插入块的首地址
    puc = (uint8_t *) pxBlockToInsert;
    //如果要插入的块能和后面合并就再合并
    if((puc+pxBlockToInsert->xBlockSize) == (uint8_t *) pxIterator->pxNextFreeBlock )
    {
			  //没到最后
        if( pxIterator->pxNextFreeBlock != pxEnd )
        {
            //记录合并后的大小
            pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
					  //指向更后一个自由块
            pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
        }
				//已经是最后了,插入块就指向End
        else
        {
            pxBlockToInsert->pxNextFreeBlock = pxEnd;
        }
    }
		//如果不能合并就让插入块执行当前块后一个块
    else
    {
        pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
    }

    //如果当前块和要插入块指向不同,当前块的下一块指向要插入的块
    if( pxIterator != pxBlockToInsert )
    {
        pxIterator->pxNextFreeBlock = pxBlockToInsert;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
}
/*-----------------------------------------------------------*/
//得到堆的状态
void vPortGetHeapStats( HeapStats_t * pxHeapStats )
{
    BlockLink_t * pxBlock;
	  //块数量、最大的大小、最小的大小(先设置为最大分配大小)
    size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY;
		//挂起所有任务
    vTaskSuspendAll();
    {
			  //当前块从开始节点下一个开始
        pxBlock = xStart.pxNextFreeBlock;

        //如果开始的下一个有东西就是初始化过了,否则就是没初始化的
        if( pxBlock != NULL )
        {
            do
            {
                //块数量加1
                xBlocks++;
								//统计最大的块大小
                if( pxBlock->xBlockSize > xMaxSize )
                {
                    xMaxSize = pxBlock->xBlockSize;
                }
								//统计最小的块大小
                if( pxBlock->xBlockSize < xMinSize )
                {
                    xMinSize = pxBlock->xBlockSize;
                }

                //移动到链中的下一个块,一直到最后
                pxBlock = pxBlock->pxNextFreeBlock;
            } while( pxBlock != pxEnd );
        }
    }
		//恢复任务挂起
    (void) xTaskResumeAll();
    //设置最大块、最小块、块数到对状态结构体
    pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize;
    pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize;
    pxHeapStats->xNumberOfFreeBlocks = xBlocks;
		//跟踪调试用的位点
    taskENTER_CRITICAL();
    {
        pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining;
        pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations;
        pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees;
        pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining;
    }
    taskEXIT_CRITICAL();
}

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

FreeRTOS之heap4 的相关文章

  • FreeRTOS例程4-串口DMA收发不定长数据

    FreeRTOS例程4 串口DMA收发不定长数据 知乎 zhihu com
  • FreeRTOS内核配置说明---FreeRTOS Kernel V10.2.1

    FreeRTOS内核是高度可定制的 使用配置文件FreeRTOSConfig h进行定制 每个FreeRTOS应用都必须包含这个头文件 用户根据实际应用来裁剪定制FreeRTOS内核 这个配置文件是针对用户程序的 而非内核 因此配置文件一般
  • FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)

    我们在前面单独介绍过FreeRTOS的任务通知和消息队列 但是在FreeRTOS中任务间的通讯还有信号量 邮箱 事件组标志等可以使用 这篇文章就这些成员与消息队列和任务通知的关系进行说明分析 增加邮箱部分 任务通知发送消息 Demo 202
  • 解决错误“ #error “include FreeRTOS.h“ must appear in source files before “include event_groups.““例子分享

    今天来给大家分享一下 关于之前自己在学习FreeRTOS过程中遇到的一个错误提示 话不多说 我们直接来看 错误分析 首先 我们看一下错误的提示 error 35 error directive include FreeRTOS h must
  • FreeRTOS学习笔记 6 - 互斥量

    目录 1 创建 2 获取 3 释放 4 测试 FreeRTOS不支持调度方式的设置 所以下面2个宏定义可以随意设置值 define RTOS IPC FLAG FIFO 0x00 define RTOS IPC FLAG PRIO 0x01
  • 【FreeRTOS】多任务创建

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • freeRTOS手册 第六章 . 中断管理

    如果我对本翻译内容享有所有权 允许任何人复制使用本文章 不会收取任何费用 如有平台向你收取费用与本人无任何关系 第六章 中断管理 章节介绍和范围 事件 嵌入式实时系统必需对环境中的事件做出响应 比如 外部网络设备收到一个发送给TCP IP栈
  • FreeRTOS_中断

    传送门 博客汇总帖 传送门 Cortex M3 中断 异常 传送门 Cortex M3笔记 基础 笔记内容参考 正点原子的FreeRTOS开发手册 cortex m3权威指南 Cortex M3和Cortex M4权威指南等 文中stm32
  • 基于HAL库的FREERTOS-----------三.队列

    一 队列简介 在实际的应用中 常常会遇到一个任务或者中断服务需要和另外一个任务进行 沟通交流 这个 沟通交流 的过程其实就是消息传递的过程 在没有操作系统的时候两个应用程序进行消息传递一般使用全局变量的方式 但是如果在使用操作系统的应用中用
  • FreeRTOS临界段和开关中断

    http blog sina com cn s blog 98ee3a930102wg5u html 本章教程为大家讲解两个重要的概念 FreeRTOS的临界段和开关中断 本章教程配套的例子含Cortex M3内核的STM32F103和Co
  • FreeRTOS学习(三)开关中断

    声明及感谢 跟随正点原子资料学习 在此作为学习的记录和总结 环境 keil stm32f103 背景知识 Cotex M3的NVIC最多支持240个IRQ 中断请求 1个不可屏蔽 NMI 1个Systick 滴答定时器 Cortex M处理
  • 基于STM32的FreeRTOS学习之中断测试实验(五)

    记录一下 方便以后翻阅 本章内容是接着上一章节进行的实际演练 1 实验目的 FreeRTOS可以屏蔽优先级低于configMAX SYSCALL INTERRUPT PRIORITY的中断 不会屏蔽高于其的中断 本次实验就是验证这个说法 本
  • 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临界段

    1 临界段 在访问共享资源时不希望被其他任务或者中断打断的代码 这段要执行的代码称为临界段代码 2 设置临界段的目的 保护共享资源 例如 全局变量 公共函数 不可重入函数 函数里面使用 了一些静态全局变量 malloc 等 保护外设的实时性
  • FreeRTOS多任务调度器基础

    Cortex M4中SysTick调度器核心 Cortex M4中的中断管理 Cortex M4中影子栈指针 Cortex M4中SVC和PendSV异常 1 Cortex M4中SysTick调度器核心 systick每一次中断都会触发内
  • 当一个任务写入变量而其他任务读取该变量时,我们是否需要信号量?

    我正在研究 freeRtos 并且我有一个名为 x 的变量 现在 每秒只有一个任务正在写入该变量 而其他任务正在读取该变量值 我需要用互斥锁来保护变量吗 如果变量为 32 位或更小 并且其值是独立的并且不与任何其他变量一起解释 则不需要互斥
  • 如何将 void* 转换为函数指针?

    我在 FreeRTOS 中使用 xTaskCreate 其第四个参数 void const 是传递给新线程调用的函数的参数 void connect to foo void const task params void on connect
  • FreeRTOS 匈牙利表示法 [重复]

    这个问题在这里已经有答案了 我是 RTOS 和 C 编程的新手 而且我仍在习惯 C 的良好实践 因此 我打开了一个使用 FreeRTOS 的项目 我注意到操作系统文件使用匈牙利表示法 我知道一点符号 但面临一些新的 标准 FreeRTOS
  • C++ freeRTOS任务,非静态成员函数的无效使用

    哪里有问题 void MyClass task void pvParameter while 1 this gt update void MyClass startTask xTaskCreate this gt task Task 204

随机推荐

  • Openstack云平台脚本部署之Neutron网络服务配置(九)

    目录 一 简介 二 部署脚本 2 1 网络节点独立部署 2 2 控制节点与网络节点融合部署 2 3 脚本详细介绍 三 参考文档 四 源码 五 系列文章 一 简介 Openstack网络服务Neutron主要负责网络连接任务 xff0c 包括
  • Android开发java调用C简单示例

    目录 下载NDK和CMake新建NDK项目写入C 43 43 代码 xff0c 并运行 本文使用的Android Studio版本是windows版Android Studio Bumblebee 2021 1 1 Patch 2 先看下最
  • 互联网单点登录集成方案

    为了迎合公司互联网化经营 xff0c 业务部门均纷纷上马了互联网的项目 xff0c 部门应用之间各自为政 xff0c 无法形成公司整体品牌效应 xff0c 以及影响用户体验 xff0c 故 xff0c 有了以下的单点登录集成方案 概述 整合
  • 【软件教程】如何让vscode连接ssh时记住密码

    准备软件 客户机安装vscode xff08 vscode官网https code visualstudio com xff09 客户机和服务器配置ssh xff0c 确保能够连接 VSCode ssh记住密码教程 一 在Client客户机
  • 车辆检测--DAVE: A Unified Framework for Fast Vehicle Detection and Annotation

    DAVE A Unified Framework for Fast Vehicle Detection and Annotation ECCV2016 本文使用深度学习进行车辆检测和属性学习 提出的系统为 Detection and Ann
  • 对抗学习用于目标检测--A-Fast-RCNN: Hard Positive Generation via Adversary for Object Detection

    A Fast RCNN Hard Positive Generation via Adversary for Object Detection CVPR 2017 Caffe code https github com xiaolonw a
  • 人脸识别--SphereFace: Deep Hypersphere Embedding for Face Recognition

    SphereFace Deep Hypersphere Embedding for Face Recognition CVPR2017 https github com wy1iu sphereface pytorch https gith
  • 运动相机检测无人机-- Detecting Flying Objects using a Single Moving Camera

    Detecting Flying Objects using a Single Moving Camera PAMI 2017 http cvlab epfl ch research unmanned detection https dri
  • [转载]Python SMTP发送邮件-smtplib模块

    在进入正题之前 xff0c 我们需要对一些基本内容有所了解 xff1a 常用的电子邮件协议有SMTP POP3 IMAP4 xff0c 它们都隶属于TCP IP协议簇 xff0c 默认状态下 xff0c 分别通过TCP端口25 110和14
  • c语言和c++有什么区别

    差不多是win98跟winXP的关系 C 43 43 是在C的基础上增加了新的理论 xff0c 玩出了新的花样 所以叫C加加 C是一个结构化语言 xff0c 它的重点在于算法和数据结构 C程序的设计首要考虑的是如何通过一个过程 xff0c
  • 梳理LVM逻辑卷管理,

    在Linux操作系统会时不时碰到卷有关的操作 xff0c 以下也是罗列了相关操作内容 xff0c 仅供参考 创建PV VG LV的方法 将各物理磁盘或分区的系统类型设为Linux LVM xff0c 其system ID为8e xff0c
  • 使用sqlyog连接 Mysql 出现1251错误

    使用sqlyog连接 Mysql 出现1251错误 简述 xff1a 1251 client does not support authentication protocol requested by server consider upg
  • 准备给ubuntu18.04安装杀毒软件

    如题 xff0c 电脑最近总出现些奇奇怪怪的小问题 xff0c 还是得装个杀毒软件 xff0c 看是不是中病毒了 输入sudo apt get install clamtk 安装完成后 xff0c 输入clamtk 即可 xff0e 卸载方
  • 使用Nginx代理地址

    DotNetCore在Linux发布时候 xff0c 由于不止一个产品组发布网站 xff0c 不像以前大家都用IIS的80发布网站 那么就存在大家抢80端口的情况 xff0c 为了让大家不比加上端口为此用Nginx代理URL实现网站地址代理
  • CentOS安装Cache数据库

    适用CentOS7 6 CentOS8上安装Intersystem公司的Cache数据库 xff0c 资料基本是空白 xff0c 分享一下 首先安装解压软件unzip和libicu xff0c 最小化安装的缺 xff0c 全安装的不缺 yu
  • Cache数据库之ECP搭建

    Cache作为非关系数据库 xff0c 其强大毋庸置疑 首先其Globle结构 xff0c 直接暴露的表Globel数据 xff0c 以及提供的M语言操作Globle达到的最优查询速度 ECP xff08 企业缓存协议 xff09 更是提供
  • Sebia电泳绘图

    Sebia这仪器真是个奇葩的存在 自己仪器有图不存文件 xff0c LIS要的话还得自己按数据绘制 还有蛋白电泳 固定电泳 画不画参考线等不同要求 xff08 奇葩的很 xff09 按理这种事不属于lis范围 xff0c 无奈国内lis太卷
  • nginx代理与负载均衡

    随着谷歌浏览器不断的改变https调用websocket和非https资源的策略 xff0c 从谷歌大概70以后不允许https调用非https资源和ws的websocket 后面实现了wss解决谷歌这一策略的影响 随着谷歌到90后版本限制
  • FreeRTOS学习第一篇

    之前在STM32Nano开发板开发是基于裸机开发 xff0c 即自己在main方法写死循环 死循环轮流执行各个任务逻辑的方法 这样做直接简单 xff0c 但是不同任务有不同优先级 xff0c 对CPU响应要求不同 逻辑容易某个任务卡住了 x
  • FreeRTOS之heap4

    操作系统离不开内存管理 FreeRTOS提供了5种内存管理方法 实现在portable MemMang里heap1到heap5 每种管理方案策略不同 我采用的是比较有代表性的heap4管理方案 该模式定义了ucHeap全局数组充当堆内存池