From c00cb2b5211e75259c86ce7e146bbd03c6adc6a1 Mon Sep 17 00:00:00 2001 From: 25mmHg <23mmhg@gmail.com> Date: Wed, 11 Aug 2021 20:41:32 +0200 Subject: [PATCH 1/3] create debugserial --- SolarbirdTiny/SolarbirdTiny.ino | 261 ++++++++++++++++--------------- SolarbirdTiny/system/serialout.S | 40 +++++ SolarbirdTiny/system/serialout.h | 47 ++++++ 3 files changed, 225 insertions(+), 123 deletions(-) create mode 100644 SolarbirdTiny/system/serialout.S create mode 100644 SolarbirdTiny/system/serialout.h diff --git a/SolarbirdTiny/SolarbirdTiny.ino b/SolarbirdTiny/SolarbirdTiny.ino index 6c3c1cf..34e07bb 100644 --- a/SolarbirdTiny/SolarbirdTiny.ino +++ b/SolarbirdTiny/SolarbirdTiny.ino @@ -9,6 +9,7 @@ #define PIN_LED_1_ANODE 0 #define PIN_LED_2_ANODE 1 #define PIN_LED_CATHODES 2 +#define DEBUGMODE // High-level access to I/O and system features // @@ -16,6 +17,7 @@ #include "system/leds.h" #include "system/piezo.h" #include "system/lowpower.h" +#include "system/serialOut.h" // Bird voice // @@ -28,13 +30,13 @@ Blackbird Bird; const bool runStartupTest = false; const bool powerDownAfterStartIfDark = false; -const unsigned long powerDownAfterStartIfDarkTimeoutMillis = 5*1000; +const unsigned long powerDownAfterStartIfDarkTimeoutMillis = 5 * 1000; const bool lightLEDsWhenPlaying = true; const int activityMaximum = 8; // should be divisible by the difference of activeSoundInterval* const int activeSoundIntervalMinimum = 1; const int activeSoundIntervalMaximum = 5; // should be a power of 2 greater than the minimum -const int idleSoundIntervalMinimum = 60*10; // all intervals must be smaller than 32768 -const int idleSoundIntervalMaximum = 60*60; +const int idleSoundIntervalMinimum = 60 * 10; // all intervals must be smaller than 32768 +const int idleSoundIntervalMaximum = 60 * 60; const int nightModeSleepSeconds = 2; const bool silentAtNight = false; @@ -43,127 +45,140 @@ const bool silentAtNight = false; void setup() { - // wait to make sure programmer is high-Z - // and be gentle with the power source - delay( 100 ); - - if ( runStartupTest ) - { - // startup test: flash LEDs - LEDs::on(); - delay( 50 ); - LEDs::off(); - delay( 500 ); - - // startup test: play all melodies once with fixed delay in between - for ( int i = 0; i < Bird.numMelodies; i++ ) { - Bird.playSingleMelody( i, lightLEDsWhenPlaying ); - delay( 1000 ); - } - - delay( 1000 ); - } - - if ( powerDownAfterStartIfDark ) - { - // check if environment is not dark - bool dark = true; - unsigned long startTime = millis(); - while ( millis()-startTime <= powerDownAfterStartIfDarkTimeoutMillis ) { - if ( LEDs::senseRawBrightness( false ) > 0 ) { - dark = false; - break; - } - } - if ( dark ) { - // no brightness sensed within defined time window, power down and don't wake up until external reset - Piezo::on(); - Piezo::tone( 1E6/220, 1E6 ); - Piezo::off(); - LowPower::powerDown(); - } - } - - // initialize random number generator with external source of randomness - pinMode( PIN_LED_1_ANODE, INPUT ); pinMode( PIN_LED_2_ANODE, INPUT ); - randomSeed( analogRead( PIN_LED_1_ANODE ) ^ analogRead( PIN_LED_2_ANODE ) ); + // wait to make sure programmer is high-Z + // and be gentle with the power source + delay( 100 ); +#ifdef DEBUGMODE + SerialOut::printString("\nH E L L O\n"); +#endif + + if ( runStartupTest ) + { + // startup test: flash LEDs + LEDs::on(); + delay( 50 ); + LEDs::off(); + delay( 500 ); + + // startup test: play all melodies once with fixed delay in between + for ( int i = 0; i < Bird.numMelodies; i++ ) { + Bird.playSingleMelody( i, lightLEDsWhenPlaying ); + delay( 1000 ); + } + + delay( 1000 ); + } + + if ( powerDownAfterStartIfDark ) + { + // check if environment is not dark + bool dark = true; + unsigned long startTime = millis(); + while ( millis() - startTime <= powerDownAfterStartIfDarkTimeoutMillis ) { + if ( LEDs::senseRawBrightness( false ) > 0 ) { + dark = false; + break; + } + } + if ( dark ) { + // no brightness sensed within defined time window, power down and don't wake up until external reset + Piezo::on(); + Piezo::tone( 1E6 / 220, 1E6 ); + Piezo::off(); + LowPower::powerDown(); + } + } + + // initialize random number generator with external source of randomness + pinMode( PIN_LED_1_ANODE, INPUT ); pinMode( PIN_LED_2_ANODE, INPUT ); + randomSeed( analogRead( PIN_LED_1_ANODE ) ^ analogRead( PIN_LED_2_ANODE ) ); } void loop() { - static int lastBrightness = 0; - static int activity = 0; - static bool active = false; - static int cyclesBeforeNextSound = 0; - - // measure current brightness (logarithmic value, proportional to order of magnitude) - int brightness = LEDs::senseBrightness(); - - // determine if in darkness (i.e., night mode) - bool dark = lastBrightness == 0 && brightness == 0; - - // compare with previous brightness (don't care for sign) and save for next cycle - int brightnessActivity = abs( lastBrightness - brightness ); - lastBrightness = brightness; - - // accumulate activity within limit - activity = activity + brightnessActivity; - activity = min( activityMaximum, activity ); - - // true if just became active in current cycle - bool justBecameActive = false; - - // sensed activity? - if ( activity > 0 ) - { - // flash LEDs, the more activity the longer - LEDs::on(); - delay( activity * 2 ); - LEDs::off(); - - // just became active? - if ( !active ) - { - // set flag and remember active state - justBecameActive = true; - active = true; - } - } - else - { - // clear active state - active = false; - } - - // time to make noise? - // either after scheduled interval or on transition from idle to active - if ( cyclesBeforeNextSound == 0 || justBecameActive ) - { - // play sound - Bird.play( lightLEDsWhenPlaying ); - - // determine waiting time before next sound (both for active and idle case) - if ( activity > 0 ) - { - // active case = mapping of max activity to min interval plus one cycle of randomness - cyclesBeforeNextSound = (activityMaximum-activity) * (activeSoundIntervalMaximum-activeSoundIntervalMinimum) / activityMaximum + activeSoundIntervalMinimum + random( 0, 1+1 ); - } - else - { - cyclesBeforeNextSound = random( idleSoundIntervalMinimum, idleSoundIntervalMaximum+1 ); - } - } - else - { - // stop counter when in night mode, i.e., remain silent at night - if ( !dark || !silentAtNight ) { - cyclesBeforeNextSound--; - } - } - - // decrease activity level - if ( activity > 0 ) { activity--; } - - // wait 1 second by day (2 seconds by night) until next cycle - LowPower::sleepSeconds( dark && !active ? nightModeSleepSeconds : 1 ); -} + static int lastBrightness = 0; + static int activity = 0; + static bool active = false; + static int cyclesBeforeNextSound = 0; + + // measure current brightness (logarithmic value, proportional to order of magnitude) + int brightness = LEDs::senseBrightness(); +#ifdef DEBUGMODE + SerialOut::printString("\nbrightness = "); + SerialOut::printHex(brightness); +#endif + + // determine if in darkness (i.e., night mode) + bool dark = lastBrightness == 0 && brightness == 0; + + // compare with previous brightness (don't care for sign) and save for next cycle + int brightnessActivity = abs( lastBrightness - brightness ); + lastBrightness = brightness; + + // accumulate activity within limit + activity = activity + brightnessActivity; + activity = min( activityMaximum, activity ); +#ifdef DEBUGMODE + SerialOut::printString("\nactivity = "); + SerialOut::printHex(activity); +#endif + + // true if just became active in current cycle + bool justBecameActive = false; + + // sensed activity? + if ( activity > 0 ) + { + // flash LEDs, the more activity the longer + LEDs::on(); + delay( activity * 2 ); + LEDs::off(); + + // just became active? + if ( !active ) + { + // set flag and remember active state + justBecameActive = true; + active = true; + } + } + else + { + // clear active state + active = false; + } + + // time to make noise? + // either after scheduled interval or on transition from idle to active + if ( cyclesBeforeNextSound == 0 || justBecameActive ) + { + // play sound + Bird.play( lightLEDsWhenPlaying ); + + // determine waiting time before next sound (both for active and idle case) + if ( activity > 0 ) + { + // active case = mapping of max activity to min interval plus one cycle of randomness + cyclesBeforeNextSound = (activityMaximum - activity) * (activeSoundIntervalMaximum - activeSoundIntervalMinimum) / activityMaximum + activeSoundIntervalMinimum + random( 0, 1 + 1 ); + } + else + { + cyclesBeforeNextSound = random( idleSoundIntervalMinimum, idleSoundIntervalMaximum + 1 ); + } + } + else + { + // stop counter when in night mode, i.e., remain silent at night + if ( !dark || !silentAtNight ) { + cyclesBeforeNextSound--; + } + } + + // decrease activity level + if ( activity > 0 ) { + activity--; + } + + // wait 1 second by day (2 seconds by night) until next cycle + LowPower::sleepSeconds( dark && !active ? nightModeSleepSeconds : 1 ); +}} diff --git a/SolarbirdTiny/system/serialout.S b/SolarbirdTiny/system/serialout.S new file mode 100644 index 0000000..978913e --- /dev/null +++ b/SolarbirdTiny/system/serialout.S @@ -0,0 +1,40 @@ +/* half-duplex 81N serial uart in hand-tuned assembler + * 1%/2% Tx/Rx timing error for 115.2kbps@8Mhz + * 2%/1% Tx/Rx timing error for 230.4kbps@8Mhz + * optimized for no jitter vs AVR305 with 1 cycle/bit jitter + * @author: Ralph Doncaster + * @version: $Id$ + * link: https://nerdralph.blogspot.com/2014/01/avr-half-duplex-software-uart.html + * shrink to TX only by 25mmHg + */ + +#define delayCount r18 +#include +; correct for avr/io.h 0x20 port offset for io instructions +#define UART_Port (PORTB-0x20) +#define UART_Tx 4 // DISCONNECT PIEZO !!!!!!!!!!!!!!!!!!!!!!!! + +.global TxTimedByte +; transmit byte in r24 with bit delay in r22 - 15 instructions +; calling code must set Tx line to idle state (high) or 1st byte may be lost +; i.e. PORTB |= (1<> (4*(3-i))) & mask); + temp = (temp < 0xA ? temp+'0' : temp-0xA+'A'); + TxTimedByte(temp, TXDELAY); + } + } +}; \ No newline at end of file From faf66741dd072e94ff788f8c53d8ec1ea72dd5af Mon Sep 17 00:00:00 2001 From: 25mmHg <23mmhg@gmail.com> Date: Thu, 12 Aug 2021 23:43:51 +0200 Subject: [PATCH 2/3] debugging --- SolarbirdTiny/SolarbirdTiny.ino | 4 ++-- SolarbirdTiny/{system => }/serialout.S | 6 +++++- SolarbirdTiny/system/serialout.h | 12 ++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) rename SolarbirdTiny/{system => }/serialout.S (88%) diff --git a/SolarbirdTiny/SolarbirdTiny.ino b/SolarbirdTiny/SolarbirdTiny.ino index 34e07bb..81f96f1 100644 --- a/SolarbirdTiny/SolarbirdTiny.ino +++ b/SolarbirdTiny/SolarbirdTiny.ino @@ -17,7 +17,7 @@ #include "system/leds.h" #include "system/piezo.h" #include "system/lowpower.h" -#include "system/serialOut.h" +#include "system/serialout.h" // Bird voice // @@ -181,4 +181,4 @@ void loop() // wait 1 second by day (2 seconds by night) until next cycle LowPower::sleepSeconds( dark && !active ? nightModeSleepSeconds : 1 ); -}} +} diff --git a/SolarbirdTiny/system/serialout.S b/SolarbirdTiny/serialout.S similarity index 88% rename from SolarbirdTiny/system/serialout.S rename to SolarbirdTiny/serialout.S index 978913e..4f415ed 100644 --- a/SolarbirdTiny/system/serialout.S +++ b/SolarbirdTiny/serialout.S @@ -1,9 +1,12 @@ +;25mmHg Kazoosh, 2021 +; must be in the same directory like the .ino!!!!!!!!!!!!!!!!!! + /* half-duplex 81N serial uart in hand-tuned assembler * 1%/2% Tx/Rx timing error for 115.2kbps@8Mhz * 2%/1% Tx/Rx timing error for 230.4kbps@8Mhz * optimized for no jitter vs AVR305 with 1 cycle/bit jitter * @author: Ralph Doncaster - * @version: $Id$ + * @orginalversion: BasicSerial3.S * link: https://nerdralph.blogspot.com/2014/01/avr-half-duplex-software-uart.html * shrink to TX only by 25mmHg */ @@ -14,6 +17,7 @@ #define UART_Port (PORTB-0x20) #define UART_Tx 4 // DISCONNECT PIEZO !!!!!!!!!!!!!!!!!!!!!!!! +;[GLOBAL implement TXTimedByte] .global TxTimedByte ; transmit byte in r24 with bit delay in r22 - 15 instructions ; calling code must set Tx line to idle state (high) or 1st byte may be lost diff --git a/SolarbirdTiny/system/serialout.h b/SolarbirdTiny/system/serialout.h index 4c27c10..94596f5 100644 --- a/SolarbirdTiny/system/serialout.h +++ b/SolarbirdTiny/system/serialout.h @@ -3,8 +3,10 @@ /* Optimized half-duplex serial uart implementation * timing within 2% using at 230.4kbps @ 8Mhz * @author: Ralph Doncaster - * @version: $Id$ + * @orginalversion: BasicSerial3.h * link: https://nerdralph.blogspot.com/2014/01/avr-half-duplex-software-uart.html + * link: https://nerdralph.blogspot.com/2013/12/writing-avr-assembler-code-with-arduino.html + * link to newer version: https://github.com/nerdralph/nerdralph/blob/master/avr/picoUART.h * shrink to TX only by 25mmHg * use: printString("\nH E L L O\n"); * use: printHex(10); @@ -19,14 +21,16 @@ #error CPU frequency F_CPU undefined #endif -extern "C" { -void TxTimedByte(char, char); +extern "C" +{ + static void TxTimedByte(char, char); +} class SerialOut { public: - SerialOut() {} + SerialOut() {} static void printString(const char* str) { From 1bdbf705b9e30e897a51eab4a6a6029ff7e9aba9 Mon Sep 17 00:00:00 2001 From: 25mmHg <23mmhg@gmail.com> Date: Sun, 15 Aug 2021 13:48:16 +0200 Subject: [PATCH 3/3] change file structure --- SolarbirdTiny/SolarbirdTiny.ino | 5 ++-- SolarbirdTiny/serialout.S | 5 ++-- SolarbirdTiny/serialout.h | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 SolarbirdTiny/serialout.h diff --git a/SolarbirdTiny/SolarbirdTiny.ino b/SolarbirdTiny/SolarbirdTiny.ino index 81f96f1..f7d0e7a 100644 --- a/SolarbirdTiny/SolarbirdTiny.ino +++ b/SolarbirdTiny/SolarbirdTiny.ino @@ -1,6 +1,8 @@ // nick@bitfasching.de, Kazoosh, 2021 // For ATtiny85 @ 8 MHz +//Do you need serial debug messages? +#define DEBUGMODE // I/O pins // @@ -9,7 +11,6 @@ #define PIN_LED_1_ANODE 0 #define PIN_LED_2_ANODE 1 #define PIN_LED_CATHODES 2 -#define DEBUGMODE // High-level access to I/O and system features // @@ -17,7 +18,7 @@ #include "system/leds.h" #include "system/piezo.h" #include "system/lowpower.h" -#include "system/serialout.h" +#include "serialout.h" // Bird voice // diff --git a/SolarbirdTiny/serialout.S b/SolarbirdTiny/serialout.S index 4f415ed..23f19c6 100644 --- a/SolarbirdTiny/serialout.S +++ b/SolarbirdTiny/serialout.S @@ -1,5 +1,5 @@ ;25mmHg Kazoosh, 2021 -; must be in the same directory like the .ino!!!!!!!!!!!!!!!!!! +; must be in the same directory with .ino!!!!!!!!!!!!!!!!!! /* half-duplex 81N serial uart in hand-tuned assembler * 1%/2% Tx/Rx timing error for 115.2kbps@8Mhz @@ -17,7 +17,6 @@ #define UART_Port (PORTB-0x20) #define UART_Tx 4 // DISCONNECT PIEZO !!!!!!!!!!!!!!!!!!!!!!!! -;[GLOBAL implement TXTimedByte] .global TxTimedByte ; transmit byte in r24 with bit delay in r22 - 15 instructions ; calling code must set Tx line to idle state (high) or 1st byte may be lost @@ -41,4 +40,4 @@ TxDelay: ror r24 out UART_Port, r0 brne TxLoop - reti ; return and enable interrupts \ No newline at end of file + reti ; return and enable interrupts diff --git a/SolarbirdTiny/serialout.h b/SolarbirdTiny/serialout.h new file mode 100644 index 0000000..7202eea --- /dev/null +++ b/SolarbirdTiny/serialout.h @@ -0,0 +1,51 @@ +//25mmHg joetronica@gmx.net, Kazoosh, 2021 + +/* Optimized half-duplex serial uart implementation + * timing within 2% using at 230.4kbps @ 8Mhz + * @author: Ralph Doncaster + * @orginalversion: BasicSerial3.h + * link: https://nerdralph.blogspot.com/2014/01/avr-half-duplex-software-uart.html + * link: https://nerdralph.blogspot.com/2013/12/writing-avr-assembler-code-with-arduino.html + * link to newer version: https://github.com/nerdralph/nerdralph/blob/master/avr/picoUART.h + * shrink to TX only by 25mmHg + * use: printString("\nH E L L O\n"); + * use: printHex(10); + */ + +#define BAUD_RATE 115200 + +#ifdef F_CPU + /* account for integer truncation by adding 3/2 = 1.5 */ + #define TXDELAY (((F_CPU/BAUD_RATE)-7 +1.5)/3) +#else + #error CPU frequency F_CPU undefined +#endif + +extern "C" +{ + static void TxTimedByte(char, char); +} + +class SerialOut +{ + public: + + SerialOut() {} + + static void printString(const char* str) + { + while (*str) TxTimedByte(*str++, TXDELAY); + } + + static void printHex(uint16_t data2hex) + { + //convert unsigned integer numbers 2 HEX and print it + static const uint16_t mask = 0xF; + for(uint8_t i=0; i<4; i++) + { + uint8_t temp = (uint8_t)((data2hex >> (4*(3-i))) & mask); + temp = (temp < 0xA ? temp+'0' : temp-0xA+'A'); + TxTimedByte(temp, TXDELAY); + } + } +};