Go Down

Topic: DMX512, interrupts and stepper motor (Read 172 times) previous topic - next topic

deeplowdock

Jul 17, 2019, 04:25 pm Last Edit: Jul 21, 2019, 12:34 am by deeplowdock
My end goal:
I'm creating a DMX512-controlled light fixture which consists of 1 stepper motor and 4 powerful LEDs. I'm using Arduino Mega, DMX shield (with Conceptinetics library), PCA9685, TMC2130 (controlled using the combination of TMCStepper and AccelStepper) and some other stuff that is probably irrelevant.

What's happening in the code:
Each time a DMX frame is received, a callback to a function is produced. In this function, values of 5 sequential channels are extracted. If these values differ from the last remembered ones, then the goal brightness values and the stepper speed are recalculated. Note that the brightness values are remapped from 0-255 to 0-4095, since the PCA9685 has 12-bit registers.

Then, there's the main loop.

Main loop (previous iteration):
Code: [Select]
void loop() {
  stepper.run(); // has to be called as often as possible so it won't miss steps
  
  /* LED #1 */
  if (led1CurrentValue != led1GoalValue) {
    led1CurrentValue += (led1GoalValue > led1CurrentValue) ? 1 : -1;
    pwmDriver.setPWM(PWM_LED1_PIN, 0, led1CurrentValue); // turned out to be a very slow blocking function
  }

  /* LED #2 */
  // etc
}


pwmDriver.setPWM (from Adafruit_PWMServoDriver) turned out to be a very slow blocking function which resulted in the stepper being noticeably slowed down for the periods of time when a LED is moving towards its goal brightness value. To cope with that, it was decided to move the AccelStepper call to the timer interrupt at 10 kHz. Here comes the current iteration.

Main loop (current iteration):
Code: [Select]
void setup() {
  Timer1.initialize(100);
  Timer1.attachInterrupt(pollStepper);
}

void pollStepper() {
  stepper.run();
}

void loop() {
  /* LED #1 */
  if (led1CurrentValue != led1GoalValue) {
    led1CurrentValue += (led1GoalValue > led1CurrentValue) ? 1 : -1;
    pwmDriver.setPWM(PWM_LED1_PIN, 0, led1CurrentValue);
  }

  /* LED #2 */
  // etc
}

At first, it appeared that the problem was solved. Everything worked in harmony, the stepper moved stably no matter what was happening to the LEDs. But only at first.

The way the DMX library works is that you select your total number of channels, your start channel and then you read data from this fixed range. Something like that:
Code: [Select]
const int CHANNELS_TOTAL = 5;
DMX_Slave dmxSlave(CHANNELS_TOTAL);
dmxSlave.onReceiveComplete(onFrameReceiveComplete);
dmxSlave.enable();
dmxSlave.setStartAddress(1);

int channel1Value = dmxSlave.getChannelValue(1);
int channel2Value = dmxSlave.getChannelValue(2);
int channel3Value = dmxSlave.getChannelValue(3);
int channel4Value = dmxSlave.getChannelValue(4);
int channel5Value = dmxSlave.getChannelValue(5);


With that being said, the problem which I'm facing now, having moved to the interrupts, is that it only properly works if the starting address is in the low range. 1 or 2 basically. Farther than that everything begins to horribly stutter. 10, 17, 150, whatever - is a no go. It makes zero sense to me. Where's the logic in that?

This behavior is only observed when using interrupts, so I'm thinking the problem's tightly connected to them. Can someone enlighten me and help to come up with a solution?

Full code: pastebin (otherwise the post is too long)

UPDATE:
I allowed nested interrupts which solved it for me. I think this is an anti-pattern, but I'm just happy it works.
So the pollStepper function now looks like this:
Code: [Select]
void pollStepper() {
  interrupts();
  stepper.run();
}

pylon

Quote
Full code: https://pastebin.com/qpfWKWr8
Wrong! Always post code to this forum. Code on pastebin will not be available in a few weeks so the content of this thread is lost. We only look at code that is posted here. The excerpts we currently see are probably not relevant. So if you want us to help you, follow the rules of the sticky post "How to use this forum - please read".

deeplowdock

Wrong! Always post code to this forum. Code on pastebin will not be available in a few weeks so the content of this thread is lost. We only look at code that is posted here. The excerpts we currently see are probably not relevant. So if you want us to help you, follow the rules of the sticky post "How to use this forum - please read".
Excuse me, my kind sir, but what do I do with this then?

pylon

There is the possibility to attach your code to the post.

Delta_G

Excuse me, my kind sir, but what do I do with this then?

Excuse me kind sir but please find the "How to use this forum" post at the top of any board and read the instructions.  Then you won't look like such an idiot for not being able to follow them. 

The biggest difference between those who make it in this game and those who don't is a willingness to read th damned documentation. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

DVDdoug

I won't attempt to analyze your code but I'm not super-surprised that you're having timing issues related to attempting to do several timing-critical things at once.

You can probably switch between the DMX & I2C communications but the servo output has to run continuously.

I don't know...   Maybe your software can be tweaked or maybe you need a dedicated servo controller.




BTW - I was considering building a moving-head light and that project and I concluded that it would more economical to buy one.   (That project never got off the ground so I never built or bought one, but I was still planning on building the DMX controller.)


deeplowdock

I allowed nested interrupts which solved it for me. I think this is an anti-pattern, but I'm just happy it works.

deeplowdock

The biggest difference between those who make it in this game and those who don't is a willingness to read th damned documentation. 
Wow man, I hope you enjoy having made it in this game and rocking all this juicy karma

pylon

Quote
Wow man, I hope you enjoy having made it in this game and rocking all this juicy karma
It's a bad idea to deride someone who hinted you that you failed to follow the rules. His post was qualified, your's definitely not.

Quote
I allowed nested interrupts which solved it for me. I think this is an anti-pattern, but I'm just happy it works.
I hope you don't expect this to be reliable. Nested interrupts are a good way to end in dead locks.

Go Up