ESP32学习之定时器和PWM

一.定时器代码如下:


#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: 11 => Group: 1, Channel: 3, Timer: 1
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
*/

// 绑定的IO
const int PWM_Pin = 2;

// PWM的通道,共16个(0-15),分为高低速两组,
// 高速通道(0-7): 80MHz时钟,低速通道(8-15): 1MHz时钟
// 0-15都可以设置,只要不重复即可,参考上面的列表
// 如果有定时器的使用,千万要避开!!!
const int LEDC_PWM_Channel = 0;//低速高速最后得到的占空比都一样,没差别,可能80MHz计数更快一点?

// PWM频率,直接设置即可(就是周期,一个周期有多少时间,1000hz代表1ms)
int Base_freq_PWM = 1000;

// PWM分辨率,取值为 0-20 之间,这里填写为10,那么后面的ledcWrite
// 这个里面填写的pwm值就在 0 - 2的10次方 之间 也就是 0-1024
int Freq_resolution_PWM = 10;

void setup() {

  pinMode(PWM_Pin, OUTPUT);

    ledcSetup(LEDC_PWM_Channel, Base_freq_PWM, Freq_resolution_PWM); // 设置通道
    ledcAttachPin(PWM_Pin, LEDC_PWM_Channel);                   //将 LEDC 通道绑定到指定 IO 口上以实现输出
}


void loop() {


  ledcWrite(LEDC_PWM_Channel, 200);//200就是占空比,200/1000*100% = 20%

}

 运行结果:周期1ms,高电平0.2ms,占空比20%,通道是8通道,属于低速通道(8-15): 1MHz时钟,周期1ms,高电平0.2ms,占空比20%,通道是8通道,属于高速通道(0-7): 80MHz时钟。低速时钟或者高速时钟最后得到的占空比都一样,没差别,可能80MHz计数更快一点?若200改为1024,就都是高电平。

 上图:周期为1ms

 上图:占空比为20%,0.2/1 *100% = 20%

再举一个例子:周期为20ms,高电平持续时间为1ms,占空比为0.05。

f=1/T=1/(20/1000)=50,T的单位为秒,所以要将ms转为s。

占空比=duty/T ===》 duty = 2.5

代码如下:

结果为(正确):

 

三.扩展知识

1、Speed Mode
LED PWM 控制器高速和低速模式,高速模式的优点是可平稳地改变定时器设置。
意思就是说高速模式下,如果定时器的设置发生了改变,那么在下一次定时器的溢出中断中就会自动改变;但是低速模式下不会自动改变的。
2、频率和占空比分辨率支持范围:
这个先了解一下占空比和分辨率
占空比:就是高电平接通时间与周期的比
例如:一个PWM频率为1000hz,那周期是1ms,如果高电平时间是100us,那么占空比就是100us:1ms=1:10;
分辨率:就是占空比的最小值
是根据PWM的位数计算的,1:2^位数,如果位数是8,那么PWM的分辨率就是1:255,要是想要达到这个分辨率那就要计数器从0计算到255才行,如果计数值太小,那么他的分辨率就达不到1:255,那PWM的输出频率就变高了。
对于esp32控制器 PWM 占空比设置的分辨率范围较广。比如,PWM 频率为 5 kHz 时,占空比分辨率最大可为 13 位。这意味着占空比可为 0 至 100% 之间的任意值,分辨率为 ~0.012%(1/(2 * 13 )= 1/8192)。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: 11 => Group: 1, Channel: 3, Timer: 1
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
*/

// 绑定的IO
const int PWM_Pin = 2;

int i = 1;

// PWM的通道,共16个(0-15),分为高低速两组,
// 高速通道(0-7): 80MHz时钟,低速通道(8-15): 1MHz时钟
// 0-15都可以设置,只要不重复即可,参考上面的列表
// 如果有定时器的使用,千万要避开!!!
const int LEDC_PWM_Channel = 0;//低速时钟或者高速时钟最后得到的占空比都一样,没差别,可能80MHz计数更快一点?

// PWM频率,直接设置即可(就是周期,一个周期有多少时间,1000hz代表1ms)
int Base_freq_PWM = 50;

// PWM分辨率,取值为 0-20 之间,这里填写为10,那么后面的ledcWrite
int Freq_resolution_PWM = 6;

void setup() {

  pinMode(PWM_Pin, OUTPUT);

    ledcSetup(LEDC_PWM_Channel, Base_freq_PWM, Freq_resolution_PWM); // 设置通道
    ledcAttachPin(PWM_Pin, LEDC_PWM_Channel);                   //将 LEDC 通道绑定到指定 IO 口上以实现输出
}


