张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出
什么是 PWM
在解释 PWM 之前首先来了解一下电路中信号的概念,其中包括模拟信号和数字信号。模拟信号是一种连续的信号,与连续函数类似,在图形上表现为一条不间断的连续曲线。数字信号为只能取有限个数值的信号,比如计算机中的高电平(1)和低电平(0)。
PWM(Pulse Width Modulation)即脉冲宽度调制,简称脉宽调制,通过对一系列的脉冲的宽度进行调制,从而等效出所需要的模拟信号。如图 1 所示,蓝色波形为调制的一系列脉冲,红色波形为模拟的正弦样信号。在模拟电路中,模拟信号的值可以连续进行变化,而数字电路是在高电平和低电平中取值,所以电压或电流会以脉冲的形式出现。通过使用 PWM 技术,我们可以在数字电路中模拟出电信号的连续变化。
图1:PWM 示意图
提示
看完上面的如果你还不明白,那么可以看看下面这个生动的解释,这个解释来源于百度知道:
“简单的说,比如你有5V电源,要控制一台灯的亮度,有一个传统办法,就是串联一个可调电阻,改变电阻,灯的亮度就会改变。还有一个办法,就是PWM调节。不用串联电阻,而是串联一个开关。假设在1秒内,有0.5秒的时间开关是打开的,0.5秒关闭,那么灯就亮0.5秒,灭0.5秒。这样持续下去,灯就会闪烁。如果把频率调高一点,比如是1毫秒,0.5毫秒开,0.5毫秒灭,那么灯的闪烁频率就很高。我们知道,闪烁频率超过一定值,人眼就会感觉不到。所以,这时你看不到灯的闪烁,只看到灯的亮度只有原来的一半。同理,如果1毫秒内,0.1毫秒开,0.9毫秒灭,那么,灯的亮度就只有原来的10分之一。”
使用 PWM 需要了解占空比(Duty Cycle)和频率(Frequency)的概念。占空比即 PWM 信号在一个周期内处于高电平的时间与整个周期的时间的比值。在 5V 电源的情况下,想要产生一个 3V 的信号,可以使用占空比为 60% 的 PWM。图 2 从波形的角度解释了 PWM。频率是 PWM 信号在 1 秒内完成一个周期的次数,单位是 Hz。如果输出的频率够高并保持一定的占空比,就可以模拟出恒定电压。图 3 对比了小灯亮度的变化与占空比的变化,通过观察图右侧的 PWM 波形可以看到占空比越高小灯越亮。
图2:占空比示意图
图3:小灯亮度变化与占空比变化对比
Raspberry Pi 上提供了硬件 PWM 功能,一共包括 2 个通道,引出了 4 个 GPIO 引脚。其中 GPIO 12 和 GPIO 18 属于通道 0,GPIO 13 和 GPIO 19 属于通道 1。但有意思的是只有通道 0 的 GPIO 18 引脚的默认功能为 PWM,其他的不是被音频处理所占用,就是引脚另有它用。启用这些引脚需要进行一些特殊配置甚至内核编程。
提示
如何启用 Raspberry Pi 上的 PWM ?
修改 /boot/config.txt ,添加 dtoverlay=pwm 。
启用 PWM 通道 1 请参考:https://github.com/raspberrypi/firmware/issues/1178
修改 GPIO 引脚功能请参考:https://www.dummies.com/computers/raspberry-pi/raspberry-pi-gpio-pin-alternate-functions 和 http://abyz.me.uk/rpi/pigpio/pigs.html
相关类
PWM 操作的相关类位于 System.Device.Pwm 命名空间下。
PwmChannel
public class PwmChannel : IDisposable
{
// 创建 PwmChannel 对象
// chip 为 PWM 芯片编号,Linux 下位于 /sys/class/pwm 文件夹下
// channel 为 通道编号
public static PwmChannel Create(int chip, int channel, int frequency = 400, double dutyCycle = 0.5);
// 占空比,取值为 0.0 - 1.0
public double DutyCycle { get; set; }
// 频率,单位为 Hz
public int Frequency { get; set; }
// 打开和关闭 PWM 通道
public void Start();
public void Stop();
}
PWM 的使用步骤
- 实例化一个 PwmChannel 对象
PwmChannel pwm = PwmChannel.Create(chip: 0, channel: 0, frequency: 400, dutyCycle: 0);
- 打开 PWM 通道
pwm.Start();
- 设置占空比/频率改变输出的 PWM 信号
pwm.DutyCycle = 0.5;
- 关闭 PWM 通道
pwm.Stop();
使用硬件 PWM 控制 LED 的亮度
硬件需求
名称 | 数量 |
---|---|
LED | x1 |
220 Ω 电阻 | x1 |
杜邦线 | 若干 |
电路
- LED 正极 - GPIO 18 (Pin 12)
- LED 负极 - GND
使用 Docker 运行示例
示例地址:https://github.com/ZhangGaoxing/dotnet-core-iot-demo/tree/master/src/PwmLed
docker build -t pwm-led-sample -f Dockerfile .
docker run --rm -it -v=/sys/class/pwm:/sys/class/pwm --privileged=true pwm-led-sample
代码
- 打开 Visual Studio ,新建一个 .NET Core 控制台应用程序,项目名称为“PwmLed”。
- 引入 System.Device.Gpio NuGet 包。
- 在 Program.cs 中,将主函数代码替换如下:
static void Main(string[] args)
{
int brightness = 0;
using PwmChannel pwm = PwmChannel.Create(chip: 0, channel: 0, frequency: 400, dutyCycle: 0);
pwm.Start();
while (brightness != 255)
{
pwm.DutyCycle = brightness / 255D;
brightness++;
Thread.Sleep(10);
}
while (brightness != 0)
{
pwm.DutyCycle = brightness / 255D;
brightness--;
Thread.Sleep(10);
}
pwm.Stop();
}
- 发布、拷贝、更改权限、运行
效果图
使用软件 PWM 控制 RGB LED
上面提到 Raspberry Pi 中默认只有 GPIO 18 这一个引脚可以使用 PWM,要控制 RGB LED 则至少需要使用 3 个 PWM,这显然是不够用的。在 Iot.Device.Bindings
这个 NuGet 包中为我们提供了使用 GPIO 模拟的软件 PWM 类 SoftwarePwmChannel
。软件 PWM 的使用效果并没有硬件 PWM 的那种“顺滑”,因为其精度完全取决于 GPIO 的速度。
提示
RGB LED 有三种颜色,但通常只有 4 个引脚,而三种单色 LED 却有 6 个引脚,为什么会少了 2 个引脚?RGB LED 分为共阳极和共阴极。如果少的两个引脚为阳极,则为共阳极 RGB LED,三个单色 LED 共用一个阳极,剩下的三个引脚为各自的阴极。共阴极 RGB LED 则相反。两种 LED 在使用上类似,但程序相反,比如共阴极时占空比越高 LED 越亮,而共阳极时,占空比越高则 LED 越暗。
硬件需求
名称 | 数量 |
---|---|
RGB LED | x1 |
220 Ω 电阻 | x3 |
杜邦线 | 若干 |
电路
- LED R - GPIO 18 (Pin 12)
- LED G - GPIO 23 (Pin 16)
- LED B - GPIO 24 (Pin 18)
- LED 阴极 - GND
使用 Docker 运行示例
示例地址:https://github.com/ZhangGaoxing/dotnet-core-iot-demo/tree/master/src/PwmRgb
docker build -t pwm-rgb-sample -f Dockerfile .
docker run --rm -it --device /dev/gpiomem pwm-rgb-sample
代码
- 打开 Visual Studio ,新建一个 .NET Core 控制台应用程序,项目名称为“PwmRgb”。
- 引入 Iot.Device.Bindings NuGet 包。
- 在 Program.cs 中,将主函数代码替换如下:
static void Main(string[] args)
{
using PwmChannel red = new SoftwarePwmChannel(pinNumber: 18, frequency: 400, dutyCycle: 0);
using PwmChannel green = new SoftwarePwmChannel(pinNumber: 23, frequency: 400, dutyCycle: 0);
using PwmChannel blue = new SoftwarePwmChannel(pinNumber: 24, frequency: 400, dutyCycle: 0);
red.Start();
green.Start();
blue.Start();
Breath(red, green, blue);
red.Stop();
green.Stop();
blue.Stop();
}
public static void Breath(PwmChannel red, PwmChannel green, PwmChannel blue)
{
int r = 255, g = 0, b = 0;
while (r != 0 && g != 255)
{
red.DutyCycle = r / 255D;
green.DutyCycle = g / 255D;
r--;
g++;
Thread.Sleep(10);
}
while (g != 0 && b != 255)
{
green.DutyCycle = g / 255D;
blue.DutyCycle = b / 255D;
g--;
b++;
Thread.Sleep(10);
}
while (b != 0 && r != 255)
{
blue.DutyCycle = b / 255D;
red.DutyCycle = r / 255D;
b--;
r++;
Thread.Sleep(10);
}
}
- 发布、拷贝、更改权限、运行
效果图
供参考
- Pulse-width modulation - Wikipedia:https://en.wikipedia.org/wiki/Pulse-width_modulation
- RPI4 : PWM0 & PWM1 Alternate pins - GitHub:https://github.com/raspberrypi/firmware/issues/1178
- Raspberry Pi GPIO Pin Alternate Functions:https://www.dummies.com/computers/raspberry-pi/raspberry-pi-gpio-pin-alternate-functions/
- PWM source code:https://github.com/dotnet/iot/tree/master/src/System.Device.Gpio/System/Device/Pwm
- 脉冲宽度调制 - 百度百科:https://baike.baidu.com/item/脉冲宽度调制/10813756
张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出的更多相关文章
- 张高兴的 .NET Core IoT 入门指南:(二)GPIO 的使用
什么是 GPIO GPIO 是 General Purpose Input Output 的缩写,即"通用输入输出". Raspberry Pi 有两行 GPIO 引脚, Rasp ...
- 张高兴的 .NET Core IoT 入门指南:(一)环境配置、Blink、部署
如何在 Raspberry Pi 的 Raspbian 上构建使用 GPIO 引脚的 IoT 程序?你可能会回答使用 C++ 或 Python 去访问 Raspberry Pi 的引脚.现在,C# 程 ...
- 张高兴的 .NET Core IoT 入门指南:(四)使用 SPI 进行通信
什么是 SPI 和上一篇文章的 I2C 总线一样,SPI(Serial Peripheral Interface,串行外设接口)也是设备与设备间通信方式的一种.SPI 是一种全双工(数据可以两个方向同 ...
- 张高兴的 .NET Core IoT 入门指南:(三)使用 I2C 进行通信
什么是 I2C 总线 I2C 总线(Inter-Integrated Circuit Bus)是设备与设备间通信方式的一种.它是一种串行通信总线,由飞利浦公司在1980年代为了让主板.嵌入式系统或手机 ...
- 张高兴的 .NET Core IoT 入门指南:(五)串口通信入门
在开始之前,首先要说明的是串口通信所用到的 SerialPort 类并不包含在 System.Device.Gpio NuGet 包中,而是在 System.IO.Ports NuGet 包中.之所以 ...
- 张高兴的 .NET IoT 入门指南:(七)制作一个气象站
距离上一篇<张高兴的 .NET Core IoT 入门指南>系列博客的发布已经过去 2 年的时间了,2 年的时间 .NET 版本发生了巨大的变化,.NET Core 也已不复存在,因此本系 ...
- 张高兴的 .NET IoT 入门指南:(八)基于 GPS 的 NTP 时间同步服务器
时间究竟是什么?这既可以是一个哲学问题,也可以是一个物理问题.古人对太阳进行观测,利用太阳的投影发明了日晷,定义了最初的时间.随着科技的发展,天文观测的精度也越来越准确,人们发现地球的自转并不是完全一 ...
- 【VB超简单入门】五、基本输出输入
之前讲了VB IDE的基本操作和概念,接下来要开始将VB语言的编程了. 程序最重要的部分是输出和输入,输入数据,经过计算机处理,再输出结果.本文将介绍两种最基本的输出输入方法,分别是Print.Msg ...
- OpenCV入门指南----人脸检测
本篇介绍图像处理与模式识别中最热门的一个领域——人脸检测(人脸识别).人脸检测可以说是学术界的宠儿,在不少EI,SCI高级别论文都能看到它的身影.甚至很多高校学生的毕业设计都会涉及到人脸检测.当然人脸 ...
随机推荐
- MOOC C++笔记(四):运算符重载
第四周:运算符重载 基本概念 运算符重载,就是对已有的运算符(C++中预定义的运算符)赋予多重的含义,使同一运算符作用于不同类型的数据时导致不同类型的行为. 运算符重载的目的是:扩展C++中提供的运算 ...
- 11-常用SQL总结
1.设置表的列不能为nullalter table run.dbo.T1 alter column Col1 int not null 2.给表添加主键alter table run.dbo.T1 a ...
- vue-router之路由元信息
路由元信息?(黑人问号脸???)是不是这么官方的解释很多人都会一脸懵?那么我们说meta,是不是很多人恍然大悟,因为在项目中用到或者看到过呢? 是的,路由元信息就是我们定义路由时配置的meta字段:那 ...
- HNU_中小学数学卷子自动生成程序(个人项目)简要分析
一.前言 首先,在这里特别感谢我的结对编程伙伴HnuLyx,他在算法上和设计思路上都与我有很大的不同,个人项目互评中,为我的项目提出了很多很好的建议,让我在认清自己不足的同时,了解到很多以前没有注意的 ...
- Spring boot 梳理 - mappingJackson2JsonView
MappingJacksonJsonView已被废弃了: http://static.javadoc.io/org.springframework/spring-webmvc/4.0.1.RELEAS ...
- 阿里云服务器CentOS6.9安装maven
1.下载maven http://maven.apache.org/download.cgi 2.移动到linux yangyuke用户下(此处由于我设置进入linux的是自定义用户yangyuke, ...
- 利用百度云接口实现车牌识别·python
一个小需求---实现车牌识别. 目前有两个想法 1. 调云在线的接口或者使用SDK做开发(配置环境和变异第三方库麻烦,当然使用python可以避免这些问题) 2. 自己实现车牌识别算法(复杂) 一开始 ...
- Java基础学习笔记(一) - 基础语法
1.Java程序开发过程 编译: 是指将我们编写的Java源文件翻译成JVM认识的class文件,javac编译器会检查我们所写的程序是否有错误,有错误就会提示出来,如果没有错误就会编译成功. 运行: ...
- .Net Core 商城微服务项目系列(九):使用Jenkins构建自动发布
1.首先通过Docker运行Consul,并保证各个服务都成功注册: 然后运行jenkins,对MI.Web项目进行发布构建,至于怎么配置之前已经写过了,和上一篇一模一样,这里贴下批处理命令: cd ...
- HttpWebRequest上传多文件和多参数——整理
1.整理HttpWebRequest上传多文件和多参数.较上一个版本,更具普适性和简易型.注意(服务方web.config中要配置)这样就可以上传大文件了 <system.webServer&g ...