[Logo] TCC discussion forum
  [Search] Search   [Recent Topics] Recent Topics   [Hottest Topics] Hottest Topics   [Top Downloads] Top Downloads   [Groups] Back to home page 
[Register] Register /  [Login] Login 


This forum is read only and new users cannot register, please ask all new questions either using GitHub discussions, or in Arduino forum tagging @davetcc.

Serial SNAFU RSS feed
Forum Index » IoAbstraction & TaskManagerIO
Author Message
milesg


Joined: Oct 8, 2021
Messages: 21
Offline
Hey all,
I got an issue which I don't believe is actually related to a malfunctioning of this library, but perhaps a limitation or conflict between the UART/I2C/Interrupts on my Teensy 4.1 or perhaps just the nature of Serial comms on Arduino.

Quick setup summary:
Teensy 4.1 with 1x 23017 and 1x 8574 on the same I2C channel (Yeah, I need lots of I/O)
3x Serial Ports for Modbus communication: 2x servo drives and 1x for receiving commands from a PC.

Most of the I/O expander pins are being used for button (19x) & encoder (1x) inputs using a separate interrupt pin on the Teensy for each expander.
And then most of the remaining non-UART/I2C/SPI pins on the teensy are going to LED and SSR outputs.

In the attached image I have captured a segment of the system all working together with my logic analyzer. You can see that when the serial commands are being sent to the servo drive, the INT pin of my 23017 (where my encoder lives) doesn't get reset quickly. This causes encoder ticks to get missed by the ISR.

In this example, that's actually not a huge deal because the serial command is only going out twice a second. But in the final operation of this system, I will be sending serial commands closer to 20x/second with each of the two serial ports going to the servo drives.

In practice this makes my encoder input almost unusable. I have a very short onEncoderChange() callback which just increments a variable that I then use in my main scheduled task to calculate what commands to send to the servos, so I'm not sure what else I may be able to do to mitigate this issue.

For example: Would moving my encoder to direct interrupt-enabled inputs on the Teensy help?

I'm working on a prototype PCB which marries all this stuff together, so I don't want to go cutting traces and bodging wires until I have some guidance from someone wiser than I.

Thanks in advance and sorry for posting another long winded thread. smilie Any insight is always greatly appreciated.
[Thumb - serial_snafu.JPG]
 Filename serial_snafu.JPG [Disk] Download
 Description No description given
 Filesize 135 Kbytes
 Downloaded:  300 time(s)

davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
Yes, this is something I've been looking at myself recently, the encoder callback runs through the task manager and relies on nothing taking a lot of time. If the serial port is unavailable for writing and causes blocking then this could cause major delays in the library.

Most Serial implementations allow for this by having an "availableForWrite" method that can be called before attempting to write anything else to that port. The TcMenu remote facilities use this to ensure that write is available before writing anything out. At the end of the day, Serial is implemented very differently on each platform and I'm really not that familiar with Teensy.

The problem here is that you may need to ensure that no blocking serial operations take place because you can't easily move the I2C operations for the encoder operations into an interrupt, as it would cause major stability problems - I2C on nearly all platforms should not be used from interrupt handlers. In your case, with so much reliance on UART, I would see if there are specific serial libraries for your board that are fully asynchronous.
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
We've also looked into reading the state of encoders using events with the encoder pin reads taking place in a raw interrupt handler, but this just does not work for I2C cases where there's no way to read the state in an interrupt. It also needs special care even for device pins, as you're reading probably a 32bit peripheral read register on Teensy (ARM). So it even causes complexities for normal cases. Further, the callback realistically needs to be made by task manager so need an event trigger.

It comes with enough issues of its own that I've abandoned the idea for now. Instead, I tend to use ESP32 a lot, and I put anything heavy onto another thread using a second task manager. I use the second task manager like a queue to do heavy operations basically. I'm not 100% sure but I do see a few threading libraries for Teensy that support the std::thread interface. That could be a good compromise to offload some work.
milesg


Joined: Oct 8, 2021
Messages: 21
Offline
Thank you very much for your comprehensive answers.

