USB Host Shield (max3421e) Receive limitations

Recently I found a topic "Arduino uno clone -- USB Host Shield -- Arduino uno clone (USB Device), Nov 2019 by ardcp".

I found it useful in testing to see if it would work on a project I testing at the moment.

Setup

Arduino Pro Mini 3.3v connected to a MAX3421E USB HOST board.
This USB Host PCB connects to the Arduino Pro Mini pin for pin like an expansion board.

FTDI232 to program the Arduino via the Arduino IDE v2.2.1.

Connected to the USB Host is a Sparkfun CH340 PCB and I have the TXO and RXI pins connected together.

So whatever the CH340 receives it transmits back out.

Now, ardcp did some really nice work and developed a CH340 driver which with his demo program, sends a string of characters to the ch340, they are looped back, received by the Arduino and sent to the serial port for viewing.

This works great.

In playing around with the sample code, I increased the size of the string to a point where there is nothing being sent back from the ch340. I found this limit to be 31 characters.

Here is the loop routine

void loop()
{
  uint8_t  buf[64];

  Usb.Task();

  //if ( Usb.getUsbTaskState() == USB_STATE_RUNNING ) {
  if (ch340.isReady()) {
    uint8_t  rcode;
    char strbuf[] = "DEADBEEF\n";
	//char strbuf[] = "123456789012345678901234567890\n"; //ok
	//char strbuf[] = "1234567890123456789012345678901\n"; //doesnt work
    //char strbuf[] = "The quick brown fox jumps over the lazy dog\n";
    //char strbuf[] = "This string contains 61 character to demonstrate CH340 buffers"; //add one symbol to it to see some garbage
    Serial.print(".");

    rcode = ch340.SndData(strlen(strbuf), (uint8_t*)strbuf);
    if (rcode)
      ErrorMessage<uint8_t>(PSTR("SndData"), rcode);

    delay(50);

    for (uint8_t i = 0; i < 64; i++)
      buf[i] = 0;

    uint16_t rcvd = 64;
    rcode = ch340.RcvData(&rcvd, buf);
    if (rcode && rcode != hrNAK)
      ErrorMessage<uint8_t>(PSTR("Ret"), rcode);

    if (rcvd) {
      Serial.print((char*)(buf));
    }
    delay(5000);
  }
}

I thought by increasing the buffer size "buf[64]" and maybe also "rcvd=64" this would help, but no luck. I also played around with the RcvData routine and inTransfer routine. No luck.

Now, my limit is 31 characters, not 64 as I thought it would be.
Can anyone give me an idea what is going on here.

I have linked the Topic here

