Welcome to ESP32 Core Board repository
5.Mutex
Mutex
Mutex1. Mutex Overview1.1 What is a Mutex1.2 Importance and Application Scenarios1.3 This Project Demonstrates2. Core Concepts2.1 How Mutex Works2.2 Mutex API2.3 Mutex vs Binary Semaphore2.4 Shared Resource Protection Principles3. Project Architecture and Flow4. Hardware Dependencies5. Source Code Analysis5.1 Header Files and Macro Configuration5.2 key_init() — Button GPIO Initialization5.3 rgb_init() — WS2812 LED Initialization5.4 rgb_set() / rgb_rainbow_loop() / rgb_red_loop() — LED Display Functions5.5 check_winner() — Win Condition Check5.6 vProducer() — Producer Task5.7 vConsumer1_Human() — Human Consumer5.8 vConsumer2_AI() — AI Consumer5.9 app_main() — Main Entry6. Operation Flow6.1 Compilation and Flashing Flow6.2 Normal Operation6.3 Edge Cases7. FAQ
1. Mutex Overview
1.1 What is a Mutex
Mutex is FreeRTOS's core mechanism for protecting shared resources. When multiple tasks need to access the same global variable, peripheral, or data structure, mutex ensures that only one task can enter the critical section at any given time, preventing data races.
1.2 Importance and Application Scenarios
Mutex is one of the most commonly used, most fundamental synchronization mechanisms in RTOS multi-tasking programming, and almost any scenario involving multi-task shared resources relies on it:
| Application Scenario | Description |
|---|---|
| Shared memory protection | When multiple tasks simultaneously read/write global variables/structs/arrays, must lock to ensure data consistency |
| Peripheral exclusive access | When multiple tasks operate on the same SPI/I2C/UART bus, prevent bus conflicts and data corruption |
| Queue/linked list operations | Insert/delete operations on FreeRTOS queues or custom linked lists require atomic protection |
| Sensor data acquisition | Acquisition task writes, display task reads, must be mutually exclusive to prevent reading "half-new half-old" data |
| File system writing | Multiple tasks writing to the same file causes data corruption, mutex ensures serialized writes |
| Display screen refresh | When multiple tasks update different areas of the same screen, lock is needed to protect frame buffer |
Core Value: Mutex is not only a tool for protecting data, but also the cornerstone of ensuring system stability and predictability. Unlocked shared resource access is one of the hardest to reproduce, hardest to debug bugs in embedded systems.
1.3 This Project Demonstrates
This project demonstrates mutex through a "Grab Goods Game" (Human vs AI): Producer produces goods every second, human holds BOOT button to grab lock→releases to grab goods, AI automatically grabs lock→grabs goods→cools down for 1s. First to grab 10 goods wins. Human wins → rainbow LED cycling, AI wins → solid red LED.
Through this game, you will intuitively understand: lock exclusivity (human keeps holding → AI can never get lock), critical section protection (all stock/score operations are inside the lock), and deadlock prevention (each task strictly follows Take → Operation → Give order).
2. Core Concepts
2.1 How Mutex Works
Task A Task B│ ││ xSemaphoreTake() │ xSemaphoreTake()▼ (got lock) ▼ (blocked, waiting)┌────────────┐ ╔══════════════╗│ Critical │ ║ Waiting for ║│ Section │ ║ lock release...║│ Access │ ╚══════════════╝│ shared ││ variables │└────────────┘ ││ ││ xSemaphoreGive() ▼ (got lock)▼ (release lock) ┌────────────┐│ Critical ││ Section │└────────────┘
2.2 Mutex API
x// Create mutexSemaphoreHandle_t xSemaphoreCreateMutex(void);
// Take lock (enter critical section)xSemaphoreTake(xMutex, portMAX_DELAY);
// Give lock (exit critical section)xSemaphoreGive(xMutex);2.3 Mutex vs Binary Semaphore
| Feature | Mutex | Binary Semaphore |
|---|---|---|
| Purpose | Mutual exclusion — protect shared resources | Synchronization — event notification |
| Priority inheritance | ✓ (auto elevate holder priority) | ✗ |
| Recursive acquisition | Configurable recursive mutex available | Not available |
| Releaser | Must be acquirer (same task Take → Give) | Any task can Give |
| Use in ISR | ✗ (cannot Take/Give in ISR) | ✓ (GiveFromISR) |
| Initial value | 1 (available, not held by any task) | Usually 0 |
Mutex priority inheritance mechanism is key to solving priority inversion. When a high-priority task waits for a lock, the low-priority task holding the lock is temporarily elevated to high priority to ensure quick lock release.
2.4 Shared Resource Protection Principles
xxxxxxxxxxAll code accessing g_goods must:1. xSemaphoreTake(xMutex, ...) ← Take lock2. Read/modify g_goods ← Critical section3. xSemaphoreGive(xMutex) ← Give lockMissing any step = data race = bug
3. Project Architecture and Flow
xxxxxxxxxxapp_main()├─ rgb_init() + key_init() ← WS2812 (GPIO48) + Button (GPIO0)├─ xSemaphoreCreateMutex() ← Create mutex (protects: g_goods, g_human_score, g_ai_score)├─ xTaskCreate(vProducer) ← Producer (Prio 2)├─ xTaskCreate(vConsumer1_Human) ← Human (Prio 3)└─ xTaskCreate(vConsumer2_AI) ← AI (Prio 2)Producer ──(take→produce n→give)──► g_goods ← Shared resourceHuman ──(hold→take...release→grab→give)──► g_human_scoreAI ──(take→grab→give→cool 1s)──► g_ai_scoreHuman holds button continuously → Lock always held by Human → AI blocks permanently at xSemaphoreTake→ Demonstrates mutex exclusivity
Game Rules:
| Parameter | Value |
|---|---|
| Win condition | First to grab 10 goods |
| Producer | Produces 2~5 randomly per second |
| AI cooldown | Waits 1 second after successful grab |
| Human operation | Hold BOOT = hold lock, release = grab + give lock |
| Human wins → Rainbow cycling | AI wins → Solid red |
4. Hardware Dependencies
| Pin | Function | Description |
|---|---|---|
| GPIO 0 | BOOT button | Human uses button to grab lock and goods |
| GPIO 48 | WS2812 RGB LED | Human wins rainbow / AI wins red |
Required Components: freertos/FreeRTOS, freertos/task, freertos/semphr, driver/gpio, led_strip, esp_log, esp_random
This project uses a third-party library again: For details, review (Third-party library installation process: 2.Basic Peripherals → 10.RGB LED Effects)
Note:
led_stripis a third-party component provided by ESP-IDF Component Registry, not built into IDF. Before use, declare dependency inmain/idf_component.yml:xxxxxxxxxx## IDF Component Manager Manifest Filedependenciesidfversion'>=4.1.0'espressif/led_strip^3.0.3After declaration, execute
idf.py build, and component manager will automatically download component tomanaged_components/directory.
5. Source Code Analysis
Full source code: Source Code → ESP-IDF → 4.FreeRTOS → 2. RTOS_Inter-task_communication → RTOS-MUTEX
5.1 Header Files and Macro Configuration
This section defines GPIO pins, game parameters, and all shared resource variables protected by mutex.
xxxxxxxxxx
static const char *TAG = "MUTEX_GAME";
// ---- GPIO Pin Definitions ----
// ---- Game Parameters ----
// ---- Rainbow Colors (WS2812 GRB Order) ----typedef struct { uint8_t green; uint8_t red; uint8_t blue;} led_color_t;
static const led_color_t rainbow_colors[] = { {0, 150, 0 }, // Red {60, 150, 0 }, // Orange {150, 150, 0 }, // Yellow {150, 0, 0 }, // Green {150, 0, 150}, // Cyan {0, 0, 150}, // Blue {0, 120, 150}, // Purple};
// ---- Shared Resources (all protected by mutex) ----static SemaphoreHandle_t xMutex = NULL;static int g_goods = 0;static int g_human_score = 0;static int g_ai_score = 0;static bool g_game_over = false;static int g_winner = 0;
static led_strip_handle_t led_strip;5.2 key_init() — Button GPIO Initialization
Configure BOOT button (GPIO0) as internal pull-up input mode, low level when pressed.
xxxxxxxxxxstatic void key_init(void){ gpio_config_t cfg = { .pin_bit_mask = 1ULL << KEY_GPIO, .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; gpio_config(&cfg);}5.3 rgb_init() — WS2812 LED Initialization
Initialize WS2812 strip on GPIO48 via RMT peripheral.
xxxxxxxxxxstatic void rgb_init(void){ led_strip_config_t strip_cfg = { .strip_gpio_num = BLINK_GPIO, .max_leds = LED_NUM, .led_model = LED_MODEL_WS2812, .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, .flags.invert_out = false, }; led_strip_rmt_config_t rmt_cfg = { .resolution_hz = 10 * 1000 * 1000, .flags.with_dma = false, }; ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_cfg, &rmt_cfg, &led_strip)); led_strip_clear(led_strip);}5.4 rgb_set() / rgb_rainbow_loop() / rgb_red_loop() — LED Display Functions
Three LED control functions: set solid color, rainbow cycle, solid red.
xxxxxxxxxxstatic void rgb_set(uint8_t r, uint8_t g, uint8_t b){ led_strip_set_pixel(led_strip, 0, r, g, b); led_strip_refresh(led_strip);}
static void rgb_rainbow_loop(void){