From: Pat Thoyts Date: Thu, 20 Aug 2015 14:10:03 +0000 (+0100) Subject: Demonstration echoing serial chars and briefly flashing an LED with PWM. X-Git-Url: https://privyetmir.co.uk/gitweb.cgi?a=commitdiff_plain;h=91e03252189c1137a4647e647a4aac367fb886ea;p=avr%2Ftimer-serial.git Demonstration echoing serial chars and briefly flashing an LED with PWM. This demo flashes an LED with a specific number of PWM pulses in response to a serial character input to output a constant small amount of light. Signed-off-by: Pat Thoyts --- 91e03252189c1137a4647e647a4aac367fb886ea diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6c2e45 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.elf +*.hex diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..75fbc86 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +# -*- Makefile -*- + +PROJECT := timer-serial +DEVICE := atmega328p +F_CPU := 16000000UL +INC := -I. +AVRDUDE := avrdude -c usbasp -p $(DEVICE) -C $(AVR_DIR)\etc\avrdude.conf + +CSRCS = $(PROJECT).c #usart.c +OBJS = $(CSRCS:.c=.o) + +CC := avr-gcc +LD := avr-gcc +OBJCOPY := avr-objcopy +RM := del >NUL +CFLAGS :=-Wall -Wstrict-prototypes -Wmissing-prototypes -Wcast-align -Wshadow \ + -std=gnu99 -fshort-enums -pedantic-errors -Os -mcall-prologues \ + -mmcu=$(DEVICE) -DF_CPU=$(F_CPU) +LDFLAGS := -Wall -mmcu=$(DEVICE)# -Wl,-v +LIBS := + +V := @ +Q := $(V:1=) +QUIET_CC = $(Q:@=@echo CC $@ &)$(CC) +QUIET_LD = $(Q:@=@echo LD $@ &)$(LD) +QUIET_OBJCOPY = $(Q:@=@echo OBJCOPY $@ &)$(OBJCOPY) +QUIET_AVRDUDE = $(Q:@=@echo AVRDUDE $@ &)$(AVRDUDE) + +all: $(PROJECT).hex + +%.hex: %.elf + $(QUIET_OBJCOPY) -j .text -j .data -O ihex $< $@ + +%.elf: $(OBJS) + $(QUIET_LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +%.o: %.c + $(QUIET_CC) $(CFLAGS) $(INC) -c $< + +flash: $(PROJECT).hex + $(QUIET_AVRDUDE) -U flash:w:$<:i + +clean: + -@$(RM) $(addprefix $(PROJECT), .elf .hex) + +.PHONY: clean +.SECONDARY: $(addsuffix .elf, $(PROJECT)) $(OBJS) diff --git a/timer-serial.c b/timer-serial.c new file mode 100644 index 0000000..c164450 --- /dev/null +++ b/timer-serial.c @@ -0,0 +1,118 @@ +/* timer-serial.c - Copyright (c) 2015 Pat Thoyts + * + * Controlled PWM signalling in response to serial input. + * When a serial character is recieved it is echoed back and + * 8 PWM pulses at 15kHz are output on pin 5 (PD5). + */ + +#include +#include +#include +#include +#include "usart.h" +#include "usart.c" + +static void init_pwm_pin5(void); +inline void pwm_off(void); +inline void pwm_on(void); +inline int pwm_is_enabled(void); + +/* The overflow interrupt can be used to count PWM pulses if necessary */ +volatile uint32_t pwm_cycles = 0; + +inline void pwm_off(void) +{ + PORTD &= ~_BV(PB5); + TCCR0A &= ~(_BV(COM0B1) | _BV(COM0B0)); +} +inline void pwm_on(void) +{ + cli(); + pwm_cycles = 0; + TCCR0A |= _BV(COM0B1) | _BV(COM0B0); + TCNT0 = 0; + sei(); +} +inline int pwm_is_enabled(void) +{ + return bit_is_set(TCCR0A, COM0B1); +} + +ISR(TIMER0_OVF_vect) +{ + if (pwm_is_enabled()) + { + if (++pwm_cycles == 8) + { + /* toggle output from normal to OCR pin (off or pwm) */ + pwm_off(); + } + } +} + +static void init_pwm_pin5(void) +{ + /* pin 5 (PD5, OC0B) is attached to timer0 (8 bit) + * fast pwm counts from BOTTOM to OCR0A when WGM2:0 = 7. + */ + cli(); + DDRD |= _BV(DDD5); /* set the pin PD5 (OC0B) as output. */ + PORTD &= ~(_BV(PD5)); + /* 15.9: select clock prescaler: /8 yields around 15kHz */ + TCCR0B &= ~(_BV(CS02) | _BV(CS01) | _BV(CS00)); + TCCR0B |= _BV(CS01); + /* 16.11.1 (table 16-4): select waveform mode: WGM2:0=7 is fast pwm */ + TCCR0A |= _BV(WGM01) | _BV(WGM00); + TCCR0B |= _BV(WGM02); + /* 16.11.1: select match mode: set OC0B on match, clear at bottom */ + pwm_on(); + /* set duty cycle: 0 is off, 255 is full on */ + OCR0B = 0x41; + OCR0A = 0x82; + /* 15.9.6: enable overflow interrupt to count PWM cycles */ + TIMSK0 |= _BV(TOIE0); + sei(); +} + +int +main (void) +{ + int n; + static const char version[] = "Timer Serial Demo v1.0\r\n"; + + /* turn off all unused peripherals */ + power_adc_disable(); + power_spi_disable(); + power_twi_disable(); + + /* set all unused pins to high-Z state */ + DDRB = 0; PORTB = 0xff; + DDRC = 0; PORTC = 0xff; + DDRD = 0; PORTD = 0xff; + + /* use PB5 (pin 13) as serial receive indicator and trigger */ + DDRB |= _BV(DDB5); PORTB &= ~_BV(PB5); + + /* enable serial and output a greeting */ + usart_init(9600); + for (n = 0; n < sizeof(version)-1; ++n) + usart_putchar(version[n]); + + init_pwm_pin5(); + _delay_ms(1); + pwm_on(); + + for (;;) + { + if (usart_is_rx_ready()) + { + char c = 0; + PORTB ^= _BV(PB5); + pwm_on(); + c = usart_getchar(); + usart_putchar(c); + PORTB ^= _BV(PB5); + } + } + return 0; +} diff --git a/usart.c b/usart.c new file mode 100644 index 0000000..0da03ba --- /dev/null +++ b/usart.c @@ -0,0 +1,36 @@ +#include +#include "usart.h" + +void usart_init(int baud) +{ + uint16_t ubrr = F_CPU / 16 / baud - 1; + UBRR0H = (uint8_t)(ubrr >> 8); /* write before UBRR0L */ + UBRR0L = (uint8_t)ubrr; + UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* enable RX and TX */ + UCSR0C = _BV(USBS0) | _BV(UCSZ01) | _BV(UCSZ00); /* async, 8,1,n */ +} + +/* nonzero if serial data is available to read. */ +uint8_t usart_is_rx_ready(void) +{ + return bit_is_set(UCSR0A, RXC0); +} + +/* nonzero if transmit register is ready to receive new data. */ +uint8_t usart_is_tx_ready(void) +{ + return bit_is_set(UCSR0A, UDRE0); +} + +int usart_getchar(void) +{ + loop_until_bit_is_set(UCSR0A, RXC0); /* wait for rx ready */ + return UDR0; +} + +int usart_putchar(char c) +{ + loop_until_bit_is_set(UCSR0A, UDRE0); /* wait until data register empty */ + UDR0 = c; + return 0; +} diff --git a/usart.h b/usart.h new file mode 100644 index 0000000..fcbd5b5 --- /dev/null +++ b/usart.h @@ -0,0 +1,12 @@ +#ifndef _usart_h_INCLUDE +#define _usart_h_INCLUDE + +#include + +void usart_init(int baud); +uint8_t usart_is_rx_ready(void); +uint8_t usart_is_tx_ready(void); +int usart_getchar(void); +int usart_putchar(char c); + +#endif /* _usart_h_INCLUDE */ \ No newline at end of file