PWM控制无源蜂鸣器发声

本文详细介绍了如何在树莓派上利用sysfs接口管理和配置硬件PWM,包括确定可用引脚、设置PWM周期和占空比,以及使用WiringPi库控制无源蜂鸣器生成音乐。同时,讲解了WiringPi库的相关函数和PWM工作模式。
摘要由CSDN通过智能技术生成

1. 使用PWM控制sysfs接口

直接使用ls /sys/class/pwm命令得到的结果为空
因此要想使用硬件PWM,首先得确认哪些引脚可以输出硬件PWM。在/boot/overlays/README中提到,只有GPIO18是在全树莓派平台上都能作为硬件PWM输出脚的。其他引脚是否可用于PWM输出,可以使用raspi-gpio查看。例如:

pi@raspberrypi:~ $ raspi-gpio funcs 12
GPIO, DEFAULT PULL, ALT0, ALT1, ALT2, ALT3, ALT4, ALT5
12, DOWN, PWM0, SD4, DPI_D8, AVEOUT_VID8, AVEIN_VID8, ARM_TMS
pi@raspberrypi:~ $ raspi-gpio funcs 41
GPIO, DEFAULT PULL, ALT0, ALT1, ALT2, ALT3, ALT4, ALT5
41, DOWN, PWM1, SD5, TE0, SD1_DAT5, SPI2_MOSI, RXD1

可以看到GPIO12的第零号替代功能是PWM0,而GPIO41的第零号替代功能是PWM1。

然后需要在树莓派的启动配置文件/boot/config.txt里面加载对应的设备树overlay,并设置参数,比如:
dtoverlay=pwm,pin=12,func=4
就会将PWM开启在12号GPIO上。注意此处的func号必须与引脚的alt功能序号匹配,有个很奇怪的顺序:

Func 0 = Input
Func 1 = Output
Func 2 = Alt 5
Func 3 = Alt 4
Func 4 = Alt 0
Func 5 = Alt 1
Func 6 = Alt 2
Func 7 = Alt 3

所以这里func设为4,对应alt0,因为raspi-gpio告诉我们12号GPIO的PWM功能在alt0上。

1.1 硬件PWM的使用

Linux内核通过sysfs支持硬件PWM,所以这个部分的内容不仅限于树莓派,实际上所有实现了对应驱动的开发板都一样。
树莓派的raspbian系统映像已经提供了对应的驱动,可以直接使用。修改并保存/boot/config.txt之后重启设备,如果设置正确,可以在目录/sys/class/pwm中看到一些东西,比如:

pi@raspberrypi:~ $ ls /sys/class/pwm
pwmchip0
pi@raspberrypi:~ $ ls /sys/class/pwm//pwmchip0
device  export  npwm  power  subsystem  uevent  unexport

这些伪文件就是Linux内核PWM驱动提供的操纵接口,在shell里可以通过cat读,通过echo重定向写。在任意编程语言里也可以通过读写文件的接口进行同样的操作。

每个控制器的通道数可以在 npwm 中读取(只读)

pi@raspberrypi:~ $ cat /sys/class/pwm//pwmchip0/npwm
2

通过在“export”中写入相应的数字来导出每个通道(请求 sysfs 激活)
首先创建一个PWM的导出,向export写几,就会创建对应的目录在pwmchipX里面:

pi@raspberrypi:~ $ echo 0 > /sys/class/pwm/pwmchip0/export 
pi@raspberrypi:~ $ ls /sys/class/pwm/pwmchip0/               
device  export  npwm  power  pwm0  subsystem  uevent  unexport
pi@raspberrypi:~ $ ls /sys/class/pwm/pwmchip0/pwm0
capture  duty_cycle  enable  period  polarity  power  uevent

这里面,period是以纳秒计数的PWM周期,duty_cycle是以纳秒计数的每周期高电平时间。比如我想要一个20KHz的PWM,占空比为80%,那我就应当:

pi@raspberrypi:~ $ echo 50000 > /sys/class/pwm/pwmchip0/pwm0/period # 两万Hz的时长是五万纳秒
pi@raspberrypi:~ $ echo 10000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle # 占空比80%,那么20%的时长就是一万纳秒

然后向enable写0或者1进行开关。

pi@raspberrypi:~ $ echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

关闭之后不会清空原有设置,再次打开会以之前设置的参数运行PWM。

如果需要释放资源,向/sys/clas/pwm/pwmchipX/unexport写对应的序号,会清空对应的PWM导出目录,并且删除配置。
参考链接:raspberry-pi - 树莓派硬件PWM的使用方法 - 个人文章 - SegmentFault 思否

2. WringPi库-硬件PWM接口函数

使用硬件PWM接口需要包含头文件:#include <wiringPi.h>
树莓派3B的WiringPi映射表
在这里插入图片描述

