Re: The R4 Serial Problem

I'd write a dedicated task instead. Isn't that the expected solution in a multi-tasking environment?

Easy solution: output is blocked until all bytes have gone into the buffer. Input loses chars, of course.

Okay, that defeats DMA. You confirm my intuitive dislike of DMA in this case.

I agree that an interrupt driven FIFO looks like the best solution, provided it really outperforms a service task.

Is it generated code, or Renesas-written library code? I always assumed the latter, although they may have some internal generation tool. (It is touted as an "FSP Library" rather than a code generator like Atmel Start or Microchip Harmony.)

In the FSP code, what happens is that you pass a pointer to a source and a length to the R_SCI_UART_Write function

This sort of API is very typical of embedded libraries from vendors. It provides what I guess is a typical bottom layer of a posix-like file system, as if that would be the code layered on top of it. Even though a lot of embedded applications tend to be more stream-like than file-like.

The obvious solution is to use an intermediate buffer

The obvious solution is to write a bare metal Serial driver that doesn't use FSP. But it seems unlikely that there will be volunteers to do that unless there is some indication from Arduino Inc that it might be an acceptable path. Sticking with FSP is likely to have political momentum.

Isn't that the expected solution in a multi-tasking environment?

I don't think the Uno R4 minima is running a multi-tasking environment. It's not one of the MBed OS system, and Uno R4 WiFi is running the network stack on a separate chip, so the Renesas chip itself can be less "advanced." (and I much prefer that it's not MBed, because that adds SO MUCH useless overhead.)

1 Like

Good luck,

As I mentioned previously, I had some code that was working pretty well! But it is something like 100 plus commits behind. It was in this PR... Which I should probably close out as well as some others that have stagnated for close to a year.

Serial: Tx use FIFO plus fixes by KurtE · Pull Request #90 · arduino/ArduinoCore-renesas (github.com)

It has been awhile since I looked at the code. But my impressions back then, was it would be difficult to get anything merged that changed the underlying code. So needed to find ways to work around it... So I believe I did something like, go ahead and pass a buffer down to it, with what I had up to N characters. The rest goes into the safe ring buffer. When there is a callback from the underlying system, it checks the ring buffer and grabs as much as will fit into its buffer and calls back to the underlying system. Plus it needed to handle issues like the underlying system would lose characters if I called while the hardware Serial object either FIFO full or write buffer full. so had to loop waiting for those conditions to resolve...

I ran the code with several different test programs like:

//connect  Serial1 TX -> Serial2 RX, Serial2 TX -> Serial3 RX, Serial3 TX -> Serial4 RX....

#if defined(ARDUINO_UNOR4_WIFI)

UART _UART4_(18, 19);
#define SerialX Serial3
#else
UART _UART2_(18, 19);
#define SerialX Serial2
#endif

#define SPD 115200
int loop_count = 0;

#define BUFFER_SIZE 80

class BufferInfoClass {
public:
  BufferInfoClass() {
    clear();
  }
  char buffer[BUFFER_SIZE];
  uint8_t cb_buffer;
  uint8_t cb_copy[BUFFER_SIZE];
  uint8_t cb_copy_cnt;
  void clear() {
    cb_buffer = 0;
    cb_copy_cnt = 0;
  }
};

BufferInfoClass buffers[3];
uint32_t millis_last_input = 0;

void setup() {
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  while (!Serial && millis() < 4000)
    ;
  Serial.begin(115200);
  delay(800);
  Serial.println("Test all Serials");
  Serial.print("Baud rate: ");
  Serial.println(SPD, DEC);
  Serial1.begin(SPD);

  SerialX.begin(SPD);
}

#define MIN(a, b) ((a) <= (b) ? (a) : (b))


void CopyFromTo(Stream &SerialF, Stream &SerialT, uint8_t buffer_index) {
  int available;
  int available_for_write;
  int cb;
  BufferInfoClass *buf = &buffers[buffer_index];
  if ((available = SerialF.available()) != 0) {
    available_for_write = SerialT.availableForWrite();
    cb = MIN(MIN(available, available_for_write), BUFFER_SIZE);
    if (cb) {
      SerialF.readBytes(&buf->buffer[buf->cb_buffer], cb);
      SerialT.write(&buf->buffer[buf->cb_buffer], cb);
      buf->cb_buffer += cb;
      buf->cb_copy[buf->cb_copy_cnt] = cb;
      buf->cb_copy_cnt++;
      millis_last_input = millis();
    }
  }
}

void memory_dump(const char *pb, uint8_t cb) {
  const char* pbA = pb;
  uint8_t cbA = cb;

  Serial.print("\t");
  for (uint8_t i = 0; i < cb; i++) {
    if (*pb < 0x10) Serial.write('0');
    Serial.print(*pb, HEX);
    Serial.print(" ");
    pb++;
  }
  
  Serial.print("\n\t ");
  for (uint8_t i = 0; i < cbA; i++) {
    if (*pbA >= ' ')  Serial.write(*pbA);
    else Serial.write(' ');
    Serial.print("  ");
    pbA++;
  }
  Serial.println();
}

void print_buffer_header(uint8_t index) {
  BufferInfoClass *buf = &buffers[index];
  Serial.print("  ");
  Serial.print(buf->cb_buffer, DEC);
  Serial.print(" (");
  for (uint8_t i = 0; i < buf->cb_copy_cnt; i++) {
    if (i != 0) Serial.print(",");
    Serial.print(buf->cb_copy[i], DEC);
  }
  Serial.print(")");
}

void CompareBuffers(uint8_t index1, uint8_t index2) {
  if (buffers[index1].cb_buffer == buffers[index2].cb_buffer) {
    if (memcmp(buffers[index1].buffer, buffers[index2].buffer, buffers[index1].cb_buffer) == 0) {
      Serial.println(" ** Match **");
      return;
    } else {
      Serial.println(" ** different **");
    }
  } else {
    Serial.println(" ** counts different **");
  }
  memory_dump(buffers[index1].buffer, buffers[index1].cb_buffer);
  memory_dump(buffers[index2].buffer, buffers[index2].cb_buffer);
}

void loop() {
  CopyFromTo(Serial, Serial1, 0);
  CopyFromTo(Serial1, Serial, 1);
  CopyFromTo(SerialX, SerialX, 2);

  // now see if we should compare the data yet or not
  if (buffers[0].cb_buffer && ((millis() - millis_last_input) > 100)) {
    Serial.println("Check buffers: ");

    print_buffer_header(0);
    Serial.println();
    print_buffer_header(1);
    CompareBuffers(0, 1);

    print_buffer_header(2);
    CompareBuffers(0, 2);

    buffers[0].clear();
    buffers[1].clear();
    buffers[2].clear();
  }
  digitalWrite(LED_BUILTIN, HIGH);
  delayMicroseconds(100);  // give time form things to propagate
  digitalWrite(LED_BUILTIN, LOW);
}

Which would take everything that was received from Serial and write it to Serial1, Everything from Serial1 to Serial and everything that the other Possible Serial object to itself.
So you could wire Serial1TX->SerialXRX SerialXTX->Serial1Rx and in in each iteration it would grab the minimum of of how bytes were available in the RX queue and the available space in the TX queue. Note: this sketch will not work at all on current release as availableForWrite returns 0...

But if all is working properly you should see that all of the UARTS are sending data at the same time... (assuming you pass it enough data)... With a stripped down version of this (like bypass check for avaialableForWrite, Each write would be done sequentially....

In Comparison, with a Teensy 4.x, I was running a similar sketch when I was coding up the HardwareSerial code as a class... And I had it working with all 7 or 8 hardware Serial classes, plus I have a library (FlexIO_t4), for the Teensy FlexIO pins, where I could define a few more Serial objects using this...

But there is a simpler version, which simply does: Serial->Serial1 and Serial1->Serial. Which allows you to use your UNO as a USB to Serial adapter. If the Serial stuff was working reasonably, the MINIMA can do this nicely, the WIFI (with default configuration), can do this in a limited way.

Difference? with MINIMA, you can detect what Baud rate, the host is telling your USB object that it wants (TERMIOS), and the USBSerial object exports this data, which allows your code to reconfigure the hardware Serial port to this. Likewise detect when this changes and likewise DTR... With WIFI - The ESP32 sees this data but has no way to inform the R4... processor of this. AND the baud rate must match the baud rate what your code supplied in the Serial.begin() or
the ESP32 and the processor wont communicate properly...

Oops, sorry I digress...

Will keep my fingers crossed!

Hey there Delta_G, great work on this one! I was racking my brain about the issue I've been seeing while trying to communicate with a single board computer...lo and behold you figured it out! I got my print times down from 8ms to 0.9ms at 115200 baud by using your PR. I can't believe they won't merge it!!

Any chance you can upload your Serial.cpp and Serial.h files with the DMA method that you mentioned above? Eagerly waiting to try it!!

Amazing thank you!! I will try it and report back.

Hi Delta_G,

I have another quick question for you. I am seeing that my TX operations to my slow SBC are causing block of my main loop. Your fixes to Serial.cpp and .h above definitely helped it, but it's not all the way gone as I'm getting missed encoder triggers only on my SBC (not on windows PC). Would it be possible to change the interrupt priority on my R4 Minima so that TX transmissions never block my ability to read my encoder interrupts?

Do I have this setup correctly below??

// Set the highest priority for the corresponding interrupt numbers
  NVIC_SetPriority((IRQn_Type)0, 0); //Pin D2
  NVIC_SetPriority((IRQn_Type)1, 0); //Pin D3
  NVIC_SetPriority((IRQn_Type)9, 0); //Pin D8
  NVIC_SetPriority((IRQn_Type)3, 0); //Pin D12
 
 //Set lower Priority for RX and TX
  NVIC_SetPriority((IRQn_Type)5, 2); //TX
  NVIC_SetPriority((IRQn_Type)6, 1); //RX

Thank you so much, I was forcing myself to think that I was making some mistake on my code...

But hours and hours passed, I was not giving credit, I decided to bring a R3 from another project thinking that this could not be me... but still willing it was me... and, SURPRISE!

All the code was working from the very beginning.

HOW IN THE WORLD, can Arduino allow this to happen? I was really wishing it was me.

I feel sorry for all the many people out there that will lose many hours or work, break projects, potentially break a lot of more sensitive stuff, but overall trust Arduino won't do such a silly thing.

I have no words to describe how disappointed I am, and how thankful for your research and PR.

Thanks a lot! Hope Arduino could keep an eye on brand quality here and test better their products before offering new versions.

I agree, the R4 is a prominent member in the Arduino hall of shame :frowning:

I'd wait for R5 and try again.

1 Like

Due to user issues with Arduino R4 using our product the IO Expander, I've rewritten the UART driver that has a properly working Tx buffer, fixed the ring buffer that is not interrupt safe, and got both Serial and Serial1 working full duplex at the same time. I'm looking for testers please contact me at my site. Once tested I plan on releasing it for free. Here's a video of both ports working at the same time at 115k; http://www.zevendevelopment.com/videos/IOExpanderArduinoR4WorkingWithBothSerialPorts.mp4

1 Like