FreeRTOS源码分析与应用开发05:信号量

2023-05-16

目录

1. 信号量概述

1.1 信号量概念

1.2 4种信号量

1.2.1 二值信号量

1.2.2 计数信号量

1.2.3 互斥信号量

1.2.4 递归互斥信号量

1.3 信号量相关控制结构

1.3.1 队列结构

1.3.2 任务结构

2. 二值信号量

2.1 创建二值信号量

2.2 获取二值信号量

2.2.1 任务级获取

2.2.2 中断级获取

2.3 释放二值信号量

2.3.1 任务级释放

2.3.2 中断级释放

3. 计数信号量

3.1 创建计数信号量

3.2 获取计数信号量

3.3 释放计数信号量

4. 互斥信号量

4.1 优先级翻转

4.1.1 优先级翻转现象

4.1.2 优先级继承原理

4.2 创建互斥信号量

4.3 获取互斥信号量

4.4 释放互斥信号量

5. 递归互斥信号量

5.1 创建递归互斥信号量

5.2 获取递归互斥信号量

5.3 释放递归互斥信号量


1. 信号量概述

1.1 信号量概念

① 信号量是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问

从本质上说,信号量是一个非负整数值,该数值对应有效的资源数量。所有成功获取信号量的任务都会将该整数减1,当整数值为0时,所有试图获取信号量的任务都会获取失败或进入阻塞状态

1.2 4种信号量

1.2.1 二值信号量

① 二值信号量通常用于互斥访问或同步,但是由于不具备优先级继承功能,所以更适合用于任务间或任务与中断间的同步

② 二值信号量在实现上是一个长度为1的队列,且队列项长度为0。当前队列中的消息数量,就标识了信号量的数量

说明:基于消息队列实现信号量,可以减小代码体积

1.2.2 计数信号量

① 计数信号量在实现上是一个长度可以不为1的队列,且队列项长度为0

② 在创建计数信号量时,可以同时指定信号量初始值

③ 计数信号量通常用于如下2个场景,

a. 事件计数

事件每发生一次就释放一次信号量,此时计数信号量的初始值应为0

b. 资源管理

信号量值标识当前可用资源个数,使用资源的任务需要先获取信号量,使用完成后释放信号量,此时计数信号量的初始值应为资源总数

1.2.3 互斥信号量

① 互斥信号量是拥有优先级继承机制的二值信号量

因为拥有优先级继承机制,互斥信号量更适合用于资源互斥访问

互斥信号量不能用于中断服务函数中

1.2.4 递归互斥信号量

① 递归互斥信号量在互斥信号量的基础上,增加了递归持有特性,即持有互斥递归信号量的任务可以再次获取该信号量

递归释放的次数要与递归获取的次数相匹配

互斥信号量也不能用于中断服务函数中

说明:互斥信号量与递归互斥信号量不能用于ISR中,是因为ISR中没有任务优先级的概念,无法正确处理优先级继承,详见后文

1.3 信号量相关控制结构

1.3.1 队列结构

① pcHead

在互斥信号量 & 递归互斥信号量中,被宏定义为uxQueueType,并被初始化为queueQUEUE_IS_MUTEX(NULL),用于标识当前队列类型

说明:为什么不通过ucQueueType判断队列类型呢 ?

ucQueueType成员被包含在configUSE_TRACE_FACILITY条件编译选项中,而该编译选项是不一定被开启的

② pcTail

在互斥信号量 & 递归互斥信号量中,被宏定义为pxMutexHolder,标识当前持有信号量的任务。初始值为NULL,即没有被任务持有

③ uxRecursiveCallCount

在递归互斥信号量中,标识当前信号量被递归获取的次数

④ uxMessagesWaiting

当前队列中的消息数量,在信号量中,就是当前可用的信号量数量

⑤ uxLength

队列长度,在信号量中,就是当前信号量的最大个数

⑥ uxItemSize

队列项长度,在信号量中,队列项长度必须为0

1.3.2 任务结构

在TCB_t结构中,如下2个字段与信号量有关,

① uxBasePriority

在互斥 & 递归互斥信号量中,记录任务的原始优先级,用于实现优先级继承机制

② uxMutexesHeld

在递归 & 互斥递归信号量中,记录该任务持有的递归 & 互斥递归信号量总数,只有当该计数减少到0时,才会恢复该任务的优先级

