ESP32学习之定时器和PWM

该文详细介绍了ESP32中定时器的使用,包括初始化、中断服务及计数器配置,展示了如何实现每秒触发一次中断并打印计数值。同时,解释了PWM的概念,通过示例代码演示了如何设置PWM频率、占空比以及分辨率,用于控制LED亮度。此外,还讨论了传统PWM方法与LEDC库的比较,并提供了舵机驱动的代码示例。

一.定时器代码如下:


#include <Arduino.h>

hw_timer_t *timer = NULL;
int interruptCounter = 0;


//	函数名称:onTimer()
//	函数功能:中断服务的功能,它必须是一个返回void(空)且没有输入参数的函数
//  为使编译器将代码分配到IRAM内,中断处理程序应该具有 IRAM_ATTR 属性
//  https://docs.espressif.com/projects/esp-idf/zh_CN/release-v4.3/zesp32/api-reference/storage/spi_flash_concurrency.html
void IRAM_ATTR TimerEvent()
{
    Serial.println(interruptCounter++);
    if (interruptCounter > 5)
    {
        interruptCounter = 1;
    }
}

void setup() {


    Serial.begin(115200);

    //	函数名称:timerBegin()
    //	函数功能:Timer初始化,分别有三个参数
    //	函数输入:1. 定时器编号(0到3,对应全部4个硬件定时器)
    //			 2. 预分频器数值(ESP32计数器基频为80Mhz,80分频得到1Mhz 单位是微秒)1Mhz=1/1000000,单位是微秒
    //			 3. 计数器向上(true)或向下(false)计数的标志
    //	函数返回:一个指向 hw_timer_t 结构类型的指针
    timer = timerBegin(0, 80, true);

    //	函数名称:timerAttachInterrupt()
    //	函数功能:绑定定时器的中断处理函数,分别有三个参数
    //	函数输入:1. 指向已初始化定时器的指针(本例子:timer)
    //			 2. 中断服务函数的函数指针
    //			 3. 表示中断触发类型是边沿(true)还是电平(false)的标志
    //	函数返回:无
    timerAttachInterrupt(timer, &TimerEvent, true);

    //	函数名称:timerAlarmWrite()
    //	函数功能:指定触发定时器中断的计数器值,分别有三个参数
    //	函数输入:1. 指向已初始化定时器的指针(本例子:timer)
    //			 2. 第二个参数是触发中断的计数器值(1000000 us -> 1s)
    //			 3. 定时器在产生中断时是否重新加载的标志
    //	函数返回:无
    timerAlarmWrite(timer, 1000000, true);
    timerAlarmEnable(timer); //	使能定时器
}


void loop() {

}

代码功能为:用定时器,每隔一秒触发中断,在串口打印interruptCounter的值

结果为(过一会再打开串口,不然会重启):

二.PWM代码如下:


#include <Arduino.h>

