Help with a simple assembly program that uses timer interrupts? (atmega328p)

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Edit: This post is updated with the solution code :)

 

Hello everyone :). I am trying to create a program that outputs a >50% duty cycle using timers. I am using 100 and 239.

 

.ORG 0x0
    JMP MAIN
.ORG 0x1C
    JMP TSR1
.ORG 0x1E
    JMP TSR2
MAIN:

    //Initializing the stack
    LDI R20,HIGH(RAMEND)
    OUT SPH,R20
    LDI R20,LOW(RAMEND)
    OUT SPL,R20

    //Initializing stack is done

    SBI DDRB,5                                  ;Setting PB5 as output

    LDI R20,239                                 ;Loading R20 with 239
    OUT OCR0A,R20                               ;Loading Timer0 with 239 

    LDI R21,100                                 ;Loading R21 with 100
    OUT OCR0B,R21                               ;Loading Timer1 with 100

    LDI R20,(1<<WGM01)                          ;Setting timer mode to CTC
    OUT TCCR0A,R20                              

    LDI R20,0x01                                ;Start Timer0
    OUT TCCR0B,R20                              ;

    LDI R20,(1<<OCIE0A)    | (1<<OCIE0B)        ;Setting the ISR vector
    STS TIMSK0,R20                              ;Enable Timer0 compare match interrupt

    SEI                                         ;Enable interrupts

HERE:

    JMP HERE

TSR1:
    CBI PORTB,5
    RETI

TSR2:

    SBI PORTB,5
    RETI

 

I am having trouble writing this program. Specially the main program. Can someone help me correct the main program? And please be kind enough to explain that :) I apologize if this question is dumb and does not suit the level of this forum :( I couldn't find similar assembly programs online.

 

Thank you:)

This topic has a solution.
Last Edited: Mon. Nov 4, 2019 - 01:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well the first thing that jumps out at me:

    SBI DDRB,5                                ;Setting PB5 as output

then..

TSR1:
    CBI PORTD,5
    RETI

TSR2:
    SBI PORTD,5
    RETI

So you set port B as an output but then drive port D to show some activity?

 

Either DDR is wrong or the CBI/SBI are operating on the wrong port.

 

But what has motivated you to write this code anyway? Is this a school assignment?

 

And if you are counting 0..100 then setting the bit one way then 100..239 before it is set the other way how is that "50 % duty"?

 

