From 5564528b18a6078ce7826a8189d829aba4b6415d Mon Sep 17 00:00:00 2001 From: MCUdude Date: Thu, 10 Dec 2020 14:51:21 +0100 Subject: [PATCH] Add comments --- avr/cores/microcore/Tone.cpp | 167 ++++++++++++--------------- avr/cores/microcore/WInterrupts.c | 24 +++- avr/cores/microcore/WMath.cpp | 14 ++- avr/cores/microcore/wiring.c | 27 ++++- avr/cores/microcore/wiring_analog.c | 14 +++ avr/cores/microcore/wiring_digital.c | 23 ++++ avr/cores/microcore/wiring_pulse.c | 12 +- avr/cores/microcore/wiring_pwm.c | 11 ++ avr/cores/microcore/wiring_shift.c | 24 ++++ 9 files changed, 213 insertions(+), 103 deletions(-) diff --git a/avr/cores/microcore/Tone.cpp b/avr/cores/microcore/Tone.cpp index 36744ead..9bb42e56 100644 --- a/avr/cores/microcore/Tone.cpp +++ b/avr/cores/microcore/Tone.cpp @@ -1,25 +1,25 @@ /*** MicroCore - Tone.cpp *** An Arduino core designed for ATtiny13 -Based on the work done by "smeezekitty" +Based on the work done by "smeezekitty" Modified and maintained by MCUdude https://github.com/MCUdude/MicroCore Optimized for size by Nerd Ralph Simplified Tone "Library" The Tone.cpp file in the official Arduino core is a complete and utter mess, -once again the developers over the years have just used a load of +once again the developers over the years have just used a load of arcane #ifdef's to try and work out what they should do for a specific chip. Which pretty much makes it totally completely unreasonably unreadable. -So we simplify by replacing the existing Tone with our own, we are not +So we simplify by replacing the existing Tone with our own, we are not composing a grapnd symphony here, let's face it, blurting out Mario Bros is about the most people use tone() for! This modified version of Tone.cpp is released under the MIT license (MIT). - + @author James Sleeman (https://github.com/sleemanj) -@license The MIT License (MIT) +@license The MIT License (MIT) */ #include "Arduino.h" @@ -36,20 +36,28 @@ static uint8_t CurrentToneMidpoint = 0; #define TONE_MAX_FREQ 2093 // 2093.0 = C6 #endif - +/** + * @brief Generates a square wave of the specified frequency (and 50% duty + * cycle) on a pin. A duration can be specified, otherwise the wave + * continues until a call to noTone(). + * + * @param pin The Arduino pin on which to generate the tone + * @param frequency Tone frequency in [Hz] + * @param length The duration of the tone in milliseconds (optional) + */ void tone(const uint8_t pin, const uint16_t frequency, const uint32_t length) { - if(frequency == 0) + if(frequency == 0) return noTone(pin); uint16_t prescaleDivider = 1; // The divider increases by powers of 2 uint8_t prescaleBitMask = 1; // The bitmask only increments unitarily uint32_t ticksPerMs = F_CPU / 1000; - + // The numbers in each if statement are the lowest frequency that can be achieved // with the prescale inside the if statement // - // eg: the frequency can be viewed as a number of transitions equal to the + // eg: the frequency can be viewed as a number of transitions equal to the // frequency * ([LOW]-transition-[HIGH]) // (we ignore the extra transition between cycles, happens anyway, err, I think, maybe, not sure) // the longer the time between transitions the lower the frequency @@ -58,14 +66,14 @@ void tone(const uint8_t pin, const uint16_t frequency, const uint32_t length) // // therefore minimum is CPU_FREQ / Prescale / 255 // maximum is CPU_FREQ / Prescale / 1 - + // We will add/remove 1 hz to account for rounding here - + #define TONE_MIN_FREQ_FOR_PRESCALE(PS) (((F_CPU / PS) / 255) + 1) #define TONE_MAX_FREQ_FOR_PRESCALE(PS) (((F_CPU / PS) / 1) - 1) - + #define TONE_RANGE_ACTIVE_FOR_PRESCALE(PS) ( TONE_MAX_FREQ >= TONE_MIN_FREQ_FOR_PRESCALE(PS) && TONE_MIN_FREQ <= TONE_MAX_FREQ_FOR_PRESCALE(PS) ) - + if(TONE_RANGE_ACTIVE_FOR_PRESCALE(1) && frequency >= TONE_MIN_FREQ_FOR_PRESCALE(1)) // (F_CPU / 1) / 255 { prescaleDivider = 1; @@ -77,19 +85,19 @@ void tone(const uint8_t pin, const uint16_t frequency, const uint32_t length) prescaleDivider = 8; ticksPerMs = (F_CPU/8) / 1000; prescaleBitMask = 2; - } + } else if(TONE_RANGE_ACTIVE_FOR_PRESCALE(64) && frequency >= TONE_MIN_FREQ_FOR_PRESCALE(64)) // (F_CPU / 64) / 255 { prescaleDivider = 64; ticksPerMs = (F_CPU/64) / 1000; prescaleBitMask = 3; - } + } else if(TONE_RANGE_ACTIVE_FOR_PRESCALE(256) && frequency >= TONE_MIN_FREQ_FOR_PRESCALE(256)) // (F_CPU / 256) / 255 { prescaleDivider = 256; ticksPerMs = (F_CPU/256) / 1000; prescaleBitMask = 4; - } + } else if(TONE_RANGE_ACTIVE_FOR_PRESCALE(1024) && frequency >= TONE_MIN_FREQ_FOR_PRESCALE(1024)) // (F_CPU / 1024) / 255 { prescaleDivider = 1024; @@ -98,138 +106,111 @@ void tone(const uint8_t pin, const uint16_t frequency, const uint32_t length) } else return; - + toneRaw(pin, (((F_CPU / prescaleDivider) / frequency) / 2), length ? length * ticksPerMs : ~(0UL), prescaleBitMask); } -void toneRaw(uint8_t pin, uint8_t midPoint, uint32_t lengthTicks, uint8_t prescaleBitMask) +void toneRaw(uint8_t pin, uint8_t midPoint, uint32_t lengthTicks, uint8_t prescaleBitMask) { // Because the t13 is so limited in flash space, this really is little more than // a because-I-can excercise in futility. // - // Because we can't do division (way too heavy) an inline function is used to + // Because we can't do division (way too heavy) an inline function is used to // do pre-calculation of the midPoint (of the timer, where we toggle the pin) - // the number of ticks, and the prescale bitmask. Naturally this is only + // the number of ticks, and the prescale bitmask. Naturally this is only // going to work when tone is fed a constant frequency and length! - - // Because we only have one timer, we can't just use millis() to + + // Because we only have one timer, we can't just use millis() to // do tone duration, since it won't be accurate. // // So instead we calculate how many ticks the tone should go for and subtract // ticks as we hit the mid-point. - + CurrentToneDuration = lengthTicks; CurrentTonePin = _BV(pin); - pinMode(pin, OUTPUT); - + pinMode(pin, OUTPUT); + // Shut down interrupts while we fiddle about with the timer. cli(); - + TCCR0B &= ~0b00000111; // Turn off the timer before changing anytning TCNT0 = 0; // Timer counter back to zero - - // Set the comparison, we will flip the bit every time this is hit - // (actually, this will set TOP of the timer and we flip on the overflow) + + // Set the comparison, we will flip the bit every time this is hit + // (actually, this will set TOP of the timer and we flip on the overflow) OCR0A = midPoint; CurrentToneMidpoint = midPoint; - + // Enable the overflow interrupt TIMSK0 |= _BV(OCIE0A); - + // Start playing and record time - digitalWrite(pin, HIGH); - - // Start the timer - TCCR0A = 0b00000011; + digitalWrite(pin, HIGH); + + // Start the timer + TCCR0A = 0b00000011; TCCR0B = 0b00001000 | prescaleBitMask; - + sei(); // We **have** to enable interrupts here even if they were disabled coming in, // otherwise it's not going to do anything. Hence not saving/restoring SREG. } - -void noTone(uint8_t pin) +/** + * @brief Stops the generation of a square wave triggered by tone(). + * + * @param pin The Arduino pin on which to stop generating the tone + */ +void noTone(uint8_t pin) { // Disable the interrupt // Note we can leave the rest of the timer setup as is, turnOnPWM() will // fix it for itself next time you analogWrite() if you need to. TIMSK0 &= ~_BV(OCIE0A); - - // Pin goes back to input state + + // Pin goes back to input state DDRB &= ~pin; } -// This function will reset Timer0 back to its default setting +/** + * @brief Resets Timer0 state back to its default MicroCore setting + */ void stopTone() { // Resets timer0 to it's default value defined in core_settings.h - #ifdef SETUP_PWM - // Set Timer0 prescaler - #if defined(PWM_PRESCALER_DEFAULT) - #if F_CPU >= 4800000L - TCCR0B |= _BV(CS00) | _BV(CS01); // PWM frequency = (F_CPU/256) / 64 - #else - TCCR0B |= _BV(CS01); // PWM frequency = (F_CPU/256) / 8 - #endif - #elif defined(PWM_PRESCALER_NONE) // PWM frequency = (F_CPU/256) / 1 - TCCR0B |= _BV(CS00); - #elif defined(PWM_PRESCALER_8) // PWM frequency = (F_CPU/256) / 8 - TCCR0B |= _BV(CS01); - #elif defined(PWM_PRESCALER_64) // PWM frequency = (F_CPU/256) / 64 - TCCR0B |= _BV(CS00) | _BV(CS01); - #elif defined(PWM_PRESCALER_256) // PWM frequency = (F_CPU/256) / 256 - TCCR0B |= _BV(CS02); - #elif defined(PWM_PRESCALER_1024) // PWM frequency = (F_CPU/256) / 1024 - TCCR0B |= _BV(CS00) | _BV(CS02); - #endif - - // Set waveform generation mode - #if defined(PWM_PHASE_CORRECT) - TCCR0A |= _BV(WGM00); - #else // (PWM_FAST) - TCCR0A |= _BV(WGM00) | _BV(WGM01); - #endif - #endif - - // Override previous PWM setup if micros() is enabled - #ifdef ENABLE_MICROS - // Clear all Timer0 settings - TCCR0B = 0; - // Set a suited prescaler based on F_CPU - #if F_CPU >= 4800000L - TCCR0B |= _BV(CS00) | _BV(CS01); // F_CPU/64 - #else - TCCR0B |= _BV(CS01); // F_CPU/8 - #endif - // Enable overflow interrupt on Timer0 - TIMSK0 |= _BV(TOIE0); - // Set timer0 couter to zero - TCNT0 = 0; - // Clear Timer0 overflow counter - timer0_overflow = 0; - // Turn on global interrupts - sei(); + #ifdef ENABLE_MICROS + // Set a suited prescaler based on F_CPU + #if F_CPU >= 4800000L + TCCR0B = _BV(CS00) | _BV(CS01); // F_CPU/64 + #else + TCCR0B = _BV(CS01); // F_CPU/8 + #endif + // Enable overflow interrupt on Timer0 + TIMSK0 = _BV(TOIE0); + // Set timer0 couter to zero + TCNT0 = 0; + // Turn on global interrupts + sei(); #endif } -ISR(TIM0_COMPA_vect) -{ +ISR(TIM0_COMPA_vect) +{ auto toneDuration = CurrentToneDuration; auto tonePin = CurrentTonePin; auto toneMidpoint = CurrentToneMidpoint; - // Toggle the pin, most AVR can toggle an output pin by writing a 1 to the input + // Toggle the pin, most AVR can toggle an output pin by writing a 1 to the input // register bit for that pin. PINB = CurrentTonePin; - + // If we have played this tone for the requested duration, stop playing it. if (toneDuration < toneMidpoint) - noTone(tonePin); + noTone(tonePin); CurrentToneDuration = toneDuration - toneMidpoint; - + TCNT0 = 0; // Restart timer } diff --git a/avr/cores/microcore/WInterrupts.c b/avr/cores/microcore/WInterrupts.c index 89f30a0f..4e54eeda 100644 --- a/avr/cores/microcore/WInterrupts.c +++ b/avr/cores/microcore/WInterrupts.c @@ -22,6 +22,21 @@ detachInterrupt(). static volatile voidFuncPtr intFunc; +/** + * @brief Initialize and enable the external interrupt pin (INT0) + * + * @param interruptNum Interrupt number. Optional parameter since ATtiny13 only + * has one interrupt pin + * @param userFunc The function to call when the interrupt occurs This function + * must take no parameters and return nothing. This function is sometimes + * referred to as an interrupt service routine. + * @param mode Defines when the interrupt should be triggered. + * Four constants are predefined as valid values: + * LOW to trigger the interrupt whenever the pin is low, + * CHANGE to trigger the interrupt whenever the pin changes value, + * RISING to trigger when the pin goes from low to high, + * FALLING for when the pin goes from high to low. + */ void attachInterrupt(__attribute__((unused)) uint8_t interruptNum, void (*userFunc)(void), uint8_t mode) { #if !defined(SAFEMODE) @@ -49,7 +64,12 @@ void attachInterrupt(__attribute__((unused)) uint8_t interruptNum, void (*userFu GIMSK |= _BV(INT0); } - +/** + * @brief Turns off the INT0 interrupt pin + * + * @param interruptNum Interrupt number. Optional parameter since ATtiny13 only + * has one interrupt pin + */ void detachInterrupt(__attribute__((unused)) uint8_t interruptNum) { // Disable INT0 on pin PB1 @@ -58,7 +78,7 @@ void detachInterrupt(__attribute__((unused)) uint8_t interruptNum) } -// AttachInterrupt ISR +// attachInterrupt ISR ISR(INT0_vect) { intFunc(); diff --git a/avr/cores/microcore/WMath.cpp b/avr/cores/microcore/WMath.cpp index 89c164e4..317a759e 100644 --- a/avr/cores/microcore/WMath.cpp +++ b/avr/cores/microcore/WMath.cpp @@ -21,6 +21,12 @@ void randomSeed(uint16_t seed) } +/** + * @brief Generates a pseudo-random number. + * + * @param howbig upper bound of the random value, exclusive + * @return int32_t A random number between min and max-1 + */ int32_t random(int32_t howbig) { if (howbig == 0) @@ -29,7 +35,13 @@ int32_t random(int32_t howbig) return random() % howbig; } - +/** + * @brief Generates a pseudo-random number. + * + * @param howsmall Lower bound of the random value, inclusive + * @param howbig upper bound of the random value, exclusive + * @return int32_t A random number between min and max-1 + */ int32_t random(int32_t howsmall, int32_t howbig) { if (howsmall >= howbig) diff --git a/avr/cores/microcore/wiring.c b/avr/cores/microcore/wiring.c index 72d8ed56..3e23ad85 100644 --- a/avr/cores/microcore/wiring.c +++ b/avr/cores/microcore/wiring.c @@ -25,6 +25,13 @@ timers. // C wrapper function for millis asm code // reduces compiler register pressure vs avr-gcc calling convention // http://nerdralph.blogspot.ca/2015/12/lightweight-avr-assembler-functions.html + +/** + * @brief Returns the number of milliseconds passed since the microcontroller + * began running the current program. + * + * @return uint32_t Number of milliseconds passed since the program started + */ uint32_t millis() { uint32_t m; @@ -47,6 +54,12 @@ ISR(TIM0_OVF_vect) timer0_overflow++; // Increment counter by one } +/** + * @brief Returns the number of microseconds since the microcontroller + * began running the current program. + * + * @return uint32_t Number of microseconds passed since the program started + */ uint32_t micros() { uint32_t x; @@ -114,7 +127,12 @@ uint32_t micros() #endif // ENABLE_MICROS -// Wrapper to deal with _delay_ms(), which is an inline function +/** + * @brief Pauses the program for the amount of time (in milliseconds) + * specified as parameter. + * + * @param ms The number of milliseconds to pause + */ void delay(uint16_t ms) { while(ms--) @@ -122,9 +140,10 @@ void delay(uint16_t ms) } -// This init() function will be executed before the setup() function does -// Edit the core_settings.h file to choose what's going to be initialized -// and what's not. +/** + * @brief Initializing function that runs before setup(). + * This is where timer0 is set up to count micros() if enabled. + */ void init() { // WARNING! Enabling micros() will affect timing functions! diff --git a/avr/cores/microcore/wiring_analog.c b/avr/cores/microcore/wiring_analog.c index eab1e7bb..45ed84de 100644 --- a/avr/cores/microcore/wiring_analog.c +++ b/avr/cores/microcore/wiring_analog.c @@ -13,6 +13,14 @@ and analogReference(). #include "core_settings.h" +/** + * @brief Configures the reference voltage used for analog input (the value + * used as the top of the input range) + * + * @param mode Analog reference level. Valid options are: + * EXTERNAL - Use Vcc as reference, + * INTERNAL1V1 - Use the internal 1.1V reference + */ void analogReference(uint8_t mode) { if (mode == DEFAULT) @@ -22,6 +30,12 @@ void analogReference(uint8_t mode) } +/** + * @brief Reads the value from the specified analog input + * + * @param pin Analog pin number (use A0..A3) + * @return int16_t Analog reading on the pin (10-bit, 0-1023) + */ int16_t analogRead(analog_pin_t pin) { // MUX1 & MUX0 are 2 lowest bits in ADMUX diff --git a/avr/cores/microcore/wiring_digital.c b/avr/cores/microcore/wiring_digital.c index 4e59029c..794e45ca 100644 --- a/avr/cores/microcore/wiring_digital.c +++ b/avr/cores/microcore/wiring_digital.c @@ -11,6 +11,16 @@ digitalWrite() and digitalRead(). #include "wiring_private.h" + +/** + * @brief Configures the specified pin to be an input or output. + * + * @param pin The Arduino pin number to set the mode of + * @param mode Pin mode. Valid options are: + * OUTPUT - set pin as output, + * INPUT - set pin as input, + * INPUT_PULLUP - set pin as input and enable internal pullup resistor + */ void pinMode(uint8_t pin, uint8_t mode) { if(mode == OUTPUT) // Pin as output @@ -24,6 +34,13 @@ void pinMode(uint8_t pin, uint8_t mode) } } + +/** + * @brief Writes a HIGH or a LOW value to a digital pin. + * + * @param pin The Arduino pin number to set the state of + * @param val The state to set. Valid options are HIGH or LOW + */ void digitalWrite(uint8_t pin, uint8_t val) { if(val) @@ -33,6 +50,12 @@ void digitalWrite(uint8_t pin, uint8_t val) } +/** + * @brief Reads a digital pin + * + * @param pin The Arduino pin to read + * @return uint8_t State of the pin + */ uint8_t digitalRead(uint8_t pin) { return !!(PINB & _BV(pin)); diff --git a/avr/cores/microcore/wiring_pulse.c b/avr/cores/microcore/wiring_pulse.c index 3f34ad6b..46dc8d54 100644 --- a/avr/cores/microcore/wiring_pulse.c +++ b/avr/cores/microcore/wiring_pulse.c @@ -16,9 +16,15 @@ dozen microseconds before the start of the pulse. #include "wiring_private.h" -// This pulseIn function is a complete rewrite of the original one, -// and does not depend on micros() to work. -// However it will only work properly with LTO enabled. +/** + * @brief Reads the pulse length (either HIGH or LOW) on a pin + * + * @param pin Pin number to read + * @param state state if the pulse to read (HIGH or LOW pulse) + * @param timeout Optional parameter. How long to wait before operation + * is skipped + * @return uint32_t pulse length in microseconds + */ uint32_t pulseIn(uint8_t pin, uint8_t state, uint32_t timeout) { // Convert the timeout from microseconds to a number of times through diff --git a/avr/cores/microcore/wiring_pwm.c b/avr/cores/microcore/wiring_pwm.c index e0cce6f1..954d8224 100644 --- a/avr/cores/microcore/wiring_pwm.c +++ b/avr/cores/microcore/wiring_pwm.c @@ -11,6 +11,11 @@ analogWrite(). #include "core_settings.h" +/** + * @brief Turns of PWM functionality on a pin + * + * @param pin Pin to disable PWM on + */ void turnOffPWM(uint8_t pin) { if(pin == 0) @@ -19,6 +24,12 @@ void turnOffPWM(uint8_t pin) TCCR0A &= ~_BV(COM0B1); } +/** + * @brief Writes a PWM value to a PWM compatible pin + * + * @param pin PWM pin (digital pin 0 or 1) + * @param val PWM value to write (8-bit, 0 - 255) + */ void analogWrite(uint8_t pin, uint8_t val) { // Set Timer0 prescaler diff --git a/avr/cores/microcore/wiring_shift.c b/avr/cores/microcore/wiring_shift.c index 5cef5c45..28a1e86d 100644 --- a/avr/cores/microcore/wiring_shift.c +++ b/avr/cores/microcore/wiring_shift.c @@ -11,6 +11,18 @@ functions shiftIn() and shiftOut(). #include "wiring_private.h" +/** + * @brief Shifts in a byte of data one bit at a time. Starts from either the + * most (i.e. the leftmost) or least (rightmost) significant bit. For + * each bit, the clock pin is pulled high, the next bit is read from the + * data line, and then the clock pin is taken low. + * + * @param dataPin Digital input pin to receive serial data + * @param clockPin The pin to toggle to signal a read from dataPin + * @param bitOrder Which order to shift in the bits + * MSBFIRST or LSBFIRST are valid options + * @return uint8_t The value read + */ uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) { uint8_t value = 0; @@ -37,6 +49,18 @@ uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) } +/** + * @brief Shifts out a byte of data one bit at a time. Starts from either the + * most (i.e. the leftmost) or least (rightmost) significant bit. Each + * bit is written in turn to a data pin, after which a clock pin is + * pulsed (taken high, then low) to indicate that the bit is available. + * + * @param dataPin The pin on which to output each bit + * @param clockPin The pin to toggle once the dataPin has been set to the correct value + * @param bitOrder Which order to shift in the bits + * MSBFIRST or LSBFIRST are valid options + * @param value The data to shift out + */ void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t value) { const uint8_t datapinMask = _BV(dataPin);