Logging using STM32 UART and Free RTOS

Hi ,

I am trying to implement logging feature using STM32F4 UART and FreeRTOS . I am ponting my printf function to use the write function as below

int _write(int fd, char* ptr, int len) {
    HAL_StatusTypeDef hstatus;

    if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
        hstatus = HAL_UART_Transmit(&huart4, (uint8_t*)ptr, len, HAL_MAX_DELAY);
        if (hstatus == HAL_OK)
            return len;
        else
            return EIO;
        }
    errno = EBADF;
    return -1;
    }

I am using minicom and a wx python UI which uses pyserial to check the logs printed out . When I have this function in bare metal code everything works fine . I can see the logs in both the terminals (Wx python UI and minicom)
When I move the logging functionality over to FreeRTOS , I can see logs only through minicom and the WxPython UI hangs .

I have not created any task for logging or writing to console in FreeRTOS . I just initialized UART and trying to print it out. Can anyone pls suggest if I would need a separate task for logging ?If so how does that work.

Thanks

I don’t see anything wrong with this write function. When you add tasks to your code you may run into some serialization issues where characters between different messages can be mixed together. Solving the character mixing will require a mutex around the printf to ensure an entire message is sent to the uart before allowing another task to send its message.

It is a mystery why WxPython UI would hang. Perhaps that UI has some special functions with the characters sent and your log messages are triggering these functions. miniterm is a pretty basic terminal so it may work better.

Can you supply the printf code that connects the printf to the _write function?

Thanks for the quick response . Below is my code .

One thing I dont understand is , the log_log function is mostly used by other tasks to print their status to console . So what should the UART task be doing ? I am new to FreeRTOS so probably this might be very basic .

And this is the WXPython application I am currently using

void log_log(int level, const char *file, int line, const char *fmt, ...) {
  log_Event ev = {
    .fmt   = fmt,
    .file  = file,
    .line  = line,
    .level = level,
  };

  lock();

  if (!L.quiet && level >= L.level) {
    init_event(&ev, stderr);
    va_start(ev.ap, fmt);
    stdout_callback(&ev);
    va_end(ev.ap);
  }

  for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
    Callback *cb = &L.callbacks[i];
    if (level >= cb->level) {
      init_event(&ev, cb->udata);
      va_start(ev.ap, fmt);
      cb->fn(&ev);
      va_end(ev.ap);
    }
  }

  unlock();
}

static void lock(void)   {
  if (L.lock) { L.lock(true, L.udata); }
}


static void unlock(void) {
  if (L.lock) { L.lock(false, L.udata); }
}

I did not notice that you had a uart task. Most applications don’t use a uart task. The HAL_UART_Transmit function at the heart of the _write function is simply writing the bytes in the string to the uart. This could be a quick operation if the hardware has a FIFO and the string fits. Or it could be a slow operation is the string is long and FIFO is shallow (or non-existent) and the string has to be copied 1 byte at a time into the UART transmit buffer. If the bytes are being copied one at a time then a second HAL_UART_Transmit could be executed in a separate task and then there is a race between the two instances and their strings which necessitates some type of mutex to keep the messages separate. Sometimes there is a UART task with a message queue as the input. Then the print messages are queued and the UART task writes them to the UART one message at a time. This pattern is effective at serializing access to the uart without requiring a mutex but it comes with the overhead of another task and a queue.

I will study the rest of the code you supplied now.

The only interesting thing I see in the wxTerm is the note in the blog that it does not handle long messages very well. It processes 1 byte at a time from the uart which is fine for short/bursty traffic but can cause problems with long messages. Of course long is undefined so it is simply “a message long enough to cause problems”. Otherwise wxTerm does not seem to have any side effects that could crash.

Thanks for taking time to look at my code . And for the detailed explanation.
I did not have task before .
I figured out it was one of the log message in the high priority task was causing it to hang . Since I cannot explain the scenario as in why? Pls let me know if you know any obvious reasons

There is a section of wxTerm that does packet decoding. I wonder if your high priority log message is triggering some behavior in the packet decoder. I did not look closely at that code but wxTerm is not very large.

Your implementation of log_log relies upon the object ‘L’ which appears to be a singleton logging object. (just guessing). Inside this object is a lock structure. If that is not using the FreeRTOS locking systems (a mutex is a good choice) then you may not be getting the behavior you expect. Using a C(or C++) standard lock system may not be compatible with FreeRTOS.

I Implemented the mutex around the log_log . It’s working fine .Thanks for patiently reviewing the code .