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