void loop() {

  for(i = 1; i <= 50;i++)
  {
  ledcWrite(LEDC_PWM_Channel, i);
  delay(20);
  }

  for(; i > 0;i--)
  {
  ledcWrite(LEDC_PWM_Channel, i);
  delay(20);
  }

}

五.传统的PWM

1、analogWrite(pin,dutyCycle)

// 引脚命名
# define analogPin 3
void setup()
{
	pinMode(analogPin,OUTPUT);
}
void loop()
{
	analogWrite(analogPin,100);		// 输出PWM,占空比为 100/255
}

2、手动实现 PWM

通过 delayMicroseconds() 手动实现频率可调的 PWM,也被称作数字IO轮转法,使用方法:

  1. 两次的digitalWrite输出状态必须相反;
  2. 可以用delay()实现毫秒级延迟,用delayMicroseconds()实现微秒级延迟。
void setup()
{
  pinMode(8, OUTPUT);				// 设置8号引脚为输出模式
}

void loop()
{
  digitalWrite(8, HIGH);
  delayMicroseconds(100); 			// 输出PWM,占空比为100/1000=10%
  digitalWrite(8, LOW);
  delayMicroseconds(1000 - 100); 	// 修改这里的1000可以调整频率,总周期为1000us,所以频率为1000Hz.
}

上面这段代码会产生一个PWM=0.1的,周期为1ms的方波(1kHz),这种方式的优缺点很明显:

  1. PWM的比例可以更精确;
  2. 周期和频率可控制;
  3. 所有的pin脚都可以输出,不局限于那几个脚;
  4. 缺点:CPU干不了其他事情了;

 六.舵机驱动程序

代码如下:


#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: 11 => Group: 1, Channel: 3, Timer: 1
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
*/

// 绑定的IO
const int PWM_Pin = 2;

int i = 1;

// PWM的通道,共16个(0-15),分为高低速两组,
// 高速通道(0-7): 80MHz时钟,低速通道(8-15): 1MHz时钟
// 0-15都可以设置,只要不重复即可,参考上面的列表
// 如果有定时器的使用,千万要避开!!!
const int LEDC_PWM_Channel = 8;//低速时钟或者高速时钟最后得到的占空比都一样,没差别,可能80MHz计数更快一点?

// PWM频率,直接设置即可(就是周期,一个周期有多少时间,1000hz代表1ms)
int Base_freq_PWM = 50;

// PWM分辨率,取值为 0-20 之间,这里填写为10,那么后面的ledcWrite
int Freq_resolution_PWM = 5;

void setup() {

  pinMode(PWM_Pin, OUTPUT);

    ledcSetup(LEDC_PWM_Channel, Base_freq_PWM, Freq_resolution_PWM); // 设置通道
    ledcAttachPin(PWM_Pin, LEDC_PWM_Channel);                   //将 LEDC 通道绑定到指定 IO 口上以实现输出
}


void loop() {

  ledcWrite(LEDC_PWM_Channel, 1.25);//0度
  delay(2000);

  ledcWrite(LEDC_PWM_Channel, 2.5);//45度
  delay(2000);


  ledcWrite(LEDC_PWM_Channel, 3.75);//90度
  delay(2000);

  ledcWrite(LEDC_PWM_Channel, 5);//135度
  delay(2000);

  ledcWrite(LEDC_PWM_Channel, 6.25);//180度
  delay(2000);
  

}

好像180度舵机没有反应,不知道是不是我的舵机坏了。

代码二,舵机从0度到180度,再从180度到0度:


#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: 11 => Group: 1, Channel: 3, Timer: 1
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
*/

// 绑定的IO
const int PWM_Pin = 2;

int i = 1;

// PWM的通道,共16个(0-15),分为高低速两组,
// 高速通道(0-7): 80MHz时钟,低速通道(8-15): 1MHz时钟
// 0-15都可以设置,只要不重复即可,参考上面的列表
// 如果有定时器的使用,千万要避开!!!
const int LEDC_PWM_Channel = 8;//低速时钟或者高速时钟最后得到的占空比都一样,没差别,可能80MHz计数更快一点?

// PWM频率,直接设置即可(就是周期,一个周期有多少时间,1000hz代表1ms)
int Base_freq_PWM = 50;

// PWM分辨率,取值为 0-20 之间,这里填写为10,那么后面的ledcWrite
int Freq_resolution_PWM = 5;

