From 804820fe687affa38a52e302179e4a2293cb1ffb Mon Sep 17 00:00:00 2001 From: root Date: Mon, 8 Dec 2014 18:39:07 +0000 Subject: fish --- Makefile | 41 ++++ blinky.c | 99 +++++++++ lib_ws2812.c | 168 +++++++++++++++ lib_ws2812.h | 64 ++++++ project.h | 28 +++ prototypes.h | 35 +++ stdio.c | 24 +++ uart.c | 686 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ uart.h | 183 ++++++++++++++++ util.c | 13 ++ 10 files changed, 1341 insertions(+) create mode 100644 Makefile create mode 100644 blinky.c create mode 100644 lib_ws2812.c create mode 100644 lib_ws2812.h create mode 100644 project.h create mode 100644 prototypes.h create mode 100644 stdio.c create mode 100644 uart.c create mode 100644 uart.h create mode 100644 util.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..767a9a8 --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +CSRCS= lib_ws2812.c blinky.c util.c uart.c stdio.c +SSRCS= +CC=avr-gcc -mmcu=atmega328p +PROJECT=blinky +HEX=${PROJECT}.hex + +AVRDUDE=avrdude + +CPPFLAGS= +CPP=${CC} -E + +CFLAGS=-O2 -MP -MD -Wall -Werror -Wno-unused + +OBJS=${CSRCS:%.c=%.o} ${SSRCS:%.S=%.o} + +default: ${HEX} + +clean: + +${PROJECT}:${OBJS} + ${CC} -o $@ ${OBJS} + +${HEX}: ${PROJECT} + avr-objcopy -O ihex -R .eeprom $< $@ + +program: ${HEX} + ${AVRDUDE} -F -V -c usbasp -p ATMEGA328P -P usb -U flash:w:$< + #${AVRDUDE} -F -V -c avrispmkII -p ATMEGA328P -P usb -U flash:w:$< + + +protos: + echo -n > prototypes.h + cproto -E "${CPP}" ${CPPFLAGS} -DPROTOTYPING ${CSRCS} > prototypes.h.tmp + mv prototypes.h.tmp prototypes.h + +clean: + /bin/rm -f ${PROJECT} ${HEX} ${OBJS} *~ *.d + + +-include ${CSRCS:%.c=%.d} + diff --git a/blinky.c b/blinky.c new file mode 100644 index 0000000..4b92d4c --- /dev/null +++ b/blinky.c @@ -0,0 +1,99 @@ +/* +* Light_WS2812 library example - RGB_blinky +* +* cycles one LED through red, green, blue +* +* This example is configured for a ATtiny85 with PLL clock fuse set and +* the WS2812 string connected to PB4. +*/ + +#include "project.h" + + +#define N_COLORS 288 +#define N_LEDS 24 + + +struct RGB color[N_COLORS]; +struct RGB led[N_LEDS]; + + +static int +map (int j, int n) +{ + j = (255UL * j) / n; + if (j > 255) + j = 255; + + return j; +} + +int +ramp (int j, int n) +{ + + if ((j >= -n) && (j < 0)) + return map (j + n, n); + if ((j >= 0) && (j <= n)) + return map (n - j, n); + + return 0; +} + + +static void +setup_colors (struct RGB *colors, int n) +{ + int i; + + int n3 = n / 3; + + for (i = 0; i < n; ++i) + { + colors[i].r = ramp (((i) % n) - n3, n3); + colors[i].g = ramp (((i + n3) % n) - n3, n3); + colors[i].b = ramp (((i + n3 + n3) % n) - n3, n3); + + printf ("%3d: %3d %3d %3d\n", i, colors[i].r, colors[i].g, colors[i].b); + + } +} + + + + +int +main (void) +{ + int i, j, k; + + setup_clocks (); + stdio_init (); + + + setup_colors (color, N_COLORS); + + + i = 0; + + while (1) + { + + for (j = 0; j < N_LEDS; ++j) + { + k = (j * (N_COLORS - 1)) / (N_LEDS - 1); + k += i; + while (k >= N_COLORS) + k -= N_COLORS; + + led[j] = color[k]; + } + + ws2812_setleds (led, N_LEDS); + _delay_ms (5); // wait for 500ms. + + + i++; + i %= N_COLORS; + } +} diff --git a/lib_ws2812.c b/lib_ws2812.c new file mode 100644 index 0000000..7f29425 --- /dev/null +++ b/lib_ws2812.c @@ -0,0 +1,168 @@ +/* +* light weight WS2812 lib V2.0b +* +* Controls WS2811/WS2812/WS2812B RGB-LEDs +* Author: Tim (cpldcpu@gmail.com) +* +* Jan 18th, 2014 v2.0b Initial Version +* +* License: GNU GPL v2 (see License.txt) +*/ + +#include "project.h" + +void inline +ws2812_setleds (struct RGB *ledarray, uint16_t leds) +{ + ws2812_setleds_pin (ledarray, leds, _BV (WS2812_PIN)); +} + +void inline +ws2812_setleds_pin (struct RGB *ledarray, uint16_t leds, uint8_t pinmask) +{ + WS2812_DDRREG |= pinmask; // Enable DDR + ws2812_sendarray_mask ((uint8_t *) ledarray, leds + leds + leds, pinmask); + _delay_us (50); +} + +void +ws2812_sendarray (uint8_t * data, uint16_t datlen) +{ + ws2812_sendarray_mask (data, datlen, _BV (WS2812_PIN)); +} + +/* + This routine writes an array of bytes with RGB values to the Dataout pin + using the fast 800kHz clockless WS2811/2812 protocol. +*/ + +// Timing in ns +#define w_zeropulse 350 +#define w_onepulse 900 +#define w_totalperiod 1250 + +// Fixed cycles used by the inner loop +#define w_fixedlow 2 +#define w_fixedhigh 4 +#define w_fixedtotal 8 + +// Insert NOPs to match the timing, if possible +#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000) +#define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000) +#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000) + +// w1 - nops between rising edge and falling edge - low +#define w1 (w_zerocycles-w_fixedlow) +// w2 nops between fe low and fe high +#define w2 (w_onecycles-w_fixedhigh-w1) +// w3 nops to complete loop +#define w3 (w_totalcycles-w_fixedtotal-w1-w2) + +#if w1>0 +#define w1_nops w1 +#else +#define w1_nops 0 +#endif + +// The only critical timing parameter is the minimum pulse length of the "0" +// Warn or throw error if this timing can not be met with current F_CPU settings. +#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000) +#if w_lowtime>550 +#error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?" +#elif w_lowtime>450 +#warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)." +#warning "Please consider a higher clockspeed, if possible" +#endif + +#if w2>0 +#define w2_nops w2 +#else +#define w2_nops 0 +#endif + +#if w3>0 +#define w3_nops w3 +#else +#define w3_nops 0 +#endif + +#define w_nop1 "nop \n\t" +#define w_nop2 "rjmp .+0 \n\t" +#define w_nop4 w_nop2 w_nop2 +#define w_nop8 w_nop4 w_nop4 +#define w_nop16 w_nop8 w_nop8 + +void inline +ws2812_sendarray_mask (uint8_t * data, uint16_t datlen, uint8_t maskhi) +{ + uint8_t curbyte, ctr, masklo; + uint8_t sreg_prev; + + masklo = ~maskhi & WS2812_PORTREG; + maskhi |= WS2812_PORTREG; + sreg_prev = SREG; + cli (); + + while (datlen--) + { + curbyte = *data++; + + asm volatile (" ldi %0,8 \n\t" "loop%=: \n\t" " out %2,%3 \n\t" // '1' [01] '0' [01] - re +#if (w1_nops&1) + w_nop1 +#endif +#if (w1_nops&2) + w_nop2 +#endif +#if (w1_nops&4) + w_nop4 +#endif +#if (w1_nops&8) + w_nop8 +#endif +#if (w1_nops&16) + w_nop16 +#endif + " sbrs %1,7 \n\t" // '1' [03] '0' [02] + " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low + " lsl %1 \n\t" // '1' [04] '0' [04] +#if (w2_nops&1) + w_nop1 +#endif +#if (w2_nops&2) + w_nop2 +#endif +#if (w2_nops&4) + w_nop4 +#endif +#if (w2_nops&8) + w_nop8 +#endif +#if (w2_nops&16) + w_nop16 +#endif + " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high +#if (w3_nops&1) + w_nop1 +#endif +#if (w3_nops&2) + w_nop2 +#endif +#if (w3_nops&4) + w_nop4 +#endif +#if (w3_nops&8) + w_nop8 +#endif +#if (w3_nops&16) + w_nop16 +#endif + " dec %0 \n\t" // '1' [+2] '0' [+2] + " brne loop%=\n\t" // '1' [+3] '0' [+4] + :"=&d" (ctr):"r" (curbyte), + "I" (_SFR_IO_ADDR (WS2812_PORTREG)), "r" (maskhi), + "r" (masklo)); + } + + SREG = sreg_prev; +} diff --git a/lib_ws2812.h b/lib_ws2812.h new file mode 100644 index 0000000..2142fa1 --- /dev/null +++ b/lib_ws2812.h @@ -0,0 +1,64 @@ +/* + * light weight WS2812 lib include + * + * Version 2.0a3 - Jan 18th 2014 + * Author: Tim (cpldcpu@gmail.com) + * + * Please do not change this file! All configuration is handled in "ws2812_config.h" + * + * License: GNU GPL v2 (see License.txt) + + + */ + +#ifndef LIGHT_WS2812_H_ +#define LIGHT_WS2812_H_ + +/* + * Structure of the LED array + */ + +struct RGB +{ + uint8_t g; + uint8_t r; + uint8_t b; +}; + +/* User Interface + * + * Input: + * ledarray: An array of GRB data describing the LED colors + * number_of_leds: The number of LEDs to write + * pinmask (optional): Bitmask describing the output bin. e.g. _BV(PB0) + * + * The functions will perform the following actions: + * - Set the data-out pin as output + * - Send out the LED data + * - Wait 50µs to reset the LEDs + */ + +void ws2812_setleds (struct RGB *ledarray, uint16_t number_of_leds); +void ws2812_setleds_pin (struct RGB *ledarray, uint16_t number_of_leds, + uint8_t pinmask); + +/* + * Old interface / Internal functions + * + * The functions take a byte-array and send to the data output as WS2812 bitstream. + * The length is the number of bytes to send - three per LED. + */ + +void ws2812_sendarray (uint8_t * array, uint16_t length); +void ws2812_sendarray_mask (uint8_t * array, uint16_t length, + uint8_t pinmask); + + +/* + * Internal defines + */ + +#define CONCAT(a, b) a ## b +#define CONCAT_EXP(a, b) CONCAT(a, b) + + +#endif /* LIGHT_WS2812_H_ */ diff --git a/project.h b/project.h new file mode 100644 index 0000000..d75338b --- /dev/null +++ b/project.h @@ -0,0 +1,28 @@ +#define F_CPU 16000000 + +#include +#include +#include +#include +#include +#include + +#include "uart.h" + +#define UART_BAUD_RATE 115200 + +#define WS2812_PORTREG PORTB +#define WS2812_DDRREG DDRB +#define WS2812_PIN 1 + +#include "lib_ws2812.h" + +#ifdef PROTOTYPING +#define NOPROTO static +#else +#define NOPROTO +#endif + +#define NOUNUSED __attribute__((unused)) + +#include "prototypes.h" diff --git a/prototypes.h b/prototypes.h new file mode 100644 index 0000000..b578f9c --- /dev/null +++ b/prototypes.h @@ -0,0 +1,35 @@ +/* lib_ws2812.c */ +void _delay_loop_1(uint8_t __count); +void _delay_loop_2(uint16_t __count); +void _delay_ms(double __ms); +void _delay_us(double __us); +void ws2812_sendarray(uint8_t *data, uint16_t datlen); +/* blinky.c */ +void _delay_loop_1(uint8_t __count); +void _delay_loop_2(uint16_t __count); +void _delay_ms(double __ms); +void _delay_us(double __us); +int ramp(int j, int n); +int main(void); +/* util.c */ +void _delay_loop_1(uint8_t __count); +void _delay_loop_2(uint16_t __count); +void _delay_ms(double __ms); +void _delay_us(double __us); +void setup_clocks(void); +/* uart.c */ +void _delay_loop_1(uint8_t __count); +void _delay_loop_2(uint16_t __count); +void _delay_ms(double __ms); +void _delay_us(double __us); +void uart_init(unsigned int baudrate); +unsigned int uart_getc(void); +void uart_putc(unsigned char data); +void uart_puts(const char *s); +void uart_puts_p(const char *progmem_s); +/* stdio.c */ +void _delay_loop_1(uint8_t __count); +void _delay_loop_2(uint16_t __count); +void _delay_ms(double __ms); +void _delay_us(double __us); +void stdio_init(void); diff --git a/stdio.c b/stdio.c new file mode 100644 index 0000000..c12a9ab --- /dev/null +++ b/stdio.c @@ -0,0 +1,24 @@ +#include "project.h" + +static int +stdio_uart_putchar (char var, FILE * stream) +{ + + if (var == '\n') + uart_putc ('\r'); + uart_putc (var); + return 0; +} + + +static FILE stdio_uart_stdout = +FDEV_SETUP_STREAM (stdio_uart_putchar, NULL, _FDEV_SETUP_WRITE); + +void +stdio_init (void) +{ + stdout = &stdio_uart_stdout; + + uart_init (UART_BAUD_SELECT (UART_BAUD_RATE, F_CPU)); + sei (); +} diff --git a/uart.c b/uart.c new file mode 100644 index 0000000..eb21b6b --- /dev/null +++ b/uart.c @@ -0,0 +1,686 @@ +/************************************************************************* +Title: Interrupt UART library with receive/transmit circular buffers +Author: Peter Fleury http://jump.to/fleury +File: $Id: uart.c,v 1.10 2013/06/02 07:27:04 peter Exp $ +Software: AVR-GCC 4.1, AVR Libc 1.4.6 or higher +Hardware: any AVR with built-in UART, +License: GNU General Public License + +DESCRIPTION: + An interrupt is generated when the UART has finished transmitting or + receiving a byte. The interrupt handling routines use circular buffers + for buffering received and transmitted data. + + The UART_RX_BUFFER_SIZE and UART_TX_BUFFER_SIZE variables define + the buffer size in bytes. Note that these variables must be a + power of 2. + +USAGE: + Refere to the header file uart.h for a description of the routines. + See also example test_uart.c. + +NOTES: + Based on Atmel Application Note AVR306 + +LICENSE: + Copyright (C) 2006 Peter Fleury + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*************************************************************************/ +#include "project.h" + + +/* + * constants and macros + */ + +/* size of RX/TX buffers */ +#define UART_RX_BUFFER_MASK ( UART_RX_BUFFER_SIZE - 1) +#define UART_TX_BUFFER_MASK ( UART_TX_BUFFER_SIZE - 1) + +#if ( UART_RX_BUFFER_SIZE & UART_RX_BUFFER_MASK ) +#error RX buffer size is not a power of 2 +#endif +#if ( UART_TX_BUFFER_SIZE & UART_TX_BUFFER_MASK ) +#error TX buffer size is not a power of 2 +#endif + +#if defined(__AVR_AT90S2313__) \ + || defined(__AVR_AT90S4414__) || defined(__AVR_AT90S4434__) \ + || defined(__AVR_AT90S8515__) || defined(__AVR_AT90S8535__) \ + || defined(__AVR_ATmega103__) + /* old AVR classic or ATmega103 with one UART */ +#define AT90_UART +#define UART0_RECEIVE_INTERRUPT UART_RX_vect +#define UART0_TRANSMIT_INTERRUPT UART_UDRE_vect +#define UART0_STATUS USR +#define UART0_CONTROL UCR +#define UART0_DATA UDR +#define UART0_UDRIE UDRIE +#elif defined(__AVR_AT90S2333__) || defined(__AVR_AT90S4433__) + /* old AVR classic with one UART */ +#define AT90_UART +#define UART0_RECEIVE_INTERRUPT UART_RX_vect +#define UART0_TRANSMIT_INTERRUPT UART_UDRE_vect +#define UART0_STATUS UCSRA +#define UART0_CONTROL UCSRB +#define UART0_DATA UDR +#define UART0_UDRIE UDRIE +#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) \ + || defined(__AVR_ATmega323__) + /* ATmega with one USART */ +#define ATMEGA_USART +#define UART0_RECEIVE_INTERRUPT USART_RXC_vect +#define UART0_TRANSMIT_INTERRUPT USART_UDRE_vect +#define UART0_STATUS UCSRA +#define UART0_CONTROL UCSRB +#define UART0_DATA UDR +#define UART0_UDRIE UDRIE +#elif defined (__AVR_ATmega8515__) || defined(__AVR_ATmega8535__) +#define ATMEGA_USART +#define UART0_RECEIVE_INTERRUPT USART_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART_UDRE_vect +#define UART0_STATUS UCSRA +#define UART0_CONTROL UCSRB +#define UART0_DATA UDR +#define UART0_UDRIE UDRIE +#elif defined(__AVR_ATmega163__) + /* ATmega163 with one UART */ +#define ATMEGA_UART +#define UART0_RECEIVE_INTERRUPT UART_RX_vect +#define UART0_TRANSMIT_INTERRUPT UART_UDRE_vect +#define UART0_STATUS UCSRA +#define UART0_CONTROL UCSRB +#define UART0_DATA UDR +#define UART0_UDRIE UDRIE +#elif defined(__AVR_ATmega162__) + /* ATmega with two USART */ +#define ATMEGA_USART0 +#define ATMEGA_USART1 +#define UART0_RECEIVE_INTERRUPT USART0_RXC_vect +#define UART1_RECEIVE_INTERRUPT USART1_RXC_vect +#define UART0_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART1_TRANSMIT_INTERRUPT USART1_UDRE_vect +#define UART0_STATUS UCSR0A +#define UART0_CONTROL UCSR0B +#define UART0_DATA UDR0 +#define UART0_UDRIE UDRIE0 +#define UART1_STATUS UCSR1A +#define UART1_CONTROL UCSR1B +#define UART1_DATA UDR1 +#define UART1_UDRIE UDRIE1 +#elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) + /* ATmega with two USART */ +#define ATMEGA_USART0 +#define ATMEGA_USART1 +#define UART0_RECEIVE_INTERRUPT USART0_RX_vect +#define UART1_RECEIVE_INTERRUPT USART1_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART1_TRANSMIT_INTERRUPT USART1_UDRE_vect +#define UART0_STATUS UCSR0A +#define UART0_CONTROL UCSR0B +#define UART0_DATA UDR0 +#define UART0_UDRIE UDRIE0 +#define UART1_STATUS UCSR1A +#define UART1_CONTROL UCSR1B +#define UART1_DATA UDR1 +#define UART1_UDRIE UDRIE1 +#elif defined(__AVR_ATmega161__) + /* ATmega with UART */ +#error "AVR ATmega161 currently not supported by this libaray !" +#elif defined(__AVR_ATmega169__) + /* ATmega with one USART */ +#define ATMEGA_USART +#define UART0_RECEIVE_INTERRUPT USART0_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART0_STATUS UCSRA +#define UART0_CONTROL UCSRB +#define UART0_DATA UDR +#define UART0_UDRIE UDRIE +#elif defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__) \ + || defined(__AVR_ATmega3250__) || defined(__AVR_ATmega3290__) ||defined(__AVR_ATmega6450__) || defined(__AVR_ATmega6490__) + /* ATmega with one USART */ +#define ATMEGA_USART0 +#define UART0_RECEIVE_INTERRUPT USART_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART_UDRE_vect +#define UART0_STATUS UCSR0A +#define UART0_CONTROL UCSR0B +#define UART0_DATA UDR0 +#define UART0_UDRIE UDRIE0 +#elif defined(__AVR_ATtiny2313__) +#define ATMEGA_USART +#define UART0_RECEIVE_INTERRUPT USART_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART_UDRE_vect +#define UART0_STATUS UCSRA +#define UART0_CONTROL UCSRB +#define UART0_DATA UDR +#define UART0_UDRIE UDRIE +#elif defined(__AVR_ATmega329__) || \ + defined(__AVR_ATmega649__) || \ + defined(__AVR_ATmega325__) || \ + defined(__AVR_ATmega645__) + /* ATmega with one USART */ +#define ATMEGA_USART0 +#define UART0_RECEIVE_INTERRUPT USART0_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART0_STATUS UCSR0A +#define UART0_CONTROL UCSR0B +#define UART0_DATA UDR0 +#define UART0_UDRIE UDRIE0 +#elif defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega640__) +/* ATmega with two USART */ +#define ATMEGA_USART0 +#define ATMEGA_USART1 +#define UART0_RECEIVE_INTERRUPT USART0_RX_vect +#define UART1_RECEIVE_INTERRUPT USART1_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART1_TRANSMIT_INTERRUPT USART1_UDRE_vect +#define UART0_STATUS UCSR0A +#define UART0_CONTROL UCSR0B +#define UART0_DATA UDR0 +#define UART0_UDRIE UDRIE0 +#define UART1_STATUS UCSR1A +#define UART1_CONTROL UCSR1B +#define UART1_DATA UDR1 +#define UART1_UDRIE UDRIE1 +#elif defined(__AVR_ATmega644__) + /* ATmega with one USART */ +#define ATMEGA_USART0 +#define UART0_RECEIVE_INTERRUPT USART0_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART0_STATUS UCSR0A +#define UART0_CONTROL UCSR0B +#define UART0_DATA UDR0 +#define UART0_UDRIE UDRIE0 +#elif defined(__AVR_ATmega164P__) || defined(__AVR_ATmega324P__) || defined(__AVR_ATmega644P__) + /* ATmega with two USART */ +#define ATMEGA_USART0 +#define ATMEGA_USART1 +#define UART0_RECEIVE_INTERRUPT USART0_RX_vect +#define UART1_RECEIVE_INTERRUPT USART1_RX_vect +#define UART0_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART1_TRANSMIT_INTERRUPT USART1_UDRE_vect +#define UART0_STATUS UCSR0A +#define UART0_CONTROL UCSR0B +#define UART0_DATA UDR0 +#define UART0_UDRIE UDRIE0 +#define UART1_STATUS UCSR1A +#define UART1_CONTROL UCSR1B +#define UART1_DATA UDR1 +#define UART1_UDRIE UDRIE1 +#else +#error "no UART definition for MCU available" +#endif + + +/* + * module global variables + */ +static volatile unsigned char UART_TxBuf[UART_TX_BUFFER_SIZE]; +static volatile unsigned char UART_RxBuf[UART_RX_BUFFER_SIZE]; +static volatile unsigned char UART_TxHead; +static volatile unsigned char UART_TxTail; +static volatile unsigned char UART_RxHead; +static volatile unsigned char UART_RxTail; +static volatile unsigned char UART_LastRxError; + +#if defined( ATMEGA_USART1 ) +static volatile unsigned char UART1_TxBuf[UART_TX_BUFFER_SIZE]; +static volatile unsigned char UART1_RxBuf[UART_RX_BUFFER_SIZE]; +static volatile unsigned char UART1_TxHead; +static volatile unsigned char UART1_TxTail; +static volatile unsigned char UART1_RxHead; +static volatile unsigned char UART1_RxTail; +static volatile unsigned char UART1_LastRxError; +#endif + + + +ISR (NOPROTO UART0_RECEIVE_INTERRUPT) +/************************************************************************* +Function: UART Receive Complete interrupt +Purpose: called when the UART has received a character +**************************************************************************/ +{ + unsigned char tmphead; + unsigned char data; + unsigned char usr; + unsigned char lastRxError; + + + /* read UART status register and UART data register */ + usr = UART0_STATUS; + data = UART0_DATA; + + /* */ +#if defined( AT90_UART ) + lastRxError = (usr & (_BV (FE) | _BV (DOR))); +#elif defined( ATMEGA_USART ) + lastRxError = (usr & (_BV (FE) | _BV (DOR))); +#elif defined( ATMEGA_USART0 ) + lastRxError = (usr & (_BV (FE0) | _BV (DOR0))); +#elif defined ( ATMEGA_UART ) + lastRxError = (usr & (_BV (FE) | _BV (DOR))); +#endif + + /* calculate buffer index */ + tmphead = (UART_RxHead + 1) & UART_RX_BUFFER_MASK; + + if (tmphead == UART_RxTail) + { + /* error: receive buffer overflow */ + lastRxError = UART_BUFFER_OVERFLOW >> 8; + } + else + { + /* store new index */ + UART_RxHead = tmphead; + /* store received data in buffer */ + UART_RxBuf[tmphead] = data; + } + UART_LastRxError |= lastRxError; +} + + +ISR (NOPROTO UART0_TRANSMIT_INTERRUPT) +/************************************************************************* +Function: UART Data Register Empty interrupt +Purpose: called when the UART is ready to transmit the next byte +**************************************************************************/ +{ + unsigned char tmptail; + + + if (UART_TxHead != UART_TxTail) + { + /* calculate and store new buffer index */ + tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK; + UART_TxTail = tmptail; + /* get one byte from buffer and write it to UART */ + UART0_DATA = UART_TxBuf[tmptail]; /* start transmission */ + } + else + { + /* tx buffer empty, disable UDRE interrupt */ + UART0_CONTROL &= ~_BV (UART0_UDRIE); + } +} + + +/************************************************************************* +Function: uart_init() +Purpose: initialize UART and set baudrate +Input: baudrate using macro UART_BAUD_SELECT() +Returns: none +**************************************************************************/ +void +uart_init (unsigned int baudrate) +{ + UART_TxHead = 0; + UART_TxTail = 0; + UART_RxHead = 0; + UART_RxTail = 0; + +#if defined( AT90_UART ) + /* set baud rate */ + UBRR = (unsigned char) baudrate; + + /* enable UART receiver and transmmitter and receive complete interrupt */ + UART0_CONTROL = _BV (RXCIE) | _BV (RXEN) | _BV (TXEN); + +#elif defined (ATMEGA_USART) + /* Set baud rate */ + if (baudrate & 0x8000) + { + UART0_STATUS = (1 << U2X); //Enable 2x speed + baudrate &= ~0x8000; + } + UBRRH = (unsigned char) (baudrate >> 8); + UBRRL = (unsigned char) baudrate; + + /* Enable USART receiver and transmitter and receive complete interrupt */ + UART0_CONTROL = _BV (RXCIE) | (1 << RXEN) | (1 << TXEN); + + /* Set frame format: asynchronous, 8data, no parity, 1stop bit */ +#ifdef URSEL + UCSRC = (1 << URSEL) | (3 << UCSZ0); +#else + UCSRC = (3 << UCSZ0); +#endif + +#elif defined (ATMEGA_USART0 ) + /* Set baud rate */ + if (baudrate & 0x8000) + { + UART0_STATUS = (1 << U2X0); //Enable 2x speed + baudrate &= ~0x8000; + } + UBRR0H = (unsigned char) (baudrate >> 8); + UBRR0L = (unsigned char) baudrate; + + /* Enable USART receiver and transmitter and receive complete interrupt */ + UART0_CONTROL = _BV (RXCIE0) | (1 << RXEN0) | (1 << TXEN0); + + /* Set frame format: asynchronous, 8data, no parity, 1stop bit */ +#ifdef URSEL0 + UCSR0C = (1 << URSEL0) | (3 << UCSZ00); +#else + UCSR0C = (3 << UCSZ00); +#endif + +#elif defined ( ATMEGA_UART ) + /* set baud rate */ + if (baudrate & 0x8000) + { + UART0_STATUS = (1 << U2X); //Enable 2x speed + baudrate &= ~0x8000; + } + UBRRHI = (unsigned char) (baudrate >> 8); + UBRR = (unsigned char) baudrate; + + /* Enable UART receiver and transmitter and receive complete interrupt */ + UART0_CONTROL = _BV (RXCIE) | (1 << RXEN) | (1 << TXEN); + +#endif + +} /* uart_init */ + + +/************************************************************************* +Function: uart_getc() +Purpose: return byte from ringbuffer +Returns: lower byte: received byte from ringbuffer + higher byte: last receive error +**************************************************************************/ +unsigned int +uart_getc (void) +{ + unsigned char tmptail; + unsigned char data; + + + if (UART_RxHead == UART_RxTail) + { + return UART_NO_DATA; /* no data available */ + } + + /* calculate /store buffer index */ + tmptail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK; + UART_RxTail = tmptail; + + /* get data from receive buffer */ + data = UART_RxBuf[tmptail]; + + data = (UART_LastRxError << 8) + data; + UART_LastRxError = 0; + return data; + +} /* uart_getc */ + + +/************************************************************************* +Function: uart_putc() +Purpose: write byte to ringbuffer for transmitting via UART +Input: byte to be transmitted +Returns: none +**************************************************************************/ +void +uart_putc (unsigned char data) +{ + unsigned char tmphead; + + + tmphead = (UART_TxHead + 1) & UART_TX_BUFFER_MASK; + + while (tmphead == UART_TxTail) + { + ; /* wait for free space in buffer */ + } + + UART_TxBuf[tmphead] = data; + UART_TxHead = tmphead; + + /* enable UDRE interrupt */ + UART0_CONTROL |= _BV (UART0_UDRIE); + +} /* uart_putc */ + + +/************************************************************************* +Function: uart_puts() +Purpose: transmit string to UART +Input: string to be transmitted +Returns: none +**************************************************************************/ +void +uart_puts (const char *s) +{ + while (*s) + uart_putc (*s++); + +} /* uart_puts */ + + +/************************************************************************* +Function: uart_puts_p() +Purpose: transmit string from program memory to UART +Input: program memory string to be transmitted +Returns: none +**************************************************************************/ +void +uart_puts_p (const char *progmem_s) +{ + register char c; + + while ((c = pgm_read_byte (progmem_s++))) + uart_putc (c); + +} /* uart_puts_p */ + + +/* + * these functions are only for ATmegas with two USART + */ +#if defined( ATMEGA_USART1 ) + +ISR (NOPROTO UART1_RECEIVE_INTERRUPT) +/************************************************************************* +Function: UART1 Receive Complete interrupt +Purpose: called when the UART1 has received a character +**************************************************************************/ +{ + unsigned char tmphead; + unsigned char data; + unsigned char usr; + unsigned char lastRxError; + + + /* read UART status register and UART data register */ + usr = UART1_STATUS; + data = UART1_DATA; + + /* */ + lastRxError = (usr & (_BV (FE1) | _BV (DOR1))); + + /* calculate buffer index */ + tmphead = (UART1_RxHead + 1) & UART_RX_BUFFER_MASK; + + if (tmphead == UART1_RxTail) + { + /* error: receive buffer overflow */ + lastRxError = UART_BUFFER_OVERFLOW >> 8; + } + else + { + /* store new index */ + UART1_RxHead = tmphead; + /* store received data in buffer */ + UART1_RxBuf[tmphead] = data; + } + UART1_LastRxError |= lastRxError; +} + + +ISR (NOPROTO UART1_TRANSMIT_INTERRUPT) +/************************************************************************* +Function: UART1 Data Register Empty interrupt +Purpose: called when the UART1 is ready to transmit the next byte +**************************************************************************/ +{ + unsigned char tmptail; + + + if (UART1_TxHead != UART1_TxTail) + { + /* calculate and store new buffer index */ + tmptail = (UART1_TxTail + 1) & UART_TX_BUFFER_MASK; + UART1_TxTail = tmptail; + /* get one byte from buffer and write it to UART */ + UART1_DATA = UART1_TxBuf[tmptail]; /* start transmission */ + } + else + { + /* tx buffer empty, disable UDRE interrupt */ + UART1_CONTROL &= ~_BV (UART1_UDRIE); + } +} + + +/************************************************************************* +Function: uart1_init() +Purpose: initialize UART1 and set baudrate +Input: baudrate using macro UART_BAUD_SELECT() +Returns: none +**************************************************************************/ +void +uart1_init (unsigned int baudrate) +{ + UART1_TxHead = 0; + UART1_TxTail = 0; + UART1_RxHead = 0; + UART1_RxTail = 0; + + + /* Set baud rate */ + if (baudrate & 0x8000) + { + UART1_STATUS = (1 << U2X1); //Enable 2x speed + baudrate &= ~0x8000; + } + UBRR1H = (unsigned char) (baudrate >> 8); + UBRR1L = (unsigned char) baudrate; + + /* Enable USART receiver and transmitter and receive complete interrupt */ + UART1_CONTROL = _BV (RXCIE1) | (1 << RXEN1) | (1 << TXEN1); + + /* Set frame format: asynchronous, 8data, no parity, 1stop bit */ +#ifdef URSEL1 + UCSR1C = (1 << URSEL1) | (3 << UCSZ10); +#else + UCSR1C = (3 << UCSZ10); +#endif +} /* uart_init */ + + +/************************************************************************* +Function: uart1_getc() +Purpose: return byte from ringbuffer +Returns: lower byte: received byte from ringbuffer + higher byte: last receive error +**************************************************************************/ +unsigned int +uart1_getc (void) +{ + unsigned char tmptail; + unsigned char data; + + + if (UART1_RxHead == UART1_RxTail) + { + return UART_NO_DATA; /* no data available */ + } + + /* calculate /store buffer index */ + tmptail = (UART1_RxTail + 1) & UART_RX_BUFFER_MASK; + UART1_RxTail = tmptail; + + /* get data from receive buffer */ + data = UART1_RxBuf[tmptail]; + + data = (UART1_LastRxError << 8) + data; + UART1_LastRxError = 0; + return data; + +} /* uart1_getc */ + + +/************************************************************************* +Function: uart1_putc() +Purpose: write byte to ringbuffer for transmitting via UART +Input: byte to be transmitted +Returns: none +**************************************************************************/ +void +uart1_putc (unsigned char data) +{ + unsigned char tmphead; + + + tmphead = (UART1_TxHead + 1) & UART_TX_BUFFER_MASK; + + while (tmphead == UART1_TxTail) + { + ; /* wait for free space in buffer */ + } + + UART1_TxBuf[tmphead] = data; + UART1_TxHead = tmphead; + + /* enable UDRE interrupt */ + UART1_CONTROL |= _BV (UART1_UDRIE); + +} /* uart1_putc */ + + +/************************************************************************* +Function: uart1_puts() +Purpose: transmit string to UART1 +Input: string to be transmitted +Returns: none +**************************************************************************/ +void +uart1_puts (const char *s) +{ + while (*s) + uart1_putc (*s++); + +} /* uart1_puts */ + + +/************************************************************************* +Function: uart1_puts_p() +Purpose: transmit string from program memory to UART1 +Input: program memory string to be transmitted +Returns: none +**************************************************************************/ +void +uart1_puts_p (const char *progmem_s) +{ + register char c; + + while ((c = pgm_read_byte (progmem_s++))) + uart1_putc (c); + +} /* uart1_puts_p */ + + +#endif diff --git a/uart.h b/uart.h new file mode 100644 index 0000000..044c45b --- /dev/null +++ b/uart.h @@ -0,0 +1,183 @@ +#ifndef UART_H +#define UART_H +/************************************************************************ +Title: Interrupt UART library with receive/transmit circular buffers +Author: Peter Fleury http://jump.to/fleury +File: $Id: uart.h,v 1.12 2012/11/19 19:52:27 peter Exp $ +Software: AVR-GCC 4.1, AVR Libc 1.4 +Hardware: any AVR with built-in UART, tested on AT90S8515 & ATmega8 at 4 Mhz +License: GNU General Public License +Usage: see Doxygen manual + +LICENSE: + Copyright (C) 2006 Peter Fleury + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +************************************************************************/ + +/** + * @defgroup pfleury_uart UART Library + * @code #include @endcode + * + * @brief Interrupt UART library using the built-in UART with transmit and receive circular buffers. + * + * This library can be used to transmit and receive data through the built in UART. + * + * An interrupt is generated when the UART has finished transmitting or + * receiving a byte. The interrupt handling routines use circular buffers + * for buffering received and transmitted data. + * + * The UART_RX_BUFFER_SIZE and UART_TX_BUFFER_SIZE constants define + * the size of the circular buffers in bytes. Note that these constants must be a power of 2. + * You may need to adapt this constants to your target and your application by adding + * CDEFS += -DUART_RX_BUFFER_SIZE=nn -DUART_RX_BUFFER_SIZE=nn to your Makefile. + * + * @note Based on Atmel Application Note AVR306 + * @author Peter Fleury pfleury@gmx.ch http://jump.to/fleury + */ + +/**@{*/ +#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304 +#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !" +#endif /* */ + +/* +** constants and macros +*/ + +/** @brief UART Baudrate Expression + * @param xtalcpu system clock in Mhz, e.g. 4000000UL for 4Mhz + * @param baudrate baudrate in bps, e.g. 1200, 2400, 9600 + */ +#define UART_BAUD_SELECT(baudRate,xtalCpu) (((xtalCpu) + 8UL * (baudRate)) / (16UL * (baudRate)) -1UL) + +/** @brief UART Baudrate Expression for ATmega double speed mode + * @param xtalcpu system clock in Mhz, e.g. 4000000UL for 4Mhz + * @param baudrate baudrate in bps, e.g. 1200, 2400, 9600 + */ +#define UART_BAUD_SELECT_DOUBLE_SPEED(baudRate,xtalCpu) ( ((((xtalCpu) + 4UL * (baudRate)) / (8UL * (baudRate)) -1UL)) | 0x8000) + +/** Size of the circular receive buffer, must be power of 2 */ +#ifndef UART_RX_BUFFER_SIZE +#define UART_RX_BUFFER_SIZE 32 +#endif /* */ +/** Size of the circular transmit buffer, must be power of 2 */ +#ifndef UART_TX_BUFFER_SIZE +#define UART_TX_BUFFER_SIZE 32 +#endif /* */ + +/* test if the size of the circular buffers fits into SRAM */ +#if ( (UART_RX_BUFFER_SIZE+UART_TX_BUFFER_SIZE) >= (RAMEND-0x60 ) ) +#error "size of UART_RX_BUFFER_SIZE + UART_TX_BUFFER_SIZE larger than size of SRAM" +#endif /* */ + +/* +** high byte error return code of uart_getc() +*/ +#define UART_FRAME_ERROR 0x1000 /* Framing Error by UART */ +#define UART_OVERRUN_ERROR 0x0800 /* Overrun condition by UART */ +#define UART_PARITY_ERROR 0x0400 /* Parity Error by UART */ +#define UART_BUFFER_OVERFLOW 0x0200 /* receive ringbuffer overflow */ +#define UART_NO_DATA 0x0100 /* no receive data available */ + +/* +** function prototypes +*/ + +/** + @brief Initialize UART and set baudrate + @param baudrate Specify baudrate using macro UART_BAUD_SELECT() + @return none +*/ +// extern void uart_init(unsigned int baudrate); + +/** + * @brief Get received byte from ringbuffer + * + * Returns in the lower byte the received character and in the + * higher byte the last receive error. + * UART_NO_DATA is returned when no data is available. + * + * @param void + * @return lower byte: received byte from ringbuffer + * @return higher byte: last receive status + * - \b 0 successfully received data from UART + * - \b UART_NO_DATA + *
no receive data available + * - \b UART_BUFFER_OVERFLOW + *
Receive ringbuffer overflow. + * We are not reading the receive buffer fast enough, + * one or more received character have been dropped + * - \b UART_OVERRUN_ERROR + *
Overrun condition by UART. + * A character already present in the UART UDR register was + * not read by the interrupt handler before the next character arrived, + * one or more received characters have been dropped. + * - \b UART_FRAME_ERROR + *
Framing Error by UART + */ +// extern unsigned int uart_getc(void); + +/** + * @brief Put byte to ringbuffer for transmitting via UART + * @param data byte to be transmitted + * @return none + */ +// extern void uart_putc(unsigned char data); + +/** + * @brief Put string to ringbuffer for transmitting via UART + * + * The string is buffered by the uart library in a circular buffer + * and one character at a time is transmitted to the UART using interrupts. + * Blocks if it can not write the whole string into the circular buffer. + * + * @param s string to be transmitted + * @return none + */ +// extern void uart_puts(const char *s ); + +/** + * @brief Put string from program memory to ringbuffer for transmitting via UART. + * + * The string is buffered by the uart library in a circular buffer + * and one character at a time is transmitted to the UART using interrupts. + * Blocks if it can not write the whole string into the circular buffer. + * + * @param s program memory string to be transmitted + * @return none + * @see uart_puts_P + */ +// extern void uart_puts_p(const char *s ); + +/** + * @brief Macro to automatically put a string constant into program memory + */ +#define uart_puts_P(__s) uart_puts_p(PSTR(__s)) + +/** @brief Initialize USART1 (only available on selected ATmegas) @see uart_init */ +// extern void uart1_init(unsigned int baudrate); +/** @brief Get received byte of USART1 from ringbuffer. (only available on selected ATmega) @see uart_getc */ +// extern unsigned int uart1_getc(void); +/** @brief Put byte to ringbuffer for transmitting via USART1 (only available on selected ATmega) @see uart_putc */ +// extern void uart1_putc(unsigned char data); +/** @brief Put string to ringbuffer for transmitting via USART1 (only available on selected ATmega) @see uart_puts */ +// extern void uart1_puts(const char *s ); +/** @brief Put string from program memory to ringbuffer for transmitting via USART1 (only available on selected ATmega) @see uart_puts_p */ +// extern void uart1_puts_p(const char *s ); +/** @brief Macro to automatically put a string constant into program memory */ +#define uart1_puts_P(__s) uart1_puts_p(PSTR(__s)) + +/**@}*/ + +#endif // UART_H + diff --git a/util.c b/util.c new file mode 100644 index 0000000..9e82554 --- /dev/null +++ b/util.c @@ -0,0 +1,13 @@ +#include "project.h" + +void +setup_clocks (void) +{ +#ifdef __AVR_ATtiny10__ + CCP = 0xD8; // configuration change protection, write signature + CLKPSR = 0; // set cpu clock prescaler =1 (8Mhz) (attiny 4/5/9/10) +#else + CLKPR = _BV (CLKPCE); + CLKPR = 0; // set clock prescaler to 1 (attiny 25/45/85/24/44/84/13/13A) +#endif +} -- cgit v1.2.3