-
Notifications
You must be signed in to change notification settings - Fork 0
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
ESP32 RMT odd behaviour when looping #11
Comments
Interesting! I agree that the result is unexpected. I have hardly used looping personally so I'm afraid I don't have a lot of first-hand experience to draw from. However, googling around suggests that there may be an issue when the stream of pulses is quite short - an interrupt needs to be generated at the end of each sequence that will re-start it. If I've carried my zero's correctly that's an interrupt every 30ms for your sequence:
Which doesn't sound too frequent at all... However, it would be interesting to see what happens if you extend your sequence:
I'm afraid I don't have my logic analyser handy this weekend otherwise I'd try it myself! Please let me know how you go. |
Hmmm… have just tried the I've verified that it is only as it loops that the first pulse is repeated, so the very first sequence of pulses that are generated when the RMT is started are correct. Also, if I write a sequence of pulses with looping disabled, the sequence is correct; then if I enable looping without writing anything new, the sequence restarts correctly but has a doubled pulse again each time it loops around to the start. I'm now thinking that I may need to just work around this for the moment using the new |
Right. Through incredibly laborious testing I have determined that 20µs after the RMT has started running through the pulse sequence it will restart from the beginning and then run through correctly to the end. It will thus repeat however many pulses entirely or partly fit within those 20µs. For the relatively slow examples I was using above, this is just the first pulse that is repeated. When I crank up to the actual speed of the protocol I am attempting to output, which has 6.67µs period pulses, I get three being repeated. It will always repeat an exact number of As far as I can see, you're just using the default ISR that is installed by |
That's right; I'm handing as much as possible off to the ESP-IDF. So I'm very surprised this isn't working properly; it's hard to believe there are not more people running into this (who are using the raw C API). Maybe few people are using looping? I don't think it's anything we're doing in the library or application code anyway! |
Perhaps we need to raise this with Espressif, or ask some of them to take a look at this issue. @jonathanhogg are you able to write a repro' in C on ESP-IDF ? Do you have the setup/means for that? Then we can post that here and call in some Espressif engineers to help debug the matter. |
So I've managed to repro it with the 3.3 IDF (tag 143d26a) and this stripped-back piece of C: #include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/rmt.h"
static const char *TAG = "test";
rmt_item32_t items[] = {
{{{ 250, 1, 750, 0 }}},
{{{ 500, 1, 500, 0 }}},
{{{ 750, 1, 250, 0 }}},
// RMT end marker
{{{ 0, 1, 0, 0 }}}
};
void app_main(void *ignore)
{
rmt_config_t config;
config.rmt_mode = RMT_MODE_TX;
config.channel = RMT_CHANNEL_0;
config.gpio_num = 12;
config.mem_block_num = 1;
config.tx_config.carrier_en = 0;
config.tx_config.loop_en = 1;
config.tx_config.idle_output_en = 1;
config.tx_config.idle_level = 0;
config.clk_div = 80;
ESP_LOGI(TAG, "rmt_config");
ESP_ERROR_CHECK(rmt_config(&config));
ESP_LOGI(TAG, "rmt_driver_install");
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
int number_of_items = sizeof(items) / sizeof(rmt_item32_t);
ESP_LOGI(TAG, "rmt_write_items");
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, items, number_of_items, false));
while (1) {
ESP_LOGI(TAG, "Waiting");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
} As for the Python example, the trace looks like: It'd be good if someone else can confirm the same behaviour to be sure I've not got a bad board or something (it's an AdaFruit HUZZAH32). |
I've been doing some reading of the ESP-IDF RMT driver code and the ESP32 technical manual and, as far as I can make out, the looping is a feature of the hardware itself and is not actually handled by the interrupt handler. I can't find any errata or complaints about looping, so I'm now seriously wondering if something is wrong with this board. I'm going to dig out another when I'm back in the studio in a day or two and see if I can reproduce. Also of note is that the ESP-IDF RMT driver will explicitly disable looping if you try to send more than 64 items as it can only do this by filling the channel buffer with the first 64 items from the sequence and then setting an interrupt to quickly swap in the next 64 items just before the end of transmission. Note that So I wonder if |
While I'm pleased to see that it's not just me, I'm somewhat thrown that looping can be this broken and it not have come up before somewhere. Perhaps it would be good to get some input from an Espressif engineer as to whether we're just doing something fundamentally wrong. @nickzoic, I think I was keeping |
Yeah now it's confirmed it ain't just one flakey chip ... I'll try and get a bit more methodical tomorrow and we can file it as a bug on the esp-idf tracker.
Thanks for finding it!
[oh, also, I don't think I mentioned it but I've configured the ESP32 at 240MHz]
|
Bonus wild observation: the little transient spike you can see at the start of those traces is caused by the { 0, 1, 0, 0 } terminator: change it to { 0, 0, 0, 0 } and it goes away! |
|
This is on a Sparkfun ESP32 Thing with Rev0 silicon. |
As a bonus, my results tabulated above don't agree with the previous screenshots. Anyone got any theories? I'm all out of time & brains for today. |
I'm lost as to why looping is so broken honestly. |
Yep. If you don't need an actual continuous loop it's easy enough to work around: put a 20µs or so blank as your first RMT item. Hopefully it's fixable in firmware though. |
Yeah, that's my current workaround. Luckily I'm generating a group of pulses every 1ms, so I just moved some of the space after the group to before instead. |
OK I've got and added this to Espressif's issues tracker |
I've also reached out by email to Espressif - they agree that it looks like an issue on their side and will take a look. Chinese New Year may slow down the response however! Thanks to both of you @jonathanhogg and @nickzoic for digging in to this in such detail. I'll keep pestering Espressif and will update you guys here if I hear anything. |
As per my comment on the Espressif issue, the problem goes away for me if I disable the TX-complete interrupt when looping is enabled. I have simply patched the STATIC mp_obj_t esp32_rmt_loop(mp_obj_t self_in, mp_obj_t loop) {
esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in);
int loop_en = mp_obj_get_int(loop);
check_esp_err(rmt_set_tx_intr_en(self->channel_id, !loop_en));
check_esp_err(rmt_set_tx_loop_mode(self->channel_id, loop_en));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp32_rmt_loop_obj, esp32_rmt_loop); And then my Python usage is to do this every time I need to change the looping output signal: rmt.loop(False)
rmt.wait_done()
rmt.write_pulses(pulses, data=data)
rmt.loop(True) |
Yay! A good result with many thanks to @koobest ! |
espressif/esp-idf#4664 just got closed, so we should check if this fixes this issue ... |
FWIW I still have this problem as of today. I am using a Heltec WiFi kit 32, Linux 19.3, VS Code and PlatformIO plugin. I am using RMT to generate a PPM signal on a pin to drive a standard RF transmitter module (FrSky in my case) for radio control of models (planes, boats, whatever). Fortunately I was able to use the workaround as suggested by @jonathanhogg above, as the low pulse is always 300us. So I was able to add a dummy first pulse of 40us (to be sure, to be sure), and make the low pulse of the final "sync" pulse 260 instead of 300. |
Yes, this branch is a bit out of date now. Looking at mainline I see that there's been no major updates on that either since initial inclusion. @mattytrentini / @dpgeorge: what's the deal at the moment with the RMT updates? Is this branch going to be updated and pulled in any time soon? If nothing else, it might be good to at least get the looping interrupt fix from above in since that's a clear bug in the current functionality. Would it be useful if I did my own micropython/micropython branch and pull request just for that? |
The IDF fix for this is not part of a release yet, so would be good to work around it as suggested. But also note that, in the git comment of that IDF fix, it says that "users needs to call
Yes! |
I tried reversing the order of the rmt_driver_install and rmt_config in my program and it solved the problem! Works a treat now. |
Ok, great! Let's get that in mainline then. |
OK. I’ll try to get some new tests done this week and put in a PR. I’m a little lost as to what exactly is implied by the “fix” to espressif/esp-idf#4664. The code referenced contains a mass of refactoring which, I assume from the commit description, is on top of a refactoring that was done some time in the past (not hunted that down). If it is apparently fixing the need to call I’m not sure I’ll be able to make enough time to test the necessary permutations! |
At this stage it's enough to just switch the order of |
@dpgeorge: I've created a new issue for this at micropython#6167 – it felt weird to have the issue not documented somewhere on the main project. PR in against that. |
@dpgeorge / @mattytrentini: I meant to say, there's a bunch of other very useful new RMT functionality that is languishing in this branch. In particular, the If it would be useful, I am happy to pick up some or all of this work and submit a PR against main. |
It sounds like a good idea to me, but I'll let @mattytrentini make the final decision |
@jonathanhogg a bit off topic maybe but I was wondering if you have tried changing the values written to the output by this function. I have been trying to do it but it seems once the write to the output has been sent (either rmt_write_items or rmt_write_sample) the function blocks regardless of whether looping is enabled or not, or anything else. I have tried every possible combination and the write function always blocks, effectively stopping the program in its tracks. I've tried everything I can think of, but I must be missing something. I just can't get it to work. Any hints? Thanks, Ian |
I'm trying to find time to massage the Of course, feel free to take my branch and complete it if you are so motivated - but this is back on my radar. |
@jonathanhogg @mattytrentini , done some more playing with this and I think I've found the answer to my problem. |
Hi @mogplus88, sorry for delay getting back to you. I've just had a go at hooking my kit back up and doing a couple of tests and – indeed – something is up. The Sorry, this was clearly insufficient testing on my part: I only checked that my simple looping example was working before declaring this a win. I'm going to do some more digging, but I'm gonna report back over on micropython#6167 so I can keep what I find associated with the main project and my last PR. |
Hey @mattytrentini, Just to let you know that I'm working on a copy of your branch over at jonathanhogg/micropython. I've rebased it (with my usual fat-fingered mistakes) to a recent HEAD, worked on the things you'd marked as "fixme", added support for disabling idle output (with I've got to do some proper testing tomorrow (UK time) with the oscilloscope to make sure I've not bust anything, but will be putting in a PR soon. |
Closing this issue since the original problem has been fixed. The additional RMT features are still languishing in the PR, but nothing useful on that can be said here. |
Hi @mattytrentini,
So I'm working with your
esp32_rmt2
branch (merged against currentmicropython/master
HEAD) and have found that the behaviour whenloop(True)
is called is strange. I'm seeing duplicated pulses in the output signal.Small example:
(Not entirely clear to me whether it matters if
loop()
is called before or after firstwrite_pulses()
, but it doesn't seem to make a difference either way.)With looping off, I see three pulses each of increasing duty cycle (Figure 1 below, captured with a USB logic-analyser). However, with looping on, I see a repeated pattern of four pulses with the first duplicated (Figure 2 below).
In my actual code I've seen up to three pulses from a longer sequence being duplicated.
I've stared at the code in
esp32_rmt.c
and the ESP-IDF RMT documentation and I can't see any reason for this behaviour. I'm hoping that you might have more experience in using this hardware, though, and may have an idea what's going wrong.Cheers,
Jonathan
The text was updated successfully, but these errors were encountered: