Very rookie question about UART: DMA vs interrupt - Page 1
Forum section: electronics / general

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 07:56:22 am
content: I'm learning STM32 bare metal (and having lots of fun), now it's time to set up UART communications.With AVR it was a no brainer: a couple of ring buffers and interrupt driven peeking and poking into UART registers. With STM32 a new player (DMA) comes into the ground and, even before diving deep into it, I'd like to understand the role it can play in UART communications.I could be wrong, but nothing prevents me from thinking that given the unpredictable and asynchronous nature of serial communication DMA would be of a little (if any, or even worse) help for reception. Therefore I'm planning NOT to use DMA for RX. Are there alternatives?On the other hand, being TX under my control it should be a better target for DMA. Even not knowing details it's not hard to imagine a string or a buffer being sent as a whole under DMA control. However I'm still puzzled on what to do when some putchr() gets called while the buffer is being spit out to USART by DMA. Do I need some sort of double buffering? Kinda one free to receive new chars from putchr and the other sending bits out to USART control, and swap them when DMA interrupt signal that tx is done?

author: pcprogrammer
author_rank: Super Contributor
timestamp: March 06, 2023, 10:18:47 am
content: Sure double buffering is an option. Switching between them in the interrupt routine that signals the DMA transfer has finished is a way to do it, but will take some consideration of handling pointers and sizes and when it might happen in the code. If it comes when you are still writing a new character in the buffer it might fail so you have to block the DMA interrupt for it.Another option might be using a circular DMA buffer and have it stop when the buffer becomes empty. Start it again when a character is send. I believe it is possible to manipulate the counters while DMA is running.Either way you will still need to hold your process when a buffer becomes full.For DMA on reception being useful, it depends on what is being communicated. If it is a package based system with same sized bursts of data, a double buffered DMA setup could be beneficial.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 06, 2023, 11:04:09 am
content: "AVR way" is just fine. UART RX by nature usually requires some kind of framing, and a parser which processes the data byte by byte anyway. All you lose is interrupt entry/exit latency per byte (some 20 CPU clock cycles), it's meaningless with usual baud rates.With TX, DMA works better as you usually have an idea about the length of the packet before sending it. If you are thinking about replacing general-purpose send/print functions, which are called here and there without clear orchestration of lengths, to use DMA, then you run into impossibility of guaranteeing these functions never block, because that would require infinite amount of RAM (and RAM is quite limited in microcontrollers). That is why I just don't bother and use blocking print functions which always have this worst-case behavior and block predictable and repeatable time everywhere.

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 11:13:49 am
content: Quote from: Siwastaja on March 06, 2023, 11:04:09 amWhat I was thinking was more like functions that don't block but can eventually overfill the ring buffer with possible data loss and event (the data loss) counting to be possibly reported somewhere...Is this a dumb idea?

author: peter-h
author_rank: Super Contributor
timestamp: March 06, 2023, 11:24:41 am
content: This can be argued both ways.These fast CPUs can generate data very fast. Even if you are using printf (with fputc patched to go to your output device) you can get very fast data going out. In my project I had debugs set up to be non blocking but after getting some weird issues I now have two sets of these functions: a) blocking and b) non-blocking but length of output is limited to 128 bytes and it truncates after that.For UARTs I would use interrupts, unless you are running at megabit speeds. See the recent thread on UARTs and DMA.

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 11:24:52 am
content: Quote from: pcprogrammer on March 06, 2023, 10:18:47 amYou're telling me that while the transmission is undergoing I could still queue data in the same ring without stopping DMA?That would be very nice as it can spare a buffer.I'll dig into it with more depth.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 06, 2023, 11:40:17 am
content: Quote from: uliano on March 06, 2023, 11:24:52 amYes, the DMA just wants a start address and number of data, it doesn't matter if it's one array or two separate arrays in C.When you print more to the buffer (make it longer), you have to store that information somewhere (increment some "pending data count" variable), and in your DMA finished handler reconfigure the DMA to make a new transfer, with the length of newly accumulated data. (There are probably other ways, too.)STM32 DMA does not have native ring buffer support (address wrapping feature), so if your data span wraps around, you need to DMA it in two pieces.This is all some work, you will need to serve DMA interrupts to keep it going, and then you still have to decide what to do if buffer is full - do you block, or do you discard data?

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 11:58:21 am
content: Quote from: Siwastaja on March 06, 2023, 11:40:17 amwait, this can produce, as a limit of n sparse putchr(), just a sequence of n DMA of 1 byte?am I wrong?Quotethis is too bad!Quoteyeah, definitely... maybe DMA is overkill and AVR way not so bad...