2. 二值信号量

2.1 创建二值信号量

#define semSEMAPHORE_QUEUE_ITEM_LENGTH	( ( uint8_t ) 0U )

#define xSemaphoreCreateBinary() \
xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, \
queueQUEUE_TYPE_BINARY_SEMAPHORE )

创建二值信号量,就是创建队列长度为1,队列项长度为0,类型为queueQUEUE_TYPE_BINARY_SEMAPHORE的队列

说明1:函数返回值

由于信号量是基于队列实现的,所以实际返回的是队列句柄。但是在semphr.h头文件中,将队列句柄类型重定义为信号量句柄,所以函数返回值类型为SemaphoreHandle_t

说明2:对应静态创建版本

与队列类似,信号量的创建也可以使用静态分配的内存,函数如下,

#define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) \
xQueueGenericCreateStatic( ( UBaseType_t ) 1, \
semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticSemaphore, \
queueQUEUE_TYPE_BINARY_SEMAPHORE )

此时需要传递一个指向StaticSemaphore_t类型的变量的地址

说明3:旧版本创建二值信号量函数

#define vSemaphoreCreateBinary( xSemaphore ) \
{ \
    ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, \
    semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \
    if( ( xSemaphore ) != NULL ) \
    { \
        ( void ) xSemaphoreGive( ( xSemaphore ) );\
    } \
} 

旧版本创建二值信号量函数与新版本有2处不同,

① 调用vSemaphoreCreateBinary时,需要将信号量句柄作为参数传递给函数

② 旧版本在创建完信号量之后会释放一次,也就是旧版本初始状态的二值信号量是可以获取的,而新版本初始状态的二值信号量是不可获取的

这点修改主要是作者为二值信号量预设的使用场景就是任务之间或任务和中断之间的同步

说明4:删除信号量

#define vSemaphoreDelete( xSemaphore ) \
vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )

对于各种信号量,删除信号量操作都是直接调用vQueueDelete函数删除队列。由于在FreeRTOS中,并不提倡不断动态创建 & 删除内核对象,所以删除队列的实现也是很简陋的(详见消息队列章节笔记)

2.2 获取二值信号量

2.2.1 任务级获取

#define xSemaphoreTake( xSemaphore, xBlockTime ) \
xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), \
NULL, ( xBlockTime ), pdFALSE )

任务级获取二值信号量,就是任务级获取消息

2.2.2 中断级获取

#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \
xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, \
( pxHigherPriorityTaskWoken ) )

中断级获取二值信号量,就是中断级获取消息

说明:从实现中可知,在获取信号量时,仅关心消息(也就是信号量)的个数,而不是实际的消息内容

2.3 释放二值信号量

2.3.1 任务级释放

#define semGIVE_BLOCK_TIME	( ( TickType_t ) 0U )

#define xSemaphoreGive( xSemaphore ) \
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, \
semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

任务级释放二值信号量,就是任务级发送消息,其中发送等待时间为0

2.3.2 中断级释放

#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \
xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), \
( pxHigherPriorityTaskWoken ) )

中断级释放二值信号量,就是中断级发送消息

注意:在中断中,只能使用二值信号量和计数信号量,不能使用互斥 & 递归互斥信号量

3. 计数信号量

3.1 创建计数信号量

// uxMaxCount:计数信号量最大值
// uxInitialCount:计数信号量初始值
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) \
xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )

QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount )
{
    QueueHandle_t xHandle;

    configASSERT( uxMaxCount != 0 );
    configASSERT( uxInitialCount <= uxMaxCount );

    // 创建队列长度为uxMaxCount,队列项长度为0的队列
    xHandle = xQueueGenericCreate( uxMaxCount,
    queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );

    // 如果队列创建成功,设置计数信号量初始值
    // 也就是当前队列中的可用消息数量
    if( xHandle != NULL )
    {
        ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
    }

    return xHandle;
}

说明:创建计数信号量也有相应的静态内存版本

#define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, \
pxSemaphoreBuffer ) \
xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), \
( pxSemaphoreBuffer ) )

3.2 获取计数信号量

与二值信号量相同

3.3 释放计数信号量

与二值信号量相同

4. 互斥信号量

4.1 优先级翻转

4.1.1 优先级翻转现象

导致优先级翻转的本质原因是不同优先级的任务需要获取相同的二值信号量