BCM引脚图:
在这里插入图片描述

  1. int wiringPiSetup(void):
    • 初始化树莓派引脚,使用的是wiringPi引脚编号表,引脚的编号为0~16
    • 需要root权限
      • 返回-1表示失败
  2. int wiringPiSetupGpio(void)
    • 初始化树莓派引脚,使用BCM GPIO引脚编号表
    • 需要root权限
    • 返回-1表示失败
  3. pwmSetClock(int divisor)
    • divisor: 设置PWM时钟的分频。范围为2~4095
      :PWM基础时钟19.2MHz,WiringPi库在初始化时,默认divisor值是32,因此默认PWM时钟就为PWMfreq=19.2×1000×1000 / 32 = 600KHz。
  4. pwmSetMode(int mode)
    • mode:设置PWM的不同工作模式,其中PWM_MODE_BAL(Balanced模式), PWM_MODE_MS(Mark:Space模式(占空比模式))
    • Mark:Space模式是传统PWM模式,树莓派默认PWM工作在Balanced模式下。需要重新设置占空比,就要设置为Mark:Space模式。
  5. pwmSetRange(int range)
    • 用来设置PWM的周期,默认值为1024.计算方式:比如600KHz的PWM时钟,range = (600 x 1000Hz) / PWMfreq
  6. pwmWrite(int pin, int value)
    • pin: 硬件PWM引脚编号(在WiringPi中的编号),将在该引脚上产生PWM波。
    • value:设置占空比,value取值范围:0~range,默认范围:0~1023。因为一个周期分为range等份,所以占空比范围为0~range。

3. 使用硬件PWM驱动无源蜂鸣器

使用PWM驱动无源蜂鸣器发出有旋律的响声。
接线图:
将无源蜂鸣器的I/O接口接在树莓派的12引脚上,因为树莓派的12引脚对应WiringPi接口中的GPIO 1,所以在代码中的引脚编号为1
在这里插入图片描述

pi@raspberrypi:~ $ vim pwm.c
/* 无源蜂鸣器 */
#include <softPwm.h>
#include <wiringPi.h>
#include <softTone.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#define PIN   1

// 音谱定义
// 低C音符的频率
#define Tone_CL1 131
#define Tone_CL2 147
#define Tone_CL3 165
#define Tone_CL4 175
#define Tone_CL5 196
#define Tone_CL6 221
#define Tone_CL7 248

// 中C音符的频率
#define Tone_CM1 262
#define Tone_CM2 294
#define Tone_CM3 330
#define Tone_CM4 350
#define Tone_CM5 393
#define Tone_CM6 441
#define Tone_CM7 495

// 高C音符的频率
#define Tone_CH1 525
#define Tone_CH2 589
#define Tone_CH3 661
#define Tone_CH4 700
#define Tone_CH5 786
#define Tone_CH6 882
#define Tone_CH7 990

// 第一首歌音谱
int makerobo_song_1[] = {Tone_CM3, Tone_CM5, Tone_CM6, Tone_CM3, Tone_CM2, Tone_CM3, Tone_CM5, Tone_CM6, Tone_CH1, Tone_CM6, Tone_CM5, Tone_CM1, Tone_CM3, Tone_CM2, Tone_CM2, Tone_CM3, Tone_CM5, Tone_CM2, Tone_CM3, Tone_CM3, Tone_CL6, Tone_CL6, Tone_CL6, Tone_CM1, Tone_CM2, Tone_CM3, Tone_CM2, Tone_CL7, Tone_CL6, Tone_CM1, Tone_CL5};

// 节拍
int makerobo_beat_1[] = {1, 1, 3, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3};

int g_stop = 1;

void sig_handler(int signum)
{
	switch( signum )
	{
		case SIGINT:
		case SIGTERM:
			g_stop = 0;
		default:
			break;
	}

	return;
}

int main()
{
	int i;

	signal(SIGINT, sig_handler);
	signal(SIGTERM, sig_handler);

	if( wiringPiSetup() == -1 )
	{
		printf("makerobo setup wirinngPi falied: %s\n", strerror(errno));
		return -1;
	}

	pinMode(PIN, PWM_OUTPUT);
	pwmSetRange(1024);
	pwmSetClock(75);
	
	while( g_stop )
	{
		printf("makerobo music is being played...\n");
		for( i=0; i<sizeof(makerobo_song_1)/4; i++ )
		{
			pwmWrite(PIN, makerobo_song_1[i]);
			delay(makerobo_beat_1[i] * 500);
		}
	}
	printf("Exit this program!\n");
	pwmWrite(PIN, 0);
	
	return 0;
}

运行:

pi@raspberrypi:~ $ gcc pwm.c -o pwm -lwiringPi
pi@raspberrypi:~ $ sudo ./pwm

参考链接:

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值