Demonstration echoing serial chars and briefly flashing an LED with PWM.
authorPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 20 Aug 2015 14:10:03 +0000 (15:10 +0100)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 20 Aug 2015 14:10:03 +0000 (15:10 +0100)
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 <patthoyts@users.sourceforge.net>
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
timer-serial.c [new file with mode: 0644]
usart.c [new file with mode: 0644]
usart.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d6c2e45
--- /dev/null
@@ -0,0 +1,2 @@
+*.elf
+*.hex
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..c164450
--- /dev/null
@@ -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 <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/power.h>
+#include <util/delay.h>
+#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 (file)
index 0000000..0da03ba
--- /dev/null
+++ b/usart.c
@@ -0,0 +1,36 @@
+#include <avr/io.h>
+#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 (file)
index 0000000..fcbd5b5
--- /dev/null
+++ b/usart.h
@@ -0,0 +1,12 @@
+#ifndef _usart_h_INCLUDE
+#define _usart_h_INCLUDE
+
+#include <stdint.h>
+
+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