② 如果低优先级的L任务已经获取了二值信号量,那么高优先级的任务H获取二值信号量将失败,进而被阻塞

其实此时已经发生了优先级翻转,也就是低优先级任务L会在高优先级任务H之前运行

③ 如果有一个中等优先级且不需要获取该二值信号量的任务M,则会恶化优先级翻转的情况

由于任务M不需要持有该信号量,且可以抢占任务L,则会导致高优先级任务H在中低优先级任务M和L之后运行

4.1.2 优先级继承原理

① 当高优先级任务H想要获取互斥信号量,并且判断出信号量目前被更低优先级的任务L占有时,则暂时将任务L的优先级提升到与任务H优先级相同

此时,中等优先级的任务M将无法抢占原先的低优先级任务L,这样就可以确保任务L尽快完成并释放信号量

② 当任务L释放互斥信号量时,将任务L的优先级还原

说明1:优先级继承机制只能缓解优先级翻转现象

根据上述分析,即使拥有优先级继承机制,高优先级任务H也必须等待低优先级任务L运行完成并释放互斥信号量

说明2:由于RTOS中需要避免优先级翻转,因此在系统设计时就要考虑可能导致优先级翻转的场景,避免不同优先级的任务持有同一个互斥信号量

4.2 创建互斥信号量

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
{
    Queue_t *pxNewQueue;
    const UBaseType_t uxMutexLength = ( UBaseType_t ) 1,
    uxMutexSize = ( UBaseType_t ) 0;

    // 创建队列长度为1,队列项长度为0的队列
    pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength,
    uxMutexSize, ucQueueType );
    prvInitialiseMutex( pxNewQueue );

    return pxNewQueue;
}

static void prvInitialiseMutex( Queue_t *pxNewQueue )
{
    if( pxNewQueue != NULL )
    {
        // 初始化互斥 & 递归互斥信号量的持有者与类型
        pxNewQueue->pxMutexHolder = NULL;
        pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;

        // 递归互斥信号量在该成员上进行递归获取计数
        pxNewQueue->u.uxRecursiveCallCount = 0;

        // 释放一次互斥信号量
        // 互斥信号量的初始状态为可用
        ( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U,
        queueSEND_TO_BACK );
    }
}

4.3 获取互斥信号量

获取互斥信号量仍然使用xSemaphoreTake函数,要点在于处理优先级继承(实际处理在xQueueGenericReceive函数中),这里分2种情况讨论,

① 可以获取互斥信号量

#if ( configUSE_MUTEXES == 1 )
{
    if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
    {
        // 设置互斥 & 递归互斥信号量的持有者
        pxQueue->pxMutexHolder =
        ( int8_t * ) pvTaskIncrementMutexHeldCount();
    }
}
#endif /* configUSE_MUTEXES */

// 增加当前任务获取互斥 & 递归互斥信号量的个数
// 并返回当前任务句柄
void *pvTaskIncrementMutexHeldCount( void )
{
    if( pxCurrentTCB != NULL )
    {
        ( pxCurrentTCB->uxMutexesHeld )++;
    }

    return pxCurrentTCB;
}

② 不可获取互斥信号量

#if ( configUSE_MUTEXES == 1 ) // 获取不到互斥信号量的分支
{
    if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
    {
        // 进入临界段
        taskENTER_CRITICAL();
        {
            // 对互斥 & 递归互斥信号量持有者进行优先级继承处理
            vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
        }
        taskEXIT_CRITICAL();
    }
}
#endif

void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
{
    TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;

    if( pxMutexHolder != NULL )
    {
        // 只有当前进程优先级高于互斥 & 递归互斥信号量持有者时,
        // 才需要进行优先级继承操作
        if( pxTCB->uxPriority < pxCurrentTCB->uxPriority )
        {
            // 如果持有者任务的事件列表项排序值没有在被使用的话,
            // 则使用当前任务优先级修改该值
            if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) &
            taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
            {
                listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ),
                ( TickType_t ) configMAX_PRIORITIES - 
                ( TickType_t ) pxCurrentTCB->uxPriority );
            }

            if( listIS_CONTAINED_WITHIN(
            &( pxReadyTasksLists[ pxTCB->uxPriority ] ),
            &( pxTCB->xStateListItem ) ) != pdFALSE )
            {
                // 如果持有者任务在就绪列表
                // 则将该任务从当前就绪列表移除,并在继承优先级之后,
                // 加入新的就绪列表
                if( uxListRemove( &( pxTCB->xStateListItem ) ) ==
                ( UBaseType_t ) 0 )
                {
                    taskRESET_READY_PRIORITY( pxTCB->uxPriority );
                }

                pxTCB->uxPriority = pxCurrentTCB->uxPriority;
                prvAddTaskToReadyList( pxTCB );
            }
            else
            {
                // 如果持有者任务不在就绪列表,则仅继承优先级
                pxTCB->uxPriority = pxCurrentTCB->uxPriority;
            }
        }
    }
}

说明1:互斥 & 递归互斥信号量不能在中断ISR中使用

因为中断ISR中没有任务优先级的概念,所以无法正确进行优先级继承处理

说明2:对事件列表项排序值的使用

① 在FreeRTOS的事件标志组机制中会以代码中的方式使用该值,此时无法修改该值,详见相关章节笔记

② 事件列表项排序值的另一种更常见用法,是将任务加入内核对象的等待列表时进行排序,优先级越高,事件列表项排序值越小,则任务越在内核对象等待列表的队首位置

一个任务在获取互斥信号量之后,也可能被阻塞在其他内核对象中(e.g. 再次获取另一个信号量),因此其事件列表项可能被加入内核对象的等待队列

从原理上说,在进行优先级继承的过程中,应该以新的事件列表项排序值重新加入内核对象等待队列,但是代码中并未进行此操作

4.4 释放互斥信号量

释放互斥信号量仍然使用xSemaphoreGive函数,要点也在优先级继承处理,实际在prvCopyDataToQueue函数中实现

// 队列项长度为0,说明是处理信号量的分支
if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
{
    #if ( configUSE_MUTEXES == 1 )
    {
        if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
        {
            // 处理优先级继承
            xReturn = xTaskPriorityDisinherit(
            ( void * ) pxQueue->pxMutexHolder );
            // 将互斥 & 递归互斥信号量的持有者置为NULL
            pxQueue->pxMutexHolder = NULL;
        }
    }
    #endif /* configUSE_MUTEXES */
}

BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
{
    TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
    BaseType_t xReturn = pdFALSE;

    if( pxMutexHolder != NULL )
    {
        // 互斥 & 递归互斥信号量只能由持有者释放
        configASSERT( pxTCB == pxCurrentTCB );

        configASSERT( pxTCB->uxMutexesHeld );
        // 减少当前任务持有的互斥 & 递归互斥信号量计数
        ( pxTCB->uxMutexesHeld )--;

        // 只有确实发生过优先级继承,此处才需要恢复优先级
        if( pxTCB->uxPriority != pxTCB->uxBasePriority )
        {
            // 只有当持有者任务持有的互斥 & 递归互斥信号量个数为0时,
            // 才恢复任务优先级
            if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )
            {
                // 释放互斥 & 递归互斥信号量的一定是持有者,
                // 且持有者任务当前一定正在运行
                // 将该任务从当前就绪列表移出,恢复优先级后
                // 重新加入对应原始优先级的就绪列表
                if( uxListRemove( &( pxTCB->xStateListItem ) ) ==
                ( UBaseType_t ) 0 )
                {
                    taskRESET_READY_PRIORITY( pxTCB->uxPriority );
                }

                pxTCB->uxPriority = pxTCB->uxBasePriority;

                listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ),
                ( TickType_t ) configMAX_PRIORITIES -
                ( TickType_t ) pxTCB->uxPriority );
                prvAddTaskToReadyList( pxTCB );

                // 标识进行了恢复优先级的操作
                // 外部需要触发一次任务调度
                // 恢复优先级一定是降低了当前任务的优先级
                // 所以要唤醒正在等待的高优先级任务(假设他还在等待的话)
                xReturn = pdTRUE;
            }
        }
    }
    return xReturn;
}

5. 递归互斥信号量

5.1 创建递归互斥信号量

#define xSemaphoreCreateRecursiveMutex() \
xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

可见初始化递归互斥信号量和初始化互斥信号量的操作是一样的,只是注册时的信号量类型不同

5.2 获取递归互斥信号量

#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) \
xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )

BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex,
TickType_t xTicksToWait )
{
    BaseType_t xReturn;
    Queue_t * const pxMutex = ( Queue_t * ) xMutex;

    configASSERT( pxMutex );

    if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() )
    {
        // 如果该递归互斥信号量已经被当前任务持有过,
        // 则增加递归持有计数
        ( pxMutex->u.uxRecursiveCallCount )++;
        xReturn = pdPASS;
    }
    else
    {
        // 如果该递归互斥信号量的持有者不是当前任务,
        // 可能没有被任务持有,或者已经被其他任务持有,
        // 则进行一次正常的获取操作,如果获取成功,则增加递归持有计数
        xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait,
        pdFALSE );

        if( xReturn != pdFAIL )
        {
            ( pxMutex->u.uxRecursiveCallCount )++;
        }
    }

    return xReturn;
}

5.3 释放递归互斥信号量

#define xSemaphoreGiveRecursive( xMutex ) \
xQueueGiveMutexRecursive( ( xMutex ) )

BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
    BaseType_t xReturn;
    Queue_t * const pxMutex = ( Queue_t * ) xMutex;

    configASSERT( pxMutex );

    // 只有递归互斥信号量的持有者才能释放
    if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() )
    {
        // 减少递归持有计数
        ( pxMutex->u.uxRecursiveCallCount )--;

        // 当递归持有计数为0时,实际释放信号量
        // 在xQueueGenericSend函数中会处理优先级继承
        if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 )
        {
            ( void ) xQueueGenericSend( pxMutex, NULL,
            queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
        }

        xReturn = pdPASS;
    }
    else
    {
        xReturn = pdFAIL;
    }
    
    return xReturn;
}

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

FreeRTOS源码分析与应用开发05:信号量 的相关文章

  • FreeRTOS快速上手

    FreeRTOS使用 一 源码下载和移植文件提取 1 1 源码下载 在网站https sourceforge net projects freertos 可以找到freertos最新的源码 1 2 移植文件提取 根据第一步 我们会得到一个f
  • FreeRTOS软件定时器创建、复位、开始和停止(备忘)

    目录 一 简介 1 1 开发环境 1 2 摘要 二 STM32CubeIDE配置 三 创建定时器 3 1 头文件声明 3 2 工程文件定义 3 3 创建定时器 3 4 开启 复位 和关闭定时器 四 定时器回调函数 一 简介 1 1 开发环境
  • Freertos中vTaskDelay()是怎么用的

    1 常见的使用场景 void vLED Task void pvParameters while 1 Heartbeat LED vTaskDelay 1000 portTICK RATE MS 说明 上面这段代码的意思是 led翻转后经过
  • FreeRTOS学习笔记<中断>

    中断概念 Cortex M的NVIC最多支持240个IRQ 中断请求 1个不可屏蔽中断 NMI 1个Systick 滴答定时器 定时器中断和多个系统异常 Cortex M处理器有多个用于管中断和异常的可编程寄存器 这些寄存器大多数都在 NV
  • 基于HAL库的FREERTOS----------一.任务

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

    FreeRTOS临界区是指那些必须完整运行 不能被打断的代码段 比如有的外设的初始化需要严格的时序 初始化过程中不能被打断 FreeRTOS 在进入临界区代码的时候需要关闭中断 当处理完临界区代码以后再打开中断 FreeRTOS 系统本身就
  • 【FreeRTOS(三)】任务状态

    文章目录 任务状态 任务挂起 vTaskSuspend 取消任务挂起 vTaskResume 挂起任务调度器 vTaskSuspendAll 取消挂起任务调度器 xTaskResumeAll 代码示例 任务挂起 取消任务挂起 代码示例 挂起
  • FreeRTOS_中断

    传送门 博客汇总帖 传送门 Cortex M3 中断 异常 传送门 Cortex M3笔记 基础 笔记内容参考 正点原子的FreeRTOS开发手册 cortex m3权威指南 Cortex M3和Cortex M4权威指南等 文中stm32
  • 【FreeRTOS】任务通知的使用

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • stm32f103zet6移植标准库的sdio驱动

    sdio移植 st官网给的标准库有给一个用于st出的评估板的sdio外设实现 但一是文件结构有点复杂 二是相比于国内正点原子和野火的板子也有点不同 因此还是需要移植下才能使用 当然也可以直接使用正点原子或野火提供的实例 但为了熟悉下sdio
  • FreeRTOS之软件定时器

    FreeRTOS之软件定时器 声明 本人按照正点原子的FreeRTOS例程进行学习的 欢迎各位大佬指责和批评 谢谢 include sys h include delay h include usart h include led h in
  • FreeRTOS学习(三)开关中断

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

    中断 当CPU在执行某一事件A时 发生另外一个更重要紧急的事件B请求CPU去处理 产生了中断 于是CPU暂时中断当前正在执行的事件A任务而对对事件B进行处理 CPU处理完事件B后再返回之前中断的位置继续执行原来的事件A 这一过程统称为中断
  • RT-Thread记录(五、RT-Thread 临界区保护与FreeRTOS的比较)

    本文聊聊临界区 以及RT Thread对临界区的处理 通过源码分析一下 RT Thread 对临界区保护的实现以及与 FreeRTOS 处理的不同 目录 前言 一 临界区 1 1 什么是临界区 1 2 RTOS中的临界区 二 RT Thre
  • freeRTOS出现任务卡死的情况。

    最近在做一个产品二代升级的项目 代码是上一任工程师留下的 很多BUG 而且融合了HAL库和LL库 以及github上下载的GSM源码 很不好用 我这边是将2G模块换成了4G 且添加了单独的BLE模块 因此只在源码的基础上 去除2G和BLE代
  • STM32 Freertos 添加 外部sram heap_5.c

    1 添加外部SRAM 初始化 2 添加heap 5 c 3 初始化heap 5 c 外部堆栈 Define the start address and size of the two RAM regions not used by the
  • FreeRTOS实时操作系统(三)任务挂起与恢复

    系列文章 FreeRTOS实时操作系统 一 RTOS的基本概念 FreeRTOS实时操作系统 二 任务创建与任务删除 HAL库 FreeRTOS实时操作系统 三 任务挂起与恢复 FreeRTOS实时操作系统 四 中断任务管理 FreeRTO
  • FreeRTOSConfig.h 配置优化及深入

    本篇目标 基于上一篇的移植freertos stm32f4 freertos 上 修改 FreeRTOSConfig h 文件的相关配置来优化辅助 FreeRtos 的使用 并且建立一些基本功能 信号量 消息地列等 的简单应用位于 stm3
  • C++ freeRTOS任务,非静态成员函数的无效使用

    哪里有问题 void MyClass task void pvParameter while 1 this gt update void MyClass startTask xTaskCreate this gt task Task 204
  • 小型 ARM 微控制器的 RTOS 内核之间的可量化差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有许多不同的 RTOS 可用于微控制器 我专门寻找支持 ARM Cortex M 处理器的 RTOS 另外 我对闭源解决方案不感兴趣 试图从网站

