From ec2837067d7e827e0af6661a45612656b2b50878 Mon Sep 17 00:00:00 2001 From: Ivan Volosyuk Date: Sun, 18 Apr 2021 22:46:56 +1000 Subject: [PATCH 1/5] Removed __ECV__ ifdefs --- MiniLedSensor.cpp | 57 ++--------------------------------------------- 1 file changed, 2 insertions(+), 55 deletions(-) diff --git a/MiniLedSensor.cpp b/MiniLedSensor.cpp index 9cc7199..a9d42e4 100644 --- a/MiniLedSensor.cpp +++ b/MiniLedSensor.cpp @@ -15,28 +15,12 @@ #include "ecv.h" -#ifdef __ECV__ -#define __attribute__(_x) -#define __volatile__ -#endif - -#ifdef __ECV__ -#pragma ECV noverifyincludefiles -#endif #include #include #include #include -#ifdef __ECV__ -#pragma ECV verifyincludefiles -#undef cli -#undef sei -extern void cli(); -extern void sei(); -#endif - #define ISR_DEBUG (0) // set nonzero to use PB2 as debug output pin #define BITVAL(_x) static_cast(1u << (_x)) @@ -49,9 +33,7 @@ extern void sei(); // PB4/ADC2 input from phototransistor // PB5/ADC0/RESET not available, used for programming -#ifndef __ECV__ __fuse_t __fuse __attribute__((section (".fuse"))) = {0xE2u, 0xDFu, 0xFFu}; -#endif const unsigned int AdcPhototransistorChan = 2; // ADC channel for the phototransistor const unsigned int AdcPortBDuet10KOutputChan = 3; // ADC channel for the 10K output bit, when we use it as an input @@ -63,12 +45,12 @@ const unsigned int PortBDuet12KOutputBit = 2; const uint8_t PortBUnusedBitMask = 0; // Approximate MPU frequency (8MHz internal oscillator) -const uint32_t F_CPU = 8000000uL; +const uint32_t F_CPU2 = 8000000uL; // IR parameters. These also allow us to receive a signal through the command input. const uint16_t interruptFreq = 8000; // interrupt frequency. We run the IR sensor at one quarter of this, i.e. 2kHz // highest usable value is about 9.25kHz because the ADC needs 13.5 clock cycles per conversion. -const uint16_t divisorIR = (uint16_t)(F_CPU/interruptFreq); +const uint16_t divisorIR = (uint16_t)(F_CPU2/interruptFreq); const uint16_t prescalerIR = 8; // needs to be high enough to get baseTopIR below 256 const uint16_t baseTopIR = (divisorIR/prescalerIR) - 1u; const uint16_t cyclesAveragedIR = 8; // must be a power of 2, max 64 (unless we make onSumIR and offSumIR uint32_t) @@ -109,10 +91,6 @@ struct IrData returns((forall r in readings :- r <= 1023) && sum == + over readings); ) -#ifdef __ECV__ - // Dummy constructor to keep eCv happy - IrData() : index(0) {} -#endif }; IrData nearData, farData, offData; @@ -127,11 +105,7 @@ bool running = false; // ISR for the timer 0 compare match A interrupt // This works on a cycle of 4 readings as follows: // far led on, near led on, leds off, null -#ifdef __ECV__ -void TIM0_COMPB_vect() -#else ISR(TIM0_COMPB_vect) -#endif writes(nearData; farData; offData) writes(volatile) pre(nearData.invar(); farData.invar(); offData.invar()) @@ -177,29 +151,6 @@ post(nearData.invar(); farData.invar(); offData.invar()) ++tickCounter; } -#if 0 // unused at present - -// Delay for a little while -// Each iteration of the loop takes 4 clocks plus one clock per NOP instruction in the body. -// The additional overhead for the function, including calling it, is 12 clocks. -// Therefore, with F_CPU = 8MHz and using 4 NOP instructions, it delays n + 1.5 microseconds. -void shortDelay(uint8_t n) -{ - for (uint8_t i = 0; i < n; ++i) - keep(i <= n) - decrease(n - i) - { -#ifndef __ECV__ // eCv doesn't understand asm - asm volatile ("nop"); - asm volatile ("nop"); - asm volatile ("nop"); - asm volatile ("nop"); -#endif - } -} - -#endif - // Give a G31 reading of about 0 inline void SetOutputOff() writes(volatile) @@ -252,9 +203,7 @@ writes(lastKickTicks; volatile) { if (GetTicks() - lastKickTicks >= kickIntervalTicks) { -#ifndef __ECV__ wdt_reset(); // kick the watchdog -#endif lastKickTicks += kickIntervalTicks; } } @@ -407,9 +356,7 @@ writes(lastKickTicks) sei(); -#ifndef __ECV__ // eCv++ doesn't understand gcc assembler syntax wdt_enable(WDTO_500MS); // enable the watchdog (we kick it when checking the fan) -#endif runIRsensor(); // doesn't return return 0; // to keep gcc happy From 6633ed197b9117c3d16971f6a5c8a0f7cae35544 Mon Sep 17 00:00:00 2001 From: Ivan Volosyuk Date: Sun, 18 Apr 2021 22:54:24 +1000 Subject: [PATCH 2/5] Instant startup in digital mode. --- MiniLedSensor.cpp | 45 +++++++-------------------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/MiniLedSensor.cpp b/MiniLedSensor.cpp index a9d42e4..7b1fc57 100644 --- a/MiniLedSensor.cpp +++ b/MiniLedSensor.cpp @@ -98,7 +98,6 @@ IrData nearData, farData, offData; // General variables volatile uint16_t tickCounter = 0; // counts system ticks, lower 2 bits also used for ADC/LED state uint16_t lastKickTicks = 0; // when we last kicked the watchdog -bool digitalOutput = false; bool running = false; @@ -225,7 +224,7 @@ writes(lastKickTicks; volatile) // Run the IR sensor and the fan void runIRsensor() -writes(running; nearData; farData; offData; lastKickTicks; digitalOutput; volatile) +writes(running; nearData; farData; offData; lastKickTicks) { running = false; nearData.init(); @@ -257,33 +256,17 @@ writes(running; nearData; farData; offData; lastKickTicks; digitalOutput; volati // Wait a while before we do this test, so that Duet firmware has a chance to turn the internal pullup (50K to 150K) off, // and Arduino/RAMPS firmware has a chance to turn the internal pullup (20K to 50K) on. SetOutputOff(); - DDRB &= ~BITVAL(PortBDuet10KOutputBit); // set the pin to an input, pullup disabled because output is off - // Wait 4 seconds, keeping the watchdog happy - DelayTicks(4u); // ignore the readings from the first few interrupts after changing mode - running = true; // start collecting readings - DelayTicks(4u * interruptFreq); // give the printer electronics time to enable/disable its pullup resistor - running = false; // stop collecting readings // Readings have been collected into all three of nearData, farData, and offData. // We are looking for a pullup resistor of no more than 75K on the output to indicate that we should use a digital output. // DC 2014-08-04 we now look for no more than 160K, because on the Arduino Due the pullups are in the range 50K-150K. - digitalOutput = offData.sum + nearData.sum + farData.sum >= (3600UL * cyclesAveragedIR * 1024UL * 3u)/(160000UL + 3600UL); + //digitalOutput = offData.sum + nearData.sum + farData.sum >= (3600UL * cyclesAveragedIR * 1024UL * 3u)/(160000UL + 3600UL); // Change back to normal operation mode ADMUX = (uint8_t)AdcPhototransistorChan; // select input 1 = phototransistor, single ended mode DDRB |= BITVAL(PortBDuet10KOutputBit); // set the pin back to being an output - // Flash the LED twice if we are providing a digital output, four times if we are providing an analog output - for (uint8_t flashesToGo = (digitalOutput) ? 2u : 4u; flashesToGo != 0u; ) - { - SetOutputSaturated(); // turn LED on - DelayTicks(interruptFreq/4u); - SetOutputOff(); - DelayTicks(interruptFreq/4u); - --flashesToGo; - } - // Clear out the data and start collecting data from the phototransistor nearData.init(); farData.init(); @@ -312,24 +295,10 @@ writes(running; nearData; farData; offData; lastKickTicks; digitalOutput; volati locFarSum = (locFarSum > locOffSum) ? locFarSum - locOffSum : 0; // Differential modulated IR sensor mode - if (locFarSum >= farThreshold && locNearSum > locFarSum) - { - if (digitalOutput) - { - SetOutputSaturated(); - } - else - { - SetOutputOn(); - } - } - else if (!digitalOutput && locFarSum >= farThreshold && locNearSum * 6UL >= locFarSum * 5UL) - { - SetOutputApproaching(); - } - else - { - SetOutputOff(); + if (locFarSum >= farThreshold && locNearSum > locFarSum) { + SetOutputSaturated(); + } else { + SetOutputOff(); } } @@ -341,7 +310,7 @@ writes(running; nearData; farData; offData; lastKickTicks; digitalOutput; volati // Main program int main(void) writes(volatile) -writes(running; digitalOutput) +writes(running) writes(nearData; farData; offData) // IR variables writes(lastKickTicks) { From 11b82bb5e413ef85051aa71e28988076f0edaa39 Mon Sep 17 00:00:00 2001 From: Ivan Volosyuk Date: Sun, 18 Apr 2021 23:04:18 +1000 Subject: [PATCH 3/5] Arduino ino file. --- IR-Sensor-Firmware.ino | 1 + 1 file changed, 1 insertion(+) create mode 100644 IR-Sensor-Firmware.ino diff --git a/IR-Sensor-Firmware.ino b/IR-Sensor-Firmware.ino new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/IR-Sensor-Firmware.ino @@ -0,0 +1 @@ + From 00e62052f0e7b4e549a3d955438dad03c90d28a7 Mon Sep 17 00:00:00 2001 From: Ivan Volosyuk Date: Sun, 18 Apr 2021 23:05:02 +1000 Subject: [PATCH 4/5] Updated compiled firmware. --- MiniLedSensor.hex | 116 ++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 66 deletions(-) diff --git a/MiniLedSensor.hex b/MiniLedSensor.hex index 059850e..829925e 100644 --- a/MiniLedSensor.hex +++ b/MiniLedSensor.hex @@ -1,66 +1,50 @@ -:100000000EC01BC01AC019C018C017C016C015C03A -:1000100014C013C012C012C010C00FC00EC01124F3 -:100020001FBECFEDCDBF20E0A0E6B0E001C01D9225 -:10003000AF39B207E1F7DAD1E9C1E2CF1F920F92EF -:100040000FB60F9211242F933F934F935F936F93AB -:100050007F938F939F93AF93BF93EF93FF9384B15D -:1000600095B1AC015370209164003091650082B766 -:100070008831E8F3C901837099278130910551F0E7 -:1000800038F08230910559F1039709F44DC070C0E2 -:10009000C0986EC0809160008823F9F0A9E7B0E0B5 -:1000A00050962D913C91519752968C915297E82FF2 -:1000B000F0E0240F351FEE0FFF1FE758FF4F608160 -:1000C0007181261B370B51963C932E935097518389 -:1000D00040838F5F877052968C93C19A49C08091FC -:1000E00060008823F9F0A6E6B0E050962D913C918F -:1000F000519752968C915297E82FF0E0240F351FBC -:10010000EE0FFF1FEA59FF4F60817181261B370BED -:1001100051963C932E935097518340838F5F877005 -:1001200052968C93C19824C0809160008823F9F086 -:10013000ACE8B0E050962D913C91519752968C913D -:100140005297E82FF0E0240F351FEE0FFF1FE45702 -:10015000FF4F60817181261B370B51963C932E9384 -:100160005097518340838F5F877052968C93C09ACB -:100170008091640090916500019690936500809352 -:100180006400FF91EF91BF91AF919F918F917F910B -:100190006F915F914F913F912F910F900FBE0F90F4 -:1001A0001F901895F8948091640090916500789460 -:1001B0002091620030916300821B930B843F914039 -:1001C00038F0A8952C503E4F309363002093620086 -:1001D00008950F931F93CF93DF938C01F894C091F0 -:1001E0006400D09165007894DDDFF894209164007C -:1001F0003091650078942C1B3D0B20173107A0F33C -:10020000DF91CF911F910F910895FC019C01205F18 -:100210003F4F11921192E217F307D9F7FC01128AAE -:10022000118A108A0895109260008CE890E0EDDF4A -:1002300089E790E0EADF86E690E0E7DFF8941CBC0F -:1002400082E08ABD13BE12BE8CE789BD18BC88E06F -:1002500088BF89BF83B7826083BF83E087B986EA9E -:1002600086B985E083B910926500109264001092FF -:100270006300109262007894C398C298BB9884E09F -:1002800090E0A7DF81E08093600080E09DE7A1DF40 -:1002900010926000209176003091770040919C0090 -:1002A00050919D008091890090918A00240F351F04 -:1002B000820F931F21E08C31924008F420E02093BC -:1002C000610082E087B9BB9A80916100811187C08B -:1002D000C4E0C39AC29A80ED97E07BDFC398C298CE -:1002E00080ED97E076DFC150A1F78CE890E08DDFDC -:1002F00089E790E08ADF86E690E087DF81E08093FF -:10030000600080E290E065DF0F2EFCE8EF2EF0E069 -:10031000FF2EF02D09E710E0C6E6D0E0C12CD12C6D -:10032000F894F70180899189F801208931894889F9 -:10033000598978948033FBE19F0720F420336BE1E7 -:10034000360718F0C39AC29A48C04817590718F4DC -:10035000841B950B02C08C2D9D2D42175307D8F599 -:10036000241B350B20353105B0F128173907C8F5A6 -:1003700080916100882319F0C39AC29A2EC0C39A53 -:10038000C2982BC0A0E0B0E0AC01BD01440F551FE6 -:10039000661F771F840F951FA61FB71F880F991F11 -:1003A000AA1FBB1FA90160E070E04A015B01880C35 -:1003B000991CAA1CBB1C880C991CAA1CBB1C480DB0 -:1003C000591D6A1D7B1D84179507A607B70718F0EE -:1003D000C398C29A02C0C398C298E4DEA1CFC2E01B -:1003E00078CF409161004111F6CFCCCFF89484E0F2 -:1003F00084BB18BA8FE087BB78949DE088E10FB684 -:10040000F894A89581BD0FBE91BD0DDFF894FFCF84 -:00000001FF +:100000000EC01BC01AC019C018C017C016C015C03A +:1000100014C013C012C048C010C00FC00EC01124BD +:100020001FBECFEDCDBF20E0A0E6B0E001C01D9225 +:10003000AE39B207E1F7DDD062C1E2CFFC01118237 +:100040001082138212821582148217821682118600 +:100050001086138612861586148617861686128ACF +:10006000118A108A0895F89480916200909163003B +:1000700078942091600030916100821B930B843F43 +:10008000914058F0A89580916000909161008C504B +:100090009E4F90936100809360000895C39AC29A26 +:1000A0000895C398C29808951F920F920FB60F92A9 +:1000B00011242F933F934F935F936F937F938F936D +:1000C0009F93EF93FF9384B195B193702091620059 +:1000D0003091630042B74831E8F323703327223070 +:1000E000310509F442C02330310509F460C02130E4 +:1000F0003105C9F0C098809162009091630001962B +:100100009093630080936200FF91EF919F918F9194 +:100110007F916F915F914F913F912F910F900FBE03 +:100120000F901F90189520919D002223E1F040919F +:1001300087005091880020918900E22FF0E0480F5D +:10014000591FEE0FFF1FE958FF4F60817181461B59 +:10015000570B5093880040938700918380832F5FD3 +:10016000277020938900C19AC6CF20919D00222339 +:10017000E1F0409174005091750020917600E22FDB +:10018000F0E0480F591FEE0FFF1FEC59FF4F608141 +:100190007181461B570B50937500409374009183F7 +:1001A00080832F5F277020937600C198A4CF209181 +:1001B0009D002223E1F040919A0050919B002091F4 +:1001C0009C00E22FF0E0480F591FEE0FFF1FE6578B +:1001D000FF4F60817181461B570B50939B004093EA +:1001E0009A00918380832F5F277020939C00C09A90 +:1001F00082CFF89484E084BB18BA8FE087BB7894F0 +:1002000088E19DE00FB6F894A89581BD0FBE91BD21 +:1002100010929D008AE890E011DF87E790E00EDF02 +:1002200084E690E00BDFF8941CBCC2E0CABD13BEAC +:1002300012BE8CE789BD18BC88E088BF89BF83B730 +:10024000826083BF83E087B986EA86B985E083B997 +:100250001092630010926200109261001092600090 +:1002600078941FDFC7B9BB9A8AE890E0E7DE87E79A +:1002700090E0E4DE84E690E0E1DE81E080939D00A2 +:10028000F894C0916200D09163007894ECDEF89409 +:10029000809162009091630078948C1B9D0B8097F5 +:1002A000A8F3F89480919A0090919B002091870088 +:1002B000309188004091740050917500789480339B +:1002C0006BE1960720F420336BE1360718F0E6DE89 +:1002D000CADEE7CF4817590778F4841B950B4217FD +:1002E000530740F4241B350B2035310518F028172F +:1002F000390768F3D6DEECCF90E080E0F0CFF894D9 +:02030000FFCF2D +:00000001FF From 222031baf068688ca9d0fb3279067aa8978fea85 Mon Sep 17 00:00:00 2001 From: Ivan Volosyuk Date: Mon, 19 Apr 2021 23:06:59 +1000 Subject: [PATCH 5/5] Speed up the measurement loop. Cleanup timer interrupts code, use ADC noise reduction, get rid of integration logic, a bit of formatting. --- MiniLedSensor.cpp | 366 +++++++++------------------------------------- MiniLedSensor.hex | 62 ++------ README.md | 4 + ecv.h | 204 -------------------------- 4 files changed, 88 insertions(+), 548 deletions(-) delete mode 100644 ecv.h diff --git a/MiniLedSensor.cpp b/MiniLedSensor.cpp index 7b1fc57..c19f43d 100644 --- a/MiniLedSensor.cpp +++ b/MiniLedSensor.cpp @@ -12,14 +12,13 @@ // Version 4: increased maximum value of the pullup resistor we look for to 150K, because it is higher on the Arduino Due // Version 5: increased maximum value of the pullup resistor we look for to 160K, to get reliable results with the 150K resistor in the test rig // Version 6: Don't enable pullup resistor on phototransistor input - -#include "ecv.h" - +// Version 7: By Ivan Volosyuk: rewrite without interrupts and with ADC noise reduction #include #include #include #include +#include #define ISR_DEBUG (0) // set nonzero to use PB2 as debug output pin @@ -28,10 +27,10 @@ // Pin assignments: // PB0/MOSI far LED drive, active high // PB1/MISO near LED drive, active high -// PB2/ADC1/SCK output to Duet via 12K resistor +// PB2/ADC1/SCK output to Duet via 12K resistor // PB3/ADC3 output to Duet via 10K resistor // PB4/ADC2 input from phototransistor -// PB5/ADC0/RESET not available, used for programming +// PB5/ADC0/RESET not available, used for programming __fuse_t __fuse __attribute__((section (".fuse"))) = {0xE2u, 0xDFu, 0xFFu}; @@ -41,309 +40,86 @@ const unsigned int PortBNearLedBit = 1; const unsigned int PortBFarLedBit = 0; const unsigned int PortBDuet10KOutputBit = 3; const unsigned int PortBDuet12KOutputBit = 2; - +const uint8_t OutputOn = BITVAL(PortBDuet10KOutputBit) | BITVAL(PortBDuet12KOutputBit); const uint8_t PortBUnusedBitMask = 0; -// Approximate MPU frequency (8MHz internal oscillator) -const uint32_t F_CPU2 = 8000000uL; - -// IR parameters. These also allow us to receive a signal through the command input. -const uint16_t interruptFreq = 8000; // interrupt frequency. We run the IR sensor at one quarter of this, i.e. 2kHz - // highest usable value is about 9.25kHz because the ADC needs 13.5 clock cycles per conversion. -const uint16_t divisorIR = (uint16_t)(F_CPU2/interruptFreq); -const uint16_t prescalerIR = 8; // needs to be high enough to get baseTopIR below 256 -const uint16_t baseTopIR = (divisorIR/prescalerIR) - 1u; -const uint16_t cyclesAveragedIR = 8; // must be a power of 2, max 64 (unless we make onSumIR and offSumIR uint32_t) - // *** max is now 16 because we add the 3 sums when we sense the pullup resistor *** - -const uint16_t farThreshold = 10u * cyclesAveragedIR; // minimum far reading for us to think the sensor is working properly -const uint16_t saturatedThreshold = 870u * cyclesAveragedIR; // minimum reading for which we consider the sensor saturated - -const uint16_t kickFreq = 16; -const uint16_t kickIntervalTicks = interruptFreq/kickFreq; - -// IR variables -typedef uint8_t invariant(value < cyclesAveragedIR) irIndex_t; - -struct IrData -{ - uint16_t readings[cyclesAveragedIR]; - volatile uint16_t sum; - irIndex_t index; - - void addReading(uint16_t arg) - writes(*this; volatile) - pre(invar()) - pre(arg <= 1023) - post(invar()) - { - sum = sum - readings[index] + arg; - readings[index] = arg; - index = static_cast((index + 1u) % cyclesAveragedIR); - } - - void init() - writes(*this; volatile) - post(invar()); - - ghost( - bool invar() const - returns((forall r in readings :- r <= 1023) && sum == + over readings); - ) - -}; - -IrData nearData, farData, offData; - -// General variables -volatile uint16_t tickCounter = 0; // counts system ticks, lower 2 bits also used for ADC/LED state -uint16_t lastKickTicks = 0; // when we last kicked the watchdog -bool running = false; - - -// ISR for the timer 0 compare match A interrupt -// This works on a cycle of 4 readings as follows: -// far led on, near led on, leds off, null -ISR(TIM0_COMPB_vect) -writes(nearData; farData; offData) -writes(volatile) -pre(nearData.invar(); farData.invar(); offData.invar()) -post(nearData.invar(); farData.invar(); offData.invar()) -{ - uint16_t adcVal = ADC & 1023u; // get the ADC reading from the previous conversion - uint8_t locTickCounter = (uint8_t)tickCounter; - while (TCNT0 < 3u * 8u) {} // delay a little until the ADC s/h has taken effect. 3 ADC clocks should be enough, and 1 ADC clock is 8 timer 0 clocks. - switch(locTickCounter & 0x03u) - { - case 0: - // Far LED is on, we just did no reading, we are doing a far reading now and an off reading next - PORTB &= ~BITVAL(PortBFarLedBit); // turn far LED off - break; - - case 1: - // LEDs are off, we just did a far reading, we are doing a off reading now and a near reading next - if (running) - { - farData.addReading(adcVal); - } - PORTB |= BITVAL(PortBNearLedBit); // turn near LED on - break; - - case 2: - // Near LED is on, we just did an off reading, we are doing a near reading now and a dummy off reading next - if (running) - { - offData.addReading(adcVal); - } - PORTB &= ~BITVAL(PortBNearLedBit); // turn near LED off - break; - - case 3: - // Far LED is on, we just did an off reading, we are doing another off reading now which will be discarded - if (running) - { - nearData.addReading(adcVal); - } - PORTB |= BITVAL(PortBFarLedBit); // turn far LED on - break; - } - ++tickCounter; -} - -// Give a G31 reading of about 0 -inline void SetOutputOff() -writes(volatile) -{ - // We do this in 2 operations, each of which is atomic, so that we don't mess up what the ISR is doing with the LEDs. - PORTB &= ~BITVAL(PortBDuet10KOutputBit); - PORTB &= ~BITVAL(PortBDuet12KOutputBit); +ISR(ADC_vect) { // ADC interrupt handler } -// Give a G31 reading of about 445 indicating we are approaching the trigger point -inline void SetOutputApproaching() -writes(volatile) -{ - // We do this in 2 operations, each of which is atomic, so that we don't mess up what the ISR is doing with the LEDs. - PORTB &= ~BITVAL(PortBDuet10KOutputBit); - PORTB |= BITVAL(PortBDuet12KOutputBit); -} - -// Give a G31 reading of about 578 indicating we are at/past the trigger point -inline void SetOutputOn() -writes(volatile) -{ - // We do this in 2 operations, each of which is atomic, so that we don't mess up what the ISR is doing with the LEDs. - PORTB |= BITVAL(PortBDuet10KOutputBit); - PORTB &= ~BITVAL(PortBDuet12KOutputBit); -} - -// Give a G31 reading of about 1023 indicating that the sensor is saturating -inline void SetOutputSaturated() -writes(volatile) -{ - // We do this in 2 operations, each of which is atomic, so that we don't mess up what the ISR is doing with the LEDs. - PORTB |= BITVAL(PortBDuet10KOutputBit); - PORTB |= BITVAL(PortBDuet12KOutputBit); -} - -// Get the tick counter from outside the ISR. As it's more than 8 bits long, we need to disable interrupts while fetching it. -inline uint16_t GetTicks() -writes(volatile) -{ - cli(); - uint16_t ticks = tickCounter; - sei(); - return ticks; -} - -// Check whether we need to kick the watchdog -void CheckWatchdog() -writes(lastKickTicks; volatile) -{ - if (GetTicks() - lastKickTicks >= kickIntervalTicks) - { - wdt_reset(); // kick the watchdog - lastKickTicks += kickIntervalTicks; - } -} - -// Delay for a specified number of ticks -void DelayTicks(uint16_t ticks) -writes(lastKickTicks; volatile) -{ - uint16_t startTicks = GetTicks(); - for (;;) - { - CheckWatchdog(); - if (GetTicks() - startTicks >= ticks) - { - break; - } - } +void startADC() { + ADCSRA = BITVAL(ADEN) | BITVAL(ADSC) | BITVAL(ADIE); // enable ADC, start conversion + sleep_enable(); + do { + sei(); + sleep_cpu(); + cli(); + } while(ADCSRA & BITVAL(ADSC)); + sleep_disable(); } // Run the IR sensor and the fan -void runIRsensor() -writes(running; nearData; farData; offData; lastKickTicks) -{ - running = false; - nearData.init(); - farData.init(); - offData.init(); - - cli(); - // Set up timer 1 in mode 2 (CTC mode) - GTCCR = 0; - TCCR0A = BITVAL(WGM01); // no direct outputs, mode 2 - TCCR0B = 0; // set the mode, clock stopped for now - TCNT0 = 0; - OCR0A = baseTopIR; - OCR0B = 0; - TIFR = BITVAL(OCF0B); // clear any pending interrupt - TIMSK = BITVAL(OCIE0B); // enable the timer 0 compare match B interrupt - TCCR0B |= BITVAL(CS01); // start the clock, prescaler = 8 - - ADMUX = (uint8_t)AdcPortBDuet10KOutputChan; // select the 10K resistor output bit, single ended mode - ADCSRA = BITVAL(ADEN) | BITVAL(ADPS2) | BITVAL(ADATE) | BITVAL(ADPS1); // enable ADC, auto trigger enable, prescaler = 64 (ADC clock ~= 125kHz) - ADCSRB = BITVAL(ADTS2) | BITVAL(ADTS0); // start conversion on timer 0 compare match B, unipolar input mode - tickCounter = 0; - lastKickTicks = 0; - sei(); - - // Determine whether to provide a digital output or a 4-state output. - // We do this by checking to see whether the connected electronics provided a pullup resistor on the output. - // If a pullup resistor is detected, we provide a digital output, else we provide an analog output. - // Wait a while before we do this test, so that Duet firmware has a chance to turn the internal pullup (50K to 150K) off, - // and Arduino/RAMPS firmware has a chance to turn the internal pullup (20K to 50K) on. - SetOutputOff(); - - - // Readings have been collected into all three of nearData, farData, and offData. - // We are looking for a pullup resistor of no more than 75K on the output to indicate that we should use a digital output. - // DC 2014-08-04 we now look for no more than 160K, because on the Arduino Due the pullups are in the range 50K-150K. - //digitalOutput = offData.sum + nearData.sum + farData.sum >= (3600UL * cyclesAveragedIR * 1024UL * 3u)/(160000UL + 3600UL); - - // Change back to normal operation mode - ADMUX = (uint8_t)AdcPhototransistorChan; // select input 1 = phototransistor, single ended mode - DDRB |= BITVAL(PortBDuet10KOutputBit); // set the pin back to being an output - - // Clear out the data and start collecting data from the phototransistor - nearData.init(); - farData.init(); - offData.init(); - - running = true; // tell interrupt handler to collect readings - DelayTicks(4u * cyclesAveragedIR); // wait until we have a full set of readings - - // Start normal operation - for (;;) - keep(nearData.invar(); farData.invar(); offData.invar()) - { - cli(); - uint16_t locNearSum = nearData.sum; - uint16_t locFarSum = farData.sum; - uint16_t locOffSum = offData.sum; - sei(); - - if (locNearSum >= saturatedThreshold || locFarSum >= saturatedThreshold) - { - SetOutputSaturated(); // sensor is saturating, so set the output full on to indicate this - } - else - { - locNearSum = (locNearSum > locOffSum) ? locNearSum - locOffSum : 0; - locFarSum = (locFarSum > locOffSum) ? locFarSum - locOffSum : 0; - - // Differential modulated IR sensor mode - if (locFarSum >= farThreshold && locNearSum > locFarSum) { - SetOutputSaturated(); - } else { - SetOutputOff(); - } - } - - CheckWatchdog(); - } -} +void runIRsensor() { + cli(); + TIFR = BITVAL(OCF0B); // clear any pending interrupt + + ADMUX = (uint8_t)AdcPortBDuet10KOutputChan; // select the 10K resistor output bit, single ended mode + ADCSRA = BITVAL(ADEN) | BITVAL(ADIE); // enable ADC + ADCSRB = 0; // ADC manual invocation + set_sleep_mode( SLEEP_MODE_ADC ); + sleep_enable(); + + // Change back to normal operation mode + ADMUX = (uint8_t)AdcPhototransistorChan; // select input 1 = phototransistor, single ended mode + DDRB |= BITVAL(PortBDuet10KOutputBit); // set the pin back to being an output + + uint8_t output = BITVAL(PortBDuet12KOutputBit) | BITVAL(PortBDuet10KOutputBit); // Full output via both resistors + PORTB = output; + sei(); + + // Start normal operation + for (;;) { + startADC(); + uint16_t offVal = ADC; // get the ADC reading from the previous conversion + PORTB = output | BITVAL(PortBNearLedBit); // turn near LED on + + startADC(); // waste some time, initial value after switch doesn't look stable + startADC(); + uint16_t nearVal = ADC; // get the ADC reading from the previous conversion + PORTB = output | BITVAL(PortBFarLedBit); // turn far LED on + startADC(); // waste some time, initial value after switch doesn't look stable + startADC(); + uint16_t farVal = ADC; // get the ADC reading from the previous conversion + PORTB = output; // turn of LEDs + + uint16_t nearClean = (nearVal > offVal) ? nearVal - offVal : 0; + uint16_t farClean = (farVal > offVal) ? farVal - offVal : 0; + + // Differential modulated IR sensor mode + if (nearVal > farVal) { + output = OutputOn; + } else { + output = 0; + } + PORTB = output; + } + } // Main program -int main(void) -writes(volatile) -writes(running) -writes(nearData; farData; offData) // IR variables -writes(lastKickTicks) -{ - cli(); - DIDR0 = BITVAL(AdcPhototransistorChan); // disable digital input buffers on ADC pins +int main(void) { + cli(); + DIDR0 = BITVAL(AdcPhototransistorChan); // disable digital input buffers on ADC pins - // Set ports and pullup resistors - PORTB = PortBUnusedBitMask; // enable pullup on unused I/O pins - - // Enable outputs - DDRB = BITVAL(PortBNearLedBit) | BITVAL(PortBFarLedBit) | BITVAL(PortBDuet10KOutputBit) | BITVAL(PortBDuet12KOutputBit); - - sei(); + // Set ports and pullup resistors + PORTB = PortBUnusedBitMask; // enable pullup on unused I/O pins - wdt_enable(WDTO_500MS); // enable the watchdog (we kick it when checking the fan) - - runIRsensor(); // doesn't return - return 0; // to keep gcc happy -} + // Enable outputs + DDRB = BITVAL(PortBNearLedBit) | BITVAL(PortBFarLedBit) | BITVAL(PortBDuet10KOutputBit) | BITVAL(PortBDuet12KOutputBit); + + sei(); -// Initialize the IR data structure -void IrData::init() -{ - for (uint8_t i = 0; i < cyclesAveragedIR; ++i) - writes(i; *this; volatile) - keep(i <= cyclesAveragedIR) - keep(forall j in 0..(i-1) :- readings[j] == 0) - decrease(cyclesAveragedIR - i) - { - readings[i] = 0; - } - index = 0; - sum = 0; + runIRsensor(); // doesn't return + return 0; // to keep gcc happy } // End diff --git a/MiniLedSensor.hex b/MiniLedSensor.hex index 829925e..6a138cd 100644 --- a/MiniLedSensor.hex +++ b/MiniLedSensor.hex @@ -1,50 +1,14 @@ -:100000000EC01BC01AC019C018C017C016C015C03A -:1000100014C013C012C048C010C00FC00EC01124BD -:100020001FBECFEDCDBF20E0A0E6B0E001C01D9225 -:10003000AE39B207E1F7DDD062C1E2CFFC01118237 -:100040001082138212821582148217821682118600 -:100050001086138612861586148617861686128ACF -:10006000118A108A0895F89480916200909163003B -:1000700078942091600030916100821B930B843F43 -:10008000914058F0A89580916000909161008C504B -:100090009E4F90936100809360000895C39AC29A26 -:1000A0000895C398C29808951F920F920FB60F92A9 -:1000B00011242F933F934F935F936F937F938F936D -:1000C0009F93EF93FF9384B195B193702091620059 -:1000D0003091630042B74831E8F323703327223070 -:1000E000310509F442C02330310509F460C02130E4 -:1000F0003105C9F0C098809162009091630001962B -:100100009093630080936200FF91EF919F918F9194 -:100110007F916F915F914F913F912F910F900FBE03 -:100120000F901F90189520919D002223E1F040919F -:1001300087005091880020918900E22FF0E0480F5D -:10014000591FEE0FFF1FE958FF4F60817181461B59 -:10015000570B5093880040938700918380832F5FD3 -:10016000277020938900C19AC6CF20919D00222339 -:10017000E1F0409174005091750020917600E22FDB -:10018000F0E0480F591FEE0FFF1FEC59FF4F608141 -:100190007181461B570B50937500409374009183F7 -:1001A00080832F5F277020937600C198A4CF209181 -:1001B0009D002223E1F040919A0050919B002091F4 -:1001C0009C00E22FF0E0480F591FEE0FFF1FE6578B -:1001D000FF4F60817181461B570B50939B004093EA -:1001E0009A00918380832F5F277020939C00C09A90 -:1001F00082CFF89484E084BB18BA8FE087BB7894F0 -:1002000088E19DE00FB6F894A89581BD0FBE91BD21 -:1002100010929D008AE890E011DF87E790E00EDF02 -:1002200084E690E00BDFF8941CBCC2E0CABD13BEAC -:1002300012BE8CE789BD18BC88E088BF89BF83B730 -:10024000826083BF83E087B986EA86B985E083B997 -:100250001092630010926200109261001092600090 -:1002600078941FDFC7B9BB9A8AE890E0E7DE87E79A -:1002700090E0E4DE84E690E0E1DE81E080939D00A2 -:10028000F894C0916200D09163007894ECDEF89409 -:10029000809162009091630078948C1B9D0B8097F5 -:1002A000A8F3F89480919A0090919B002091870088 -:1002B000309188004091740050917500789480339B -:1002C0006BE1960720F420336BE1360718F0E6DE89 -:1002D000CADEE7CF4817590778F4841B950B4217FD -:1002E000530740F4241B350B2035310518F028172F -:1002F000390768F3D6DEECCF90E080E0F0CFF894D9 -:02030000FFCF2D +:100000000EC013C012C011C010C00FC00EC00DC072 +:100010001BC00BC00AC009C008C007C006C011241D +:100020001FBECFEDCDBF1AD04FC0EACF88EC86B946 +:1000300085B7806285BF78948895F8943699FBCF10 +:1000400085B78F7D85BF08951F920F920FB60F92CF +:1000500011240F900FBE0F901F901895F89484E014 +:1000600084BB18BA8FE087BB7894F89488E088BF87 +:1000700083E087B988E886B913B885B7877E88603A +:1000800085BF85B7806285BF82E087B9BB9A8CE067 +:1000900088BB7894CCE0CADF84B195B18C2F8260A4 +:1000A00088BBC4DFC3DF04B115B18C2F816088BB6E +:1000B000BDDFBCDF84B195B1C8BBC0E0801791073C +:0C00C00008F4CCE0C8BBE7CFF894FFCFF9 :00000001FF diff --git a/README.md b/README.md index 15b7a7e..4795337 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ R2 IR Sensor ================== ###(Based on the OrmerodSensorBoard) +This is a heavily modified version of the sensor firmware, which increases speed of the sensor. Firstly, the start time is instant instead of around 15 seconds I observed in original version. Secondly, due to the increased responsiveness - on Anet A8 with glass bed I'm getting 0.002 mm error instead of 0.15 mm in the original code. It produces only digital output: just on/off values instead of trying to autodetect digital or analog output to produce. + +--- + The IR Sensor PCBA uses two IR emitter diodes and one phototransistor to sense the nozzle distance from the build plate. The circuit is based off Dave Crocker's (dc42) Mini Differential IR board, along with his V1 firmware. This folder contains the firmware sources, including the compiled .hex file. The firmware is compiled in Atmel Studio (with the ATTiny library installed). The .pdp files are for doing static checking or formal verification of the firmware using Escher C++ Verifier. diff --git a/ecv.h b/ecv.h deleted file mode 100644 index 69c632b..0000000 --- a/ecv.h +++ /dev/null @@ -1,204 +0,0 @@ -#if !defined(__ECV_H_INCLUDED__) - -#define __ECV_H_INCLUDED__ - -#ifdef __ECV__ -#pragma ECV noverify -#endif - -/* Define the "normal" versions of the Arc keywords. - * If any of these clash with identifiers in the user program, you can #undef them after including this file. */ - -#define any _ecv_any -#define array _ecv_array -#define assert _ecv_assert -#define assume _ecv_assume -#define decrease _ecv_decrease -#define disjoint _ecv_disjoint -#define exists _ecv_exists -#define forall _ecv_forall -#define ghost _ecv_ghost -#define holds _ecv_holds -#define idiv _ecv_idiv -#define imod _ecv_imod -#define in _ecv_in -#define integer _ecv_integer -#define invariant _ecv_invariant -#define keep _ecv_keep -#define let _ecv_let -#define maxof _ecv_maxof -#define minof _ecv_minof -#define min_sizeof _ecv_min_sizeof -#define not_null _ecv_not_null -#define null _ecv_null -#define old _ecv_old -#define out _ecv_out -#define over _ecv_over -#define post _ecv_post -#define pre _ecv_pre -#define result _ecv_result -#define returns _ecv_returns -#define some _ecv_some -#define spec _ecv_spec -#define that _ecv_that -#define those _ecv_those -#define value _ecv_value -#define writes _ecv_writes -#define yield _ecv_yield -#define zero_init _ecv_zero_init - -#if defined(__ECV__) || !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 19901L) -# define restrict _ecv_restrict -#endif - -#if defined(__cplusplus) -# define early _ecv_early -# define from _ecv_from -# if defined(__ECV__) || (__cplusplus < 201103L) -// The C++'11 final, nullptr and override keywords are available whenever processing C++ with eCv -# define final _ecv_final -# define nullptr _ecv_nullptr -# define override _ecv_override -# endif -#endif - -#if defined(__ECV__) - -/************************ The following definitions are only active when running eCv **********************/ - -#if defined(_MSC_VER) - -/******************** Definitions for Microsoft compilers *********************/ - -/* Define Microsoft compiler-specific keywords as expanding to nothing */ -#define __declspec(_x) -#define __w64 -#define __inline inline -#define __cdecl -#define __fastcall -#define __stdcall - -#if _MSC_VER >= 1400 - -/ * Microsoft file crtdefs.h (which is included by nearly everything else) contains some references to types not defined there. - * To avoid problems, we include crtdefs.h here along with the files that define the missing types. */ -#define __STDC_WANT_SECURE_LIB__ (0) /* need this to undo some inline declarations that don't compile in eCv */ -#define _CRT_SECURE_NO_WARNINGS (1) -#include "crtdefs.h" -#include "locale.h" /* defines "struct lconv" */ -struct __lc_time_data { int _x; }; /* this is defined only in the CRT source */ -struct threadmbcinfostruct { int _x; }; /* this is defined only in the CRT source */ -#endif - -#endif /* end Microsoft compiler specific */ - -#if defined(__GNUC__) - -/*********************** Definitions for Gnu Compiler Collection C and C++ compilers ********************/ - -#define __inline__ inline -#define __restrict__ _ecv_restrict - -/* Hide gcc __attribute__ keyword from eCv. - * WARNING: some header files (e.g. _mingw.h) may "#undef __attribute__", which undoes this. */ -#define __attribute__(_x) - -/* The following are needed to handle gcc's implementation of variable argument lists */ -typedef struct __builtin_va_list__ { int x; } __builtin_va_list; -extern void __builtin_va_start(__builtin_va_list, const char*); -extern void __builtin_va_end(__builtin_va_list); - -#endif /* end gcc specific */ - -#if defined HI_TECH_C - -/****************************** Definitions for HiTech C compilers ********************************/ - -#define bit bool /* define HiTech bit type as equivalent to bool */ - -/* Define the HiTech C extra type qualifiers */ -#define interrupt _ecv_interrupt /* functions may be flagged as interrupt routines */ -#define persistent -#define near -#define bank0 -#define bank1 -#define bank2 -#define bank3 -#define eeprom -#define __interrupt _ecv_interrupt /* functions may be flagged as interrupt routines */ -#define __persistent -#define __near -#define __bank0 -#define __bank1 -#define __bank2 -#define __bank3 -#define __eeprom - -#define asm(_x) /* hide assembler from eCv */ - -#endif /* end HiTech compiler specific */ - -#if defined __IAR_SYSTEMS_ICC__ - -/****************************** Definitions for IAR C/C++ compilers ********************************/ - -/* Define the IAR extra keywords */ -#define __cc_version1 -#define __cc_version2 -#define __data16 -#define __data20 -#define __interrupt _ecv_interrupt /* functions may be flagged as interrupt routines */ -#define __intrinsic -#define __monitor -#define __no_init -#define __no_pic -#define __noreturn -#define __persistent -#define __ramfunc -#define __raw -#define __regvar -#define __root -#define __ro_placement -#define __save_reg20 -#define __task - -#endif /* end IAR compiler specific */ - -/**************************** End of compiler-specific definitions ***************************/ - -#else /* not running eCv */ - -/* Define eCv macros as expanding to nothing */ -#define _ecv_array -#define _ecv_assert(_x) ((void)0) -#define _ecv_assume(_x) -#define _ecv_change(_x) -#define _ecv_decrease(_x) -#define _ecv_ghost(_x) -#define _ecv_interrupt -#define _ecv_invariant(_x) -#define _ecv_keep(_x) -#define _ecv_not_null(_x) (_x) -#define _ecv_null -#define _ecv_out -#define _ecv_pass ((void)0) -#define _ecv_post(_x) -#define _ecv_pre(_x) -#define _ecv_restrict -#define _ecv_returns(_x) -#define _ecv_spec -#define _ecv_writes(_x) - -#if defined(__cplusplus) -#define _ecv_early -#define _ecv_final -#define _ecv_from -#define _ecv_nullptr (0) -#define _ecv_override -#endif - -#endif /* end "if defined(__ECV__) ... else ..." */ - -#endif /* end header guard */ - -/* End of file */