mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +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
|
||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
|
||||
@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
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
|
||||
@@ -177,7 +179,7 @@ endif
|
||||
DRIVERS = $(wildcard */)
|
||||
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
||||
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))
|
||||
|
||||
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