mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-17 21:23:58 +00:00
tone: add package for producing tones using the PWM interface
This commit is contained in:
committed by
Ron Evans
parent
6bc466f79b
commit
df343190c2
@@ -117,6 +117,8 @@ smoke-test:
|
|||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
|
||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-bluefruit ./examples/tone
|
||||||
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/fourwire/main.go
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/fourwire/main.go
|
||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
|
||||||
@@ -177,7 +179,7 @@ endif
|
|||||||
DRIVERS = $(wildcard */)
|
DRIVERS = $(wildcard */)
|
||||||
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
||||||
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
|
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
|
||||||
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am
|
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone
|
||||||
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
|
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
|
||||||
|
|
||||||
unit-test:
|
unit-test:
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/tone"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Configuration for the Adafruit Circuit Playground Bluefruit.
|
||||||
|
pwm = machine.PWM0
|
||||||
|
pin = machine.D12
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
speaker, err := tone.New(pwm, pin)
|
||||||
|
if err != nil {
|
||||||
|
println("failed to configure PWM")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two tone siren.
|
||||||
|
for {
|
||||||
|
println("nee")
|
||||||
|
speaker.SetNote(tone.B5)
|
||||||
|
time.Sleep(time.Second / 2)
|
||||||
|
|
||||||
|
println("naw")
|
||||||
|
speaker.SetNote(tone.A5)
|
||||||
|
time.Sleep(time.Second / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
package tone
|
||||||
|
|
||||||
|
// Note represents a MIDI note number. For example, Note(69) is A4 or 440Hz.
|
||||||
|
type Note uint8
|
||||||
|
|
||||||
|
// Define all the notes in a format similar to the Tone library in the Arduino
|
||||||
|
// IDE.
|
||||||
|
const (
|
||||||
|
A0 Note = iota + 21 // 27.5Hz
|
||||||
|
AS0
|
||||||
|
B0
|
||||||
|
C1
|
||||||
|
CS1
|
||||||
|
D1
|
||||||
|
DS1
|
||||||
|
E1
|
||||||
|
F1
|
||||||
|
FS1
|
||||||
|
G1
|
||||||
|
GS1
|
||||||
|
A1 // 55Hz
|
||||||
|
AS1
|
||||||
|
B1
|
||||||
|
C2
|
||||||
|
CS2
|
||||||
|
D2
|
||||||
|
DS2
|
||||||
|
E2
|
||||||
|
F2
|
||||||
|
FS2
|
||||||
|
G2
|
||||||
|
GS2
|
||||||
|
A2 // 110Hz
|
||||||
|
AS2
|
||||||
|
B2
|
||||||
|
C3
|
||||||
|
CS3
|
||||||
|
D3
|
||||||
|
DS3
|
||||||
|
E3
|
||||||
|
F3
|
||||||
|
FS3
|
||||||
|
G3
|
||||||
|
GS3
|
||||||
|
A3 // 220Hz
|
||||||
|
AS3
|
||||||
|
B3
|
||||||
|
C4
|
||||||
|
CS4
|
||||||
|
D4
|
||||||
|
DS4
|
||||||
|
E4
|
||||||
|
F4
|
||||||
|
FS4
|
||||||
|
G4
|
||||||
|
GS4
|
||||||
|
A4 // 440Hz
|
||||||
|
AS4
|
||||||
|
B4
|
||||||
|
C5
|
||||||
|
CS5
|
||||||
|
D5
|
||||||
|
DS5
|
||||||
|
E5
|
||||||
|
F5
|
||||||
|
FS5
|
||||||
|
G5
|
||||||
|
GS5
|
||||||
|
A5 // 880Hz
|
||||||
|
AS5
|
||||||
|
B5
|
||||||
|
C6
|
||||||
|
CS6
|
||||||
|
D6
|
||||||
|
DS6
|
||||||
|
E6
|
||||||
|
F6
|
||||||
|
FS6
|
||||||
|
G6
|
||||||
|
GS6
|
||||||
|
A6 // 1760Hz
|
||||||
|
AS6
|
||||||
|
B6
|
||||||
|
C7
|
||||||
|
CS7
|
||||||
|
D7
|
||||||
|
DS7
|
||||||
|
E7
|
||||||
|
F7
|
||||||
|
FS7
|
||||||
|
G7
|
||||||
|
GS7
|
||||||
|
A7 // 3520Hz
|
||||||
|
AS7
|
||||||
|
B7
|
||||||
|
C8
|
||||||
|
CS8
|
||||||
|
D8
|
||||||
|
DS8
|
||||||
|
E8
|
||||||
|
F8
|
||||||
|
FS8
|
||||||
|
G8
|
||||||
|
GS8
|
||||||
|
A8 // 7040Hz
|
||||||
|
AS8
|
||||||
|
B8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Period returns the period in nanoseconds of a single wave.
|
||||||
|
func (n Note) Period() uint64 {
|
||||||
|
if n == 0 {
|
||||||
|
// Assume that a zero note means no sound.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
octave := (n - 9) / 12
|
||||||
|
note := (n - 9) - octave*12
|
||||||
|
|
||||||
|
// Start with a base period (in nanoseconds) of 6.875Hz (quarter the
|
||||||
|
// frequency of A0) and shift it right with the octave to get the base
|
||||||
|
// period of this note.
|
||||||
|
// 145454545 = 1e9 / 6.875
|
||||||
|
basePeriod := uint32(145454545) >> octave
|
||||||
|
|
||||||
|
// Make the pitch higher based on the note within the octave.
|
||||||
|
period := uint64(basePeriod) * uint64(tones[note]) / 32768
|
||||||
|
|
||||||
|
return period
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants to calculate the pitch within an octave. Python oneliner:
|
||||||
|
// [round(1e9/(440*(2**(n/12))) / (1e9/440) * 0x8000) for n in range(12)]
|
||||||
|
var tones = [12]uint16{
|
||||||
|
32768,
|
||||||
|
30929,
|
||||||
|
29193,
|
||||||
|
27554,
|
||||||
|
26008,
|
||||||
|
24548,
|
||||||
|
23170,
|
||||||
|
21870,
|
||||||
|
20643,
|
||||||
|
19484,
|
||||||
|
18390,
|
||||||
|
17358,
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package tone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PWM is the interface necessary for controlling a speaker.
|
||||||
|
type PWM interface {
|
||||||
|
Configure(config machine.PWMConfig) error
|
||||||
|
Channel(pin machine.Pin) (channel uint8, err error)
|
||||||
|
Top() uint32
|
||||||
|
Set(channel uint8, value uint32)
|
||||||
|
SetPeriod(period uint64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speaker is a configured audio output channel based on a PWM.
|
||||||
|
type Speaker struct {
|
||||||
|
pwm PWM
|
||||||
|
ch uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Speaker instance readily configured for the given PWM and
|
||||||
|
// pin combination. The lowest frequency possible is 27.5Hz, or A0. The audio
|
||||||
|
// output uses a PWM so the audio will form a square wave, a sound that
|
||||||
|
// generally sounds rather harsh.
|
||||||
|
func New(pwm PWM, pin machine.Pin) (Speaker, error) {
|
||||||
|
err := pwm.Configure(machine.PWMConfig{
|
||||||
|
Period: uint64(1e9) / 55 / 2,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return Speaker{}, err
|
||||||
|
}
|
||||||
|
ch, err := pwm.Channel(pin)
|
||||||
|
if err != nil {
|
||||||
|
return Speaker{}, err
|
||||||
|
}
|
||||||
|
return Speaker{pwm, ch}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop disables the speaker, setting the output to low continuously.
|
||||||
|
func (s Speaker) Stop() {
|
||||||
|
s.pwm.Set(s.ch, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPeriod sets the period for the signal in nanoseconds. Use the following
|
||||||
|
// formula to convert frequency to period:
|
||||||
|
//
|
||||||
|
// period = 1e9 / frequency
|
||||||
|
//
|
||||||
|
// You can also use s.SetNote() instead for MIDI note numbers.
|
||||||
|
func (s Speaker) SetPeriod(period uint64) {
|
||||||
|
// Disable output.
|
||||||
|
s.Stop()
|
||||||
|
|
||||||
|
if period == 0 {
|
||||||
|
// Assume a period of 0 is intended as "no output".
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconfigure period.
|
||||||
|
s.pwm.SetPeriod(period)
|
||||||
|
|
||||||
|
// Make this a square wave by setting the channel position to half the
|
||||||
|
// period.
|
||||||
|
s.pwm.Set(s.ch, s.pwm.Top()/2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNote starts playing the given note. For example, s.SetNote(C4) will
|
||||||
|
// produce a 440Hz square wave tone.
|
||||||
|
func (s Speaker) SetNote(note Note) {
|
||||||
|
period := note.Period()
|
||||||
|
s.SetPeriod(period)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user