void setup() {

  pinMode(PWM_Pin, OUTPUT);

    ledcSetup(LEDC_PWM_Channel, Base_freq_PWM, Freq_resolution_PWM); // 设置通道
    ledcAttachPin(PWM_Pin, LEDC_PWM_Channel);                   //将 LEDC 通道绑定到指定 IO 口上以实现输出
}


void loop() {

  for(i = 0; i < 8; i++)
  {
  ledcWrite(LEDC_PWM_Channel, i);
  delay(100);
  }

  for(; i > 0; i--)
  {
  ledcWrite(LEDC_PWM_Channel, i);
  delay(100);
  }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/30413.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis 哨兵模式

哨兵模式 自动选举 Redis 主服务器&#xff08;当主服务器宕机时&#xff09; 在 Redis2.8 之前&#xff0c;采用手动配置主从机的形式&#xff08;会导致一段时间服务不可用&#xff09; Redis2.8 之后&#xff0c;Redis正是提供了 Sentinel&#xff08;哨兵&#xff09;来解…

逆向Android开发工程,抓包!抓包!学习哪里?

抓包是什么&#xff1f; 在Android逆向工程中&#xff0c;抓包是一项重要的技术&#xff0c;用于获取手机应用程序与服务器之间的通信数据。通过抓包&#xff0c;可以分析应用程序的网络请求&#xff0c;获取请求的URL、参数、响应数据等信息&#xff0c;对应用程序的行为进行…

Python编程入门基础及高级技能、Web开发、数据分析和机器学习与人工智能

文章目录 入门基础安装 Python 环境&#xff0c;选择一个 IDE&#xff0c;如 PyCharm、VSCode等。学习基本语法&#xff1a;变量、数据类型、条件语句、循环语句、函数、异常处理等。熟悉标准库&#xff1a;常用模块、内置函数等。学习基本的面向对象编程&#xff08;OOP&#…

Rust in Action笔记 第六章 内存

Option<T>类型在Rust中使用了空指针优化&#xff08;null pointer optimization&#xff09;来保证该类型在编译后的二进制文件中占用0个字节。None变量是通过一个空指针null pointer来表示&#xff1b;内存地址、指针、引用的区别&#xff0c;内存地址是指在内存中的一个…

【kubernetes】Etcd集群部署与验证

前言:二进制部署kubernetes集群在企业应用中扮演着非常重要的角色。无论是集群升级,还是证书设置有效期都非常方便,也是从事云原生相关工作从入门到精通不得不迈过的坎。通过本系列文章,你将从虚拟机准备开始,到使用二进制方式从零到一搭建起安全稳定的高可用kubernetes集…

基于OpenMV的自动驾驶智能小车模拟系统

一、项目简介 基于机器视觉模块OpenMV采集车道、红绿灯、交通标志等模拟路况信息&#xff0c;实现一辆能车道保持、红绿灯识别、交通标志识别、安全避障以及远程WiFi控制的多功能无人驾驶小车。 赛道规格&#xff1a; 1、编程所需软件&#xff1a; OpenMV&#xff1a;使用Op…

Python自动化测试框架:Pytest和Unittest的区别

pytest和unittest是Python中常用的两种测试框架&#xff0c;它们都可以用来编写和执行测试用例&#xff0c;但两者在很多方面都有所不同。本文将从不同的角度来论述这些区别&#xff0c;以帮助大家更好地理解pytest和unittest。 1. 原理 pytest是基于Python的assert语句和Pytho…

Git第二章、多人协作

一、多人协作一 目前&#xff0c;我们所完成的工作如下&#xff1a; • 基本完成 Git 的所有本地库的相关操作&#xff0c;git基本操作&#xff0c;分支理解&#xff0c;版本回退&#xff0c;冲突解决等等 • 申请码云账号&#xff0c;将远端信息clone到本地&#xff0c;以及推…

一种基于目标的可解释的自动驾驶预测和规划策略

摘要&#xff1a; 本文介绍了一种通过理性逆向规划进行目标识别和多模态轨迹预测的方法。通过将目标识别与MCTS 计划相结合&#xff0c;为自车生成优化计划。 最近炒得比较火的影子模式实际就是在通过数据收集的方式不断模拟自动驾驶系统按照人类驾驶习惯实现人之间的交互过程…

Qt 中动态加载窗口(C++)

在编程中&#xff0c;我经常会遇见要根据用户触发按钮&#xff0c;动态生成窗口的情况。在此有两种方法可以动态生成窗口&#xff1a;一&#xff1a;直接在槽函数中调用窗口类。二&#xff1a;将 **.ui 添加到资源文件&#xff0c;通过 QUiLoader 加载。 现将两种方法介绍如下…

linuxOPS系统服务_Linux下用户管理

用户概念以及基本作用 **用户&#xff1a;**指的是Linux操作系统中用于管理系统或者服务的人 一问&#xff1a;管理系统到底在管理什么&#xff1f; 答&#xff1a;Linux下一切皆文件&#xff0c;所以用户管理的是相应的文件 二问&#xff1a;如何管理文件呢&#xff1f; …

【新星计划回顾】第六篇学习计划-通过自定义函数和存储过程模拟MD5数据

&#x1f3c6;&#x1f3c6;时间过的真快&#xff0c;这是导师回顾新星计划学习的第六篇文章&#xff01; 最近这段时间非常忙&#xff0c;虽然导师首次参与新星计划活动已经在4月16日圆满结束&#xff0c;早想腾出时间来好好整理活动期间分享的知识点。 &#x1f3c6;&#x1…

Unsupervised Learning(无监督学习)

目录 Introduction Clustering&#xff08;聚类&#xff09; Dimension Reduction&#xff08;降维&#xff09; PCA&#xff08;Principle component analysis&#xff0c;主成分分析&#xff09; Word Embedding&#xff08;词嵌入&#xff09; Matrix Factorization(矩…

适合嵌入式开发的GUI(嵌入式学习)

嵌入式开发的GUI如何选择&#xff1f; 常见的嵌入式GUI开发方法轻量级GUI库优缺点 基于Web技术优缺点 Qt框架优缺点 原生开发优缺点 嵌入式系统的限制 常见的嵌入式GUI开发方法 嵌入式开发中的GUI&#xff08;图形用户界面&#xff09;是指在嵌入式系统中实现图形化的用户界面…

Linux权限管理(超详解哦)

Linux权限 引言文件访问者的分类文件类型与访问权限文件类型访问权限 文件权限值的表示方法修改权限的指令chmod修改文件权限通过角色/-/权限来修改通过三个八进制数修改 chown修改所有者chgrp修改所属组umask修改或查看文件权限掩码文件创建时的权限 目录的权限粘滞位 总结 引…

驱动模块和printk函数

目录 1. 驱动模板 1.1. 在源码工程路径下创建.c文件 1.2. 编写驱动模板 1.3. 将模板放到ubuntu上 1.4. 书写Makefile 1.5. 编译和安装 2. printk 2.1. Source Insight查找命令 2.2. printk讲解 2.2.1. 分析函数 2.2.2. 编写代码 2.3. 拓展 2.3.1. 关于printk函数测…

ESP32开发环境搭建Windows VSCode集成Espressif IDF插件ESP32_IDF_V5.0开发编译环境搭建

一、安装ESP32-IDF库 下载网址&#xff1a;https://dl.espressif.com/dl/esp-idf/ 打开上面的网页&#xff0c;选择单击页面中 ESP32-IDF v5.0.2 - Offine Installer&#xff0c;5.0.2是当前最新版本&#xff0c;如果没有ESP32-IDF v5.0.2 - Offine Installer&#xff0c;说明…

ADB WIFI 链接

ADB WiFi链接手机 必须在同一网络下&#xff08;本人用的台式机网线手机连路由器WIFI&#xff09; 1.先确认USB数据线是否成功链接了手机 adb devices不管前面设备是什么名字&#xff0c;但是后面必须为device状态才算链接成功了&#xff0c;offline状态是不行的 有些没开启…

《微服务架构设计模式》第二章 服务的拆分策略

内容总结自《微服务架构设计模式》 服务的拆分策略 一、架构是什么软件架构的41视图模型为什么重要分层架构风格 二、定义微服务如何定义服务拆分难点定义服务API 一、架构是什么 软件架构的定义&#xff1a;计算机系统的软件架构是构建这个系统所需要的一组结构&#xff0c;包…

物联网Lora模块从入门到精通(八)Lora无线通信

一、前言 在某些环境下&#xff0c;无法通过有线传输数据&#xff0c;这时候我们需要使用Lora无线通信传输数据&#xff0c;Lora无线数据传输具有低功耗、距离长的特点&#xff0c;常用于工厂内等&#xff0c;需要Lora基站。 我曾做过距离测试&#xff1a;Lora模块距离测试-物联…