Interrut Driven Oscillator


Closed Thread
Results 1 to 6 of 6
  1. #1
    barkerben's Avatar
    barkerben Guest

    Default Interrut Driven Oscillator

    Hi, This program is supposed to use TMR1 interrupts to oscillate at a user defined frequency. The idea is that it will be integrated with a serial program so that the frequency can be set via a PC. I think the following makes sense, from what I have read / been told, but if anyone has a chance t have a quick glance that would be brilliant. Either, have a good New Years!

    Cheers,


    Ben


    '***************************************
    '* Name : OSCILLATOR_1
    '* Author : Ben Barker
    '* Notice : Copyright (c) 2004 Ben Barker
    '* : All Rights Reserved
    '* Date : 31/12/2004
    '* Version : 1.0
    '* PIC in use : 16F876
    '***************************************


    'Control Lines:
    '---------------

    ClockOut var portB.0
    TRISB = TRISB = %00000000 'Set portB to outputs

    'Software Defines:
    '-----------------

    TimerReload VAR WORD 'Reload value for TMR1
    dummy VAR WORD 'dummy variable used to deal with 32 bit operations
    DesiredFreq VAR WORD 'Desired frequency in mHz
    postscaler VAR BYTE 'Postscaler software counter - TMR1 has no hardware postscaler


    'Initialization routine:
    '------------------------

    init:
    ADCON1 = 7 'Disable ADC
    CMCON = 7 'Set PORTA to digital for future use


    postscaler = 0 'This variable is incremented at each timer interrupt, then reset when it reaches 2 to give a 1:2 postscaler

    ''''''''''''''''''''''''''''''''''''''''''''
    'This section sets the output frequency, and works as follows:
    '
    'We have set the postscaler to 1:2 and the postscaler to 1:8
    'This, our TIMER1 frequency with a 4Mhz XT is 4000000/4/8/2 = 62500Hz
    'This means it steps through 62500 steps per second.
    'If we preload the timer so that there are only, for example, 6250 steps
    'before overflowing again, then it will overflow every 0.1 sec - giving a 10Hz
    'output. We define a reload value as the nmber of steps away from
    'overflow we reset the timer to after each interrupt is triggered.
    '
    '62500/reload_value = freq_in_Hz
    '
    'Thus (62500*100)/reload_value = freq_in_mHz
    '
    'Now, TMR1 is a 16bit counter, so has 2^16 = 65536 steps available. TMR1 counts
    'up from zero, so to get our actual reload value we need to calculate:
    '65536-reload_value
    '
    'However, due to variable rollover, 65536-reload_value is equal to 0-reload_value
    '(The sign is neglected, magnitude is the same)
    '''''''''''''''''''''''''''''''''''''''''''''

    DesiredFreq = 1000 'Set desired output frequency to 10Hz (specified in mHz)

    Dummy = 62500
    Dummy = Dummy * 1000
    TMR1Reload = DIV32 DesiredFreq
    TMR1Reload = 0 - TMR1Reload

    ON INTERRUPT GOTO timer

    PIE1.0=1 'Enable TMR1 interrupts
    INTCON.6=1 'Enables Peripheral Interrupts
    INTCON.7=1 'Enable Global interrupts
    T1CKPS1 = 1 'Set up TMR1 prescaler to 1:8 (see data sheet)
    T1CKPS0 = 1


    'Main function:
    '---------------

    main:
    'Loop forever here waiting for interrupts to occur
    goto main

    'Interrupt Service Routine (ISR):
    '---------------------------------

    timer:
    DISABLE 'Disable interrupts during ISR (shouldn't be needed - if it is, program is taking too long anyway)

    postscaler=postscaler+1 'increment postscaler
    if postscaler=2 then 'check new postscaler value, and respond
    postscaler=0
    TOGGLE CLOCKOUT 'toggle clock output pin
    endif

    'This output is not entirely accurate, as the ISR takes finite time. Rather than TMR1
    'overflowing and then immediately sarting from preloaded value, it overflows, starts
    'counting, and is then reloaded. This introduces a small error - could be ironed out
    'by quantifying latency and adjusting reload value accordingly.

    TMR1L = TMR1Reload.lowbyte 'Reload timer with value to give needed frequency
    TMR1H = TMR1Reload.highbyte
    PIR1.0= 0 'Reset interrupt flag

    ENABLE 'Re-enable interrupts

    RESUME 'Return to main program loop

  2. #2
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Hi Ben,

    To over come the Timer Re-load problem. You can ADD the TMR1Reload value to the current TMR1 count, instead of just loading the value and losing the time it took to get there.

    This way the frequency will stay the same. Only the rising and falling edges will vary depending on how long it takes to service the interrupt.

    The best way to ADD the value in is with a little ASM because it's easier to figure out how long it will take.
    Code:
    ASM
        RST?RP                         ; Set to BANK0
        BCF     T1CON,TMR1ON           ; Turn off timer
        MOVF    _TMR1Reload ,W         ;  1
        ADDWF   TMR1L,F                ;  1    ; reload timer with correct value
        BTFSC   STATUS,C               ;  1/2
        INCF    TMR1H,F                ;  1
        MOVF    _TMR1Reload+1,W        ;  1
        ADDWF   TMR1H,F                ;  1
        BSF     T1CON,TMR1ON           ;  1    ; Turn TIMER1 back on
    ENDASM
    This way, you know for sure that it takes 7 cycles to reload the timer. Just add 7 to the TMR1Reload that you already have.
    Code:
    TMR1Reload = 0 - TMR1Reload + 7
    You'll also need to place TMR1Reload in BANK0
    Code:
    TimerReload VAR WORD BANK0  'Reload value for TMR1
    HTH,
       Darrel

  3. #3
    barkerben's Avatar
    barkerben Guest


    Did you find this post helpful? Yes | No

    Default

    Hi - Thanks for that - that was similar to what I thought might be needed. In fact, accurate frequencies are not that important, as long as the rates are consistent. However, since it shouldn't add much to the length of the code it is certainly worth including. As far as I can see the basic structure of the code is ok... The idea is I will combine this with serial driven interrupts so that a PC can control the output frequency etc. Servicing the interrupt routine in time may be tricky, but with 300 baud I hope to be ok!

    I had a couple of questions about the ASM though - sorry If I've missed something obvious - I have used assembler before, but am a little rusty...:

    RST?RP ; Set to BANK0
    Firstly, what is the significance of the '?' - is it a standard ASM

    MOVF _TMR1Reload ,W ; 1

    Also, what is the significance of the _ prefix to the variable names as above in ASM...?

    Finally, what is the significance of using Bank 0

    I'll be back in the lab next week, so will let you know how it goes. Thanks for your help, and for the other people who have helped me out on the forum.


    Cheers,


    Ben

  4. #4
    barkerben's Avatar
    barkerben Guest


    Did you find this post helpful? Yes | No

    Default

    Ah - sorry about that. I see the underscore is needed is asm to refer to basic variables, and that BANK0 needs to be specified because that is the one chosen in the ASM code. Presumably its choice was arbitrary, but once chosen, consistency between the variable declaration and the ASM code was needed.

    However, I am still unsure about these 3 lines of code:


    BTFSC STATUS,C
    INCF TMR1H,F
    MOVF _TMR1Reload+1,W

    Why can we not merely have:

    RST?RP
    BCF T1CON,TMR1ON
    MOVF _TMR1Reload ,W
    ADDWF TMR1L,F
    ADDWF TMR1H,F
    BSF T1CON,TMR1ON


    I have a feeling it must be something to do with the problems writing to a 16 bit timer, but can't quite get it clear. I will probably be very embarassed when I realsie how simple it is, but till then a hint would be brilliant.


    Cheers,


    Ben

  5. #5
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    RST?RP is a Macro that comes with PBP. It's not a standard ASM directive.

    It's pretty much the same thing as this
    Code:
        bcf    STATUS, RP1
        bcf    STATUS, RP0
    PREV_BANK = 0
    Except it optimizes out anything that is not needed. So if BANK1 is currently selected (RP1 = 0, RP0 = 1) then all it will compile to is
    Code:
        bcf    STATUS, RP0
    PREV_BANK = 0
    Saving 1 word of code space and 1 instruction cycle.

    If the current bank was already BANK0, then no code is generated, saving 2 words and 2 instruction cycles. When you are changing banks several hundred times in a program, the savings can be considerable.

    For more info on the "?" character, check out one of my other posts here.
    http://www.picbasic.co.uk/forum/show...d=539#post2009

    The underscore prefixing the variable name is require for any variables that are defined in PBP. PBP adds the underscore to make sure that variable names don't conflict with other symbol names used by either PBP or the ASM compiler (PM or MPASM)

    Placing the variable in BANK0 let's the ASM routine access it without having to figure out what bank it's in first. It would add more cycles to the routine, depending on which bank it was located in. That would change the number of cycles it takes to re-load the timer. Instead of 7, it could be 9 or 11 throwing off the timing.

    For the RS232 part, you shouldn't have any problems. RS232 is very slow. You should be able to run at a much higher baud rate without missing anything. Just be sure to use the Hardware USART with interrupts. SERIN2 timing is software based, and as such will mean that other "ON INTERRUPTS" will not occur untill the SERIN/OUT has completed, many milliseconds later.

    Best regards,
        Darrel

  6. #6
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Ben,

    Maybe a few more comments in the ASM routine will help.
    Code:
    ASM
        RST?RP                         ;        Set to BANK0
        BCF     T1CON,TMR1ON           ;        Turn off timer
        MOVF    _TMR1Reload ,W         ;  1      Get LOW byte of TMR1Reload 
        ADDWF   TMR1L,F                ;  1      add it to LOW byte of TMR1
        BTFSC   STATUS,C               ;  1/2    If addition caused a carry
        INCF    TMR1H,F                ;  1      add carry to HIGH byte of TMR1
        MOVF    _TMR1Reload+1,W        ;  1      Get HIGH byte of TMR1Reload
        ADDWF   TMR1H,F                ;  1      add it to HIGH byte of TMR1
        BSF     T1CON,TMR1ON           ;  1     Turn TIMER1 back on
    ENDASM
    HTH,
        Darrel
    Last edited by Darrel Taylor; - 2nd January 2005 at 00:38.

Similar Threads

  1. PBP and 18F2550
    By Fredrick in forum mel PIC BASIC Pro
    Replies: 16
    Last Post: - 15th January 2012, 06:34
  2. PICBasic newbie problem
    By ELCouz in forum mel PIC BASIC Pro
    Replies: 32
    Last Post: - 12th February 2008, 00:55
  3. Oscillator Troubles
    By CocaColaKid in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 13th December 2007, 17:15
  4. Advice please - Warning message
    By malc-c in forum mel PIC BASIC Pro
    Replies: 50
    Last Post: - 23rd January 2007, 13:20
  5. PIC12F675, accuracy of baud rate with Internal Oscillator
    By Chris Mayhew in forum mel PIC BASIC Pro
    Replies: 3
    Last Post: - 31st August 2005, 22:41

Members who have read this thread : 2

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts