linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH] USB: serial: ch341: fix wrong baud rate setting calculation
@ 2019-11-01  9:25 Michael Dreher
  2019-11-01 14:20 ` Johan Hovold
  0 siblings, 1 reply; 10+ messages in thread
From: Michael Dreher @ 2019-11-01  9:25 UTC (permalink / raw)
  To: linux-usb

Hi Johan

>> https://github.com/nospam2000/ch341-baudrate-calculation/blob/master/README.md
>Interesting! From a very quick glance this looks very promising (although he doesn't seem to handle rounding yet). I'll take a closer look and compare with what I've done. 

That’s my code. I actually do rounding. It might not be obvious that the '(*2 + 1) / 2’
in  ((2UL * CH341_OSC_F) + / (prescaler * priv->baud_rate) + 1UL) / 2UL
does a bankers rounding, that why I explained it in the README.MD:
https://github.com/nospam2000/ch341-baudrate-calculation#rounding-issues

Rounding issues

Why not just using the simple formula (CH341_CRYSTAL_FREQ / (prescaler * baud_rate))? Because we are using integer arithmetic and truncating values after the division leads to an error which only goes into the positive direction because the fractional part of divisor is lost.
With floating point arithmetic you would do 5/10 bankers rounding to spread the error equally between positive and negative range:

divisor = TRUNC(CH341_CRYSTAL_FREQ / (prescaler * baud_rate) + 0.5)

which is equal to

divisor = TRUNC((10 * CH341_CRYSTAL_FREQ / (prescaler * baud_rate) + 5) / 10)

The formula in the code does the same but using dual system integer arithmetic and therefore use *2 +1 /2 instead of *10 +5 /10.

> I also noticed that we should always pick the higher base clock if the resulting divisor is odd (and within the valid range).

The general rule from the perspective of mathematics is to use the highest possible divider (and therefore the lowest possible prescaler)
because that gives the lowest possible relative error in the division. As long as the result of divider*prescaler is the same it doesn’t
matter which combination you choose. For example my algorithm prefers 1*208=208 over 2*104=208 of the other algorithms which
doesn’t make a difference.

At 110 baud there is the only difference between my algorithm and Jonathan’s code for the commonly used baud rates
because I  pick 512*213 whereas he doesn’t use the 512 prescaler and therefore uses 1024*107 (both values are odd).

The reason why my algoorithm and Jonathan’s have different end results is because I use all possible prescaler combinations and he
doesn’t use the prescalers 64 and 512 which gives different results for baud rates around 100.

Please look at my comparison table at 
https://github.com/nospam2000/ch341-baudrate-calculation/blob/master/patches/README.MD
which is created by the unit test
at https://github.com/nospam2000/ch341-baudrate-calculation/blob/master/patches/check_baud_rates_unittest.c

baud    errOrig errMike errJon  pre*divOrig     pre*divMike     pre*divJon
46      -0.10%  -0.10%  -0.10%  1024*255        1024*255        1024*255
50      +0.16%  +0.16%  +0.16%  1024*234        1024*234        1024*234
75      +0.16%  +0.16%  +0.16%  1024*156        1024*156        1024*156
110     -0.44%  +0.03%  -0.44%  1024*107        512*213         1024*107
135     -0.22%  -0.22%  -0.22%  1024*87         512*174         1024*87
150     +0.16%  +0.16%  +0.16%  1024*78         512*156         1024*78
300     +0.16%  +0.16%  +0.16%  1024*39         512*78          1024*39
600     +0.16%  +0.16%  +0.16%  128*156         128*156         128*156
1200    +0.16%  +0.16%  +0.16%  128*78          64*156          128*78
1800    +0.16%  +0.16%  +0.16%  128*52          64*104          128*52
2400    +0.16%  +0.16%  +0.16%  128*39          64*78           128*39
4800    +0.16%  +0.16%  +0.16%  16*156          16*156          16*156
7200    +0.16%  +0.16%  +0.16%  16*104          8*208           16*104
9600    +0.16%  +0.16%  +0.16%  16*78           8*156           16*78
14400   +0.16%  +0.16%  +0.16%  16*52           8*104           16*52
19200   +0.16%  +0.16%  +0.16%  16*39           8*78            16*39
31250   +0.00%  +0.00%  +0.00%  2*192           2*192           2*192
38400   +0.16%  +0.16%  +0.16%  2*156           2*156           2*156
45450   +0.01%  +0.01%  +0.01%  2*132           2*132           2*132
56000   +0.13%  +0.13%  +0.13%  2*107           1*214           2*107
57600   +0.16%  +0.16%  +0.16%  2*104           1*208           2*104
76800   +0.16%  +0.16%  +0.16%  2*78            1*156           2*78
100000  +0.00%  +0.00%  +0.00%  2*60            1*120           2*60
115200  +0.16%  +0.16%  +0.16%  2*52            1*104           2*52
128000  -0.27%  -0.27%  -0.27%  2*47            1*94            2*47
153846  +0.00%  +0.00%  +0.00%  2*39            1*78            2*39
187500  +0.00%  +0.00%  +0.00%  2*32            1*64            2*32
230400  +0.16%  +0.16%  +0.16%  2*26            1*52            2*26
250000  +0.00%  +0.00%  +0.00%  2*24            1*48            2*24
256000  -2.34%  -0.27%  -0.27%  2*24            1*47            1*47
307200  -2.34%  +0.16%  +0.16%  2*20            1*39            1*39
460800  +0.16%  +0.16%  +0.16%  2*13            1*26            2*13
500000  +0.00%  +0.00%  +0.00%  2*12            1*24            2*12
750000  +0.00%  +0.00%  +0.00%  2*8             1*16            2*8
857143  -0.00%  -0.00%  -0.00%  2*7             1*14            2*7
921600  -6.99%  +0.16%  +0.16%  2*7             1*13            1*13
1000000 +0.00%  +0.00%  +0.00%  2*6             1*12            2*6
1090909 -8.33%  +0.00%  +0.00%  2*6             1*11            1*11
1200000 +0.00%  +0.00%  +0.00%  2*5             1*10            2*5
1333333 -10.00% +0.00%  +0.00%  2*5             1*9             1*9
1500000 +0.00%  +0.00%  +0.00%  2*4             2*4             2*4
2000000 +0.00%  +0.00%  +0.00%  2*3             2*3             2*3
3000000 +0.00%  +0.00%  +0.00%  2*2             2*2             2*2


baud means the requested baud rate
errOrig is the relative error of the resulting baud rate using the original code
errMike is the relative error of the resulting baud rate using my new code
errJon is the relative error of the resulting baud rate using Jonathan Olds' code
pre/divOrig the values of prescaler and divisor using the original code
pre/divMike the values of prescaler and divisor using my new code
pre/divJon the values of prescaler and divisor using Jonathan Olds' code

  Michael


^ permalink raw reply	[flat|nested] 10+ messages in thread
* [PATCH] USB: serial: ch341: fix wrong baud rate setting calculation
@ 2019-06-08  5:13 jontio
  2019-06-20 13:43 ` Johan Hovold
  0 siblings, 1 reply; 10+ messages in thread
