From de0f579d043896e2c3a480ad821fe03888baf819 Mon Sep 17 00:00:00 2001 From: Armin Date: Mon, 12 Oct 2020 21:25:43 +0200 Subject: [PATCH] Version 1.4.2 - New example ReactionTimeTestGame --- .github/workflows/LibraryBuild.yml | 8 +- README.md | 18 +- examples/LightToTone/EasyButtonAtInt01.cpp.h | 6 +- examples/LightToTone/EasyButtonAtInt01.h | 70 +- examples/LightToTone/LightToTone.ino | 4 +- examples/OneMelody/ATtinySerialOut.cpp | 30 +- examples/OneMelody/ATtinySerialOut.h | 35 +- examples/OneMelody/OneMelody.ino | 2 +- .../BlinkLed.cpp | 48 +- .../PlayChristmasMelodyUSDistance/BlinkLed.h | 18 +- .../PlayChristmasMelodyUSDistance/HCSR04.cpp | 24 +- .../PlayChristmasMelodyUSDistance/HCSR04.h | 3 +- .../PlayChristmasMelodyUSDistance.ino | 21 +- examples/RandomMelody/ATtinySerialOut.cpp | 30 +- examples/RandomMelody/ATtinySerialOut.h | 35 +- examples/RandomMelody/RandomMelody.ino | 2 +- examples/ReactionTimeTestGame/Breadboard.h | 65 + .../ReactionTimeTestGame/MultiFuncShield.cpp | 1341 +++++++++++++++++ .../ReactionTimeTestGame/MultiFuncShield.h | 226 +++ .../ReactionTimeTestGame.ino | 332 ++++ examples/ReactionTimeTestGame/TimerOne.cpp | 59 + examples/ReactionTimeTestGame/TimerOne.h | 619 ++++++++ .../ReactionTimeTestGame/known_16bit_timers.h | 169 +++ library.json | 2 +- library.properties | 4 +- src/PlayRtttl.cpp | 43 +- src/PlayRtttl.h | 100 +- 27 files changed, 3076 insertions(+), 238 deletions(-) create mode 100644 examples/ReactionTimeTestGame/Breadboard.h create mode 100644 examples/ReactionTimeTestGame/MultiFuncShield.cpp create mode 100644 examples/ReactionTimeTestGame/MultiFuncShield.h create mode 100644 examples/ReactionTimeTestGame/ReactionTimeTestGame.ino create mode 100644 examples/ReactionTimeTestGame/TimerOne.cpp create mode 100644 examples/ReactionTimeTestGame/TimerOne.h create mode 100644 examples/ReactionTimeTestGame/known_16bit_timers.h diff --git a/.github/workflows/LibraryBuild.yml b/.github/workflows/LibraryBuild.yml index 51f7d35..757ed4b 100644 --- a/.github/workflows/LibraryBuild.yml +++ b/.github/workflows/LibraryBuild.yml @@ -65,22 +65,22 @@ jobs: - arduino-boards-fqbn: digistump:avr:digispark-tiny:clock=clock1 # ATtiny85 board @1 MHz platform-url: https://raw.githubusercontent.com/ArminJo/DigistumpArduino/master/package_digistump_index.json - sketches-exclude: LightToTone PlayChristmasMelodyUSDistance RandomMelody # Only OneMelody example + sketches-exclude: LightToTone PlayChristmasMelodyUSDistance RandomMelody ReactionTimeTestGame # Only OneMelody example build-properties: # the flags were put in compiler.cpp.extra_flags OneMelody: -DDEBUG - arduino-boards-fqbn: esp8266:esp8266:huzzah:eesz=4M3M,xtal=80 platform-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json - sketches-exclude: LightToTone PlayChristmasMelodyUSDistance # EasyButton and Talkie library not available + sketches-exclude: LightToTone PlayChristmasMelodyUSDistance ReactionTimeTestGame # EasyButton and Talkie library not available - arduino-boards-fqbn: esp32:esp32:featheresp32:FlashFreq=80 platform-url: https://dl.espressif.com/dl/package_esp32_index.json - sketches-exclude: LightToTone PlayChristmasMelodyUSDistance # EasyButton and Talkie library not available + sketches-exclude: LightToTone PlayChristmasMelodyUSDistance ReactionTimeTestGame # EasyButton and Talkie library not available - arduino-boards-fqbn: STM32:stm32:GenF1:pnum=BLUEPILL_F103C8 platform-url: https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json - sketches-exclude: LightToTone PlayChristmasMelodyUSDistance # EasyButton and Talkie library not available + sketches-exclude: LightToTone PlayChristmasMelodyUSDistance ReactionTimeTestGame # EasyButton and Talkie library not available # Do not cancel all jobs / architectures if one job fails fail-fast: false diff --git a/README.md b/README.md index 891d82e..8609244 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # [PlayRtttl](https://github.com/ArminJo/PlayRtttl) Available as Arduino library "PlayRtttl" -### [Version 1.4.1](https://github.com/ArminJo/PlayRtttl/releases) +### [Version 1.4.2](https://github.com/ArminJo/PlayRtttl/releases) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Installation instructions](https://www.ardu-badge.com/badge/PlayRtttl.svg?)](https://www.ardu-badge.com/PlayRtttl) @@ -48,7 +48,7 @@ const int TONE_PIN = 11; # Compile options / macros for this library To customize the library to different requirements, there are some compile options / makros available.
-Modify it by commenting them out or in, or change the values if applicable. Or define the macro with the -D compiler option for gobal compile (the latter is not possible with the Arduino IDE, so consider to use [sloeber](https://eclipse.baeyens.it). +Modify it by commenting them out or in, or change the values if applicable. Or define the macro with the -D compiler option for gobal compile (the latter is not possible with the Arduino IDE, so consider to use [Sloeber](https://eclipse.baeyens.it). Some options which are enabed by default can be disabled also by defining a *inhibit* macro like `USE_NO_RTX_EXTENSIONS`. | Macro | Default | File | Disable macro | Description | |-|-|-|-|-| @@ -56,6 +56,16 @@ Some options which are enabed by default can be disabled also by defining a *inh | `SUPPORT_RTX_FORMAT` | enabled | PlayRtttl.h | `USE_NO_RTX_EXTENSIONS` | Enables evaluating RTX format definitions `'s'` and `'l'`. | | `RTX_STYLE_DEFAULT` | 'N' | PlayRtttl.h | | (Natural) Tone length = note length - 1/16. | +### Modifying compile options with Arduino IDE +First use *Sketch > Show Sketch Folder (Ctrl+K)*.
+If you did not yet stored the example as your own sketch, then you are instantly in the right library folder.
+Otherwise you have to navigate to the parallel `libraries` folder and select the library you want to access.
+In both cases the library files itself are located in the `src` directory.
+ +### Modifying compile options with Sloeber IDE +If you are using Sloeber as your IDE, you can easily define global symbols with *Properties > Arduino > CompileOptions*.
+![Sloeber settings](https://github.com/ArminJo/ServoEasing/blob/master/pictures/SloeberDefineSymbols.png) + # Running with 1 MHz If running with 1 MHz, e.g on an ATtiny, the millis() interrupt needs so much time, that it disturbes the tone() generation by interrupt. You can avoid this by using a tone pin, which is directly supported by hardware. Look at the appropriate *pins_arduino.h*, find `digital_pin_to_timer_PGM[]` and choose pins with TIMER1x entries. @@ -64,6 +74,8 @@ More RTTTL songs can be found under http://www.picaxe.com/RTTTL-Ringtones-for-Tu [C array of songs on GitHub](https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h) # Revision History +### Version 1.4.2 +- New example ReactionTimeTestGame. ### Version 1.4.1 - Removed blocking wait for ATmega32U4 Serial in examples. @@ -91,7 +103,7 @@ More RTTTL songs can be found under http://www.picaxe.com/RTTTL-Ringtones-for-Tu ### Version 1.2.0 - No Serial.print statements in this library anymore, to avoid problems with different Serial implementations. - Function `playRandomRtttlBlocking()` + `startPlayRandomRtttlFromArrayPGM()` do not print name now. If needed, use new functions `playRandomRtttlSampleBlockingAndPrintName()` + `startPlayRandomRtttlFromArrayPGMAndPrintName()`. -- Printing functions have parameter (..., Stream * aSerial) to print to any serial. Call it (..., &Serial) to use standard Serial; +- Printing functions have parameter (..., Stream *aSerial) to print to any serial. Call it (..., &Serial) to use standard Serial; - `playRandomRtttlBlocking()` renamed to `playRandomRtttlSampleBlocking()` and bug fixing. ### Version 1.1.0 diff --git a/examples/LightToTone/EasyButtonAtInt01.cpp.h b/examples/LightToTone/EasyButtonAtInt01.cpp.h index e81f480..33ae590 100644 --- a/examples/LightToTone/EasyButtonAtInt01.cpp.h +++ b/examples/LightToTone/EasyButtonAtInt01.cpp.h @@ -53,15 +53,15 @@ // For external measurement of code timing //#define MEASURE_EASY_BUTTON_INTERRUPT_TIMING -#if defined(MEASURE_EASY_BUTTON_INTERRUPT_TIMING) || defined (LED_FEEDBACK_TEST) +#if defined(MEASURE_EASY_BUTTON_INTERRUPT_TIMING) || defined(LED_FEEDBACK_TEST) #include "digitalWriteFast.h" #endif #if defined(USE_BUTTON_0) -EasyButton * EasyButton::sPointerToButton0ForISR; +EasyButton *EasyButton::sPointerToButton0ForISR; #endif #if defined(USE_BUTTON_1) -EasyButton * EasyButton::sPointerToButton1ForISR; +EasyButton *EasyButton::sPointerToButton1ForISR; #endif // @formatter:off // the eclipse formatter has problems with // comments in undefined code blocks diff --git a/examples/LightToTone/EasyButtonAtInt01.h b/examples/LightToTone/EasyButtonAtInt01.h index 5142974..575b75a 100644 --- a/examples/LightToTone/EasyButtonAtInt01.h +++ b/examples/LightToTone/EasyButtonAtInt01.h @@ -37,29 +37,7 @@ #define VERSION_EASY_BUTTON "3.1.0" #define VERSION_EASY_BUTTON_MAJOR 3 #define VERSION_EASY_BUTTON_MINOR 1 - -/* - * Version 3.1.0 - 6/2020 - * - 2 sets of constructors, one for only one button used and one for the second button if two buttons used. - * - Map pin numbers for Digispark pro boards, for use with with digispark library. - * - * Version 3.0.0 - 5/2020 - * - Added button release handler and adapted examples. - * - Revoke change for "only one true result per press for checkForLongPressBlocking()". It is superseded by button release handler. - * - Support buttons which are active high by defining BUTTON_IS_ACTIVE_HIGH. - * - Improved detection of maximum bouncing period used in DebounceTest. - * - * Version 2.1.0 - 5/2020 - * - Avoid 1 ms delay for checkForLongPressBlocking() if button is not pressed. - * - Only one true result per press for checkForLongPressBlocking(). - * - * Version 2.0.0 - 1/2020 - * - Ported to ATtinyX5 and ATiny167. - * - Support also PinChangeInterrupt for button 1 on Pin PA0 to PA7 for ATtiniy87/167. - * - Long press detection support. - * - Double press detection support. - * - Renamed to EasyButtonAtInt01.cpp.h - */ +// The change log is at the bottom of the file #if defined(__AVR__) #include @@ -83,7 +61,7 @@ //#define BUTTON_IS_ACTIVE_HIGH /* * Define USE_ATTACH_INTERRUPT to force use of the arduino function attachInterrupt(). - * Needed if you get the error " multiple definition of `__vector_1'" (or `__vector_2'), because another library uses the attachInterrupt() function. + * Required if you get the error " multiple definition of `__vector_1'" (or `__vector_2'), because another library uses the attachInterrupt() function. * For one button it needs additional 160 bytes FLASH, for 2 buttons it needs additional 88 bytes. */ //#define USE_ATTACH_INTERRUPT @@ -124,9 +102,9 @@ * Activate LED_BUILTIN as long as button is pressed */ //#define LED_FEEDBACK_TEST -#if defined (LED_FEEDBACK_TEST) +#if defined(LED_FEEDBACK_TEST) # if ! defined(BUTTON_TEST_FEEDBACK_LED_PIN) -# if defined (LED_BUILTIN) +# if defined(LED_BUILTIN) # define BUTTON_TEST_FEEDBACK_LED_PIN LED_BUILTIN // if not specified, use built in led - pin 13 on Uno board # else # error "LED_FEEDBACK_TEST defined but no BUTTON_TEST_FEEDBACK_LED_PIN or LED_BUILTIN defined" @@ -146,7 +124,7 @@ //#define TRACE #ifdef TRACE -#warning "If using TRACE, the timing of the interrupt service routine changes, e.g. you will see more spikes, than expected!" +#warning If using TRACE, the timing of the interrupt service routine changes, e.g. you will see more spikes, than expected! #endif /* @@ -171,7 +149,7 @@ # if ! defined(INT1_PIN) #define INT1_PIN 3 # elif (INT1_PIN != 2) && (INT1_PIN > 5) -#error "INT1_PIN (for PCINT0 interrupt) can only be 0,1,3,4,5" +#error INT1_PIN (for PCINT0 interrupt) can only be 0,1,3,4,5 # endif #define INT1_DDR_PORT (DDRB) #define INT1_IN_PORT (PINB) @@ -218,11 +196,11 @@ #undef INT1_PIN #define INT1_PIN 6 // PA6 # else -#error "INT1_PIN (for PCINT0 interrupt) can only be 5 to 12" +#error INT1_PIN (for PCINT0 interrupt) can only be 5 to 12 # endif # else // defined(ARDUINO_AVR_DIGISPARKPRO) # if (INT1_PIN > 7) -#error "INT1_PIN (for PCINT0 interrupt) can only be 0 to 7" +#error INT1_PIN (for PCINT0 interrupt) can only be 0 to 7 # endif # endif // defined(ARDUINO_AVR_DIGISPARKPRO) #define INT1_DDR_PORT (DDRA) @@ -241,7 +219,7 @@ # if ! defined(INT1_PIN) #define INT1_PIN 3 # elif (INT1_PIN > 7) -#error "INT1_PIN (for PCINT2 interrupt) can only be Arduino pins 0 to 7 (PD0 to PD7)" +#error INT1_PIN (for PCINT2 interrupt) can only be Arduino pins 0 to 7 (PD0 to PD7) # endif #define INT1_DDR_PORT (DDRD) #define INT1_IN_PORT (PIND) @@ -255,7 +233,7 @@ #if defined(USE_BUTTON_1) && ((! defined(ISC10)) || ((defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)) && INT1_PIN != 3)) \ && ! defined(INTENTIONALLY_USE_PCI0_FOR_BUTTON1) && !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)) -#warning "Using PCINT0 interrupt for button 1" +#warning Using PCINT0 interrupt for button 1 #endif #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) @@ -357,10 +335,10 @@ class EasyButton { #endif #if defined(USE_BUTTON_0) - static EasyButton * sPointerToButton0ForISR; + static EasyButton *sPointerToButton0ForISR; #endif #if defined(USE_BUTTON_1) - static EasyButton * sPointerToButton1ForISR; + static EasyButton *sPointerToButton1ForISR; #endif }; // end of class definition @@ -380,6 +358,30 @@ void __attribute__ ((weak)) handleINT1Interrupt(); #endif #endif // defined(__AVR__) + +/* + * Version 3.1.0 - 6/2020 + * - 2 sets of constructors, one for only one button used and one for the second button if two buttons used. + * - Map pin numbers for Digispark pro boards, for use with with digispark library. + * + * Version 3.0.0 - 5/2020 + * - Added button release handler and adapted examples. + * - Revoke change for "only one true result per press for checkForLongPressBlocking()". It is superseded by button release handler. + * - Support buttons which are active high by defining BUTTON_IS_ACTIVE_HIGH. + * - Improved detection of maximum bouncing period used in DebounceTest. + * + * Version 2.1.0 - 5/2020 + * - Avoid 1 ms delay for checkForLongPressBlocking() if button is not pressed. + * - Only one true result per press for checkForLongPressBlocking(). + * + * Version 2.0.0 - 1/2020 + * - Ported to ATtinyX5 and ATiny167. + * - Support also PinChangeInterrupt for button 1 on Pin PA0 to PA7 for ATtiniy87/167. + * - Long press detection support. + * - Double press detection support. + * - Renamed to EasyButtonAtInt01.cpp.h + */ + #endif /* EASY_BUTTON_AT_INT01_H_ */ #pragma once diff --git a/examples/LightToTone/LightToTone.ino b/examples/LightToTone/LightToTone.ino index 7310323..b0b3900 100644 --- a/examples/LightToTone/LightToTone.ino +++ b/examples/LightToTone/LightToTone.ino @@ -11,7 +11,7 @@ * Copyright (C) 2018 Armin Joachimsmeyer * armin.joachimsmeyer@gmail.com * - * This file is part of PlayRttl https://github.com/ArminJo/PlayRttl. + * This file is part of PlayRttl https://github.com/ArminJo/PlayRtttl. * * PlayRttl is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -152,7 +152,7 @@ void loop() { if (tThresholdCount > 10) { // stop playing melody stopPlayRtttl(); - break; // not really needed here, since the while condition will also change because of stopPlayRtttl. + break; // not really required here, since the while condition will also change because of stopPlayRtttl. } } else { tThresholdCount = 0; diff --git a/examples/OneMelody/ATtinySerialOut.cpp b/examples/OneMelody/ATtinySerialOut.cpp index d312931..f33b830 100644 --- a/examples/OneMelody/ATtinySerialOut.cpp +++ b/examples/OneMelody/ATtinySerialOut.cpp @@ -32,7 +32,9 @@ * */ -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ + || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ + || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) #include "ATtinySerialOut.h" #include // for eeprom_read_byte() in writeString_E() @@ -62,10 +64,6 @@ #define TX_DDR DDRB #endif // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) -#if defined(Serial) -#undef Serial -#endif - void write1Start8Data1StopNoParity(uint8_t aValue); bool sUseCliSeiForWrite = true; @@ -98,7 +96,7 @@ void useCliSeiForStrings(bool aUseCliSeiForWrite) { /* * Write String residing in RAM */ -void writeString(const char * aStringPtr) { +void writeString(const char *aStringPtr) { #ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT if (sUseCliSeiForWrite) { #endif @@ -117,7 +115,7 @@ void writeString(const char * aStringPtr) { /* * Write string residing in program space (FLASH) */ -void writeString_P(const char * aStringPtr) { +void writeString_P(const char *aStringPtr) { uint8_t tChar = pgm_read_byte((const uint8_t * ) aStringPtr); // Comparing with 0xFF is safety net for wrong string pointer while (tChar != 0 && tChar != 0xFF) { @@ -137,7 +135,7 @@ void writeString_P(const char * aStringPtr) { /* * Write string residing in program space (FLASH) */ -void writeString(const __FlashStringHelper * aStringPtr) { +void writeString(const __FlashStringHelper *aStringPtr) { PGM_P tPGMStringPtr = reinterpret_cast(aStringPtr); uint8_t tChar = pgm_read_byte((const uint8_t * ) aStringPtr); // Comparing with 0xFF is safety net for wrong string pointer @@ -158,7 +156,7 @@ void writeString(const __FlashStringHelper * aStringPtr) { /* * Write string residing in EEPROM space */ -void writeString_E(const char * aStringPtr) { +void writeString_E(const char *aStringPtr) { uint8_t tChar = eeprom_read_byte((const uint8_t *) aStringPtr); // Comparing with 0xFF is safety net for wrong string pointer while (tChar != 0 && tChar != 0xFF) { @@ -175,19 +173,19 @@ void writeString_E(const char * aStringPtr) { } } -void writeStringWithoutCliSei(const char * aStringPtr) { +void writeStringWithoutCliSei(const char *aStringPtr) { while (*aStringPtr != 0) { write1Start8Data1StopNoParity(*aStringPtr++); } } -void writeStringWithCliSei(const char * aStringPtr) { +void writeStringWithCliSei(const char *aStringPtr) { while (*aStringPtr != 0) { write1Start8Data1StopNoParityWithCliSei(*aStringPtr++); } } -void writeStringSkipLeadingSpaces(const char * aStringPtr) { +void writeStringSkipLeadingSpaces(const char *aStringPtr) { // skip leading spaces while (*aStringPtr == ' ' && *aStringPtr != 0) { aStringPtr++; @@ -373,11 +371,11 @@ size_t TinySerialOut::write(uint8_t aByte) { return 1; } -void TinySerialOut::print(const char* aStringPtr) { +void TinySerialOut::print(const char *aStringPtr) { writeString(aStringPtr); } -void TinySerialOut::print(const __FlashStringHelper * aStringPtr) { +void TinySerialOut::print(const __FlashStringHelper *aStringPtr) { writeString(aStringPtr); } @@ -433,12 +431,12 @@ void TinySerialOut::println(char aChar) { println(); } -void TinySerialOut::println(const char* aStringPtr) { +void TinySerialOut::println(const char *aStringPtr) { print(aStringPtr); println(); } -void TinySerialOut::println(const __FlashStringHelper * aStringPtr) { +void TinySerialOut::println(const __FlashStringHelper *aStringPtr) { print(aStringPtr); println(); } diff --git a/examples/OneMelody/ATtinySerialOut.h b/examples/OneMelody/ATtinySerialOut.h index da5bd66..eefe970 100644 --- a/examples/OneMelody/ATtinySerialOut.h +++ b/examples/OneMelody/ATtinySerialOut.h @@ -52,15 +52,17 @@ #ifndef ATTINY_SERIAL_OUT_H_ #define ATTINY_SERIAL_OUT_H_ -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ + || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ + || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) #include -#define VERSION_ATTINY_SERIAL_OUT "1.2.0" +#define VERSION_ATTINY_SERIAL_OUT "1.2.1" #define VERSION_ATTINY_SERIAL_OUT_MAJOR 1 #define VERSION_ATTINY_SERIAL_OUT_MINOR 2 #if (F_CPU != 1000000) && (F_CPU != 8000000) && (F_CPU != 16000000) -#error "F_CPU value must be 1000000, 8000000 or 16000000." +#error F_CPU value must be 1000000, 8000000 or 16000000. #endif #if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) @@ -102,13 +104,13 @@ void write1Start8Data1StopNoParity(uint8_t aValue); void write1Start8Data1StopNoParityWithCliSei(uint8_t aValue); void writeValue(uint8_t aValue); -void writeString(const char * aStringPtr); -void writeString(const __FlashStringHelper * aStringPtr); -void writeString_P(const char * aStringPtr); -void writeString_E(const char * aStringPtr); -void writeStringWithCliSei(const char * aStringPtr); -void writeStringWithoutCliSei(const char * aStringPtr); -void writeStringSkipLeadingSpaces(const char * aStringPtr); +void writeString(const char *aStringPtr); +void writeString(const __FlashStringHelper *aStringPtr); +void writeString_P(const char *aStringPtr); +void writeString_E(const char *aStringPtr); +void writeStringWithCliSei(const char *aStringPtr); +void writeStringWithoutCliSei(const char *aStringPtr); +void writeStringSkipLeadingSpaces(const char *aStringPtr); void writeBinary(uint8_t aByte); // write direct without decoding void writeChar(uint8_t aChar); // Synonym for writeBinary @@ -141,10 +143,10 @@ class TinySerialOut // virtual functions of Print class size_t write(uint8_t aByte); - operator bool(); // To support "while (!Serial); // wait for serial port to connect. Needed for Leonardo only + operator bool(); // To support "while (!Serial); // wait for serial port to connect. Required for Leonardo only - void print(const __FlashStringHelper * aStringPtr); - void print(const char* aStringPtr); + void print(const __FlashStringHelper *aStringPtr); + void print(const char *aStringPtr); void print(char aChar); void print(uint8_t aByte, uint8_t aBase = 10); void print(int16_t aInteger, uint8_t aBase = 10); @@ -153,8 +155,8 @@ class TinySerialOut void print(uint32_t aLong, uint8_t aBase = 10); void print(double aFloat, uint8_t aDigits = 2); - void println(const char* aStringPtr); - void println(const __FlashStringHelper * aStringPtr); + void println(const char *aStringPtr); + void println(const __FlashStringHelper *aStringPtr); void println(char aChar); void println(uint8_t aByte, uint8_t aBase = 10); void println(int16_t aInteger, uint8_t aBase = 10); @@ -176,6 +178,9 @@ class TinySerialOut extern TinySerialOut SerialOut; #define Serial SerialOut #else +# if defined(Serial) +#undef Serial +# endif extern TinySerialOut Serial; #endif #define Print TinySerialOut diff --git a/examples/OneMelody/OneMelody.ino b/examples/OneMelody/OneMelody.ino index b862c66..7dc455e 100644 --- a/examples/OneMelody/OneMelody.ino +++ b/examples/OneMelody/OneMelody.ino @@ -8,7 +8,7 @@ * Copyright (C) 2019 Armin Joachimsmeyer * armin.joachimsmeyer@gmail.com * - * This file is part of PlayRttl https://github.com/ArminJo/PlayRttl. + * This file is part of PlayRttl https://github.com/ArminJo/PlayRtttl. * * PlayRttl is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/examples/PlayChristmasMelodyUSDistance/BlinkLed.cpp b/examples/PlayChristmasMelodyUSDistance/BlinkLed.cpp index edd7f4a..8f6deff 100644 --- a/examples/PlayChristmasMelodyUSDistance/BlinkLed.cpp +++ b/examples/PlayChristmasMelodyUSDistance/BlinkLed.cpp @@ -29,12 +29,12 @@ /* * The simple blocking variant */ -void blinkLEDBlocking(uint8_t aLedPin, uint16_t aDelay, uint8_t aRepetitions) { - for (int i = 0; i < aRepetitions; ++i) { +void blinkLEDBlocking(uint8_t aLedPin, uint8_t aBlinkCount, uint16_t aDelayMillis) { + for (int i = 0; i < aBlinkCount; ++i) { digitalWrite(aLedPin, HIGH); - delay(aDelay); + delay(aDelayMillis); digitalWrite(aLedPin, LOW); - delay(aDelay); + delay(aDelayMillis); } } @@ -44,9 +44,9 @@ BlinkLed::BlinkLed(uint8_t aLedPin) { setOnOffTime(1000, 1000); } -BlinkLed::BlinkLed(uint8_t aLedPin, bool aInitState, unsigned int aOnTime, unsigned int aOffTime) { +BlinkLed::BlinkLed(uint8_t aLedPin, bool aInitState, unsigned int aOnTimeMillis, unsigned int aOffTimeMillis) { init(aLedPin, aInitState); - setOnOffTime(aOnTime, aOffTime); + setOnOffTime(aOnTimeMillis, aOffTimeMillis); } void BlinkLed::init(uint8_t aLedPin, bool aInitState) { @@ -59,9 +59,9 @@ void BlinkLed::init(uint8_t aLedPin, bool aInitState) { /* * No count specified here, so set to BLINK_LED_FOREVER */ -void BlinkLed::setOnOffTime(unsigned int aOnTime, unsigned int aOffTime) { - onDelay = aOnTime; - offDelay = aOffTime; +void BlinkLed::setOnOffTime(unsigned int aOnTimeMillis, unsigned int aOffTimeMillis) { + onDelayMillis = aOnTimeMillis; + offDelayMillis = aOffTimeMillis; } // must be called continuously in loop() @@ -73,7 +73,7 @@ void BlinkLed::update() { if (state) { // check for on time gone to change state - if ((millis() - lastUpdate >= onDelay)) { + if ((millis() - lastUpdateMillis >= onDelayMillis)) { toggle(); // count blinks if (numberOfBlinks > 0) { @@ -84,14 +84,14 @@ void BlinkLed::update() { } } } - } else if ((millis() - lastUpdate >= offDelay)) { + } else if ((millis() - lastUpdateMillis >= offDelayMillis)) { toggle(); } } -void BlinkLed::start(signed int aBlinkCount, unsigned int aOnTime, unsigned int aOffTime) { - onDelay = aOnTime; - offDelay = aOffTime; +void BlinkLed::start(signed int aBlinkCount, unsigned int aOnTimeMillis, unsigned int aOffTimeMillis) { + onDelayMillis = aOnTimeMillis; + offDelayMillis = aOffTimeMillis; start(aBlinkCount); } @@ -99,7 +99,7 @@ void BlinkLed::start(signed int aBlinkCount, unsigned int aOnTime, unsigned int * set to 50% duty cycle */ void BlinkLed::start(signed int aBlinkCount, unsigned int aPeriod) { - onDelay = offDelay = aPeriod / 2; + onDelayMillis = offDelayMillis = aPeriod / 2; start(aBlinkCount); } @@ -116,7 +116,7 @@ void BlinkLed::start() { digitalWrite(pin, HIGH); state = true; enabled = true; - lastUpdate = millis(); + lastUpdateMillis = millis(); } /* @@ -133,8 +133,8 @@ void BlinkLed::blink(signed int aBlinkCount, unsigned int aPeriod) { /* * No count specified here, so set to BLINK_LED_FOREVER */ -void BlinkLed::startWithOnOffTime(unsigned int aOnTime, unsigned int aOffTime) { - start(BLINK_LED_FOREVER, aOnTime, aOffTime); +void BlinkLed::startWithOnOffTime(unsigned int aOnTimeMillis, unsigned int aOffTimeMillis) { + start(BLINK_LED_FOREVER, aOnTimeMillis, aOffTimeMillis); } /* @@ -148,17 +148,17 @@ void BlinkLed::startWithPeriod(unsigned int aPeriod) { * set to 50% duty cycle */ void BlinkLed::startWithFrequency(float aFrequency) { - offDelay = onDelay = 500.0 / aFrequency; + offDelayMillis = onDelayMillis = 500.0 / aFrequency; start(BLINK_LED_FOREVER); } -void BlinkLed::startWithOnTime(unsigned int aOnTime) { - onDelay = aOnTime; +void BlinkLed::startWithOnTime(unsigned int aOnTimeMillis) { + onDelayMillis = aOnTimeMillis; start(); } -void BlinkLed::startWithOffTime(unsigned int aOffTime) { - offDelay = aOffTime; +void BlinkLed::startWithOffTime(unsigned int aOffTimeMillis) { + offDelayMillis = aOffTimeMillis; start(); } @@ -166,7 +166,7 @@ void BlinkLed::startWithOffTime(unsigned int aOffTime) { void BlinkLed::toggle() { state = !state; digitalWrite(pin, state); - lastUpdate = millis(); + lastUpdateMillis = millis(); } // Force ON and disable blink diff --git a/examples/PlayChristmasMelodyUSDistance/BlinkLed.h b/examples/PlayChristmasMelodyUSDistance/BlinkLed.h index 133cb61..6eba2b4 100644 --- a/examples/PlayChristmasMelodyUSDistance/BlinkLed.h +++ b/examples/PlayChristmasMelodyUSDistance/BlinkLed.h @@ -27,7 +27,7 @@ #define BLINK_LED_H_ // The simple blocking variant -void blinkLEDBlocking(uint8_t aLedPin, uint16_t aDelay, uint8_t aRepetitions); +void blinkLEDBlocking(uint8_t aLedPin, uint8_t aBlinkCount, uint16_t aDelayMillis); #define BLINK_LED_FOREVER -1 class BlinkLed { @@ -35,24 +35,24 @@ class BlinkLed { // constructors and initialization BlinkLed(); BlinkLed(uint8_t aLedPin); - BlinkLed(uint8_t aLedPin, bool aInitState, unsigned int aOnTime, unsigned int aOffTime); + BlinkLed(uint8_t aLedPin, bool aInitState, unsigned int aOnTimeMillis, unsigned int aOffTime); void init(uint8_t aLedPin, bool aInitState); - void setOnOffTime(unsigned int aOnTime, unsigned int aOffTime); + void setOnOffTime(unsigned int aOnTimeMillis, unsigned int aOffTime); void blink(signed int aBlinkCount, unsigned int aPeriod); // blocking version void update(); // must be called continuously in loop() // Blinking ends after aBlinkCount - void start(signed int aBlinkCount, unsigned int aOnTime, unsigned int aOffTime); // start + void start(signed int aBlinkCount, unsigned int aOnTimeMillis, unsigned int aOffTime); // start void start(signed int aBlinkCount, unsigned int aPeriod); // start void start(signed int aBlinkCount); // use old values for on and off time void start(); // Blinking goes forever until off() - void startWithOnOffTime(unsigned int aOnTime, unsigned int aOffTime); + void startWithOnOffTime(unsigned int aOnTimeMillis, unsigned int aOffTime); void startWithPeriod(unsigned int aPeriod); - void startWithOnTime(unsigned int aOnTime); + void startWithOnTime(unsigned int aOnTimeMillis); void startWithOffTime(unsigned int aOffTime); void startWithFrequency(float aFrequency); // set to 50% duty cycle @@ -66,9 +66,9 @@ class BlinkLed { uint8_t pin; // Pin number connected to LED anode volatile bool state; // LED state (volatile so that the compiler doesn't optimize this variable into some constant) signed int numberOfBlinks; // Negative values mean forever - unsigned int onDelay; // On time in milliseconds - unsigned int offDelay; // Off time in milliseconds - unsigned long lastUpdate; // The last time (millis) LED was updated + unsigned int onDelayMillis; // On time in milliseconds + unsigned int offDelayMillis; // Off time in milliseconds + unsigned long lastUpdateMillis; // The last time (millis) LED was updated bool enabled = true; // LED enabled/disabled state }; diff --git a/examples/PlayChristmasMelodyUSDistance/HCSR04.cpp b/examples/PlayChristmasMelodyUSDistance/HCSR04.cpp index c3badb5..2e9d3d9 100644 --- a/examples/PlayChristmasMelodyUSDistance/HCSR04.cpp +++ b/examples/PlayChristmasMelodyUSDistance/HCSR04.cpp @@ -4,6 +4,8 @@ * US Sensor (HC-SR04) functions. * The non blocking functions are using pin change interrupts and need the PinChangeInterrupt library to be installed. * + * 58,23 us per centimeter and 17,17 cm/ms (forth and back). + * * Supports 1 Pin mode as you get on the HY-SRF05 if you connect OUT to ground. * You can modify the HC-SR04 modules to 1 Pin mode by: * Old module with 3 16 pin chips: Connect Trigger and Echo direct or use a resistor < 4.7 kOhm. @@ -85,6 +87,7 @@ void initUSDistancePin(uint8_t aTriggerOutEchoInPin) { /* * Start of standard blocking implementation using pulseInLong() since PulseIn gives wrong (too small) results :-( + * @return 0 if uninitialized or timeout happened */ unsigned int getUSDistance(unsigned int aTimeoutMicros) { if (sHCSR04Mode == HCSR04_MODE_UNITITIALIZED) { @@ -93,7 +96,6 @@ unsigned int getUSDistance(unsigned int aTimeoutMicros) { // need minimum 10 usec Trigger Pulse digitalWrite(sTriggerOutPin, HIGH); - // If in if (sHCSR04Mode == HCSR04_MODE_USE_1_PIN) { // do it AFTER digitalWrite to avoid spurious triggering by just switching pin to output @@ -126,6 +128,7 @@ unsigned int getUSDistance(unsigned int aTimeoutMicros) { * * Use pulseInLong, this uses micros() as counter, relying on interrupts being enabled, which is not disturbed by (e.g. the 1 ms timer) interrupts. * Only thing is that the pulse ends when we are in an interrupt routine, thus prolonging the measured pulse duration. + * I measured 6 us for the millis() and 14 to 20 us for the Servo signal generating interrupt. This is equivalent to around 1 to 3 mm distance. * Alternatively we can use pulseIn() in a noInterrupts() context, but this will effectively stop the millis() timer for duration of pulse / or timeout. */ #if ! defined(__AVR__) || defined(TEENSYDUINO) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) @@ -135,10 +138,6 @@ unsigned int getUSDistance(unsigned int aTimeoutMicros) { #else unsigned long tUSPulseMicros = pulseInLong(tEchoInPin, HIGH, aTimeoutMicros); #endif - if (tUSPulseMicros == 0) { -// timeout happened -> change value to timeout value. This eases comparison with different distances. - tUSPulseMicros = aTimeoutMicros; - } return tUSPulseMicros; } @@ -149,18 +148,13 @@ unsigned int getCentimeterFromUSMicroSeconds(unsigned int aDistanceMicros) { /* * @return Distance in centimeter @20 degree (time in us/58.25) - * aTimeoutMicros/58.25 if timeout happens - * 0 if pins are not initialized + * 0 if timeout or pins are not initialized + * * timeout of 5825 micros is equivalent to 1 meter * Default timeout of 20000 micro seconds is 3.43 meter */ unsigned int getUSDistanceAsCentiMeter(unsigned int aTimeoutMicros) { - unsigned int tDistanceMicros = getUSDistance(aTimeoutMicros); - if (tDistanceMicros == 0) { -// timeout happened - tDistanceMicros = aTimeoutMicros; - } - return (getCentimeterFromUSMicroSeconds(tDistanceMicros)); + return (getCentimeterFromUSMicroSeconds(getUSDistance(aTimeoutMicros))); } // 58,23 us per centimeter (forth and back) @@ -170,6 +164,10 @@ unsigned int getUSDistanceAsCentiMeterWithCentimeterTimeout(unsigned int aTimeou return getUSDistanceAsCentiMeter(tTimeoutMicros); } +/* + * Trigger US sensor as fast as sensible if called in a loop to test US devices. + * trigger pulse is equivalent to 10 cm and then we wait for 20 ms / 3.43 meter + */ void testUSSensor(uint16_t aSecondsToTest) { for (long i = 0; i < aSecondsToTest * 50; ++i) { digitalWrite(sTriggerOutPin, HIGH); diff --git a/examples/PlayChristmasMelodyUSDistance/HCSR04.h b/examples/PlayChristmasMelodyUSDistance/HCSR04.h index 8dcdb05..ebb359d 100644 --- a/examples/PlayChristmasMelodyUSDistance/HCSR04.h +++ b/examples/PlayChristmasMelodyUSDistance/HCSR04.h @@ -35,10 +35,11 @@ #include #define US_DISTANCE_DEFAULT_TIMEOUT_MICROS 20000 +#define US_DISTANCE_DEFAULT_TIMEOUT_CENTIMETER 343 // Timeout of 20000L is 3.43 meter + #define US_DISTANCE_TIMEOUT_MICROS_FOR_1_METER 5825 // Timeout of 5825 is 1 meter #define US_DISTANCE_TIMEOUT_MICROS_FOR_2_METER 11650 // Timeout of 11650 is 2 meter #define US_DISTANCE_TIMEOUT_MICROS_FOR_3_METER 17475 // Timeout of 17475 is 3 meter -#define US_DISTANCE_DEFAULT_TIMEOUT_CENTIMETER 343 // Timeout of 20000L is 3.43 meter void initUSDistancePins(uint8_t aTriggerOutPin, uint8_t aEchoInPin = 0); void initUSDistancePin(uint8_t aTriggerOutEchoInPin); // Using this determines one pin mode diff --git a/examples/PlayChristmasMelodyUSDistance/PlayChristmasMelodyUSDistance.ino b/examples/PlayChristmasMelodyUSDistance/PlayChristmasMelodyUSDistance.ino index 8b76952..c3867cd 100644 --- a/examples/PlayChristmasMelodyUSDistance/PlayChristmasMelodyUSDistance.ino +++ b/examples/PlayChristmasMelodyUSDistance/PlayChristmasMelodyUSDistance.ino @@ -6,7 +6,7 @@ * Copyright (C) 2019 Armin Joachimsmeyer * armin.joachimsmeyer@gmail.com * - * This file is part of PlayRttl https://github.com/ArminJo/PlayRttl. + * This file is part of PlayRttl https://github.com/ArminJo/PlayRtttl. * * PlayRttl is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,7 +54,7 @@ EasyButton Button0AtPB6; #define NUMBER_OF_CONSECUTIVE_OUT_RANGE_READINGS 5 #define DELAY_MILLIS_FOR_OUT_RANGE_READING 1000 -#define PIN_SPEAKER 3 +#define PIN_BUZZER 3 #define PIN_ECHO_IN 4 #define PIN_TRIGGER_OUT 5 @@ -107,11 +107,11 @@ void loop() { /* * Output distance with talkie */ - if (tCentimeter < 300) { - sayQNumber(&Voice, tCentimeter); - } else { + if (tCentimeter == 0) { Voice.sayQ(sp2_TIME); Voice.sayQ(sp2_OUT); + } else { + sayQNumber(&Voice, tCentimeter); } Voice.wait(); } @@ -119,7 +119,7 @@ void loop() { if (tCentimeter > MINIMUM_DISTANCE_CENTIMETER && tCentimeter < MAXIMUM_DISTANCE_CENTIMETER) { sInRangeCounter++; - tRandomSeed +=tCentimeter; + tRandomSeed += tCentimeter; if (sInRangeCounter >= NUMBER_OF_CONSECUTIVE_IN_RANGE_READINGS) { /* * Now an object is for a longer time in the right range. @@ -143,7 +143,12 @@ void loop() { /* * Output distance with talkie */ - sayQNumber(&Voice, tCentimeter); + if (tCentimeter > 0) { + sayQNumber(&Voice, tCentimeter); + } else { + Voice.sayQ(sp2_TIME); + Voice.sayQ(sp2_OUT); + } Voice.wait(); } #endif @@ -179,7 +184,7 @@ void playRandomSongAndBlink() { char StringBuffer[16]; Serial.println(); Serial.print("Now playing: "); - startPlayRandomRtttlFromArrayPGM(PIN_SPEAKER, RTTTLChristmasMelodies, ARRAY_SIZE_CHRISTMAS_MELODIES, StringBuffer, + startPlayRandomRtttlFromArrayPGM(PIN_BUZZER, RTTTLChristmasMelodies, ARRAY_SIZE_CHRISTMAS_MELODIES, StringBuffer, sizeof(StringBuffer)); Serial.println(StringBuffer); diff --git a/examples/RandomMelody/ATtinySerialOut.cpp b/examples/RandomMelody/ATtinySerialOut.cpp index d312931..f33b830 100644 --- a/examples/RandomMelody/ATtinySerialOut.cpp +++ b/examples/RandomMelody/ATtinySerialOut.cpp @@ -32,7 +32,9 @@ * */ -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ + || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ + || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) #include "ATtinySerialOut.h" #include // for eeprom_read_byte() in writeString_E() @@ -62,10 +64,6 @@ #define TX_DDR DDRB #endif // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) -#if defined(Serial) -#undef Serial -#endif - void write1Start8Data1StopNoParity(uint8_t aValue); bool sUseCliSeiForWrite = true; @@ -98,7 +96,7 @@ void useCliSeiForStrings(bool aUseCliSeiForWrite) { /* * Write String residing in RAM */ -void writeString(const char * aStringPtr) { +void writeString(const char *aStringPtr) { #ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT if (sUseCliSeiForWrite) { #endif @@ -117,7 +115,7 @@ void writeString(const char * aStringPtr) { /* * Write string residing in program space (FLASH) */ -void writeString_P(const char * aStringPtr) { +void writeString_P(const char *aStringPtr) { uint8_t tChar = pgm_read_byte((const uint8_t * ) aStringPtr); // Comparing with 0xFF is safety net for wrong string pointer while (tChar != 0 && tChar != 0xFF) { @@ -137,7 +135,7 @@ void writeString_P(const char * aStringPtr) { /* * Write string residing in program space (FLASH) */ -void writeString(const __FlashStringHelper * aStringPtr) { +void writeString(const __FlashStringHelper *aStringPtr) { PGM_P tPGMStringPtr = reinterpret_cast(aStringPtr); uint8_t tChar = pgm_read_byte((const uint8_t * ) aStringPtr); // Comparing with 0xFF is safety net for wrong string pointer @@ -158,7 +156,7 @@ void writeString(const __FlashStringHelper * aStringPtr) { /* * Write string residing in EEPROM space */ -void writeString_E(const char * aStringPtr) { +void writeString_E(const char *aStringPtr) { uint8_t tChar = eeprom_read_byte((const uint8_t *) aStringPtr); // Comparing with 0xFF is safety net for wrong string pointer while (tChar != 0 && tChar != 0xFF) { @@ -175,19 +173,19 @@ void writeString_E(const char * aStringPtr) { } } -void writeStringWithoutCliSei(const char * aStringPtr) { +void writeStringWithoutCliSei(const char *aStringPtr) { while (*aStringPtr != 0) { write1Start8Data1StopNoParity(*aStringPtr++); } } -void writeStringWithCliSei(const char * aStringPtr) { +void writeStringWithCliSei(const char *aStringPtr) { while (*aStringPtr != 0) { write1Start8Data1StopNoParityWithCliSei(*aStringPtr++); } } -void writeStringSkipLeadingSpaces(const char * aStringPtr) { +void writeStringSkipLeadingSpaces(const char *aStringPtr) { // skip leading spaces while (*aStringPtr == ' ' && *aStringPtr != 0) { aStringPtr++; @@ -373,11 +371,11 @@ size_t TinySerialOut::write(uint8_t aByte) { return 1; } -void TinySerialOut::print(const char* aStringPtr) { +void TinySerialOut::print(const char *aStringPtr) { writeString(aStringPtr); } -void TinySerialOut::print(const __FlashStringHelper * aStringPtr) { +void TinySerialOut::print(const __FlashStringHelper *aStringPtr) { writeString(aStringPtr); } @@ -433,12 +431,12 @@ void TinySerialOut::println(char aChar) { println(); } -void TinySerialOut::println(const char* aStringPtr) { +void TinySerialOut::println(const char *aStringPtr) { print(aStringPtr); println(); } -void TinySerialOut::println(const __FlashStringHelper * aStringPtr) { +void TinySerialOut::println(const __FlashStringHelper *aStringPtr) { print(aStringPtr); println(); } diff --git a/examples/RandomMelody/ATtinySerialOut.h b/examples/RandomMelody/ATtinySerialOut.h index da5bd66..eefe970 100644 --- a/examples/RandomMelody/ATtinySerialOut.h +++ b/examples/RandomMelody/ATtinySerialOut.h @@ -52,15 +52,17 @@ #ifndef ATTINY_SERIAL_OUT_H_ #define ATTINY_SERIAL_OUT_H_ -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ + || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ + || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) #include -#define VERSION_ATTINY_SERIAL_OUT "1.2.0" +#define VERSION_ATTINY_SERIAL_OUT "1.2.1" #define VERSION_ATTINY_SERIAL_OUT_MAJOR 1 #define VERSION_ATTINY_SERIAL_OUT_MINOR 2 #if (F_CPU != 1000000) && (F_CPU != 8000000) && (F_CPU != 16000000) -#error "F_CPU value must be 1000000, 8000000 or 16000000." +#error F_CPU value must be 1000000, 8000000 or 16000000. #endif #if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) @@ -102,13 +104,13 @@ void write1Start8Data1StopNoParity(uint8_t aValue); void write1Start8Data1StopNoParityWithCliSei(uint8_t aValue); void writeValue(uint8_t aValue); -void writeString(const char * aStringPtr); -void writeString(const __FlashStringHelper * aStringPtr); -void writeString_P(const char * aStringPtr); -void writeString_E(const char * aStringPtr); -void writeStringWithCliSei(const char * aStringPtr); -void writeStringWithoutCliSei(const char * aStringPtr); -void writeStringSkipLeadingSpaces(const char * aStringPtr); +void writeString(const char *aStringPtr); +void writeString(const __FlashStringHelper *aStringPtr); +void writeString_P(const char *aStringPtr); +void writeString_E(const char *aStringPtr); +void writeStringWithCliSei(const char *aStringPtr); +void writeStringWithoutCliSei(const char *aStringPtr); +void writeStringSkipLeadingSpaces(const char *aStringPtr); void writeBinary(uint8_t aByte); // write direct without decoding void writeChar(uint8_t aChar); // Synonym for writeBinary @@ -141,10 +143,10 @@ class TinySerialOut // virtual functions of Print class size_t write(uint8_t aByte); - operator bool(); // To support "while (!Serial); // wait for serial port to connect. Needed for Leonardo only + operator bool(); // To support "while (!Serial); // wait for serial port to connect. Required for Leonardo only - void print(const __FlashStringHelper * aStringPtr); - void print(const char* aStringPtr); + void print(const __FlashStringHelper *aStringPtr); + void print(const char *aStringPtr); void print(char aChar); void print(uint8_t aByte, uint8_t aBase = 10); void print(int16_t aInteger, uint8_t aBase = 10); @@ -153,8 +155,8 @@ class TinySerialOut void print(uint32_t aLong, uint8_t aBase = 10); void print(double aFloat, uint8_t aDigits = 2); - void println(const char* aStringPtr); - void println(const __FlashStringHelper * aStringPtr); + void println(const char *aStringPtr); + void println(const __FlashStringHelper *aStringPtr); void println(char aChar); void println(uint8_t aByte, uint8_t aBase = 10); void println(int16_t aInteger, uint8_t aBase = 10); @@ -176,6 +178,9 @@ class TinySerialOut extern TinySerialOut SerialOut; #define Serial SerialOut #else +# if defined(Serial) +#undef Serial +# endif extern TinySerialOut Serial; #endif #define Print TinySerialOut diff --git a/examples/RandomMelody/RandomMelody.ino b/examples/RandomMelody/RandomMelody.ino index 52c20fd..281c975 100644 --- a/examples/RandomMelody/RandomMelody.ino +++ b/examples/RandomMelody/RandomMelody.ino @@ -8,7 +8,7 @@ * Copyright (C) 2019 Armin Joachimsmeyer * armin.joachimsmeyer@gmail.com * - * This file is part of PlayRttl https://github.com/ArminJo/PlayRttl. + * This file is part of PlayRttl https://github.com/ArminJo/PlayRtttl. * * PlayRttl is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/examples/ReactionTimeTestGame/Breadboard.h b/examples/ReactionTimeTestGame/Breadboard.h new file mode 100644 index 0000000..b225b08 --- /dev/null +++ b/examples/ReactionTimeTestGame/Breadboard.h @@ -0,0 +1,65 @@ +/* + * Breadboard.h + * + * Pin mappings for the example breadboard + * + * Copyright 2019 Armin Joachimsmeyer + * This code is released under GPLv3 license. + * + * This file is part of Arduino-Lessons-for-School https://github.com/ArminJo/Arduino-Lessons-for-School. + * + * ServoEasing 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. + */ + +#ifndef BREADBOARD_H_ +#define BREADBOARD_H_ + +#define PIN_GREEN_LED 2 +#define PIN_YELLOW_LED 3 +#define PIN_RED_LED 4 + +#define PIN_RIGHT_LED PIN_GREEN_LED +#define PIN_MIDDLE_LED PIN_YELLOW_LED +#define PIN_LEFT_LED PIN_RED_LED + +#define PIN_RIGHT_BUTTON A1 +#define PIN_LEFT_BUTTON A4 + +#define PIN_LASER 5 +#define PIN_SERVO_HORIZONTAL 6 +#define PIN_SERVO_VERTICAL 7 +#define PIN_NEOPIXEL 8 +#define PIN_TRIGGER_OUT 9 +#define PIN_ECHO_IN 10 +#define PIN_BUZZER 11 +#define PIN_INTERNAL_LED 13 // aka LED_BUILTIN + +#define PIN_POTENTIOMETER A0 +#define PIN_LDR A5 + +/* + * Set output pins to OUTPUT and button pins to INPUT_PULLUP + */ +void initBreadboardPins() { + + // Enable output on the LED pins + pinMode(PIN_RED_LED, OUTPUT); + pinMode(PIN_YELLOW_LED, OUTPUT); + pinMode(PIN_GREEN_LED, OUTPUT); + + // Prepare for buttons at the pins + pinMode(PIN_RIGHT_BUTTON, INPUT_PULLUP); + pinMode(PIN_LEFT_BUTTON, INPUT_PULLUP); + + pinMode(PIN_LASER, OUTPUT); + pinMode(PIN_TRIGGER_OUT, OUTPUT); +// Is done by tone() +// pinMode(PIN_BUZZER, OUTPUT); + pinMode(LED_BUILTIN, OUTPUT); + +} + +#endif /* BREADBOARD_H_ */ diff --git a/examples/ReactionTimeTestGame/MultiFuncShield.cpp b/examples/ReactionTimeTestGame/MultiFuncShield.cpp new file mode 100644 index 0000000..a4d7fae --- /dev/null +++ b/examples/ReactionTimeTestGame/MultiFuncShield.cpp @@ -0,0 +1,1341 @@ +#include "MultiFuncShield.h" + +#define BUTTON_SAMPLE_INTERVAL_SCALE 20 +#define BUTTON_SAMPLE_INTERVAL (1000 / BUTTON_SAMPLE_INTERVAL_SCALE) + + +MultiFuncShield MFS; + +// Display specific variables + +const byte LED[] = {LED_1_PIN, LED_2_PIN, LED_3_PIN, LED_4_PIN}; + +/* Segment byte maps for numbers 0 to 9 */ +const byte SEGMENT_MAP_DIGIT[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90}; +/* Segment byte maps for alpha a-z */ +const byte SEGMENT_MAP_ALPHA[] = {136, 131, 167, 161, 134, 142, 144, 139 ,207, 241, 182, 199, 182, 171, 163, 140, 152, 175, 146, 135, 227, 182, 182, 182, 145, 182}; + +/* Byte maps to select digit 1 to 4 */ +const byte SEGMENT_SELECT[] = {0xF1,0xF2,0xF4,0xF8}; +const char DISPLAY_OVERFLOW_ERROR = 'E'; + +const byte BLINK_ON_COUNT = 65; +const byte BLINK_OFF_COUNT = 20; + +volatile byte displayMemory[4] = {255,255,255,255}; +byte displayTimerScaler = 4; + + +// Sonar ranger specific variables + +int sonarData[9]; +byte sonarDataIndex = 0; + +// LM35 specific variables + +int lm35Data[8]; +byte lm35DataIndex = 0; + +// Misc methods and functions. +void isrWrapper (); +void WriteValueToSegment(byte Segment, byte Value); +byte AsciiToSegmentValue (byte ascii); +void writeBeeper (byte value); +byte readButton (byte btnIndex); +void writeLed(byte ledIdx, byte value); + +int MedianOf9(int s0, int s1, int s2, int s3, int s4, int s5, int s6, int s7, int s8); +int MedianOf5(int s0, int s1, int s2, int s3, int s4); + +// Pulse in counter port specifics. +uint8_t pulseInBit; +uint8_t pulseInPort; + + +void initShield() +{ + /* Set each LED pin to outputs */ + pinMode(LED[0], OUTPUT); + pinMode(LED[1], OUTPUT); + pinMode(LED[2], OUTPUT); + pinMode(LED[3], OUTPUT); + + /* Turn all the LED's off */ + digitalWrite(LED[0], HIGH); + digitalWrite(LED[1], HIGH); + digitalWrite(LED[2], HIGH); + digitalWrite(LED[3], HIGH); + + /* Set Segment display DIO pins to outputs */ + pinMode(LATCH_PIN,OUTPUT); + pinMode(CLK_PIN,OUTPUT); + pinMode(DATA_PIN,OUTPUT); + + WriteValueToSegment(0,255); + + /* Set the buzzer pin to an output and turn the buzzer off */ + pinMode(BEEPER_PIN, OUTPUT); + digitalWrite(BEEPER_PIN, HIGH); +} + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::initialize(TimerOne *timer1Instance) +{ + initShield(); + + timer1 = timer1Instance; + timer1->attachInterrupt(isrWrapper, 1000); // effectively, 1000 times per second +} + + +void MultiFuncShield::initialize() +{ + initShield(); +} + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::initSonar(byte level) +{ + sonarSmoothingLevel = level; + sonarDataIndex = 0; + + for (int i=0; i < 9; i++) + { + sonarData[i] = 0; + } +} + + +// ---------------------------------------------------------------------------------------------------- +unsigned int MultiFuncShield::getSonarDataCm(byte triggerPin, byte echoPin) +{ + uint8_t bit = digitalPinToBitMask(echoPin); + uint8_t port = digitalPinToPort(echoPin); + uint8_t stateMask = (HIGH ? bit : 0); + + noInterrupts(); + digitalWrite(triggerPin, LOW); //Low, high and low level take a short time to TrigPin pulse + delayMicroseconds(2); + digitalWrite(triggerPin, HIGH); + delayMicroseconds(10); + digitalWrite(triggerPin, LOW); + interrupts(); + + // wait for any previous pulse to end + while ((*portInputRegister(port) & bit) == stateMask) ; + + // wait for the pulse to start + while ((*portInputRegister(port) & bit) != stateMask) ; + + unsigned long timeStart = micros(); + // wait for the pulse to stop + while ((*portInputRegister(port) & bit) == stateMask) ; + + sonarData [sonarDataIndex] = ((micros() - timeStart) * 141) >> 13; // (value * 1.00) / 58 + + int medianReading; + + if (sonarSmoothingLevel == SMOOTHING_NONE) + { + medianReading = sonarData [sonarDataIndex]; + } + else if (sonarSmoothingLevel == SMOOTHING_MODERATE) + { + sonarDataIndex++; + if (sonarDataIndex >= 5) + { + sonarDataIndex = 0; + } + medianReading = MedianOf5(sonarData[0], sonarData[1], sonarData[2], sonarData[3], sonarData[4]); + } + else + { + sonarDataIndex++; + if (sonarDataIndex >= 9) + { + sonarDataIndex = 0; + } + medianReading = MedianOf9(sonarData[0], sonarData[1], sonarData[2], sonarData[3], sonarData[4], sonarData[5], sonarData[6], sonarData[7], sonarData[8]); + } + + return medianReading; +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::initLM35(byte level) +{ + lm35SmoothingLevel = level; + lm35DataIndex = 0; + + for (int i=0; i < 8; i++) + { + lm35Data[i] = 0; + } +} + + +// ---------------------------------------------------------------------------------------------------- +int MultiFuncShield::getLM35Data() +{ + + lm35Data [lm35DataIndex] = analogRead(LM35_PIN); + + int reading =0; + + if (lm35SmoothingLevel == SMOOTHING_NONE) + { + reading = lm35Data [lm35DataIndex]; + } + else if (lm35SmoothingLevel == SMOOTHING_MODERATE) + { + lm35DataIndex++; + if (lm35DataIndex >= 4) + { + lm35DataIndex = 0; + } + + for (int i=0; i<4; i++) + { + reading = reading + lm35Data[i]; + } + } + else + { + lm35DataIndex++; + if (lm35DataIndex >= 8) + { + lm35DataIndex = 0; + } + + for (int i=0; i<8; i++) + { + reading = reading + lm35Data[i]; + } + } + + if (lm35SmoothingLevel == SMOOTHING_NONE) + { + return ((unsigned long)1250 * reading) >> 8;; + } + else if (lm35SmoothingLevel == SMOOTHING_MODERATE) + { + return ((unsigned long)1250 * reading) >> 10; + } + else + { + return ((unsigned long)1250 * reading) >> 11; + } +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::initPulseInCounter(byte pin, unsigned int timeOut, byte trigger) +{ + pulseInWriteInProgress = 1; + + pulseInBit = digitalPinToBitMask(pin); + pulseInPort = digitalPinToPort(pin); + + pulseInTimeOut = timeOut; + pulseInPin = pin; + pulseInPeriodCounter =timeOut; + pulseInPeriod_volatile =0; + pulseInPeriod_safe =0; + pulseInState =0; + pulseInTrigger = trigger; + pulseInTotalCount_volatile = 0; + pulseInTotalCount_safe = 0; + + pulseInWriteInProgress = 0; + pulseInEnabled = true; +} + +void MultiFuncShield::disablePulseInCounter () +{ + pulseInEnabled = false; +} + +// ---------------------------------------------------------------------------------------------------- +unsigned int MultiFuncShield::getPulseInPeriod() +{ + unsigned int period; + + pulseInReadInProgress = 1; + period = pulseInPeriod_safe; + pulseInReadInProgress =0; + + return period; +} + + +// ---------------------------------------------------------------------------------------------------- +unsigned long MultiFuncShield::getPulseInTotalCount() +{ + unsigned long count; + + pulseInReadInProgress = 1; + count = pulseInTotalCount_safe; + pulseInReadInProgress =0; + + return count; +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::resetPulseInTotalCount() +{ + pulseInWriteInProgress = 1; + pulseInTotalCount_volatile = 0; + pulseInTotalCount_safe = 0; + pulseInWriteInProgress = 0; +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::setPulseInTimeOut(unsigned int timeOut) +{ + pulseInWriteInProgress = 1; + pulseInTimeOut = timeOut; + pulseInWriteInProgress = 0; +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::queueButton (byte button) +{ + if (buttonBufferCount <= sizeof (buttonBuffer)) + { + buttonBuffer [button_write_pos] = button; + buttonBufferCount++; + button_write_pos++; + + if (button_write_pos >= sizeof (buttonBuffer)) + { + button_write_pos = 0; + } + } +} + + +// ---------------------------------------------------------------------------------------------------- +byte MultiFuncShield::getButton () +{ + byte button = 0; + + if (buttonBufferCount > 0) + { + button = buttonBuffer [button_read_pos]; + buttonBufferCount--; + button_read_pos++; + + if (button_read_pos >= sizeof (buttonBuffer)) + { + button_read_pos = 0; + } + } + + return button; +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::beep(unsigned int onPeriod, unsigned int offPeriod, byte cycles, unsigned int loopCycles, unsigned int loopDelayPeriod) +{ + cycles = cycles == 0 ? 1 : cycles; + + beeperModifyInProgress = 1; // must do this first before changing volatile fields. + + if (onPeriod == 0) + { + digitalWrite(BEEPER_PIN, 1); // turn off beeper immediately + } + + beeperState = 0; + beeperOnPeriodReloadValue = onPeriod * 10; + beeperOffPeriodReloadValue = offPeriod * 10; + beeperPeriodCounter = onPeriod * 10; + + beeperCycleReloadValue = cycles; + beeperCycleCounter = cycles; + + beeperLoopCycleCounter = loopCycles; + beeperLoopDelayPeriodReloadValue = loopDelayPeriod * 10; + + beeperModifyInProgress = 0; // must do this last. +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::setBeepOffPeriod(unsigned int offPeriod) +{ + beeperModifyInProgress = 1; // must do this first before changing volatile fields. + + if (beeperState == 1) + { + if (offPeriod * 10 < beeperPeriodCounter) + { + beeperPeriodCounter = offPeriod * 10; + } + } + + beeperOffPeriodReloadValue = offPeriod * 10; + beeperModifyInProgress = 0; // must do this last. +} + + +// ---------------------------------------------------------------------------------------------------- +//void MultiFuncShield::setLedControlMask(byte controlMask) +//{ +// ledControlMask = controlMask; +//} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::writeLeds(byte leds, byte lit) +{ + if (lit) + { + ledState = ledState | leds; + //ledControlMask = ledControlMask | leds; + } + else + { + ledState = ledState & (255 - leds); + //ledControlMask = ledControlMask & (255 - leds); + } +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::blinkLeds(byte leds, byte enabled) +{ + if (enabled) + { + ledBlinkEnabled = ledBlinkEnabled | leds; + } + else + { + ledBlinkEnabled = ledBlinkEnabled & (255 - leds); + } +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::write(int integer) +{ + char displayText[5] = {' ',' ',' ',' ',0}; + + if (integer > 9999 || integer < -999) + { + displayText[3] = DISPLAY_OVERFLOW_ERROR; + write(displayText); + } + else if (integer == 0) + { + displayText[3] = '0'; + write (displayText); + } + else + { + byte sign = 0; + if (integer < 0) + { + sign = 1; + integer = integer * -1; + } + + byte idx = 3; + for (; idx >=0 && integer !=0; integer /= 10, idx--) + { + displayText[idx]=(integer % 10) + '0'; + } + + if (sign) + { + displayText[idx] = '-'; + } + + write (displayText); + } +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::write(float number, byte decimalPlaces) +{ + char outstr[7]; + dtostrf(number, 4, decimalPlaces, outstr); + + if (strlen(outstr) > 5) + { + outstr[0] = DISPLAY_OVERFLOW_ERROR; + outstr[1] = 0; + } + write(outstr,1); +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::write(const char *text, byte rightJustify) +{ + byte displayBuf[] = {0,0,0,0}, *pBuf = displayBuf; + + byte idx =0; + + for (; *text != 0 && idx < sizeof(displayBuf); text++) + { + byte offset = 0; + + if (*text == '.') + { + if (idx > 0) + { + displayBuf[idx-1] = displayBuf[idx-1] & 127; + } + else + { + displayBuf[idx] = AsciiToSegmentValue(*text); + idx++; + } + } + else + { + displayBuf[idx] = AsciiToSegmentValue(*text); + idx++; + } + } + + for (; idx < sizeof(displayBuf); idx++) + { + displayBuf[idx] = 255; + } + + // Copy display buffer to display memory + + if (rightJustify) + { + // right justify + int i_src = sizeof(displayBuf)-1; + int i_dst = sizeof(displayMemory)-1; + + for (; i_src >= 0 && displayBuf[i_src] == 255; i_src--) ; + + for (; i_src >= 0 && i_dst >= 0; i_src--, i_dst--) + { + displayMemory[i_dst] = displayBuf[i_src]; + } + + for (; i_dst >= 0; i_dst--) + { + displayMemory[i_dst] = 255; + } + } + // left justify + else + { + for (int i =0; i < sizeof(displayBuf); i++) + { + displayMemory[i] = displayBuf[i]; + } + } +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::blinkDisplay(byte digits, byte enabled) +{ + if (enabled) + { + blinkEnabled = blinkEnabled | digits; + } + else + { + blinkEnabled = blinkEnabled & (255 - digits); + } +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::setTimer(unsigned long thousandths) +{ + timerWriteInProgress = 1; + timer_volatile = thousandths; + timerWriteInProgress = 0; + + timerReadInProgress = 1; + timer_safe = thousandths; + timerReadInProgress = 0; +} + + +// ---------------------------------------------------------------------------------------------------- +unsigned long MultiFuncShield::getTimer() +{ + unsigned long timer; + timerReadInProgress = 1; + timer = timer_safe; + timerReadInProgress = 0; + + return timer; +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::wait(unsigned long thousandths) +{ + setTimer(thousandths); + while (getTimer()) __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::isrCallBack() +{ + byte displayEnabled = 1; + + displayTimerScaler--; + + if (displayTimerScaler == 0) + { + displayTimerScaler = 4; + + // Global bink control + if (blinkEnabled || ledBlinkEnabled) + { + blinkCounter++; + if (blinkState) + { + displayEnabled = 1; + if (blinkCounter > BLINK_ON_COUNT) + { + blinkState = 0; + blinkCounter = 0; + displayEnabled = 0; + } + } + else + { + displayEnabled = 0; + if (blinkCounter > BLINK_OFF_COUNT) + { + blinkState = 1; + blinkCounter = 0; + displayEnabled = 1; + } + } + } + + // Digit display blink control + if (blinkEnabled & (1 << displayIdx)) + { + if (displayEnabled) + { + WriteValueToSegment(displayIdx, displayMemory[displayIdx]); + } + else + { + WriteValueToSegment(displayIdx, 255); + } + } + else + { + WriteValueToSegment(displayIdx, displayMemory[displayIdx]); + } + + displayIdx++; + if (displayIdx > sizeof(displayMemory)-1) + { + displayIdx = 0; + } + + + // LED output and blink control. + + byte ledOutputNew = (ledState & (displayEnabled ? 255 : 0) & ledBlinkEnabled) | (ledState & ~ledBlinkEnabled); + + if (ledOutputNew != ledOutput) + { + for (byte ledIdx = 0; ledIdx < 4; ledIdx++) + { + if ((ledOutputNew ^ ledOutput) & (1 << ledIdx)) // only set LED if its state has changed + { + if (ledBlinkEnabled & (1 << ledIdx)) + { + //digitalWrite(LED[ledIdx], !(displayEnabled && ledState & (1 << ledIdx))); + writeLed(ledIdx, !(displayEnabled && ledState & (1 << ledIdx))); + } + else + { + //digitalWrite(LED[ledIdx], !(ledState & (1 << ledIdx))); + writeLed(ledIdx, !(ledState & (1 << ledIdx))); + } + } + } + ledOutput = ledOutputNew; + } + } + + + // Beeper control. + + if (!beeperModifyInProgress && beeperOnPeriodReloadValue) + { + if (beeperPeriodCounter == 0) + { + switch (beeperState) + { + case 0: // on period + if (beeperOffPeriodReloadValue) + { + beeperPeriodCounter = beeperOffPeriodReloadValue; + beeperState = 1; + break; + } + // Fall thru to next state immediately. + case 1: // off period + beeperCycleCounter--; + if (beeperCycleCounter) + { + beeperPeriodCounter = beeperOnPeriodReloadValue; + beeperState = 0; + break; + } + else + { + beeperCycleCounter = beeperCycleReloadValue; + beeperPeriodCounter = beeperLoopDelayPeriodReloadValue; + beeperState = 2; + + if (beeperLoopDelayPeriodReloadValue) + { + break; + } + } + // Fall thru to next state immediately. + case 2: // loop cycle delay period + if (beeperLoopCycleCounter == 0) // loop beeper indefinitely + { + beeperPeriodCounter = beeperOnPeriodReloadValue; + beeperState = 0; + } + else + { + beeperLoopCycleCounter--; + if (beeperLoopCycleCounter == 0) + { + beeperOnPeriodReloadValue = 0; // beeper activity has now ended. + //digitalWrite(BEEPER_PIN, 1); + writeBeeper(1); + } + else + { + beeperPeriodCounter = beeperOnPeriodReloadValue; + beeperState = 0; + } + } + break; + } + } + + if (beeperPeriodCounter) + { + beeperPeriodCounter--; + } + + if (beeperState == 0) + { + // beep on + //digitalWrite(BEEPER_PIN, 0); + writeBeeper(0); + } + else + { + // beep off + //digitalWrite(BEEPER_PIN, 1); + writeBeeper(1); + } + } + + + // Bump button sample interval counter + + if (buttonSampleIntervalCounter++ >= BUTTON_SAMPLE_INTERVAL) + { + buttonSampleIntervalCounter =0; + + byte btnStateNow; + + for (int i=0; i < sizeof(buttonPins); i++) + { + //btnStateNow = !digitalRead(buttonPins[i]); + btnStateNow = !readButton(i); + + // If button state has changed, action the change. + if (buttonState[i] != btnStateNow) + { + // if button state changes to pressed, queue SHORT PRESS to buffer. + if (btnStateNow) + { + queueButton((i+1) | BUTTON_PRESSED_IND); + } + else + { + // otherwise button state has changed to up, queue SHORT or LONG RELEASE state to buffer, and reset pressed time counter. + if (buttonPressTime[i] > (1000 / BUTTON_SAMPLE_INTERVAL)) + { + queueButton((i+1) | BUTTON_LONG_RELEASE_IND); + } + else + { + queueButton((i+1) | BUTTON_SHORT_RELEASE_IND); + } + buttonPressTime[i] = 0; + } + buttonState[i] = btnStateNow; + } + + // if button state pressed, increment pressed time counter. Queue LONG PRESS to buffer, if button is held long. + if (btnStateNow) + { + if (buttonPressTime[i] > (1000 / BUTTON_SAMPLE_INTERVAL) && (buttonPressTime[i] & 3) == 0) + { + queueButton((i+1) | BUTTON_LONG_PRESSED_IND); + } + + if (buttonPressTime[i] < 65535) + { + buttonPressTime[i]++; + } + } + } + } + + // Pulse in processing + + if (pulseInEnabled && !pulseInWriteInProgress) + { + //byte pulseInStateNow = digitalRead(pulseInPin); + byte pulseInStateNow = (*portInputRegister(pulseInPort) & pulseInBit ? 1 : 0); + + // Has the state of the pulse changed? + if ((pulseInState != pulseInStateNow) && (pulseInStateNow == pulseInTrigger)) + { + pulseInTotalCount_volatile++; + + pulseInPeriod_volatile = (pulseInPeriodCounter >= pulseInTimeOut ? 0 : pulseInPeriodCounter); + pulseInPeriodCounter = 0; + } + else + { + if (pulseInPeriodCounter < pulseInTimeOut) + { + pulseInPeriodCounter++; + } + else + { + pulseInPeriod_volatile = 0; + } + } + + if (!pulseInReadInProgress) + { + pulseInPeriod_safe = pulseInPeriod_volatile; + pulseInTotalCount_safe = pulseInTotalCount_volatile; + } + + if (pulseInState != pulseInStateNow) + { + pulseInState = pulseInStateNow; + } + } + + // Bump the count down timer. + if (timer_volatile && !timerWriteInProgress) + { + timer_volatile--; + } + + if (!timerReadInProgress) + { + timer_safe = timer_volatile; + } + + if (userInterrupt) + { + userInterrupt(); + } +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::manualDisplayRefresh() +{ + WriteValueToSegment(displayIdx, displayMemory[displayIdx]); + + displayIdx++; + if (displayIdx > sizeof(displayMemory)-1) + { + displayIdx = 0; + } +} + + +// ---------------------------------------------------------------------------------------------------- +void MultiFuncShield::manualButtonHandler() +{ + byte btnStateNow; + + for (int i=0; i < sizeof(buttonPins); i++) + { + btnStateNow = !digitalRead(buttonPins[i]); + + // If button state has changed, action the change. + + if (buttonState[i] != btnStateNow) + { + // if button state changes to pressed, queue SHORT PRESS to buffer. + if (btnStateNow) + { + queueButton((i+1) | BUTTON_PRESSED_IND); + } + else + { + // otherwise button state has changed to up, queue SHORT RELEASE state to buffer. + queueButton((i+1) | BUTTON_SHORT_RELEASE_IND); + } + buttonState[i] = btnStateNow; + } + } +} + + +// ---------------------------------------------------------------------------------------------------- +void isrWrapper () +{ + MFS.isrCallBack(); +} + + +// ---------------------------------------------------------------------------------------------------- +byte AsciiToSegmentValue (byte ascii) +{ + byte segmentValue = 182; + + if (ascii >= '0' && ascii <= '9') + { + segmentValue = SEGMENT_MAP_DIGIT[ascii - '0']; + } + else if (ascii >= 'a' && ascii <='z') + { + segmentValue = SEGMENT_MAP_ALPHA[ascii - 'a']; + } + else if (ascii >= 'A' && ascii <='Z') + { + segmentValue = SEGMENT_MAP_ALPHA[ascii - 'A']; + } + else + { + switch (ascii) + { + case '-': + segmentValue = 191; + break; + case '.': + segmentValue = 127; + break; + case '_': + segmentValue = 247; + break; + case ' ': + segmentValue = 255; + break; + } + } + + return segmentValue; +} + +// ---------------------------------------------------------------------------------------------------- +int MedianOf5(int s0, int s1, int s2, int s3, int s4) +{ + int tmp; + + if (s1 > s2) + { + tmp = s1; + s1 = s2; + s2 = tmp; + } + + if (s0 > s1) + { + tmp = s0; + s0 = s1; + s1 = tmp; + } + if (s3 > s4) + { + tmp = s3; + s3 = s4; + s4 = tmp; + } + + if (s1 > s2) + { + tmp = s1; + s1 = s2; + s2 = tmp; + } + + if (s0 > s3) + { + s3 = s0; + } + + if (s1 > s4) + { + s1 = s4; + } + + if (s1 > s3) + { + s3 = s1; + } + + if (s2 > s3) + { + s2 = s3; + } + + return s2; +} + + +// ---------------------------------------------------------------------------------------------------- +// Find the median value, given nine data samples. +int MedianOf9(int s0, int s1, int s2, int s3, int s4, int s5, int s6, int s7, int s8) +{ + int tmp; + + if (s1 > s2) + { + tmp = s1; + s1 = s2; + s2 = tmp; + } + if (s4 > s5) + { + tmp = s4; + s4 = s5; + s5 = tmp; + } + if (s7 > s8) + { + tmp = s7; + s7 = s8; + s8 = tmp; + } + + if (s0 > s1) + { + tmp = s0; + s0 = s1; + s1 = tmp; + } + if (s3 > s4) + { + tmp = s3; + s3 = s4; + s4 = tmp; + } + if (s6 > s7) + { + tmp = s6; + s6 = s7; + s7 = tmp; + } + + if (s1 > s2) + { + tmp = s1; + s1 = s2; + s2 = tmp; + } + if (s4 > s5) + { + tmp = s4; + s4 = s5; + s5 = tmp; + } + if (s7 > s8) + { + tmp = s7; + s7 = s8; + s8 = tmp; + } + + if (s3 > s6) + { + tmp = s3; + s3 = s6; + s6 = tmp; + } + if (s4 > s7) + { + tmp = s4; + s4 = s7; + s7 = tmp; + } + if (s5 > s8) + { + s5 = s8; + } + if (s0 > s3) + { + tmp = s0; + s3 = tmp; + } + + if (s1 > s4) + { + tmp = s1; + s1 = s4; + s4 = tmp; + } + if (s2 > s5) + { + s2 = s5; + } + if (s3 > s6) + { + tmp = s3; + s3 = s6; + s6 = tmp; + } + + if (s4 > s7) + { + s4 = s7; + } + if (s1 > s3) + { + s3 = s1; + } + + if (s2 > s6) + { + tmp = s2; + s2 = s6; + s6 = tmp; + } + + if (s2 > s3) + { + s3 = s2; + } + if (s4 > s6) + { + s4 = s6; + } + + if (s3 > s4) + { + s4 = s3; + } + return s4; +} + +/* ---------------------------------------------------------------------- */ + +#if defined(__AVR_ATmega328P__) // Uno + + /* Write a value to one of the 4 digits of the display */ + void WriteValueToSegment(byte Segment, byte Value) + { + bitClear(PORTD, 4); + + for (uint8_t i = 0; i < 8; i++) { + bitWrite(PORTB, 0, !!(Value & (1 << (7 - i)))); + bitSet(PORTD, 7); + bitClear(PORTD, 7); + } + + for (uint8_t i = 0; i < 8; i++) { + bitWrite(PORTB, 0, !!(SEGMENT_SELECT[Segment] & (1 << (7 - i)))); + bitSet(PORTD, 7); + bitClear(PORTD, 7); + } + + bitSet(PORTD, 4); + } + + + byte readButton (byte btnIndex) + { + switch (btnIndex) + { + case 0: + return bitRead(PINC, 1); + case 1: + return bitRead(PINC, 2); + case 2: + return bitRead(PINC, 3); + } + } + + void writeBeeper (byte value) + { + bitWrite(PORTD, 3, value); + } + + void writeLed(byte ledIdx, byte value) + { + switch (ledIdx) + { + case 0: + bitWrite(PORTB, 5, value); + break; + case 1: + bitWrite(PORTB, 4, value); + break; + case 2: + bitWrite(PORTB, 3, value); + break; + case 3: + bitWrite(PORTB, 2, value); + break; + } + } + +#elif defined(__AVR_ATmega32U4__) // Leonardo + + /* Write a value to one of the 4 digits of the display */ + void WriteValueToSegment(byte Segment, byte Value) + { + bitClear(PORTD, 4); + + for (uint8_t i = 0; i < 8; i++) { + bitWrite(PORTB, 4, !!(Value & (1 << (7 - i)))); + bitSet(PORTE, 6); + bitClear(PORTE, 6); + } + + for (uint8_t i = 0; i < 8; i++) { + bitWrite(PORTB, 4, !!(SEGMENT_SELECT[Segment] & (1 << (7 - i)))); + bitSet(PORTE, 6); + bitClear(PORTE, 6); + } + + bitSet(PORTD, 4); + } + + + byte readButton (byte btnIndex) + { + switch (btnIndex) + { + case 0: + return bitRead(PINF, 6); + case 1: + return bitRead(PINF, 5); + case 2: + return bitRead(PINF, 4); + } + } + + void writeBeeper (byte value) + { + bitWrite(PORTD, 0, value); + } + + void writeLed(byte ledIdx, byte value) + { + switch (ledIdx) + { + case 0: + bitWrite(PORTC, 7, value); + break; + case 1: + bitWrite(PORTD, 6, value); + break; + case 2: + bitWrite(PORTB, 7, value); + break; + case 3: + bitWrite(PORTB, 6, value); + break; + } + } + +#elif defined(__AVR_ATmega2560__) // Mega 2560 + +/* Write a value to one of the 4 digits of the display */ +/* +void WriteValueToSegment(byte Segment, byte Value) +{ + digitalWrite(LATCH_PIN,LOW); + shiftOut(DATA_PIN, CLK_PIN, MSBFIRST, Value); + shiftOut(DATA_PIN, CLK_PIN, MSBFIRST, SEGMENT_SELECT[Segment] ); + digitalWrite(LATCH_PIN,HIGH); +} +*/ + + /* Write a value to one of the 4 digits of the display */ + void WriteValueToSegment(byte Segment, byte Value) + { + bitClear(PORTG, 5); + + for (uint8_t i = 0; i < 8; i++) { + bitWrite(PORTH, 5, !!(Value & (1 << (7 - i)))); + bitSet(PORTH, 4); + bitClear(PORTH, 4); + } + + for (uint8_t i = 0; i < 8; i++) { + bitWrite(PORTH, 5, !!(SEGMENT_SELECT[Segment] & (1 << (7 - i)))); + bitSet(PORTH, 4); + bitClear(PORTH, 4); + } + + bitSet(PORTG, 5); + } + + + byte readButton (byte btnIndex) + { + switch (btnIndex) + { + case 0: + return bitRead(PINF, 1); + case 1: + return bitRead(PINF, 2); + case 2: + return bitRead(PINF, 3); + } + } + + void writeBeeper (byte value) + { + bitWrite(PORTE, 5, value); + } + + void writeLed(byte ledIdx, byte value) + { + switch (ledIdx) + { + case 0: + bitWrite(PORTB, 7, value); + break; + case 1: + bitWrite(PORTB, 6, value); + break; + case 2: + bitWrite(PORTB, 5, value); + break; + case 3: + bitWrite(PORTB, 4, value); + break; + } + } +#endif diff --git a/examples/ReactionTimeTestGame/MultiFuncShield.h b/examples/ReactionTimeTestGame/MultiFuncShield.h new file mode 100644 index 0000000..5aa6b99 --- /dev/null +++ b/examples/ReactionTimeTestGame/MultiFuncShield.h @@ -0,0 +1,226 @@ +#include "TimerOne.h" + +#ifndef MultiFuncShield_h_ +#define MultiFuncShield_h_ + +#include "Arduino.h" + +#define ON 1 +#define OFF 0 + +#define LED_1_PIN 13 +#define LED_2_PIN 12 +#define LED_3_PIN 11 +#define LED_4_PIN 10 +#define POT_PIN 0 +#define BEEPER_PIN 3 +#define BUTTON_1_PIN A1 +#define BUTTON_2_PIN A2 +#define BUTTON_3_PIN A3 +#define LATCH_PIN 4 +#define CLK_PIN 7 +#define DATA_PIN 8 +#define LM35_PIN A4 + +#define DIGIT_1 1 +#define DIGIT_2 2 +#define DIGIT_3 4 +#define DIGIT_4 8 +#define DIGIT_ALL 15 + +#define LED_1 1 +#define LED_2 2 +#define LED_3 4 +#define LED_4 8 +#define LED_ALL 15 + +// button state indicators +#define BUTTON_PRESSED_IND (0 << 6) +#define BUTTON_SHORT_RELEASE_IND (1 << 6) +#define BUTTON_LONG_PRESSED_IND (2 << 6) +#define BUTTON_LONG_RELEASE_IND (3 << 6) + +#define BUTTON_1_PRESSED (1 | BUTTON_PRESSED_IND) +#define BUTTON_1_SHORT_RELEASE (1 | BUTTON_SHORT_RELEASE_IND) +#define BUTTON_1_LONG_PRESSED (1 | BUTTON_LONG_PRESSED_IND) +#define BUTTON_1_LONG_RELEASE (1 | BUTTON_LONG_RELEASE_IND) + +#define BUTTON_2_PRESSED (2 | BUTTON_PRESSED_IND) +#define BUTTON_2_SHORT_RELEASE (2 | BUTTON_SHORT_RELEASE_IND) +#define BUTTON_2_LONG_PRESSED (2 | BUTTON_LONG_PRESSED_IND) +#define BUTTON_2_LONG_RELEASE (2 | BUTTON_LONG_RELEASE_IND) + +#define BUTTON_3_PRESSED (3 | BUTTON_PRESSED_IND) +#define BUTTON_3_SHORT_RELEASE (3 | BUTTON_SHORT_RELEASE_IND) +#define BUTTON_3_LONG_PRESSED (3 | BUTTON_LONG_PRESSED_IND) +#define BUTTON_3_LONG_RELEASE (3 | BUTTON_LONG_RELEASE_IND) + +#define SMOOTHING_NONE 0 +#define SMOOTHING_MODERATE 1 +#define SMOOTHING_STRONG 2 + + +class MultiFuncShield +{ + + public: + // Pointer to user interrupt with frequency of 1khz. + void (*userInterrupt)() = NULL; + + // Initializes this instance using a TimerOne instance. A 1khz interrupt is attached. + void initialize(TimerOne *timer1); + + // Initializes this instance, but interrupt based features are not available. + void initialize(); + + // For internal use only. + void isrCallBack(); + + // Initiates a millisecond countdown timer. + void setTimer (unsigned long thousandths); + + // Gets the current value of the countdown timer. + unsigned long getTimer(); + + // Initiates and waits for millisecond countdown timer to reach 0. + void wait(unsigned long thousandths); + + // Writes to the LED digit display. + void write(const char *textstring, byte rightJustify =0); + void write(int integer); + void write(float number, byte decimalPlaces = 1); + + // Manually refreshes the Led digit display. + // Not to be used whilst interrupt based features are available. + void manualDisplayRefresh(); + + // Blinks the digits on the LED digit display. + void blinkDisplay(byte digits, // use bitwise or, e.g. DIGIT_1 | DIGIT_2 + byte enabled = ON // turns on/off the blinking + ); + + // Turns LEDs on or off. + void writeLeds(byte leds, // use bitwise or, e.g. LED_1 | LED_2 + byte lit // ON or OFF + ); + + // Blinks the LEDs. + void blinkLeds(byte leds, // use bitwise or, e.g. LED_1 | LED_2 + byte enabled = ON // ON or OFF + ); + + // Engage the beeper, which is managed in the background. Period timing is in 100th of second + void beep(unsigned int onPeriod = 20, unsigned int offPeriod = 0, byte cycles = 1, unsigned int loopCycles = 1 /* 0=indefinitely */, unsigned int loopDelayPeriod =0); + + // Use this to set the off period whilst the beeper is engaged, + void setBeepOffPeriod(unsigned int offPeriod); + + // Queues a button action to the button queue, e.g BUTTON_1_PRESSED + void queueButton (byte button); + + // Pulls a button action from the button queue. + byte getButton(); + + // Queues button short press and release actions. Long button presses are not supported, and long releases are reported as short releases. + // Should not be used whilst interrupt based features are available. + void manualButtonHandler(); + + // Initializes the pulse counter. Used for counting pulses applied to an input pin. Max pulse frequency 500hz. + void initPulseInCounter(byte pin = BUTTON_1_PIN, // input pin + unsigned int timeOut = 3000, // the number of milliseconds to wait for a pulse, before resetting pulse in period to 0. + byte trigger = LOW // trigger counter on either rising or falling edge + ); + + void disablePulseInCounter(); + + // Gets the period of the most recent pulse (in milliseconds). + unsigned int getPulseInPeriod(); + + // Gets the total number pulses counted. + unsigned long getPulseInTotalCount(); + + // Resets the pulse counter to 0. + void resetPulseInTotalCount(); + + // Sets the pulse in timeout, which is the number of milliseconds to wait for a pulse, before resetting pulse in period to 0. + void setPulseInTimeOut(unsigned int timeOut); + + // Initializes the sonar reading feature. Needs HC-SR04 sonar module. + void initSonar(byte level = SMOOTHING_MODERATE); // level 0=none, 1=moderate, 2=strong. + + // Gets the distance measured in centimeters, using HC-SR04 sonar module. + unsigned int getSonarDataCm(byte triggerPin, byte echoPin); + + // Initializes temperature reading feature. Needs LM35 sensor. Must remove jumper J1 from shield. + void initLM35(byte level = SMOOTHING_MODERATE); // level 0=none, 1=moderate, 2=strong + + // Gets the temperature reading in 1 tenths of a centigrade. + int getLM35Data(); + + private: + TimerOne *timer1; + volatile byte timerReadInProgress = 0; + volatile byte timerWriteInProgress = 0; + + const byte buttonPins[3] = {BUTTON_1_PIN, BUTTON_2_PIN, BUTTON_3_PIN}; // must correspond to button macros above + + volatile byte buttonBuffer[sizeof(buttonPins) * 2]; + volatile char buttonBufferCount = 0; + volatile byte button_write_pos = 0; + volatile byte button_read_pos = 0; + + unsigned int buttonSampleIntervalCounter =0; + byte buttonState[sizeof(buttonPins)] = {0,0,0}; // current up or down state + unsigned int buttonPressTime[sizeof(buttonPins)] = {0,0,0}; + + volatile unsigned long timer_volatile = 0; // count down timer 1000th of a second resolution. + volatile unsigned long timer_safe = 0; + + volatile byte beeperModifyInProgress = 0; + volatile byte beeperState =0; // 0=on period; 1=off period + volatile unsigned int beeperOnPeriodReloadValue =0; + volatile unsigned int beeperOffPeriodReloadValue =0; + volatile unsigned int beeperPeriodCounter = 0; + volatile byte beeperCycleReloadValue = 0; + volatile byte beeperCycleCounter =0; + volatile unsigned int beeperLoopCycleCounter =0; + volatile unsigned int beeperLoopDelayPeriodReloadValue =0; + + byte displayIdx = 0; + byte blinkEnabled = 0; // least significant bits mapped to display digits. + byte blinkState = 0; + byte blinkCounter = 0; + + //byte ledControlMask = 0; // soft enable / disable LED. Disable LEDs here if using PWM from TImerOne library. + byte ledState =0; // least significant bits mapped to LEDs + byte ledBlinkEnabled =0; // least significant bits mapped to LEDs + byte ledOutput=0; // current led outputs (taking into consideration blink) + + volatile byte pulseInEnabled = false; + volatile byte pulseInReadInProgress =0; + volatile byte pulseInWriteInProgress =0; + + volatile unsigned int pulseInTimeOut = 3000; // time frame for measuring pulse period. + volatile byte pulseInPin = BUTTON_1_PIN; + volatile unsigned int pulseInPeriodCounter = 3000; + volatile byte pulseInTrigger = LOW; // trigger on LOW or HIGH + volatile unsigned int pulseInPeriod_volatile =0; + volatile unsigned int pulseInPeriod_safe =0; + volatile byte pulseInState =0; + volatile unsigned long pulseInTotalCount_volatile = 0; + volatile unsigned long pulseInTotalCount_safe = 0; + + byte sonarSmoothingLevel = SMOOTHING_MODERATE; + byte lm35SmoothingLevel = SMOOTHING_MODERATE; +}; + +extern MultiFuncShield MFS; + +// returns median of 5 data samples. +extern int MedianOf5(int s0, int s1, int s2, int s3, int s4); + +// returns median of 9 data samples. +extern int MedianOf9(int s0, int s1, int s2, int s3, int s4, int s5, int s6, int s7, int s8); + +#endif + diff --git a/examples/ReactionTimeTestGame/ReactionTimeTestGame.ino b/examples/ReactionTimeTestGame/ReactionTimeTestGame.ino new file mode 100644 index 0000000..7c5f7c2 --- /dev/null +++ b/examples/ReactionTimeTestGame/ReactionTimeTestGame.ino @@ -0,0 +1,332 @@ +/* + * Game to measure reaction time with 2 buttons, 3 LEDs and a buzzer + * + * Aufgaben: + * 1. Warte bis ein Button gedrückt wird und gebe die Reaktionszeit aus. Schalte die LED des Spielers an und erhöhe die Punkte des Spielers. + * Benutze digitalRead() und z.B. "sRightPlayerScore++". + * 2. Wenn der Punktestand POINTS_FOR_WIN erreicht hat, lasse die LED blinken und spiele eine Melodie. + * Benutze blinkLed() und playRtttlBlockingPGM(PIN_BUZZER, StarWars). Selektiere "StarWars" und drücke Taste F3. + * 3. Bestrafe cheaten (der Button ist schon vor dem angehen der LED gedrückt) mit Punktabzug und signalisiere das mit einer blinkenden LED. + * + * + * Copyright (C) 2018-2020 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of PlayRttl https://github.com/ArminJo/PlayRtttl. + * + * PlayRttl 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 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, see . + */ + +#include +#include "PlayRtttl.h" + +#define VERSION_EXAMPLE "2.1" + +/* + * Comment the next line out if you want to run this program on an Arduino Multifunction Shield + * https://www.electroschematics.com/getting-started-with-the-arduino-multifunction-shield/ + */ +//#define MULTI_FUNCTION_SHIELD +/* + * Comment the next line out, if you use the breadboard layout for school lessons + * https://github.com/ArminJo/Arduino-Lessons-for-School#universal-breadboard-layout-for-all-lessons + */ +//#define BREADBOARD_LAYOUT +#ifdef MULTI_FUNCTION_SHIELD +#include "MultiFuncShield.h" +// inverse logic for LEDS and buzzer: they are active at LOW +#define PIN_BUZZER 3 + +#define PIN_RED_LED 13 +#define PIN_RED_LED_2 12 +#define PIN_RED_LED_3 11 +#define PIN_RED_LED_4 10 +#define PIN_RIGHT_LED PIN_RED_LED_4 +#define PIN_LEFT_LED PIN_RED_LED +#define PIN_START_LED PIN_RED_LED_2 +#define PIN_START_LED_2 PIN_RED_LED_3 + +#define PIN_RIGHT_BUTTON A3 +#define PIN_LEFT_BUTTON A1 + +#define PIN_SERVO_HORIZONTAL 6 +#define PIN_SERVO_VERTICAL 5 + +#define TRIGGER_OUT_PIN 9 +#define ECHO_IN_PIN A5 + +#define PIN_POTENTIOMETER A0 + +#elif defined(BREADBOARD_LAYOUT) +#include "Breadboard.h" + +#define PIN_START_LED PIN_MIDDLE_LED + +#else +/* + * Layout for "Simon Says" layout + * https://learn.sparkfun.com/tutorials/sparkfun-inventors-kit-experiment-guide---v40/circuit-2c-simon-says-game + * https://dingfluence.dingfabrik.de/pages/viewpage.action?pageId=31653962 + */ +#define PIN_BUZZER 10 + +#define PIN_RIGHT_LED 7 +#define PIN_LEFT_LED 5 +#define PIN_START_LED 3 +#define PIN_START_LED_2 9 + +#define PIN_RIGHT_BUTTON 6 +#define PIN_LEFT_BUTTON 4 +#endif + +const int POINTS_FOR_WIN = 3; + +int sLeftPlayerScore, sRightPlayerScore; +int sLeftPlayerWins = 0, sRightPlayerWins = 0; // Count wins, to play a random song every second win. +int tHighScore = 8000; // the minimum reaction time in millis + +void blinkLEDBlocking(uint8_t aLedPin, uint8_t aBlinkCount, uint16_t aDelay); + +// The setup function is called once at startup of the sketch +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + Serial.begin(115200); +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) + delay(2000); // To be able to connect Serial monitor after reset and before first printout +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ "\r\nVersion " VERSION_EXAMPLE " from " __DATE__)); + + // Enable output on the LED pins + pinMode(PIN_RIGHT_LED, OUTPUT); + pinMode(PIN_LEFT_LED, OUTPUT); + pinMode(PIN_START_LED, OUTPUT); +#if defined(PIN_START_LED_2) + pinMode(PIN_START_LED_2, OUTPUT); +#endif + +#ifdef MULTI_FUNCTION_SHIELD + // LEDS are active low on the MuFu shield so switch them off by writing a HIGH + digitalWrite(PIN_RIGHT_LED, HIGH); + digitalWrite(PIN_LEFT_LED, HIGH); + digitalWrite(PIN_START_LED, HIGH); + digitalWrite(PIN_START_LED_2, HIGH); + + Timer1.initialize(); + MFS.initialize(&Timer1); + setTonePinIsInverted(true);// configure PlayRtttl output polarity +#endif + + // Prepare for buttons reading + pinMode(PIN_RIGHT_BUTTON, INPUT_PULLUP); + pinMode(PIN_LEFT_BUTTON, INPUT_PULLUP); +} + +/* + * Die mittlere Led nach einer zufälligen Zeit anschalten und dann warten, welche Taste zuerst gedrückt wird. + * Die Zeit bis zum Drücken der Taste ausgeben und die Led an der zuerst gedrückten Taste leuchten lassen. + * Wenn ein Spieler die Punktzahl erreicht hat eine Melodie spielen. + * Danach kurz warten und die Messung wieder von vorne beginnen. + */ +void loop() { + int tRightPlayerButton; + int tLeftPlayerButton; + + delay(random(500, 4000)); // random delay before next lap + + /* + * Task 3. Check for cheating just before switching led on and give feedback if detected + * Bestrafe cheaten (der Button ist schon vor dem angehen der LED gedrückt) mit Punktabzug und signalisiere das mit einer blinkenden LED. + */ + tRightPlayerButton = digitalRead(PIN_RIGHT_BUTTON); + tLeftPlayerButton = digitalRead(PIN_LEFT_BUTTON); + if (tRightPlayerButton == LOW || tLeftPlayerButton == LOW) { + /* + * Cheating detected -> blink and decrement score + */ + if (tRightPlayerButton == LOW) { + blinkLEDBlocking(PIN_RIGHT_LED, 5, 50); + sRightPlayerScore--; + } else { + blinkLEDBlocking(PIN_LEFT_LED, 5, 50); + sLeftPlayerScore--; + } + return; // start next lap + } + + uint32_t tStartMillis = millis(); + /* + * No cheating here -> switch on start LED(s) and wait for first button to be pressed + */ +#ifdef MULTI_FUNCTION_SHIELD + digitalWrite(PIN_START_LED, LOW); + digitalWrite(PIN_START_LED_2, LOW); + digitalWrite(PIN_BUZZER, LOW); + MFS.write(""); + delay(20); + digitalWrite(PIN_BUZZER, HIGH); +#else + tone(PIN_BUZZER, 2200, 40); + digitalWrite(PIN_START_LED, HIGH); +# if defined(PIN_START_LED_2) + digitalWrite(PIN_START_LED_2, HIGH); +# endif +#endif + + /* + * Task 1. Wait for press after LED switched on, give LED feedback and output reaction time + * Warte bis ein Button gedrückt wird und gebe die Reaktionszeit aus. Schalte die LED des Spielers an und erhöhe die Punkte des Spielers. + * Benutze digitalRead() und z.B. "sRightPlayerScore++". + */ + do { + tRightPlayerButton = digitalRead(PIN_RIGHT_BUTTON); + tLeftPlayerButton = digitalRead(PIN_LEFT_BUTTON); + } while (tRightPlayerButton != LOW && tLeftPlayerButton != LOW); + int tReactionTimeMilliseconds = millis() - tStartMillis; + + /* + * Manage high score + */ +#ifdef MULTI_FUNCTION_SHIELD + bool tIsHighScore = false; +#endif + if (tReactionTimeMilliseconds < tHighScore) { + tHighScore = tReactionTimeMilliseconds; +#ifdef MULTI_FUNCTION_SHIELD + tIsHighScore = true; +#endif + } + + /* + * One button is pressed here + * Output result to Serial + */ + if (tRightPlayerButton == LOW) { + // Right button was pressed + sRightPlayerScore++; +#ifdef MULTI_FUNCTION_SHIELD + digitalWrite(PIN_RIGHT_LED, LOW); +#else + digitalWrite(PIN_RIGHT_LED, HIGH); +#endif + Serial.print("Right player wins with "); + } else { + // Left button must be pressed here + sLeftPlayerScore++; +#ifdef MULTI_FUNCTION_SHIELD + digitalWrite(PIN_LEFT_LED, LOW); +#else + digitalWrite(PIN_LEFT_LED, HIGH); +#endif + Serial.print("Left player wins with "); + } + Serial.print(tReactionTimeMilliseconds); + Serial.println(" ms"); + +#ifdef MULTI_FUNCTION_SHIELD + // show time on 4 digit display + MFS.write(tReactionTimeMilliseconds); + MFS.blinkDisplay(DIGIT_ALL, tIsHighScore); +#endif + + /* + * Task 2. Check for score level, blink LED and play melody + * Wenn der Punktestand POINTS_FOR_WIN erreicht hat, lasse die LED blinken und spiele eine Melodie. + * Benutze blinkLed() und playRtttlBlockingPGM(PIN_BUZZER, StarWars). Selektiere "StarWars" und drücke Taste F3. + */ + if (sRightPlayerScore >= POINTS_FOR_WIN) { + sLeftPlayerScore = 0; + sRightPlayerScore = 0; + /* + * Here we use the non blocking version of blink and tone, which enables them to act simultaneously. + * The simple one goes like this: + * RightLed.blink(5, 100); + * playRtttlBlockingPGM(PIN_BUZZER, StarWars); + */ + sLeftPlayerWins++; + if (sLeftPlayerWins == 1) { + // Play StarWars the first win + startPlayRtttlPGM(PIN_BUZZER, StarWars); + } else { + startPlayRandomRtttlFromArrayPGMAndPrintName(PIN_BUZZER, RTTTLMelodies, ARRAY_SIZE_MELODIES, &Serial); + } + // update both libraries to let them act simultaneously + // break if button is pressed after 2000 milliseconds + uint32_t tLastLedChangeMillis = millis(); + uint32_t tStartCheckButtonMillis = millis(); + while (updatePlayRtttl() && (((millis() - tStartCheckButtonMillis) < 2000) || digitalRead(PIN_RIGHT_BUTTON))) { + if (millis() - tLastLedChangeMillis > 200) { + tLastLedChangeMillis = millis(); + digitalWrite(PIN_RIGHT_LED, !digitalRead(PIN_RIGHT_LED)); // toggle LED + } + } + digitalWrite(PIN_RIGHT_LED, LOW); + stopPlayRtttl(); // in case we left by button press + + } else if (sLeftPlayerScore >= POINTS_FOR_WIN) { + sLeftPlayerScore = 0; + sRightPlayerScore = 0; + //Here we use the non blocking version of blink and tone, which enables them to act simultaneously. + sRightPlayerWins++; + if (sRightPlayerWins == 1) { + // Play MissionImpossible the first win + startPlayRtttlPGM(PIN_BUZZER, MissionImp); + } else { + startPlayRandomRtttlFromArrayPGMAndPrintName(PIN_BUZZER, RTTTLMelodies, ARRAY_SIZE_MELODIES, &Serial); + } + uint32_t tLastLedChangeMillis = millis(); + uint32_t tStartCheckButtonMillis = millis(); + while (updatePlayRtttl() && (((millis() - tStartCheckButtonMillis) < 2000) || digitalRead(PIN_LEFT_BUTTON))) { + if (millis() - tLastLedChangeMillis > 200) { + tLastLedChangeMillis = millis(); + digitalWrite(PIN_LEFT_LED, !digitalRead(PIN_LEFT_LED)); // toggle LED + } + } + digitalWrite(PIN_LEFT_LED, LOW); + stopPlayRtttl(); // in case we left by button press + } + +#ifdef MULTI_FUNCTION_SHIELD + // switch buzzer off + digitalWrite(PIN_BUZZER, HIGH); +#endif + + // Wait before switching off LEDs + delay(300); + + // Switching off LEDs +#ifdef MULTI_FUNCTION_SHIELD + digitalWrite(PIN_RIGHT_LED, HIGH); + digitalWrite(PIN_LEFT_LED, HIGH); + digitalWrite(PIN_START_LED, HIGH); + digitalWrite(PIN_START_LED_2, HIGH); +#else + digitalWrite(PIN_RIGHT_LED, LOW); + digitalWrite(PIN_LEFT_LED, LOW); + digitalWrite(PIN_START_LED, LOW); +# if defined(PIN_START_LED_2) + pinMode(PIN_START_LED_2, LOW); +# endif +#endif + +} // loop end + +void blinkLEDBlocking(uint8_t aLedPin, uint8_t aBlinkCount, uint16_t aDelay) { + for (int i = 0; i < aBlinkCount; ++i) { + digitalWrite(aLedPin, HIGH); + delay(aDelay); + digitalWrite(aLedPin, LOW); + delay(aDelay); + } +} diff --git a/examples/ReactionTimeTestGame/TimerOne.cpp b/examples/ReactionTimeTestGame/TimerOne.cpp new file mode 100644 index 0000000..8a287fa --- /dev/null +++ b/examples/ReactionTimeTestGame/TimerOne.cpp @@ -0,0 +1,59 @@ +/* + * 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 Oct 2009 by Dan Clemens to work with timer1 of the ATMega1280 or Arduino Mega + * Modified April 2012 by Paul Stoffregen + * Modified again, June 2014 by Paul Stoffregen + * Modified July 2017 by Stoyko Dimitrov - added support for ATTiny85 except for the PWM functionality + * + * 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. + * + */ + +#include "TimerOne.h" + +TimerOne Timer1; // preinstatiate + +unsigned short TimerOne::pwmPeriod = 0; +unsigned char TimerOne::clockSelectBits = 0; +void (*TimerOne::isrCallback)() = TimerOne::isrDefaultUnused; + +// interrupt service routine that wraps a user defined function supplied by attachInterrupt +#if defined (__AVR_ATtiny85__) +ISR(TIMER1_COMPA_vect) +{ + Timer1.isrCallback(); +} +#elif defined(__AVR__) +ISR(TIMER1_OVF_vect) +{ + Timer1.isrCallback(); +} +#elif defined(__arm__) && defined(TEENSYDUINO) && (defined(KINETISK) || defined(KINETISL)) +void ftm1_isr(void) +{ + uint32_t sc = FTM1_SC; + #ifdef KINETISL + if (sc & 0x80) FTM1_SC = sc; + #else + if (sc & 0x80) FTM1_SC = sc & 0x7F; + #endif + Timer1.isrCallback(); +} +#elif defined(__arm__) && defined(TEENSYDUINO) && (defined(__IMXRT1052__) || defined(__IMXRT1062__)) +void TimerOne::isr(void) +{ + FLEXPWM1_SM3STS = FLEXPWM_SMSTS_RF; + Timer1.isrCallback(); +} + +#endif + +void TimerOne::isrDefaultUnused() +{ +} diff --git a/examples/ReactionTimeTestGame/TimerOne.h b/examples/ReactionTimeTestGame/TimerOne.h new file mode 100644 index 0000000..383e76d --- /dev/null +++ b/examples/ReactionTimeTestGame/TimerOne.h @@ -0,0 +1,619 @@ +/* + * 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 April 2012 by Paul Stoffregen - portable to other AVR chips, use inline functions + * Modified again, June 2014 by Paul Stoffregen - support Teensy 3.x & even more AVR chips + * Modified July 2017 by Stoyko Dimitrov - added support for ATTiny85 except for the PWM functionality + * + * + * 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. + * + */ + +#ifndef TimerOne_h_ +#define TimerOne_h_ + +#include "Arduino.h" + +#include "known_16bit_timers.h" +#if defined (__AVR_ATtiny85__) +#define TIMER1_RESOLUTION 256UL // Timer1 is 8 bit +#elif defined(__AVR__) +#define TIMER1_RESOLUTION 65536UL // Timer1 is 16 bit +#else +#define TIMER1_RESOLUTION 65536UL // assume 16 bits for non-AVR chips +#endif + +// Placing nearly all the code in this .h file allows the functions to be +// inlined by the compiler. In the very common case with constant values +// the compiler will perform all calculations and simply write constants +// to the hardware registers (for example, setPeriod). + + +class TimerOne +{ + +#if defined (__AVR_ATtiny85__) + public: + //**************************** + // Configuration + //**************************** + void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { + TCCR1 = _BV(CTC1); //clear timer1 when it matches the value in OCR1C + TIMSK |= _BV(OCIE1A); //enable interrupt when OCR1A matches the timer value + setPeriod(microseconds); + } + void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { + const unsigned long cycles = microseconds * ratio; + if (cycles < TIMER1_RESOLUTION) { + clockSelectBits = _BV(CS10); + pwmPeriod = cycles; + } else + if (cycles < TIMER1_RESOLUTION * 2UL) { + clockSelectBits = _BV(CS11); + pwmPeriod = cycles / 2; + } else + if (cycles < TIMER1_RESOLUTION * 4UL) { + clockSelectBits = _BV(CS11) | _BV(CS10); + pwmPeriod = cycles / 4; + } else + if (cycles < TIMER1_RESOLUTION * 8UL) { + clockSelectBits = _BV(CS12); + pwmPeriod = cycles / 8; + } else + if (cycles < TIMER1_RESOLUTION * 16UL) { + clockSelectBits = _BV(CS12) | _BV(CS10); + pwmPeriod = cycles / 16; + } else + if (cycles < TIMER1_RESOLUTION * 32UL) { + clockSelectBits = _BV(CS12) | _BV(CS11); + pwmPeriod = cycles / 32; + } else + if (cycles < TIMER1_RESOLUTION * 64UL) { + clockSelectBits = _BV(CS12) | _BV(CS11) | _BV(CS10); + pwmPeriod = cycles / 64UL; + } else + if (cycles < TIMER1_RESOLUTION * 128UL) { + clockSelectBits = _BV(CS13); + pwmPeriod = cycles / 128; + } else + if (cycles < TIMER1_RESOLUTION * 256UL) { + clockSelectBits = _BV(CS13) | _BV(CS10); + pwmPeriod = cycles / 256; + } else + if (cycles < TIMER1_RESOLUTION * 512UL) { + clockSelectBits = _BV(CS13) | _BV(CS11); + pwmPeriod = cycles / 512; + } else + if (cycles < TIMER1_RESOLUTION * 1024UL) { + clockSelectBits = _BV(CS13) | _BV(CS11) | _BV(CS10); + pwmPeriod = cycles / 1024; + } else + if (cycles < TIMER1_RESOLUTION * 2048UL) { + clockSelectBits = _BV(CS13) | _BV(CS12); + pwmPeriod = cycles / 2048; + } else + if (cycles < TIMER1_RESOLUTION * 4096UL) { + clockSelectBits = _BV(CS13) | _BV(CS12) | _BV(CS10); + pwmPeriod = cycles / 4096; + } else + if (cycles < TIMER1_RESOLUTION * 8192UL) { + clockSelectBits = _BV(CS13) | _BV(CS12) | _BV(CS11); + pwmPeriod = cycles / 8192; + } else + if (cycles < TIMER1_RESOLUTION * 16384UL) { + clockSelectBits = _BV(CS13) | _BV(CS12) | _BV(CS11) | _BV(CS10); + pwmPeriod = cycles / 16384; + } else { + clockSelectBits = _BV(CS13) | _BV(CS12) | _BV(CS11) | _BV(CS10); + pwmPeriod = TIMER1_RESOLUTION - 1; + } + OCR1A = pwmPeriod; + OCR1C = pwmPeriod; + TCCR1 = _BV(CTC1) | clockSelectBits; + } + + //**************************** + // Run Control + //**************************** + void start() __attribute__((always_inline)) { + TCCR1 = 0; + TCNT1 = 0; + resume(); + } + void stop() __attribute__((always_inline)) { + TCCR1 = _BV(CTC1); + } + void restart() __attribute__((always_inline)) { + start(); + } + void resume() __attribute__((always_inline)) { + TCCR1 = _BV(CTC1) | clockSelectBits; + } + + //**************************** + // PWM outputs + //**************************** + //Not implemented yet for ATTiny85 + //TO DO + + //**************************** + // Interrupt Function + //**************************** + void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { + isrCallback = isr; + TIMSK |= _BV(OCIE1A); + } + void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { + if(microseconds > 0) setPeriod(microseconds); + attachInterrupt(isr); + } + void detachInterrupt() __attribute__((always_inline)) { + //TIMSK = 0; // Timer 0 and Timer 1 both use TIMSK register so setting it to 0 will override settings for Timer1 as well + TIMSK &= ~_BV(OCIE1A); + } + static void (*isrCallback)(); + static void isrDefaultUnused(); + + private: + static unsigned short pwmPeriod; + static unsigned char clockSelectBits; + static const byte ratio = (F_CPU)/ ( 1000000 ); + +#elif defined(__AVR__) + +#if defined (__AVR_ATmega8__) + //in some io definitions for older microcontrollers TIMSK is used instead of TIMSK1 + #define TIMSK1 TIMSK +#endif + + public: + //**************************** + // Configuration + //**************************** + void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { + TCCR1B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timer + TCCR1A = 0; // clear control register A + setPeriod(microseconds); + } + void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { + const unsigned long cycles = ((F_CPU/100000 * microseconds) / 20); + if (cycles < TIMER1_RESOLUTION) { + clockSelectBits = _BV(CS10); + pwmPeriod = cycles; + } else + if (cycles < TIMER1_RESOLUTION * 8) { + clockSelectBits = _BV(CS11); + pwmPeriod = cycles / 8; + } else + if (cycles < TIMER1_RESOLUTION * 64) { + clockSelectBits = _BV(CS11) | _BV(CS10); + pwmPeriod = cycles / 64; + } else + if (cycles < TIMER1_RESOLUTION * 256) { + clockSelectBits = _BV(CS12); + pwmPeriod = cycles / 256; + } else + if (cycles < TIMER1_RESOLUTION * 1024) { + clockSelectBits = _BV(CS12) | _BV(CS10); + pwmPeriod = cycles / 1024; + } else { + clockSelectBits = _BV(CS12) | _BV(CS10); + pwmPeriod = TIMER1_RESOLUTION - 1; + } + ICR1 = pwmPeriod; + TCCR1B = _BV(WGM13) | clockSelectBits; + } + + //**************************** + // Run Control + //**************************** + void start() __attribute__((always_inline)) { + TCCR1B = 0; + TCNT1 = 0; // TODO: does this cause an undesired interrupt? + resume(); + } + void stop() __attribute__((always_inline)) { + TCCR1B = _BV(WGM13); + } + void restart() __attribute__((always_inline)) { + start(); + } + void resume() __attribute__((always_inline)) { + TCCR1B = _BV(WGM13) | clockSelectBits; + } + + //**************************** + // PWM outputs + //**************************** + void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) { + unsigned long dutyCycle = pwmPeriod; + dutyCycle *= duty; + dutyCycle >>= 10; + if (pin == TIMER1_A_PIN) OCR1A = dutyCycle; + #ifdef TIMER1_B_PIN + else if (pin == TIMER1_B_PIN) OCR1B = dutyCycle; + #endif + #ifdef TIMER1_C_PIN + else if (pin == TIMER1_C_PIN) OCR1C = dutyCycle; + #endif + } + void pwm(char pin, unsigned int duty) __attribute__((always_inline)) { + if (pin == TIMER1_A_PIN) { pinMode(TIMER1_A_PIN, OUTPUT); TCCR1A |= _BV(COM1A1); } + #ifdef TIMER1_B_PIN + else if (pin == TIMER1_B_PIN) { pinMode(TIMER1_B_PIN, OUTPUT); TCCR1A |= _BV(COM1B1); } + #endif + #ifdef TIMER1_C_PIN + else if (pin == TIMER1_C_PIN) { pinMode(TIMER1_C_PIN, OUTPUT); TCCR1A |= _BV(COM1C1); } + #endif + setPwmDuty(pin, duty); + TCCR1B = _BV(WGM13) | clockSelectBits; + } + void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) { + if (microseconds > 0) setPeriod(microseconds); + pwm(pin, duty); + } + void disablePwm(char pin) __attribute__((always_inline)) { + if (pin == TIMER1_A_PIN) TCCR1A &= ~_BV(COM1A1); + #ifdef TIMER1_B_PIN + else if (pin == TIMER1_B_PIN) TCCR1A &= ~_BV(COM1B1); + #endif + #ifdef TIMER1_C_PIN + else if (pin == TIMER1_C_PIN) TCCR1A &= ~_BV(COM1C1); + #endif + } + + //**************************** + // Interrupt Function + //**************************** + + void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { + isrCallback = isr; + TIMSK1 = _BV(TOIE1); + } + void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { + if(microseconds > 0) setPeriod(microseconds); + attachInterrupt(isr); + } + void detachInterrupt() __attribute__((always_inline)) { + TIMSK1 = 0; + } + static void (*isrCallback)(); + static void isrDefaultUnused(); + + private: + // properties + static unsigned short pwmPeriod; + static unsigned char clockSelectBits; + + + + + + +#elif defined(__arm__) && defined(TEENSYDUINO) && (defined(KINETISK) || defined(KINETISL)) + +#if defined(KINETISK) +#define F_TIMER F_BUS +#elif defined(KINETISL) +#define F_TIMER (F_PLL/2) +#endif + +// Use only 15 bit resolution. From K66 reference manual, 45.5.7 page 1200: +// The CPWM pulse width (duty cycle) is determined by 2 x (CnV - CNTIN) and the +// period is determined by 2 x (MOD - CNTIN). See the following figure. MOD must be +// kept in the range of 0x0001 to 0x7FFF because values outside this range can produce +// ambiguous results. +#undef TIMER1_RESOLUTION +#define TIMER1_RESOLUTION 32768 + + public: + //**************************** + // Configuration + //**************************** + void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { + setPeriod(microseconds); + } + void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { + const unsigned long cycles = (F_TIMER / 2000000) * microseconds; + // A much faster if-else + // This is like a binary serch tree and no more than 3 conditions are evaluated. + // I haven't checked if this becomes significantly longer ASM than the simple ladder. + // It looks very similar to the ladder tho: same # of if's and else's + + /* + // This code does not work properly in all cases :( + // https://github.com/PaulStoffregen/TimerOne/issues/17 + if (cycles < TIMER1_RESOLUTION * 16) { + if (cycles < TIMER1_RESOLUTION * 4) { + if (cycles < TIMER1_RESOLUTION) { + clockSelectBits = 0; + pwmPeriod = cycles; + }else{ + clockSelectBits = 1; + pwmPeriod = cycles >> 1; + } + }else{ + if (cycles < TIMER1_RESOLUTION * 8) { + clockSelectBits = 3; + pwmPeriod = cycles >> 3; + }else{ + clockSelectBits = 4; + pwmPeriod = cycles >> 4; + } + } + }else{ + if (cycles > TIMER1_RESOLUTION * 64) { + if (cycles > TIMER1_RESOLUTION * 128) { + clockSelectBits = 7; + pwmPeriod = TIMER1_RESOLUTION - 1; + }else{ + clockSelectBits = 7; + pwmPeriod = cycles >> 7; + } + } + else{ + if (cycles > TIMER1_RESOLUTION * 32) { + clockSelectBits = 6; + pwmPeriod = cycles >> 6; + }else{ + clockSelectBits = 5; + pwmPeriod = cycles >> 5; + } + } + } + */ + if (cycles < TIMER1_RESOLUTION) { + clockSelectBits = 0; + pwmPeriod = cycles; + } else + if (cycles < TIMER1_RESOLUTION * 2) { + clockSelectBits = 1; + pwmPeriod = cycles >> 1; + } else + if (cycles < TIMER1_RESOLUTION * 4) { + clockSelectBits = 2; + pwmPeriod = cycles >> 2; + } else + if (cycles < TIMER1_RESOLUTION * 8) { + clockSelectBits = 3; + pwmPeriod = cycles >> 3; + } else + if (cycles < TIMER1_RESOLUTION * 16) { + clockSelectBits = 4; + pwmPeriod = cycles >> 4; + } else + if (cycles < TIMER1_RESOLUTION * 32) { + clockSelectBits = 5; + pwmPeriod = cycles >> 5; + } else + if (cycles < TIMER1_RESOLUTION * 64) { + clockSelectBits = 6; + pwmPeriod = cycles >> 6; + } else + if (cycles < TIMER1_RESOLUTION * 128) { + clockSelectBits = 7; + pwmPeriod = cycles >> 7; + } else { + clockSelectBits = 7; + pwmPeriod = TIMER1_RESOLUTION - 1; + } + + uint32_t sc = FTM1_SC; + FTM1_SC = 0; + FTM1_MOD = pwmPeriod; + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_CPWMS | clockSelectBits | (sc & FTM_SC_TOIE); + } + + //**************************** + // Run Control + //**************************** + void start() __attribute__((always_inline)) { + stop(); + FTM1_CNT = 0; + resume(); + } + void stop() __attribute__((always_inline)) { + FTM1_SC = FTM1_SC & (FTM_SC_TOIE | FTM_SC_CPWMS | FTM_SC_PS(7)); + } + void restart() __attribute__((always_inline)) { + start(); + } + void resume() __attribute__((always_inline)) { + FTM1_SC = (FTM1_SC & (FTM_SC_TOIE | FTM_SC_PS(7))) | FTM_SC_CPWMS | FTM_SC_CLKS(1); + } + + //**************************** + // PWM outputs + //**************************** + void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) { + unsigned long dutyCycle = pwmPeriod; + dutyCycle *= duty; + dutyCycle >>= 10; + if (pin == TIMER1_A_PIN) { + FTM1_C0V = dutyCycle; + } else if (pin == TIMER1_B_PIN) { + FTM1_C1V = dutyCycle; + } + } + void pwm(char pin, unsigned int duty) __attribute__((always_inline)) { + setPwmDuty(pin, duty); + if (pin == TIMER1_A_PIN) { + *portConfigRegister(TIMER1_A_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + } else if (pin == TIMER1_B_PIN) { + *portConfigRegister(TIMER1_B_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + } + } + void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) { + if (microseconds > 0) setPeriod(microseconds); + pwm(pin, duty); + } + void disablePwm(char pin) __attribute__((always_inline)) { + if (pin == TIMER1_A_PIN) { + *portConfigRegister(TIMER1_A_PIN) = 0; + } else if (pin == TIMER1_B_PIN) { + *portConfigRegister(TIMER1_B_PIN) = 0; + } + } + + //**************************** + // Interrupt Function + //**************************** + void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { + isrCallback = isr; + FTM1_SC |= FTM_SC_TOIE; + NVIC_ENABLE_IRQ(IRQ_FTM1); + } + void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { + if(microseconds > 0) setPeriod(microseconds); + attachInterrupt(isr); + } + void detachInterrupt() __attribute__((always_inline)) { + FTM1_SC &= ~FTM_SC_TOIE; + NVIC_DISABLE_IRQ(IRQ_FTM1); + } + static void (*isrCallback)(); + static void isrDefaultUnused(); + + private: + // properties + static unsigned short pwmPeriod; + static unsigned char clockSelectBits; + +#undef F_TIMER + +#elif defined(__arm__) && defined(TEENSYDUINO) && (defined(__IMXRT1052__) || defined(__IMXRT1062__)) + + public: + //**************************** + // Configuration + //**************************** + void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { + setPeriod(microseconds); + } + void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { + uint32_t period = (float)F_BUS_ACTUAL * (float)microseconds * 0.0000005f; + uint32_t prescale = 0; + while (period > 32767) { + period = period >> 1; + if (++prescale > 7) { + prescale = 7; // when F_BUS is 150 MHz, longest + period = 32767; // period is 55922 us (~17.9 Hz) + break; + } + } + //Serial.printf("setPeriod, period=%u, prescale=%u\n", period, prescale); + FLEXPWM1_FCTRL0 |= FLEXPWM_FCTRL0_FLVL(8); // logic high = fault + FLEXPWM1_FSTS0 = 0x0008; // clear fault status + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8); + FLEXPWM1_SM3CTRL2 = FLEXPWM_SMCTRL2_INDEP; + FLEXPWM1_SM3CTRL = FLEXPWM_SMCTRL_HALF | FLEXPWM_SMCTRL_PRSC(prescale); + FLEXPWM1_SM3INIT = -period; + FLEXPWM1_SM3VAL0 = 0; + FLEXPWM1_SM3VAL1 = period; + FLEXPWM1_SM3VAL2 = 0; + FLEXPWM1_SM3VAL3 = 0; + FLEXPWM1_SM3VAL4 = 0; + FLEXPWM1_SM3VAL5 = 0; + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8) | FLEXPWM_MCTRL_RUN(8); + pwmPeriod = period; + } + //**************************** + // Run Control + //**************************** + void start() __attribute__((always_inline)) { + stop(); + // TODO: how to force counter back to zero? + resume(); + } + void stop() __attribute__((always_inline)) { + FLEXPWM1_MCTRL &= ~FLEXPWM_MCTRL_RUN(8); + } + void restart() __attribute__((always_inline)) { + start(); + } + void resume() __attribute__((always_inline)) { + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_RUN(8); + } + + //**************************** + // PWM outputs + //**************************** + void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) { + if (duty > 1023) duty = 1023; + int dutyCycle = (pwmPeriod * duty) >> 10; + //Serial.printf("setPwmDuty, period=%u\n", dutyCycle); + if (pin == TIMER1_A_PIN) { + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8); + FLEXPWM1_SM3VAL5 = dutyCycle; + FLEXPWM1_SM3VAL4 = -dutyCycle; + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8); + } else if (pin == TIMER1_B_PIN) { + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8); + FLEXPWM1_SM3VAL3 = dutyCycle; + FLEXPWM1_SM3VAL2 = -dutyCycle; + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8); + } + } + void pwm(char pin, unsigned int duty) __attribute__((always_inline)) { + setPwmDuty(pin, duty); + if (pin == TIMER1_A_PIN) { + FLEXPWM1_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(8); + IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_01 = 6; // pin 6 FLEXPWM1_PWM3_B + } else if (pin == TIMER1_B_PIN) { + FLEXPWM1_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(8); + IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00 = 6; // pin 7 FLEXPWM1_PWM3_A + } + } + void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) { + if (microseconds > 0) setPeriod(microseconds); + pwm(pin, duty); + } + void disablePwm(char pin) __attribute__((always_inline)) { + if (pin == TIMER1_A_PIN) { + IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_01 = 5; // pin 6 FLEXPWM1_PWM3_B + FLEXPWM1_OUTEN &= ~FLEXPWM_OUTEN_PWMB_EN(8); + } else if (pin == TIMER1_B_PIN) { + IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00 = 5; // pin 7 FLEXPWM1_PWM3_A + FLEXPWM1_OUTEN &= ~FLEXPWM_OUTEN_PWMA_EN(8); + } + } + //**************************** + // Interrupt Function + //**************************** + void attachInterrupt(void (*f)()) __attribute__((always_inline)) { + isrCallback = f; + attachInterruptVector(IRQ_FLEXPWM1_3, &isr); + FLEXPWM1_SM3STS = FLEXPWM_SMSTS_RF; + FLEXPWM1_SM3INTEN = FLEXPWM_SMINTEN_RIE; + NVIC_ENABLE_IRQ(IRQ_FLEXPWM1_3); + } + void attachInterrupt(void (*f)(), unsigned long microseconds) __attribute__((always_inline)) { + if(microseconds > 0) setPeriod(microseconds); + attachInterrupt(f); + } + void detachInterrupt() __attribute__((always_inline)) { + NVIC_DISABLE_IRQ(IRQ_FLEXPWM1_3); + FLEXPWM1_SM3INTEN = 0; + } + static void isr(void); + static void (*isrCallback)(); + static void isrDefaultUnused(); + + private: + // properties + static unsigned short pwmPeriod; + static unsigned char clockSelectBits; + +#endif +}; + +extern TimerOne Timer1; + +#endif + diff --git a/examples/ReactionTimeTestGame/known_16bit_timers.h b/examples/ReactionTimeTestGame/known_16bit_timers.h new file mode 100644 index 0000000..01cd4e9 --- /dev/null +++ b/examples/ReactionTimeTestGame/known_16bit_timers.h @@ -0,0 +1,169 @@ +#ifndef known_16bit_timers_header_ +#define known_16bit_timers_header_ + +// Wiring-S +// +#if defined(__AVR_ATmega644P__) && defined(WIRING) + #define TIMER1_A_PIN 5 + #define TIMER1_B_PIN 4 + #define TIMER1_ICP_PIN 6 + +// Teensy 2.0 +// +#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY) + #define TIMER1_A_PIN 14 + #define TIMER1_B_PIN 15 + #define TIMER1_C_PIN 4 + #define TIMER1_ICP_PIN 22 + #define TIMER1_CLK_PIN 11 + #define TIMER3_A_PIN 9 + #define TIMER3_ICP_PIN 10 + +// Teensy++ 2.0 +#elif defined(__AVR_AT90USB1286__) && defined(CORE_TEENSY) + #define TIMER1_A_PIN 25 + #define TIMER1_B_PIN 26 + #define TIMER1_C_PIN 27 + #define TIMER1_ICP_PIN 4 + #define TIMER1_CLK_PIN 6 + #define TIMER3_A_PIN 16 + #define TIMER3_B_PIN 15 + #define TIMER3_C_PIN 14 + #define TIMER3_ICP_PIN 17 + #define TIMER3_CLK_PIN 13 + +// Teensy 3.0 +// +#elif defined(__MK20DX128__) + #define TIMER1_A_PIN 3 + #define TIMER1_B_PIN 4 + #define TIMER1_ICP_PIN 4 + +// Teensy 3.1 / Teensy 3.2 +// +#elif defined(__MK20DX256__) + #define TIMER1_A_PIN 3 + #define TIMER1_B_PIN 4 + #define TIMER1_ICP_PIN 4 + #define TIMER3_A_PIN 32 + #define TIMER3_B_PIN 25 + #define TIMER3_ICP_PIN 32 + +// Teensy 3.5 / Teensy 3.6 +// +#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) + #define TIMER1_A_PIN 3 + #define TIMER1_B_PIN 4 + #define TIMER1_ICP_PIN 4 + #define TIMER3_A_PIN 29 + #define TIMER3_B_PIN 30 + #define TIMER3_ICP_PIN 29 + +// Teensy-LC +// +#elif defined(__MKL26Z64__) + #define TIMER1_A_PIN 16 + #define TIMER1_B_PIN 17 + #define TIMER1_ICP_PIN 17 + #define TIMER3_A_PIN 3 + #define TIMER3_B_PIN 4 + #define TIMER3_ICP_PIN 4 + +// Teensy 4 +// +#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) + #define TIMER1_A_PIN 6 + #define TIMER1_B_PIN 7 + #define TIMER3_A_PIN 8 + #define TIMER3_B_PIN 9 + +// Arduino Mega +// +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + #define TIMER1_A_PIN 11 + #define TIMER1_B_PIN 12 + #define TIMER1_C_PIN 13 + #define TIMER3_A_PIN 5 + #define TIMER3_B_PIN 2 + #define TIMER3_C_PIN 3 + #define TIMER4_A_PIN 6 + #define TIMER4_B_PIN 7 + #define TIMER4_C_PIN 8 + #define TIMER4_ICP_PIN 49 + #define TIMER5_A_PIN 46 + #define TIMER5_B_PIN 45 + #define TIMER5_C_PIN 44 + #define TIMER3_ICP_PIN 48 + #define TIMER3_CLK_PIN 47 + +// Arduino Leonardo, Yun, etc +// +#elif defined(__AVR_ATmega32U4__) + #define TIMER1_A_PIN 9 + #define TIMER1_B_PIN 10 + #define TIMER1_C_PIN 11 + #define TIMER1_ICP_PIN 4 + #define TIMER1_CLK_PIN 12 + #define TIMER3_A_PIN 5 + #define TIMER3_ICP_PIN 13 + +// Uno, Duemilanove, LilyPad, etc +// +#elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega328P__) || defined (__AVR_ATmega328__) || defined (__AVR_ATmega8__) + #define TIMER1_A_PIN 9 + #define TIMER1_B_PIN 10 + #define TIMER1_ICP_PIN 8 + #define TIMER1_CLK_PIN 5 + + // attiny167 +// +#elif defined (__AVR_ATtiny167__) + #define TIMER1_A_PIN 14 + #define TIMER1_B_PIN 11 + //#define TIMER1_ICP_PIN 8 + //#define TIMER1_CLK_PIN 5 + +// Sanguino +// +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) + #define TIMER1_A_PIN 13 + #define TIMER1_B_PIN 12 + #define TIMER1_ICP_PIN 14 + #define TIMER1_CLK_PIN 1 + +// Wildfire - Wicked Devices +// +#elif defined(__AVR_ATmega1284P__) && defined(WILDFIRE_VERSION) && WILDFIRE_VERSION >= 3 + #define TIMER1_A_PIN 5 // PD5 + #define TIMER1_B_PIN 8 // PD4 + #define TIMER1_ICP_PIN 6 // PD6 + #define TIMER1_CLK_PIN 23 // PB1 + #define TIMER3_A_PIN 12 // PB6 + #define TIMER3_B_PIN 13 // PB7 + #define TIMER3_ICP_PIN 9 // PB5 + #define TIMER3_CLK_PIN 0 // PD0 +#elif defined(__AVR_ATmega1284P__) && defined(WILDFIRE_VERSION) && WILDFIRE_VERSION < 3 + #define TIMER1_A_PIN 5 // PD5 + #define TIMER1_B_PIN 4 // PD4 + #define TIMER1_ICP_PIN 6 // PD6 + #define TIMER1_CLK_PIN 15 // PB1 + #define TIMER3_A_PIN 12 // PB6 + #define TIMER3_B_PIN 13 // PB7 + #define TIMER3_ICP_PIN 11 // PB5 + #define TIMER3_CLK_PIN 0 // PD0 + +// Mighty-1284 - Maniacbug +// +#elif defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + #define TIMER1_A_PIN 12 // PD5 + #define TIMER1_B_PIN 13 // PD4 + #define TIMER1_ICP_PIN 14 // PD6 + #define TIMER1_CLK_PIN 1 // PB1 + #define TIMER3_A_PIN 6 // PB6 + #define TIMER3_B_PIN 7 // PB7 + #define TIMER3_ICP_PIN 5 // PB5 + #define TIMER3_CLK_PIN 8 // PD0 + +#endif + +#endif diff --git a/library.json b/library.json index 6f38c91..2feb470 100644 --- a/library.json +++ b/library.json @@ -6,7 +6,7 @@ "type": "git", "url": "https://github.com/ArminJo/PlayRtttl" }, - "version": "1.4.1", + "version": "1.4.2", "exclude": "pictures", "authors": { "name": "Armin Joachimsmeyer", diff --git a/library.properties b/library.properties index 6d83460..3d642a6 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=PlayRtttl -version=1.4.1 +version=1.4.2 author=Armin Joachimsmeyer maintainer=Armin Joachimsmeyer sentence=Plays RTTTL / RTX melodies/ringtones from FLASH or RAM.
-paragraph=Improved Arduino library version of the RTTTL.pde example code written by Brett Hagman.
Uses the Arduino tone() function.
Fatures:
  • Non blocking version.
  • support all octaves below 8.
  • Name output function.
  • Sample melodies.
  • Random play of melodies array.
  • Supports inverted tone pin logic i.e. tone pin is HIGH at playing a pause.
  • Accepts even invalid specified RTTTL files found in the wild.
  • Support of RTX format.
  • setNumberOfLoops() and setDefaultStyle() functions.


New: Removed blocking wait for ATmega32U4 Serial in examples.
+paragraph=Improved Arduino library version of the RTTTL.pde example code written by Brett Hagman.
Uses the Arduino tone() function.
Fatures:
  • Non blocking version.
  • support all octaves below 8.
  • Name output function.
  • Sample melodies.
  • Random play of melodies array.
  • Supports inverted tone pin logic i.e. tone pin is HIGH at playing a pause.
  • Accepts even invalid specified RTTTL files found in the wild.
  • Support of RTX format.
  • setNumberOfLoops() and setDefaultStyle() functions.

New: New example ReactionTimeTestGame.
category=Other url=https://github.com/ArminJo/PlayRtttl architectures=* diff --git a/src/PlayRtttl.cpp b/src/PlayRtttl.cpp index 99556f6..ee7d05a 100644 --- a/src/PlayRtttl.cpp +++ b/src/PlayRtttl.cpp @@ -12,7 +12,7 @@ * * The example melodies may have copyrights you have to respect. * - * This file is part of PlayRttl https://github.com/ArminJo/PlayRttl. + * This file is part of PlayRttl https://github.com/ArminJo/PlayRtttl. * * PlayRttl is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ #include @@ -75,7 +74,7 @@ void playRtttlBlocking(uint8_t aTonePin, const char *aRTTTLArrayPtr) { * Version for RTTTL Data in RAM. Ie. you must call updatePlayRtttl() in your loop. * Since we do not need all the pgm_read_byte() calls this version is more simple and maybe better to understand. */ -void startPlayRtttl(uint8_t aTonePin, const char * aRTTTLArrayPtr, void (*aOnComplete)()) { +void startPlayRtttl(uint8_t aTonePin, const char *aRTTTLArrayPtr, void (*aOnComplete)()) { sPlayRtttlState.Flags.IsPGMMemory = false; sPlayRtttlState.OnComplete = aOnComplete; sPlayRtttlState.TonePin = aTonePin; @@ -249,7 +248,7 @@ void stopPlayRtttl(void) { sPlayRtttlState.Flags.IsRunning = false; } -char getNextCharFromRTTLArray(const char* aRTTTLArrayPtr) { +char getNextCharFromRTTLArray(const char *aRTTTLArrayPtr) { if (sPlayRtttlState.Flags.IsPGMMemory) { return pgm_read_byte(aRTTTLArrayPtr); } @@ -272,7 +271,7 @@ bool updatePlayRtttl(void) { long tMillis = millis(); if (tMillis >= sPlayRtttlState.MillisOfNextAction) { - const char * tRTTTLArrayPtr = sPlayRtttlState.NextTonePointer; + const char *tRTTTLArrayPtr = sPlayRtttlState.NextTonePointer; char tChar; tChar = getNextCharFromRTTLArray(tRTTTLArrayPtr); @@ -408,7 +407,7 @@ bool updatePlayRtttl(void) { /* * now play the note */ -# if defined (SUPPORT_RTX_EXTENSIONS) +# if defined(SUPPORT_RTX_EXTENSIONS) #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" unsigned long tDurationOfTone; #endif @@ -422,7 +421,7 @@ bool updatePlayRtttl(void) { #if defined(ESP32) ledcWriteTone(0, tFrequency); #else -# if defined (SUPPORT_RTX_EXTENSIONS) +# if defined(SUPPORT_RTX_EXTENSIONS) if (sPlayRtttlState.StyleDivisorValue != 0) { /* * handle style parameter, compute duration of tone output for note and do rounding for integer division @@ -440,7 +439,7 @@ bool updatePlayRtttl(void) { # endif # if defined(TCCR2A) - if(sPlayRtttlState.TonePin == 11) { + if (sPlayRtttlState.TonePin == 11) { // switch to direct hardware toggle output at OC2A / pin 11 TCCR2A |= _BV(COM2A0); } @@ -482,7 +481,7 @@ bool updatePlayRtttl(void) { sPointerToSerial->print(Notes[tNote] >> (NOTES_OCTAVE - tOctave), 10); # endif sPointerToSerial->print(F(" Hz for ")); -# if defined (SUPPORT_RTX_EXTENSIONS) +# if defined(SUPPORT_RTX_EXTENSIONS) if (sPlayRtttlState.StyleDivisorValue != 0 && tNote <= 12) { sPointerToSerial->print(tDurationOfTone, 10); sPointerToSerial->print(F(" of ")); @@ -498,7 +497,7 @@ bool updatePlayRtttl(void) { return true; } -void getRtttlName(const char *aRTTTLArrayPtr, char * aBuffer, uint8_t aBuffersize) { +void getRtttlName(const char *aRTTTLArrayPtr, char *aBuffer, uint8_t aBuffersize) { char tChar = *aRTTTLArrayPtr++; while (tChar != ':' && aBuffersize > 1) { *aBuffer++ = tChar; @@ -512,7 +511,7 @@ void getRtttlName(const char *aRTTTLArrayPtr, char * aBuffer, uint8_t aBuffersiz * Prints text "Now playing: Song xy" * call it e.g. printNamePGM(RTTTLMelodies[tRandomIndex], &Serial); */ -void printName(const char *aRTTTLArrayPtr, Print * aSerial) { +void printName(const char *aRTTTLArrayPtr, Print *aSerial) { char StringBuffer[16]; aSerial->print(F("Now playing: ")); getRtttlName(aRTTTLArrayPtr, StringBuffer, sizeof(StringBuffer)); @@ -525,7 +524,7 @@ void printName(const char *aRTTTLArrayPtr, Print * aSerial) { * char StringBuffer[16] is sufficient for most titles. */ void startPlayRandomRtttlFromArray(uint8_t aTonePin, const char * const aSongArray[], uint8_t aNumberOfEntriesInSongArray, - char* aBufferPointer, uint8_t aBufferSize, void (*aOnComplete)()) { + char *aBufferPointer, uint8_t aBufferSize, void (*aOnComplete)()) { uint8_t tRandomIndex = random(0, aNumberOfEntriesInSongArray - 1); char* tSongPtr = (char*) aSongArray[tRandomIndex]; startPlayRtttl(aTonePin, tSongPtr, aOnComplete); @@ -536,7 +535,7 @@ void startPlayRandomRtttlFromArray(uint8_t aTonePin, const char * const aSongArr } void startPlayRandomRtttlFromArrayAndPrintName(uint8_t aTonePin, const char * const aSongArray[], - uint8_t aNumberOfEntriesInSongArray, Print * aSerial, void (*aOnComplete)()) { + uint8_t aNumberOfEntriesInSongArray, Print *aSerial, void (*aOnComplete)()) { uint8_t tRandomIndex = random(0, aNumberOfEntriesInSongArray - 1); char* tSongPtr = (char*) aSongArray[tRandomIndex]; startPlayRtttl(aTonePin, tSongPtr, aOnComplete); @@ -548,12 +547,12 @@ void startPlayRandomRtttlFromArrayAndPrintName(uint8_t aTonePin, const char * co * Plays one of the samples from RTTTLMelodies array */ void playRandomRtttlSampleBlocking(uint8_t aTonePin) { - uint8_t tRandomIndex = random(0, sizeof(RTTTLMelodies) / sizeof(char *) - 1); + uint8_t tRandomIndex = random(0, sizeof(RTTTLMelodies) / sizeof(char*) - 1); char* tSongPtr = (char*) RTTTLMelodies[tRandomIndex]; playRtttlBlocking(aTonePin, tSongPtr); } -void playRandomRtttlSampleBlockingAndPrintName(uint8_t aTonePin, Print * aSerial) { +void playRandomRtttlSampleBlockingAndPrintName(uint8_t aTonePin, Print *aSerial) { uint8_t tRandomIndex = random(0, sizeof(RTTTLMelodies) / sizeof(char *) - 1); char* tSongPtr = (char*) RTTTLMelodies[tRandomIndex]; printName(tSongPtr, aSerial); @@ -570,7 +569,7 @@ void playRtttlBlockingPGM(uint8_t aTonePin, const char *aRTTTLArrayPtrPGM) { /* * Non blocking version for RTTTL Data in FLASH. Ie. you must call updatePlayRtttl() in your loop. */ -void startPlayRtttlPGM(uint8_t aTonePin, const char * aRTTTLArrayPtrPGM, void (*aOnComplete)()) { +void startPlayRtttlPGM(uint8_t aTonePin, const char *aRTTTLArrayPtrPGM, void (*aOnComplete)()) { sPlayRtttlState.Flags.IsPGMMemory = true; sPlayRtttlState.OnComplete = aOnComplete; sPlayRtttlState.TonePin = aTonePin; @@ -598,7 +597,7 @@ void startPlayRtttlPGM(uint8_t aTonePin, const char * aRTTTLArrayPtrPGM, void (* sPlayRtttlState.DefaultDuration = DEFAULT_DURATION; sPlayRtttlState.DefaultOctave = DEFAULT_OCTAVE; sPlayRtttlState.TimeForWholeNoteMillis = (60 * 1000L / DEFAULT_BPM) * 4; -#if defined (SUPPORT_RTX_EXTENSIONS) +#if defined(SUPPORT_RTX_EXTENSIONS) sPlayRtttlState.NumberOfLoops = 1; sPlayRtttlState.StyleDivisorValue = sDefaultStyleDivisorValue; #endif @@ -740,7 +739,7 @@ void startPlayRtttlPGM(uint8_t aTonePin, const char * aRTTTLArrayPtrPGM, void (* updatePlayRtttl(); } -void getRtttlNamePGM(const char *aRTTTLArrayPtrPGM, char * aBuffer, uint8_t aBuffersize) { +void getRtttlNamePGM(const char *aRTTTLArrayPtrPGM, char *aBuffer, uint8_t aBuffersize) { #if !defined(__AVR__) // Let the function work for non AVR platforms getRtttlName(aRTTTLArrayPtrPGM, aBuffer, aBuffersize); #else @@ -754,7 +753,7 @@ void getRtttlNamePGM(const char *aRTTTLArrayPtrPGM, char * aBuffer, uint8_t aBuf #endif } -void printNamePGM(const char *aRTTTLArrayPtrPGM, Print * aSerial) { +void printNamePGM(const char *aRTTTLArrayPtrPGM, Print *aSerial) { #if !defined(__AVR__) // Let the function work for non AVR platforms printName(aRTTTLArrayPtrPGM, aSerial); #else @@ -772,7 +771,7 @@ void printNamePGM(const char *aRTTTLArrayPtrPGM, Print * aSerial) { * char StringBuffer[16] is sufficient for most titles. */ void startPlayRandomRtttlFromArrayPGM(uint8_t aTonePin, const char * const aSongArrayPGM[], uint8_t aNumberOfEntriesInSongArrayPGM, - char* aBufferPointer, uint8_t aBufferSize, void (*aOnComplete)()) { + char *aBufferPointer, uint8_t aBufferSize, void (*aOnComplete)()) { #if !defined(__AVR__) // Let the function work for non AVR platforms startPlayRandomRtttlFromArray(aTonePin, aSongArrayPGM, aNumberOfEntriesInSongArrayPGM, aBufferPointer, aBufferSize, aOnComplete); #else @@ -790,7 +789,7 @@ void startPlayRandomRtttlFromArrayPGM(uint8_t aTonePin, const char * const aSong * !!! Songs are in an array stored in FLASH containing pointers to song arrays also stored in FLASH, see PlayRtttl.h. !!! */ void startPlayRandomRtttlFromArrayPGMAndPrintName(uint8_t aTonePin, const char * const aSongArrayPGM[], - uint8_t aNumberOfEntriesInSongArrayPGM, Print * aSerial, void (*aOnComplete)()) { + uint8_t aNumberOfEntriesInSongArrayPGM, Print *aSerial, void (*aOnComplete)()) { #if !defined(__AVR__) // Let the function work for non AVR platforms startPlayRandomRtttlFromArrayAndPrintName(aTonePin, aSongArrayPGM, aNumberOfEntriesInSongArrayPGM, aSerial, aOnComplete); #else @@ -815,7 +814,7 @@ void playRandomRtttlSampleBlockingPGM(uint8_t aTonePin) { #endif } -void playRandomRtttlSampleBlockingPGMAndPrintName(uint8_t aTonePin, Print * aSerial) { +void playRandomRtttlSampleBlockingPGMAndPrintName(uint8_t aTonePin, Print *aSerial) { #if !defined(__AVR__) // Let the function work for non AVR platforms playRandomRtttlSampleBlockingAndPrintName(aTonePin, aSerial); #else diff --git a/src/PlayRtttl.h b/src/PlayRtttl.h index b61e514..2feb1c3 100644 --- a/src/PlayRtttl.h +++ b/src/PlayRtttl.h @@ -13,7 +13,7 @@ * bhagman@roguerobotics.com * * - * This file is part of PlayRttl https://github.com/ArminJo/PlayRttl. + * This file is part of PlayRttl https://github.com/ArminJo/PlayRtttl. * * PlayRttl is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,49 +34,17 @@ #define SRC_PLAYRTTTL_H_ #if defined(__SAM3X8E__) -#error "Sorry no tone library for Arduino Due" +#error Sorry no tone library for Arduino Due #endif #if defined(__AVR__) #include #endif #include "pitches.h" -#define VERSION_PLAY_RTTTL "1.4.1" +#define VERSION_PLAY_RTTTL "1.4.2" #define VERSION_PLAY_RTTTL_MAJOR 1 #define VERSION_PLAY_RTTTL_MINOR 4 - -/* - * Version 1.4.1 - 9/2020 - * - Removed blocking wait for ATmega32U4 Serial in examples. - * - * Version 1.4.0 - 1/2020 - * - Supporting direct tone output at pin 11 for ATmega328. Can be used with interrupt blocking libraries for NeoPixel etc. - * - Use Print * instead of Stream *. - * - Improved non-AVR compatibility. - * - New Christmas songs example. - * - * Version 1.3.0 - 10/2019 - * - Support all octaves below 8. - * - New styles '1' to '9' in addition to RTX styles 'C', 'N', 'S'. - * - * Version 1.2.2 - 6/2019 - * - Porting to non AVR architectures. - * - * Version 1.2.1 - 5/2019 - * - Natural is the new default style. - * - New RTTTLMelodiesSmall sample array with less entries. - * - Parameter now order independent. - * - Modified oneMelody example. - * - * Version 1.2.0 - 5/2019 - * - No Serial.print statements in this library anymore, to avoid problems with different Serial implementations. - * - Function playRandomRtttlBlocking() + startPlayRandomRtttlFromArrayPGM() do not print name now. If needed, use new functions playRandomRtttlSampleBlockingAndPrintName() + startPlayRandomRtttlFromArrayPGMAndPrintName(). - * - Printing functions have parameter (..., Print * aSerial) to print to any serial. Call it (..., &Serial) to use standard Serial; - * - playRandomRtttlBlocking() renamed to playRandomRtttlSampleBlocking() and bug fixing. - * - * Version 1.1 - 5/2019 - * - new setNumberOfLoops() and setDefaultStyle() functions. - */ +// The change log is at the bottom of the file #if ! defined(USE_NO_RTX_EXTENSIONS) // if defined it suppresses the next 2 defines / useful for ATtinies to shrink code up to 182 bytes // Even without `SUPPORT_RTX_EXTENSIONS` the default style is natural (Tone length = note length - 1/16) @@ -114,33 +82,33 @@ void setDefaultStyle(uint8_t aDefaultStyleDivisorValue); uint8_t convertStyleCharacterToDivisorValue(char aStyleCharacter); #endif -void getRtttlName(const char *aRTTTLArrayPtr, char * aBuffer, uint8_t aBuffersize); -void printName(const char *aRTTTLArrayPtr, Print * aSerial); +void getRtttlName(const char *aRTTTLArrayPtr, char *aBuffer, uint8_t aBuffersize); +void printName(const char *aRTTTLArrayPtr, Print *aSerial); void startPlayRtttl(uint8_t aTonePin, const char *aRTTTLArrayPtr, void (*aOnComplete)()=NULL); void playRtttlBlocking(uint8_t aTonePin, const char *aRTTTLArrayPtr); void startPlayRandomRtttlFromArray(uint8_t aTonePin, const char* const aSongArray[], uint8_t aNumberOfEntriesInSongArray, - char* aBufferPointer = NULL, uint8_t aBufferSize = 0, void (*aOnComplete)()=NULL); + char *aBufferPointer = NULL, uint8_t aBufferSize = 0, void (*aOnComplete)()=NULL); void startPlayRandomRtttlFromArrayAndPrintName(uint8_t aTonePin, const char* const aSongArray[], - uint8_t aNumberOfEntriesInSongArray, Print * aSerial, void (*aOnComplete)()=NULL); + uint8_t aNumberOfEntriesInSongArray, Print *aSerial, void (*aOnComplete)()=NULL); void playRandomRtttlSampleBlocking(uint8_t aTonePin); -void playRandomRtttlSampleBlockingAndPrintName(uint8_t aTonePin, Print * aSerial); +void playRandomRtttlSampleBlockingAndPrintName(uint8_t aTonePin, Print *aSerial); -void getRtttlNamePGM(const char *aRTTTLArrayPtrPGM, char * aBuffer, uint8_t aBuffersize); -void printNamePGM(const char *aRTTTLArrayPtrPGM, Print * aSerial); +void getRtttlNamePGM(const char *aRTTTLArrayPtrPGM, char *aBuffer, uint8_t aBuffersize); +void printNamePGM(const char *aRTTTLArrayPtrPGM, Print *aSerial); void startPlayRtttlPGM(uint8_t aTonePin, const char *aRTTTLArrayPtrPGM, void (*aOnComplete)()=NULL); void playRtttlBlockingPGM(uint8_t aTonePin, const char *aRTTTLArrayPtrPGM); void startPlayRandomRtttlFromArrayPGM(uint8_t aTonePin, const char* const aSongArrayPGM[], uint8_t aNumberOfEntriesInSongArrayPGM, - char* aBufferPointer = NULL, uint8_t aBufferSize = 0, void (*aOnComplete)()=NULL); + char *aBufferPointer = NULL, uint8_t aBufferSize = 0, void (*aOnComplete)()=NULL); void startPlayRandomRtttlFromArrayPGMAndPrintName(uint8_t aTonePin, const char* const aSongArrayPGM[], - uint8_t aNumberOfEntriesInSongArrayPGM, Print * aSerial, void (*aOnComplete)()=NULL); + uint8_t aNumberOfEntriesInSongArrayPGM, Print *aSerial, void (*aOnComplete)()=NULL); void playRandomRtttlSampleBlockingPGM(uint8_t aTonePin); -void playRandomRtttlSampleBlockingPGMAndPrintName(uint8_t aTonePin, Print * aSerial); +void playRandomRtttlSampleBlockingPGMAndPrintName(uint8_t aTonePin, Print *aSerial); // To be called from loop. - Returns true if tone is playing, false if tone has ended or stopped bool updatePlayRtttl(void); @@ -149,7 +117,7 @@ void stopPlayRtttl(void); struct playRtttlState { long MillisOfNextAction; - const char * NextTonePointer; + const char *NextTonePointer; struct { uint8_t IsRunning :1; // is false after boot @@ -170,7 +138,7 @@ struct playRtttlState { // The divisor for the formula: Tone length = note length - note length * (1 / divisor) // If 0 then Tone length = note length; uint8_t StyleDivisorValue; - const char * LastTonePointer; // used for loops + const char *LastTonePointer; // used for loops #endif }; @@ -298,4 +266,40 @@ static const char * const RTTTLChristmasMelodies[] PROGMEM = { JingleBell, Rudol WinterWonderland, LetItSnow, Frosty, LastChristmas, AllIWant, AmazingGrace }; #define ARRAY_SIZE_CHRISTMAS_MELODIES (sizeof(RTTTLChristmasMelodies)/sizeof(const char *)) // 11 +/* + * Version 1.4.2 11/2020 + * - New example ReactionTimeTestGame. + * + * Version 1.4.1 - 9/2020 + * - Removed blocking wait for ATmega32U4 Serial in examples. + * + * Version 1.4.0 - 1/2020 + * - Supporting direct tone output at pin 11 for ATmega328. Can be used with interrupt blocking libraries for NeoPixel etc. + * - Use Print * instead of Stream *. + * - Improved non-AVR compatibility. + * - New Christmas songs example. + * + * Version 1.3.0 - 10/2019 + * - Support all octaves below 8. + * - New styles '1' to '9' in addition to RTX styles 'C', 'N', 'S'. + * + * Version 1.2.2 - 6/2019 + * - Porting to non AVR architectures. + * + * Version 1.2.1 - 5/2019 + * - Natural is the new default style. + * - New RTTTLMelodiesSmall sample array with less entries. + * - Parameter now order independent. + * - Modified oneMelody example. + * + * Version 1.2.0 - 5/2019 + * - No Serial.print statements in this library anymore, to avoid problems with different Serial implementations. + * - Function playRandomRtttlBlocking() + startPlayRandomRtttlFromArrayPGM() do not print name now. If needed, use new functions playRandomRtttlSampleBlockingAndPrintName() + startPlayRandomRtttlFromArrayPGMAndPrintName(). + * - Printing functions have parameter (..., Print *aSerial) to print to any serial. Call it (..., &Serial) to use standard Serial; + * - playRandomRtttlBlocking() renamed to playRandomRtttlSampleBlocking() and bug fixing. + * + * Version 1.1 - 5/2019 + * - new setNumberOfLoops() and setDefaultStyle() functions. + */ + #endif /* SRC_PLAYRTTTL_H_ */