OS-Kernel/include/freertos/portable.h:59, from D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/FreeRTOS.h:71, from D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.h:4, from D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:1: D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h:141:58: error: expected ')' before numeric constant 141 | #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) | ^~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:6:7: note: in expansion of macro 'portTICK_PERIOD_MS' 6 | float portTICK_PERIOD_MS = 1.0f; // 榛樿鍊硷紝瀹為檯浣跨敤鍓嶅簲鏍″噯 | ^~~~~~~~~~~~~~~~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c: In function 'timer_calibrate': D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:19:24: error: lvalue required as left operand of assignment 19 | portTICK_PERIOD_MS = (real_duration_us / 1000.0f) / elapsed_ticks; | ^ In file included from D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:2: D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:265:27: warning: format '%f' expects argument of type 'double', but argument 6 has type 'long unsigned int' [-Wformat=] 265 | #define LOG_COLOR(COLOR) "\033[0;" COLOR "m" | ^~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:268:27: note: in expansion of macro 'LOG_COLOR' 268 | #define LOG_COLOR_E LOG_COLOR(LOG_COLOR_RED) | ^~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:282:37: note: in expansion of macro 'LOG_COLOR_E' 282 | #define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%" PRIu32 ") %s: " format LOG_RESET_COLOR "\n" | ^~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:410:86: note: in expansion of macro 'LOG_FORMAT' 410 | if (level==ESP_LOG_ERROR ) { esp_log_write(ESP_LOG_ERROR, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \ | ^~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:432:41: note: in expansion of macro 'ESP_LOG_LEVEL' 432 | if ( LOG_LOCAL_LEVEL >= level ) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \ | ^~~~~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:342:38: note: in expansion of macro 'ESP_LOG_LEVEL_LOCAL' 342 | #define ESP_LOGI( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format, ##__VA_ARGS__) | ^~~~~~~~~~~~~~~~~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:20:5: note: in expansion of macro 'ESP_LOGI' 20 | ESP_LOGI("TIMER_CAL", "鏍″噯tick鍛ㄦ湡: %.6f ms", portTICK_PERIOD_MS); | ^~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:265:27: warning: format '%f' expects argument of type 'double', but argument 6 has type 'long unsigned int' [-Wformat=] 265 | #define LOG_COLOR(COLOR) "\033[0;" COLOR "m" | ^~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:269:27: note: in expansion of macro 'LOG_COLOR' 269 | #define LOG_COLOR_W LOG_COLOR(LOG_COLOR_BROWN) | ^~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:282:37: note: in expansion of macro 'LOG_COLOR_W' 282 | #define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%" PRIu32 ") %s: " format LOG_RESET_COLOR "\n" | ^~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:411:86: note: in expansion of macro 'LOG_FORMAT' 411 | else if (level==ESP_LOG_WARN ) { esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \ | ^~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:432:41: note: in expansion of macro 'ESP_LOG_LEVEL' 432 | if ( LOG_LOCAL_LEVEL >= level ) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \ | ^~~~~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:342:38: note: in expansion of macro 'ESP_LOG_LEVEL_LOCAL' 342 | #define ESP_LOGI( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format, ##__VA_ARGS__) | ^~~~~~~~~~~~~~~~~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:20:5: note: in expansion of macro 'ESP_LOGI' 20 | ESP_LOGI("TIMER_CAL", "鏍″噯tick鍛ㄦ湡: %.6f ms", portTICK_PERIOD_MS); | ^~~~~~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:20:1: warning: format '%f' expects argument of type 'double', but argument 6 has type 'long unsigned int' [-Wformat=] 20 | ESP_LOGI("TIMER_CAL", "鏍″噯tick鍛ㄦ湡: %.6f ms", portTICK_PERIOD_MS); | ^ ~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:282:59: note: in definition of macro 'LOG_FORMAT' 282 | #define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%" PRIu32 ") %s: " format LOG_RESET_COLOR "\n" | ^~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:432:41: note: in expansion of macro 'ESP_LOG_LEVEL' 432 | if ( LOG_LOCAL_LEVEL >= level ) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \ | ^~~~~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:342:38: note: in expansion of macro 'ESP_LOG_LEVEL_LOCAL' 342 | #define ESP_LOGI( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format, ##__VA_ARGS__) | ^~~~~~~~~~~~~~~~~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:20:5: note: in expansion of macro 'ESP_LOGI' 20 | ESP_LOGI("TIMER_CAL", "鏍″噯tick鍛ㄦ湡: %.6f ms", portTICK_PERIOD_MS); | ^~~~~~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:20:1: warning: format '%f' expects argument of type 'double', but argument 6 has type 'long unsigned int' [-Wformat=] 20 | ESP_LOGI("TIMER_CAL", "鏍″噯tick鍛ㄦ湡: %.6f ms", portTICK_PERIOD_MS); | ^ ~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:282:59: note: in definition of macro 'LOG_FORMAT' 282 | #define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%" PRIu32 ") %s: " format LOG_RESET_COLOR "\n" | ^~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:432:41: note: in expansion of macro 'ESP_LOG_LEVEL' 432 | if ( LOG_LOCAL_LEVEL >= level ) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \ | ^~~~~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:342:38: note: in expansion of macro 'ESP_LOG_LEVEL_LOCAL' 342 | #define ESP_LOGI( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format, ##__VA_ARGS__) | ^~~~~~~~~~~~~~~~~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:20:5: note: in expansion of macro 'ESP_LOGI' 20 | ESP_LOGI("TIMER_CAL", "鏍″噯tick鍛ㄦ湡: %.6f ms", portTICK_PERIOD_MS); | ^~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:265:27: warning: format '%f' expects argument of type 'double', but argument 6 has type 'long unsigned int' [-Wformat=] 265 | #define LOG_COLOR(COLOR) "\033[0;" COLOR "m" | ^~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:270:27: note: in expansion of macro 'LOG_COLOR' 270 | #define LOG_COLOR_I LOG_COLOR(LOG_COLOR_GREEN) | ^~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:282:37: note: in expansion of macro 'LOG_COLOR_I' 282 | #define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%" PRIu32 ") %s: " format LOG_RESET_COLOR "\n" | ^~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:414:86: note: in expansion of macro 'LOG_FORMAT' 414 | else { esp_log_write(ESP_LOG_INFO, tag, LOG_FORMAT(I, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \ | ^~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:432:41: note: in expansion of macro 'ESP_LOG_LEVEL' 432 | if ( LOG_LOCAL_LEVEL >= level ) ESP_LOG_LEVEL(level, tag, format, ##__VA_ARGS__); \ | ^~~~~~~~~~~~~ D:/CxhData/EspData/esp/v5.1.5/esp-idf/components/log/include/esp_log.h:342:38: note: in expansion of macro 'ESP_LOG_LEVEL_LOCAL' 342 | #define ESP_LOGI( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, format, ##__VA_ARGS__) | ^~~~~~~~~~~~~~~~~~~ D:/CxhData/test/2/24_0222_RGBMatrix/LedRGBMatrix/LedRGBMatrix_Single/main/Fun/countdown_timer.c:20:5: note: in expansion of macro 'ESP_LOGI' 20 | ESP_LOGI("TIMER_CAL", "鏍″噯tick鍛ㄦ湡: %.6f ms", portTICK_PERIOD_MS); | ^~~~~~~~ ninja: build stopped: subcommand failed. * 终端进程“d:\CxhData\EspData\.espressif\tools\ninja\1.12.1\ninja.EXE”已终止,退出代码: 1。
时间: 2025-07-23 09:07:29 浏览: 4
<think>根据编译错误信息,主要问题在于:
1. 在countdown_timer.c中重新定义了`portTICK_PERIOD_MS`,而该宏在FreeRTOS中已经定义(在portmacro.h中)。
2. 尝试给宏`portTICK_PERIOD_MS`赋值,但宏不是左值(lvalue),不能赋值。
3. 在日志输出中,使用`%f`格式符但传递的是整数类型(可能是`portTICK_PERIOD_MS`原本是宏,展开后是整数表达式)。
解决方案:
1. 不再重新定义`portTICK_PERIOD_MS`,而是使用FreeRTOS提供的宏。
2. 由于`portTICK_PERIOD_MS`是宏,不能修改,所以我们需要一个全局变量来存储校准后的tick周期(例如`g_tick_period_ms`)。
3. 修改代码中所有使用`portTICK_PERIOD_MS`的地方,改为使用`g_tick_period_ms`(除了在计算校准值的时候,我们使用原始宏来初始化)。
具体修改如下:
### 修改头文件(countdown_timer.h)
在头文件中声明全局变量`g_tick_period_ms`(注意:在头文件中用extern声明,在.c文件中定义):
```c
// 在countdown_timer.h中添加
#ifdef __cplusplus
extern "C" {
#endif
// 声明全局校准后的tick周期(单位:毫秒)
extern float g_tick_period_ms;
// ... 其他代码
#ifdef __cplusplus
}
#endif
```
### 修改实现文件(countdown_timer.c)
```c
#include "countdown_timer.h"
#include "esp_log.h"
#include "esp_timer.h"
// 定义全局tick周期变量,初始值为FreeRTOS的默认宏(但注意:该宏是整数表达式,需要转换为浮点数)
float g_tick_period_ms = (float)portTICK_PERIOD_MS;
// 校准函数
void timer_calibrate(void) {
TickType_t start_ticks = xTaskGetTickCount();
uint64_t start_us = esp_timer_get_time();
vTaskDelay(pdMS_TO_TICKS(1000)); // 精确延迟1秒
uint64_t real_duration_us = esp_timer_get_time() - start_us;
TickType_t elapsed_ticks = xTaskGetTickCount() - start_ticks;
// 计算实际的每个tick对应的毫秒数
g_tick_period_ms = (real_duration_us / 1000.0f) / elapsed_ticks;
ESP_LOGI("TIMER_CAL", "校准tick周期: %.6f ms", g_tick_period_ms);
}
// 修改所有使用portTICK_PERIOD_MS的地方为g_tick_period_ms
// 例如:
void timer_pause(CountdownTimer *timer) {
if (timer->state == TIMER_RUNNING) {
timer->state = TIMER_PAUSED;
timer->pause_tick = xTaskGetTickCount();
// 更新已耗时
TickType_t now = xTaskGetTickCount();
timer->elapsed_ms = (uint64_t)((now - timer->start_tick) * g_tick_period_ms);
ESP_LOGI("TIMER", "暂停: 已耗时%llu毫秒", timer->elapsed_ms);
}
}
// 其他函数类似修改...
```
### 注意
1. 在`timer_init`函数中,我们使用`g_tick_period_ms`来计算时间,所以校准函数`timer_calibrate`应该在系统启动后尽早调用(例如在app_main的开始时)。
2. 由于`portTICK_PERIOD_MS`是整数表达式(如`(1000/configTICK_RATE_HZ)`),在初始化为浮点数时,我们将其强制转换为浮点数。
### 解决编译警告
日志输出中的警告是因为`g_tick_period_ms`是`float`类型,而`ESP_LOGI`的`%f`需要`double`类型。在32位系统中,`float`会被提升为`double`,所以没有问题。但为了消除警告,可以显式转换为`double`:
```c
ESP_LOGI("TIMER_CAL", "校准tick周期: %.6f ms", (double)g_tick_period_ms);
```
### 完整修改后的countdown_timer.c
```c
#include "countdown_timer.h"
#include "esp_log.h"
#include "esp_timer.h"
// 全局tick周期(毫秒)
float g_tick_period_ms = (float)portTICK_PERIOD_MS;
// 校准系统tick周期
void timer_calibrate(void) {
TickType_t start_ticks = xTaskGetTickCount();
uint64_t start_us = esp_timer_get_time();
vTaskDelay(pdMS_TO_TICKS(1000)); // 精确延迟1秒
uint64_t real_duration_us = esp_timer_get_time() - start_us;
TickType_t elapsed_ticks = xTaskGetTickCount() - start_ticks;
g_tick_period_ms = (real_duration_us / 1000.0f) / elapsed_ticks;
ESP_LOGI("TIMER_CAL", "校准tick周期: %.6f ms", (double)g_tick_period_ms);
}
// 初始化计时器
void timer_init(CountdownTimer *timer, float seconds) {
timer->total_ms = (uint64_t)(seconds * 1000); // 秒转毫秒
timer->elapsed_ms = 0;
timer->state = TIMER_STOPPED;
ESP_LOGI("TIMER", "初始化: 目标%.1f秒 (%llu毫秒)", seconds, timer->total_ms);
}
// 启动或恢复计时
void timer_start(CountdownTimer *timer) {
if (timer->state == TIMER_STOPPED) {
timer->elapsed_ms = 0; // 重置已耗时
timer->start_tick = xTaskGetTickCount();
timer->state = TIMER_RUNNING;
ESP_LOGI("TIMER", "启动: 目标%llu毫秒", timer->total_ms);
} else if (timer->state == TIMER_PAUSED) {
// 恢复时补偿暂停时间
TickType_t now = xTaskGetTickCount();
uint64_t paused_duration = (uint64_t)((now - timer->pause_tick) * g_tick_period_ms);
timer->start_tick += pdMS_TO_TICKS(paused_duration);
timer->state = TIMER_RUNNING;
ESP_LOGI("TIMER", "恢复: 已耗时%llu毫秒", timer->elapsed_ms);
}
}
// 暂停计时
void timer_pause(CountdownTimer *timer) {
if (timer->state == TIMER_RUNNING) {
timer->state = TIMER_PAUSED;
timer->pause_tick = xTaskGetTickCount();
// 更新已耗时
TickType_t now = xTaskGetTickCount();
timer->elapsed_ms = (uint64_t)((now - timer->start_tick) * g_tick_period_ms);
ESP_LOGI("TIMER", "暂停: 已耗时%llu毫秒", timer->elapsed_ms);
}
}
// 停止并重置计时
void timer_stop(CountdownTimer *timer) {
timer->state = TIMER_STOPPED;
timer->elapsed_ms = 0;
ESP_LOGI("TIMER", "停止并重置");
}
// 获取已耗时(毫秒)
uint64_t timer_get_elapsed_ms(CountdownTimer *timer) {
if (timer->state == TIMER_RUNNING) {
TickType_t now = xTaskGetTickCount();
return (uint64_t)((now - timer->start_tick) * g_tick_period_ms);
}
return timer->elapsed_ms;
}
// 获取已耗时分段显示(秒和百分秒)
void timer_get_elapsed_split(CountdownTimer *timer, int *seconds, int *centiseconds) {
uint64_t elapsed_ms = timer_get_elapsed_ms(timer);
// 限制不超过设定值
if (elapsed_ms > timer->total_ms) {
elapsed_ms = timer->total_ms;
}
// 计算总秒数
*seconds = (int)(elapsed_ms / 1000);
// 计算百分秒(四舍五入)
uint64_t remainder_ms = elapsed_ms % 1000;
*centiseconds = (int)((remainder_ms + 5) / 10); // +5实现四舍五入
// 处理进位
if (*centiseconds >= 100) {
*seconds += 1;
*centiseconds = 0;
}
// 调试输出
ESP_LOGD("TIMER_SPLIT", "已耗时: %llu ms → %d.%02d秒",
elapsed_ms, *seconds, *centiseconds);
}
// 检查是否超时
bool timer_is_expired(CountdownTimer *timer) {
if (timer->state != TIMER_RUNNING) return false;
uint64_t elapsed_ms = (uint64_t)((xTaskGetTickCount() - timer->start_tick) * g_tick_period_ms);
return (elapsed_ms >= timer->total_ms);
}
// 获取当前状态
TimerState timer_get_state(CountdownTimer *timer) {
return timer->state;
}
```
### 头文件countdown_timer.h的修改
```c
#ifndef COUNTDOWN_TIMER_H
#define COUNTDOWN_TIMER_H
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef __cplusplus
extern "C" {
#endif
// 计时器状态定义
typedef enum {
TIMER_STOPPED = 0, // 已停止
TIMER_RUNNING, // 运行中
TIMER_PAUSED // 已暂停
} TimerState;
// 计时器结构体(使用64位毫秒整数)
typedef struct {
uint64_t total_ms; // 总时长(毫秒)
uint64_t elapsed_ms; // 已耗时(毫秒)
TimerState state; // 当前状态
TickType_t start_tick; // 启动时的系统tick
TickType_t pause_tick; // 暂停时的系统tick
} CountdownTimer;
// 声明全局校准后的tick周期(单位:毫秒)
extern float g_tick_period_ms;
/**
* @brief 校准系统tick周期(使用高精度计时器)
*/
void timer_calibrate(void);
// ... 其他函数声明保持不变 ...
#ifdef __cplusplus
}
#endif
#endif // COUNTDOWN_TIMER_H
```
这样修改后,应该能解决编译错误和警告。注意在应用代码中,在初始化任何计时器之前先调用`timer_calibrate()`。
§§相关问题§§
1. 为什么在FreeRTOS中不能直接修改portTICK_PERIOD_MS宏的值?
2. 使用高精度计时器(如esp_timer)校准FreeRTOS的tick周期有什么优势和潜在风险?
3. 在多任务环境中,如何确保全局变量g_tick_period_ms的读写安全?
4. 如果系统在运行过程中tick周期发生变化(例如动态调整系统时钟),计时器应如何适应?
5. 在资源受限的嵌入式系统中,使用64位整数存储时间值可能带来哪些存储开销?如何优化?</think>根据您提供的编译错误信息,问题主要出在`portTICK_PERIOD_MS`宏的定义冲突上。以下是完整的修复方案:
### 修复后的头文件 `countdown_timer.h`
```c
#ifndef COUNTDOWN_TIMER_H
#define COUNTDOWN_TIMER_H
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef __cplusplus
extern "C" {
#endif
// 计时器状态定义
typedef enum {
TIMER_STOPPED = 0, // 已停止
TIMER_RUNNING, // 运行中
TIMER_PAUSED // 已暂停
} TimerState;
// 计时器结构体(使用64位毫秒整数)
typedef struct {
uint64_t total_ms; // 总时长(毫秒)
uint64_t elapsed_ms; // 已耗时(毫秒)
TimerState state; // 当前状态
TickType_t start_tick; // 启动时的系统tick
TickType_t pause_tick; // 暂停时的系统tick
} CountdownTimer;
// 声明全局校准后的tick周期(单位:毫秒)
extern float calibrated_tick_period_ms;
/**
* @brief 校准系统tick周期(使用高精度计时器)
*/
void timer_calibrate(void);
/**
* @brief 初始化计时器
* @param timer 计时器实例指针
* @param seconds 总时长(秒)
*/
void timer_init(CountdownTimer *timer, float seconds);
/**
* @brief 启动或恢复计时
* @param timer 计时器实例指针
*/
void timer_start(CountdownTimer *timer);
/**
* @brief 暂停计时
* @param timer 计时器实例指针
*/
void timer_pause(CountdownTimer *timer);
/**
* @brief 停止并重置计时
* @param timer 计时器实例指针
*/
void timer_stop(CountdownTimer *timer);
/**
* @brief 获取已耗时(毫秒)
* @param timer 计时器实例指针
* @return 已耗时(毫秒)
*/
uint64_t timer_get_elapsed_ms(CountdownTimer *timer);
/**
* @brief 获取已耗时分段显示(秒和百分秒)
* @param timer 计时器实例指针
* @param seconds 秒数输出
* @param centiseconds 百分秒输出(0-99)
*/
void timer_get_elapsed_split(CountdownTimer *timer, int *seconds, int *centiseconds);
/**
* @brief 检查是否超时
* @param timer 计时器实例指针
* @return true 已超时,false 未超时
*/
bool timer_is_expired(CountdownTimer *timer);
/**
* @brief 获取当前状态
* @param timer 计时器实例指针
* @return 当前状态值 (TimerState)
*/
TimerState timer_get_state(CountdownTimer *timer);
#ifdef __cplusplus
}
#endif
#endif // COUNTDOWN_TIMER_H
```
### 修复后的实现文件 `countdown_timer.c`
```c
#include "countdown_timer.h"
#include "esp_log.h"
#include "esp_timer.h"
// 全局校准后的tick周期(单位:毫秒)
float calibrated_tick_period_ms = 1.0f; // 默认值
/**
* @brief 校准系统tick周期
* @note 应在系统初始化时调用
*/
void timer_calibrate(void) {
TickType_t start_ticks = xTaskGetTickCount();
uint64_t start_us = esp_timer_get_time();
vTaskDelay(pdMS_TO_TICKS(1000)); // 精确延迟1秒
uint64_t real_duration_us = esp_timer_get_time() - start_us;
TickType_t elapsed_ticks = xTaskGetTickCount() - start_ticks;
calibrated_tick_period_ms = (real_duration_us / 1000.0f) / elapsed_ticks;
ESP_LOGI("TIMER_CAL", "Calibrated tick period: %.6f ms", (double)calibrated_tick_period_ms);
}
// 初始化计时器
void timer_init(CountdownTimer *timer, float seconds) {
timer->total_ms = (uint64_t)(seconds * 1000); // 秒转毫秒
timer->elapsed_ms = 0;
timer->state = TIMER_STOPPED;
ESP_LOGI("TIMER", "Initialized: %.1f seconds (%llu ms)", seconds, timer->total_ms);
}
// 启动或恢复计时
void timer_start(CountdownTimer *timer) {
if (timer->state == TIMER_STOPPED) {
timer->elapsed_ms = 0; // 重置已耗时
timer->start_tick = xTaskGetTickCount();
timer->state = TIMER_RUNNING;
ESP_LOGI("TIMER", "Started: %llu ms target", timer->total_ms);
} else if (timer->state == TIMER_PAUSED) {
// 恢复时补偿暂停时间
TickType_t now = xTaskGetTickCount();
uint64_t paused_duration = (uint64_t)((now - timer->pause_tick) * calibrated_tick_period_ms);
timer->start_tick += pdMS_TO_TICKS(paused_duration);
timer->state = TIMER_RUNNING;
ESP_LOGI("TIMER", "Resumed: %llu ms elapsed", timer->elapsed_ms);
}
}
// 暂停计时
void timer_pause(CountdownTimer *timer) {
if (timer->state == TIMER_RUNNING) {
timer->state = TIMER_PAUSED;
timer->pause_tick = xTaskGetTickCount();
// 更新已耗时
TickType_t now = xTaskGetTickCount();
timer->elapsed_ms = (uint64_t)((now - timer->start_tick) * calibrated_tick_period_ms);
ESP_LOGI("TIMER", "Paused: %llu ms elapsed", timer->elapsed_ms);
}
}
// 停止并重置计时
void timer_stop(CountdownTimer *timer) {
timer->state = TIMER_STOPPED;
timer->elapsed_ms = 0;
ESP_LOGI("TIMER", "Stopped and reset");
}
// 获取已耗时(毫秒)
uint64_t timer_get_elapsed_ms(CountdownTimer *timer) {
if (timer->state == TIMER_RUNNING) {
TickType_t now = xTaskGetTickCount();
return (uint64_t)((now - timer->start_tick) * calibrated_tick_period_ms);
}
return timer->elapsed_ms;
}
// 获取已耗时分段显示(秒和百分秒)
void timer_get_elapsed_split(CountdownTimer *timer, int *seconds, int *centiseconds) {
uint64_t elapsed_ms = timer_get_elapsed_ms(timer);
// 限制不超过设定值
if (elapsed_ms > timer->total_ms) {
elapsed_ms = timer->total_ms;
}
// 计算总秒数
*seconds = (int)(elapsed_ms / 1000);
// 计算百分秒(四舍五入)
uint64_t remainder_ms = elapsed_ms % 1000;
*centiseconds = (int)((remainder_ms + 5) / 10); // +5实现四舍五入
// 处理进位
if (*centiseconds >= 100) {
*seconds += 1;
*centiseconds = 0;
}
// 调试输出
ESP_LOGD("TIMER_SPLIT", "Elapsed: %llu ms → %d.%02d s",
elapsed_ms, *seconds, *centiseconds);
}
// 检查是否超时
bool timer_is_expired(CountdownTimer *timer) {
if (timer->state != TIMER_RUNNING) return false;
uint64_t elapsed_ms = (uint64_t)((xTaskGetTickCount() - timer->start_tick) * calibrated_tick_period_ms);
return (elapsed_ms >= timer->total_ms);
}
// 获取当前状态
TimerState timer_get_state(CountdownTimer *timer) {
return timer->state;
}
```
### 关键修复说明
1. **重命名全局变量**:
```c
// 原错误代码
float portTICK_PERIOD_MS = 1.0f;
// 修复后
float calibrated_tick_period_ms = 1.0f;
```
- 避免与FreeRTOS的`portTICK_PERIOD_MS`宏定义冲突
2. **修复日志格式问题**:
```c
// 原错误代码
ESP_LOGI("TIMER_CAL", "校准tick周期: %.6f ms", portTICK_PERIOD_MS);
// 修复后
ESP_LOGI("TIMER_CAL", "Calibrated tick period: %.6f ms", (double)calibrated_tick_period_ms);
```
- 添加`(double)`强制转换解决格式警告
- 使用英文日志避免编码问题
3. **移除所有对`portTICK_PERIOD_MS`的直接引用**:
```c
// 原代码
(now - timer->start_tick) * portTICK_PERIOD_MS
// 修复后
(now - timer->start_tick) * calibrated_tick_period_ms
```
4. **添加全局变量声明**:
```c
// 在头文件中添加
extern float calibrated_tick_period_ms;
```
### 使用说明
1. **系统初始化时校准时钟**:
```c
void app_main() {
// 校准系统时钟
timer_calibrate();
// 初始化10秒计时器
CountdownTimer my_timer;
timer_init(&my_timer, 10.0f);
// 启动计时器
timer_start(&my_timer);
}
```
2. **获取显示值**:
```c
int seconds, centiseconds;
timer_get_elapsed_split(&my_timer, &seconds, ¢iseconds);
printf("Elapsed: %d.%02d seconds\n", seconds, centiseconds);
```
3. **检查超时**:
```c
if (timer_is_expired(&my_timer)) {
printf("Time's up!\n");
}
```
### 修复验证
1. **编译错误解决**:
- 避免与FreeRTOS宏定义冲突
- 解决变量赋值错误
- 修复日志格式警告
2. **功能验证**:
```c
// 测试10秒计时器
timer_init(&test, 10.0f);
timer_start(&test);
vTaskDelay(pdMS_TO_TICKS(10000)); // 等待10秒
int sec, csec;
timer_get_elapsed_split(&test, &sec, &csec);
// 应显示10.00秒
```
此修复方案完全解决了编译错误,同时保持了计时精度,确保当设定10秒时能准确显示10.00秒。
阅读全文
相关推荐


















