Hi,
So, I've played around some more.....
It won't run at anything below 16MHz. Even at 16MHz the interrupt routine, executing at 10kHz cosumes 70% of the available CPU cycles.
I've only tried it on an 18F25K20 @64MHz, you try it on a 16F or 12F1840 and see if it works.
You can not cut'n'paste this into a program using DT-Ints. Try it stand-alone first and we'll see about DT-ints later - if needed.
/Henrik.
Code:
'****************************************************************
'* Name : 3xSound generator.pbp *
'* Author : Henrik Olsson *
'* Notice : Written by Henrik Olsson 2015 *
'* : Free software, use as you wish. *
'* Date : 2015-01-14 *
'* Version : 1.0 *
'* Notes : Written in response to a thread on the PBP forum: *
'* : www.picbasic.co.uk/forum/showthread.php?t=19684 *
'* *
'* : Tones are generated using the DDS tecnique with a *
'* simple 16bit accumulator to which a value gets *
'* added once every interrupt (100us). When the *
'* accumulator overflows the output is toggled. *
'* Three generators are incorporated since that was *
'* the request. More could be added if the PIC is *
'* clocked fast enough. With three generators 16MHz *
'* is the lowest you want to use. The interrupts *
'* then uses ~70% of the available CPU cycles when *
'* three tones are generated. *
'* *
'* Version history: *
'* v1.0 2015-01-14 - Initial version *
'* *
'****************************************************************
' CONFIG's, TRIS's, ANSEL's CMCON's and anything device specific is not shown
DEFINE OSC 64
DEFINE INTHAND Generate ' We're treating the interrupt as an ASM routine (see notes!)
'-------------------------------------------------------------------------------------------
' These variables and alises are needed by the sound generator and needs to be
' accessed by the user.
Channel_1 VAR PortC.0 ' Output for sound channel 1
Channel_2 VAR PortC.1 ' Output for sound channel 2
Channel_3 VAR PortC.2 ' Output for sound channel 3
TMR2IF VAR PIR1.1 ' Alias to TMR2 Interrupt Request Flag - verify against datasheet.
Output_1 VAR WORD ' Desired output frequency, Channel 1, 13.107 units per Hz
Output_2 VAR WORD ' Desired output frequency, Channel 2, 13.107 units per Hz
Output_3 VAR WORD ' Desired output frequency, Channel 3, 13.107 units per Hz
Duration_1 VAR WORD ' Duration of tone, Channel 1, 100us units.
Duration_2 VAR WORD ' Duration of tone, Channel 2, 100us units.
Duration_3 VAR WORD ' Duration of tone, Channel 3, 100us units.
'-------------------------------------------------------------------------------------------
' These are variables used by the interrupt service routine.
' No user servicable parts inside......
R0_SHADOW VAR WORD ' Variable used for saving PBP system variables in the ISR
R1_SHADOW VAR WORD ' Variable used for saving PBP system variables in the ISR
R4_SHADOW VAR WORD ' Variable used for saving PBP system variables in the ISR
Accumulator_1 VAR WORD ' DDS accumulator for sound generator 1
Accumulator_2 VAR WORD ' DDS accumulator for sound generator 2
Accumulator_3 VAR WORD ' DDS accumulator for sound generator 3
old_1 VAR WORD ' Used to detect overflow of accumulator 1
old_2 VAR WORD ' Used to detect overflow of accumulator 2
old_3 VAR WORD ' Used to detect overflow of accumulator 3
'--------------------------------------------------------------------------------------------
' We want 10kHz interrupt rate, that's 100us between interrupts.
' The interrupt routine needs around ~280 instruction cycles when
' outputting on three channels.
'
' At 16MHz that would be 400 instructions - 70% used by the generator.
' At 20MHz that would be 500 instructions - 56% used by the generator
' At 40MHz that would be 1000 instructions - 28% used by the generator
' At 64MHz that would be 1600 instructions - 17.5% used by the generator
'
' T2CON and PR2 needs to be setup so that an interrupt is triggered every 100us
' Use one of the follwing:
' PR2 = 99 : T2CON = %00000110 ' For 64MHz - 1:16 prescaler
' PR2 = 49 : T2CON = %00000110 ' For 32MHz - 1:16 prescaler
' PR2 = 124 : T2CON = %00000101 ' For 20MHz - 1:4 prescaler
' PR2 = 99 : T2CON = %00000101 ' For 16MHz - 1:4 prescaler
PR2 = 99 : T2CON = %00000110 'TMR2 on, postscaler 1:1, prescaler 1:4
IPR1.1 = 1 ' TMR2 high priority
PIE1.1 = 1 ' TMR2 IE
INTCON.6 = 1 ' PIE
INTCON.7 = 1 ' GIE
Main:
' All three notes will play simultanously for the specified duration.
' The sound is generated in the background so the PAUSE 1000 will
' execute at the same time as the notes are played. Remember though that
' the interrupt "steals" time so the PAUSE isn't correct. The slower the
' PIC clock is the more inacurate it gets.
Output_1 = 857 : Duration_1 = 500 'Play C2 (65.4Hz) for 50ms
Output_2 = 5767 : Duration_2 = 2500 'Play A4 (440.0Hz)for 250ms
Output_3 = 25893 : Duration_3 = 1000 'Play B6 (1975.5Hz) for 100ms
Pause 1000
Goto Main
'-------------------------------------------------------------------------------
' Actual interrupt routine here. Please note that this is written in PBP but
' the interrupt is handled as if it were a ASM interrupt. As far as I can see
' it uses PBP system variables R0, R1, R4 so I'm saving/restoring those.
' If ANY code gets added here then the ASM-listning needs to be checked to
' see of the added PBP code uses any more system variables and if so
' those needs to saved/restored as well.
' When all three channels produces output it needs ~280 instructions cycles.
@ Generate:
@ MOVE?WW R0, _R0_SHADOW ; Save PBP system variable R0
@ MOVE?WW R1, _R1_SHADOW ; Save PBP system variable R1
@ MOVE?WW R4, _R4_SHADOW ; Save PBP system variable R4
TMR2IF = 0
IF Duration_1 > 0 THEN
Accumulator_1 = Accumulator_1 + Output_1
If Accumulator_1 < old_1 THEN Toggle Channel_1
old_1 = Accumulator_1
Duration_1 = Duration_1 - 1
ELSE
Channel_1 = 0
ENDIF
IF Duration_2 > 0 THEN
Accumulator_2 = Accumulator_2 + Output_2
If Accumulator_2 < old_2 THEN Toggle Channel_2
old_2 = Accumulator_2
Duration_2 = Duration_2 - 1
ELSE
Channel_2 = 0
ENDIF
If Duration_3 > 0 THEN
Accumulator_3 = Accumulator_3 + Output_3
If Accumulator_3 < old_3 THEN Toggle Channel_3
old_3 = Accumulator_3
Duration_3 = Duration_3 - 1
ELSE
Channel_3 = 0
ENDIF
@ MOVE?WW _R0_SHADOW, R0 ; Restore PBP system variable R0
@ MOVE?WW _R1_SHADOW, R1 ; Restore PBP system variable R1
@ MOVE?WW _R4_SHADOW, R4 ; Restore PBP system variable R4
@ retfie FAST
'-------------------------------------------------------------------------------
END
Bookmarks