From: jontio @ 2019-06-08  5:13 UTC (permalink / raw)
  To: johan, linux-usb, jontio

For some wanted baud rates ch341_set_baudrate_lcr() calculates the "a"
value such that it produces a significantly different baud rate than the
desired one. This means some hardware can't communicate with the CH34x
chip. Particularly obvious wrong baud rates are 256000 and 921600 which
deviate by 2.3% and 7.4% respectively. This proposed patch will bring the
errors for these baud rates to below 0.5%. This patch will significantly
improve the error of some other unusual baud rates too (such as 1333333
from 10% error to 0% error). Currently ch341_set_baudrate_lcr() will
accept any baud rate and can produce a practically arbitrary large error
(for example a 40% error for 5000000) this patch does not address this
issue.

Signed-off-by: jontio <jontio@i4free.co.nz>
---
 drivers/usb/serial/ch341.c | 45 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index 3bb1fff02bed..7cd1d6f70b56 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -54,6 +54,9 @@
 #define CH341_BAUDBASE_FACTOR 1532620800
 #define CH341_BAUDBASE_DIVMAX 3
 
+/* Chip frequency is 12Mhz. not quite the same as (CH341_BAUDBASE_FACTOR>>7) */
+#define CH341_OSC_FREQUENCY 12000000
+
 /* Break support - the information used to implement this was gleaned from
  * the Net/FreeBSD uchcom.c driver by Takanori Watanabe.  Domo arigato.
  */
@@ -168,6 +171,48 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev,
 	factor = 0x10000 - factor;
 	a = (factor & 0xff00) | divisor;
 
+	/*
+	 * Calculate baud error using the 0,1,2,3 LSB and
+	 * also the error without the divisor (LSB==7).
+	 * Decide whether the divisor should be used.
+	 */
+	uint32_t msB = (a>>8) & 0xFF;
+	uint32_t lsB = a & 0xFF;
+	int32_t baud_wanted = priv->baud_rate;
+	uint32_t denom = ((1<<(10-3*lsB))*(256-msB));
+	/*
+	 * baud_wanted==(CH341_OSC_FREQUENCY/256) implies MSB==0 for no divisor
+	 * the 100 is for rounding.
+	 */
+	if (denom && ((baud_wanted+100) >= (((uint32_t)CH341_OSC_FREQUENCY)>>8))) {
+
+		/* Calculate error for divisor */
+		int32_t baud_expected = ((uint32_t)CH341_OSC_FREQUENCY) / denom;
+		uint32_t baud_error_difference = abs(baud_expected-baud_wanted);
+
+		/* Calculate a for no divisor */
+		uint32_t a_no_divisor = ((0x10000-(((uint32_t)CH341_OSC_FREQUENCY)<<8) /
+			baud_wanted+128) & 0xFF00) | 0x07;
+
+		/* a_no_divisor is only valid for MSB<248 */
+		if ((a_no_divisor>>8) < 248) {
+
+			/* Calculate error for no divisor */
+			int32_t baud_expected_no_divisor = ((uint32_t)CH341_OSC_FREQUENCY) /
+				(256-(a_no_divisor>>8));
+			uint32_t baud_error_difference_no_divisor =
+				abs(baud_expected_no_divisor-baud_wanted);
+
+			/*
+			 * If error using no divisor is less than using
+			 * a divisor then use it instead for the "a" word.
+			 */
+			if (baud_error_difference_no_divisor < baud_error_difference)
+				a = a_no_divisor;
+		}
+
+	}
+
 	/*
 	 * CH341A buffers data until a full endpoint-size packet (32 bytes)
 	 * has been received unless bit 7 is set.
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-11-01 19:18 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-01  9:25 [PATCH] USB: serial: ch341: fix wrong baud rate setting calculation Michael Dreher
2019-11-01 14:20 ` Johan Hovold
  -- strict thread matches above, loose matches on Subject: below --
2019-06-08  5:13 jontio
2019-06-20 13:43 ` Johan Hovold
2019-06-29  0:05   ` Jonathan Olds
2019-10-29 19:18     ` Jonathan Olds
2019-10-30  9:47       ` Johan Hovold
2019-11-01  0:17         ` Jonathan Olds
2019-11-01  9:57           ` Johan Hovold
2019-11-01 19:18             ` Jonathan Olds

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).