/*
 * LEDC Chan to Group/Channel/Timer Mapping
** ledc: 0  => Group: 0, Channel: 0, Timer: 0
** ledc: 1  => Group: 0, Channel: 1, Timer: 0
** ledc: 2  => Groupiimer: 1
** ledc: 4  => Group: 0, Channel: 4, Timer: 2
** ledc: 5  => Group: 0, Channel: 5, Timer: 2
** ledc: 6  => Group: 0, Channel: 6, Timer: 3
** ledc: 7  => Group: 0, Channel: 7, Timer: 3
** ledc: 8  => Group: 1, Channel: 0, Timer: 0
** ledc: 9  => Group: 1, Channel: 1, Timer: 0
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
** ledc
<think>我们正在讨论ESP32C6的定时器相关知识。根据之前提供的引用,虽然主要涉及ESP32C3通用ESP32定时器,但我们可以推断ESP32系列在定时器设计上有一定的延续性。由于ESP32C6是最新型号,其定时器特性可能与之前的型号类似但有所改进。 从引用中我们得知: - ESP32C3有2个54位通用定时器(引用[2]) - 通用定时器支持向上/向下计数,带16位分频器(引用[2]) - 定时器可用于产生定时中断或回调(引用[1], [5]) - 在回调函数中不能使用打印函数,否则会导致系统崩溃(引用[5]) 对于ESP32C6,我们可以参考ESP-IDF编程指南官方技术参考手册。以下是学习路径: 1. **官方文档**:ESP-IDF编程指南中关于定时器的章节([Timer Group](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32c6/api-reference/peripherals/tg_timer.html))是最权威的资料。ESP32C6的定时器通常属于定时器组(Timer Group),每个组包含多个硬件定时器。 2. **定时器特性**:根据之前ESP32系列的设计,ESP32C6可能具有: - 多个定时器组(例如2组) - 每个组包含若干定时器(如每个组2个,共4个) - 定时器位数可能是64位(因为引用[2]提到ESP32是64位,而ESP32C3是54位,ESP32C6可能会恢复到64位或更高) - 支持自动重装载(Auto-reload) - 支持多种计数模式(递增、递减) - 分频器(用于降低计数时钟频率) 3. **编程步骤**(以通用定时器为例,参考引用[5]): a. 初始化定时器:创建定时器句柄,配置定时器参数(计数方向、分频系数等) b. 配置定时器警报:设置触发中断的时间间隔 c. 安装定时器中断服务:注册中断回调函数 d. 启动定时器 e. 在回调函数中处理定时事件(注意:不能调用阻塞函数或打印函数) 4. **注意事项**: - 中断回调函数必须快速执行,不能调用任何阻塞函数(如vTaskDelay)或打印函数(如printf, ESP_LOGI),否则可能导致系统崩溃(引用[5])。 - 可以使用队列(queue)或任务通知(task notification)将事件传递给任务,在任务中处理耗时操作。 5. **代码示例**(基于ESP-IDF的gptimer驱动,这是ESP-IDF v5.0及以上版本推荐使用的定时器驱动): 以下是一个简单的ESP32C6定时器使用示例: ```c #include "driver/gptimer.h" static bool IRAM_ATTR timer_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) { // 注意:此回调在中断上下文中运行,不能使用任何阻塞函数或打印函数 // 可以通过发送队列等方式通知任务处理事件 BaseType_t high_task_awoken = pdFALSE; // 示例:发送事件到队列(假设队列已在外部创建) // xQueueSendFromISR(event_queue, &event_value, &high_task_awoken); return (high_task_awoken == pdTRUE); } void app_main() { // 1. 创建定时器句柄 gptimer_handle_t gptimer = NULL; gptimer_config_t timer_config = { .clk_src = GPTIMER_CLK_SRC_DEFAULT, // 默认时钟源 .direction = GPTIMER_COUNT_UP, // 递增计数 .resolution_hz = 1000000, // 1MHz的分辨率,即1个计数=1微秒 }; ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer)); // 2. 配置警报(定时周期为1秒) gptimer_alarm_config_t alarm_config = { .alarm_count = 1000000, // 1秒(因为分辨率是1MHz) .reload_count = 0, // 重装载值为0,表示每次到达警报值后从0开始重新计数 .flags.auto_reload_on_alarm = true, // 使能自动重载 }; ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config)); // 3. 注册回调函数 gptimer_event_callbacks_t cbs = { .on_alarm = timer_callback, // 设置警报回调函数 }; ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL)); // 4. 启动定时器 ESP_ERROR_CHECK(gptimer_enable(gptimer)); ESP_ERROR_CHECK(gptimer_start(gptimer)); // 这里可以创建队列,并在主循环中等待处理定时器事件 while(1) { // ... 处理事件 vTaskDelay(pdMS_TO_TICKS(1000)); } } ``` 6. **学习资源**: - ESP-IDF编程指南(C6版本):https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32c6/index.html - ESP32C6技术参考手册:需要登录乐鑫官网下载,其中详细描述了定时器的寄存器级操作。 7. **常见问题**: - 定时器重启问题:如引用[4]所示,可能是由于在中断回调中进行了非法操作(如打印)导致。请确保回调函数中不调用任何可能引起阻塞或分配内存的函数。 §§相关问题§§ 1. ESP32C6的定时器ESP32C3有什么主要区别? 2. 如何配置ESP32C6定时器以触发周期性中断? 3. 在ESP32C6定时器中断回调中可以进行哪些操作? 4. 如何解决ESP32C6定时器导致的系统重启问题? </think>### ESP32C6定时器学习指南 根据引用资料ESP32系列芯片的通用特性,以下是ESP32C6定时器的核心知识及学习路径: #### 一、基础特性(参考[1][2][3]) 1. **硬件架构**: - ESP32C6配备**通用定时器组(TIMG)**,每组包含多个硬件定时器 - 定时器为**54/64位计数器**(具体位数需查手册),支持向上/向下计数模式 - 内置**16位分频器**,时钟源可选(如APB总线时钟) ```c // 典型配置结构(伪代码) gptimer_config_t timer_cfg = { .clk_src = GPTIMER_CLK_SRC_DEFAULT, .direction = GPTIMER_COUNT_UP, .resolution_hz = 1_000_000, // 1MHz分辨率 }; ``` 2. **关键功能**: - 周期定时中断 - 自动重装载(Auto-reload) - 脉冲计数/信号捕获 - PWM波形生成(需配合LEDC外设) #### 二、学习资料推荐 1. **官方文档**(首要资源): - [ESP-IDF编程指南 - 定时器章节](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32c6/api-reference/peripherals/tg_timer.html) - [ESP32C6技术参考手册](https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf)(寄存器级详解) 2. **实操教程**: - **基础定时器配置**(参考[3][5]): ```c // 初始化→警报设置→回调注册→启动 gptimer_handle_t timer; gptimer_new_timer(&timer_cfg, &timer); gptimer_set_alarm_action(timer, &alarm_cfg); gptimer_register_event_callbacks(timer, &cbs, NULL); gptimer_start(timer); ``` - **中断注意事项**(关键!参考[4][5]): - 回调函数中**禁止使用`printf`/`ESP_LOGI`** - 避免阻塞操作,可通过队列传递事件到任务处理 3. **示例代码库**: - ESP-IDF示例目录:`examples/peripherals/timer_group/` - 官方GitHub:[esp-idf定时器示例](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/timer_group) #### 三、调试技巧(参考[4]) 1. **常见崩溃场景**: - 中断回调中调用I/O函数 → 触发`abort()` - 寄存器配置冲突 → 检查外设时钟使能状态 2. **诊断工具**: - 使用`panic_handler`解析寄存器dump - 启用看门狗监控定时器稳定性 #### 四、扩展学习 1. **软件定时器对比**(参考[3]): | **特性** | 硬件定时器 | FreeRTOS软件定时器 | |----------------|---------------------|---------------------| | 精度 | 微秒级 | 毫秒级 | | 中断上下文 | 是 | 否(任务上下文) | | 资源占用 | 专用外设 | 内存/CPU调度 | | 适用场景 | 高精度定时/信号处理 | 任务调度/简单延时 | 2. **高级应用**: - 低功耗模式下的定时唤醒(结合睡眠模式) - 多定时器同步触发(PWM相位控制) --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aurora Smith

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值