This portion of my assembly code is supposed to set up Timer/counter1 and toggle the OC1B pin (PB2, the buzzer) at 1500Hz. I am trying to set up the prescaler to divide the CPU clock (20MHz) by 1024 so that the counter increases at 19531.25Hz. Then, I set the OCR1BL register to 13 so that the output compare match occurs at 1500Hz (19531.25/13~=1500). Can someone help me understand if I’ve set up the registers correctly, and how I can start/stop this buzzing in my program at will (I tried to implement this in the start_buzz and stop_buzz subroutines below by setting and clearing the appropriate clock select bits in TCCR1B):
Initialization during reset:
SBI DDRB, 2 ;Set PORTB, Pin 2 as output so that Timer1 can switch it with Output Compare Interrupt
LDI tmp, COM1B0
STS TCCR1A, tmp ;Sets up toggling of OC1B pin during every output compare interrupt (PB2 has to be output)
LDI tmp, (1<<WGM12) ;enables CTC (Clear Timer on Compare match) mode
STS TCCR1B, tmp
LDI lowb, 13 ;Set the low byte for the Timer1 compare match to 13
STS OCR1BL, lowb; (because 19531/13=1500Hz, our buzzer's desired freq)
I also perform a SEI here among other initializations, and then go to sleep. A USART0 Rx interrupt calls the start and stop buzzing routines.
LDI tmp, (5<<CS10);Sets pins 0 and 2 for cpu/1024 timer clock
STS TCCR1B, tmp ;This sets the timer1 clock to cpu/1024 (=20000000/1024=19531.25Hz)
LDS tmp, TCCR1B; ;Load current settings in Timer1 control register B
ANDI tmp, ~(5<<CS10); Clears pins 0 and 2
STS TCCR1B, tmp ;This stops the timer by clearing the CS10:2 bits
Do I need to fiddle with TIMSK1 and set Output Compare B Match Interrupt enable? I thought this would make my program try to start an interrupt which I don’t need as long as the act of matching automatically toggles OC1B; I’m just trying to create a 1500Hz square wave. Wait… if I’m toggling at 1500Hz doesn’t that mean I’m only generating a 750Hz square wave? I’m confused!
Before I look too closely at your register settings, is there a reason why you are trying to do this in assembly rather than C? Also, I don’t think you said what happens when you run your code.
You don’t need to enable interrupts (enabling an interrupt without writing an ISR for that interrupt will actually mess things up). If you toggle at 1500 Hz, then yes, your buzzer frequency will only be 750 Hz. However, I wouldn’t recommend the toggle approach. Rather, I would use the ICR1 register as the TOP value and OCR1B to set the duty cycle (e.g. ICR1 = 13, OCR1B = 6).
I’m actually programming in assembly because I’m taking a course at my school that’s teaching us assembly on the Motorola 68HC12 and I thought I could get better lab experience by learning how to control the peripherals on the ATmega168. I’m really just doing things the hard way for the sake of learning.
After your repy I took a look at the demo projects on the LV-168 page, and I noticed one of them (Project 2) had C source code for setting up the timer to buzz at a certain frequency based on the push of a button. It turns out the C source code also deals closely with the registers on the ATmega168 so I was able to translate what was already working there into code that now finally works in assembly. I only used the toggle OC1B approach because it seemed to be the simplest way for me to acheive the square wave.
At any rate, I noticed that the timer is always running (meaning the prescaler is initialized at the start of the code) and the only thing that turns the buzzing on/off is the state of bit 2 in the DDR register for port B. Also, in my code posted here, I am loading the value for the compare match into the wrong register for the CTC mode - I should have originally written that value to OCR1AL not OCR1BL. The buzzing seems to be working the way I expected it to now.
Thanks for your help
I’m glad you got it working. Setting up a hardware PWM isn’t hard, but there are so many registers involved that I always need to go to the datasheet, and I usually still somehow get something wrong the first time.
It would have been fine for you to turn off the PWM by disabling the timer, but toggling output state of the I/O pin makes a simple solution, too.
If you have any other questions about assembly on the AVR, please ask. I’ve used it a few times in the past for time-critical ISRs, so I should be able to help out some (if it comes back to me!).