// port of arduino Tone library for swordfish
// (device = 18FxxK22)
module tone
// **WARNING**
// Do NOT connect the pin directly to some sort of audio input... the voltage is considerably
// higher (5V) than standard line level voltages, and can damage sound card inputs, etc.
// If required, use a voltage divider to bring the voltage down
// Also, the output pin cannot directly drive a low-impedance 8/16 ohm speaker.
//
// specify default output pin
#option _TONE_OUTPUT_PIN = PORTC.2
// 88-key piano note frequencies, in Hz
public const NOTE_B0 as word = 31
public const NOTE_C1 as word = 33
public const NOTE_CS1 as word = 35
public const NOTE_D1 as word = 37
public const NOTE_DS1 as word = 39
public const NOTE_E1 as word = 41
public const NOTE_F1 as word = 44
public const NOTE_FS1 as word = 46
public const NOTE_G1 as word = 49
public const NOTE_GS1 as word = 52
public const NOTE_A1 as word = 55
public const NOTE_AS1 as word = 58
public const NOTE_B1 as word = 62
public const NOTE_C2 as word = 65
public const NOTE_CS2 as word = 69
public const NOTE_D2 as word = 73
public const NOTE_DS2 as word = 78
public const NOTE_E2 as word = 82
public const NOTE_F2 as word = 87
public const NOTE_FS2 as word = 93
public const NOTE_G2 as word = 98
public const NOTE_GS2 as word = 104
public const NOTE_A2 as word = 110
public const NOTE_AS2 as word = 117
public const NOTE_B2 as word = 123
public const NOTE_C3 as word = 131
public const NOTE_CS3 as word = 139
public const NOTE_D3 as word = 147
public const NOTE_DS3 as word = 156
public const NOTE_E3 as word = 165
public const NOTE_F3 as word = 175
public const NOTE_FS3 as word = 185
public const NOTE_G3 as word = 196
public const NOTE_GS3 as word = 208
public const NOTE_A3 as word = 220
public const NOTE_AS3 as word = 233
public const NOTE_B3 as word = 247
public const NOTE_C4 as word = 262
public const NOTE_CS4 as word = 277
public const NOTE_D4 as word = 294
public const NOTE_DS4 as word = 311
public const NOTE_E4 as word = 330
public const NOTE_F4 as word = 349
public const NOTE_FS4 as word = 370
public const NOTE_G4 as word = 392
public const NOTE_GS4 as word = 415
public const NOTE_A4 as word = 440
public const NOTE_AS4 as word = 466
public const NOTE_B4 as word = 494
public const NOTE_C5 as word = 523
public const NOTE_CS5 as word = 554
public const NOTE_D5 as word = 587
public const NOTE_DS5 as word = 622
public const NOTE_E5 as word = 659
public const NOTE_F5 as word = 698
public const NOTE_FS5 as word = 740
public const NOTE_G5 as word = 784
public const NOTE_GS5 as word = 831
public const NOTE_A5 as word = 880
public const NOTE_AS5 as word = 932
public const NOTE_B5 as word = 988
public const NOTE_C6 as word = 1047
public const NOTE_CS6 as word = 1109
public const NOTE_D6 as word = 1175
public const NOTE_DS6 as word = 1245
public const NOTE_E6 as word = 1319
public const NOTE_F6 as word = 1397
public const NOTE_FS6 as word = 1480
public const NOTE_G6 as word = 1568
public const NOTE_GS6 as word = 1661
public const NOTE_A6 as word = 1760
public const NOTE_AS6 as word = 1865
public const NOTE_B6 as word = 1976
public const NOTE_C7 as word = 2093
public const NOTE_CS7 as word = 2217
public const NOTE_D7 as word = 2349
public const NOTE_DS7 as word = 2489
public const NOTE_E7 as word = 2637
public const NOTE_F7 as word = 2794
public const NOTE_FS7 as word = 2960
public const NOTE_G7 as word = 3136
public const NOTE_GS7 as word = 3322
public const NOTE_A7 as word = 3520
public const NOTE_AS7 as word = 3729
public const NOTE_B7 as word = 3951
public const NOTE_C8 as word = 4186
public const NOTE_CS8 as word = 4435
public const NOTE_D8 as word = 4699
public const NOTE_DS8 as word = 4978
public const notes() as word =
(
NOTE_B0,
NOTE_C1,
NOTE_CS1,
NOTE_D1,
NOTE_DS1,
NOTE_E1,
NOTE_F1,
NOTE_FS1,
NOTE_G1,
NOTE_GS1,
NOTE_A1,
NOTE_AS1,
NOTE_B1,
NOTE_C2,
NOTE_CS2,
NOTE_D2,
NOTE_DS2,
NOTE_E2,
NOTE_F2,
NOTE_FS2,
NOTE_G2,
NOTE_GS2,
NOTE_A2,
NOTE_AS2,
NOTE_B2,
NOTE_C3,
NOTE_CS3,
NOTE_D3,
NOTE_DS3,
NOTE_E3,
NOTE_F3,
NOTE_FS3,
NOTE_G3,
NOTE_GS3,
NOTE_A3,
NOTE_AS3,
NOTE_B3,
NOTE_C4,
NOTE_CS4,
NOTE_D4,
NOTE_DS4,
NOTE_E4,
NOTE_F4,
NOTE_FS4,
NOTE_G4,
NOTE_GS4,
NOTE_A4,
NOTE_AS4,
NOTE_B4,
NOTE_C5,
NOTE_CS5,
NOTE_D5,
NOTE_DS5,
NOTE_E5,
NOTE_F5,
NOTE_FS5,
NOTE_G5,
NOTE_GS5,
NOTE_A5,
NOTE_AS5,
NOTE_B5,
NOTE_C6,
NOTE_CS6,
NOTE_D6,
NOTE_DS6,
NOTE_E6,
NOTE_F6,
NOTE_FS6,
NOTE_G6,
NOTE_GS6,
NOTE_A6,
NOTE_AS6,
NOTE_B6,
NOTE_C7,
NOTE_CS7,
NOTE_D7,
NOTE_DS7,
NOTE_E7,
NOTE_F7,
NOTE_FS7,
NOTE_G7,
NOTE_GS7,
NOTE_A7,
NOTE_AS7,
NOTE_B7,
NOTE_C8,
NOTE_CS8,
NOTE_D8,
NOTE_DS8
)
// for optional icd debugger _trap() support
#if isoption(MPLAB_ICD) and (MPLAB_ICD)
include "mplabicd.bas"
#option _TONE_TRAP = true
#endif
#option _TONE_TRAP = false
// CCP requires using instruction cycle (FOSC/4)
const FCYC as longword = (_clock * 1000000) / 4
// sound output pin
dim OUTPUT_PIN as _TONE_OUTPUT_PIN._TONE_OUTPUT_PIN@
// ccp1 interrupt bits (18FxxK22)
dim CCP1IF as PIR1.bits(2),
CCP1IE as PIE1.bits(2)
// duration 'timer'
// setting duration = -1 --> play until stop() is called
dim tmr_toggle_count as longint = 0
interrupt ccp1_interrupt()
if (tmr_toggle_count <> 0) then
toggle(OUTPUT_PIN) // toggle pin
if (tmr_toggle_count > 0) then
tmr_toggle_count = tmr_toggle_count - 1
endif
else
CCP1IE = 0 // disable the interrupt
OUTPUT_PIN = 0 // keep pin low after stop
endif
CCP1IF = 0
end interrupt
// load CCP1 compare value
inline sub load_compare(w as word)
CCP1IF = 0
CCPR1H = w.bytes(1)
CCPR1L = w.bytes(0)
CCP1IE = 1
end sub
// initialize TMR1 and CCP1 (18FxxK22)
public sub init(enable_intr as boolean = true)
low(OUTPUT_PIN)
// TMR1 setup
T1CON = (%00 << 6) + // TMR1 clock source is instruction clock (FOSC/4)
(%00 << 4) + // prescaler value 1:1
(%0 << 3) + // sec osc disabled
(%0 << 2) + // ignored
(%0 << 1) + // 8-bit rd/wr
(%0 << 0) // TMR1 OFF
TMR1H = 0
TMR1L = 0
// CCP1 setup
CCP1IF = 0
CCP1IE = 0
// CCP1 (output on pin RC2)
CCP1CON = (%0000 << 4) + // unused
(%1011 << 0) // Compare mode: Special Event Trigger (CCPxIF is set, TMRX is reset)
// assign TMR1 to CCP1 (K22 specific)
CCPTMRS0.bits(1) = 0 // CCP1 timer select bits: 00 = CCP1 – Capture/Compare modes use Timer1
CCPTMRS0.bits(0) = 0
if (enable_intr) then
enable(ccp1_interrupt)
endif
end sub
//
// play a freq with optional duration
// freq in hz, duration in msecs
// if duration is not specified then the tone plays until stop() is called
//
public sub play(freq as word, duration as longint = 0)
dim prescalarbits as byte
dim ocr as longword = 0
// compute compare value for the 16-bit timer assuming prescaler = 1
ocr = FCYC / 2 / freq
prescalarbits = %00 // prescaler = 1
if (ocr > $ffff) then // if we exceed range with prescaler == 1 then
prescalarbits = %11 << 4 // set prescaler = 8
ocr = ocr >> 3 // divide by 8
endif
// compare regs are 16-bit (minus one for decr to 0 wrap)
ocr.words(0) = ocr.words(0) - 1
// set TMR prescaler
T1CON.bits(0) = 0 // stop TMR1
TMR1H = 0
TMR1L = 0
T1CON = (T1CON and %11001111) or prescalarbits
// calculate how long to play the note in terms of the toggle count
if (duration > 0) then
tmr_toggle_count = word(2 * freq) * duration / 1000
else
tmr_toggle_count = -1
endif
// set the output compare regs for the given timer and turn on the interrupts
load_compare(ocr)
T1CON.bits(0) = 1 // turn on TMR1
end sub
// stop playing
public sub stop()
T1CON.bits(0) = 0 // stop TMR1
CCP1IE = 0 // disable intr
OUTPUT_PIN = 0
#if (_TONE_TRAP)
_trap() // halt debugger
#endif
end sub
// check to see if a note is currently playing
// returns true = playing, false = not playing/done
public function isPlaying() as boolean
result = boolean(CCP1IE)
end function
end module