From 9743c4f5d739a969ddf245477645d7512576f4c6 Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Tue, 26 Jul 2011 00:40:07 +0300 Subject: [PATCH 01/12] add support for usbasp programmer --- hardware/arduino/programmers.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hardware/arduino/programmers.txt b/hardware/arduino/programmers.txt index 466f602a3c0..3548abae2ac 100644 --- a/hardware/arduino/programmers.txt +++ b/hardware/arduino/programmers.txt @@ -18,3 +18,7 @@ arduinoisp.name=Arduino as ISP arduinoisp.communication=serial arduinoisp.protocol=stk500v1 arduinoisp.speed=19200 + +usbasp.name=USBasp +usbasp.communication=usb +usbasp.protocol=usbasp From 1299bcec863ae56a4902c9cde281e26eb38adf13 Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Tue, 26 Jul 2011 00:41:02 +0300 Subject: [PATCH 02/12] add description of ATmega16 board --- hardware/arduino/boards.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/hardware/arduino/boards.txt b/hardware/arduino/boards.txt index 0ae40d3b619..84af772f3c5 100644 --- a/hardware/arduino/boards.txt +++ b/hardware/arduino/boards.txt @@ -336,3 +336,21 @@ atmega8.build.mcu=atmega8 atmega8.build.f_cpu=16000000L atmega8.build.core=arduino +############################################################## + +atmega16.name=Arduino w/ ATmega16 + +atmega16.upload.protocol=stk500 +atmega16.upload.maximum_size=15360 +atmega16.upload.speed=9600 + +atmega16.bootloader.low_fuses=0xdf +atmega16.bootloader.high_fuses=0xda +atmega16.bootloader.path=atmega16 +atmega16.bootloader.file=ATmega16BOOT.hex +atmega16.bootloader.unlock_bits=0x3F +atmega16.bootloader.lock_bits=0x3F + +atmega16.build.mcu=atmega16 +atmega16.build.f_cpu=7372800L +atmega16.build.core=arduino From 992ced3f307668f454beddb70d2c63de474fa1a7 Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Tue, 26 Jul 2011 00:56:59 +0300 Subject: [PATCH 03/12] add ATmega16 pinout --- hardware/arduino/cores/arduino/pins_arduino.c | 129 ++++++++++++++++++ libraries/Firmata/Boards.h | 13 ++ 2 files changed, 142 insertions(+) diff --git a/hardware/arduino/cores/arduino/pins_arduino.c b/hardware/arduino/cores/arduino/pins_arduino.c index 62e10ad151b..c53f2299889 100755 --- a/hardware/arduino/cores/arduino/pins_arduino.c +++ b/hardware/arduino/cores/arduino/pins_arduino.c @@ -351,6 +351,135 @@ const uint8_t PROGMEM digital_pin_to_timer_PGM[] = { NOT_ON_TIMER , // PK 6 ** 68 ** A14 NOT_ON_TIMER , // PK 7 ** 69 ** A15 }; +#elif defined(__AVR_ATmega16__) +const uint16_t PROGMEM port_to_mode_PGM[] = { + NOT_A_PORT, + (uint16_t) &DDRA, + (uint16_t) &DDRB, + (uint16_t) &DDRC, + (uint16_t) &DDRD, +}; + +const uint16_t PROGMEM port_to_output_PGM[] = { + NOT_A_PORT, + (uint16_t) &PORTA, + (uint16_t) &PORTB, + (uint16_t) &PORTC, + (uint16_t) &PORTD, +}; + +const uint16_t PROGMEM port_to_input_PGM[] = { + NOT_A_PORT, + (uint16_t) &PINA, + (uint16_t) &PINB, + (uint16_t) &PINC, + (uint16_t) &PIND, +}; + +const uint8_t PROGMEM digital_pin_to_port_PGM[] = { + PA, /* 0 */ + PA, + PA, + PA, + PA, + PA, + PA, + PA, + PB, /* 8 */ + PB, + PB, + PB, + PB, + PB, + PB, + PB, + PC, /* 16 */ + PC, + PC, + PC, + PC, + PC, + PC, + PC, + PD, /* 24 */ + PD, + PD, + PD, + PD, + PD, + PD, + PD, +}; + +const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = { + _BV(0), /* 0, port A */ + _BV(1), + _BV(2), + _BV(3), + _BV(4), + _BV(5), + _BV(6), + _BV(7), + _BV(0), /* 8, port B */ + _BV(1), + _BV(2), + _BV(3), + _BV(4), + _BV(5), + _BV(6), + _BV(7), + _BV(0), /* 16, port C */ + _BV(1), + _BV(2), + _BV(3), + _BV(4), + _BV(5), + _BV(6), + _BV(7), + _BV(0), /* 24, port D */ + _BV(1), + _BV(2), + _BV(3), + _BV(4), + _BV(5), + _BV(6), + _BV(7), +}; + +const uint8_t PROGMEM digital_pin_to_timer_PGM[] = { + NOT_ON_TIMER, /* 0 - port A */ + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, /* 8 - port B */ + NOT_ON_TIMER, + NOT_ON_TIMER, + TIMER0A, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, /* 16 - port C */ + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, /* 24 - port D */ + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + TIMER1B, + TIMER1A, + NOT_ON_TIMER, + TIMER2, +}; #else // these arrays map port names (e.g. port B) to the // appropriate addresses for various functions (e.g. reading diff --git a/libraries/Firmata/Boards.h b/libraries/Firmata/Boards.h index 20364544774..77906950e0b 100644 --- a/libraries/Firmata/Boards.h +++ b/libraries/Firmata/Boards.h @@ -151,6 +151,19 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_SERVO(p) ((p) - 2) #define ARDUINO_PINOUT_OPTIMIZE 1 +#elif defined(__AVR_ATmega16__) +#define TOTAL_ANALOG_PINS 0 +#define TOTAL_PINS 32 // 32 digital +#define VERSION_BLINK_PIN 0 +#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) <= 31) +#define IS_PIN_ANALOG(p) (0) +#define IS_PIN_PWM(p) IS_PIN_DIGITAL(p) +#define IS_PIN_SERVO(p) (0) +#define IS_PIN_I2C(p) (0) +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_ANALOG(p) (p) +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) + // Arduino Mega #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) From 108d421e3c2f73acb07817a1c31d23165662d855 Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Tue, 26 Jul 2011 00:58:10 +0300 Subject: [PATCH 04/12] ATmega16 bootloader --- .../bootloaders/atmega16/ATmega16BOOT.c | 477 ++++++++++++++++++ .../bootloaders/atmega16/ATmega16BOOT.hex | 63 +++ .../arduino/bootloaders/atmega16/Makefile | 73 +++ 3 files changed, 613 insertions(+) create mode 100755 hardware/arduino/bootloaders/atmega16/ATmega16BOOT.c create mode 100644 hardware/arduino/bootloaders/atmega16/ATmega16BOOT.hex create mode 100644 hardware/arduino/bootloaders/atmega16/Makefile diff --git a/hardware/arduino/bootloaders/atmega16/ATmega16BOOT.c b/hardware/arduino/bootloaders/atmega16/ATmega16BOOT.c new file mode 100755 index 00000000000..709269aee23 --- /dev/null +++ b/hardware/arduino/bootloaders/atmega16/ATmega16BOOT.c @@ -0,0 +1,477 @@ +/**********************************************************/ +/* Serial Bootloader for Atmel mega8 AVR Controller */ +/* */ +/* ATmega16BOOT.c */ +/* */ +/* Copyright (c) 2003, Jason P. Kyle */ +/* Copyright (c) 2011, D. Milinevskyy */ +/* */ +/* Hacked by DojoCorp - ZGZ - MMX - IVR */ +/* Hacked by David A. Mellis */ +/* */ +/* 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 */ +/* (at your option) 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. */ +/* */ +/* You should have received a copy of the GNU General */ +/* Public License along with this program; if not, write */ +/* to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* Licence can be viewed at */ +/* http:// www.fsf.org/licenses/gpl.txt */ +/* */ +/* Target = Atmel AVR m16 */ +/**********************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TIME_COUNT F_CPU + +/* SW_MAJOR and MINOR needs to be updated from time to time to avoid warning message from AVR Studio */ +#define HW_VER 0x02 +#define SW_MAJOR 0x01 +#define SW_MINOR 0x12 + +// AVR-GCC compiler compatibility +// avr-gcc compiler v3.1.x and older doesn't support outb() and inb() +// if necessary, convert outb and inb to outp and inp +#ifndef outb +#define outb(sfr,val) (_SFR_BYTE(sfr) = (val)) +#endif +#ifndef inb +#define inb(sfr) _SFR_BYTE(sfr) +#endif + +/* defines for future compatibility */ +#ifndef cbi +#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#endif +#ifndef sbi +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +#endif + +/* Adjust to suit whatever pin your hardware uses to enter the bootloader */ +#define eeprom_rb(addr) eeprom_read_byte((uint8_t *)(addr)) +#define eeprom_rw(addr) eeprom_read_word((uint16_t *)(addr)) +#define eeprom_wb(addr, val) eeprom_write_byte((uint8_t *)(addr), (uint8_t)(val)) + +/* Onboard LEDs */ +#define LED_DDR DDRA +#define LED_PORT PORTA +#define LED_PIN PINA +#define LED0 PINA0 +#define LED1 PINA1 +#define LED2 PINA2 +#define LED3 PINA3 +#define LED4 PINA4 +#define LED5 PINA5 +#define LED6 PINA6 +#define LED7 PINA7 + +#define SIG1 0x1E // Yep, Atmel is the only manufacturer of AVR micros. Single source :( +#define SIG2 0x94 +#define SIG3 0x03 + +static void putch(uint8_t); +static uint8_t getch(void); +static void eat(uint8_t); + +static void byte_response(uint8_t val); +static void stream_response(uint8_t *val, uint8_t len); +#define nothing_response() stream_response(0, 0) + +static void boot_program_page(uint32_t page, uint8_t *buf); + +static void launch_app(void); + +union address { + uint16_t word; + struct { + uint8_t low; + uint8_t high; + } h; +}; + +union length { + uint16_t word; + struct { + uint8_t low; + uint8_t high; + } h; +}; + +uint8_t buff[256]; + +uint8_t sig[] = {SIG1, SIG2, SIG3}; + +int main(void) +{ + uint8_t i, ch; + uint8_t eeprom; + union length length; + union address address; + + cli(); // Disable interrupts, just to be sure + + /* initialize UART(s) depending on CPU defined */ + UBRRH = (((F_CPU/BAUD_RATE)/16)-1)>>8; // set baud rate + UBRRL = (((F_CPU/BAUD_RATE)/16)-1); + UCSRB = (1< 0x85) getch(); + nothing_response(); + } +#endif + + /* AVR ISP/STK500 board requests */ + else if (ch=='A') { + switch(getch()) { + case 0x80: + byte_response(HW_VER); // Hardware version + break; + case 0x81: + byte_response(SW_MAJOR); // Software major version + break; + case 0x82: + byte_response(SW_MINOR); // Software minor version + break; + default: + byte_response(0x00); // Covers various unnecessary responses we don't care about + } + } + + /* Device Parameters DON'T CARE, DEVICE IS FIXED */ + else if (ch=='B') { + eat(20); + nothing_response(); + } + + /* Parallel programming stuff DON'T CARE */ + else if (ch=='E') { + eat(5); + nothing_response(); + } + + /* Enter programming mode */ + else if (ch=='P') { + nothing_response(); + } + + /* Leave programming mode */ + else if (ch=='Q') { + nothing_response(); + + launch_app(); + } + +#if 0 + /* Erase device, don't care as we will erase one page at a time anyway. */ + else if (ch=='R') { + nothing_response(); + } +#endif + + /* Universal SPI programming command, disabled. Would be used for fuses and lock bits. */ + else if (ch=='V') { + eat(4); + byte_response(0x00); + } + + /* Set address, little endian. EEPROM in bytes, FLASH in words */ + /* Perhaps extra address bytes may be added in future to support > 128kB FLASH. */ + /* This might explain why little endian was used here, big endian used everywhere else. */ + else if (ch=='U') { + address.h.low = getch(); + address.h.high = getch(); + nothing_response(); + } + + /* Write memory, length is big endian and is in bytes */ + else if (ch=='d') { + uint8_t *b; + uint16_t n; + + length.h.high = getch(); + length.h.low = getch(); + + eeprom = 0; + if (getch() == 'E') + eeprom = 1; + + b = buff; + n = length.word; + memset(buff, 0xff, sizeof(buff)); + while (n--) { + *b++ = getch(); // Store data in buffer, can't keep up with serial data stream whilst programming pages + } + + b = buff; + n = length.word; + if (eeprom) { // Write to EEPROM one byte at a time + while (n--) { + eeprom_wb(address.word++, *b++); + } + } else { // Write to FLASH one page at a time + address.word = address.word << 1; // address * 2 -> byte location + + if (n>1), "m"(EECR), "r"(address.h.low), "r"(address.h.high), "z"(address.word), "x"(b) : "r0","r1","r16","r17","r18"); + +#endif + b += SPM_PAGESIZE; + n -= SPM_PAGESIZE; + address.word += SPM_PAGESIZE; + } + } + nothing_response(); + } + + /* Read memory block mode, length is big endian. */ + else if (ch=='t') { + uint8_t *b; + uint16_t n; + + length.h.high = getch(); + length.h.low = getch(); + + eeprom = 0; + if (getch() == 'E') + eeprom = 1; + else + address.word = address.word << 1; // address * 2 -> byte location in case of FLASH + + b = buff; + n = length.word; + while (n--) { + if (eeprom) + *b = eeprom_rb(address.word); + else + *b = pgm_read_byte_near(address.word); + + ++b; + ++address.word; + } + + stream_response(buff, length.word); + } + + /* Get device signature bytes */ + else if (ch=='u') { + stream_response(sig, 3); + } + +#if 0 + /* Read oscillator calibration byte */ + else if (ch=='v') { + byte_response(0x00); + } +#endif + } /* end of forever loop */ +} + +static void putch(uint8_t ch) +{ + while (!(inb(UCSRA) & _BV(UDRE))); + + outb(UDR,ch); +} + +static uint8_t getch(void) +{ + uint32_t count = MAX_TIME_COUNT; + + while (!(inb(UCSRA) & _BV(RXC)) && --count); + + if (!count) + launch_app(); + + return inb(UDR); +} + +static void eat(uint8_t count) +{ + while (count--) + getch(); // need to handle time out +} + +static void byte_response(uint8_t val) +{ + stream_response(&val, 1); +} + +static void stream_response(uint8_t *val, uint8_t len) +{ + if (getch() == ' ') { + putch(0x14); + while (len--) + putch(*(val++)); + putch(0x10); + } +} + +static void boot_program_page(uint32_t page, uint8_t *buf) +{ + uint16_t i, w; + uint8_t sreg; + +#if 0 + // Save status register and disable interrupts. + sreg = SREG; + cli(); +#endif + + eeprom_busy_wait(); + + boot_page_erase(page); + boot_spm_busy_wait(); // Wait until the memory is erased. + + for (i=0; i $@ + +size: $(PROGRAM).hex + $(SIZE) $^ + +# Rules for building the .text rom images + +text: hex bin srec + +hex: $(PROGRAM).hex +bin: $(PROGRAM).bin +srec: $(PROGRAM).srec + +%.hex: %.elf + $(OBJCOPY) -j .text -j .data -O ihex $< $@ + +%.srec: %.elf + $(OBJCOPY) -j .text -j .data -O srec $< $@ + +%.bin: %.elf + $(OBJCOPY) -j .text -j .data -O binary $< $@ From 72b05aef8d057745de2c8b539863c6299778de0b Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Tue, 26 Jul 2011 19:29:32 +0300 Subject: [PATCH 05/12] fix Tone lib for ATmega16 --- hardware/arduino/cores/arduino/Tone.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hardware/arduino/cores/arduino/Tone.cpp b/hardware/arduino/cores/arduino/Tone.cpp index c3910e7a611..8322eede497 100755 --- a/hardware/arduino/cores/arduino/Tone.cpp +++ b/hardware/arduino/cores/arduino/Tone.cpp @@ -36,7 +36,7 @@ Version Modified By Date Comments #include "wiring.h" #include "pins_arduino.h" -#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) +#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega16__) #define TCCR2A TCCR2 #define TCCR2B TCCR2 #define COM2A1 COM21 @@ -48,6 +48,14 @@ Version Modified By Date Comments #define TIMSK1 TIMSK #endif +#if defined(__AVR_ATmega16__) +#define TCCR0A TCCR0 +#define TCCR0B TCCR0 +#define OCR0A OCR0 +#define TIMSK0 TIMSK +#define OCIE0A OCIE0 +#endif + // timerx_toggle_count: // > 0 - duration specified // = 0 - stopped @@ -100,6 +108,13 @@ static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255, 255, 255, 255, 25 const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 1 */ }; static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255 */ }; +#elif defined(__AVR_ATmega16__) + +#define AVAILABLE_TONE_PINS 3 + +const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2, 1, 0 }; +static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255, 255, 255 }; + #else #define AVAILABLE_TONE_PINS 1 From cace964929feb41f5d12971fe3c03748bf4df50b Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Tue, 26 Jul 2011 19:36:43 +0300 Subject: [PATCH 06/12] Allow pins [0:7] to be analog(as long as digital) on ATmega16 --- libraries/Firmata/Boards.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/Firmata/Boards.h b/libraries/Firmata/Boards.h index 77906950e0b..fa5bd2cb40f 100644 --- a/libraries/Firmata/Boards.h +++ b/libraries/Firmata/Boards.h @@ -152,11 +152,11 @@ writePort(port, value, bitmask): Write an 8 bit port. #define ARDUINO_PINOUT_OPTIMIZE 1 #elif defined(__AVR_ATmega16__) -#define TOTAL_ANALOG_PINS 0 -#define TOTAL_PINS 32 // 32 digital +#define TOTAL_ANALOG_PINS 8 +#define TOTAL_PINS 32 // 32 digital(8 of them analog) #define VERSION_BLINK_PIN 0 #define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) <= 31) -#define IS_PIN_ANALOG(p) (0) +#define IS_PIN_ANALOG(p) ((p) >= 0 && (p) <= 7) #define IS_PIN_PWM(p) IS_PIN_DIGITAL(p) #define IS_PIN_SERVO(p) (0) #define IS_PIN_I2C(p) (0) From be43894996b5d04aaf326749cfc7a968b76976c0 Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Thu, 28 Jul 2011 19:28:29 +0300 Subject: [PATCH 07/12] bootloader capabilities extended: - do not start app on UART RX timeout if INT0 was detected - immediately start app on INT1 event - since bootloader does not fit 512 words enable O2 gcc optimization level --- hardware/arduino/boards.txt | 4 +- .../bootloaders/atmega16/ATmega16BOOT.c | 165 ++++++++++++------ .../bootloaders/atmega16/ATmega16BOOT.hex | 159 ++++++++++------- .../arduino/bootloaders/atmega16/Makefile | 4 +- 4 files changed, 217 insertions(+), 115 deletions(-) diff --git a/hardware/arduino/boards.txt b/hardware/arduino/boards.txt index 84af772f3c5..8db448e883c 100644 --- a/hardware/arduino/boards.txt +++ b/hardware/arduino/boards.txt @@ -341,11 +341,11 @@ atmega8.build.core=arduino atmega16.name=Arduino w/ ATmega16 atmega16.upload.protocol=stk500 -atmega16.upload.maximum_size=15360 +atmega16.upload.maximum_size=14336 atmega16.upload.speed=9600 atmega16.bootloader.low_fuses=0xdf -atmega16.bootloader.high_fuses=0xda +atmega16.bootloader.high_fuses=0x98 atmega16.bootloader.path=atmega16 atmega16.bootloader.file=ATmega16BOOT.hex atmega16.bootloader.unlock_bits=0x3F diff --git a/hardware/arduino/bootloaders/atmega16/ATmega16BOOT.c b/hardware/arduino/bootloaders/atmega16/ATmega16BOOT.c index 709269aee23..b0298e0ca57 100755 --- a/hardware/arduino/bootloaders/atmega16/ATmega16BOOT.c +++ b/hardware/arduino/bootloaders/atmega16/ATmega16BOOT.c @@ -33,6 +33,7 @@ /**********************************************************/ #include +#include #include #include #include @@ -92,13 +93,21 @@ static uint8_t getch(void); static void eat(uint8_t); static void byte_response(uint8_t val); -static void stream_response(uint8_t *val, uint8_t len); +static void stream_response(const uint8_t *val, uint8_t len); #define nothing_response() stream_response(0, 0) static void boot_program_page(uint32_t page, uint8_t *buf); +static void blink_led(uint8_t led); static void launch_app(void); +#define IT_LEVEL 0 +#define IT_RAISING_EDGE 1 +#define IT_FALLING_EDGE 2 + +static void enable_interrupt(uint8_t it, uint8_t sence); +static void disable_interrupt(uint8_t it); + union address { uint16_t word; struct { @@ -115,32 +124,48 @@ union length { } h; }; -uint8_t buff[256]; +static uint8_t buff[256]; +static const uint8_t sig[] = {SIG1, SIG2, SIG3}; +static volatile uint8_t loop_forever = 0; + +ISR(INT0_vect, ISR_BLOCK) +{ + disable_interrupt(0); -uint8_t sig[] = {SIG1, SIG2, SIG3}; + loop_forever = 1; + + outb(LED_PORT, inb(LED_PORT) & ~_BV(LED1)); + sbi(LED_DDR, LED1); +} + +ISR(INT1_vect, ISR_BLOCK) +{ + launch_app(); +} int main(void) { - uint8_t i, ch; + uint8_t ch; uint8_t eeprom; union length length; union address address; - cli(); // Disable interrupts, just to be sure - - /* initialize UART(s) depending on CPU defined */ + /* initialize UART */ UBRRH = (((F_CPU/BAUD_RATE)/16)-1)>>8; // set baud rate UBRRL = (((F_CPU/BAUD_RATE)/16)-1); UCSRB = (1< 0x85) getch(); nothing_response(); } -#endif /* AVR ISP/STK500 board requests */ else if (ch=='A') { @@ -222,12 +243,10 @@ int main(void) launch_app(); } -#if 0 /* Erase device, don't care as we will erase one page at a time anyway. */ else if (ch=='R') { nothing_response(); } -#endif /* Universal SPI programming command, disabled. Would be used for fuses and lock bits. */ else if (ch=='V') { @@ -280,6 +299,9 @@ int main(void) boot_program_page(address.word, b); #else asm volatile( + "lds r19,%1 \n" // Save status register and disable interrupts. + "cli \n" + "clr r17 \n" // word count "ldi r18,0x03 \n" // Erase page pointed to by Z ((1<>1), "m"(EECR), "r"(address.h.low), "r"(address.h.high), "z"(address.word), "x"(b) : "r0","r1","r16","r17","r18"); + "clr __zero_reg__ \n" // restore zero register + : "=m"(SPMCR), "=m"(SREG) : "M"(SPM_PAGESIZE>>1), "m"(EECR), "r"(address.h.low), "r"(address.h.high), "z"(address.word), "x"(b) : "r0","r1","r16","r17","r18","r19"); #endif b += SPM_PAGESIZE; n -= SPM_PAGESIZE; @@ -372,12 +395,10 @@ int main(void) stream_response(sig, 3); } -#if 0 /* Read oscillator calibration byte */ else if (ch=='v') { byte_response(0x00); } -#endif } /* end of forever loop */ } @@ -390,12 +411,20 @@ static void putch(uint8_t ch) static uint8_t getch(void) { - uint32_t count = MAX_TIME_COUNT; + uint32_t count; - while (!(inb(UCSRA) & _BV(RXC)) && --count); + for (;;) { + count = MAX_TIME_COUNT; - if (!count) - launch_app(); + while (!(inb(UCSRA) & _BV(RXC)) && --count); + + if (!count) { + if (!loop_forever) + launch_app(); + } else { + break; + } + } return inb(UDR); } @@ -411,7 +440,7 @@ static void byte_response(uint8_t val) stream_response(&val, 1); } -static void stream_response(uint8_t *val, uint8_t len) +static void stream_response(const uint8_t *val, uint8_t len) { if (getch() == ' ') { putch(0x14); @@ -426,52 +455,90 @@ static void boot_program_page(uint32_t page, uint8_t *buf) uint16_t i, w; uint8_t sreg; -#if 0 - // Save status register and disable interrupts. + // Save status register and disable interrupts sreg = SREG; cli(); -#endif eeprom_busy_wait(); boot_page_erase(page); - boot_spm_busy_wait(); // Wait until the memory is erased. + boot_spm_busy_wait(); // Wait until the memory is erased for (i=0; i Date: Thu, 28 Jul 2011 19:35:35 +0300 Subject: [PATCH 08/12] imported Tone library --- libraries/Tone/Tone.cpp | 515 ++++++++++++++++++ libraries/Tone/Tone.h | 142 +++++ libraries/Tone/changelog.txt | 17 + libraries/Tone/examples/DTMFTest/DTMFTest.pde | 46 ++ libraries/Tone/examples/RTTTL/RTTTL.pde | 222 ++++++++ libraries/Tone/examples/ToneTest/ToneTest.pde | 67 +++ libraries/Tone/keywords.txt | 112 ++++ 7 files changed, 1121 insertions(+) create mode 100644 libraries/Tone/Tone.cpp create mode 100644 libraries/Tone/Tone.h create mode 100644 libraries/Tone/changelog.txt create mode 100644 libraries/Tone/examples/DTMFTest/DTMFTest.pde create mode 100644 libraries/Tone/examples/RTTTL/RTTTL.pde create mode 100644 libraries/Tone/examples/ToneTest/ToneTest.pde create mode 100644 libraries/Tone/keywords.txt diff --git a/libraries/Tone/Tone.cpp b/libraries/Tone/Tone.cpp new file mode 100644 index 00000000000..aa222ee7c24 --- /dev/null +++ b/libraries/Tone/Tone.cpp @@ -0,0 +1,515 @@ +/* $Id: Tone.cpp 119 2010-07-17 18:56:36Z bhagman@roguerobotics.com $ + + A Tone Generator Library + + Written by Brett Hagman + http://www.roguerobotics.com/ + bhagman@roguerobotics.com + + This library 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 3 of the License, or + (at your option) any later version. + + This library 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*************************************************/ + +#include +#include +#include +#include +#include "Tone.h" + +#if defined(__AVR_ATmega8__) +#define TCCR2A TCCR2 +#define TCCR2B TCCR2 +#define COM2A1 COM21 +#define COM2A0 COM20 +#define OCR2A OCR2 +#define TIMSK2 TIMSK +#define OCIE2A OCIE2 +#define TIMER2_COMPA_vect TIMER2_COMP_vect +#define TIMSK1 TIMSK +#endif + +// timerx_toggle_count: +// > 0 - duration specified +// = 0 - stopped +// < 0 - infinitely (until stop() method called, or new play() called) + +#if !defined(__AVR_ATmega8__) +volatile int32_t timer0_toggle_count; +volatile uint8_t *timer0_pin_port; +volatile uint8_t timer0_pin_mask; +#endif + +volatile int32_t timer1_toggle_count; +volatile uint8_t *timer1_pin_port; +volatile uint8_t timer1_pin_mask; +volatile int32_t timer2_toggle_count; +volatile uint8_t *timer2_pin_port; +volatile uint8_t timer2_pin_mask; + +#if defined(__AVR_ATmega1280__) +volatile int32_t timer3_toggle_count; +volatile uint8_t *timer3_pin_port; +volatile uint8_t timer3_pin_mask; +volatile int32_t timer4_toggle_count; +volatile uint8_t *timer4_pin_port; +volatile uint8_t timer4_pin_mask; +volatile int32_t timer5_toggle_count; +volatile uint8_t *timer5_pin_port; +volatile uint8_t timer5_pin_mask; +#endif + + +#if defined(__AVR_ATmega1280__) + +#define AVAILABLE_TONE_PINS 6 + +// Leave timers 1, and zero to last. +const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2, 3, 4, 5, 1, 0 }; + +#elif defined(__AVR_ATmega8__) + +#define AVAILABLE_TONE_PINS 2 + +const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2, 1 }; + +#else + +#define AVAILABLE_TONE_PINS 3 + +// Leave timer 0 to last. +const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2, 1, 0 }; + +#endif + + + +// Initialize our pin count + +uint8_t Tone::_tone_pin_count = 0; + + +void Tone::begin(uint8_t tonePin) +{ + if (_tone_pin_count < AVAILABLE_TONE_PINS) + { + _pin = tonePin; + _timer = pgm_read_byte(tone_pin_to_timer_PGM + _tone_pin_count); + _tone_pin_count++; + + // Set timer specific stuff + // All timers in CTC mode + // 8 bit timers will require changing prescalar values, + // whereas 16 bit timers are set to either ck/1 or ck/64 prescalar + switch (_timer) + { +#if !defined(__AVR_ATmega8__) + case 0: + // 8 bit timer + TCCR0A = 0; + TCCR0B = 0; + bitWrite(TCCR0A, WGM01, 1); + bitWrite(TCCR0B, CS00, 1); + timer0_pin_port = portOutputRegister(digitalPinToPort(_pin)); + timer0_pin_mask = digitalPinToBitMask(_pin); + break; +#endif + + case 1: + // 16 bit timer + TCCR1A = 0; + TCCR1B = 0; + bitWrite(TCCR1B, WGM12, 1); + bitWrite(TCCR1B, CS10, 1); + timer1_pin_port = portOutputRegister(digitalPinToPort(_pin)); + timer1_pin_mask = digitalPinToBitMask(_pin); + break; + case 2: + // 8 bit timer + TCCR2A = 0; + TCCR2B = 0; + bitWrite(TCCR2A, WGM21, 1); + bitWrite(TCCR2B, CS20, 1); + timer2_pin_port = portOutputRegister(digitalPinToPort(_pin)); + timer2_pin_mask = digitalPinToBitMask(_pin); + break; + +#if defined(__AVR_ATmega1280__) + case 3: + // 16 bit timer + TCCR3A = 0; + TCCR3B = 0; + bitWrite(TCCR3B, WGM32, 1); + bitWrite(TCCR3B, CS30, 1); + timer3_pin_port = portOutputRegister(digitalPinToPort(_pin)); + timer3_pin_mask = digitalPinToBitMask(_pin); + break; + case 4: + // 16 bit timer + TCCR4A = 0; + TCCR4B = 0; + bitWrite(TCCR4B, WGM42, 1); + bitWrite(TCCR4B, CS40, 1); + timer4_pin_port = portOutputRegister(digitalPinToPort(_pin)); + timer4_pin_mask = digitalPinToBitMask(_pin); + break; + case 5: + // 16 bit timer + TCCR5A = 0; + TCCR5B = 0; + bitWrite(TCCR5B, WGM52, 1); + bitWrite(TCCR5B, CS50, 1); + timer5_pin_port = portOutputRegister(digitalPinToPort(_pin)); + timer5_pin_mask = digitalPinToBitMask(_pin); + break; +#endif + } + } + else + { + // disabled + _timer = -1; + } +} + + + +// frequency (in hertz) and duration (in milliseconds). + +void Tone::play(uint16_t frequency, uint32_t duration) +{ + uint8_t prescalarbits = 0b001; + int32_t toggle_count = 0; + uint32_t ocr = 0; + + if (_timer >= 0) + { + // Set the pinMode as OUTPUT + pinMode(_pin, OUTPUT); + + // if we are using an 8 bit timer, scan through prescalars to find the best fit + if (_timer == 0 || _timer == 2) + { + ocr = F_CPU / frequency / 2 - 1; + prescalarbits = 0b001; // ck/1: same for both timers + if (ocr > 255) + { + ocr = F_CPU / frequency / 2 / 8 - 1; + prescalarbits = 0b010; // ck/8: same for both timers + + if (_timer == 2 && ocr > 255) + { + ocr = F_CPU / frequency / 2 / 32 - 1; + prescalarbits = 0b011; + } + + if (ocr > 255) + { + ocr = F_CPU / frequency / 2 / 64 - 1; + prescalarbits = _timer == 0 ? 0b011 : 0b100; + + if (_timer == 2 && ocr > 255) + { + ocr = F_CPU / frequency / 2 / 128 - 1; + prescalarbits = 0b101; + } + + if (ocr > 255) + { + ocr = F_CPU / frequency / 2 / 256 - 1; + prescalarbits = _timer == 0 ? 0b100 : 0b110; + if (ocr > 255) + { + // can't do any better than /1024 + ocr = F_CPU / frequency / 2 / 1024 - 1; + prescalarbits = _timer == 0 ? 0b101 : 0b111; + } + } + } + } + +#if !defined(__AVR_ATmega8__) + if (_timer == 0) + TCCR0B = (TCCR0B & 0b11111000) | prescalarbits; + else +#endif + TCCR2B = (TCCR2B & 0b11111000) | prescalarbits; + } + else + { + // two choices for the 16 bit timers: ck/1 or ck/64 + ocr = F_CPU / frequency / 2 - 1; + + prescalarbits = 0b001; + if (ocr > 0xffff) + { + ocr = F_CPU / frequency / 2 / 64 - 1; + prescalarbits = 0b011; + } + + if (_timer == 1) + TCCR1B = (TCCR1B & 0b11111000) | prescalarbits; +#if defined(__AVR_ATmega1280__) + else if (_timer == 3) + TCCR3B = (TCCR3B & 0b11111000) | prescalarbits; + else if (_timer == 4) + TCCR4B = (TCCR4B & 0b11111000) | prescalarbits; + else if (_timer == 5) + TCCR5B = (TCCR5B & 0b11111000) | prescalarbits; +#endif + + } + + + // Calculate the toggle count + if (duration > 0) + { + toggle_count = 2 * frequency * duration / 1000; + } + else + { + toggle_count = -1; + } + + // Set the OCR for the given timer, + // set the toggle count, + // then turn on the interrupts + switch (_timer) + { + +#if !defined(__AVR_ATmega8__) + case 0: + OCR0A = ocr; + timer0_toggle_count = toggle_count; + bitWrite(TIMSK0, OCIE0A, 1); + break; +#endif + + case 1: + OCR1A = ocr; + timer1_toggle_count = toggle_count; + bitWrite(TIMSK1, OCIE1A, 1); + break; + case 2: + OCR2A = ocr; + timer2_toggle_count = toggle_count; + bitWrite(TIMSK2, OCIE2A, 1); + break; + +#if defined(__AVR_ATmega1280__) + case 3: + OCR3A = ocr; + timer3_toggle_count = toggle_count; + bitWrite(TIMSK3, OCIE3A, 1); + break; + case 4: + OCR4A = ocr; + timer4_toggle_count = toggle_count; + bitWrite(TIMSK4, OCIE4A, 1); + break; + case 5: + OCR5A = ocr; + timer5_toggle_count = toggle_count; + bitWrite(TIMSK5, OCIE5A, 1); + break; +#endif + + } + } +} + + +void Tone::stop() +{ + switch (_timer) + { +#if !defined(__AVR_ATmega8__) + case 0: + TIMSK0 &= ~(1 << OCIE0A); + break; +#endif + case 1: + TIMSK1 &= ~(1 << OCIE1A); + break; + case 2: + TIMSK2 &= ~(1 << OCIE2A); + break; + +#if defined(__AVR_ATmega1280__) + case 3: + TIMSK3 &= ~(1 << OCIE3A); + break; + case 4: + TIMSK4 &= ~(1 << OCIE4A); + break; + case 5: + TIMSK5 &= ~(1 << OCIE5A); + break; +#endif + } + + digitalWrite(_pin, 0); +} + + +bool Tone::isPlaying(void) +{ + bool returnvalue = false; + + switch (_timer) + { +#if !defined(__AVR_ATmega8__) + case 0: + returnvalue = (TIMSK0 & (1 << OCIE0A)); + break; +#endif + + case 1: + returnvalue = (TIMSK1 & (1 << OCIE1A)); + break; + case 2: + returnvalue = (TIMSK2 & (1 << OCIE2A)); + break; + +#if defined(__AVR_ATmega1280__) + case 3: + returnvalue = (TIMSK3 & (1 << OCIE3A)); + break; + case 4: + returnvalue = (TIMSK4 & (1 << OCIE4A)); + break; + case 5: + returnvalue = (TIMSK5 & (1 << OCIE5A)); + break; +#endif + + } + return returnvalue; +} + + +#if !defined(__AVR_ATmega8__) +ISR(TIMER0_COMPA_vect) +{ + if (timer0_toggle_count != 0) + { + // toggle the pin + *timer0_pin_port ^= timer0_pin_mask; + + if (timer0_toggle_count > 0) + timer0_toggle_count--; + } + else + { + TIMSK0 &= ~(1 << OCIE0A); // disable the interrupt + *timer0_pin_port &= ~(timer0_pin_mask); // keep pin low after stop + } +} +#endif + + +ISR(TIMER1_COMPA_vect) +{ + if (timer1_toggle_count != 0) + { + // toggle the pin + *timer1_pin_port ^= timer1_pin_mask; + + if (timer1_toggle_count > 0) + timer1_toggle_count--; + } + else + { + TIMSK1 &= ~(1 << OCIE1A); // disable the interrupt + *timer1_pin_port &= ~(timer1_pin_mask); // keep pin low after stop + } +} + + +ISR(TIMER2_COMPA_vect) +{ + int32_t temp_toggle_count = timer2_toggle_count; + + if (temp_toggle_count != 0) + { + // toggle the pin + *timer2_pin_port ^= timer2_pin_mask; + + if (temp_toggle_count > 0) + temp_toggle_count--; + } + else + { + TIMSK2 &= ~(1 << OCIE2A); // disable the interrupt + *timer2_pin_port &= ~(timer2_pin_mask); // keep pin low after stop + } + + timer2_toggle_count = temp_toggle_count; +} + + + +#if defined(__AVR_ATmega1280__) + +ISR(TIMER3_COMPA_vect) +{ + if (timer3_toggle_count != 0) + { + // toggle the pin + *timer3_pin_port ^= timer3_pin_mask; + + if (timer3_toggle_count > 0) + timer3_toggle_count--; + } + else + { + TIMSK3 &= ~(1 << OCIE3A); // disable the interrupt + *timer3_pin_port &= ~(timer3_pin_mask); // keep pin low after stop + } +} + +ISR(TIMER4_COMPA_vect) +{ + if (timer4_toggle_count != 0) + { + // toggle the pin + *timer4_pin_port ^= timer4_pin_mask; + + if (timer4_toggle_count > 0) + timer4_toggle_count--; + } + else + { + TIMSK4 &= ~(1 << OCIE4A); // disable the interrupt + *timer4_pin_port &= ~(timer4_pin_mask); // keep pin low after stop + } +} + +ISR(TIMER5_COMPA_vect) +{ + if (timer5_toggle_count != 0) + { + // toggle the pin + *timer5_pin_port ^= timer5_pin_mask; + + if (timer5_toggle_count > 0) + timer5_toggle_count--; + } + else + { + TIMSK5 &= ~(1 << OCIE5A); // disable the interrupt + *timer5_pin_port &= ~(timer5_pin_mask); // keep pin low after stop + } +} + +#endif diff --git a/libraries/Tone/Tone.h b/libraries/Tone/Tone.h new file mode 100644 index 00000000000..be6ca1c2461 --- /dev/null +++ b/libraries/Tone/Tone.h @@ -0,0 +1,142 @@ +/* $Id: Tone.h 113 2010-06-16 20:16:29Z bhagman@roguerobotics.com $ + + A Tone Generator Library + + Written by Brett Hagman + http://www.roguerobotics.com/ + bhagman@roguerobotics.com + + This library 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 3 of the License, or + (at your option) any later version. + + This library 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*************************************************/ + +#ifndef _Tone_h +#define _Tone_h + +#include + +/************************************************* +* Public Constants +*************************************************/ + +#define NOTE_B0 31 +#define NOTE_C1 33 +#define NOTE_CS1 35 +#define NOTE_D1 37 +#define NOTE_DS1 39 +#define NOTE_E1 41 +#define NOTE_F1 44 +#define NOTE_FS1 46 +#define NOTE_G1 49 +#define NOTE_GS1 52 +#define NOTE_A1 55 +#define NOTE_AS1 58 +#define NOTE_B1 62 +#define NOTE_C2 65 +#define NOTE_CS2 69 +#define NOTE_D2 73 +#define NOTE_DS2 78 +#define NOTE_E2 82 +#define NOTE_F2 87 +#define NOTE_FS2 93 +#define NOTE_G2 98 +#define NOTE_GS2 104 +#define NOTE_A2 110 +#define NOTE_AS2 117 +#define NOTE_B2 123 +#define NOTE_C3 131 +#define NOTE_CS3 139 +#define NOTE_D3 147 +#define NOTE_DS3 156 +#define NOTE_E3 165 +#define NOTE_F3 175 +#define NOTE_FS3 185 +#define NOTE_G3 196 +#define NOTE_GS3 208 +#define NOTE_A3 220 +#define NOTE_AS3 233 +#define NOTE_B3 247 +#define NOTE_C4 262 +#define NOTE_CS4 277 +#define NOTE_D4 294 +#define NOTE_DS4 311 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_FS4 370 +#define NOTE_G4 392 +#define NOTE_GS4 415 +#define NOTE_A4 440 +#define NOTE_AS4 466 +#define NOTE_B4 494 +#define NOTE_C5 523 +#define NOTE_CS5 554 +#define NOTE_D5 587 +#define NOTE_DS5 622 +#define NOTE_E5 659 +#define NOTE_F5 698 +#define NOTE_FS5 740 +#define NOTE_G5 784 +#define NOTE_GS5 831 +#define NOTE_A5 880 +#define NOTE_AS5 932 +#define NOTE_B5 988 +#define NOTE_C6 1047 +#define NOTE_CS6 1109 +#define NOTE_D6 1175 +#define NOTE_DS6 1245 +#define NOTE_E6 1319 +#define NOTE_F6 1397 +#define NOTE_FS6 1480 +#define NOTE_G6 1568 +#define NOTE_GS6 1661 +#define NOTE_A6 1760 +#define NOTE_AS6 1865 +#define NOTE_B6 1976 +#define NOTE_C7 2093 +#define NOTE_CS7 2217 +#define NOTE_D7 2349 +#define NOTE_DS7 2489 +#define NOTE_E7 2637 +#define NOTE_F7 2794 +#define NOTE_FS7 2960 +#define NOTE_G7 3136 +#define NOTE_GS7 3322 +#define NOTE_A7 3520 +#define NOTE_AS7 3729 +#define NOTE_B7 3951 +#define NOTE_C8 4186 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 + + +/************************************************* +* Definitions +*************************************************/ + +class Tone +{ + public: + void begin(uint8_t tonePin); + bool isPlaying(); + void play(uint16_t frequency, uint32_t duration = 0); + void stop(); + + private: + static uint8_t _tone_pin_count; + uint8_t _pin; + int8_t _timer; +}; + +#endif diff --git a/libraries/Tone/changelog.txt b/libraries/Tone/changelog.txt new file mode 100644 index 00000000000..927575500d1 --- /dev/null +++ b/libraries/Tone/changelog.txt @@ -0,0 +1,17 @@ +$Id: changelog.txt 120 2010-07-17 19:01:15Z bhagman@roguerobotics.com $ + +Tone Library + +Version Modified By Date Comments +------- ----------- -------- -------- +0001 B Hagman 09/08/02 Initial coding +0002 B Hagman 09/08/18 Fixed: Multiple pins. +0003 B Hagman 09/08/18 Fixed: Moved initialization from constructor to + begin(). +0004 B Hagman 09/09/26 Fixed: Problems with ATmega8. +0005 B Hagman 09/11/23 Fixed: Scanned prescalars for best fit on 8 bit + timers + 09/11/25 Fixed: Pin toggle method to XOR. + 09/11/25 Fixed: timer0 from being excluded. +0006 B Hagman 10/03/21 Fixed: License updates, minor fixes. + B Hagman 10/07/17 Fixed: (more) problems with ATmega8 (thanks to Pete62) diff --git a/libraries/Tone/examples/DTMFTest/DTMFTest.pde b/libraries/Tone/examples/DTMFTest/DTMFTest.pde new file mode 100644 index 00000000000..92ac9ff46bb --- /dev/null +++ b/libraries/Tone/examples/DTMFTest/DTMFTest.pde @@ -0,0 +1,46 @@ +// DTMF (Dual Tone Multiple Frequency) Demonstration + +// http://en.wikipedia.org/wiki/Dual-tone_multi-frequency + +// To mix the output of the signals to output to a small speaker (i.e. 8 Ohms or higher), +// simply use 1K Ohm resistors from each output pin and tie them together at the speaker. +// Don't forget to connect the other side of the speaker to ground! + +#include + +Tone freq1; +Tone freq2; + +const int DTMF_freq1[] = { 1336, 1209, 1336, 1477, 1209, 1336, 1477, 1209, 1336, 1477 }; +const int DTMF_freq2[] = { 941, 697, 697, 697, 770, 770, 770, 852, 852, 852 }; + +void setup() +{ + Serial.begin(9600); + freq1.begin(11); + freq2.begin(12); +} + +void playDTMF(uint8_t number, long duration) +{ + freq1.play(DTMF_freq1[number], duration); + freq2.play(DTMF_freq2[number], duration); +} + + +void loop() +{ + int i; + uint8_t phone_number[] = { 8, 6, 7, 5, 3, 0 ,9 }; + + for(i = 0; i < sizeof(phone_number); i ++) + { + Serial.print(phone_number[i], 10); + Serial.print(' '); + playDTMF(phone_number[i], 500); + delay(600); + } + + Serial.println(); + delay(4000); +} diff --git a/libraries/Tone/examples/RTTTL/RTTTL.pde b/libraries/Tone/examples/RTTTL/RTTTL.pde new file mode 100644 index 00000000000..175e2035987 --- /dev/null +++ b/libraries/Tone/examples/RTTTL/RTTTL.pde @@ -0,0 +1,222 @@ +// A fun sketch to demonstrate the use of the Tone library. + +// To mix the output of the signals to output to a small speaker (i.e. 8 Ohms or higher), +// simply use 1K Ohm resistors from each output pin and tie them together at the speaker. +// Don't forget to connect the other side of the speaker to ground! + +// You can get more RTTTL (RingTone Text Transfer Language) songs from +// http://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation + +#include + +Tone tone1; + +#define OCTAVE_OFFSET 0 + +int notes[] = { 0, +NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4, +NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, +NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6, +NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7 +}; + +//char *song = "The Simpsons:d=4,o=5,b=160:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p,8f#,8f#,8f#,8g,a#.,8c6,8c6,8c6,c6"; +//char *song = "Indiana:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6"; +//char *song = "TakeOnMe:d=4,o=4,b=160:8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5,8f#5,8e5,8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5"; +//char *song = "Entertainer:d=4,o=5,b=140:8d,8d#,8e,c6,8e,c6,8e,2c.6,8c6,8d6,8d#6,8e6,8c6,8d6,e6,8b,d6,2c6,p,8d,8d#,8e,c6,8e,c6,8e,2c.6,8p,8a,8g,8f#,8a,8c6,e6,8d6,8c6,8a,2d6"; +//char *song = "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c"; +//char *song = "Xfiles:d=4,o=5,b=125:e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,g6,f#6,e6,d6,e6,2b.,1p,g6,f#6,e6,d6,f#6,2b.,1p,e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,e6,2b."; +//char *song = "Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f"; +//char *song = "20thCenFox:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p,8e,8g#,8b,1c#6,8f#,8a,8c#6,1e6,8a,8c#6,8e6,1e6,8b,8g#,8a,2b"; +//char *song = "Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6"; +//char *song = "MASH:d=8,o=5,b=140:4a,4g,f#,g,p,f#,p,g,p,f#,p,2e.,p,f#,e,4f#,e,f#,p,e,p,4d.,p,f#,4e,d,e,p,d,p,e,p,d,p,2c#.,p,d,c#,4d,c#,d,p,e,p,4f#,p,a,p,4b,a,b,p,a,p,b,p,2a.,4p,a,b,a,4b,a,b,p,2a.,a,4f#,a,b,p,d6,p,4e.6,d6,b,p,a,p,2b"; +//char *song = "StarWars:d=4,o=5,b=45:32p,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#.6,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#6"; +//char *song = "GoodBad:d=4,o=5,b=56:32p,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,d#,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,c#6,32a#,32d#6,32a#,32d#6,8a#.,16f#.,32f.,32d#.,c#,32a#,32d#6,32a#,32d#6,8a#.,16g#.,d#"; +//char *song = "TopGun:d=4,o=4,b=31:32p,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,16f,d#,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,g#"; +//char *song = "A-Team:d=8,o=5,b=125:4d#6,a#,2d#6,16p,g#,4a#,4d#.,p,16g,16a#,d#6,a#,f6,2d#6,16p,c#.6,16c6,16a#,g#.,2a#"; +//char *song = "Flinstones:d=4,o=5,b=40:32p,16f6,16a#,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,d6,16f6,16a#.,16a#6,32g6,16f6,16a#.,32f6,32f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,a#,16a6,16d.6,16a#6,32a6,32a6,32g6,32f#6,32a6,8g6,16g6,16c.6,32a6,32a6,32g6,32g6,32f6,32e6,32g6,8f6,16f6,16a#.,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#6,16c7,8a#.6"; +//char *song = "Jeopardy:d=4,o=6,b=125:c,f,c,f5,c,f,2c,c,f,c,f,a.,8g,8f,8e,8d,8c#,c,f,c,f5,c,f,2c,f.,8d,c,a#5,a5,g5,f5,p,d#,g#,d#,g#5,d#,g#,2d#,d#,g#,d#,g#,c.7,8a#,8g#,8g,8f,8e,d#,g#,d#,g#5,d#,g#,2d#,g#.,8f,d#,c#,c,p,a#5,p,g#.5,d#,g#"; +//char *song = "Gadget:d=16,o=5,b=50:32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,32d#,32f,32f#,32g#,a#,d#6,4d6,32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,8d#"; +//char *song = "Smurfs:d=32,o=5,b=200:4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8f#,p,8a#,p,4g#,4p,g#,p,a#,p,b,p,c6,p,4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8b,p,8f,p,4f#"; +//char *song = "MahnaMahna:d=16,o=6,b=125:c#,c.,b5,8a#.5,8f.,4g#,a#,g.,4d#,8p,c#,c.,b5,8a#.5,8f.,g#.,8a#.,4g,8p,c#,c.,b5,8a#.5,8f.,4g#,f,g.,8d#.,f,g.,8d#.,f,8g,8d#.,f,8g,d#,8c,a#5,8d#.,8d#.,4d#,8d#."; +//char *song = "LeisureSuit:d=16,o=6,b=56:f.5,f#.5,g.5,g#5,32a#5,f5,g#.5,a#.5,32f5,g#5,32a#5,g#5,8c#.,a#5,32c#,a5,a#.5,c#.,32a5,a#5,32c#,d#,8e,c#.,f.,f.,f.,f.,f,32e,d#,8d,a#.5,e,32f,e,32f,c#,d#.,c#"; +char *song = "MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d"; + +void setup(void) +{ + Serial.begin(9600); + tone1.begin(13); +} + +#define isdigit(n) (n >= '0' && n <= '9') + +void play_rtttl(char *p) +{ + // Absolutely no error checking in here + + byte default_dur = 4; + byte default_oct = 6; + int bpm = 63; + int num; + long wholenote; + long duration; + byte note; + byte scale; + + // format: d=N,o=N,b=NNN: + // find the start (skip name, etc) + + while(*p != ':') p++; // ignore name + p++; // skip ':' + + // get default duration + if(*p == 'd') + { + p++; p++; // skip "d=" + num = 0; + while(isdigit(*p)) + { + num = (num * 10) + (*p++ - '0'); + } + if(num > 0) default_dur = num; + p++; // skip comma + } + + Serial.print("ddur: "); Serial.println(default_dur, 10); + + // get default octave + if(*p == 'o') + { + p++; p++; // skip "o=" + num = *p++ - '0'; + if(num >= 3 && num <=7) default_oct = num; + p++; // skip comma + } + + Serial.print("doct: "); Serial.println(default_oct, 10); + + // get BPM + if(*p == 'b') + { + p++; p++; // skip "b=" + num = 0; + while(isdigit(*p)) + { + num = (num * 10) + (*p++ - '0'); + } + bpm = num; + p++; // skip colon + } + + Serial.print("bpm: "); Serial.println(bpm, 10); + + // BPM usually expresses the number of quarter notes per minute + wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds) + + Serial.print("wn: "); Serial.println(wholenote, 10); + + + // now begin note loop + while(*p) + { + // first, get note duration, if available + num = 0; + while(isdigit(*p)) + { + num = (num * 10) + (*p++ - '0'); + } + + if(num) duration = wholenote / num; + else duration = wholenote / default_dur; // we will need to check if we are a dotted note after + + // now get the note + note = 0; + + switch(*p) + { + case 'c': + note = 1; + break; + case 'd': + note = 3; + break; + case 'e': + note = 5; + break; + case 'f': + note = 6; + break; + case 'g': + note = 8; + break; + case 'a': + note = 10; + break; + case 'b': + note = 12; + break; + case 'p': + default: + note = 0; + } + p++; + + // now, get optional '#' sharp + if(*p == '#') + { + note++; + p++; + } + + // now, get optional '.' dotted note + if(*p == '.') + { + duration += duration/2; + p++; + } + + // now, get scale + if(isdigit(*p)) + { + scale = *p - '0'; + p++; + } + else + { + scale = default_oct; + } + + scale += OCTAVE_OFFSET; + + if(*p == ',') + p++; // skip comma for next note (or we may be at the end) + + // now play the note + + if(note) + { + Serial.print("Playing: "); + Serial.print(scale, 10); Serial.print(' '); + Serial.print(note, 10); Serial.print(" ("); + Serial.print(notes[(scale - 4) * 12 + note], 10); + Serial.print(") "); + Serial.println(duration, 10); + tone1.play(notes[(scale - 4) * 12 + note]); + delay(duration); + tone1.stop(); + } + else + { + Serial.print("Pausing: "); + Serial.println(duration, 10); + delay(duration); + } + } +} + +void loop(void) +{ + play_rtttl(song); + Serial.println("Done."); + while(1); +} diff --git a/libraries/Tone/examples/ToneTest/ToneTest.pde b/libraries/Tone/examples/ToneTest/ToneTest.pde new file mode 100644 index 00000000000..dd174098a94 --- /dev/null +++ b/libraries/Tone/examples/ToneTest/ToneTest.pde @@ -0,0 +1,67 @@ +// Duelling Tones - Simultaneous tone generation. +// To mix the output of the signals to output to a small speaker (i.e. 8 Ohms or higher), +// simply use 1K Ohm resistors from each output pin and tie them together at the speaker. +// Don't forget to connect the other side of the speaker to ground! + +// This example plays notes 'a' through 'g' sent over the Serial Monitor. +// 's' stops the current playing tone. Use uppercase letters for the second. + +#include + +int notes[] = { NOTE_A3, + NOTE_B3, + NOTE_C4, + NOTE_D4, + NOTE_E4, + NOTE_F4, + NOTE_G4 }; + +// You can declare the tones as an array +Tone notePlayer[2]; + +void setup(void) +{ + Serial.begin(9600); + notePlayer[0].begin(11); + notePlayer[1].begin(12); +} + +void loop(void) +{ + char c; + + if(Serial.available()) + { + c = Serial.read(); + + switch(c) + { + case 'a'...'g': + notePlayer[0].play(notes[c - 'a']); + Serial.println(notes[c - 'a']); + break; + case 's': + notePlayer[0].stop(); + break; + + case 'A'...'G': + notePlayer[1].play(notes[c - 'A']); + Serial.println(notes[c - 'A']); + break; + case 'S': + notePlayer[1].stop(); + break; + + default: + notePlayer[1].stop(); + notePlayer[0].play(NOTE_B2); + delay(300); + notePlayer[0].stop(); + delay(100); + notePlayer[1].play(NOTE_B2); + delay(300); + notePlayer[1].stop(); + break; + } + } +} diff --git a/libraries/Tone/keywords.txt b/libraries/Tone/keywords.txt new file mode 100644 index 00000000000..f1b23b2184e --- /dev/null +++ b/libraries/Tone/keywords.txt @@ -0,0 +1,112 @@ +####################################### +# Syntax Coloring Map For Tone +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Tone KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +play KEYWORD2 +stop KEYWORD2 +begin KEYWORD2 +isPlaying KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +NOTE_B0 LITERAL1 +NOTE_C1 LITERAL1 +NOTE_CS1 LITERAL1 +NOTE_D1 LITERAL1 +NOTE_DS1 LITERAL1 +NOTE_E1 LITERAL1 +NOTE_F1 LITERAL1 +NOTE_FS1 LITERAL1 +NOTE_G1 LITERAL1 +NOTE_GS1 LITERAL1 +NOTE_A1 LITERAL1 +NOTE_AS1 LITERAL1 +NOTE_B1 LITERAL1 +NOTE_C2 LITERAL1 +NOTE_CS2 LITERAL1 +NOTE_D2 LITERAL1 +NOTE_DS2 LITERAL1 +NOTE_E2 LITERAL1 +NOTE_F2 LITERAL1 +NOTE_FS2 LITERAL1 +NOTE_G2 LITERAL1 +NOTE_GS2 LITERAL1 +NOTE_A2 LITERAL1 +NOTE_AS2 LITERAL1 +NOTE_B2 LITERAL1 +NOTE_C3 LITERAL1 +NOTE_CS3 LITERAL1 +NOTE_D3 LITERAL1 +NOTE_DS3 LITERAL1 +NOTE_E3 LITERAL1 +NOTE_F3 LITERAL1 +NOTE_FS3 LITERAL1 +NOTE_G3 LITERAL1 +NOTE_GS3 LITERAL1 +NOTE_A3 LITERAL1 +NOTE_AS3 LITERAL1 +NOTE_B3 LITERAL1 +NOTE_C4 LITERAL1 +NOTE_CS4 LITERAL1 +NOTE_D4 LITERAL1 +NOTE_DS4 LITERAL1 +NOTE_E4 LITERAL1 +NOTE_F4 LITERAL1 +NOTE_FS4 LITERAL1 +NOTE_G4 LITERAL1 +NOTE_GS4 LITERAL1 +NOTE_A4 LITERAL1 +NOTE_AS4 LITERAL1 +NOTE_B4 LITERAL1 +NOTE_C5 LITERAL1 +NOTE_CS5 LITERAL1 +NOTE_D5 LITERAL1 +NOTE_DS5 LITERAL1 +NOTE_E5 LITERAL1 +NOTE_F5 LITERAL1 +NOTE_FS5 LITERAL1 +NOTE_G5 LITERAL1 +NOTE_GS5 LITERAL1 +NOTE_A5 LITERAL1 +NOTE_AS5 LITERAL1 +NOTE_B5 LITERAL1 +NOTE_C6 LITERAL1 +NOTE_CS6 LITERAL1 +NOTE_D6 LITERAL1 +NOTE_DS6 LITERAL1 +NOTE_E6 LITERAL1 +NOTE_F6 LITERAL1 +NOTE_FS6 LITERAL1 +NOTE_G6 LITERAL1 +NOTE_GS6 LITERAL1 +NOTE_A6 LITERAL1 +NOTE_AS6 LITERAL1 +NOTE_B6 LITERAL1 +NOTE_C7 LITERAL1 +NOTE_CS7 LITERAL1 +NOTE_D7 LITERAL1 +NOTE_DS7 LITERAL1 +NOTE_E7 LITERAL1 +NOTE_F7 LITERAL1 +NOTE_FS7 LITERAL1 +NOTE_G7 LITERAL1 +NOTE_GS7 LITERAL1 +NOTE_A7 LITERAL1 +NOTE_AS7 LITERAL1 +NOTE_B7 LITERAL1 +NOTE_C8 LITERAL1 +NOTE_CS8 LITERAL1 +NOTE_D8 LITERAL1 +NOTE_DS8 LITERAL1 From 3940d99fbc08f6245fa1a6d3a7752045fd3c838c Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Thu, 28 Jul 2011 20:37:29 +0300 Subject: [PATCH 09/12] Tone library adaptation for ATmega16 --- libraries/Tone/Tone.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/Tone/Tone.cpp b/libraries/Tone/Tone.cpp index aa222ee7c24..6bf705f4b6c 100644 --- a/libraries/Tone/Tone.cpp +++ b/libraries/Tone/Tone.cpp @@ -27,7 +27,7 @@ #include #include "Tone.h" -#if defined(__AVR_ATmega8__) +#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega16__) #define TCCR2A TCCR2 #define TCCR2B TCCR2 #define COM2A1 COM21 @@ -39,6 +39,14 @@ #define TIMSK1 TIMSK #endif +#if defined(__AVR_ATmega16__) +#define TCCR0A TCCR0 +#define TCCR0B TCCR0 +#define OCR0A OCR0 +#define TIMSK0 TIMSK +#define OCIE0A OCIE0 +#endif + // timerx_toggle_count: // > 0 - duration specified // = 0 - stopped @@ -83,6 +91,13 @@ const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2, 3, 4, 5, 1, 0 }; const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2, 1 }; +#elif defined(__AVR_ATmega16__) + +#define AVAILABLE_TONE_PINS 3 + +const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2, 1, 0 }; +static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 31, 28, 11 }; + #else #define AVAILABLE_TONE_PINS 3 From 2b9a7f6079c65e83b142c902151218fe4670b75f Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Fri, 29 Jul 2011 10:13:02 +0300 Subject: [PATCH 10/12] imported TimerOne library --- libraries/TimerOne/TimerOne.cpp | 137 ++++++++++++++++++ libraries/TimerOne/TimerOne.h | 47 ++++++ .../TimerOne/examples/timer_one/timer_one.pde | 25 ++++ 3 files changed, 209 insertions(+) create mode 100644 libraries/TimerOne/TimerOne.cpp create mode 100644 libraries/TimerOne/TimerOne.h create mode 100644 libraries/TimerOne/examples/timer_one/timer_one.pde diff --git a/libraries/TimerOne/TimerOne.cpp b/libraries/TimerOne/TimerOne.cpp new file mode 100644 index 00000000000..58a4094e53f --- /dev/null +++ b/libraries/TimerOne/TimerOne.cpp @@ -0,0 +1,137 @@ +/* + * Interrupt and PWM utilities for 16 bit Timer1 on ATmega168/328 + * Original code by Jesse Tane for http://labs.ideo.com August 2008 + * Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support + * Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop + * Modified June 2011 by Lex Talionis to add a function to read the timer + * + * This is free software. You can redistribute it and/or modify it under + * the terms of Creative Commons Attribution 3.0 United States License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ + * or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. + * + * See Google Code project http://code.google.com/p/arduino-timerone/ for latest + */ +#ifndef TIMERONE_cpp +#define TIMERONE_cpp + +#include "TimerOne.h" + +TimerOne Timer1; // preinstatiate + +ISR(TIMER1_OVF_vect) // interrupt service routine that wraps a user defined function supplied by attachInterrupt +{ + Timer1.isrCallback(); +} + +void TimerOne::initialize(long microseconds) +{ + TCCR1A = 0; // clear control register A + TCCR1B = _BV(WGM13); // set mode 8: phase and frequency correct pwm, stop the timer + setPeriod(microseconds); +} + +void TimerOne::setPeriod(long microseconds) +{ + long cycles = (F_CPU / 2000000) * microseconds; // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2 + if(cycles < RESOLUTION) clockSelectBits = _BV(CS10); // no prescale, full xtal + else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11); // prescale by /8 + else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10); // prescale by /64 + else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12); // prescale by /256 + else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10); // prescale by /1024 + else cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10); // request was out of bounds, set as maximum + ICR1 = pwmPeriod = cycles; // ICR1 is TOP in p & f correct pwm mode + TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); + TCCR1B |= clockSelectBits; // reset clock select register +} + +void TimerOne::setPwmDuty(char pin, int duty) +{ + unsigned long dutyCycle = pwmPeriod; + dutyCycle *= duty; + dutyCycle >>= 10; + if(pin == 1 || pin == 9) OCR1A = dutyCycle; + else if(pin == 2 || pin == 10) OCR1B = dutyCycle; +} + +void TimerOne::pwm(char pin, int duty, long microseconds) // expects duty cycle to be 10 bit (1024) +{ + if(microseconds > 0) setPeriod(microseconds); + if(pin == 1 || pin == 9) { + DDRB |= _BV(PORTB1); // sets data direction register for pwm output pin + TCCR1A |= _BV(COM1A1); // activates the output pin + } + else if(pin == 2 || pin == 10) { + DDRB |= _BV(PORTB2); + TCCR1A |= _BV(COM1B1); + } + setPwmDuty(pin, duty); + start(); +} + +void TimerOne::disablePwm(char pin) +{ + if(pin == 1 || pin == 9) TCCR1A &= ~_BV(COM1A1); // clear the bit that enables pwm on PB1 + else if(pin == 2 || pin == 10) TCCR1A &= ~_BV(COM1B1); // clear the bit that enables pwm on PB2 +} + +void TimerOne::attachInterrupt(void (*isr)(), long microseconds) +{ + if(microseconds > 0) setPeriod(microseconds); + isrCallback = isr; // register the user's callback with the real ISR + TIMSK1 = _BV(TOIE1); // sets the timer overflow interrupt enable bit + sei(); // ensures that interrupts are globally enabled + start(); +} + +void TimerOne::detachInterrupt() +{ + TIMSK1 &= ~_BV(TOIE1); // clears the timer overflow interrupt enable bit +} + +void TimerOne::start() +{ + TCCR1B |= clockSelectBits; +} + +void TimerOne::stop() +{ + TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); // clears all clock selects bits +} + +void TimerOne::restart() +{ + TCNT1 = 0; +} + +unsigned long TimerOne::read() //returns the value of the timer in microseconds +{ //rember! phase and freq correct mode counts up to then down again + unsigned int tmp=TCNT1; + char scale=0; + switch (clockSelectBits) + { + case 1:// no prescalse + scale=0; + break; + case 2:// x8 prescale + scale=3; + break; + case 3:// x64 + scale=6; + break; + case 4:// x256 + scale=8; + break; + case 5:// x1024 + scale=10; + break; + } + while (TCNT1==tmp) //if the timer has not ticked yet + { + //do nothing -- max delay here is ~1023 cycles + } + tmp = ( (TCNT1>tmp) ? (tmp) : (ICR1-TCNT1)+ICR1 );//if we are counting down add the top value to how far we have counted down + return ((tmp*1000L)/(F_CPU /1000L))< +#include + +#define RESOLUTION 65536 // Timer1 is 16 bit + +class TimerOne +{ + public: + + // properties + unsigned int pwmPeriod; + unsigned char clockSelectBits; + + // methods + void initialize(long microseconds=1000000); + void start(); + void stop(); + void restart(); + unsigned long read(); + void pwm(char pin, int duty, long microseconds=-1); + void disablePwm(char pin); + void attachInterrupt(void (*isr)(), long microseconds=-1); + void detachInterrupt(); + void setPeriod(long microseconds); + void setPwmDuty(char pin, int duty); + void (*isrCallback)(); +}; + +extern TimerOne Timer1; +#endif \ No newline at end of file diff --git a/libraries/TimerOne/examples/timer_one/timer_one.pde b/libraries/TimerOne/examples/timer_one/timer_one.pde new file mode 100644 index 00000000000..7997879a564 --- /dev/null +++ b/libraries/TimerOne/examples/timer_one/timer_one.pde @@ -0,0 +1,25 @@ +/* + * Timer1 library example + * June 2008 | jesse dot tane at gmail dot com + */ + +#include "TimerOne.h" + +void setup() +{ + pinMode(10, OUTPUT); + Timer1.initialize(500000); // initialize timer1, and set a 1/2 second period + Timer1.pwm(9, 512); // setup pwm on pin 9, 50% duty cycle + Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt +} + +void callback() +{ + digitalWrite(10, digitalRead(10) ^ 1); +} + +void loop() +{ + // your program here... +} + From 13c10aa14c24c62c5050e937b903d4a55a33678a Mon Sep 17 00:00:00 2001 From: Dmytro Milinevskyy Date: Fri, 29 Jul 2011 13:56:52 +0300 Subject: [PATCH 11/12] fix TimerOne not to harm other timers --- libraries/TimerOne/TimerOne.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/TimerOne/TimerOne.cpp b/libraries/TimerOne/TimerOne.cpp index 58a4094e53f..15dd70efdd6 100644 --- a/libraries/TimerOne/TimerOne.cpp +++ b/libraries/TimerOne/TimerOne.cpp @@ -79,7 +79,7 @@ void TimerOne::attachInterrupt(void (*isr)(), long microseconds) { if(microseconds > 0) setPeriod(microseconds); isrCallback = isr; // register the user's callback with the real ISR - TIMSK1 = _BV(TOIE1); // sets the timer overflow interrupt enable bit + TIMSK1 |= _BV(TOIE1); // sets the timer overflow interrupt enable bit sei(); // ensures that interrupts are globally enabled start(); } @@ -134,4 +134,4 @@ unsigned long TimerOne::read() //returns the value of the timer in microseconds return ((tmp*1000L)/(F_CPU /1000L))< Date: Fri, 29 Jul 2011 14:08:05 +0300 Subject: [PATCH 12/12] TimerOne library adaptation for ATmega16 --- libraries/TimerOne/TimerOne.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/TimerOne/TimerOne.cpp b/libraries/TimerOne/TimerOne.cpp index 15dd70efdd6..95ba5bfe09c 100644 --- a/libraries/TimerOne/TimerOne.cpp +++ b/libraries/TimerOne/TimerOne.cpp @@ -17,6 +17,10 @@ #include "TimerOne.h" +#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega16__) +#define TIMSK1 TIMSK +#endif + TimerOne Timer1; // preinstatiate ISR(TIMER1_OVF_vect) // interrupt service routine that wraps a user defined function supplied by attachInterrupt