-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cleanup of hardware dependencies. Merge in SAM support. #437
Conversation
(0548bca) sam, samd code, which is based on MrBryonMiller's code. Code reorganized and cleaned.
First, I'm just going to say is thank you for the architecture clean up and improvements and doing a better job than me abstracting all this. Second, since you also had a look at the receiving code, I was wondering why it was based on a timer that fires no matter what, vs a pin interrupt that would only fire each time the IR signal toggles from low to high or high to low. Is it because on some AVR chips it's just difficult to do this on any pin while on newer chips per pin interrupts are trivial? |
By the way, does ESP32 in that branch need re-testing? If so let me know and I'll happily do it. |
I suspect the original design decision was related to the relatively limited resources available on 8-bit AVRs and that reserving interrupts in addition to 1 timer would reduce/limit the wider application/adaptation of the library (on 8-bit MCUs). This would still be valid today for AVRs and will continue to be a 'conflict' as more and more lower cost 32 bit MCUs are released. You might of course design it differently, if only focused on 32 bit MCUs with lots of flash, RAM & peripherals available or as a stand-alone 8-bit application. It would be interesting to know the trend/quantum of active users of 8-bit vs 32-bit MCUs over time. I suspect it is still largely 8-bit in Arduino-land(guess?), particularly with beginners & non-experts. @z3t0 ? |
@AnalysIR I'm confused. Are you saying it's less expensive on a limited resource chip to interrupt based on time no matter what, than to have a pin interrupt that only fires if the pin actually changes? |
No. Simply when creating a library for general use along with other 3rd party libraries...all on a resource limited platform....it makes less sense to 'reserve/use-up' multiple resources. Essentially for AVRs the library uses one timer (for Rx &Tx/PWM). If it was designed with interrupts, then it would still use up 1 timer for TxPWM plus the interrupt/pin. If it was the only functionality running on the MCU, then not a big problem. However, if you want it to work alongside other libraries & user applications, then it is a good idea for the library to use up as few resources as possible - on an 8-bit platform. |
@AnalysIR ok, maybe I just don't really understand how the code works. |
The additional timer is still required to generate the Carrier PWM for IR Tx.(generating the carrier without a dedicated Timer using bit-banging will vary by MCU/XTAL/Compiler/Optimisation and would be messy to manage/support for a general purpose library). All in all, the original design choice has stood the test of time very well for 8-bit MCUs. 32 bit platforms complicates things because of the relative abundance of resources. It is also important to note that standard IR receivers can distort mark/space timings by up to +/-200uSecs. For custom & stand-alone applications, interrupts are clearly the better way to go on all platforms....although I remember looking at the new ESP32 datasheet and it seems to have some new dedicated IR peripheral built in, which on first read seemed to be overkill, as typical interrupts/PWM/Timers seem to serve all IR needs. I have ordered some ESP32s to investigate this new IR peripheral. Essentially, it all boils down to making design decisions, based on the future 'goals' of the library while also taking the existing user base into consideration. I believe @z3t0 is looking into documenting the Goals and once that is in place getting answers to questions like yours should be more straightforward. |
@AnalysIR thank you for explaining, I totally missed out on the fact that this library uses a timer for sending too (which I haven't used at all). So if you're only receiving, you're losing out by using a timer over a pin interrupt, but if you're doing both, you've already lost the timer, so you might as well use it for both, makes sense. |
@bengtmartensson I just tested this on ESP32, it works fine. You're just missing one line for it to compile ok and after that it's ok. Good job with the refactoring:
|
@marcmerlin , @AnalysIR , @z3t0 |
Although in general you are of course correct. In this case, I have to disagree as I believe that the 'discussion' was both warranted and worthwhile, particularly given the recent efforts of @marcmerlin on this and related topics. I guess as long as PRs & Issues are the only places for discussion, there will always be a certain amount of spill-over like this. |
Regarding the bit-banging of the IR signal carrier: |
I started looking at @bengtmartensson excellent work pulling all of this together. Thank you! In reviewing the result I was going to make comments in two areas but first I thought I could help out by testing compilation and operation on an AdaFruit Feather M0 (a SAMD processor). I realized that the SEND_PIN option I initially allowed for (and which @ladyada kept in her version) had been removed. Now the pin is hard-coded as pin 3. Unfortunately I cannot find this pin at the headers of the product so I cannot test nor use this PR unless I modify it. This brings me to my first comment.
|
@MrBryonMiller :
@z3t0 : how about renaming Soft carrier (which I consider a better name than "bit banging"): I used an arduino.org M0+, 48kHz. (Sorry for that; I ordered it as "Arduino Zero"). I tested with IRSenddemo (which is supposed to generate a 40kHz carrier), and analyzed the generated signal using a board like this, deploying a non-demodulation TSMP58000 and the AGirs firmware and IrScrutinizer. (I have high confidence in this configuration.) I then, with parameters like @ladyada's, measured around 32kHz. So I changed them to the current ones, which gave pretty exactly 40kHz. So, in this sense, it HAS been tested empirically, although not extensively. (No need to test with a 'scope, though.) Having said that, I am not all convinced about the sanity of the implementation, neither the old one nor mine. It is completely non-robust against (varying) computational delays. I/We should probably try to cook up something better. But, if possible, a hardware carrier (hardware PWM) implementation should normally be preferred over a soft carrier implementation. |
Makes sense to me. |
That is generally my experience as well. Are there any particular benefits for a soft implementation? |
parameter, as requested by MrBryonMiller. Also changed the default for SAM to the one used by him and ladyada (9).
Yes, I will edit library file so I can give this a try.
Yes! This is the way I originally had it. @ladyada added in the invert argument but later agreed that it was a little too much.
I know you weren't asking me about my opinion but I thought I'd weigh in. Only reason to keep the name TIMER_PWM_PIN is it lets you keep the description "semantically drifted". I love that description.
I'm embarrassed to admit but the main reason was to get something working for a project I was building without having to dig through more processor specs about it timers and output multiplexers. Added benefit was it didn't tie up a second timer and it allowed complete output pin flexibility. I'm now wondering if the same timer used during receive couldn't be switched during send to run a PWM (and switched back when receive is re-enabled). Also whether with the M0's output multiplexers whether broad output pin flexibility is free even with HW PWM. I'm thinking about digging into this but results could be months away. Regarding the use of the PULSE_CORRECTIONs terms : According to my software base measurement, corrections should not be necessary, but @bengtmartensson actual measurements suggest they are needed. I have to resolve this in my mind. So I have managed to round up a fairly modern, fairly accurate oscilloscope (as a supplement for my 1970's era Heathkit) and I'm going to be investigating pulse width and frequency accuracies with and without corrections. Hopefully results to follow in a day or so. |
@bengtmartensson I did not realize the default for the soft carrier was to use spin wait. I did not realize this until I saw your last commit. I figured that might have been contributing to me getting inconsistent results (in addition to the cheap receiver devices). So I tested your latest with my program that sends 50 NEC values in a row. What I found was that anywhere from 1 to 4 times out of the 50 the program would get hung up in the send routine. I could wait it out but each time it took right around 90 seconds before it would continue on. I instrumented enough to watch the value of micros() when the hangup occurred and micros was nowhere near wrapping around its 32 bit value. (as one would expect since the program only runs a few minutes, nowhere near 4,000 seconds.) I've studied your mark code in some detail and can't see anything might cause this. I thought that maybe there was something in you library that cause the receive interrupt to continue to run during send but could not find anything wrong with that either. Have you seen anything like this? Anyone else? |
Just a few quick comments:
|
Works as expected when USE_SPIN_WAIT is defined. Hangs up randomly when USE_SPIN_WAIT is commented out. Please note, when I say works as expected I mean sending program progresses normally. But there are still a high level of receive errors. TSOP4838 should be here tomorrow.
No, but it'd be nice if we could. |
@MrBryonMiller :
Please also remove the irRecv instance. To be on the safe side, no not instantiate Serial at all. Just as @AnalysIR said that he will not accept results from a cheapie IR receiver, I will not accept results with intermingled console output.
Which at least shows that it can be valuable to have it, at least as a troubleshooting option. |
Following still gets hung up every 3-10 times through loop.
|
@MrBryonMiller Again, it runs fine for me using, I think, the same processor. I received a M0 Feather, but I have not have time to test it. After all, this is about the PR, not sorting out your problems... How do we proceed with the PR? |
I make sure I did not contaminate your library while I was making comment changes to SPIN_WAIT I downloaded your latest commit and reran my tests.
Perhaps - but if I change the IR library to @ladyada's library there is no anomalous behavior.
It is impossible to tell with the version of the sketch you asked me to build that has no Serial.prints other than occasionally the receiver shows nothing being received for 90 seconds or so. For the version of the program that has prints, for those iteration where a hangup occurs, I can see a print is made just before calling sendNEC and about 90 seconds after the return from sendNEC. The prints after the call shows a little more than 90 million microseconds has elapsed. Please remember this 90 second problem does not occur on every send. At least 40 out of 50 seem to work fine (although many with receive decode errors).
I also think we are using the same processor - but if you install the Feather M0 following AdaFruit's instructions you do get some different core libraries. I think I looked at the implementations of micros() and delayMicroseconds() for the two cores and I could not tell a difference, but what do I know.
I am going to rerun my test using using a TSOP34438 on my receiver end and using SPIN_WAIT from the @bengtmartensson library to see if the reception errors clear up (I should have results shortly). Depending on the results of that I would ask deferring a decision until someone can confirm the library does not conflict with something on a Feather M0. If an immediate decision does need to be made and if you wanted SAM support and you felt that support should include a Feather M0 and if it were up to me I would have to recommend @ladyada's PR over than this one. I think that would be a shame since we should all recognize that @bengtmartensson did an awful lot of cleanup and generalization work. |
I have retested with TSOP4838's. I used @bengtmartensson's library with one edit - I defined USE_SPIN_WAIT. 50 out of 50 were received correctly. Thank you @AnalysIR!
I would change my recommendation - If an immediate decision does need to be made and if you wanted SAM support and you felt that support should include a Feather M0 and if it were up to me I would have to recommend either this PR with change of using SPIN_WAIT being the default or @ladyada's PR. I would be willing to reconsider the change of my USE_SPIN_WAIT recommendation if someone can confirm that the Feather M0 does not get hung up. |
@bengtmartensson I fixed this by changing your test in sleepMicros from But what I really wish you would consider is replacing your entire loop, including the USE_SPIN_WAIT distinction, with the code I suggested earlier. I have re-included it here with some added comments and updating the 'now' variable name and using SENDPIN ON and OFF to be consistent with your work.
I know it has open loop timing considerations, but I feel they are small. And it has the advantage of terminating the Mark when its time is up, even if its in the middle of HIGH time - just like a HW PWM implementation would. |
for targetTime large.
EXCELLENT!! Explains what happens without any guesswork, which is very satisfying, also theoretically. (Just slightly embarrassing for me...:-( ). I selected another fix though.
Actually, I consider my code better. It encapsulates the waits better, allowing for a clean selection between spin wait and normal wait. Also,
is outright wrong. |
Yes - mystery is still where did 90 or so seconds come from. Looking inside of delayMicroseconds() I
I respectfully disagree - but I give up.
I might not have done this in the "right" way but I needed to perform signed math so wrap-arounds worked out correctly. So I needed now to be a long. But again, I give up. Bottom Line : I looked at @bengtmartensson latest commit - I have tested send and receive with a Feather M0. I have previously done some level of regression testing with other machines. I believe this commit would make a fine addition to this great Library. |
Just a final remark:
AFAIK, what happens when a signed variable in C (C++) "wraps around" is undefined. There might even be hardware "traps" fired when wrapping around. C is not Java, which is guaranteed to use 2-complement. There might be compiler flags that, for a certain processor, forces a particular behavior. But, if the aim is to write portable code (that runs on all processors) -- which should be our aim -- stay out of undefined behavior. |
Any progress on reviewing/merging this? |
@bengtmartensson Sorry I am currently busy with things in life and will not have a chance to merge or review any times soon. I would suggest we go ahead and create a beta release with the changes in this merge and add that to the releases. Thoughts? |
Almost 3 years has passed, and no sign of a merge... I have implemented all the features: SAM support send and receive, soft carrier PWM, and much more, in the just released version 1.1.0 of my library Infrared4Arduino. (Available in the Arduino library manager using the name Infrared, in a few hours). |
This PR attempts to clean up the low-level hardware dependencies. This problem was made glaring by #425 and its implementation by @marcmerlin. Later, the changes for SAM boards (see #390), first by @MrBryonMiller, later fixed by @ladyada, https://github.com/adafruit/Arduino-IRremote. These changes have been "cleaned" in the same sense.
All explicit hardware dependencies are now either in
boarddefs.h
, or in a board-specific file. Conditional compilation in the other files are now exclusively governed by "high-level" properties. Soft carrier (which sometimes is called "bit-banging") have been implemented hardware-independent.