From 21db75294d90acc0976cffe58b056148f9112a2d Mon Sep 17 00:00:00 2001 From: Gabriel Staples Date: Sat, 26 Mar 2016 23:52:21 -0400 Subject: [PATCH 1/2] Version 0.2.0 released, w/0.5us-res timestamp funcs --- .gitignore | 1 + .../PPM_Writer_demo2/PPM_Writer_demo2.ino | 80 +++++++++++ eRCaGuy_PPM_Writer.cpp | 130 +++++++++++++++++- eRCaGuy_PPM_Writer.h | 35 ++++- eRCaGuy_TimerCounterTimers.h | 113 +++++++++++++++ 5 files changed, 349 insertions(+), 10 deletions(-) create mode 100644 Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino create mode 100644 eRCaGuy_TimerCounterTimers.h diff --git a/.gitignore b/.gitignore index 078410f..b9681f4 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ Temporary Items eRCaGuy_PPM_Writer - time (work) log.ods .~lock.eRCaGuy_PPM_Writer - time (work) log.ods# eRCaGuy_PPM_Writer - What to work on next - Gabriel.odt +.~lock.eRCaGuy_PPM_Writer - What to work on next - Gabriel.odt# diff --git a/Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino b/Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino new file mode 100644 index 0000000..8ec0d43 --- /dev/null +++ b/Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino @@ -0,0 +1,80 @@ +/* +PPM_Writer_demo2.ino +By Gabriel Staples +http://www.ElectricRCAircraftGuy.com +My contact info is available by clicking the "Contact Me" tab at the top of my website. +Written: 26 March 2016 +Updated: 26 March 2016 + +LICENSE: GNU GPLV3 or later (refer to .h file and attached license for details) + +-outputs PPM signal on Arduino pin 9 +*/ + +#include + +const byte CH1 = 0, + CH2 = 1, + CH3 = 2, + CH4 = 3, + CH5 = 4, + CH6 = 5, + CH7 = 6, + CH8 = 7; + + +void setup() +{ + Serial.begin(115200); + Serial.println(F("\n\nbegin")); + + //manually set a few channels + PPMWriter.setChannelVal(CH1,900*2); //set channel 1 (index 0) in the PPM train to 900us + PPMWriter.setChannelVal(CH2,1000*2); //set channel 2 (index 2) in the PPM train to 1000us + PPMWriter.setChannelVal(CH3,1100*2); + PPMWriter.setChannelVal(CH4,1200*2); + + PPMWriter.setPPMPeriod(20000*2UL); //currently you cannot set above 65535 since Timer1 is a 16-bit timer/counter, and I'm not taking into account rollovers past this value, *yet*; NB: the UL is *mandatory* to force the constant/literal multiplication here to follow unsigned long rules *instead of* signed int rules, which I believe are default otherwise here in this compiler's C++ handling; take out the UL and you'll see weird results/errors for any values where the result of the multiplication is > 32767, which is the max value storable in a signed int. + Serial.print(F("PPMPeriod(0.5us) = ")); Serial.println(PPMWriter.getPPMPeriod()); + Serial.print(F("PPMFreq(Hz) = ")); Serial.println(PPMWriter.getPPMFrequency()); + + PPMWriter.begin(); //start the PPM train; default will be 1500us for all channels, 22ms for each PPM frame (I'm copying the Spektrum DX8 signal) +} + +void loop() +{ + static unsigned long loopCount = 0; + + static byte ch_i = 0; + if (PPMWriter.readChannelFlag(ch_i)==true) + { + unsigned long t_now = micros(); //us + unsigned long t_now2 = PPMWriter.getMicros(); //us + unsigned long count_now = PPMWriter.getCount(); //0.5us units + static unsigned long t_now_old = t_now; //us + static unsigned long t_now2_old = t_now2_old; //us + static unsigned long count_now_old = count_now; //0.5us + + //print data + Serial.print(F("ch_i = ")); Serial.print(ch_i); Serial.print(F(", frameNum = ")); + Serial.print(PPMWriter.getFrameNumber()); + Serial.print(F(", t_now(us) = ")); Serial.print(t_now); Serial.print(F(", t_now2(us) = ")); Serial.print(t_now2); + Serial.print(F(", count_now(0.5us) = ")); Serial.print(count_now); + //deltas: + Serial.print(F(", delta times = ")); Serial.print(t_now - t_now_old); Serial.print(", "); + Serial.print(t_now2 - t_now2_old); Serial.print(", "); Serial.println(count_now - count_now_old); + + //updates + t_now_old = t_now; //us + t_now2_old = t_now2; //us + count_now_old = count_now; //0.5us + + // ch_i++; + // if (ch_i >= PPMWriter.getNumChannels()) + // ch_i = 0; //reset + } + +} + + + diff --git a/eRCaGuy_PPM_Writer.cpp b/eRCaGuy_PPM_Writer.cpp index c98fa95..735d3bf 100644 --- a/eRCaGuy_PPM_Writer.cpp +++ b/eRCaGuy_PPM_Writer.cpp @@ -58,6 +58,9 @@ Pin Mapping: https://www.arduino.cc/en/Hacking/PinMapping168 #include "eRCaGuy_PPM_Writer.h" #include //http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html +//NOTE/TODO: ALLOW ONE TO CHOOSE WHICH TIMER YOU'D LIKE TO USE FOR THIS LIBRARY IN THE "eRCaGuy_TimerCounterTimers.h" file +#include "eRCaGuy_TimerCounterTimers.h" + //macros #define readPinA() (PINB & _BV(1)) //Arduino pin 9 #define togglePinA() (TCCR1C = _BV(FOC1A)) //see datasheet pg. 135 & 132; force OC1A pin (Arduino D9) to toggle; datasheet pg. 132: setting one or both of the COM1A1:0 bits to 1 overrides the normal port functionality of the I/O pin it is connected to, so this is how you must toggle the pin; digitalWrite(9,HIGH/LOW) on pin 9 will NOT work anymore on this pin; note, however, that *reading* the pin port directly, or calling digitalRead(9) DOES still work! @@ -77,7 +80,10 @@ eRCaGuy_PPM_Writer PPMWriter; //preinstantiation of object //======================================================================================================== //ISRs //======================================================================================================== + +//-------------------------------------------------------------------------------------------------------- //Timer 1 Compare Match A interrupt +//-------------------------------------------------------------------------------------------------------- ISR(TIMER1_COMPA_vect) { // writePin2HIGH; //FOR MEASURING THE ISR PROCESSING TIME. ANSWER: <= ~6us per ISR interrupt @@ -85,11 +91,22 @@ ISR(TIMER1_COMPA_vect) // writePin2LOW; } -//Here is where the magic happens (ie: the actual writing of the PPM signal) -void eRCaGuy_PPM_Writer::compareMatchISR() +//-------------------------------------------------------------------------------------------------------- +//Timer 1 Overflow Interrupt +//-------------------------------------------------------------------------------------------------------- +ISR(TIMER1_OVF_vect) //Timer1's counter has overflowed +{ + PPMWriter.overflowISR(); +} + +//-------------------------------------------------------------------------------------------------------- +//compareMatchISR +//-Here is where the magic happens (ie: the actual writing of the PPM signal) +//-------------------------------------------------------------------------------------------------------- +inline void eRCaGuy_PPM_Writer::compareMatchISR() { //local variables - unsigned int incrementVal; //units of 0.5us + long incrementVal; //units of 0.5us; make long too allow for (and later be able to handle) the rare case of negative values, which would occur if someone tries to set the PPM period too short if (_currentState==FIRST_EDGE) { @@ -134,6 +151,18 @@ void eRCaGuy_PPM_Writer::compareMatchISR() _timeSinceFrameStart += incrementVal; //0.5us; this will be the time elapsed at the start of the NEXT Compare Match A interrupt } +//-------------------------------------------------------------------------------------------------------- +//overflowISR +//-this is used to generate a 0.5us timestamp capability, to get better resolution than what micros() can provide +//--(micros only has a 4us resolution) +//-------------------------------------------------------------------------------------------------------- +inline void eRCaGuy_PPM_Writer::overflowISR() +{ + _overflowCount++; + if (_userOverflowFuncOn) + _p_userOverflowFunction(); //call the user-attached function +} + //======================================================================================================== //eRCaGuy_PPM_Writer CLASS METHODS //======================================================================================================== @@ -157,6 +186,8 @@ eRCaGuy_PPM_Writer::eRCaGuy_PPM_Writer() _channelSpace = DEFAULT_CHANNEL_SPACE; //0.5us _frameNumber = 0; _PPMPolarity = PPM_WRITER_NORMAL; + _overflowCount = 0; //for 0.5us timestamps + _userOverflowFuncOn = false; } //-------------------------------------------------------------------------------------------------------- @@ -184,12 +215,100 @@ void eRCaGuy_PPM_Writer::begin() OCR1A = TCNT1 + 100; //set to interrupt (to begin PPM signal generation) 50us (100 counts) from now - //enable Output Compare A interrupt (TIMSK1, datasheet pg. 136) - TIMSK1 = _BV(OCIE1A); + TIMSK1 = _BV(OCIE1A); //enable Output Compare A interrupt (TIMSK1, datasheet pg. 136) + overflowInterruptOn(); ensurePPMPolarity(); } +//-------------------------------------------------------------------------------------------------------- +//getCount +//-get the total 32-bit (unsigned long) count on the timer, ***in units of COUNTS, not microseconds*** +//--ex: with prescaler = 8, you have 0.5us/count for a 16Mhz Arduino, so you divide counts by 2 to get us +//-Note that the time returned WILL update even in Interrupt Service Routines (ISRs), so if you call this function in an ISR, and you want the time to be as close as possible to a certain event that occurred which called the ISR you are in, make sure to call getCount() first thing when you enter the ISR. +//-Also, note that calling getCount() is faster than calling getMicros() and is therefore the preferable way to measure a time interval. +//--For example: call getCount() at the beginning of some event, then at the end. Take the difference and divide it by 2 (assuming 16Mhz Arduino, prescaler is 8) to get the time interval in microseconds. +//-------------------------------------------------------------------------------------------------------- +unsigned long eRCaGuy_PPM_Writer::getCount() +{ + unsigned long totalCount; //units of COUNTS + uint8_t SREG_old = SREG; //back up the AVR Status Register; see example in datasheet on pg. 14, as well as Nick Gammon's "Interrupts" article - http://www.gammon.com.au/forum/?id=11488 + noInterrupts(); //prepare for critical section of code + { + unsigned int TCNTn_save = TCNTn; //grab the counter value from timer + bool overflowFlag = bitRead(TIFRn,0); //grab the timer overflow flag value; see datasheet pg. 160, for ex. + if (overflowFlag) //if the overflow flag is set + { + TCNTn_save = TCNTn; //update variable just saved since the overflow flag could have just tripped between previously saving the TCNTn value and reading bit 0 of TIFRn. If this is the case, TCNTn might have just changed from 255 to 0 (for an 8-bit timer), and so we need to grab the new value of TCNTn to prevent an error of up to 127.5us in any time obtained using this counter. (Note: 255 counts / 2 counts/us = 127.5us). + //Note: this line of code DID in fact fix the error just described, in which I periodically saw an error of ~127.5us in some values read in by some PWM read code I wrote. + _overflowCount++; //force the overflow count to increment + TIFRn |= _BV(0); //reset the Timer overflow flag since we just manually incremented above; ex: see datasheet pg. 160; this prevents execution of the timer's overflow ISR + } + totalCount = _overflowCount*TC_RESOLUTION + TCNTn_save; + } + SREG = SREG_old; //restore interrupt-enable state + return totalCount; +} + +//-------------------------------------------------------------------------------------------------------- +//getMicros +//-returns the 32-bit timer time, with full resolution, as a float. +//--Ex: for a 16Mhz Arduino, with prescaler of 8, the resolution is 0.5us per count; with prescaler of 1, the resolution is 0.0625us per count. Both of these are better than the default Arduino micros() resolution of 4us +//-this function is slower than calling getCount() and therefore is not the preferred way of getting time. It is better to get the time by calling getCount() then dividing the value by the appropriate divisor to convert to us. +//-------------------------------------------------------------------------------------------------------- +float eRCaGuy_PPM_Writer::getMicros() +{ + return (float)getCount()/2; //returns a time stamp in us +} + +//-------------------------------------------------------------------------------------------------------- +//overflowInterruptOff +//-turns off the timer's overflow interrupt so that you no longer interrupt your code (every 128us for an 8-bit timer with prescaler of 8) in order to increment your overflow counter. +//-This may be desirable when you are no longer needing timestamps and want your main code or other interrupts to run more jitter-free, but you don't want to call end() in order to change all of the timer's settings back to default. +//-Assuming 16Mhz Arduino, 8-bit timer, and prescaler of 8 (ie: 0.5us/count), turning off the overflow interrupt will give you savings of approximately 4~5us every 128us. This is a CPU processing time savings of ~4%. +//--Source: Nick Gammon; "Interrupts" article; "How long does it take to execute an ISR?" section, found here: http://www.gammon.com.au/forum/?id=11488 +//-Note: If you disable the timer overflow interrupt but still call getCount() or getMicros() at least every 128us (or whatever your interrupt period is given your clock speed, timer size [8 or 16-bit], and prescaler), you will notice no difference in the counter, since calling getCount() or getMicros() also checks the interrupt flag and increments the overflow counter automatically. You have to wait > 128us between calls before you see any missed overflow counts. +//-------------------------------------------------------------------------------------------------------- +void eRCaGuy_PPM_Writer::overflowInterruptOff() +{ + TIMSKn &= ~_BV(0); +} + +//-------------------------------------------------------------------------------------------------------- +//overflowInterruptOn +//-turns the timer's overflow interrupt back on, so that the overflow counter will start to increment again +//-see "overflowInterruptOff" for details +//-------------------------------------------------------------------------------------------------------- +void eRCaGuy_PPM_Writer::overflowInterruptOn() +{ + TIMSKn |= _BV(0); +} + +//-------------------------------------------------------------------------------------------------------- +//attachOverflowInterrupt(myFunction) +//-this allows you to attach your own custom function that you want to be called every overflow interrupt. Very useful for recurring events, for example, but you'll have to write some more smarts into your function if you want specific timing. +//-this will also be very useful for my upcoming SoftwarePWM library, for example, which will allow software PWMing (analogWrite) on EVERY Arduino pin! +//-------------------------------------------------------------------------------------------------------- +void eRCaGuy_PPM_Writer::attachOverflowInterrupt(void (*myFunc)()) +{ + //ensure atomic access + uint8_t SREGbak = SREG; + noInterrupts(); + _userOverflowFuncOn = true; + _p_userOverflowFunction = myFunc; //attach function + SREG = SREGbak; //restore interrupt state +} + +//-------------------------------------------------------------------------------------------------------- +//detachOverflowInterrupt +//-detach (stop) your attached function +//--ie: prevent it from being called +//-------------------------------------------------------------------------------------------------------- +void eRCaGuy_PPM_Writer::detachOverflowInterrupt() +{ + _userOverflowFuncOn = false; //this is already atomic since it is a single byte; no atomic guards necessary +} + //-------------------------------------------------------------------------------------------------------- //ensurePPMPolarity() //-private method @@ -278,6 +397,7 @@ void eRCaGuy_PPM_Writer::setChannelVal(byte channel_i, unsigned int val) //setPPMPeriod //-control the PPM output frequency by setting the desired PPM train frame period directly //-units are 0.5us +//-currently, the longest value you can set is 2^16 - 1, or 65535, since the longest timer/counter is only 16-bits, and I'm not taking into account roll-overs beyond the length of the timer/counter, *yet*. ***********TODO: do take them into account so I can start using 8-bit timer/counters for this library too.*************** //-ex: setPPMPeriod(20000*2) sets a pd of 20000us (40000 0.5us counts), which results in a PPM freq. of 1/20ms=50Hz //-WARNING: if your channel values are all at their max values, the local variable "_minFrameSpace" acts as a safety feature to ensure the frame space after the last channel is longer than the longest channel. This is necessary because that is how a device *reading* a PPM signal finds the first frame: it knows that the *longest* frame is the frame space, and that the first channel comes after that. In the event that you set the PPM period too short, or have too many channels, with all of them maxed out, the PPM writer will force the frame space after the last channel to be at least as long as what is set in the local variable "_minFrameSpace." In this event, the PPM period will be lengthened for that frame, and the desired PPM frequency will not be reached. //--ex: assume 9 channels, max channel value of 2100us, PPM period set to 20ms; if all channels are simultaneously maxed out it takes 2100x9 = 18.9ms just to write the channels. To keep the 20ms PPM period, the frameSpace would have to be 20-18.9 = 1.1ms. However, THIS WILL BREAK THE SIGNAL AND PREVENT THE DEVICE READING THE PPM SIGNAL FROM DETERMINING WHERE THE START OF THE PPM FRAME IS. Therefore, the _minFrameSpace (default 3ms last I set it) will be used instead of 1.1ms. This makes the PPM frame 18.9ms + 3ms = 21.9ms (45.7Hz) instead of the desired 20ms (50Hz). Depending on your settings, since this PPM writer is totally user-customizable, you may get even more drastic results. Ex: if you were trying to fit 12 full-length channels in a 20ms PPM frame....you do the math. diff --git a/eRCaGuy_PPM_Writer.h b/eRCaGuy_PPM_Writer.h index 4e7b412..f79f9cc 100644 --- a/eRCaGuy_PPM_Writer.h +++ b/eRCaGuy_PPM_Writer.h @@ -7,13 +7,22 @@ By Gabriel Staples Website: http://www.ElectricRCAircraftGuy.com My contact info is available by clicking the "Contact Me" tab at the top of my website. Library Written: 2 July 2015 -Library Last Updated: 13 Sept 2015 +Library Last Updated: 26 March 2016 VERSIONING SYSTEM: -Using Semantic Versioning 2.0.0 (http://semver.org/) -Current Library Version 0.1.0 + +Current Library Version 0.2.0 HISTORY (newest on top): +20160326 - Version 0.2.0 released; major additions to add in a 0.5us-resolution timestamp capability as a replacement for the micros() function which only has a 4us resolution; also added a few other things + -functions added include: + --getCount() + --getMicros() + --overflowInterruptOff(); + --overflowInterruptOn(); + --attachOverflowInterrupt(); + --detachOverflowInterrupt(); 20150705 - First working version, V0.1.0, released */ @@ -71,7 +80,7 @@ const unsigned int DEFAULT_MIN_CHANNEL_VAL = 900*2; //0.5us const unsigned int DEFAULT_CHANNEL_VAL = 1500*2; //0.5us const unsigned int DEFAULT_MIN_FRAME_SPACE = max(DEFAULT_MAX_CHANNEL_VAL + 100*2,3000*2); //0.5us; min time gap between each frame, ie: between the end of the last channel and the start of the first channel const unsigned int DEFAULT_CHANNEL_SPACE = 400*2; //0.5us; the pulse that signifies the start of each new channel -const unsigned long DEFAULT_FRAME_PERIOD = 22000*2; //0.5us; note: the default period for the Spektrum DX8, for instance, is 22ms, or a freq. of 45.45Hz +const unsigned long DEFAULT_FRAME_PERIOD = 22000UL*2UL; //0.5us; note: the default period for the Spektrum DX8, for instance, is 22ms, or a freq. of 45.45Hz // const unsigned int MAX_OUTPUT_COMPARE_INCREMENT = 2^16; //make 2^16, or 65536, since Timer1 is a 16-bit timer/counter const boolean PPM_WRITER_NORMAL = 0; //base-line HIGH, channelSpace pulses are LOW const boolean PPM_WRITER_INVERTED = 1; //base-line LOW, channelSpace pulses are HIGH @@ -116,8 +125,18 @@ class eRCaGuy_PPM_Writer void end(); //ends PPM output, but otherwise leaves the timer settings as-is boolean readChannelFlag(byte channel_i); //each channel has a flag (single bit) that is set true when the channel value is completed being written, and the flag is cleared once a true value is read via this function; this is useful if you want to have some sort of event-driven programming that does something immediately after a specific channel value is written; ex: "while(readChannelFlag(channel_i)==false){};" will stay in the while loop until that channel value is written to the PPM signal - //ISR - void compareMatchISR(); //interrupt service routine for output compare matches + //ISRs + inline void compareMatchISR(); //interrupt service routine for output compare matches + inline void overflowISR(); //ISR called when timer overflows; for 0.5us timestamps + + //for 0.5us timestamps: + unsigned long getCount(); + float getMicros(); + void overflowInterruptOff(); + void overflowInterruptOn(); + //for custom userOverflowFunction + void attachOverflowInterrupt(void (*myFunc)()); + void detachOverflowInterrupt(); private: @@ -133,6 +152,12 @@ class eRCaGuy_PPM_Writer volatile unsigned int _channelSpace; //0.5us volatile unsigned long _frameNumber; volatile boolean _PPMPolarity; + //for 0.5us timestamps + volatile unsigned long _overflowCount; //a counter of how many times an overflow has occurred on the timer/counter + //for a custom userOverflowFunction + void (* volatile _p_userOverflowFunction)(void); //custom user function to attach to overflow ISR + //this is a volatile pointer to a function returning a type void, and accepting void as an input parameter; very confusing, I know. See here for help & details: 1) http://stackoverflow.com/questions/31898819/volatile-pointer-to-function-showing-compile-error-when-using-without-typedef-n and 2) http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword + volatile bool _userOverflowFuncOn; //non-volatile: unsigned int _maxChannelVal; //0.5us unsigned int _minChannelVal; //0.5us diff --git a/eRCaGuy_TimerCounterTimers.h b/eRCaGuy_TimerCounterTimers.h new file mode 100644 index 0000000..d66484e --- /dev/null +++ b/eRCaGuy_TimerCounterTimers.h @@ -0,0 +1,113 @@ +/* +eRCaGuy_TimerCounter Library +By Gabriel Staples +Website: http://www.ElectricRCAircraftGuy.com +My contact info is available by clicking the "Contact Me" tab at the top of my website. +************************************************** +* SEE eRCaGuy_TimerCounter.h FILE FOR MORE INFO. +************************************************** +*/ + +/* +=================================================================================================== + LICENSE & DISCLAIMER + Copyright (C) 2013-2015 Gabriel Staples. All right reserved. + + This file is part of eRCaGuy_TimerCounter. + + I AM WILLING TO DUAL-LICENSE THIS SOFTWARE. HOWEVER, UNLESS YOU HAVE PAID FOR AND RECEIVED A RECEIPT + FOR AN ALTERNATE LICENSE AGREEMENT, FROM ME, THE COPYRIGHT OWNER, THIS SOFTWARE IS LICENSED AS FOLLOWS: + + ------------------------------------------------------------------------------------------------ + License: GNU General Public License Version 3 (GPLv3) - https://www.gnu.org/licenses/gpl.html + ------------------------------------------------------------------------------------------------ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 http://www.gnu.org/licenses/ +=================================================================================================== +*/ + +#ifndef eRCaGuy_TimerCounterTimers_h +#define eRCaGuy_TimerCounterTimers_h + +//----------------INSTRUCTIONS TO USER------------------ +//CHOOSE YOUR TIMER YOU WANT TO USE FOR THIS LIBRARY. +//SIMPLY UNCOMMENT THE ONE YOU WANT TO USE, AND COMMENT +//OUT THE ONE CURRENTLY SET TO BE USED. +//------------------------------------------------------ + +//Note to self: TC stands for "TimerCounter" library + + +//For the Arduino Uno, Nano, Pro Mini, etc (Atmega328/168-based) +//-Timer0, 1, and 2 may also work with the Mega2560; not tested at this time +//-Timer0 & 1 may also work the Leonardo (ATmega32u4); not tested at this time + +// #define TC_USE_TIMER0 //8-bit, normally controls micros(), millis(), etc <--use if you don't want any conflict with the servo library, and you are going to use eRCaGuy_TimerCounter in place of micros() and millis() +#define TC_USE_TIMER1 //16-bit, used by the servo library, etc <--use as the default for eRCaGuy_TimerCounter +// #define TC_USE_TIMER2 //8-bit, used by some libraries, including the core Arduino tone() function <--use if you don't want any conflict with the servo library, and you still want to have access to micros() and millis() + + + +//------------------------------------------------------- +//NOTHING FOR USER TO CHANGE BELOW THIS POINT +//------------------------------------------------------- + +//Necessary defines + +#if defined(TC_USE_TIMER0) + #define OVERFLOW_VECTOR TIMER0_OVF_vect + #define TC_IS_8BIT_TIMER + #define TCCRnA TCCR0A + #define TCCRnB TCCR0B + #define TCNTn TCNT0 + #define TIFRn TIFR0 + #define TIMSKn TIMSK0 +#elif defined(TC_USE_TIMER1) + #define OVERFLOW_VECTOR TIMER1_OVF_vect + #define TC_IS_16BIT_TIMER + #define TCCRnA TCCR1A + #define TCCRnB TCCR1B + #define TCNTn TCNT1 + #define TIFRn TIFR1 + #define TIMSKn TIMSK1 +#elif defined(TC_USE_TIMER2) + #define OVERFLOW_VECTOR TIMER2_OVF_vect + #define TC_IS_8BIT_TIMER + #define TCCRnA TCCR2A + #define TCCRnB TCCR2B + #define TCNTn TCNT2 + #define TIFRn TIFR2 + #define TIMSKn TIMSK2 +#endif + +#if defined(TC_IS_8BIT_TIMER) + #define TC_RESOLUTION (256) +#elif defined(TC_IS_16BIT_TIMER) + #define TC_RESOLUTION (65536) +#endif + +//FOR SPEED PROFILING WITH OSCILLOSCOPE +//-Don't forget that a direct-port-access write like this takes 2 clock cycles, or 0.125 us, so you have to subtract +//2 clock cycles (NOT 4) from whatever time period you profile with the oscilloscope. +#define PROFILE_PIN2_HIGH PORTD |= _BV(2) //write Arduino pin D2 HIGH +#define PROFILE_PIN2_LOW PORTD &= ~_BV(2) //write Arduino pin D2 LOW + + +#endif + +/*========================================================================================== +Unused code at this time: +#if defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) +#endif +===========================================================================================*/ \ No newline at end of file From c98b4eadfe2cb1d44a6bb57d65ebf40d2e34e0e8 Mon Sep 17 00:00:00 2001 From: Gabriel Staples Date: Mon, 28 Mar 2016 00:09:06 -0400 Subject: [PATCH 2/2] comments & example2 update --- Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino | 14 ++++++++++++++ eRCaGuy_PPM_Writer.h | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino b/Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino index 8ec0d43..5829281 100644 --- a/Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino +++ b/Examples/PPM_Writer_demo2/PPM_Writer_demo2.ino @@ -39,6 +39,12 @@ void setup() Serial.print(F("PPMFreq(Hz) = ")); Serial.println(PPMWriter.getPPMFrequency()); PPMWriter.begin(); //start the PPM train; default will be 1500us for all channels, 22ms for each PPM frame (I'm copying the Spektrum DX8 signal) + + + PPMWriter.overflowInterruptOff(); + PPMWriter.overflowInterruptOn(); + PPMWriter.attachOverflowInterrupt(blinkLED); + // PPMWriter.detachOverflowInterrupt(); } void loop() @@ -76,5 +82,13 @@ void loop() } +void blinkLED() +{ + pinMode(13,OUTPUT); + static bool led_state = LOW; + led_state = !led_state; //toggle + digitalWrite(13,led_state); +} + diff --git a/eRCaGuy_PPM_Writer.h b/eRCaGuy_PPM_Writer.h index f79f9cc..7fbc8cb 100644 --- a/eRCaGuy_PPM_Writer.h +++ b/eRCaGuy_PPM_Writer.h @@ -79,7 +79,7 @@ const unsigned int DEFAULT_MAX_CHANNEL_VAL = 2100*2; //0.5us units const unsigned int DEFAULT_MIN_CHANNEL_VAL = 900*2; //0.5us const unsigned int DEFAULT_CHANNEL_VAL = 1500*2; //0.5us const unsigned int DEFAULT_MIN_FRAME_SPACE = max(DEFAULT_MAX_CHANNEL_VAL + 100*2,3000*2); //0.5us; min time gap between each frame, ie: between the end of the last channel and the start of the first channel -const unsigned int DEFAULT_CHANNEL_SPACE = 400*2; //0.5us; the pulse that signifies the start of each new channel +const unsigned int DEFAULT_CHANNEL_SPACE = 400*2; //0.5us; the short pulse whose first edge signifies the start of each new channel const unsigned long DEFAULT_FRAME_PERIOD = 22000UL*2UL; //0.5us; note: the default period for the Spektrum DX8, for instance, is 22ms, or a freq. of 45.45Hz // const unsigned int MAX_OUTPUT_COMPARE_INCREMENT = 2^16; //make 2^16, or 65536, since Timer1 is a 16-bit timer/counter const boolean PPM_WRITER_NORMAL = 0; //base-line HIGH, channelSpace pulses are LOW @@ -102,7 +102,7 @@ class eRCaGuy_PPM_Writer void setMinFrameSpace(unsigned int minFrameSpace); //0.5us void setChannelSpace(unsigned int channelSpace); //0.5us void setFrameNumber(unsigned long frameNum); //manually set the frameNumber to a specific value; ex: 0, to reset it - void setPPMPolarity(boolean polarity); + void setPPMPolarity(boolean polarity); //set PPM_WRITER_NORMAL or PPM_WRITER_INVERTED //"get" methods //primary methods