本节内容:
一、什么是PWM?有何用途?
二、Arduino产生PWM信号的两种方式
三、串口输入输出的基本方法
四、串口控制变色灯的实现
一、什么是PWM?有何用途?
(一)什么是PWM
1.PWM介绍
PWM,全称:Pulse Width Modulation,通常中文翻译为:脉冲宽度调制,通过将一段数字信号编码为方波信号,在外部作用事物上达到拟输出效果的一种手段。实际上,就是通过数字控制产生不同占空比的方波来模拟输出。也就是使用PWM输出模拟信号。
图中的高电平和低电平所占比例都不同,我们知道,电压信号是离散的,在这里我们只能输出0V或者5V电压,那么想要连续输出3.7V或者其他介于0-5V之间的电压怎么办呢?
(1)通过一系列的计算我们得到,高电平信号占持续时间占这个周期得百分比决定了输出电压信号的大小。
(2)这里说百分比,就是下面要说的,占空比。
2.占空比
占空比就是在一个调制周期内,某个信号持续的时间占这个时间段的百分比。在前一个图中,占空比就是不同的,并且是高电平的占空比。因此,低占空比意味着输出的能量低,因为在一个周期内大部分时间信号处于关闭状态,如果PWM控制的负载为LED,则具体表现例如LED灯很暗。高占空比意味着输出的能量高,在一个周期内,大部分时间信号处于on状态,具体表现为LED比较亮。
综上,我们大概可以了解PWM究竟是什么了:脉宽调制,也就是高电平信号在一个调制周期中持续时间长短,它可以用占空比去衡量,占空比越大,脉冲宽度越宽。
(二)PWM用途
1.PWM软件法控制充电电流
该方法的基本思想就是利用单片机具有的PWM端口,在不改变PWM方波周期的前提下,通过软件的方法调整单片机的PWM控制寄存器来调整PWM的占空比,从而控制充电电流。
2.PWM在推力调制中的应用
1962年,Nicklas等提出了脉冲调制理论,指出利用喷气脉冲对航天器控制是简单有效的控制方案,同时能使时间或能量达到最优控制。
脉宽调制发动机控制方式是在每一个脉动周期内,通过改变阀门在开或关位置上停留的时间来改变流经阀门的气体流量,从而改变总的推力效果,对于质量流率不变的系统,可以通过脉宽调制技术来获得变推力的效果。
3.在LED中的应用
在LED控制中PWM作用于电源部分,脉宽调制的脉冲频率通常大于100Hz,人眼就不会感到闪烁。
二、产生PWM信号的两种方式
先确定哪些引脚支持PWM,ATmega328的3, 5, 6, 9, 10, 和 11引脚支持PWM
(一)适用于所有GPIO的纯软件编程方式
void setup(){
pinMode(11, OUTPUT);//设定11号端口为输出
}
void loop(){
digitalWrite(11, HIGH); //11号引脚输出高电平
delayMicroseconds(100); // 大约10%占空比的1KHz方波 digitalWrite(10, LOW); //10号引脚输出低电平
delayMicroseconds(900);//延时900微秒
}
适用于所有GPIO的纯软件编程方式的优缺点
PWM 的比例可以更精确;
周期和频率可控制;
所有的 pin 脚都可以输出,不局限于那几个脚;
缺点:CPU 干不了其他事情了;
(二)通过analogWrite函数来完成PWM信号输出
analogWrite(pin,value)
作用:让一个支持PWM输出的引脚持续输出指定脉冲宽度的方波。
参数:pin:PWM输出的引脚编号。Pin可以等于3、5、6、9、10、11
value:用于控制占空比,范围:0~255。值为0表示占空比为0,值为255表示占空比为100%,值为127表示占空比为50%。
void setup() {
pinMode(9,OUTPUT);//设定9号端口为输出端口
void loop() {
analogWrite(9, led);//将led的值写入9号端口
if (value==0)
{
digitalWrite(pin,LOW);//输出低电平
}
else if (value==255)
{
digitalWrite(pin,HIGH); //输出高电平
}
二、串口输入输出的基本方法
(一)Arduino串口的使用——与计算机交流
• Arduino与计算机通信最常用的方式就是串口通信
• 我们使用USB线连接Arduino Uno与计算机时,Arduino Uno会在计算机上虚拟出一个串口设备,此时两者之间便建立了串口连接。通过此连接,Arduino Uno便可与计算机互传数据。
(二)串口初始化
• 使用串口与计算机通信,需要先使用Serial.begin() 初始化Arduino的串口通信功能。
• 参数speed是指串口通信波特率,这是设定串口通信速率的参数。串口通信的双方必须使用同样的波特率,方能正常进行通信。
• 波特率是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数。例如9600波特表示每秒发送9600bit的数据。通信双方需要使用一致的的波特率才能正常通信。Arduino串口通信通常会使用以下波特率:
• 300、600、1200、2400、4800、9600、14400、19200、28800、38400、57600、115200 (单位:bps)
• 波特率越大,说明串口通信的速率越快。
(三)串口输出
串口初始化完成后,我们便可以使用Serial.print() 或Serial.println() 向计算机发送信息了。
• 参数val是你要输出的数据,各种类型的数据均可。
• Serial.println(val) 语句也是使用串口输出数据,不同的是println() 函数会在输出完指定数据后,再输出一组回车换行符。
• 使用串口输出数据到计算机:
上传该程序到Arduino Uno,然后可以通过Arduino IDE右上角的图标打开串口监视器。
注意保证串口监视器右下方的波特率和上传程序中的一致,才能保证正常运行。
(四)串口输入
除了输出,串口同样可以接收由计算机输出的数据。接收串口数据需要使用Serial.read()函数。
• 调用该语句,每次都会返回一个字节的数据,这个返回值便是当前串口读取到的数据。
• 上传以下程序到Arduino:
上传成功后,运行串口监视器,在发送按键左侧的文本框中输入要发送的信息,如"arduino",你会看到Arduino在输出了你输入的信息同时,还输出了很多乱码,这些乱码是因为串口缓冲区中没有可读数据造成的。当缓冲区中没有可读数据时,read()函数会返回int型值-1,而int型-1对应的char型数据便是该乱码。
在使用串口时,Arduino Uno会在SRAM中开辟一段大小为256 bytes的空间,串口接收到的数据都会被暂时存放进这个空间中,这个存储空间,我们称之为缓冲区。当你调用Serial.read()语句时,Arduino便会从缓冲区取出一个字节的数据。
通常使用串口读取数据时,需要搭配Serial.available() 语句使用。
Serial.available() 的返回值便是当前缓冲区中接收到的数据字节数。
Serial.available() 可以搭配if 或者while使用,先检测缓冲区中是否有可读数据,如果有数据,再读取,没有数据便跳过读取或等待读取。如:
上传完成后,打开串口监视器,键入并发送任意信息。你会看到Arduino输出了你发送过去的信息,并且不会再出现乱码了。
需要注意的是,在串口监视器右下角有两个选项,一个是设置结束符,一个是设置波特率。如果你设置了结束符,则在你最后发送完数据后,串口监视器会自动发送一组你设定的结束符,如回车符和换行符。
四、串口控制变色灯的实现
(一)简单回顾下之前的内容:
1.什么是PWM?
脉冲宽度调制技术,通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形(含形状和幅值)。
2.analogWrite(pin,value)函数
参数:pin:整型,要写入的引脚编号;
value:占空比,在0~255之间。(0时候为关,即LOW;255为开,即HIGH)
返回值:无
(二)本节所需的组件:
共阴极 RGB LED灯
3个470欧电阻
Arduino Nano开发板
面包板和跳线
(三)认识一下RGB LED:
RGB LED灯可以通过混合红色、绿色和蓝色这三种基本颜色来发出不同的颜色。所以它实际上由3个独立的LED组成,红色、绿色和蓝色包装在一个盒子里。这就是为什么它有4个引脚,3种颜色中的每一种都有一根引线,而RGB LED类型则有共用阴极或阳极。在本篇文章中,我使用的是共阴极。
共阳极和共阴极在使用上是有区别的,区别分为以下两点:
(1)接线中的改变,共阳的话,共用端需要接5V,而不是GND,否则LED不能被点亮。
(2)在颜色的调配上,共阳极与共阴极是完全相反的。
举个例子:共阴RGB显示红色为R-255,G-0,B-0。然而共阳则完全相反,RGB数值是R-0,G-255,B-255。
(四)工作原理
RGB只是简单的把三个颜色的LED灯封装在一个LED中。只要当做三个灯使用就可以了。我们都知道红色、绿色、蓝色是三原色,通过组合这些基本颜色可以得到任何色调。我们将使用PWM模拟输出,这将为LED提供不同的电压电平,通过串口得到数值,再使Arduino通过PWM口对三种颜色明暗的调节,也就analogWrite(value)语句,就能让LED调出任何你想要的颜色。
(五)电路原理图
我们将阴极接地,3个阳极通过470欧姆电阻连接到Arduino开发板上的3个数字引脚,这些引脚可提供PWM信号。我们将使用PWM模拟模拟输出,这将为LED提供不同的电压电平,以便我们可以获得所需的颜色。
(六)源代码
我使用的是引脚9、10和11,分别将它们命名为RedPin、GreenPin和BluePin。并定义一个字符串变量str和一个整形数组RGB。
String str; //定义字符串变量str
int RedPin = 9; //红灯连接引脚为9的接口
int GreenPin = 10; //绿灯连接10接口
int BluePin = 11; //蓝灯连接11接口
int RGB[3]; //定义整形数组RGB
在setup部分,我们需要将它们定义为输出。开启串口。
void setup(){//创建函数
Serial.begin(9600); // 打开串口
pinMode(RedPin, OUTPUT); //设定红灯引脚为输出模式
pinMode(GreenPin, OUTPUT);//设定绿灯引脚为输出模式
pinMode(BluePin, OUTPUT);//设定蓝灯引脚为输出模式
}
现在在loop函数中我们将制作程序,它将根据串口输入的数值改变LED的颜色。
void loop(){//创建循环函数
if (Serial.available() > 0) { // 当串口接收到数据时
str = Serial.readString(); //将串口接收到数据赋给str变量
}
if (str != "") { //当接收到数据时
//对输入的数字分割读取并赋给数组
RGB[0] = str.substring(0, str.indexOf(',')).toInt();
RGB[1] = str.substring(str.indexOf(',')+1, str.lastIndexOf(',')).toInt();
RGB[2] = str.substring(str.lastIndexOf(',')+1).toInt();
}
setcolor(RGB[0], RGB[1], RGB[2]); //调用setcolor函数
}
函数参考:
① 变量.Substring(参数1,参数2);
截取字串的一部分,参数1为左起始位数,参数2为截取几位。
如:string s1 = str.Substring(0,2); //截取第0位到第2位三位字节赋给s1
② 变量.IndexOf()
查找字串中指定字符或字串首次出现的位置,返首索引值。
如:str1.IndexOf("字"); //查找“字”在str1中的索引值(位置)
③变量 .toInt()
可以将字符串转换成整型
如:上述代码将字符串转化为整型
然后我定义了一个名为setcolor()的自定义函数,它接受3个不同的参数red、green和blue。这些参数表示LED的亮度或使用analogWrite()函数创建的PWM信号的占空比,在0到255之间变化。
void setcolor(int red, int green, int blue){//创建setColor函数
analogWrite(RedPin,redValue);//analogWrite函数通过PWM的方式在引脚上输出一个模拟量,控制LED亮度
analogWrite(GreenPin,greenValue);//analogWrite函数通过PWM的方式在引脚上输出一个模拟量,控制LED亮度
analogWrite(BluePin,blueValue);//analogWrite函数通过PWM的方式在引脚上输出一个模拟量,控制LED亮度
}
(七)实验步骤
电路的连接非常简单:
1. 将RGB LED插入面包板。
2. 将共阴极的公共端,与Arduino中的GND引脚连接。
3. 将每个LED引线(红色、绿色和蓝色)与Arduino中的数字引脚相连, 每个引脚与470欧姆电阻串联。
4. 利用USB连至计算机。
5. 输入代码,上传即可实现。
(八)实验结果
(九)map函数的用法
1.函数原型:map(value, fromLow, fromHigh, toLow, toHigh)
2.功能: 将数字从一个范围重新映射到另一个范围。
3.参数:value: 要映射的数字
fromLow: 值当前范围的下限
fromHigh: 值当前范围的上限
toLow: 值的目标值范围的下限
toHigh: 值的目标值范围的上限
4.返回值:映射的值
5.例如:val = map(analogRead(0),0,1023,100, 200); // 将analog0 所读取到的讯号对等转换至100 – 200之间的数值。
以上实现了如 输入255,0,0 得到红色 等 输入对应值RGB 呈现相应颜色 的过程。