16F628A - Multiple frequency divider

General discussion on mikroPascal.
Post Reply
Author
Message
chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

16F628A - Multiple frequency divider

#1 Post by chimimic » 17 Mar 2009 13:14

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

FreeMan
Posts: 38
Joined: 25 Jul 2006 04:32

Other ways, but at which cost?

#2 Post by FreeMan » 17 Mar 2009 19:03

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.

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
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:

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
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.

FreeMan
Posts: 38
Joined: 25 Jul 2006 04:32

Aplogy and update

#3 Post by FreeMan » 17 Mar 2009 19:40

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:

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

chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

#4 Post by chimimic » 17 Mar 2009 21:30

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

FreeMan
Posts: 38
Joined: 25 Jul 2006 04:32

Pointers can be used

#5 Post by FreeMan » 18 Mar 2009 13:20

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.

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.

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

chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

#6 Post by chimimic » 19 Mar 2009 13:56

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.

FreeMan
Posts: 38
Joined: 25 Jul 2006 04:32

Consider using TMR0

#7 Post by FreeMan » 19 Mar 2009 15:37

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:

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.

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:

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.
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

FreeMan
Posts: 38
Joined: 25 Jul 2006 04:32

I think there is ..

#8 Post by FreeMan » 19 Mar 2009 16:04

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

chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

#9 Post by chimimic » 19 Mar 2009 19:20

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 ;-)

FreeMan
Posts: 38
Joined: 25 Jul 2006 04:32

Try TMR0 interrupt

#10 Post by FreeMan » 19 Mar 2009 21:07

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:

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.
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.

chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

#11 Post by chimimic » 19 Mar 2009 22:12

Hi FreeMan,

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.
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).

FreeMan
Posts: 38
Joined: 25 Jul 2006 04:32

i am guilty, my fault.

#12 Post by FreeMan » 20 Mar 2009 20:22

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.

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.
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:

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.
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.

chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

#13 Post by chimimic » 22 Mar 2009 14:01

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".

chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

#14 Post by chimimic » 22 Mar 2009 14:34

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 ;-)

chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

#15 Post by chimimic » 13 Jul 2009 15:29

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

Post Reply

Return to “mikroPascal General”