If you really want a 50% duty output to a pin the time can do all that without any ISRs or anything like that (but you don't get to choose which pin - the one for output is fixed for a particular timer channel).

 

EDIT: now I check the datasheet for 328P I see that for timer 0 OC0A is on PD6 and OC0B is on PD5 so I guess it is PD5 you meant to use? But in that case why bother with OCR0A at all and why bother with interrupts? In about 3 lines you can set the timer to just pulse PD5 at 50% with no further interaction.

Last Edited: Mon. Sep 16, 2019 - 03:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the reply kind sir. I really apologize for my mistake. I meant to put PORTB in both TSR1 and TSR2.

 

Yes this is a school homework but not compulsory. I found this slides online  http://nicerland.com/avr/ . I tested the code in page 15 and observed a 50% duty cycle in the oscilloscope. So now objective is to increase the on time (Duty cycle to 60% 70%).

 

About the 100 and 239,

 

If I set one timer interrupt at 100, When It overflows the output would be high.

When the 239 counter overflows output at PB5 would be low.

And like this the on time would be bigger than off time. (239-100>100). I apologize if this logic is stupid :( 

 

So I am guessing this logic can be used to achieve above mentioned objective?

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


You appear to have missed the point of PWM. It's true there's a thing you can do called "soft PWM" where you use two interrupts from a timer to set then reset an output at chosen places in a cycle - that allows you to do PWM on any pin. But usually to do PWM it is much, much simpler if you are willing to accept the chips choice of output pin (as I say for timer 0 and channel B on a 328 that pin is PD5).

 

So you can forget all the ISR stuff. If the plan here is for the pin to go on at 100 and off at 239 (so it's on for 41% of the time and off for 59%) then you just need a mode where you can set the timer to count a complete cycle from 0..239 then tell it to switch state when it reaches 100 within that. So it will be on for 100 and off for 139 (or vice versa).

 

To give you the ability to control that upper limit of 239 you want a mode where "TOP" is adjustable. As it looks like you are happy to use the B output only then you can use a mode where A sets "TOP" (that is period/frequency).

 

So I'd suggest using WGM mode 7 from:

 

 

That is "Fast PWM" with TOP set by OCR0A. So you just set the WGM00 and WGM01 bits in TCCR0A and the WGM02 bit in TCCR0B. Set OCRA to 256 to set the top (actually I think that is supposed to read OCR0A in the datasheet!) and now you have a counter doing PWM from 0..239 (240 steps). Then you use OCR0B to set the "switchover point" - so 100. And finally you need to say how it should treat the OC0B pin:

 

 

If you want the pin to be low for the first 100 then go high for the next 139 use the 11 pattern, or if you want it the "other way up" with it high for the first 100 then low for the last 139 use the 10 combination.

 

Having done all this you don't need the ISRs, you don't need to set bits in TIMSK0 and you don't need to SEI. The timer will do all this "pin wiggling" with no further interaction once the timer starts. About the only thing you might want to do is vary the 100:139 ratio at some point by simply changing OCR0B from 100 to some other value.

Last Edited: Mon. Sep 16, 2019 - 03:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you want 50% on a random pin of your choice, set up an IRQ at twice the desired freq.  Upon each IRQ, just toggle the desired pin & you'll get 50% duty (at reasonable frequencies).

If you can use one of the preassigned timer pins, then just use the timer itself (no IRQ & no active code).

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for all the replies. Means a lot and every time I learn a lot from your posts as I am a beginner.

 

Is it possible to change the original code I posted to complete the required objective? Thank you again for the very informative replies.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

esavrperson wrote:
s it possible to change the original code I posted to complete the required objective?
You mean to simply do PWM on PB5 ?

.def R20=temp

.ORG 0x0
    JMP MAIN

MAIN:

    //Initializing the stack

    LDI R20,HIGH(RAMEND)
    OUT SPH,R20
    LDI R20,LOW(RAMEND)
    OUT SPL,R20

    //Initializing stack is done

    SBI DDRB,5                                ;Setting PB5 (OC0B) as output

    LDI temp, 239                             ;Set total counting range,
    OUT OCR0A, R20                            ;that is period / (frequency) 

    LDI temp, 100                             ;Set Duty ratio 100:239
    OUT OCR0B, temp

    ;Set timer mode to Mode 7 (Fast PWM, OCR0A=TOP) and OC0B to high then low
    LDI temp, (1 << WGM01) | (1 << WGM00) | (1 << COM0B1)
    OUT TCCR0A, temp

    LDI temp, (1 << CS00) | (1 << WGM02)      ;Start Timer0 (and completete
    OUT TCCR0B, temp                          ;setting Mode 7)

    ; PWM now started on OC0B, nothing more required...
loop:
    RJMP loop
    

Or something like that.

Last Edited: Tue. Sep 17, 2019 - 08:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you very much sir :) I had a misunderstanding about what TCCR0A and TCCR0B do. Now it's all good. Thank you very much again for the replies :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello again I thought to update this thread without creating a new one :) Now I fully realize how to use external interrupts and timer interrupts in ctc mode. I am trying to build a line follower car. I understand that these two interrupts can be used to create the line following part. But I'm having issues assuming how the whole code would look like, Could you please help me with supplying a skeleton code to achieve the above? 

 

Thanks for any help :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a completely different, new question - so start a new thread, with a title that accurately reflects the new question.

 

And see Tip #5 to mark this thread as resolved.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...