随机推荐

  • Swift的可选链,类型转换和扩展

    可选链 Optional Chaining 可选链是一种请求或调用属性 xff0c 方法 xff0c 子脚本的过程 可选性体现于请求或调用的目标当前可能为nil 若不为nil则成功调用 xff0c 否则返回nil并将链失效 调用可选链的返回
  • iOS小米遥控器的手势监听及UI实现

    这篇文章通过实例实现了一个类似小米手势遥控器的功能页面 效果图如下所示 xff1a 触摸事件的响应通过对系统的触摸实践监听来进行 通过一个数组来对点的集合进行缓存和分析 void touchesBegan NSSet touches wit
  • 博客搬家至Github

    为了使用Markdown写作更方便一些 xff0c 以后将使用github pages来管理博客 地址 xff1a Rannie s Page 欢迎来访
  • C++使用http向服务器发送json数据

    span class token macro property span class token directive hash span span class token directive keyword include span spa
  • 如何使用Git将Github项目拉到本地

    如何使用Git将Github项目拉到本地 前言 因为国内访问GIthub速度比较慢 xff0c 复制粘贴代码又慢效率也低 xff0c 所以建议下载Git工具 xff0c 直接把Github的项目整个下载到本地的文件夹 安装配置git 步骤如
  • 笔记本 - 数据分析百宝箱

    Numpy 一 基本操作 xff1a 属性 xff1a improt numpy as np 生成数组 xff1a array 61 np array 1 2 3 2 3 4 xff0c dtype 61 np int float arra
  • Faiss(5):IndexIVFPQ原理

    说明 原本想尝试自己从头写 xff0c 但看了下网上的各位前辈的博客后 xff0c 感觉自己还是才疏学浅 xff0c 没有理解透彻 xff0c 所以在这里做个搬运工 xff0c 偶尔加些个人的理解在里面 原文链接 xff1a https b
  • cmake(3):编译库和链接可执行文件

    1 说明 在实际开发的过程当中 xff0c 我们会经常需要将部分程序编译成静态或动态库的形式 xff0c 供其他应用程序调用而不是将所有文件一次编译为一个可执行文件 这篇笔记就记录使用cmake编译动态和静态库以及将库链接到可执行文件中的过
  • RTOS原理与实现02:基本任务切换实现

    目录 1 任务定义与切换原理 1 1 任务是什么 1 1 1 任务的外观 1 1 2 任务的内在 1 2 任务切换原理 1 2 1 任务切换的本质 1 2 2 要保存哪些任务运行状态 1 2 3 任务运行状态保存方案 1 3 设计实现 1
  • cmake(5):选择编译器及设置编译器选项

    1 说明 在实际的项目平台中可能安装有多个版本的编译器 xff0c 同时由于不同的功能可能会需要设置不同的编译参数 xff0c 这篇笔记就记录如何选择指定的编译器和配置参数 2 选择编译器 2 1 初始状态 我使用的开发平台默认安装的gcc
  • Faiss(14):IndexIVFPQ的CPU search过程分析

    1 说明 之前分析过了faiss 在GPU中的search过程 xff0c 这里分析一下IndexIVFPQ在CPU中的search过程 xff0c 即不将index拷贝到GPU中 2 过程分析 2 1 python接口 CPU searc
  • cmake(8):install命令详解

    1 说明 之前的示例中有提到使用cmake的install命令来自动安装库和头文件 xff0c 但是只是使用到了install命令很基础很少的部分 xff0c 其实该命令作用十分丰富 xff0c 本篇文档用于说明该命令的详细使用方法 2 i
  • cmake(9):包含指定目录的头文件

    1 说明 在编译程序时 xff0c 如果需要用到外部的头文件 xff0c 而该头文件又没有被添加到系统默认的路径中 xff08 如 xff1a usr include xff0c usr local include和 usr lib gcc
  • cmake(10):使用cmake编译linux驱动或内核模块

    1 说明 这篇笔记用于说明如何使用cmake构建Linux驱动 xff0c 这样可以方便地将driver和app作为一个整体统一构建 2 示例 首先来看一个代码示例 xff0c 为了简化起见 xff0c 我直接在驱动目录下进行构建而没有作为
  • Boost(1):Boost库简介及安装

    1 Boost库介绍 Boost是一个功能强大 构造精巧 跨平台 开源并且完全免费的C 43 43 程序库 xff0c 在1998年由Beman G Dawes发起倡议并建立 使用了许多现代C 43 43 编程技术 xff0c 内容涵盖字符
  • Ubuntu:与Windows共享文件夹

    1 说明 我个人更喜欢在windows下编辑代码或文档 xff0c 而运行环境又经常在Linux环境下进行 xff0c 那么Windows和Linux之间的协作就显得很有必要了 通常有两种方式来实现两个系统之间的文件共享 xff1a 在Li
  • C语言处理参数的 getopt() 函数

    前言 C语言程序主要通过 main 函数的参数来传递命令行参数 xff1a 默认传递命令行参数 int main int argc char argv 其中 argc 表示参数个数 xff08 包含程序本身 xff09 xff0c argv
  • SPD5详解

    SPD介绍 SPD xff08 serial presence detect xff09 xff0c 即串行存在检测 xff0c 是DIMM的相关描述信息 在每根内存条上 xff0c 都有一份SPD数据 xff0c 这份数据保存在一个可擦写
  • 基于CentOS更新 glibc - 解决 `GLIBC_2.29‘ not found

    说明 在一个 CentOS Stream8 上安装或运行某些程序时 xff0c 报 96 GLIBC 2 29 39 not found xff0c 因为系统自带的 glibc 库版本只到 glibc 2 28 strings usr li
  • FreeRTOS源码分析与应用开发05:信号量

    目录 1 信号量概述 1 1 信号量概念 1 2 4种信号量 1 2 1 二值信号量 1 2 2 计数信号量 1 2 3 互斥信号量 1 2 4 递归互斥信号量 1 3 信号量相关控制结构 1 3 1 队列结构 1 3 2 任务结构 2 二