You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32队列整数组发送而非逐字节传输的定时器功能异常求助

问题与代码分析

我尝试将UART接收的数组传入hex_to_bin函数,数据接收正常且已传入队列。希望队列每次仅发送一个字节,以便在turn_on_off任务中提取比特并执行定时器功能。使用二进制信号量后比特提取成功,但定时器功能无法正常运行,附上代码:

#include "main.h"

QueueHandle_t data_queue;

void turn_on_off(void *pvParameters)
{
  uint8_t bit;
  int data_index = 0;
  timer_start(TIMER_GROUP, TIMER_INDEX);
  gpio_set_level(GPIO_NUM_2, true);
  int bit_check = 0;
  int prev_bit_check;

  UartDataStruct UartDataStruct_Onewire;

  timer_set_counter_value(TIMER_GROUP, TIMER_INDEX, 0);
  while (true)
  {
    if ((xQueueReceive(data_queue, &UartDataStruct_Onewire, portMAX_DELAY) == pdPASS))
    {
      // printf("Received data is : %s | len : %d\r\n",&UartDataStruct_Onewire.UartDataBuffer[0],UartDataStruct_Onewire.len);
      timer_set_counter_value(TIMER_GROUP, TIMER_INDEX, 0);

      while (true)
      {
        timer_get_counter_value(TIMER_GROUP, TIMER_INDEX, &task_counter_value);
        // printf("Task counter value: %lld",task_counter_value);
        if (bit_check == 0)
        {
          if (task_counter_value > 200000) // 1000 count per ms
          {
            gpio_set_level(GPIO_NUM_2, false);
            if (task_counter_value > 256000)
            {
              gpio_set_level(GPIO_NUM_2, true);
              bit_check++;
              timer_set_counter_value(TIMER_GROUP, TIMER_INDEX, 0);
            }
          }
        }
        else
        {
          for (data_index = 0; data_index < UartDataStruct_Onewire.len - 1; data_index++)
          {
            for (int itr = 7; itr >= 0; itr--)
            {
              bit = (UartDataStruct_Onewire.UartDataBuffer[data_index] >> itr) & 0x01;
              // ESP_LOGI("Onewire","Bit extracted: %d", bit);
              if (bit == 1)
              {
                for (prev_bit_check = bit_check; bit_check > prev_bit_check;)
                {
                  if (task_counter_value > 30000) // 1000 COUNT PER ms
                  {
                    gpio_set_level(GPIO_NUM_2, false);
                    if (task_counter_value > 38000)
                    {
                      gpio_set_level(GPIO_NUM_2, true);
                      bit_check++;
                      timer_set_counter_value(TIMER_GROUP, TIMER_INDEX, 0);
                    }
                  }
                }
              }
              else
              {
                for (prev_bit_check = bit_check; bit_check > prev_bit_check;)
                {
                  if (task_counter_value > 6000) // 1000 COUNT PER ms
                  {
                    gpio_set_level(GPIO_NUM_2, false);
                    if (task_counter_value > 26000)
                    {
                      gpio_set_level(GPIO_NUM_2, true);
                      bit_check++;
                      timer_set_counter_value(TIMER_GROUP, TIMER_INDEX, 0);
                    }
                  }
                }
              }
            }
            printf("---------------\r\n");
          }
        }
        if (data_index == UartDataStruct_Onewire.len - 1)
        {
          bit_check = 0;
          break;
        }
      }
    }
  }
}

问题根源分析

  • 死循环阻塞任务:内层for (prev_bit_check = bit_check; bit_check > prev_bit_check;)是死循环,只有定时器计数满足条件时bit_check才会递增,否则会持续占用CPU,既无法更新定时器计数值,也无法触发任务切换,导致定时器逻辑完全无法执行。
  • 定时器计数值未实时更新:task_counter_value仅在最外层while(true)中读取一次,后续循环始终使用旧值,永远无法满足定时器触发条件。
  • 队列设计不符合需求:当前队列传递的是包含多字节数组的UartDataStruct结构体,但需求是每次发送单个字节,队列元素类型应改为uint8_t。
  • 嵌套循环逻辑混乱:内层while(true)与外层for循环的退出条件衔接错误,可能导致任务无法正常退出当前数据处理流程。

解决方案

  1. 调整队列类型:初始化队列时指定元素为uint8_t,UART接收回调中每次发送一个字节到队列:
    // 队列初始化示例
    data_queue = xQueueCreate(10, sizeof(uint8_t));
    
  2. 替换轮询为FreeRTOS定时API:使用vTaskDelayUntil实现精确定时,避免轮询占用CPU,同时确保GPIO电平切换的时间精度。
  3. 简化比特处理逻辑:去掉冗余嵌套,直接按比特顺序处理每个字节。

修改后的代码示例

#include "main.h"

QueueHandle_t data_queue;

void turn_on_off(void *pvParameters)
{
    uint8_t current_byte;
    uint8_t bit;
    TickType_t last_wake_time;
    
    // 初始化定时器(确保已正确配置计数模式、时钟源等)
    timer_start(TIMER_GROUP, TIMER_INDEX);
    gpio_set_level(GPIO_NUM_2, true);

    while (true)
    {
        // 从队列接收单个字节
        if (xQueueReceive(data_queue, &current_byte, portMAX_DELAY) == pdPASS)
        {
            // 处理起始位(对应原逻辑的200ms低+56ms高)
            gpio_set_level(GPIO_NUM_2, false);
            vTaskDelay(pdMS_TO_TICKS(200));
            gpio_set_level(GPIO_NUM_2, true);
            vTaskDelay(pdMS_TO_TICKS(56));
            last_wake_time = xTaskGetTickCount();

            // 提取当前字节的每一位(从高位到低位)
            for (int itr = 7; itr >= 0; itr--)
            {
                bit = (current_byte >> itr) & 0x01;
                if (bit == 1)
                {
                    // 比特1:8ms低电平 + 22ms高电平(总30ms)
                    gpio_set_level(GPIO_NUM_2, false);
                    vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(8));
                    gpio_set_level(GPIO_NUM_2, true);
                    vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(22));
                }
                else
                {
                    // 比特0:6ms低电平 + 20ms高电平(总26ms)
                    gpio_set_level(GPIO_NUM_2, false);
                    vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(6));
                    gpio_set_level(GPIO_NUM_2, true);
                    vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(20));
                }
            }
            printf("---------------\r\n");
        }
    }
}

内容的提问来源于stack exchange,提问作者Balaji Baski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 20:42:32