Jon Chandler
- Joined Jun 12, 2008
- 1,595
LCSC has quality parts.
Sorry I guess I have a variant I use wrong..
Sorry I guess I have a variant I use wrong..
You can use an FT232RL... Got it from ArrowNAC in 2014 for $3.65 low qty to play with. OEM: FTDI.I recently designed a circuit that needs to be interfaced to a PC via a USB port. For that purpose, I've used the MCP2221A chip, which is quite effective and easy to implement. The thing is that said chip has become the most expensive component (by far) in the design. Are there any other more economic alternatives out there that could be used instead?

What micro are you using?The MCU can be programmed even with the CH340C completely connected to the circuit, so the use of an in-circuit jumper is not necessary as was the case with the MCP2221A.
You're right, the CH340 datasheet sucks and doesn't give any details as to its buffer size.Didn't expect to see such bad speed limitations on the CH340C. 115200 baud is pretty much a standard speed for USB to UART serial converters so you would think that should work without issues on the chip or OS driver side.
What's the actual serial clock speed (checked with a scope or something) when set to 115200? If it's off by more than a few percent that can cause framing/overrun errors in back to back characters without delays between characters.
The MCP2221A has decent internal buffers.
448-Byte Buffer to Handle Data Throughput at Any Supported UART Baud Rate: - 64-byte transmit - 384-byte receive
"CH340 has built-in separate transmit-receive buffer" but states no size.
Is your controllers serial routines interrupt driven or polled? Without a sizable internal chip buffer, polling or slow interrupts can drop characters if there is a tiny (2 -> 8 byte) FIFO on the actual uart at 115200.
Is your serial interfaces on the uC polled or interrupt driven without software buffers or interrupt driven with ring-buffers?You're right, the CH340 datasheet sucks and doesn't give any details as to its buffer size.
But let me give you an example. When I tried to transmit a string of 50 characters from the MCU to the computer via the CH340, at least 5 or 6 of those characters would be received with errors (reported as "?" within the received string), with two or three characters missing too. The way I was transmitting said characters from the MCU was simply to wait until the MCU's transmit buffer was empty before sending the next one. That was never an issue with the MCP2221A. But the only way to make things work with the CH340C was to include a delay of 15ms between characters, which is huge considering a transmission speed of 115200 bauds. Even a delay of 10ms will report an error every 75 characters or so, if they're all transmitted in a row.
To be fair, the MCP2221A also gets choked when receiving characters from the USB and then translating them to UART. I had to include a 10ms delay routine in the computer software to get rid of all the glitches. All this comes from my refusal of using the proper UART control lines to implement a robust hardware protocol. It's just that I want to keep my design's pin count as low as possible ... I'm kinda cheap that way ...
I was using the PIC16LF15323, but I had to switch to the PCI16LF1823 because the latter can run Timer1 using an external oscillator as a source, independent of the internal system oscillator. What this means is that keeping track of time using a 32.758 kHz external crystal is a piece of cake while the MCU runs at 32 MHz so as to be able to communicate through the UART at 115200 bauds.What micro are you using?
In this project, the UART is being polled for both transmission and reception. The MCU I'm using has a meager two-byte buffer in it. You're going to have to enlighten me as to what "ring-buffers" means.Is your serial interfaces on the uC polled or interrupt driven without software buffers or interrupt driven with ring-buffers?
Polling, OK.In this project, the UART is being polled for both transmission and reception. The MCU I'm using has a meager two-byte buffer in it. You're going to have to enlighten me as to what "ring-buffers" means.


Excellent explanation, thanks for putting in the time. I've used said "ring buffer" technique in the past in different projects and it's quite practical, and efficient code-wise. My personal term for it is "rollover buffer"Polling, OK.
View attachment 343529
View attachment 343530
https://users.ece.utexas.edu/~valvano/mspm0/ebook/Ch8_SerialCommunication.htm
That's a major part of the problem at 115200 unless it's done very fast (at the max FOSC possible for the controller) in a dedicated time when only serial data is expected on the RX side and correctly on the TX side by making sure the shift register is idle/empty (TRMT bit set) before pushing another character into the shift-register from the UART register (buffer). I think that's right for most PIC 8-bit controllers but double check for each device to be sure.
Examples:
https://onlinedocs.microchip.com/ox...UID-FD5172BC-F1D8-431D-9F08-800C5BF4C055.html
https://onlinedocs.microchip.com/ox...UID-6DBE5F62-8C0A-4B29-A556-AE2694C676F2.html
"Ring-buffers" (circular buffer) just means a buffer that goes round (start and finish of data) in the buffer space allocated instead of having a fixed "linear" array index start and a varying index end of data in buffer memory. For a receive interrupt routine using a ring-buffer to store the incoming data usually minimizes interrupt time with well written (short and fast as possible) code. For TX interrupt driven routines, a linear array type buffer is usually all that's needed and can work with received data if simplicity is needed.
http://www.simplyembedded.org/tutorials/interrupt-free-ring-buffer/
https://www.allaboutcircuits.com/te...ouble-buffering-technique-interrupt-friendly/
Just to be sure. For the Tx side. So, are you checking buffer and/or the transmit Shift Register empty flag (the TRMT bit)? On a typical system with there are both and it's possible that while the buffer indicates clear for data, the Tx TSR is still busy. If the TSR is still busy then a write to the buffer register can (by eratta or configuration for things like 9-bit transmission) corrupt the data currently being shifted out even if the documentation says the TSR will wait until the STOP bit is transmitted before being load with data from the TXREG.Excellent explanation, thanks for putting in the time. I've used said "ring buffer" technique in the past in different projects and it's quite practical, and efficient code-wise. My personal term for it is "rollover buffer"
And yes, all of my Tx routines check for the state of the transmission buffer before pushing characters for processing, so that's not an issue at least on the MCU side. But the CH340C seems to be not as fast as the MCU, and that's what was causing the glitches, which have already been taken care of. Albeit at a cost. But in this particular project the required delay between characters is not an issue since data transmission will be done every couple of seconds, and then only about 50 characters at a time.