author: pcprogrammer
author_rank: Super Contributor
timestamp: March 06, 2023, 12:13:56 pm
content: Quote from: Siwastaja on March 06, 2023, 11:40:17 amIt is possible with the STM32 DMA to do a circular memory buffer to peripheral.I use it in thisproject(Is a bare metal project, so no HAL shit)It is possible to keep track of where the DMA is by looking at the counters. I'm also using it in another project that is not finished yet, where it is constantly transmitting data out of a UART and in the receiving MCU it is also using DMA to put the data in a buffer. Making it stop when there is no data will be tricky, but I believe it to be possible.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 06, 2023, 12:26:57 pm
content: Quote from: pcprogrammer on March 06, 2023, 12:13:56 pmCircular mode just repeats the same transfer from beginning. I don't think you can do this (say 256-long circular buffer):@0, len = 100 (expected addresses 0..99, so far so good)@100, len = 100 (expected addresses 100..199, so far so good)@200, len = 100 (expected addresses 200..255, 0..43, not automatically possible in one go)

author: pcprogrammer
author_rank: Super Contributor
timestamp: March 06, 2023, 12:39:58 pm
content: Quote from: Siwastaja on March 06, 2023, 12:26:57 pmI would have to look into it to see what the possibilities are. In the not finished project I use it, to constantly transfer the data from the same memory locations and modify the data as needed. The DMA just keeps on going, so that works. If it is possible to manipulate the system such that it halts after circling back to the beginning, I can't say for sure. It might also depend on the version of the DMA used. With so many different STM32 flavors out there it is hard to know them all.I faintly recall something about the DMA in the F103 not being able to work with certain circular settings, but it did work in one of my setups. Things like this are fun experiments, but I would not base a "commercial" system on them.I agree with others that for basic UART stuff just using the interrupt is easiest and safest.

