My test code using a timer & dma tx seems to be working as expected but the simple loop() code
(gpio_toggle_bit(PIN_MAP[LED_BUILTIN].gpio_device, PIN_MAP[LED_BUILTIN].gpio_bit);) seems to stop working sometimes even though the timer/dma code is still running fine in the background. Also when the LED stops flashing I have to press reset before being able to upload a new sketch over serial USB.
The time I am in the ISR is about 185uS.
On a side note I noticed that a delayMicroseconds(DMX_MAB); in the timer ISR with a DMX_MAB value of zero would lockup the board and windows would report a USB device error.
If you think the problem is staying in your ISR too long, why not try just setting a flag in your ISR so the next time your loop function loops it takes that long action? unless your timing needs to be very precise (or your loop is very slow), this should solve it
.
What i also try to do is use my loop for preparing data that an ISR then can send (via dma or whatever) when the time is right
. This ofcourse still requires your loop to be fast enough, but prevents the expensive process of readying the data from being part of the ISR
.
As mentioned the loop code is just a simple pin toggle and nothing else at the moment so should have no timing issues.
I wish to send a Universe of data every 40ms but the critical/difficult part is the BREAK and the MARK AFTER BREAK (MAB) timings as to do them on a pin configured for UART I have to switch back to digital mode else digitalWrite does not work and then switch the pin to UART mode again. Another option might be to do it the Arduino way where the UART baud rate is flipped between 250kbps and an arbitrary baud rate that generated the 175us BREAK time when sending a single zero byte. The problem with this is the MAB timing ends up longer than I would hope for of about 8-10us.
Maybe another question should be what timers are used for what in the STM32 core. Maybe timer 2 is used for some other purpose and that somehow affects the loop code.
There is no absolute answer to that question, the only advice is spend as little time as possible in the ISR.
If you can get away with setting a “triggered” flag, or writing a couple of bytes here or there, then you will be much safer than you would be if you attempt to do anything remotely complex or anything that has an indeterminate execution period.
If you are in a very tight loop, and timing is absolutely microsecond critical, then even a few extra program cycles in the ISR may be enough to throw out your timing.
If on the other hand you get one trigger for your ISR per day, the ISR can do pretty much whatever it likes.
This general rule holds true no matter what the architecture of the CPU is.
What happens is the code runs fine outputting a BREAK signal and then 512 bytes of serial data (8N2 @ 250000 baud) every 40ms using interrupts and DMA.
All the loop() code does is toggle the LED on off but after a random? amount of time the LED stops flashing but the BREAK & 512 bytes are still being transmitted okay on Serial1 tx pin so the MCU has not locked up fully.
I have added in trap code to signal errors in configuring the DMA channel and DMA transfer errors but nothing comes up on Serial (not ideal having serial calls in an interrupt but removing them does not help.
Is there a freeMem type function like used on the Arduino for the STM32?
What happens is the code runs fine outputting a BREAK signal and then 512 bytes of serial data (8N2 @ 250000 baud) every 40ms using interrupts and DMA.
All the loop() code does is toggle the LED on off but after a random? amount of time the LED stops flashing but the BREAK & 512 bytes are still being transmitted okay on Serial1 tx pin so the MCU has not locked up fully.
I have added in trap code to signal errors in configuring the DMA channel and DMA transfer errors but nothing comes up on Serial (not ideal having serial calls in an interrupt but removing them does not help.
Is there a freeMem type function like used on the Arduino for the STM32?
<…> how long it is safe to remain in a timer compare interrupt.
I would say somewhere you are causing the systick interrupt to not get serviced.
If you use a debugger check the variable that holds the ms. Not the systick itself, systick is a hardware counter and will continue counting, just the interrupts for it which should increase a variable may not be serviced.
EDIT: Just read this doesn’t apply to ARMv7-M, so probably doesn’t make a difference at all one more thing, use the interrupt attribute in your ISRs, so their code is safe, otherwise they may modify variables that affect the function that was interrupted when they were called.
https://gcc.gnu.org/onlinedocs/gcc-6.1. … butes.html
– I don’t see where you initialize Serial, but it may happen to be done in a default init routine.
– Serial2 is not used, so why to initialize it in setup? (shouldn’t be Serial?)
– have you checked that transmitting 512 bytes at 250kBauds is finished for sure within 40mseconds?
– timer2 ISR: is it really necessary to use PA9 once as GPIO and then for Serial1? Does is have any real scope? You could “simulate” a low period by sending 0 (or other value) as UART data.
– setup_dma_transfer() can be called once in setup, which should then include setup_tube_config(). After that use only dma_enable()/disable() where needed.
– tube config, once configured, is not needed to rewritten again in timer2 ISR.
– digitalWrite() in main loop may be conflicting with pinMode()/digitalWrite() called in timer2 ISR if they both access the same GPIO(A?) port, or the delay() may be conflicting with delayMicroseconds() from the ISR. That’s why the main loop stalls.
In this case the data will still be output over Serial1 because that is interrupt driven.
I am a bit confused by your code. You set up Serial2, but I dont see it used, then Serial1 is used for both DMA transfers of the DMZ data, and debug information, and in case of error you are sending the debug information before disabling DMA. Since DMA is still active, I dont know what the DMA controller does when the UART send a DMA request, but would be worth reading in the datasheet to make sure that doesn’t cause any issue.
– I don’t see where you initialize Serial, but it may happen to be done in a default init routine.
I had realized about a hour ago this is not done but adding it in does not help.
– Serial2 is not used, so why to initialize it in setup? (shouldn’t be Serial?)
The code is commented out in loop(). I used a jumper wire to connect tx1 to rx2 for quick testing.
– have you checked that transmitting 512 bytes at 250kBauds is finished for sure within 40mseconds?
Yes it finishes in < 30ms but for testing I have sometimes only been sending about 10 bytes.
– timer2 ISR: is it really necessary to use PA9 once as GPIO and then for Serial1? Does is have any real scope? You could “simulate” a low period by sending 0 (or other value) as UART data.
When the pin is configured for USART digitalWrite will not alter the pin state so need to switch the pin mode between digital i/o and USART evert time.
I could maybe reduce the baud rate and stop bit count and send a zero to simulate the BREAK period but the timer/dma still work fine after the LED stops flashing.
– setup_dma_transfer() can be called once in setup, which should then include setup_tube_config(). After that use only dma_enable()/disable() where needed.
I think I tried that but it did not work. I wondered if switching the pin between digital i/o and USART meant it all needed setting up again.
– tube config, once configured, is not needed to rewritten again in timer2 ISR.
I just set the DMA start address and data length in the timer isr else it starts reading out wrong memory. Maybe turning on circular buffer will prevent the need to do this?
– digitalWrite() in main loop may be conflicting with pinMode()/digitalWrite() called in timer2 ISR if they both access the same GPIO(A?) port, or the delay() may be conflicting with delayMicroseconds() from the ISR. That’s why the main loop stalls.
In this case the data will still be output over Serial1 because that is interrupt driven.
That sound reasonable and checkable as I could disable interrupts around the loop digitalWrite but maybe not an ideal solution as this will effect timers?
– I don’t see where you initialize Serial, but it may happen to be done in a default init routine.
I had realized about a hour ago this is not done but adding it in does not help.
– Serial2 is not used, so why to initialize it in setup? (shouldn’t be Serial?)
The code is commented out in loop(). I used a jumper wire to connect tx1 to rx2 for quick testing.
– have you checked that transmitting 512 bytes at 250kBauds is finished for sure within 40mseconds?
Yes it finishes in < 30ms but for testing I have sometimes only been sending about 10 bytes.
– timer2 ISR: is it really necessary to use PA9 once as GPIO and then for Serial1? Does is have any real scope? You could “simulate” a low period by sending 0 (or other value) as UART data.
When the pin is configured for USART digitalWrite will not alter the pin state so need to switch the pin mode between digital i/o and USART evert time.
I could maybe reduce the baud rate and stop bit count and send a zero to simulate the BREAK period but the timer/dma still work fine after the LED stops flashing.
– setup_dma_transfer() can be called once in setup, which should then include setup_tube_config(). After that use only dma_enable()/disable() where needed.
I think I tried that but it did not work. I wondered if switching the pin between digital i/o and USART meant it all needed setting up again.
– tube config, once configured, is not needed to rewritten again in timer2 ISR.
I just set the DMA start address and data length in the timer isr else it starts reading out wrong memory. Maybe turning on circular buffer will prevent the need to do this?
– digitalWrite() in main loop may be conflicting with pinMode()/digitalWrite() called in timer2 ISR if they both access the same GPIO(A?) port, or the delay() may be conflicting with delayMicroseconds() from the ISR. That’s why the main loop stalls.
In this case the data will still be output over Serial1 because that is interrupt driven.
That sound reasonable and checkable as I could disable interrupts around the loop digitalWrite but maybe not an ideal solution as this will effect timers?
– digitalWrite() in main loop may be conflicting with pinMode()/digitalWrite() called in timer2 ISR if they both access the same GPIO(A?) port, or the delay() may be conflicting with delayMicroseconds() from the ISR. That’s why the main loop stalls.
In this case the data will still be output over Serial1 because that is interrupt driven.
But to be sure, for test purpose, I would just remove everything else than the delayMicroseconds() from the ISR, to see whether main loop still hangs or not, due to any possible conflict between the delay functions. If not, then add the needed features one by one to the ISR, and check each time the behavior.
This way you could figure out what exactly is causing the trouble.
In my spare time at work I soldered up another Baite clone and loaded a sketch with more debug stuff and have been running it with a logic analyser on and for some reason it has not gone wrong yet though the test setup is not exactly the same as at home.
When I got home I loaded the same sketch on the original board and it stops working (DMX fine, LED stops flashing) after random amounts of time if plugged into the PC USB port but if I plug it in to just a power socket then it ran all night without problem? So now I don’t know if it was the test rig or PC or blind luck causing the errors.
I will try and get time to analyse up the board at home on my days off and see if it goes wrong.
With the analyser I now have timings and the total time in the Timer2 ISR is about 188us though most of this is just waiting on the BREAK signal timing so I could reduce this at the cost of needing another timer.
As the Timer2 exits it sets the DMA transfer going and this takes about 23ms to send 512 bytes of DMX so with a frame rate of 25 FPS I have about 17ms free and even for 30 FPS frame rate I would have about 15ms free.
In my spare time at work I soldered up another Baite clone and loaded a sketch with more debug stuff and have been running it with a logic analyser on and for some reason it has not gone wrong yet though the test setup is not exactly the same as at home.
When I got home I loaded the same sketch on the original board and it stops working (DMX fine, LED stops flashing) after random amounts of time if plugged into the PC USB port but if I plug it in to just a power socket then it ran all night without problem? So now I don’t know if it was the test rig or PC or blind luck causing the errors.
I will try and get time to analyse up the board at home on my days off and see if it goes wrong.
With the analyser I now have timings and the total time in the Timer2 ISR is about 188us though most of this is just waiting on the BREAK signal timing so I could reduce this at the cost of needing another timer.
As the Timer2 exits it sets the DMA transfer going and this takes about 23ms to send 512 bytes of DMX so with a frame rate of 25 FPS I have about 17ms free and even for 30 FPS frame rate I would have about 15ms free.
I remember the SerialUSB can block for periods of time if nothing in the host is reading the data, perhaps that’s what happens to your code.