With a FIFO and code that polls or uses interrupts driven by the UART (Transmit Interrupt), this can cause corruption of the last character from the buffer without the Transmit Shift register empty bit workaround.Module: UART (Transmit Interrupt)
When using UTXISEL<1:0> = 01 (interrupt when
the last character is shifted out of the Transmit
Shift register) and the final character is being
shifted out through the Transmit Shift Register
(TSR), the TX interrupt may occur before the final
bit is shifted out.
Work around
If it is critical that the interrupt processing occurs
only when all transmit operations are complete,
after which the following work around can be
implemented:
Hold off the interrupt routine processing by adding
a loop at the beginning of the routine that polls the
Transmit Shift register empty bit, as shown in
This small change saved me about a couple of thousand bucks for the first batch. And it's gonna keep on saving me a bundle in the near future too.just out of curiosity, what kind of savings we are talking about? unless this is produced in sufficiently high volume, it will not be worth it...
Since my program does not make use of UART interrupts, it only polls the state of the transmission buffer after sending a character through the UART. Here's my code:Just to be sure. For the Tx side. So, are you checking buffer and/or the transmit Shift Register empty flag (the TRMT bit)? On a typical system with there are both and it's possible that while the buffer indicates clear for data, the Tx TSR is still busy. If the TSR is still busy then a write to the buffer register can (by eratta or configuration for things like 9-bit transmission) corrupt the data currently being shifted out even if the documentation says the TSR will wait until the STOP bit is transmitted before being load with data from the TXREG.
Just checking the TRMT bit before pushing the next character will add a small delay that can (YMMV) fix TX corruption errors at higher speeds on some devices.
View attachment 343534
Example TSR related errata for a controller:
With a FIFO and code that polls or uses interrupts driven by the UART (Transmit Interrupt), this can cause corruption of the last character from the buffer without the Transmit Shift register empty bit workaround.
;*****************************************************************************************
; Transmit W through the serial port
; Exits bank 0
;*****************************************************************************************
Transmit_W:
banksel TXSTA ;bank 3
Tx_Loop:
btfss TXSTA, TRMT ;loop until TXSTA is set, that is, until the transmit
goto Tx_Loop ;buffer is empty
movwf TXREG ;transmit W throught the UART
call Delay_0.5ms@32MHz ;delay 1.5ms, for the benefit of the CH340C, this
call Delay_0.5ms@32MHz ;because the chip has not been installed with data flow
call Delay_0.5ms@32MHz ;control in its hardware (CTS, RTS, DTR, etc)
movlb 0x00 ;select memory bank 0 before leaving
return
The usage of interrupts doesn't matter as polling will have the same defect in operation if the TSR interaction is buggy in the controllers hardware UART implementation. It's shouldn't make any difference if the hardware didn't have bugs. That's the point of checking the TSR empty flag, there are bugs in some devices. This is one workaround for those bugs.Since my program does not make use of UART interrupts, it only polls the state of the transmission buffer after sending a character through the UART. Here's my code:
Code:;***************************************************************************************** ; Transmit W through the serial port ; Exits bank 0 ;***************************************************************************************** Transmit_W: banksel TXSTA ;bank 3 Tx_Loop: btfss TXSTA, TRMT ;loop until TXSTA is set, that is, until the transmit goto Tx_Loop ;buffer is empty movwf TXREG ;transmit W throught the UART call Delay_0.5ms@32MHz ;delay 1.5ms, for the benefit of the CH340C, this call Delay_0.5ms@32MHz ;because the chip has not been installed with data flow call Delay_0.5ms@32MHz ;control in its hardware (CTS, RTS, DTR, etc) movlb 0x00 ;select memory bank 0 before leaving return
Yeah, I know. Using three calls for the same delay instead of a single one is rather clumsy. But the device's code is actually quite short and there's no need for this sort of optimization. Also, I don't think that calling said delay before or after sending the character makes any difference. I added the delay after the transmit instruction just because it was a late addition to the code.