Last active
November 27, 2022 00:07
generating tones with Atmega328p's timer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include <avr/interrupt.h> | |
#include <avr/io.h> | |
#include <avr/power.h> | |
// system clock frequency | |
#define F_CPU 16000000UL | |
// global variable to determine the beep duration | |
static volatile uint32_t duration = 0x00; | |
ISR(TIMER0_COMPA_vect) { | |
if (!duration) { | |
// mask the interrupt | |
TIMSK0 &= ~_BV(OCIE0A); | |
// disable clock | |
TCCR0B = 0x00; | |
// set the port state to low, when generation is finished | |
PORTD &= _BV(PORTD6); | |
} | |
else { | |
duration--; | |
} | |
} | |
static void timer_freq_prescale(uint32_t a_freq, uint8_t *a_ocr, uint8_t *a_prescaler) { | |
// prescaler table for timer 0 | |
uint8_t prescalers[] = { 0x00, 0x03, 0x06, 0x08, 0x0a, 0x00 }; | |
uint16_t ocr = 0x00; | |
uint8_t prescaler = 0x00; | |
do { | |
ocr = (uint16_t) (F_CPU / ((a_freq << 1) * (0x01 << prescalers[prescaler]))); | |
++prescaler; | |
} while ((ocr > 255) && (prescalers[prescaler])); | |
--ocr; | |
if (ocr > 255) ocr = 255; | |
*a_ocr = ocr & 0xff; | |
*a_prescaler = prescaler; | |
} | |
void beep(uint16_t a_freq, uint16_t a_duration) { | |
uint8_t prescaler = 0x00, | |
ocr = 0x00; | |
sei(); | |
power_timer0_enable(); | |
// configure the COMP0A pin as output | |
DDRD |= _BV(PORTD6); | |
// calculate the frequency | |
timer_freq_prescale(a_freq, &ocr, &prescaler); | |
// COM0A1/COM0A0 = 0x01 - toggle OC0A on Compare Match, mode = CTC | |
TCCR0A = 0x42; | |
OCR0A = ocr; | |
TCNT0 = 0x00; | |
// configure the prescaler, now the timer is running and the tone is being generated | |
TCCR0B = prescaler & 0x07; | |
// calculate the amount of cycles | |
duration = (uint32_t)((a_freq * a_duration)/500); | |
// unmask the interrupt | |
TIMSK0 |= _BV(OCIE0A); | |
// block until the tone fades out | |
while(duration); | |
} | |
int main(void) | |
{ | |
uint16_t freq = 100; | |
while (1) { | |
for(uint8_t i = 0; i < 64; i++) { | |
// 100 ms beep of increasing frequency | |
beep(freq + (10 * i), 100); | |
} | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TARGET=beeper | |
SOURCES=beeper.c | |
DEPS= | |
COBJ=$(SOURCES:.c=.o) | |
CC=avr-gcc | |
OBJC=avr-objcopy | |
MCU=atmega328p | |
CFLAGS=-I. -Wall -Os -DF_CPU=16000000UL -std=c99 | |
LDFLAGS= | |
ISPPORT=/dev/ttyACM0 | |
ISPDIR=/usr/share/arduino/hardware/tools | |
ISP=$(ISPDIR)/avrdude | |
ISPFLAGS=-c arduino -p $(MCU) -P $(ISPPORT) -b 115200 -C $(ISPDIR)/avrdude.conf | |
all: $(TARGET) | |
%.o: %.c $(DEPS) | |
@echo -e "\tCC" $< | |
@$(CC) -mmcu=$(MCU) -c -o $@ $< $(CFLAGS) | |
$(TARGET): $(COBJ) | |
@echo -e "\tLINKING CC" $< | |
@$(CC) -mmcu=$(MCU) -o $(TARGET) $(COBJ) $(LDFLAGS) | |
$(OBJC) -O ihex -R .eeprom $(TARGET) $(TARGET).hex | |
$(OBJC) -O binary -R .eeprom $(TARGET) $(TARGET).bin | |
clean: | |
$(MAKE) -C $(PCA_PREFIX) clean | |
@echo ========== cleanup ========== | |
rm -rf *.o *.bin *.hex $(TARGET) | |
read: | |
$(ISP) $(ISPFLAGS) -U flash:r:$(TARGET)_backup.hex:i | |
install: | |
$(ISP) $(ISPFLAGS) -U flash:w:$(TARGET).hex |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment