tag 标签: freertos

相关帖子
相关博文
  • 2025-5-18 20:54
    73 次阅读|
    0 个评论
    7. ESP32开发之freeRTOS的互斥量
    什么是互斥量 互斥量的应用场合 互斥量的API函数 基本代码结构 互斥量使用举例 递归锁 递归锁举例 总结 什么是互斥量 在freeRTOS中,多个任务访问一块共享资源,会产生竞争现象。 比如马路上只有一个很早以前的电话亭,A、B都想要打电话,然后他们就开始打架了。但是如果A先进去了然后把门锁了,B想进去打电话的话只能在外面等,必须等到A把门锁打开。 互斥量的应用场合 像freeRTOS的多任务系统,任务A正在使用某个资源,还没用完的时候,任务B也来使用,就可能会导致问题。 就比如串口,任务A正在用串口发送数据,此时任务B也来用这个串口发送数据,这样就会导致数据混乱。 简而言之,多任务使用共享资源的情况下,就需要使用互斥量了。 这里有个特别强调的注意点 :按照正常的情况来说,只要任务A获取了互斥量,其他任务都无法释放互斥量才对。但是freeRTOS中并没有实现,其他任务也可释放互斥量。所以在freeRTOS中,大家默认谁获取互斥量,就该谁释放。 互斥量的API函数 创建互斥量 /* 返回值:正确返回SemaphoreHandle_t 变量,错误返回NULL */ SemaphoreHandle_t xSemaphoreCreateMutex( void ); 获取 /* 参数: xSemaphore : 创建的互斥量 xBlockTime : 获取锁等待时间,超时获取失败 返回值: 获取到,返回pdTRUE,反之,pdFALSE */ SemaphoreHandle_t xSemaphoreTake( xSemaphore, xBlockTime ) 释放 /* 参数: xSemaphore : 创建的互斥量 返回值: 释放成功,返回pdTRUE,反之,pdFALSE */ SemaphoreHandle_t xSemaphoreGive( xSemaphore, xBlockTime ) 基本代码结构 if(xSemaphoreTake(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ /*资源处理代码,这里可以称之为临界区*/ xSemaphoreGive(xSemaphore); } 互斥量使用举例 首先举一个错误例子,这样会有个对比 void taskA(void* pvPram) { while(1){ for(int i=0;i5;i++){ num++; ESP_LOGI("taskA","num is %d",num); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void taskB(void* pvPram) { while(1){ num++; ESP_LOGI("taskB","num is %d",num); vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { xSemaphore=xSemaphoreCreateMutex(); xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); xTaskCreatePinnedToCore(taskB,"taskB",2048,NULL,4,NULL,1); } 打印结果如下:代码目的是taskA执行过程中,num值是连续增加的,然而打印中会发现,taskA打印并不连续,会被taskB抢占。 使用mutex函数 /** * Copyright (C) 2024-2034 HalfMoon2. * All rights reserved. * * @file Filename without the absolute path * @brief Brief description * @author HalfMoon2 * @date 2025-05-07 * @version v0.1 * * @revision history: * 2025-05-07 - Initial version. */ #include stdio.h #include freertos/FreeRTOS.h #include freertos/task.h #include freertos/semphr.h #include esp_log.h SemaphoreHandle_t xSemaphore; BaseType_t num=0; void taskA(void* pvPram) { while(1){ /*如果在1S获取到互斥量,那么执行num++五次并打印 */ if(xSemaphoreTake(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ for(int i=0;i5;i++){ num++; ESP_LOGI("taskA","num is %d",num); } xSemaphoreGive(xSemaphore); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void taskB(void* pvPram) { while(1){ if(xSemaphoreTake(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ num++; ESP_LOGI("taskB","num is %d",num); xSemaphoreGive(xSemaphore); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { xSemaphore=xSemaphoreCreateMutex(); /*为了产生竞争,两个任务优先级设置一样 */ xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); xTaskCreatePinnedToCore(taskB,"taskB",2048,NULL,4,NULL,1); } 打印结果: 分析: 从打印看,当任务A执行完五次num++后,才会轮到任务B执行一次。完全符合执行逻辑。 递归锁 递归锁的引出 假设有两个互斥量M1、M2,taskA获取了M1,taskB获取了M2。然而taskA需要再获取M2才能执行下一步,且taskB也需要再获取M1才能执行下一步。这样就导致了taskA、taskB都无法释放互斥量。导致了死锁。 再假设有一个互斥量M1,任务A获取了M1后,执行一个函数,然而此函数中也获取了M1,此时任务A还没释放M1,这将导致任务A自己把自己锁了。 然而递归锁,可以多次获取,可以解决以上问题 递归锁的函数 /*创建递归锁*/ SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void ); /*获得 */ BaseType_t xSemaphoreTakeRecursive( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait); /*释放 */ BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore ); 递归锁举例 死锁举例 SemaphoreHandle_t xSemaphore; BaseType_t num=0; void func(BaseType_t *a) { //获取互斥量等待时间设置为portMAX_DELAY,因为taskA为释放互斥量,导致此处一直等待 if(xSemaphoreTakeRecursive(xSemaphore,portMAX_DELAY)==pdTRUE){ *a=*a+1; xSemaphoreGiveRecursive(xSemaphore); } } void taskA(void* pvPram) { while(1){ /*如果在1S获取到互斥量,那么执行num++五次并打印 */ if(xSemaphoreTake(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ func(num); ESP_LOGI("taskA","num is %d",num); xSemaphoreGive(xSemaphore); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { xSemaphore=xSemaphoreCreateMutex(); /*为了产生竞争,两个任务优先级设置一样 */ xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); } 打印如下:程序将一直锁着,无法执行 使用递归锁 SemaphoreHandle_t xSemaphore; BaseType_t num=0; void func(BaseType_t *a) { //获取互斥量等待时间设置为portMAX_DELAY,因为taskA为释放互斥量,导致此处一直等待 if(xSemaphoreTakeRecursive(xSemaphore,portMAX_DELAY)==pdTRUE){ *a=*a+1; xSemaphoreGiveRecursive(xSemaphore); } } void taskA(void* pvPram) { while(1){ /*如果在1S获取到互斥量,那么执行num++五次并打印 */ if(xSemaphoreTakeRecursive(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ func(num); ESP_LOGI("taskA","num is %d",num); xSemaphoreGiveRecursive(xSemaphore); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { xSemaphore=xSemaphoreCreateRecursiveMutex(); /*为了产生竞争,两个任务优先级设置一样 */ xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); } 打印如下:发现完全可以正常打印了 总结 为了避免死锁,在中断中不要使用互斥量 如果有嵌套使用互斥量,请使用递归锁,递归的层数由queueQUEUE_TYPE_RECURSIVE_MUTEX决定 虽然freeRTOS没有实现谁锁谁开,但是开发过程中还是默认这么操作了
  • 2025-5-18 20:48
    80 次阅读|
    0 个评论
    6. ESP32开发之freeRTOS的信号量
    什么是信号量 信号量能干啥 信号量的函数 实例举例 总结 什么是信号量 简而言之,就是发出通知,接收通知的任务获得通知后去干啥啥。通知有多有少。自定义通知数量的,叫计数型信号量;只有有无(即“0”,“1”)通知的,叫二进制信号量。 信号量能干啥 资源管理:控制多个任务对共享资源(如外设、内存块)的访问权限,避免竞争条件 任务同步 :实现任务间的时序协调(如等待某个事件完成) 中断与任务通信:在中断服务程序(ISR)中快速通知任务处理事件(需使用 xxxFromISR 版本的函数) 信号量的函数 创建二进制信号量函数原型 SemaphoreHandle_t xSemaphoreCreateBinary()//返回值为SemaphoreHandle_t结构体指针,即创建的信号量,不成功返回NULL 释放信号量,使信号量计数加1 xSemaphoreGive(xSemaphore) 获取信号量,使信号量计数减1 xSemaphoreTake(xSemaphore, //创建的信号量 xBlockTime //等待时间,超时则返回pdFulse ); 创建计数型信号量 SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, //最大计数值 UBaseType_t uxInitialCount//初始计数值 );//返回值为SemaphoreHandle_t结构体指针,即创建的信号量,不成功返回NULL 删除信号量 void vSemaphoreDelete( SemaphoreHandle_t xSemaphore ); 实例 二进制信号量的使用 /** * Copyright (C) 2024-2034 HalfMoon2. * All rights reserved. * * @file Filename without the absolute path * @brief Brief description * @author HalfMoon2 * @date 2025-04-29 * @version v0.1 * * @revision history: * 2025-04-29 - Initial version. */ #include stdio.h #include freertos/FreeRTOS.h #include freertos/task.h #include freertos/semphr.h #include esp_log.h SemaphoreHandle_t semap=NULL; void taskA(void *pvParam) { while(1){ xSemaphoreGive(semap); vTaskDelay(pdMS_TO_TICKS(1000)); } } void taskB(void *pvParam) { BaseType_t num=0; while(1){ if(xSemaphoreTake(semap,pdMS_TO_TICKS(500))==pdPASS){ num++; ESP_LOGI("taskB","num is %d",num); }else{ ESP_LOGI("taskB","Take failed"); } } } void app_main(void) { semap=xSemaphoreCreateBinary(); xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); xTaskCreatePinnedToCore(taskB,"taskB",2048,NULL,3,NULL,1); } 结果解析:taskB任务每500ms获取一次信号量,当获取成功则num变量加一,然而taskA任务每1000ms发出一个信号量,所以taskB每间隔500ms会失败一次。 计数型信号量使用 /** * Copyright (C) 2024-2034 HalfMoon2. * All rights reserved. * * @file Filename without the absolute path * @brief Brief description * @author HalfMoon2 * @date 2025-04-30 * @version v0.1 * * @revision history: * 2025-04-30 - Initial version. */ #include stdio.h #include stdio.h #include freertos/FreeRTOS.h #include freertos/task.h #include freertos/semphr.h #include esp_log.h SemaphoreHandle_t semap=NULL; void taskA(void *pvParam) { while(1){ xSemaphoreGive(semap); vTaskDelay(pdMS_TO_TICKS(1000)); } } void taskB(void *pvParam) { BaseType_t num=0; while(1){ if(xSemaphoreTake(semap,pdMS_TO_TICKS(400))==pdPASS){ num++; ESP_LOGI("taskB","num is %d",num); }else{ ESP_LOGI("taskB","Take failed"); } } } void app_main(void) { semap=xSemaphoreCreateCounting(3,2); xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); xTaskCreatePinnedToCore(taskB,"taskB",2048,NULL,3,NULL,1); } 结果解析:创建计数型信号量时,初始值为2,taskA优先级高些先执行,信号量计数值加1,信号量计数值为3,taskB每400ms获取信号量前三次时成功的,第四次时,由于taskA阻塞时间为1000ms,这个时候其还没发出信号量,信号量计数值为0,所以taskB获取失败。 总结 二进制信号量可以说是计数型信号量的特殊用法。 信号量相对于消息队列要简单很多,区分好应用场景。信号量只传通知,消息队列是要传整个消息结构体的。如果看源码的话,信号量的API背后用的也是队列的API。 二进制信号量对后面学习互斥量给予了启发,一个任务如果take了此信号量不give,另一个任务想take会是什么结果呢?
  • 热度 2
    2025-4-17 16:27
    366 次阅读|
    2 个评论
    【原创奖励】ESP开发之ubuntu环境搭建
    1. 在Ubuntu官网下载Ubuntu server 20.04版本 https://releases.ubuntu.com/20.04.6/ 2. 在vmware下安装Ubuntu 3. 改Ubuntu静态IP $ sudo vi /etc/netplan/00-installer-config.yaml # This is the network config written by 'subiquity' network: renderer: networkd ethernets: ens33: #dhcp4: true addresses: - 192.168.4.251/24 nameservers: addresses: routes: - to: default via: 192.168.4.1 version: 2 $ sudo netplan apply $ ip addr show ens33 $ ip route show $ reboot 4. 使用SecureCRT使用SSH远程连接虚拟机的ubuntu 5. 安装各种必要的工具 sudo apt-get install git wget flex bison gperf python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0 net-tools 6. 新建esp32目录并进入 $ mkdir esp32 $ cd esp32 7. 拉取gitee工具(原因是从官方下载大概率会失败) $ git clone https://gitee.com/EspressifSystems/esp-gitee-tools.git 8. 执行gitee工具切换镜像脚本 $ cd esp-gitee-tools $ ./jihu-mirror.sh set 9. 拉取esp-idf源码 $ cd .. $ git clone --recursive https://github.com/espressif/esp-idf.git 10. 切换esp-idf版本分支到v5.3 $ cd esp-idf $ git checkout v5.3 $ git submodule update --init --recursive 如果提示失败或有错误试下这句:../esp-gitee-tools/submodule-update.sh 11. 更换pip源 $ pip config set global.index-url http://mirrors.aliyun.com/pypi/simple $ pip config set global.trusted-host mirrors.aliyun.com 12. 安装编译工具 $ ../esp-gitee-tools/install.sh 13. 设置环境变量并将环境变量放到.bashrc中(这样下次启动后上一步设置的环境变量会重新加载) $ source export.sh $ echo "source ~/esp32/esp-idf/export.sh" ~/.bashrc 14. 设置USB串口权限(解决下载代码时报USB串口权限问题) $ sudo usermod -aG dialout usrname usrname需要换成你的用户名 15. 重启 16. windows安装VSCODE,并安装如下插件 17. 通过以上插件远程连接ubuntu 这样就可以建立远程连接了,代码就可以在vscode进行查看和更改,但是我们会发现每次都需要输入密码,我们通过如下方式解决 解决使用VsCode远程ssh连接虚拟机ubuntu需要重复输入密码 18. VSCode中为远程主机安装插件 19. 按照上图依次安装如下插件 20. 进行插件配置 c/c++插件 改ESP-IDF配置 键盘同时按下ctl+shift+p,在弹出的对话框输入如下,这样我们就可以任意查看和跳转代码位置了。 21. 建立第一个工程 使用vscode远程连接虚拟机,在终端窗口进行操作 创建存放工程的目录并进入目录 mkdir esp32-prj cd esp32-prj 使用命令创建工程 idf.py create-project helloworld 进入目录更改目标芯片,一般默认为esp32 idf.py set-target esp32-s3 进行第一次编译 idf.py build 打开工程 如图操作,这样才可以方便的跳转和查看代码,每个新的工程都需要执行一次 编译代码 烧录,先连接开发板,并保证开发板是连接到了虚拟机 idf.py flash 监视开发板执行情况 idf.py monitor 退出监控使用CTL+]组合键 烧录指令可以和监控指令可以放在一起执行 idf.py flash monitor
  • 热度 5
    2020-8-23 17:10
    4272 次阅读|
    3 个评论
    定时器 硬件定时器: CPU内部自带的定时器模块,通过初始化、配置可以实现定时,定时时间到以后就会执行相应的定时器中断处理函数。硬件定时器一般都带有其他功能,比如PWM输出、输入捕获等功能。但缺点是硬件定时器数量少!!! 软件定时器: 软件定时器允许设置一段时间,当设置的时间到达以后就执行指定的功能函数,被定时器调用的这个功能函数就叫做定时器的回调函数。回调函数的两次执行间隔,就叫做定时器的定时周期。简而言之,当定时器的定时周期到了就会执行回调函数。 回调函数的注意事项: 软件定时器的 回调函数是在定时器服务任务中执行 的,所以一定 不能在回调函数中调用任何会阻塞任务的 API 函数 !比如,定时器回调函数中 千万不能调用 vTaskDelay()、vTaskDelayUnti() ,还有一些 访问队列或者信号量的非零阻塞时间的 API 函数也不能调用。 定时器服务 FreeRTOS软件定时器的API函数,大多都 使用队列发送命令给定时器服务任务 。这个队列叫做定时器命令队列。定时器命令队列是提供给FreeRTOS的软件定时器使用的,用户不能直接访问!通信过程如下所示: 通信过程详解: 定时器服务任务,是在任务调度器中创建的: 在创建定时器服务任务中,有创建定时器任务的函数: 定时器服务任务函数分析: 定时器消息队列的命令,是在第3个函数中处理的。 其中,创建队列的函数如下所示: 首先创建了一个定时器消息队列结构体: 接下来创建定时器命令消息队列: 定时器相关配置: 之前提到,软件定时器有一个定时器服务任务和定时器命令队列,这两个东西肯定是要配置的,配置方法和之前其他条件编译一样,而且相关的配置也是放到文件 FreeRTOSConfig.h 中的,涉及到的配置如下: 单次定时器和周期定时器: 定时器相关的操作与API函数: 1 复位软件定时器 2 创建软件定时器: 这个宏 portTICK_PERIOD_MS 非常有用,如下所示: # define configTICK_RATE_HZ ((TickType_t)1000) # define portTICK_PERIOD_MS ((TickType_t)1000 / configTICK_RATE_HZ) 其中,configTICK_RATE_HZ 为每秒的 Tick 频率,portTICK_PERIOD_MS 则为每 Tick 一次需要多少 ms(周期ms)。 这个宏定义,可以用于 将时间转换成 Tick 数,即 Tick 数 = 总时间 / (每次 Tick 的时间) 如我们需要 延时800ms ,则 Tick_count = 800 / (portTICK_PERIOD_MS) 3 开启定时器: 4 停止定时器: 测试实验 软件定时器测试实验: 创建两个任务, start_task 用于创建 timer_ctrl_task 和两个软件定时器; 周期定时器(周期1000节拍),单次定时器(周期2000节拍) timer_ctrl_task 任务通过检测2个按键,来开始或者停止两个软件定时器; 程序很简单,如下所示: TimerHandle_t auto_reload_timer_handle; // 周期定时器句柄 TimerHandle_t one_shot_timer_handle; // 单次定时器句柄 void start_task ( void *pvParameters) { taskENTER_CRITICAL(); // 创建周期定时器 auto_reload_timer_handle = xTimerCreate(( char * ) "auto_reload_timer" , (TickType_t )( 1000 / portTICK_PERIOD_MS), // 定时1秒 (UBaseType_t )pdTRUE, // 周期 ( void * ) 1 , (TimerCallbackFunction_t)auto_reload_timer_callback); // 创建单次定时器 one_shot_timer_handle = xTimerCreate(( char * ) "one_shot_timer" , (TickType_t )( 2000 / portTICK_PERIOD_MS), // 定时2秒 (UBaseType_t )pdFALSE, // 单次 ( void * ) 2 , (TimerCallbackFunction_t)one_shot_timer_callback); // 创建定时器控制任务 xTaskCreate((TaskFunction_t )timer_ctrl_task, ( char * ) "timer_ctrl_task" , ( uint16_t )TIMER_TASK_SIZE, ( void * ) NULL , (UBaseType_t )TIMER_TASK_PRIO, (TaskHandle_t * )&timer_task_Handle); vTaskDelete(Start_Task_Handle); taskEXIT_CRITICAL(); } void timer_ctrl_task ( void *pvParameters) { printf ( "\r\ntimer_ctrl_task start!\r\n" ); printf ( "press KEY1 to start auto_reload_timer\r\n" ); printf ( "press KEY2 to start one_shot_timer\r\n" ); for (;;) { if (key_scan(KEY1_GPIO_Port, KEY1_Pin) == KEY_ON) { // 发送队列阻塞时间设置为0 if (xTimerStart(auto_reload_timer_handle, 0 ) == pdFAIL) { printf ( "auto_reload_timer start failed\r\n" ); } } if (key_scan(KEY2_GPIO_Port, KEY2_Pin) == KEY_ON) { // 发送队列阻塞时间设置为0 if (xTimerStart(one_shot_timer_handle, 0 ) == pdFAIL) { printf ( "one_shot_timer start failed\r\n" ); } } vTaskDelay( 50 ); } } // 周期定时器回调函数 void auto_reload_timer_callback (TimerHandle_t pxTimer) { static uint16_t times = 0 ; printf ( "auto_reload_timer running = %d times\r\n" , ++times); } // 单次定时器回调函数 void one_shot_timer_callback (TimerHandle_t pxTimer) { static uint16_t times = 0 ; printf ( "one_shot_timer running = %d times\r\n" , ++times); } 程序运行结果如下: 多定时器共用回调函数: 前面提到,创建定时器时有一个pvTimerID,可以用于区分不同的定时器,如下所示: 函数原型如下所示: 其实就是根据定时器句柄,来获取 TimerID 通过在回调函数中调用这个函数获取ID,就能直到当前是哪个定时器调用回调函数,如下所示: // 定时器共用回调函数 void timer_common_callback (TimerHandle_t pxTimer) { static uint16_t one_shot_times = 0 ; static uint16_t auto_reload_times = 0 ; if (pvTimerGetTimerID(pxTimer) == ( void *) 1 ) { printf ( "周期定时器运行%d次\r\n" , ++auto_reload_times); } if (pvTimerGetTimerID(pxTimer) == ( void *) 2 ) { printf ( "单次定时器运行%d次\r\n" , ++one_shot_times); } } 测试结果和之前分开写回调函数一致,如下所示: 既然创建定时器时,已经给每个定时器创建了句柄,那么定时器回调函数中,应该也可以直接通过句柄来进行当前是那个定时器在调用回调函数,如下所示: // 定时器共用回调函数 void timer_common_callback (TimerHandle_t pxTimer) { static uint16_t one_shot_times = 0 ; static uint16_t auto_reload_times = 0 ; # if 0 if (pvTimerGetTimerID(pxTimer) == ( void *) 1 ) { printf ( "周期定时器运行%d次\r\n" , ++auto_reload_times); } if (pvTimerGetTimerID(pxTimer) == ( void *) 2 ) { printf ( "单次定时器运行%d次\r\n" , ++one_shot_times); } # else if (pxTimer == auto_reload_timer_handle) { printf ( "周期定时器运行%d次\r\n" , ++auto_reload_times); } if (pxTimer == one_shot_timer_handle) { printf ( "单次定时器运行%d次\r\n" , ++one_shot_times); } # endif } 两种方式的运行结果相同,但推荐使用获取ID的方式,因为这是FreeRTOS推荐的。 转载于: https://blog.csdn.net/dingyc_ee/article/details/104118508
  • 热度 4
    2020-8-23 17:05
    2353 次阅读|
    0 个评论
    计数型信号量简介: 计数型信号量的创建: 计数型信号量动态创建函数: 释放和获取信号量(与二值信号量相同) 释放信号量: 获取信号量: 测试实验 用按键来模拟事件,按键按下后表示有事件发生,则释放计数型信号量。 创建两个任务,任务1用于按键检测和释放信号量,按键2用于获取信号量。 测试程序如下所示: void start_task ( void *pvParameters) { taskENTER_CRITICAL(); count_semphr = xSemaphoreCreateCounting( 5 , 0 ); if (count_semphr != NULL ) { printf ( "计数型信号量创建成功\n" ); } else { printf ( "计数型信号量创建失败\n" ); } // 创建任务1 xTaskCreate((TaskFunction_t ) task1_task, ( char * ) "task1_task" , ( uint16_t ) TASK1_TASK_SIZE, ( void * ) NULL , (UBaseType_t ) TASK1_TASK_PRIO, (TaskHandle_t * ) &Task1_Handle); // 创建任务2 xTaskCreate((TaskFunction_t )task2_task, ( char * ) "task2_task" , ( uint16_t )TASK2_TASK_SIZE, ( void * ) NULL , (UBaseType_t )TASK2_TASK_PRIO, (TaskHandle_t * )&Task2_Handle); taskEXIT_CRITICAL(); // 删除开始任务 vTaskDelete(Start_Task_Handle); } void task1_task ( void *pvParameters) { BaseType_t error_state; for (;;) { // 按键KEY1用于释放信号量 if (key_scan(KEY1_GPIO_Port, KEY1_Pin) == KEY_ON) { if (count_semphr != NULL ) { error_state = xSemaphoreGive(count_semphr); if (error_state == pdTRUE) { printf ( "信号量释放成功\n" ); } else { printf ( "信号量释放失败\n" ); } } } vTaskDelay( 20 ); } } void task2_task ( void *pvParameters) { BaseType_t error_state; UBaseType_t current_semphr_value; for (;;) { // 按键KEY2用于获取信号量 if (key_scan(KEY2_GPIO_Port, KEY2_Pin) == KEY_ON) { if (count_semphr != NULL ) { // 等待获取信号量 error_state = xSemaphoreTake(count_semphr, portMAX_DELAY); if (error_state == pdTRUE) { printf ( "获取信号量成功\n" ); current_semphr_value = uxSemaphoreGetCount(count_semphr); printf ( "当前信号量计数值为%d\n" , ( int )current_semphr_value); } else { printf ( "获取信号量失败\n" ); } } } vTaskDelay( 50 ); } } 测试结果: 转载于: https://blog.csdn.net/dingyc_ee/article/details/104114161
相关资源