author: peter-h
author_rank: Super Contributor
timestamp: March 06, 2023, 01:43:14 pm
content: QuoteYes; I did a waveform generator with that (driving a DAC).One could probably make use of this for TX. Set the DMA transfer count to the number of bytes known to be valid in the circular buffer - why not? Once the count = 0 then yeah you have to do something in software to restart it all. But the wrapping of the circular buffer output pointer should be automatic.I found the double buffer mode problematic. A long story. It would be better to set up 2 (or more) buffers in RAM and load the address of the next one into the DMA, when the previous transfer finished. That way there may be little gaps in the transmission, due to the ISR, but these can be hidden in the UART TX buffering (1 byte).

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 02:36:01 pm
content: Quote from: peter-h on March 06, 2023, 01:43:14 pmLet me see if I got it.While DMA is spitting out bytes (and possibly updating the back index of ringbuffer) I can still add new bytes upfront (provided I'm not wrapping around, I would think about that later) and when I receive the interrupt from DMA having reached the original limit I just have to restart it with the new limit?

author: peter-h
author_rank: Super Contributor
timestamp: March 06, 2023, 04:31:51 pm
content: Yes that should work. I have not tried it and have never heard of anybody else who has. I did all my UART stuff under interrupt. And that was not trivial - see link below. I was also waggling RS485 driver control signals and the DMA solution would not do that for you.Basically you interrupt on DMA transfer count = 0.But see here toohttps://www.eevblog.com/forum/microcontrollers/uart-weird-stuff-and-uart-dma/

author: Siwastaja
author_rank: Super Contributor
timestamp: March 07, 2023, 08:42:50 am
content: Quote from: peter-h on March 06, 2023, 01:43:14 pmI can't see how this would happen. In circular mode, after NDTR transfers, the new transfer starts from the originally written memory address (M0AR).If you want to generate exactly 57 requests, the only way to do it is to set NDTR=57. If you want these 57 items to start from buf, the only way to do that is to set M0AR=&buf. If you use circular mode, there is no way to automatically detect buffer access would exceed the range of your buf[], there is no register to configure sizeof buf. Also there is no way to go back to buf, there is no register to configure origin of buffer.Ergo, circular mode only works with uninterrupted stream of bytes. I don't see how it would be helpful for example when printing arbitrary lengths of text in a ring buffer. If you think so, please show an example (pseudocode is fine).

author: peter-h
author_rank: Super Contributor
timestamp: March 07, 2023, 11:40:10 am
content: OK, so it is not possible.What one would have to do is linear transfers only. But that's still quite a large potential CPU time saving. You can get all data out of a circular buffer with just two linear operations.

author: NorthGuy
author_rank: Super Contributor
timestamp: March 07, 2023, 03:37:56 pm
content: Whether to use DMA or not and how to use depends on the application. Many factors are involved, such as data pattern, speed etc.For transferring printf() data, if baud rate is low and you want to make it non-blocking, it's probably better to use two buffers (often called ping-pong buffers) - one buffer is being transferred through DMA, other being filled by you.When you get a next character to send, check if the DMA transfer is ongoing, if so, write the character into the buffer. If not, check if you can pass your character directly to UART, if so, do it. Otherwise, store the character into a buffer, swap buffers, and start the DMA transfer for whatever amount of data you have in the buffer up to now.Every time a DMA transfer finishes, switch the buffers, and initiate a new transfer if there are any characters in the new buffer. There's a race condition between switching buffers and writing a character into the buffer. You'll have to deal with it.If you use higher baud rate (e.g. few MBaud), it might be better to make your UART transfers blocking, depending on whether you can tolerate corresponding delays.

author: peter-h
author_rank: Super Contributor
timestamp: March 07, 2023, 04:44:48 pm
content: Sure, although I would say- most embedded printfs come out via fputc or some such, and you have no exactly idea how much will get output (see past threads on sprintf and snprintf which is much more correct)- I'd say most embedded printf-type debugs can be truncated quite safely to a say 128 byte buffer- most megabit-speed serial comms are half duplex (master/slave)- you have to be pretty careful using the printf family for debugs, because a lot of these are not thread-safe or safe for ISR use (see past threads on printf, printf using the heap, newlib, mutexes, etc)So it all gets quite specialised.

author: NorthGuy
author_rank: Super Contributor
timestamp: March 07, 2023, 05:22:54 pm
content: Quote from: peter-h on March 07, 2023, 04:44:48 pmYou can supply your own putc() or whatever your documentation says. Then you can either make it blocking, or you can supply a buffer of whatever length you need.Quote from: peter-h on March 07, 2023, 04:44:48 pmI often dump blocks of memory, which can be way longer than 128 bytes.Quote from: peter-h on March 07, 2023, 04:44:48 pmWhy is that? I routinely use 2M UART for debugging. Still takes time. Like 40 characters will take 200 us, so this usually cannot be done from within ISRs without causing some sort of disturbance. At any rate, converters in printf() are usually poorly written, so printf() will take time anyway. You don't want to add assault to the injury by also making it to use slow blocking UART.Quote from: peter-h on March 07, 2023, 04:44:48 pmSafe or not safe, if you run multiple printf() in parallel the characters get intermixed. But if it is only for debugging, who cares.

author: AVI-crak
author_rank: Regular Contributor
timestamp: March 08, 2023, 03:38:37 am
content: Quote from: NorthGuy on March 07, 2023, 05:22:54 pmThis is not always the case, my version of printo() is very fast, and always faster than the debug port.https://github.com/AVI-crak/Rtos_cortex/blob/master/sPrint.huliano - There are many uses for UART paired with DMA.1) The simplest and most banal one is to copy the string to the buffer and start DMA. Copying takes very little time, and the UART takes a very long time. While the transfer is in progress, other tasks can be performed, because the main task will be blocked waiting for the DMA to stop. Without using the OS - waiting for an interrupt from the UART and DMA become equivalent.2) A more complex option is a ring buffer.Unlike the receiving node, the output buffer can stop - because the filling is done by soft copying. For control, two pointers or counters are used - the head and the tail. You are allowed to change the head, and the DMA handler is allowed to change the tail. When the head catches up with the tail - the buffer is completely full, you have to wait for DMA.The DMA handler can be completely placed in an interrupt, there is a simple and short code without waiting.Ring buffer can work well even without OS. But only if you print short messages that can fit in the buffer. Printing huge text will cause the buffer to fill completely, and block while sending. From this point of view, it is easier to use the first option.

author: uliano
author_rank: Regular Contributor
timestamp: March 08, 2023, 10:17:35 am
content: @AVI-crackMy idea, as inexperienced hobbyist, is to use a ring buffer and drop (to /dev/null) incoming text when it is full. This because I think that on most (at least those who controls mosfets, motors, devices, ...) MCU firmwares stopping isn't an option, and a log with "holes" in it is still providing me information (even where the hole starts!).

author: Siwastaja
author_rank: Super Contributor
timestamp: March 08, 2023, 10:48:57 am
content: I don't like to printf() ASCII reporting stuff out of timing-critical things. Sure, using DMA for UART makes it many orders of magnitude faster than a blocking version, but printf() is still usually too slow for something like a PWM / control loop interrupt handler.Instead, I use this pattern (quick pseudocode):Code:typedef struct{int32_t setpoint;int32_t measured_speed;int64_t some_internal_value;int16_t phase_current;} trace_elem_t;volatile trace_elem_t trace;volatile int trace_i;ISR(){if(trace_i < NUM_ELEM(trace)){trace.xxx = yyy; // collect valuestrace_i++;}}main(){if(trigger_condition)trace_i = 0;if(trace_i == NUM_ELEM(trace)) // trace full{// blocking UART print of trace in csv format}}Then some gnuplot, matlab or python script to process the CSV, plot it, whatever. This is BETTER than printf debugging, not much work, and very efficient for timing.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 08, 2023, 10:57:36 am
content: I also use this idea:Code:typedef struct{uint32_t info; // any stored datauint32_t timestamp; // u32 millisecond countuint16_t line; // C code line numberuint8_t file_id; // .c file specific number - #define SRC_FILE_IDuint8_t severity : 3;uint8_t reserved : 5;} diag_trace_t;diag_trace_t trace;Code:ALWAYS_INLINE void diag_log(uint32_t info, int file, int line){diag_trace_t tmp = {.info = info, .timestamp = millisec_u32(), .file_id = file, .line = line, .severity = SEVERITY_LOG};boot_app_shared.diagnostics.trace[++boot_app_shared.diagnostics.trace_idx] = tmp;}#define LOG(info) do{diag_log((info), SRC_FILE_ID, __LINE__);} while(0)Example use:Code:#define SRC_FILE_ID 0x15 // unique, in the beginning of each .c file...LOG(some_interesting_var);... looks like this on a parser, which, during compile time of the parser, reads out the codebase .c files to figure out SRC_FILE_IDs and code context for each line number:Code:11 175520295 ( 1:23:36:46:538.1) : WARN diagnostics.c:62 info: U65535 S+65535 0x0000ffffif(!ASSWARN(instance < NUM_ELEM(boot_app_shared.diagnostics.chunks), instance)) return -1;12 175521309 ( 1:23:36:47:528.3) : WARN networking.c:79 info: U65535 S+65535 0x0000ffffif(!ASSWARN(instance < NUM_ELEM(connections_rc), instance)) return -1;

author: peter-h
author_rank: Super Contributor
timestamp: March 08, 2023, 12:25:59 pm
content: For STM32 debugging, the SWV ITM console is also fast; you can set a clock speed in the MHz range. You need STLINK V3.I just don't find it reliable.

author: uliano
author_rank: Regular Contributor
timestamp: March 08, 2023, 12:53:26 pm
content: Quote from: Siwastaja on March 08, 2023, 10:48:57 amI try to follow:you are copying relevant info to ram, in binary format (efficient) each ISR() call, and in the superloop you (slowly) dump it but only when your ram buffer is full.by doing so (having a blocking dump) you may (and probably will) clog your superloop to the point that you probably have moved all the rest of the code to ISRs.I still have some points unclear:why you wait for buffer full before start dumping? to simplify (and fasten) things without the circular hassle?also i miss completely the role of trigger_condition :-(

author: uliano
author_rank: Regular Contributor
timestamp: March 08, 2023, 02:17:16 pm
content: Quote from: peter-h on March 08, 2023, 12:25:59 pmthere's also SEGGER RTT but I would prefer something less of a lock-in

author: Siwastaja
author_rank: Super Contributor
timestamp: March 08, 2023, 02:25:43 pm
content: Quote from: uliano on March 08, 2023, 12:53:26 pmMy projects tend to have, especially on ARM Cortex (because it has such good and simple to use interrupt priority system, including software interrupts allowing continuing a high-priority task as low-priority task), either completely empty "superloop", or superloop which does totally non-timing critical things, such as blocking networking code or UART debug printing.Yeah, rest of the code in ISRs. That works very well. It is like event-driven programming. (Really the main difference to using threaded RTOS is, with simple interrupts, tasks are always triggered so that function execution starts at the beginning, while RTOS threads can have longer functions that wait for other signals and continue execution. In bare-metal (no OS) project, you only have ONE thread capable of that behavior (what you call superloop). Sometimes I take advantage of that; sometimes not.) Interrupt-driven bare metal works very well in projects where you don't have dependencies on poorly designed, blocking libraries.Besides, blocking UART print has completely predictable timing.QuoteThat was just a made-up example which I wrote in 3 minutes in the reply window, to communicate the idea with code instead of wall of text. Obviously you choose what you exactly want to do. For periodic interrupts (like control loops), checking for "trace full" condition is good because it happens quickly anyway, and if you have too much data, just ignore the rest.QuoteI mean something like a pushbutton "start recording", or maybe trigger it when motor turns on, some voltage level is exceeded, or maybe even leave that code in production and trigger on an error condition. What I show is, after all, pretty equivalent to what storage oscillosscopes do, but with insight into internal state variables if you so wish.The only other (possibly even better performance) option really is hardware trace monitor system (like Segger RTT and friends). The massive advantage of your own code instrumentation is, it works in production, over the air, whatever. You don't need to plug in a debugger and hope to catch the event on the lab table!And performance gain between printf() and binary data collection is probably nearly two orders of magnitude, and same can be said about RAM usage. You get much longer trace in the same amount of RAM when you don't need to store repeated text labels, separators, or inefficient decimal numbers.

author: uliano
author_rank: Regular Contributor
timestamp: March 08, 2023, 02:49:09 pm
content: Quote from: Siwastaja on March 08, 2023, 02:25:43 pmI used to keep an event (one or few bytes each) queue loaded in ISRs and processed in superloop, but then AVR interrupts were much simpler.I should spend time (lots of it) reading manuals and re-thinking about all that stuff.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 08, 2023, 02:59:34 pm
content: Note that not everybody agrees with me. But I have worked that way for some 6-7 years now and have zero regrets. I think it boils down to this difference: instead of setting a "process data" flag in a fast peripheral ISR, trigger a software interrupt (NVIC->STIR = MADEUP_IRQ_NUMBER, anything that does not clash with peripheral IRQ numbers you want to use) and do the processing there. The rest is the same: instead of processing the data in superloop, you are processing it in an ISR. Both will be interrupted by further high-priority interrupts. Then you set the interrupt priorities to control what pre-empts what.Periodic housekeeping tasks can be done in a timer interrupt, which can be of fairly low priority.

author: AVI-crak
author_rank: Regular Contributor
timestamp: March 08, 2023, 04:42:37 pm
content: Quote from: uliano on March 08, 2023, 02:49:09 pmFor real-time tasks, for sharing with an external hardware solution that has its own quartz, for controlling motors or other inert loads - printf ()cannot be used!!! This was one of the reasons printo() existed - a very quick answer.In fact, you have completely different problems that have not yet manifested themselves.1) You need a galvanically isolated channel. All designs with power supply from the network - require galvanic isolation. Concurrently - this data channel can later be used as a manager.The simplest and most lame option is to use an ESP8266 or something similar.The solution is a little more complicated - to use a galvanic isolator. This option will evenly add headaches on the hot and cold side, but is guaranteed to save you money.2) You need to review all the lines of printing in the context of the relevance of the moment of application. It makes no sense to accumulate tedious reports, you do not have accounting there. Even if at some point the current exceeded the allowable limit - there is no point in sending 100500 messages about this, just make a decision on the spot. For example, by changing one or more control signals - so that the current becomes less. And then you can send a message.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 08, 2023, 04:49:53 pm
content: Quote from: AVI-crak on March 08, 2023, 04:42:37 pmHere, you are just saying "don't make any bugs" in a convoluted way. Of course exceeding current is dealt with by changing the state to something that will lower the current.But visibility into the system is needed because we make false assumptions and human errors, and introduce bugs. Having logged internal state around the happening, you improve your chances of finding and fixing the bug.Besides, there are cases when the system is kinda working, but not optimally. For example, a control loop might cause the current to go out-of-bounds, and a pulse-by-pulse safety comparator triggers on every cycle, which might manifest itself as suboptimal performance of motor. You need visibility into the control loop calculations to understand what is happening and how to make it better.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 08, 2023, 04:52:17 pm
content: Here is an example plot of a csv coming from a short trace of a motor controller ISR which worked the way shown above. Way more information than a few debug prints.

author: NorthGuy
author_rank: Super Contributor
timestamp: March 08, 2023, 04:54:21 pm
content: Quote from: uliano on March 08, 2023, 10:17:35 amCollecting status information on your FETs and motors is a task which is completely different than providing printf() facility to your program - neraly nothing in common.Imagine you have a hammer. It can be used to drive nails, but some may also be used to pull nails. You can collect extensive information on how to drive nails with the hammer, but all this information will be completely useless if what you want to do is pulling nails. Same with UART. This is a tool, just like a hammer. Any design starts from the task, not from the tools. Imagine what an architect would achieve if he started a design for a house from writing instructions to the carpenter on how to swing his hammer.If you want to collect data, first start by calculating bandwidth. Say, you have a 30 kHz control loop interrupt where you need to adjust PWM for your motor. Say, you want to collect two bytes of information each interrupt. This is 60 kBytes/sec. Clearly, if you run UART at less than 600 kBaud you will have losses. But if your baud rate is above 600 k then you can transmit all your data. So, running UART at 1MBuad would work fine, assuming there's enough bandwidth on all the path from your MCU to the PC to transport at this rate.Then you analyze how the data originate. For example, if you want to transmit from your 30 kHz control loop then your data originate at regular intervals - 2 bytes every 33 us. Your task it to pass the data to UART. Obviously, by the time you need to transmit, the data you passed to the UART from the previous interrupt is already gone and UART hardware is empty. Thus, there's no need to verify anything - simply push your data.Then you think how to pass the data to the UART. Here you consult your datasheet to figure out if it is possible to pass two bytes one immediately after the other. Most UART modules can do this because they have built-in FIFOs or shadow registers. Assume yours can do this. Then you just write your 2 bytes to the UART module register from your ISR and nothing else needs to be done - only two lines of code.Then you think what you do on PC. PC has lots of resources and naturally this is the place where you want to do most of the processing. You need to establish the data position in the stream of bytes (because there may be a shift by one byte), then you can do whatever you want - log it, plot it, or whatever it is you're passing data for. If you cannot program on PC, use the same C which you use on your MCU - works just as well, even better.The above is not an instruction on what you must do. I actually don't know your task, so I cannot know how to solve it. Rather, this is meant as a demonstration of how, by thinking logically, you can simplify your work and create a simple and reliable solution. If you start thinking about the real task you need to perform, you will find a good solution. But you will never find a generic solution which works for all possible situations.

author: AVI-crak
author_rank: Regular Contributor
timestamp: March 08, 2023, 05:06:45 pm
content: Quote from: Siwastaja on March 08, 2023, 04:49:53 pmThere will always be mistakes. If there is a possibility of an error, then it must first be corrected, and only then reported about it.A quick response is much more important than a message to the user. Actuality!!!

author: Siwastaja
author_rank: Super Contributor
timestamp: March 08, 2023, 05:16:43 pm
content: Quote from: AVI-crak on March 08, 2023, 05:06:45 pmYou misunderstood. Nothing of this had anything to do with user interfaces. The idea is to collect data during development, and even in production,for the developers, to enable bughunting.

author: AVI-crak
author_rank: Regular Contributor
timestamp: March 08, 2023, 06:42:08 pm
content: Maybe you are right.Printing messages and collecting logs are different processes. We need to decide on a direction.

author: peter-h
author_rank: Super Contributor
timestamp: March 08, 2023, 08:53:54 pm
content: It would be interesting to see some code which simply extends the UART TX and RX FIFOs, using DMA and RAM, with functions like ipqcount, opqspace, readbuf, writebuf. That's basically what is needed.

author: dietert1
author_rank: Super Contributor
timestamp: March 08, 2023, 09:01:36 pm
content: Remember using STM32Cube and HAL library with DMA for UART. DMA would receive into a circular buffer. After some hours i learned how to extract chunks of incoming data from that buffer. The application main loop was polling the DMA request state and then parsing available data without terminating the original DMA request. There was only one DMA request at boot time and it would continue forever, unless some error happened like buffer overrun. That software could run for days streaming in megabytes of data. I wouldn't want to implement all that from scratch.Regards, Dieter

author: AVI-crak
author_rank: Regular Contributor
timestamp: March 08, 2023, 10:31:18 pm
content: Quote from: dietert1 on March 08, 2023, 09:01:36 pmThere are literally 20 lines of code - the kind that do useful work. You just need to peel off the husk, and it will become beautiful and compact.