PWM on TCA0 and TCA1 does not work (name edited)
microPaul opened this issue · 18 comments
I don't know if I have a programmer's "pilot error" problem or if PWM on the DX Core is not working correctly.
I'm using a home made AVR64DB64 board. My test code is below. My fundamental problem is that I can only get a PWM signal to appear on A4 and A5 pins. I can't get a PWM output on PA3 or PA2 (I have an external crystal on PA0 and PA1 so I certainly wouldn't expect PWM to work on those two pins). Additionally, I can't get PWM output on PB4, PC4, PD4, PE4, PF4 or PG4 (I only tried the x4 pin to save time). I have run a port pin toggle test on each of those pins to ensure that my PCB wiring is correct (all those ports toggle as expected).
From the DxCore documentation I was left with the impression that up to 6 PWM signals could be generated concurrently. Is that an incorrect understanding? I understand that the 6 PWM outputs all have to be on the same port (A, B, C etc) but I thought that up to 6 WPM signals could be generated at the same time.
Any suggestions would be appreciated.
#define TESTPIN PIN_PA3
//#define PWM 1 // commment out to run port toggle test instead of PWM
void setup(void) {
#if defined(PWM)
analogWrite(TESTPIN, 32); // 12.5%
#else
pinMode(TESTPIN, OUTPUT); // init TESTPIN as output for toggle test
#endif
}
///////////////////////////////////////////////////////////
// loop
///////////////////////////////////////////////////////////
void loop(void) {
while (1) {
#if defined(PWM)
// do nothing in loop if testing PWM
#else
// if port toggle is the test, then toggle every 10.00 ms
uint32_t timeRef;
if (micros() - timeRef > 10000) { // wait for 10.00ms timeout
timeRef = micros(); // reinit the time reference
digitalWrite(TESTPIN, !digitalRead(TESTPIN)); // toggle the output pin
}
#endif
}
}
(Uh, you did know you can do digitalWrite(pin,CHANGE) there right?) (as does digitalWriteFast(pin, CHANGE)
But this does not reproduce for me. Something is not going correctly, on a DA (don't have a DB handy)
Thank you Spence. And I did not know about the CHANGE option, so thanks for pointing that out to me.
Just for another data point I pulled out a AVR128DA48 board that I bought from your Tindie store a few years ago and reran my test code. I can get the 10ms toggle on A2, A3, A4 and A5 (those were the only pins I tested) but no PWM on A2 or A3. I did get PWM on A4 and A5. So that's pretty much the same results I got on my DB64 board. In case it's useful I'm using Arduino IDE 2.1.1 (not sure I should have upgraded) and DxCore 1.5.8.
Thanks for looking into this. I plan to use PWM for a future project, but I probably won't get to that for about a month, so there 's no urgency on my end to get this resolved.
Huh. That is very strange.
I can't fix this problem if I have no idea how to reproduce it. You have run the exact same code on the exact same chip soldered to the exact same board, and yet you have different results. The only possibility is something changed in the github version (I always run the github versions since that's what I'm developing) or you misread the version number and are running 1.5.6.
1.5.0-1.5.6 doesn't have working PWM. I didn't know this until someone reported it in 1.5.6, and then I spent over a week of agony and several pages of paper covered om scrawl. desperately trying to find an efficient way to convert between the port of a TCA1 mapping and the port. I do not belive any algorithm is possible that is any faster or more flash efficient than using a lookup table. And god did I try.... And the whole thing was made much more confusing, because a DD and DB with the same number of pins, and the same values in the PORTMUX registers (let's put TCD on PORTA since that's where it is on DA/B, where the mux options other than that are broken) - in this case, at one point, I had gotten the DD working! Huzzah! Confirmed on a DD32 . Okay let's just double check that the DA or DB work, and we can close this one.... .... Are you fucking kidding me!? What happened to the PWM from TCA, TCD?! And then I notice the pins aren't set output. How did analog write not turn th em output? Simple misnesting BS. How did it work on the DD? According to my man on the inside, there was considerable debate on this aspect of behavior internally, and the "Timers deserve a port direction override!" camp won, and timers now will generally have port direction and value overrides. Presumably this change followed the infiltrating an Atmelist cell within the company and dismantling it.
Note: I'm with the Atmelists, timers do not deserve direction override! )
Note that the TCBs have always had direction override. Naturally they deserve it least being such crap PWM timers.... One key advantage ton not having the direction override is that you can have the timer in PWM mode while not generating PWM (you can still use the first 3 channels as level inputs to the CCL)
Note that in ALL of those cases the datasheet says that they won't output pwm unless the pins are set output.
That was far from the only PWM problem in 1.5.0-5
Yes, this is very weird. And I totally understand that you can't fix things if your system doesn't exhibit the problem that I'm experiencing.
I've done some additional testing, but it provides me with no insights.
Thinking that I had some bad load of something on my main computer, I uninstalled both Arduino IDEs as well as Microchip MPE Lab X and the Microchip x32 and x8 compilers - everything AVR related I could think of. I also deleted all the stuff in /user/AppData for Arduino, Arduino15 and Microchip - everything AVR related that I could find. Restarted the PC. Downloaded Arduino IDE 1.8.19. Added your JSON to the IDE preferences. Use the board manager to load DxCore 1.5.8. Closed and restarted the IDE. Opened my test program, compiled, uploaded, ran it. Got the same results as before: PWM works on A4 and A5 but does not work on A3.
So I figure there's still some residual file that's being incorporated in the compile that's messing this up. I then turn to my new Dell Laptop. Two weeks old. Only has Firefox and Zoom loaded onto it (so far). I then go through the process above to get IDE 1.8.19 and DxCore 1.5.8 installed on it. Opened my test program, compiled, uploaded, ran it. Got the same results as before: PWM works on A4 and A5 but does not work on A3. Bummer!
I realize this isn't any help to you since your system doesn't exhibit the problem, but if there are any other tests you might recommend that I try, I would be happy to do so. It's not a big deal if I can't resolve this. If I can't then I'll probably just do a takeOverTCA0()
and do my own direct TCA0 register writes and hope that can be an effective work-around.
Another thought. Am I not getting the latest version of the DxCore if I'm letting the Arduino IDE Board Manager (via your JSON) do the updating as opposed to some sort of manual update directly from your GitHub repository?
board manager has 1.5.8, github has 1.5.9-dev
It is getting close to the release but the blocking issue is a doozy.
Ok, thanks for that info. I'm in no rush so I think I will wait for board manager to get 1.5.9 and see if that solves my problem.
Sorry about the doozy.
And then that was derailed by a messy moveout followed by a website outage that I still have not sorted out completely
1.5.10 is now available.
Please confirm whether this version fixes the issue, I belive it should work,
Unfortunately, I'm still getting poor results on PWM with DxCore 1.5.10, installed via board manager.
I do get good PWM output on PA4 and PA5, but not on (and these are the only other pins I tested for PWM) PA2, PA3, PC5, PD5.
I'm using the program below on your Tindie AVR128DA48 board (I also tried on my AVR64DB64 board, same results). To test for PWM on the pins in question I first comment out line 2 ("#define PWM 1") and run the program to confirm that my scope probe is on the correct pin and that the pin toggles. I then uncomment line 2 (so that the define PWM 1 becomes effective), recompile and download, and the see if there is a PWM signal on the pin in question, using the scope as the indicator.
#define TESTPIN PIN_PA2
#define PWM 1 // comment out to run port toggle test instead of PWM
void setup(void) {
#if defined(PWM)
analogWrite(TESTPIN, 32); // 12.5%
#else
pinMode(TESTPIN, OUTPUT); // init TESTPIN as output for toggle test
#endif
}
///////////////////////////////////////////////////////////
// loop
///////////////////////////////////////////////////////////
void loop(void) {
while (1) {
#if defined(PWM)
// do nothing in loop if testing PWM
#else
// if port toggle is the test, then toggle every 1.00 ms
uint32_t timeRef;
if (micros() - timeRef > 1000) { // wait for 1.00ms timeout
timeRef = micros(); // reinit the time reference
digitalWrite(TESTPIN,CHANGE); // toggle the output pin
}
#endif
}
}
Is there someone else who could try my program (or a similar test program) to confirm that the problem is real, and not just a problem under my roof. I know that you (Spence) are not seeing the problem on the same board that I'm using, which makes it impossible for you to troubleshoot. I did also previously test on a relatively new laptop and I get the same results as described above that I get on my main development pc.
Good Morning!
Just a quick test on a bare metal AVR64DD32@16MHz (no PC5):
PA2+3 OK:
https://github.com/pcfreak1201/DxCore/blob/master/PIC_2.png
https://github.com/pcfreak1201/DxCore/blob/master/PIC_3.png
PD5 no PWM - but I didn't check the datasheet...
https://github.com/pcfreak1201/DxCore/blob/master/PIC_4.png
https://github.com/pcfreak1201/DxCore/blob/master/PIC_5.png
Thanks for the testing! So you are getting PWM on PA2 and PA3 (and I am not). I have a homebrew nano work-a-like board using an AVR64DD32 so I will retest PWM on that, to see if I can get PWM on PA2 and PA3.
I'm assuming from Spence's GitHub write up on PWM using TCA0 in split mode, that once you use AnalogWrite on a particular port (PA, PC, PD, PF [ assuming 64DD32 ]) then you can have up to five more PWMs on the same port (Px0-Px5). Basically PWM on the pins specified in the data sheet (section 17.3.7 for the DB parts and 17.3.6 for the DD parts).
Of the 6 pins you tried, only 3 are pwm by default - they can all be used for PWM, but not all at the same time, and only after pointing the TCA to that port, Of the remainder 2 worked as expected.
PA2, PA3, and PD5 are not expected to provide PWM by default.
PA2 and PA3 can be served by TCB0 and TCB1, those are pointed at the alternate pins, the less useful PF4 and PF5 (we do not provide a means short of a custom variant to change TCB PORTMUXing They can also be served by TCA0, but that is pointed at PC0~5 by the core at startup.
PA4,5 are being served by TCD0 (which can provide PWM to PA4 or PA6 and PA5 or PA7)
PC5 is expected work, being served by TCA0 in the core's default configuration.
PD5 can be served by TCA0 but TCA0 is not pointed at it in the core's default configuration.
The DD32 and the DA48 are not the same part and they do not have the same pin mappings (actually, there is a pinmapping shift between DA32 and DD32, and between DA32 and DA48!). The double D's are a totally different ball game from the DA/DB parts - They have fewer peripherals, so some pins that were very useful are less useful, making them more appealing as PWM pins, the TCD PORTMUX works (and it is readily movable and the core knows to check where it's pointed in analogWrite and turnOffPWM) (Oh, and another thing about PWM starting with the double D's: PWM now has port output level AND direction override - if you enable the compare output, the pin is forced output. This doesn't matter... except if you ignore the advice from the timers reference and move a TCA to a different set of pins (TCD too) while it is already outputting PWM. On DA/DB, the analogous pins in the new mapping will not start outputting PWM unless they're set output. On the DD and going forward, the timer will override the direction and PWM will start coming out of the pins on the port you moved it to, even if they're not set output. I can come up with arguments for either side here, neither seems more compelling, so I would have deferred to tradition and not given timers direction override. Note that the shitty type B timers have always had port direction override.
Note also that according to the datasheets for all of these devices, none of the timers are supposed to have direction override. My source on the inside indicated that the change in the DD was intended and the result of an internal debate, and that was - apparently - the winning side.
I do rather wish they'd had these debates before they started shipping Dx's though -neither option is markedly better, but
https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/DA48.md
https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/DD32.md
https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/DA32.md
Referring to the DA48 page linked above, without changing the TCA mapping, PWM pins should be PA[4+5, 4+7, 5+6 or 6+7], PB0-5, PC0-5, PF4, PF5. If the TCA0 is moved to another port, (which is easy, see that document for code examples), there's 1 free PWM pin on TCB3 hidden under TCA0 on PC1. There's also one on TCB2 on PC0 - but TCB2 is used as the millis timer by default (TCBs are lousy PWM timers. They are meant to be utility timers, with rudimentary PWM tacked on. So it was considered acceptable to trade one TCB PWM output for using the same system implemented on mTC (where the TCB is not used for PWM) for TCB millis, which configures the overflow to happen once per millisecond, thus making the ISR trivial, fast, and small, especially since the core's default TCA mapping covered up two of them, (PORTC was picked because: We don't want to use PORTA - porta, particularly it's first half, is packed with peripherals, and it';s other half has the TCD on it (which can't be moved on DA/DB currently due to a silicon bug), PORTB already has a TCA on it (and one which has no other mapping options that give 6 pins, just a 3 pin one (on portc) - on the DB-series 64-pin parts, mux options 2 and 3 work correctly for TCA1, and it may or may not have been fixed in time for the 64DA64's). PORTD is kinda the main analog port (and this turns out to have been the right move to make, as that's where the DB put the OPAMPS), PORTE only has 4 pins. PORTF has the two TCB refugees from PA2/PA3, and PG only exists on 64-pin parts. And since we wanted the same portmux options to be used for 48 and 64 pins, there you go. If my crystal ball were working, I'd have made different decision on the 64-pin parts - but that would have required me to for see that like a year later I'd implement PORTMUX switching logic that would make the TCA ports chosen much less consequential (since they are trivial to change), and that three years later we;d have seen only a single modern AVR get a significant die rev in the interim, so I was still thinking we'd have seen a working TCD mux by now *
, and to have correctly predicted which port the MVIO would be on the DB - I knew it would have MVIO and basically what MVIO would do, but not which port it would be, and my prediction was incorrect.
I do maintain that my decision on the TCBs was the right one. since I haven't implemented TCB portmux switching and really have no desire to, the TCBs really should be on the less-otherwise-useful of the two pins they can be on. PA2 and 3 are a TWI pair, - ironically, they get another non-MVIO option for those pins at the same pincount that they get
Really though, it's impossible to come up with a one-size-fits-all set of portmux options, which is why we let you change almost all of them (TCB being a notable exception, and it is that specifically because it's a bad PWM timer; the benefit is small (you normally try to avoid the TCBs PWM and use the TCAs or TCD for PWM; if you use any timers for anything other than PWM, it will usually be a TCB (tone and servo. plus a lot of other stuff), leaving fewer available for PWM, though the overhead for handling portmux for pwm output on those not-used-for-pwm timers would not go away - and that overhead gets added not just to analogWrite() but to digitalWrite() as well,.
*
At this point I'm concerned; at this rate the time they get around to a die rev, the sun may have exhausted it's fuel and inflated into a red giant, incinerating the earth in the process - that would make semiconductor manufacture on earth untenable. So if we haven't gotten working silicon by then, we can just forget about ever getting any... though I guess at that point, we'd have other concerns, like the impending overflow in year 2^32 - or the unwelcome career change on account of the sun, from an embedded systems engineer to a combustion engineer. "Combustion engineer?" "Er, sorry, that's "combusting engineer" "I'm not sure I'd call that a career..." "Of course it is! In fact, I'll bet we'd be doing that for the rest of our lives!"
I have confirmed that all TCA PWM is currently broken on DxCore. It is failing to set CTRLB to enable the compare channels.
Thank you Spence for your info about the PWM issue. My apologies for not responding sooner to your posting on this issue a couple of weeks ago. I've been swamped with other things including an insidious I2C issue that I'm very sure has nothing to do with either the DxCore or the associated I2C library. I think it's a problem with the LCD display and its library. Anyway, that's my problem to solve on that issue.
I wish you a painless straight forward resolution of the current PWM issue. Thanks again for creating the DxCore. It's an AVR life-changer.
Nope, it's not gonna be painless or straightforward. It's looking like as far from that as imaginable, This is a problem related to the most difficult piece of code I have written for the core in C, and just as hard to debug as asm is, because I can only smuggle 4 values out of analogWrite. But all those values prove that analogWrite works, but a key register is somehow magically changing itself back to 0 and I am baffled.
I already wasted an hour debugging serial last night, eventually tracking the problem to... two backwards wires in the cable to the adapter (TX and RX were reversed). So then I could finally get debug info out. Then I see that CTRLB wasn't getting set. But in situ investigations demonstrate that this is not the case - after I set it, I see the right value there, but something is clearing it before the first pulse starts somehow, and I cannot see how this is happening.
This has been corrected., The actual problem turned out to have been left over code from development, investigating a difference between the AVR DA and DB
On the DD, the TCA and TCD have port direction override. On the DA/DB they do NOT. Apparently the intent was for all of them to have direction override, but nobody told the docs group because the datasheets are still describing the wrong behavior (which is the realworld behavior on the DA/DB, but is not on the DD) [I initially assumed "sure, sure, they're retroactively changing their intent" but the next time I read the applicable chapters, i noticed that both TCA and TCD have a register to control the pins when the timer is stopped.... in retrospect, what's the point?! - Unless the timer was expected to forcibly hold onto the pins!