It is a long ago, I have looked at the USB Host shield.
However, I have just made a small test using two Arduino clones, both using the ch340 chip.
The SerialEvent sketch was used on the one connected to the USB port of the USB Host shield after having changed the baud rate to 115200, Serial.begin(115200); in the setup().
Loading the ch340 sketch on the Uno with the USB Host shield gave strange output on the serial monitor.
Using Serial.begin(19200); and adjusting the monitor speed accordingly, the "DEADBEEF" appeared.
The code:

 if (ch340.isReady()) {
    uint8_t  rcode;
    //char strbuf[] = "DEADBEEF\n";
    //char strbuf[] = "The quick brown fox jumps over the lazy dog\n";
    char strbuf[] = "This string contains 61 character to demonstrate CH340 buffers"; //add one symbol to it to see some garbage
    Serial.print(".");

does not work with the SerialEvent as it is missing a newline at the end.
To test the limit, this worked:

if (ch340.isReady()) {
    uint8_t  rcode;
    //char strbuf[] = "DEADBEEF\n";
    //char strbuf[] = "The quick brown fox jumps over the lazy dog\n";
    char strbuf[] = "This string contains 61 character to demonstrate CH340 buffers1\n"; //add one symbol to it to see some garbage
    Serial.print(".");

Adding one more character gave no returned string - as expected.
I have not investigated why 115200 baud did not work connected to the serial monitor.

Thanks for the reply ardcp. I know it has been a long time since your first post about this subject.

I did a quick test this morning before heading off to work.

The ch340 was setup at 9600 and the serial monitor also setup at 9600.
I still only receive 32 characters. Do you think its a serial monitor issue?

In my application, I will be sending initially 6 characters and the device will be responding with around 40 characters.

I will do some more testing over the weekend.

After some testing and some reading on serial port errors, I found WormFood's AVR Baud Rate Calculator.
It shows that on the Arduino pro Mini, using an fosc = 8MHz @115.2K there are many errors.

So I think I need to ditch the Arduino Pro mini and use another micro with better accuracy.

The problem I had with the serial monitor at 115200 baud was caused by an USB meter. Without this, 115200 baud was working fine .
In my setup, the USB worked half duplex because the SerialEvent only replied after having received the full message. Connecting TX an RX together the connection becomes full duplex. I do not know if this is the cause of your problems.

Just realized I had a ch340 USB serial converter laying somewhere.
Tested by connecting TX and RX together with this result:

07:42:10.174 -> Start
07:42:11.666 -> Start
07:42:12.461 -> mcr: 0
07:42:12.959 -> .⸮⸮⸮contains 61 character to demo.....Start
07:42:49.898 -> Start
07:42:50.693 -> mcr: 0
07:42:51.190 -> .DEADBEEF
07:42:56.228 -> .DEADBEEF
07:43:01.299 -> .DEADBEEF
07:43:06.336 -> .DEADBEEF
07:43:15.186 -> Start
07:43:16.810 -> Start
07:43:17.572 -> mcr: 0
07:43:18.070 -> .The quick brown fox jumps over t.

Same problem as you experienced, only the first 32 characters works when echoing the serial output immediately.
Even a 8 MHz cpu ought to handle send then receive at 9600 baud.

At 9600, the error is 0.2%, but I agree it should work.
If you're USB meter worked, I might setup a spare micro to send some characters when it receives a certain character sequence and see what happens.

By the way, thank you for testing with the ch340. It's nice to see I am not going crazy or doing something stupid.

Edit: I am not a C++ expert, but looking at your code I don't see any reference to the MAX3421e library. I am assuming everything on the MAX3421e is left as default.

Reading the datasheet of the chip,

Blockquote
The MAX3421E is put into half-duplex mode at power on, or when the SPI master clears the FDUPSPI bit.

Could this be an issue?

Edit 2: some code from the lightweight-usb-host code.

Blockquote
MAXreg_wr( rPINCTL,(bmFDUPSPI+bmINTLEVEL+bmGPXB )); //Full-duplex SPI, level interrupt, GPX
MAX3421E_reset(); //stop/start the oscillator

Blockquote
Edit: I am not a C++ expert, but looking at your code I don't see any reference to the MAX3421e library. I am assuming everything on the MAX3421e is left as default.

Yes, the MAX3421e is left as default as I did not need full duplex.

Blockquote

Blockquote
The MAX3421E is put into half-duplex mode at power on, or when the SPI master clears the FDUPSPI bit.

Could this be an issue?

Yes, that is the issue. Looking into the Max3421e source files, it is working half duplex.
If you need full duplex, the Host shield 2.0 might support it.
I have not done any test with that.

When the source code talks about "half-duplex" it is talking about the SPI port and not the USB Host port.

Must have had a bad morning as I overlooked the SPI port and the Host Shield is 2.0.
Having no experience with the Max3421e, I am not able to help resolving the problem.

My last reply finished a little abruptly.
I am amazed that you made this all work without even doing any config on the MAX3421E.
You did a really great job on the CH340 driver.

I will first try and get the SPI in full duplex and see how that goes.

Fortunately other more knowledgeable people have done all the hard work as there is only sparse information about the ch340.
Connecting the ch340 dongle to a USB port on a PC and shorting TX/RX gave no problem even at 115200 baud.
Looking into the source of the CH341 driver of the Linux kernel, I came across a remark in the function to set the baud rate:

       /*
	 * CH341A buffers data until a full endpoint-size packet (32 bytes)
	 * has been received unless bit 7 is set.
	 */

Trying that out did not work.
However, resetting bit 7 made it sort of work. At high speed, 230400 baud, all 64 bytes was returned without errors.

The code was changed in cdch340.cpp from:

 if (a < 0)
    return 255;
 
  uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CH340_OUT, CH340_REQ_WRITE_REG, cmd1 & 0xff, cmd1 >> 8 , a, 0, 0, NULL, NULL));
  if (!rv)
    rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CH340_OUT, CH340_REQ_WRITE_REG, cmd2 & 0xff, cmd2 >> 8 , lcr, 0, 0, NULL, NULL));
  if (rv && rv != hrNAK) {
    Release();
  }

  return rv;
}

to:


 if (a < 0)
    return 255;

  a &= ~(0x8000);
 
  uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CH340_OUT, CH340_REQ_WRITE_REG, cmd1 & 0xff, cmd1 >> 8 , a, 0, 0, NULL, NULL));
  if (!rv)
    rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CH340_OUT, CH340_REQ_WRITE_REG, cmd2 & 0xff, cmd2 >> 8 , lcr, 0, 0, NULL, NULL));
  if (rv && rv != hrNAK) {
    Release();
  }

  return rv;
}