I wasn't able to find a Serial library that seemed to be non-blocking, but there is a well regarded TeensyThreads.h library which supports the std::thread function as you suggested. I'm gonna have to jump down that rabbit hole to figure out how to implement it as I'm definitely not used to working with these more advanced types of functions and thread-safe programming. It does appear to have good documentation and examples so I'll see what I can do.

Am I to understand that this could allow the CPU cycles to be divided up in a such a way that the i2c calls in the TaskManager for my encoder could still sneak in during Serial TX/RX taking place on another thread? That would be great!

Not knowing much about multiple threads as a software solution, I was considering doing something I've done in the past, where I have a dedicated ATTINY85 purely as an encoder counter which in turn is set up as as an I2C slave I can poll for cumulative counts when needed. Looking at my data capture above I see that the I2C calls are relatively fast compared to the Serial signals, so it may be possible to keep everything running well enough since I've divided my tasks with hardware instead of software, but that'd of course take bit of new hardware to design/test out.
Edit: I guess I would then have to feed that number back into tcMenu somehow for jogging through options and changing menu values. Not sure how that would work yet.

Either way, I've got plenty to look at now. Thanks again!
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
Are you sure that availableForWrite wouldn't work for you? It returns true only when you can write to the serial port without blocking.
milesg


Joined: Oct 8, 2021
Messages: 21
Offline
Hi Dave, I did look into that. The main issue is that when I'm moving the jog wheel I need to actively be sending updated commands to the servo drive as fast as possible (10-15 times a second seems to be a sweet spot), so if I'm sending serial commands that often (available or not) they will be interfering with the next encoder input that I need to be looking at... Kind of a chicken and egg scenario I suppose. I am also requesting/receiving servo position information from the servo drive several times a second, AND this is all eventually happening with 2 separate drives/servos. So there's very little, if any, time that I'm not relying on constant serial data.

I did look into multi-threading, but it seems that with the single core CPU of the Teensy, I can only just divide up my tasks into time slices (100ms by default which is way too coarse), but even with shorter slices, there's a big caveat regarding Serial in that I need to lock (mutex) such commands so that they have time to finish, which of course negates the whole potential advantage for my use case. There are full on RTOS packages I looked at, but it quickly started going over my head. Perhaps something to keep in mind as I advance my skills.

Since it's what I know, and what I had available, I did setup a Teensy that I was able to bodge in to my circuit for testing. My screen updates are only happening 5 times/sec as per my tcMenu settings, and I'm querying the cumulative count about 10 times/sec over i2C. This result either gets fed into my motor equations or into a call to the encoder increment() command for working with TcMenu navigation. I'm using Paul Stoffregen's encoder library which is very fast/efficient for in/decrementing the count. The count gets zeroed on each query for the next time. In practice this is actually working out very well. The system is the most responsive I've seen it and I am getting my commands out without interfering with anything else (all my buttons on the IOExpanders obviously don't need such rapid synching, so the rest of the IOAbstraction stuff is working great.)

This "Multi-Core" approach isn't the most elegant, but I'm going to need to update the PCB design anyway, and a tiny85 is only $2, so I'm tempted to stick with it. At least for now.

Thank you again for all your advice.
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
This "Multi-Core" approach isn't the most elegant, but I'm going to need to update the PCB design anyway, and a tiny85 is only $2, so I'm tempted to stick with it. At least for now.


To be honest, whatever works quickly and cheaply is the best solution! I've seen many solutions that have two devices.
 
Forum Index » IoAbstraction & TaskManagerIO
Go to:   
Mobile view
Powered by JForum 2.7.0 © 2020 JForum Team • Maintained by Andowson Chang and Ulf Dittmer

This site uses cookies to analyse traffic, serve ads by Google AdSense (non-personalized in EEA/UK), and to record consent. We also embed Twitter, Youtube and Disqus content on some pages, these companies have their own privacy policies.

Our privacy policy applies to all pages on our site

Should you need further guidance on how to proceed: External link for information about cookie management.