16F628A - Multiple frequency divider
16F628A - Multiple frequency divider
Hi to all,
I started a new project that involve production of 12 frequencies from a single clock.
These 12 frequencies have all a different fixed frequency ratio from the single input clock :
Fout1 = Fin / 239
Fout2 = Fin / 253
Fout3 = Fin / 268
...
Fout12 = Fin / 451
My first approch is to use interrupts on RB port and simply count edge on 12 independants (word) counters, and reset counters when freq ratio is reached.
In your opinion, is this method enough good, or is there a better way (as for exemple directly work with input external clock) ?
Thanks in advance for any help.
Best regards,
Remy
I started a new project that involve production of 12 frequencies from a single clock.
These 12 frequencies have all a different fixed frequency ratio from the single input clock :
Fout1 = Fin / 239
Fout2 = Fin / 253
Fout3 = Fin / 268
...
Fout12 = Fin / 451
My first approch is to use interrupts on RB port and simply count edge on 12 independants (word) counters, and reset counters when freq ratio is reached.
In your opinion, is this method enough good, or is there a better way (as for exemple directly work with input external clock) ?
Thanks in advance for any help.
Best regards,
Remy
Other ways, but at which cost?
Peace be with you,
Ok, i do believe and suggest to you strongly to fellow your lead since it is the simplest way to do this project.
So here i will through some ideas but they are not very attractive since they necessitate a lot of thinking and many neurone to burn, not worth it. i am only sharing those ideas for educational perpuses.
i do suggest to you to fellow your idea since it is simple to modify and direct to maintain in case of errors. There is always a cost to be paied when attemting to take an other road, the 2 questions are: at which cost? and are you willing to pay the price?
Now, since that is said, straight to the point: those ideas are deduced from mathematics and very logical. i will begin by the simplest of them to explane ( i hope my explanation would be simple enough )!.
Those ideas here will only work in this specific case: (Fout11 - Fout0) < Fout0. we notice from the few numbers presented that we are indeed in this case. nice.
Note1: If you are in the case: Fout0 > (Fout11-Fout0) then some adjustment ( instruction should be done) in the interrupt.
Case 1: in this case we assume everything is simple, so we make an assemption ( little normalisation ) that the diffrence between any 2 Fout is fixed.
i mean : Fout2 - Fout1 = Fout3 - Fout2 = Fout4 - Fout3 .... = Fout12 - Fout11 = 14
Simple enough ?, ok now we do this
Fout1 = 239 = 14 * 17 + 1
Fout2 = 253 = 14 * 18 + 1
Fout3 = 267 = 14 * 19 + 1 ( Notice normalisation here )
...
Fout12 = 393 = 14 * 28 + 1 ( Notice normalisation here )
In this specific case the work would be very simple: we use 2 counters, one counts always 0..14 the second 0..29.
Case 2:
Well since the simple parts is done lets do the hardest one, that is at any delta frequency tolerated
dFout0 = Fout2 - Fout1
dFout1 = Fout3 - Fout2
dFout2 = Fout4 - Fout3
....
dFout11= Fout12- Fout11
and all dFouti ( and i=0..11) are diffrent from each other that is:
dFout0 <> dFout1 and dFout1 <> dFout2 and so forth.
Just one unique assemption to simplify the problem is: (Fout11 - Fout0) < Fout0
If we are not in this case further precausion should be considered in the interrupt part.
You have guessed it, we need a table here to store in it all difference between frequencies and we end up by this complicated code:
Those 2 codes are NOT tested, so test them and use them at your own risk.
after running some numbers to try to find out what is are all the frequencies you expect i find out this:
i, d, Fout
1 14 239
2 15 253
3 16 268 i: is the index,
4 17 284 d: is the difference between nighboor frequencies
5 18 301 Fout is Fout of course.
6 19 319
7 20 338
8 21 358
9 22 379
10 23 401
11 24 424
12 25 448
Suggestion, Those last numbers are the numbers you working with AND if you are willing to use this code, so you can use the first case and just use one variable instead of a table. And you will increase it each time you initialise Cnt0 to 0.
If your head is hurting then thats normal, it will go away after a moment.
Peace be with you,
Freeman.
Ok, i do believe and suggest to you strongly to fellow your lead since it is the simplest way to do this project.
So here i will through some ideas but they are not very attractive since they necessitate a lot of thinking and many neurone to burn, not worth it. i am only sharing those ideas for educational perpuses.
i do suggest to you to fellow your idea since it is simple to modify and direct to maintain in case of errors. There is always a cost to be paied when attemting to take an other road, the 2 questions are: at which cost? and are you willing to pay the price?
Now, since that is said, straight to the point: those ideas are deduced from mathematics and very logical. i will begin by the simplest of them to explane ( i hope my explanation would be simple enough )!.
Those ideas here will only work in this specific case: (Fout11 - Fout0) < Fout0. we notice from the few numbers presented that we are indeed in this case. nice.
Note1: If you are in the case: Fout0 > (Fout11-Fout0) then some adjustment ( instruction should be done) in the interrupt.
Case 1: in this case we assume everything is simple, so we make an assemption ( little normalisation ) that the diffrence between any 2 Fout is fixed.
i mean : Fout2 - Fout1 = Fout3 - Fout2 = Fout4 - Fout3 .... = Fout12 - Fout11 = 14
Simple enough ?, ok now we do this
Fout1 = 239 = 14 * 17 + 1
Fout2 = 253 = 14 * 18 + 1
Fout3 = 267 = 14 * 19 + 1 ( Notice normalisation here )
...
Fout12 = 393 = 14 * 28 + 1 ( Notice normalisation here )
In this specific case the work would be very simple: we use 2 counters, one counts always 0..14 the second 0..29.
Code: Select all
Dim
iFout as Byte ' Index which output is selected now
Cnt0, Cnt1 as byte ' 2 counters
sub procedure interrupt
Cnt0 = Cnt0 + 1
If Cnt0 > 14 then
Cnt0 = 0
Cnt1 = Cnt1 + 1
If Cnt1 > 17 then ' 239 = 17 * 14 + 1
' -------- this is depanding on your output pins ' Begin
If iFout < 8 then
PortC.iFout = PortC.iFout Xor 1
Else ' Here Cnt0 is used as
Cnt0 = Fout - 8 ' temporary variable to
PortD.iFout = PortD.Cnt0 Xor 1 ' minimize the code
End If
Cnt0 = 0
' -------- this is depanding on your output pins ' End
iFout = iFout + 1 ' Point to the next one
If iFout > 11 then ' if we are over 451 then
iFout = 0 ' - Point to the first one
Cnt1 = 11 ' - Cnt1 = ( (( 393 -1 )/ 14 ) - 17 )
Cnt0 = 1 ' - Cnt0 = 393 - ( (Cnt1 + 17 ) * 14 )
End If
end If
End If
ClearBit( INTCON, RBIF) ' Clear portB interrupt flag
end sub
Well since the simple parts is done lets do the hardest one, that is at any delta frequency tolerated
dFout0 = Fout2 - Fout1
dFout1 = Fout3 - Fout2
dFout2 = Fout4 - Fout3
....
dFout11= Fout12- Fout11
and all dFouti ( and i=0..11) are diffrent from each other that is:
dFout0 <> dFout1 and dFout1 <> dFout2 and so forth.
Just one unique assemption to simplify the problem is: (Fout11 - Fout0) < Fout0
If we are not in this case further precausion should be considered in the interrupt part.
You have guessed it, we need a table here to store in it all difference between frequencies and we end up by this complicated code:
Code: Select all
Const
dFout as byte[12] = ' Steps between 2 frequencies
(
14, ' Fout1 - Fout0
15 , ' Fout2 - Fout1
..., ' Fout3 - Fout2
.... '
... ' Fout11 - Fout10
)
Dim
iFout as Byte ' Index which output is selected now
Cnt0, Cnt1 as byte ' 2 counters
sub procedure interrupt
Cnt0 = Cnt0 + 1
If Cnt0 > dFout[iFout] then
Cnt0 = 0
Cnt1 = Cnt1 + 1
If Cnt1 > 17 then ' 239 = 17 * 14 + 1
If iFout < 8 then
PortC.iFout = PortC.iFout Xor 1
Else ' Here Cnt0 is used as
Cnt0 = Fout - 8 ' temporary variable to
PortD.iFout = PortD.Cnt0 Xor 1 ' minimize the code
End If
Cnt0 = 0
iFout = iFout + 1 ' Point to the next one
If iFout > 11 then ' if we are over 451 then
iFout = 0 ' - Point to the first one
Cnt1 = 15 ' - Cnt1 = (( 451 / dFout[0] ) - 17 )
Cnt0 = 3 ' - Cnt0 = 451 - ( (Cnt1 + 17 ) * dFout[0] )
End If
end If
End If
ClearBit( INTCON, RBIF) ' Clear portB interrupt flag
end sub
after running some numbers to try to find out what is are all the frequencies you expect i find out this:
i, d, Fout
1 14 239
2 15 253
3 16 268 i: is the index,
4 17 284 d: is the difference between nighboor frequencies
5 18 301 Fout is Fout of course.
6 19 319
7 20 338
8 21 358
9 22 379
10 23 401
11 24 424
12 25 448
Suggestion, Those last numbers are the numbers you working with AND if you are willing to use this code, so you can use the first case and just use one variable instead of a table. And you will increase it each time you initialise Cnt0 to 0.
If your head is hurting then thats normal, it will go away after a moment.
Peace be with you,
Freeman.
Aplogy and update
Peace be with you,
It is me again, i think i was absorbed by some mathematical storm giving such a hard time to your post. i do apologise for my last post, it was foolish.
as i said before you way of doing it is the best one and you do it very straight forwad like this:
Small, simple and elegent solution, i dont think there is better then this one.
Note: after the first time iFout = 12, Cnt0 will always be upper 211.
Peace be with you,
freeman
It is me again, i think i was absorbed by some mathematical storm giving such a hard time to your post. i do apologise for my last post, it was foolish.
as i said before you way of doing it is the best one and you do it very straight forwad like this:
Code: Select all
Const
Fout as Word[12] =
( 239,
253,
...
451
)
Dim
iFout as Byte
Counter as Word
sub procedure interrupt
Cnt0 = Cnt0 + 1
If Cnt0 > Fout[iFout] then
' -------- this is depanding on your output pins ' Begin
If iFout < 8 then
PortC.iFout = PortC.iFout Xor 1
Else ' Here Cnt0 is used as
Cnt0 = Fout - 8 ' temporary variable to
PortD.iFout = PortD.Cnt0 Xor 1 ' minimize the code
End If
Cnt0 = 0
' -------- this is depanding on your output pins ' End
iFout = iFout + 1 ' Point to the next freq
If iFout > 11 Then
iFout = 0 ' Point to the first freq
Cnt0 = 212 ' 451 - 239
end If
End If
ClearBit( INTCON, RBIF) ' Clear portB interrupt flag
End Sub
Small, simple and elegent solution, i dont think there is better then this one.
Note: after the first time iFout = 12, Cnt0 will always be upper 211.
Peace be with you,
freeman
Hi Freeman,
a big thank for these suggestions, very appreciated.
I'll test them and report here results of tests.
To be complete on my side, the goal of this new project is to generate the 12 "base" musical notes for an electronique instrument, that are followed by binary counters for successives dividing by 2, to get lower octaves.
I have not a mathematical brain and searched a simple solution. But I think I understood yours ;-)
Sincerely yours,
Remy
a big thank for these suggestions, very appreciated.
I'll test them and report here results of tests.
To be complete on my side, the goal of this new project is to generate the 12 "base" musical notes for an electronique instrument, that are followed by binary counters for successives dividing by 2, to get lower octaves.
I have not a mathematical brain and searched a simple solution. But I think I understood yours ;-)
Sincerely yours,
Remy
Pointers can be used
Peace be with you dear Remy,
i am glad you understood it, just worried maybe it made u waist of your time. anyway, it is done now, lets see some ideas.
so if my understanding is correct you dont want to have always the output enabled, therefore i taugh about having a simple table ( boolean table ) to store in it if we want the output to be enabled or disabled.
Further more, i think by using pointers your code can be faster, i didnt tested it but i think so.
after this, i beleive a lot of work have been done and you got a lot to test then.
If the output pins are not ordered, you can always use this (inside the interrupt )
Assuming:
Fout0..3 = PortC.4..7
Fout4..5 = PortC.0..1
Fout6..7 = PortC.2..3
and so forth
then use select case and separate each case alone.
Peace be with you Remy,
freeman
i am glad you understood it, just worried maybe it made u waist of your time. anyway, it is done now, lets see some ideas.
so if my understanding is correct you dont want to have always the output enabled, therefore i taugh about having a simple table ( boolean table ) to store in it if we want the output to be enabled or disabled.
Further more, i think by using pointers your code can be faster, i didnt tested it but i think so.
Code: Select all
Const
Fout as Word[12] =
( 239,
253,
...
451
)
Dim
iFout as Byte
Counter as Word
Dim
Ptr_PortX as ^Byte ' Points to the Port
Prt, ' Port selection
Pin as Byte ' Pin Selection
PortX as Byte[2] ' Store Ports adresses
EnaFout as Byte[12] ' This is boolean just to say if we
' allow or not allow that frequency output
sub procedure interrupt
If TestBit( INTCON, RBIF) Then ' PortB interrupt
Cnt0 = Cnt0 + 1
If Cnt0 > Fout[iFout] then
' -------- this is depanding on your output pins ' Begin
Cnt0 = 0
Prt = iFout >> 3 ' under 7 ( PortC ) or over 7 ( PortD ) ?
Pin = iFout And $7 ' filter only 0..7
Ptr_PortX = PortX[Prt] ' Get the PortAdress
Ptr_PortX^.Pin = (Ptr_PortX^.Pin Xor 1) And EnaFout[iFout] ' Frequency out
' -------- this is depanding on your output pins ' End
iFout = iFout + 1 ' Point to the next freq
If iFout > 11 Then
iFout = 0 ' Point to the first freq
Cnt0 = 212 ' 451 - 239
end If
End If
ClearBit( INTCON, RBIF) ' Clear portB interrupt flag
End IF
End Sub
main:
.... ' your configuration here
PortX[0] = @PortC ' We use PortC.0 To PortC.7
PortX[1] = @PortD ' We use PortD.0 To PortD.3
... ' your code here.
end.
If the output pins are not ordered, you can always use this (inside the interrupt )
Assuming:
Fout0..3 = PortC.4..7
Fout4..5 = PortC.0..1
Fout6..7 = PortC.2..3
and so forth
then use select case and separate each case alone.
Peace be with you Remy,
freeman
Hi Freeman,
first tests made with my first approch, based on the following schematic :
http://www.sonelec-musique.com/images2/ ... 3_base.gif
and the following code :
http://www.sonelec-musique.com/logiciel ... 16f628.zip
Works well for low input frequency (some KHz), but don't work at all for the frequency I have to use (470 KHz), output frequencies are all lower than expected. A little better if PIC is cadenced at 20 MHz rater than 4 MHz, but it's clearly not the better solution.
As it's a musical instrument working on several octaves, generator will be always enabled and each notes activated independantly, after counters / dividers.
I'm at the moment trying your method.
first tests made with my first approch, based on the following schematic :
http://www.sonelec-musique.com/images2/ ... 3_base.gif
and the following code :
http://www.sonelec-musique.com/logiciel ... 16f628.zip
Works well for low input frequency (some KHz), but don't work at all for the frequency I have to use (470 KHz), output frequencies are all lower than expected. A little better if PIC is cadenced at 20 MHz rater than 4 MHz, but it's clearly not the better solution.
As it's a musical instrument working on several octaves, generator will be always enabled and each notes activated independantly, after counters / dividers.
I'm at the moment trying your method.
Consider using TMR0
eace be with you dear Remy,
ofcourse you can not achieve higher frequency with this code, obviously because the PIC is working with the Fosc/4. In case Quarts of 4MHz u have Fosc/4 = 1MHz and for 20 MHz you get Fosc/4 = 5Mhz.
But still, if you write an interrupt which hase for instance 20 instructions ( example ) then you this interruption can only achieve: (Fosc/4) / 20 ( rafly, not exact calculation ). For 4Mhz we get 50Khz, this are NOT exact calculation but just to have certain qualitative view on what is happening inside the PIC.
( in really we can not be sure about the number of micro-instructions inside an interrupt since we write in basic/C/pascal and the compiler will transfert it to hex, and do not forget the pop/push register to be saved/read)!!!
Thats why this code is not ment to work with high frequency.
Well in your case of generating the octave notes, i would suggest to change completly your aproach by using internal timer0 interrupt and then, i guess you can move the big code inside the main look insteed of the interrupt, and leave the interrupt very small, just increasing the counter.
Timer0 interrupt you have to calibrate it to your needs, and if it is not tuned enough, so then you can use Timer1 since it is 16bits times, which allows wider range of calibration for the timing interupt.
so my suggestion would be as fellow:
this method would be better to generate the tones you would like. just one thing, since i am not familliar with the tones so i dont knew which frequencies you are looking for exactly, but i suspect they are audible which would be lower then 20Khz.
Note: you have to generate the maximum frequency tone from the Tmr0 interrupt then it would be possible to get the other frequencies from it.
Note2: do NOT even think to get over 100Khz from a pic, to work with such frequencies you would consider to work with more bigger and sofisticated pics like Pic24!! When working with PIC16 i believe 20Khz is very resonable, of course you wont have 20.000Hz exactly, you will always have some hertz more or less, then you have to modify: Tmr0Init value so then you change the frequency till you are satisfied.
To consider also:
http://www.mikroe.com/forum/viewtopic.p ... ight=sound
http://www.mikroe.com/forum/viewtopic.p ... =soundinit
Just to experimenting perpuses, you can write a small program like the one from the help file:
and then just change the Freq value and see what will happen, if you have the correct values of the sound you looking for.
Peace be with you Remy,
freeman
ofcourse you can not achieve higher frequency with this code, obviously because the PIC is working with the Fosc/4. In case Quarts of 4MHz u have Fosc/4 = 1MHz and for 20 MHz you get Fosc/4 = 5Mhz.
But still, if you write an interrupt which hase for instance 20 instructions ( example ) then you this interruption can only achieve: (Fosc/4) / 20 ( rafly, not exact calculation ). For 4Mhz we get 50Khz, this are NOT exact calculation but just to have certain qualitative view on what is happening inside the PIC.
( in really we can not be sure about the number of micro-instructions inside an interrupt since we write in basic/C/pascal and the compiler will transfert it to hex, and do not forget the pop/push register to be saved/read)!!!
Thats why this code is not ment to work with high frequency.
Well in your case of generating the octave notes, i would suggest to change completly your aproach by using internal timer0 interrupt and then, i guess you can move the big code inside the main look insteed of the interrupt, and leave the interrupt very small, just increasing the counter.
Timer0 interrupt you have to calibrate it to your needs, and if it is not tuned enough, so then you can use Timer1 since it is 16bits times, which allows wider range of calibration for the timing interupt.
so my suggestion would be as fellow:
Code: Select all
Const
Fout as Word[12] =
( 239,
253,
...
451
)
TmrInit as Byte = 00 ' this is to calibrate Tmr0 Interrupt timing=frequency
Dim
iFout as Byte
Cnt0 as Word
Dim
Ptr_PortX as ^Byte ' Points to the Port
Prt, ' Port selection
Pin as Byte ' Pin Selection
PortX as Byte[2] ' Store Ports adresses
EnaFout as Byte[12] ' This is boolean just to say if we
' allow or not allow that frequency output
sub procedure interrupt
If TestBit( INTCON, T0IF) Then ' PortB interrupt
Cnt0 = Cnt0 + 1
Tmr0 = TmrInit
ClearBit( INTCON, RBIF) ' Clear portB interrupt flag
End IF
End Sub
main:
.... ' your configuration/ Initialisation here
PortX[0] = @PortC ' We use PortC.0 To PortC.7
PortX[1] = @PortD ' We use PortD.0 To PortD.3
Cnt0 = 0
iFout = 0
... ' your code here.
While True ' ---------------------- Main Loop
... ' your code here.
If Cnt0 > Fout[iFout] then
' -------- this is depanding on your output pins ' Begin
Cnt0 = 0
Prt = iFout >> 3 ' under 7 ( PortC ) or over 7 ( PortD ) ?
Pin = iFout And $7 ' filter only 0..7
Ptr_PortX = PortX[Prt] ' Get the PortAdress
Ptr_PortX^.Pin = (Ptr_PortX^.Pin Xor 1) And EnaFout[iFout] ' Frequency out
' -------- this is depanding on your output pins ' End
iFout = iFout + 1 ' Point to the next freq
If iFout > 11 Then
iFout = 0 ' Point to the first freq
Cnt0 = 212 ' 451 - 239
end If
End If
Wend ' ---------------------- Main Loop
end.
Note: you have to generate the maximum frequency tone from the Tmr0 interrupt then it would be possible to get the other frequencies from it.
Note2: do NOT even think to get over 100Khz from a pic, to work with such frequencies you would consider to work with more bigger and sofisticated pics like Pic24!! When working with PIC16 i believe 20Khz is very resonable, of course you wont have 20.000Hz exactly, you will always have some hertz more or less, then you have to modify: Tmr0Init value so then you change the frequency till you are satisfied.
To consider also:
http://www.mikroe.com/forum/viewtopic.p ... ight=sound
http://www.mikroe.com/forum/viewtopic.p ... =soundinit
Just to experimenting perpuses, you can write a small program like the one from the help file:
Code: Select all
program Sound_test
Const Freq as integer = 300
main:
CMcon = 7
PORTB = 0 ' Clear PORTB
TRISB = 0 ' PORTB is output
INTCON = 0 ' Disable all interrupts
' ADCON1 = $82 ' Configure VDD as Vref, and analog channels
TRISA = $FF ' PORTA is input
Sound_Init(PORTB, 2) ' Initialize sound at RB2
while true ' Play in loop:
Sound_Play(freq, 200) ' Play the sound
wend
end.
Peace be with you Remy,
freeman
I think there is ..
Peace be with you,
Dear chimimic, i think there is a little mistake in your schematic.
The basic use of Interrupt in PortB is by using PB0, i mean PortB.0, and in the interrupt you do NOT have to check if this pin in high or low. since the interrupt will reviel it.
anyway, i will give you a better answer when i finish working today, after 5pm.
peace be with you,
freeman
Dear chimimic, i think there is a little mistake in your schematic.
The basic use of Interrupt in PortB is by using PB0, i mean PortB.0, and in the interrupt you do NOT have to check if this pin in high or low. since the interrupt will reviel it.
anyway, i will give you a better answer when i finish working today, after 5pm.
peace be with you,
freeman
thanks again for all these details !
For interrupts generated by RB4..7, yes, I know it and didn't read logical state of these pins, all is done in interrupt procedure.
At the start of this project, I wanted use int 4 MHz osc or use ext quartz, but I didn't get needed reference frequency of 471,68 KHz, regardless configuration of TMR0 or TMR1 preload counters values. It's why I tried to work on this manner.
Well, I have no choice and have to try the others ways ;-)
For interrupts generated by RB4..7, yes, I know it and didn't read logical state of these pins, all is done in interrupt procedure.
At the start of this project, I wanted use int 4 MHz osc or use ext quartz, but I didn't get needed reference frequency of 471,68 KHz, regardless configuration of TMR0 or TMR1 preload counters values. It's why I tried to work on this manner.
Well, I have no choice and have to try the others ways ;-)
Try TMR0 interrupt
Peace be with you dear chimimic,
Ok now we have a better chat.
The portB you are using is not completly false, and not completly right, since it is generated when ever one of the pins 4..7 of the portb are changed! so, to use RB4..7 interrupt those pins must be input and not output, also we can not use them for other perpuses!!!
since you have only one input the best suggestion for this case is to use RB0 and also in the interrupt you do not have to verify the state of this pin. i do not have the same pic as the one you have, also i do not use Pascal so i will write some little code to do some testing for you but it is for you to try it and make modification on it.
Also, to achieve higher frequency we need to make the interrupt as small as possible, very compact!!!
The best aproch in programming is by beginig from a little working program then begin adding parts step by step until we achieve what we want. ok then here it is:
Now, after testing your first aproach and then do what you want, if you still didnt get what you want, then try this aproach, using TMR0 and no pin input at all
So please forgive my Pascal, i dont have pascal compiler so you would have to try it and correct everything in it.
i believe that this code maybe better then the first aproach of using port B interrupt.
Now you have to do the last steps alone, since i dont have pascal either your pic, or material to assit you.
for now you would have to change the values of T0Init till you get the frequencies you wants.
Some pointer:
- if you really want to use RB0, have you considered using lower frequency input and recalculate your divisions??
- When using the program i gave you above sonsider 2 things:
+ T0Init can only be: 00..200. 00 ( low freq ), 200 (high freq) , do NOT put higher then 200 the PIC will bug and mis-behave!!
+ If after using all values you didn't get what you wanted, so try to change the numbers you have for Fout!!!, recalculate them till you get what you want.
- Last point, you can also add a little thing inside the interrupt like:
PORTB.7 := PortB.7 Xor 1; // just for mesuring freq.
And with this you can knew what is the frequency inside the interrupt, then from there you can calculate all other frequencies .
Peace be with you, chimimic,
freeman.
Ok now we have a better chat.
The portB you are using is not completly false, and not completly right, since it is generated when ever one of the pins 4..7 of the portb are changed! so, to use RB4..7 interrupt those pins must be input and not output, also we can not use them for other perpuses!!!
since you have only one input the best suggestion for this case is to use RB0 and also in the interrupt you do not have to verify the state of this pin. i do not have the same pic as the one you have, also i do not use Pascal so i will write some little code to do some testing for you but it is for you to try it and make modification on it.
Also, to achieve higher frequency we need to make the interrupt as small as possible, very compact!!!
The best aproch in programming is by beginig from a little working program then begin adding parts step by step until we achieve what we want. ok then here it is:
Code: Select all
const
Fout : Word[12] =
(
239, 253, 268, 284, 301, 319,
338, 358, 379, 402, 426, 451
) ;
var
// PortX : Byte[2]; // Store Port Adress
PPtr : ^Byte; // Port Pointer
Counter : Word; // One counter for everything!!!
iFout, // freq Index
Pin : Byte // Pin Selection
// EnaFout : Byte[12] // This is boolean just to say if we
procedure interrupt;
begin
// No need to test if this interrupt is generated or not
// since you have ONLY one interupt here in this program.
// Therefore if we are here it means RB0 interrupt is calling.
// if TestBit(INTCON, INTE) then // Interrupt in RB0
// begin
Inc(Counter)
ClearBit(INTCON, INTE); // Clear RB0 Flag
// End;
End;
procedure Prog_Init;
begin
// comparators off
CMCON := 7;
OPTION_REG.NOT_RBPU := 1; //disable pullup
OPTION_REG.INTEDG := 1; // int on rising edge of RB0 !!!!
// interrupt enabled for RB0 and RB4..7 pins (clock in on RB7)
INTCON := %10010000;
// 76543210
// ||||||||
// |||||||+--0: RBIF: RB4..7 Port Change
// ||||||+---1: INTF: RB0/INT Flag
// |||||+----2: T0IF: TMR0 Overflow
// ||||+-----3: RBIE: RB Port 4..7
// |||+------4: INTE: RB0/INT enable
// ||+-------5: T0IE: timer0 interrupt
// |+--------6: PEIE: Perifiral interrupt enable
// +---------7: GIE: 1 Enable
T1CON := 0;// .T1OSCEN := 0; T1CON.TMR1ON := 0;
TRISA := %00000000; // PortA as output
TRISB := %10000000; // PortB as output, but RB7 as input (for ref clock)
PORTA := $00; // clear portA
PORTB := $00; // clear portB
// only used for "welcome" indication
PortA := $FF;
PortB := $FF;
Delay_ms(10);
PortA := $00;
PortB := $00;
// PortX[0] := @PortA ; // if this not working try: ^PortA
// PortX[1] := @PortB ; // if this not working try: ^PortB
iFout := 0 ; // Initialize freq index.
Counter := 0 ; // Initialize counter to Zero.
end;
begin
Prog_Init;
while true do
begin
If Counter > Fout[iFout] then
Begin
If iFout<5 Then
Begin
PPtr := @PortA; // if this not work, try: ^PortB
Pin := iFout; // pin 0 to 4
end;
Else
begin
PPtr := @PortB; // if this not work, try: ^PortB
Pin := iFout - 4; // pin 1 to 7
end;
PPtr^.Pin = PPtr^.Pin Xor 1;
Inc(iFout);
If iFout>11 Then
Begin
iFout := 0;
Counter := 212;
end;
End;
end;
end.
Now, after testing your first aproach and then do what you want, if you still didnt get what you want, then try this aproach, using TMR0 and no pin input at all
Code: Select all
const
Fout : Word[12] =
(
239, 253, 268, 284, 301, 319,
338, 358, 379, 402, 426, 451
) ;
T0Init : Byte = $80;
var
// PortX : Byte[2]; // Store Port Adress
PPtr : ^Byte; // Port Pointer
Counter : Word; // One counter for everything!!!
iFout, // freq Index
Pin : Byte // Pin Selection
// EnaFout : Byte[12] // This is boolean just to say if we
procedure interrupt;
begin
Inc(Counter);
TMR0 := T0Init;
ClearBit(INTCON, T0IF);
End;
procedure Prog_Init;
begin
// comparators off
CMCON := 7;
// OPTION_REG.NOT_RBPU := 1; //disable pullup
// OPTION_REG.INTEDG := 1; // int on rising edge of RB0 !!!!
// interrupt enabled for RB0 and RB4..7 pins (clock in on RB7)
OPTION_REG = $0F; // only T0CS=0, PSA=1 are needed.
INTCON := %10100000;
// 76543210
// ||||||||
// |||||||+--0: RBIF: RB4..7 Port Change
// ||||||+---1: INTF: RB0/INT Flag
// |||||+----2: T0IF: TMR0 Overflow
// ||||+-----3: RBIE: RB Port 4..7
// |||+------4: INTE: RB0/INT enable
// ||+-------5: T0IE: timer0 interrupt
// |+--------6: PEIE: Perifiral interrupt enable
// +---------7: GIE: 1 Enable
T1CON := 0;// .T1OSCEN := 0; T1CON.TMR1ON := 0;
TRISA := %00000000; // PortA as output
TRISB := %10000000; // PortB as output, but RB7 as input (for ref clock)
PORTA := $00; // clear portA
PORTB := $00; // clear portB
// only used for "welcome" indication
PortA := $FF;
PortB := $FF;
Delay_ms(10);
PortA := $00;
PortB := $00;
// PortX[0] := @PortA ; // if this not working try: ^PortA
// PortX[1] := @PortB ; // if this not working try: ^PortB
iFout := 0 ; // Initialize freq index.
Counter := 0 ; // Initialize counter to Zero.
end;
begin
Prog_Init;
while true do
begin
If Counter > Fout[iFout] then
Begin
If iFout<5 Then
Begin
PPtr := @PortA; // if this not work, try: ^PortB
Pin := iFout; // pin 0 to 4
end;
Else
begin
PPtr := @PortB; // if this not work, try: ^PortB
Pin := iFout - 4; // pin 1 to 7
end;
PPtr^.Pin = PPtr^.Pin Xor 1;
Inc(iFout);
If iFout>11 Then
Begin
iFout := 0;
Counter := 212;
end;
End;
end;
end.
i believe that this code maybe better then the first aproach of using port B interrupt.
Now you have to do the last steps alone, since i dont have pascal either your pic, or material to assit you.
for now you would have to change the values of T0Init till you get the frequencies you wants.
Some pointer:
- if you really want to use RB0, have you considered using lower frequency input and recalculate your divisions??
- When using the program i gave you above sonsider 2 things:
+ T0Init can only be: 00..200. 00 ( low freq ), 200 (high freq) , do NOT put higher then 200 the PIC will bug and mis-behave!!
+ If after using all values you didn't get what you wanted, so try to change the numbers you have for Fout!!!, recalculate them till you get what you want.
- Last point, you can also add a little thing inside the interrupt like:
PORTB.7 := PortB.7 Xor 1; // just for mesuring freq.
And with this you can knew what is the frequency inside the interrupt, then from there you can calculate all other frequencies .
Peace be with you, chimimic,
freeman.
Hi FreeMan,
just finished to try your last code without TMR0 :
All outputs generate a signal at the same frequency (for example 226 ms period with Fin = 4 KHz), but they start all one after the other, either ClockIn (on RB0) is 4 KHz or more. I'll try to detect what is wrong.
(actually, tests are made under Proteus / Isis, not with my EasyPic board).
just finished to try your last code without TMR0 :
Code: Select all
program electronique_orgue_003_16f628;
var
iCount: word; // one counter for all freq needed
iFout: byte; // freq index
PPtr: ^byte; // Port Pointer
Pin: byte; // Pin Selection
const
Fout: Array[12] of word =
(239, 253, 268, 284, 301, 319, 338, 358, 379, 402, 426, 451);
{
cSi = 239; cLaD = 253; cLa = 268; cSolD = 284; cSol = 301; cFaD = 319;
cFa = 338; cMi = 358; cReD = 379; cRe = 402; cDoD = 426; cDo = 451;
}
procedure Prog_Init;
begin
// comparators off
CMCON := 7;
OPTION_REG.NOT_RBPU := 1; // disable pullup
OPTION_REG.INTEDG := 1; // int on rising edge of RB0 ;-)
T1CON := 0;
TRISA := %00000000; // PortA as output
TRISB := %00000001; // PortB as output, but RB0 as input (for ref clock)
PORTA := $00; // clear portA
PORTB := $00; // clear portB
// interrupt enabled for RB0 pin (clock input on RB0)
INTCON.GIE := 1;
INTCON.PEIE := 0;
INTCON.INTE := 1;
INTCON.INTF := 0;
iFout := 0;
iCount := 0;
end;
procedure interrupt;
begin
inc(iCount);
ClearBit(INTCON, INTF);
end;
begin
{ Main program }
Prog_Init;
while true do
begin
if iCount > Fout[iFout] then
begin
// select Port A pin...
if iFout < 5 then
begin
PPtr := @PortA;
Pin := iFout; // pin 0 to 4
end
else
// ... or select Port B pin
begin
PPtr := @PortB; // if this not work, try: ^PortB
Pin := iFout - 4; // pin 1 to 7
end;
// change state of "selected" pin
PPtr^.Pin := PPtr^.Pin Xor 1;
// prepare for the next freq index
Inc(iFout);
// reset
if iFout > 11 then
begin
iFout := 0;
iCount := 212;
end;
end;
end;
end.
(actually, tests are made under Proteus / Isis, not with my EasyPic board).
i am guilty, my fault.
Peace be with you dear chimimic,
i pled guity, it is my fault, my mathematical model was wrong, i apologise for that.
The point is: i tryed to find a commun Counter to the frequencies like this the the focus will be on using only one counter instead of 12, therefore reduce the processing time = reduce the interrupt = increase the frequency input at RB0.
Since i coudn't find a comment ground till now, i believe using 12 counter will be the solution for the moment, till maybe in the future someone can find the idea of using only one counter instead of 12.
Suggestions: if you are not satisfied by the result i suggest to you for example to redure the frequency input to maybe 20Khz or less, and recalculte the frequencies Fout! this way you may get what you want.
Again, this program is NOT ment to detect intrrupts over 50KHZ (this number is not exact, it is just a guess), so, the safiest zone is lower frequencies!!!
Note, according to my understanding, if your target is to use, 470KHz, and the output frequency is (Fout(0)) = 470/239 = 1.966KHz. normalisation => Fout(0) = 2Khz. and this is the highest frequency output you have, so why dont you use a singal of 20KHz as input and use a divider of 10 which makes fout(0) = 20 / 10 = 2!!. Does this make sence to you?
Now what will fellow are alternative ideas.
Suggestion 1: i got a better way to produce what you want to achieve, also your input signal can go as high as 20MHz. the idea is very simple, if you want just to produce those notes/sounds and nothing else, no other processing, consider this:
do not use cristal as an input frequency for the PIC, try to use your own input to the Osc input pin 16 ( RA7/OSC1/CLKIN) and of course the program would be modified for this perpuses, and you will not need any interrupt. the program is the mainly the same as before just the whole program is in the main program like this:
In this case you can go as far as 20MHz input in PIN 16 of the pic16f628.
Suggestion 2: you can use the program in suggestion one and use a cristal of 20MHz and just try it to see/mesure the frequencies output in each pin. Like this you can at list knew what are the limits frequency that the pic can produce simultanuously/sametime.
You got enough ideas to test for this week end and i think you got a lot of things to try and test.
PS: those program presented here are not tested since i dont have pascal and also i dont use pascal, so please forgive me if i made any mistake in them.
Peace be with you,
freeman.
i pled guity, it is my fault, my mathematical model was wrong, i apologise for that.
The point is: i tryed to find a commun Counter to the frequencies like this the the focus will be on using only one counter instead of 12, therefore reduce the processing time = reduce the interrupt = increase the frequency input at RB0.
Since i coudn't find a comment ground till now, i believe using 12 counter will be the solution for the moment, till maybe in the future someone can find the idea of using only one counter instead of 12.
Code: Select all
program electronique_orgue_004_16f628;
var
// iCount: word; // one counter for all freq needed
iFout: byte; // freq index
PPtr: ^byte; // Port Pointer
Pin: byte; // Pin Selection
Fcnt: Array[12] of Word; // counter for each frequency
const
Fout: Array[12] of word =
(239, 253, 268, 284, 301, 319, 338, 358, 379, 402, 426, 451);
{
cSi = 239; cLaD = 253; cLa = 268; cSolD = 284; cSol = 301; cFaD = 319;
cFa = 338; cMi = 358; cReD = 379; cRe = 402; cDoD = 426; cDo = 451;
}
procedure Prog_Init;
begin
// comparators off
CMCON := 7;
OPTION_REG.NOT_RBPU := 1; // disable pullup
OPTION_REG.INTEDG := 1; // int on rising edge of RB0 ;-)
T1CON := 0;
TRISA := %00000000; // PortA as output
TRISB := %00000001; // PortB as output, but RB0 as input (for ref clock)
PORTA := $00; // clear portA
PORTB := $00; // clear portB
// interrupt enabled for RB0 pin (clock input on RB0)
INTCON.GIE := 1;
INTCON.PEIE := 0;
INTCON.INTE := 1;
INTCON.INTF := 0;
// iFout := 0;
// iCount := 0;
end;
procedure interrupt;
begin
inc(Fcnt[0]); // increase each frequency counter alone
inc(Fcnt[1]);
inc(Fcnt[2]); // This is of course will increase the time of been inside
inc(Fcnt[3]); // this interrupt, which means the input signal frequency
inc(Fcnt[4]); // would be very very small.
inc(Fcnt[5]);
inc(Fcnt[6]);
inc(Fcnt[7]);
inc(Fcnt[8]);
inc(Fcnt[9]);
inc(Fcnt[10]);
inc(Fcnt[11]);
ClearBit(INTCON, INTF);
end;
begin
{ Main program }
Prog_Init;
while true do
begin
For iFout := 0 to 11 Do
Begin
If Fcnt[iFout] > Fout[iFout] then
Begin
Fcnt[iFout] := Fcnt[iFout] - Fout[iFout]; // Keep always the counter under Fout.
// select Port A pin...
if iFout < 5 then
begin
PPtr := @PortA;
Pin := iFout; // pin 0 to 4
end
else
// ... or select Port B pin
begin
PPtr := @PortB; // if this not work, try: ^PortB
Pin := iFout - 4; // pin 1 to 7
end;
// change state of "selected" pin
PPtr^.Pin := PPtr^.Pin Xor 1;
End;
End;
end;
end.
Again, this program is NOT ment to detect intrrupts over 50KHZ (this number is not exact, it is just a guess), so, the safiest zone is lower frequencies!!!
Note, according to my understanding, if your target is to use, 470KHz, and the output frequency is (Fout(0)) = 470/239 = 1.966KHz. normalisation => Fout(0) = 2Khz. and this is the highest frequency output you have, so why dont you use a singal of 20KHz as input and use a divider of 10 which makes fout(0) = 20 / 10 = 2!!. Does this make sence to you?
Now what will fellow are alternative ideas.
Suggestion 1: i got a better way to produce what you want to achieve, also your input signal can go as high as 20MHz. the idea is very simple, if you want just to produce those notes/sounds and nothing else, no other processing, consider this:
do not use cristal as an input frequency for the PIC, try to use your own input to the Osc input pin 16 ( RA7/OSC1/CLKIN) and of course the program would be modified for this perpuses, and you will not need any interrupt. the program is the mainly the same as before just the whole program is in the main program like this:
Code: Select all
begin
{ Main program }
Prog_Init;
INTCON := 00 // just as confirmation of no use of interrupt.
while true do
begin
inc(Fcnt[0]); // increase each frequency counter alone
inc(Fcnt[1]);
inc(Fcnt[2]); // This is of course will increase the time of been inside
inc(Fcnt[3]); // this interrupt, which means the input signal frequency
inc(Fcnt[4]); // would be very very small.
inc(Fcnt[5]);
inc(Fcnt[6]);
inc(Fcnt[7]);
inc(Fcnt[8]);
inc(Fcnt[9]);
inc(Fcnt[10]);
inc(Fcnt[11]);
For iFout := 0 to 11 Do
Begin
If Fcnt[iFout] > Fout[iFout] then
Begin
Fcnt[iFout] := Fcnt[iFout] - Fout[iFout]; // Keep always the counter under Fout.
// select Port A pin...
if iFout < 5 then
begin
PPtr := @PortA;
Pin := iFout; // pin 0 to 4
end
else
// ... or select Port B pin
begin
PPtr := @PortB; // if this not work, try: ^PortB
Pin := iFout - 4; // pin 1 to 7
end;
// change state of "selected" pin
PPtr^.Pin := PPtr^.Pin Xor 1;
End;
End;
end;
end.
Suggestion 2: you can use the program in suggestion one and use a cristal of 20MHz and just try it to see/mesure the frequencies output in each pin. Like this you can at list knew what are the limits frequency that the pic can produce simultanuously/sametime.
You got enough ideas to test for this week end and i think you got a lot of things to try and test.
PS: those program presented here are not tested since i dont have pascal and also i dont use pascal, so please forgive me if i made any mistake in them.
Peace be with you,
freeman.
Thanks fot these new ideas, Freeman.
No problem for mistake done on unique counter, I keep this idea under hand as I think it is usable, may by counting difference between two count step.
Using "high" 470K freq to directly drive TMR1 counter was a solution I thought, as I told on my first post (I used this method for my freq meter 004, that can work until 20 MHz). But in fact, I wasn't on the good way because I wanted use interrupt caused by TMR1 overflow, and final Fout timing were not good.
Using 470K ref clock to drive PIC via OSC input pin is perhaps the better solution, I'll try it.
About fixed divider : the 12 values (239, ..., 451) are the best choice to get a similar shift from one note to the other. If we try to reduce them (by a factor of 2 or more), the notes are not well "adjusted".
No problem for mistake done on unique counter, I keep this idea under hand as I think it is usable, may by counting difference between two count step.
Using "high" 470K freq to directly drive TMR1 counter was a solution I thought, as I told on my first post (I used this method for my freq meter 004, that can work until 20 MHz). But in fact, I wasn't on the good way because I wanted use interrupt caused by TMR1 overflow, and final Fout timing were not good.
Using 470K ref clock to drive PIC via OSC input pin is perhaps the better solution, I'll try it.
About fixed divider : the 12 values (239, ..., 451) are the best choice to get a similar shift from one note to the other. If we try to reduce them (by a factor of 2 or more), the notes are not well "adjusted".
Well, I made test with ext osc, interrupt disabled, and I absollutly can't get desired output frequencies.
Even if I drive PIC with 20 MHz clock and even if I only change state of one pin (RB0 in my case), max freq in main routine can't go upper 200 KHz. If I include code with counters and counting procs, main freq down to some KHz.
Seems to be not so easy, perhaps impossible ;-)
Even if I drive PIC with 20 MHz clock and even if I only change state of one pin (RB0 in my case), max freq in main routine can't go upper 200 KHz. If I include code with counters and counting procs, main freq down to some KHz.
Seems to be not so easy, perhaps impossible ;-)
Hello,
For this project, I decided to not work anymore with 16F628A, and transposed code in 18F2420, cadenced with internal osc (with 4x PLL). I have to say it's my first project with a PIC of the 18F familly, and I have a lot of things to learn...
Results are better, even if I can't get what I want. At the moment, I can play with this basic generator, each 12 notes are correctly played but at low frequency, that don't allow me to get large frequency division for lower octaves.
I have now to try to optimise code to allow output freq higher, but even if I can't get this, I already have a little functionnal instrument
;-)
Regards,
Remy
For this project, I decided to not work anymore with 16F628A, and transposed code in 18F2420, cadenced with internal osc (with 4x PLL). I have to say it's my first project with a PIC of the 18F familly, and I have a lot of things to learn...
Results are better, even if I can't get what I want. At the moment, I can play with this basic generator, each 12 notes are correctly played but at low frequency, that don't allow me to get large frequency division for lower octaves.
I have now to try to optimise code to allow output freq higher, but even if I can't get this, I already have a little functionnal instrument
;-)
Regards,
Remy