Note that this was only a test. Not removing the problem, but indicating the problem lies in the configuration of the CH340 and a more accurate speed calculation.
At 115200 baud the max packet length was 55 bytes and 32 bytes at 9600 baud.

Thanks for the update ardcp.
It's great that you can squeeze a few more bytes out of it.

The code changes look simple but they differ from the cdch340.cpp file in the zip attached to the original thread.

There are 2 subroutines:

uint8_t CH340::SetBaudRate(uint32_t baud, uint8_t lcr)
where
a = (factor & 0xff00) | divisor;

uint8_t CH340::SetBaudRateOnly(uint32_t baud)
a = (factor & 0xff00) | divisor;

Do I just change?

a = (factor & 0xff00) | divisor;
to
a &= ~(0x8000);

or is the Factor Divisor calculation important?

I have some success ardcp. :slight_smile:
After looking at quite a few Driver codes I managed to get 64 bytes with no errors or missing bytes. Note this is at 9600 on the Serial Monitor and 115200 on the USB.

The change is in the SetBaudRate subroutine in cdch340.cpp.
Instead of this

  if (factor > 0xfff0)
    return 255;

  factor = 0x10000 - factor;
  a = (factor & 0xff00) | divisor;

try this

  if (factor > 0xfff0)
    return 255;

  factor = 0x10000 - factor;
  a = (factor & 0xff00) >> 8 | divisor;

You were correct saying the documentation was difficult to find. I was trying to find info on the Control Registers of the CH340G but I had no luck.

Looks like it works great on 115200 SerialMon and 115200 USB.

This is using the ch340 USB serial converter.

Now I have connected it to the Creality Ender3v2 but I am getting garbage!!! :frowning:

Ok, last post for tonight.
When I plug in the Ender3v2 into my Win10 PC, the USB port is using CH341 driver.

Looking at the BaudRate routine your Driver uses the following command (registers?)
cmd1 = 0x1312;
cmd2 = 0x0f2c;

yet in the CH341 Driver I found it uses
CMD_C1 0xA1
CMD_C2 0xA4

I am so confused...

Well done. I had completely overlooked that only the lower byte is used in the ctrlReq function.
A few tests show that the ch340 now works with baud rates 38400 - 230400 and 64 bytes send/receive.
After looking in other source files, the speed calculation used here is probably not precise enough for lower or higher speeds.
It appears there are many different versions of the ch340, but then a check ,to figure out which one, needs to be added. Or different sketches used.
Can you check if your Ender3v2 uses a baud rate of 250000 or 115200?
A search reveals some confusing information about the speed.
Edit: The ch340 does not support 250000.

So Ender3v2 port speed is definitely 115200.
Circuit diagram indicates a CH340G with a 12MHz crystal.

I also ran a simple receive loopback sketch on a Wemos D1 Mini v2.
This board also has a CH340G with I think a 12MHz crystal. (cannot see markings)

Wemos now connected to USB Host port. Arduino Pro Mini is sending "12345\n"
Here is the Serial Monitor:

21:51:57.066 -> out>12345
21:51:57.066 -> 
21:51:57.132 -> rcvd: 5
21:51:57.132 -> in<!f���
21:52:02.117 -> out>12345
21:52:02.117 -> 
21:52:02.181 -> rcvd: 5
21:52:02.181 -> in<%dS�
21:52:07.183 -> out>12345
21:52:07.183 -> 
21:52:07.249 -> rcvd: 5
21:52:07.249 -> in<�b���
21:52:12.234 -> out>12345
21:52:12.234 -> 
21:52:12.309 -> rcvd: 5
21:52:12.309 -> in<%bW�

Any ideas for debugging?

Edit: 115200 on USB and Serial.

Edit2: By the way, what is your opinion for printing received chars on monitor? rcvd is length of buf, buf is the return string of characters.

Code1

    if (rcvd) {
      Serial.print("rcvd: ");
      Serial.print(rcvd);          
      Serial.print("\n");

      Serial.print("in<");
      Serial.print((char*)(buf));
      Serial.print("\n");
    }

or Code 2

    if (rcvd) {  //more than zero bytes received
      Serial.print("rcvd: ");
      Serial.print(rcvd);          
      Serial.print("\n");

      for (uint16_t i = 0; i < rcvd; i++) {
        Serial.print((char)buf[i]);  //printing on the screen
      }
      
    }

Depends if you clear the buf before receiving or not.
If you clear the buf, use code 1 else code 2.
Do you check the return code something like:

rcode = ch340.RcvData(&rcvd, buf);
if (rcode && rcode != hrNAK) {
      Serial.print("Rcv Ret: ");
      Serial.println(rcode);
}

With the fix for the higher baud rates now working I have gone back to your original ch340.ino sketch.

Still getting garbage.

More testing tonight...