在FreeRTOS中,互斥访问通常通过使用互斥量(mutexes)来实现,这是防止多个任务或线程同时访问共享资源的一种机制。互斥量确保在任何时刻,只有一个任务可以访问特定的资源或代码段。这在多任务操作中非常重要,因为它避免了数据竞争和条件竞争,从而保证了程序的正确性和稳定性。
想像一个带锁的箱子,同一时间只能有1个人用钥匙开锁拿里面的东西。
本示例通过互斥对象来防止多个任务同时访问LED
从 https://gitee.com/billyzh/esp32-cpp-lesson 下载本教程的源码到本地硬盘文件夹,如d:\esp32-cpp-lesson
在VSCode中,选择【文件】->【打开文件夹...】选择上一步保存的文件夹打开
打开项目后,选择config.h文件,修改第10行为
#define APP_LESSON64_A 1
打开unit6-lesson64a/board_config.h文件,设置LED使用的引脚,
#define BUILTIN_LED_PIN GPIO_NUM_4
配置启用GpioLed
#define CONFIG_USE_LED_GPIO 1
创建LED实例,代码如下(unit6-lesson64a/my_board.cpp):
MyBoard::MyBoard() : Board() {
Log::Info(TAG, "===== Create Board ...... =====");
Log::Info(TAG, "initial led.");
led_ = new GpioLed(BUILTIN_LED_PIN, false);
Log::Info( TAG, "===== Board config completed. =====");
}
程序很简单,就是创建一个GpioLed实例。
互斥对象应用
代码如下(unit6-lesson64a/my_application.cpp):
void MyApplication::OnInit() {
mutex_ = new FrtMutex(); // 创建FreeRTOS互斥对象
task1_ = new Task("Task1");
task1_->OnLoop([this](){
Task1Loop();
});
task1_->Start( 4096, tskIDLE_PRIORITY+1);
task2_ = new Task("Task2");
task2_->OnLoop([this](){
Task2Loop();
});
task2_->Start( 4096, tskIDLE_PRIORITY+1);
}
void MyApplication::Task1Loop() {
AccessResource("task1");
delay(200);
}
void MyApplication::Task2Loop() {
AccessResource("task2");
delay(300);
}
void MyApplication::AccessResource(const std::string& owner) {
MutexGuard guard(mutex_, 0);
if (guard.IsLocked())
{
// 独占访问代码
Led *led = Board::GetInstance().GetLed();
led->TurnOn();
delay(500);
led->TurnOff();
delay(500);
count_++;
Log::Info(TAG, "call by %s, count: %d", owner.c_str(), count_);
}
}
程序解读
1. 在OnInit方法内,创建一个互斥对象Mutex实例和两个Task实例,然后启动任务;
2. Task1Loop方法是任务1的循环体方法,具体为每200ms调用AccessResource方法;
3. Task2Loop方法是任务2的循环体方法,具体为每200ms调用AccessResource方法;
4. AccessResource方法内通过MutexGuard的构造函数来尝试加锁mutex_,若加锁成功,则闪烁LED,方法执行完毕后guard实例释放,在MutexGuard的析构函数释放mutex_
FrtMutex代码如下(framework/sys/mutex/frt_mutex.h):
class FrtMutex : public Mutex {
public:
FrtMutex()
{
mutex_semaphore_ = xSemaphoreCreateMutex(); // 创建互斥信号量
}
~FrtMutex()
{
vSemaphoreDelete(mutex_semaphore_);
}
bool Lock(int timeout_ms = 0) override
{
TickType_t tick = timeout_ms>=0 ? pdMS_TO_TICKS(timeout_ms) : portMAX_DELAY;
return xSemaphoreTake(mutex_semaphore_, tick) == pdTRUE;
}
void Unlock() override
{
xSemaphoreGive(mutex_semaphore_);
}
private:
SemaphoreHandle_t mutex_semaphore_ = NULL;
};
程序解读
FrtMutex是一个非常典型的用对象封装C代码的例子,在构造函数/析构函数中创建和删除互斥量, 在Lock/Unlock方法中进行加锁和释放锁。
MutexGuard代码如下(framework/sys/mutex.h):
class MutexGuard {
public:
MutexGuard(Mutex *mutex, int timeout_ms=0) : mutex_(mutex) {
is_locked_ = mutex_->Lock(timeout_ms);
if (!is_locked_)
{
Log::Error("Mutex", "Failed to lock mutex");
}
}
~MutexGuard() {
if (is_locked_)
{
mutex_->Unlock();
}
}
MutexGuard(const MutexGuard&) = delete;
MutexGuard& operator=(const MutexGuard&) = delete;
const bool IsLocked() const { return is_locked_; }
private:
Mutex *mutex_;
bool is_locked_;
};
程序解读
MutexGuard是一个辅助类,它实现在构造函数和析构函数中实施一对相反的操作,此处为互斥对象的加锁和释放锁。
这样可以让我们在方法开头处实例此类(调用构造函数),方法结束后变量超出作用域释放(调用析构函数)来完成这一对操作的自动处理。
编译项目并上传开发板检验
勾股定理,是一个基本的几何定理,指直角三角形的两条直角边的平方和等于斜边的平方。中国古代称直角三角形为勾股形,并且直角边中较小者为勾,另一长直角边为股,斜边为弦,所以称这个定理为勾股定理,也有人称商高定理。
在ESP32的开发,经常会有系统崩溃一直重启的情况,那么如何快速定位出现异常的代码呢?
本节主要讲解FreeRTOS任务间如何使用消息队列、事件组和二进制信号量进行通信。
本节主要讲解Task类,FreeRTOS多任务的使用。
本节主要讲解Timer类,FreeRTOS定时器的使用。
本节主要讲解舵机驱动类和用按键控制舵机。
本节主要讲解执行器件类型和用按键控制继电器。
本小节主要讲解红外接收和遥控器件,以及遥控操作LED。
本小节讲解模拟量传感器使用,旋转电位器,DHT11温湿度传感器和实现自定义传感器类。
本小节讲解Sensor类及派生类、数字量传感器使用和传感器的推荐交互流程。
本小节讲解ESP32内置触摸引脚的用法,