-
Notifications
You must be signed in to change notification settings - Fork 22
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
Asynchronous DmxInput #8
Conversation
DmxInput is now running in the background, using a DMA channel and the DMA_IRQ0 interrupt. This approach gives the following advantages: - Supports both blocking and non-blocking use - CPU knows when a packet was received - Supports any number of channels (not just powers of 2) - Requires no memory alignment of the buffer An alternative method using the DMA ring feature was also considered. This method would have meant almost no cpu load with just one interrupt every 2**32 received channels. However, this would require the number of channels to be a power of two, the buffer would need to be naturally aligned (256 channels would need a 256-byte aligned buffer), and there would be no way to tell if dmx signals have stopped coming in.
Hi! This looks really cool! Nice work :) Unfortunately, I'm on vacation with all my Arduino stuff left at home. I'll take a look at the code and run some tests sometime next week. I'll look over the code and perhaps leave some comments in the meantime. Could you take a look at the Arduino Linter checks? It seems to be complaining about the naming of the file RGB_input_async.ino |
src/DmxInput.cpp
Outdated
@@ -10,6 +10,13 @@ | |||
#include <irq.h> | |||
#include <Arduino.h> // REMOVE ME | |||
|
|||
int DmxInput::_buffer_size() { | |||
int num_channels = _end_channel+1; | |||
return num_channels+((4-num_channels%4)%4); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the purpose of this to 32-align the buffer in memory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No.
This code rounds the buffer size up to the next whole integer. This is needed because the pio is configured to push 32 bits (four channels) at once. This makes the DMA transfers more efficient.
This approach has one downside: We may not be able to receive the last few (up to 3) DMX channels. However, i believe most DMX signals will contain a multiple of four channels so in most cases we can receive everything.
src/DmxInput.cpp
Outdated
|
||
//initialize with some starting values | ||
for(int i=0;i<_buffer_size();i++) { | ||
((volatile uint8_t*)_buf)[i] = i; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to use memset
here instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not with the increasing i
, right?
I added this code mostly for debugging. For production there isn't actually a reason to initialize the memory at all.
src/DmxInput.cpp
Outdated
@@ -116,4 +167,6 @@ void DmxInput::end() | |||
|
|||
// Unclaim the sm | |||
pio_sm_unclaim(_pio, _sm); | |||
|
|||
free((void*)_buf); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the call to free
necessary when the buffer isn't heap allocated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, good catch! I was experimenting with a malloc`d buffer and forgot to remove this. I will remove it.
I have 2 general questions about the architecture of the code:
|
Shouldn't be much hassle. The interrupt handler would need to know all the instances and which one caused the interrupt. Currently i just have a global variable, hence only a single instance.
A separate class would contain lots of duplicated code. I think the breaking API change can be removed without a separate class. It would make the async read a but more cumbersome to use, but that tradeoff might be worth it. |
Nice work! You're an absolute legend! I'll run some tests next week and merge the pull request and create a release if everything is in order :) |
LGTM 🚀 |
Add async DmxInput capability.
DmxInput is now running in the background,
using a DMA channel and the DMA_IRQ0 interrupt.
This approach gives the following advantages:
Current downsides:
Only one instance of DmxInput is supported. Could be changed if required.Breaking change of the existing API. This is mostly due to the pre-allocated buffer. Could be changed at the expense of an extra "start" function call for the async mode.Alternative implementations
An alternative method using the DMA ring feature was also
considered. This method would have meant almost no cpu load
with just one interrupt every 2**32 received channels.
However, this would require the number of channels
to be a power of two, the buffer would need to be naturally aligned
(256 channels would need a 256-byte aligned buffer), and there would be
no way to tell if dmx signals have stopped coming in.