An Improved usb-serial firmware for the ATMega 16U2 (& 8U2)

364 views
Skip to first unread message

urjaman

unread,
Jun 8, 2015, 2:51:04 AM6/8/15
to devel...@arduino.cc
Hi,

https://github.com/urjaman/fast-usbserial

I'm announcing this so somebody else doesnt do the same work again - although I didnt check with anyone before starting, but whatever.
Hopefully I'm in the right place, sorry if not.

My testing shows it should be capable of 2MBaud full-duplex (unlike the arduino-usbserial that doesnt even finish my full-duplex test at 115200),
but I'd like to hear more testing from other people.

So this is a stand-alone project derived from Arduino-usbserial and that old LUFA, but i knew i'd need to modify LUFA so the bits
of it that were needed are included into the project.

Maybe if people like this I'd be OK if a version of this was essentially upstreamed in place of Arduino-usbserial, but I wont be pushing for that.
I dont have the time for long-term maintenance of such stuff.

--
I havent yet figured out what to sign off with...

Mattias Schlenker

unread,
Jun 8, 2015, 2:55:18 AM6/8/15
to devel...@arduino.cc
This might fix issue 2963 https://github.com/arduino/Arduino/issues/2963 and thus should allow for proper 230400 on the Uno.

--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.

Darryl Piper

unread,
Jun 8, 2015, 7:31:06 AM6/8/15
to devel...@arduino.cc
the usbserial code for the 16u2 and 8u2 is broken....  its incorrectly ignores that the calculated baud rate setting might be greater than 4095.  thus it doesnt switch into the use_2x mode correctly on some baud_rates.

basically this is not good

    /* Must turn off USART before reconfiguring it, otherwise incorrect operation may occur */
    UCSR1B = 0;
    UCSR1A = 0;
    UCSR1C = 0;

    /* Special case 57600 baud for compatibility with the ATmega328 bootloader. */   
    UBRR1  = (CDCInterfaceInfo->State.LineEncoding.BaudRateBPS == 57600)
             ? SERIAL_UBBRVAL(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS)
             : SERIAL_2X_UBBRVAL(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS);   

    UCSR1C = ConfigMask;
    UCSR1A = (CDCInterfaceInfo->State.LineEncoding.BaudRateBPS == 57600) ? 0 : (1 << U2X1);
    UCSR1B = ((1 << RXCIE1) | (1 << TXEN1) | (1 << RXEN1));




i ended up using code like this....


    UCSR1B = 0;
    UCSR1A = 0;
    UCSR1C = 0;

    /* calculate which way to program the serial port is best */
    uint8_t use2x;
    uint16_t baud_setting;
    int32_t baud = CDCInterfaceInfo->State.LineEncoding.BaudRateBPS;

    int32_t baud_setting_not = ((F_CPU + (8L * baud)) / (16L * baud)) - 1L;
    int32_t baud_setting_u2x = ((F_CPU + (4L * baud)) / ( 8L * baud)) - 1L;
    if ( abs((int32_t)((F_CPU / (16L*(baud_setting_not + 1L))) - baud)) <= abs((int32_t)((F_CPU / (8L*(baud_setting_u2x + 1L))) - baud)) ) {
        use2x = 0;
        baud_setting = baud_setting_not;
    }
    else {
        use2x = (1 << U2X1);
        baud_setting = baud_setting_u2x;
    }

    /* make sure we dont go out of range of the 12 bit UBRR register */
    if ( use2x == 0 && baud_setting >= 4096 ) {
        /* hope the error isnt too much ! */
        use2x = (1 << U2X1);
        baud_setting = baud_setting_u2x;
    }

    /* Special case 57600 baud for compatibility with the ATmega328 bootloader. */
    if ( baud == 57600 ) {
        /* have to switch back to this larger error value */
        baud_setting = baud_setting_not;
        use2x = 0;
    }

    UCSR1C = ConfigMask;
    UBRR1 = baud_setting;
    UCSR1A = use2x;
    UCSR1B = ((1 << RXCIE1) | (1 << TXEN1) | (1 << RXEN1));



and I run my UnoR3 and mega 2560 both 8u2 and 16u2 versions nearly always at 1M baud.  and your test code in ( https://github.com/arduino/Arduino/issues/2963 )works on my UnoR3 board.


Darryl




--
--
 Darryl

Darryl Piper

unread,
Jun 8, 2015, 8:14:45 AM6/8/15
to devel...@arduino.cc
argh, just realised  I dont use the standard hardware serial routines !  

dont forget the UDR0 register is for both reading and writing,  and at high speed. the UCSR0A bit 6 is set when data has been transmited out and no data is waiting in the UDR0 register.

so blindly reading data from it even when data is flagged as being ready by bit 7 of UCSR0A.  doesnt work.  test the TXC0 flag, and then you can read the right data.

--
--
 Darryl

Urja Rannikko

unread,
Jun 8, 2015, 10:27:45 AM6/8/15
to devel...@arduino.cc
Hey, thanks for passing my mail through the filters :), but ...

On Mon, Jun 8, 2015 at 3:14 PM, Darryl Piper
<darryl...@googlemail.com> wrote:
> argh, just realised I dont use the standard hardware serial routines !
>
> dont forget the UDR0 register is for both reading and writing, and at high
> speed. the UCSR0A bit 6 is set when data has been transmited out and no data
> is waiting in the UDR0 register.
>
> so blindly reading data from it even when data is flagged as being ready by
> bit 7 of UCSR0A. doesnt work. test the TXC0 flag, and then you can read
> the right data.
>
>
Suddenly, we're not even talking about my firmware and we're talking
foobar. Please no.

The UART (well USART, but we use UART features...) has a shift
register and a buffer register both directions, UDR mapped to the
proper buffer register for a given direction. UDRE will give the
status of the TX buffer register so you have a byte of time to fill it
without losing full speed transmission (if it didnt exist, it would be
practically impossible to send at full speed). TXC will flag when the
TX shift register is empty if you're waiting to do something when all
data really is out (like go to sleep or turn off UART).
RXC flags when data is moved from RX shift register to RX buffer
register, so you again have one byte of time to take it out (again,
2Mbaud receive would be ~impossible without it, given you'd have one
bit time (8 cycles!) to take the data out ...)

> On 8 June 2015 at 12:31, Darryl Piper <darryl...@googlemail.com> wrote:
>>
>> the usbserial code for the 16u2 and 8u2 is broken.... its incorrectly
>> ignores that the calculated baud rate setting might be greater than 4095.
>> thus it doesnt switch into the use_2x mode correctly on some baud_rates.

The default code uses U2X for all baud rates except 57600, what are
you talking about?
... oh you mean it should switch off U2X in the low end... (U2X
increases baudrate given divisor, so increases minimum baudrate, maybe
you have this inside out?...)

I (already before, just look at my github) improved the code a tiny
bit to turn off U2X for those rates which have no improvement in the
baudrate error with U2X (for better noise performance), but i'll need
to check if it applies to this one case i figured out.

At 16Mhz U2X: 16M / 8 / 4096, max baudrate 488 .... baud. Okay so
baudrates lower than 488 will fail.
But 16Mhz without U2X is just max 244 baud,
so what standard rates didnt work (that could): 300 baud.
I'll go and check that, but it wasnt part of my goals for a _fast_ usbserial :P


--
Urja Rannikko

Andrew Kroll

unread,
Jun 9, 2015, 3:49:33 AM6/9/15
to devel...@arduino.cc
I'm fine on the mega2560 at 1Mbaud, however I've noticed here that 2MBaud sometimes will cause the 16u2 to actually lock up when there is too much going on in both directions at the same time.

I suspect this is because it suffers from a stack smash into the heap and variables, and I have seen this also happen if the USB supply voltage is a touch too low.

I've not had the chance to throw the Dragon at it to track it down exactly.

Currently 1MBaud seems to be OK to use for what I am working on, but faster rates are always better.


--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.



--
Visit my github for awesome Arduino code @ https://github.com/xxxajk

Darryl Piper

unread,
Jun 15, 2015, 11:10:58 AM6/15/15
to devel...@arduino.cc
Okay,  have put one my Uno r3, back to stock,  and that other post sure enough shows errors on high baud rates,  changed the 16U2 firmware to use your code,  and even at 1M baud no errors on that test.  So your code looking good.

one thing I did add into your code was the ID's for the common r3 Uno and Mega 2560 ( which I have )

along the lines of

    #if (ARDUINO_MODEL_PID == ARDUINO_UNO_R3_PID)
        .Header                 = {.Size = USB_STRING_LEN(15), .Type = DTYPE_String},
        .UnicodeString          = L"Arduino Uno R3+"
    #elif (ARDUINO_MODEL_PID == ARDUINO_UNO_PID)
        .Header                 = {.Size = USB_STRING_LEN(11), .Type = DTYPE_String},
        .UnicodeString          = L"Arduino Uno"
    #elif (ARDUINO_MODEL_PID == ARDUINO_MEGA2560_PID)
        .Header                 = {.Size = USB_STRING_LEN(17), .Type = DTYPE_String},
        .UnicodeString          = L"Arduino Mega 2560"   
    #elif (ARDUINO_MODEL_PID == ARDUINO_MEGA2560_R3_PID)
        .Header                 = {.Size = USB_STRING_LEN(19), .Type = DTYPE_String},
        .UnicodeString          = L"Arduino Mega 2560r3"
    #endif


with bits added to Descriptors.h and that into Descriptors.c   also added the ARDUINO_MODEL_PID  and ARDUINO_VENDOR_ID to mimic the USB id of the stock items, so it didnt want or need another driver adding.

So your code looks good,  tests fine for me.
--
 Darryl

Urja Rannikko

unread,
Jun 18, 2015, 5:45:25 PM6/18/15
to devel...@arduino.cc
On Thu, Jun 18, 2015 at 7:10 PM, Michael Schwager <mi...@schwager.com> wrote:
> ...Maybe I misunderstood the purpose of this library. It is not for running
> sketches on a Leonardo or what have you, but rather it is for the USB -to-
> serial board, correct?
Correct. It is meant for the ATmega *U2 in an usb-serial converter
only helper role,
eg in the Uno R3 and Mega 2560.

Just some rationale for the hack:
The register usage hack wins 3 regs * 2 cycles per push/pop * 2 (push and pop)
=> 12 minus 2 (movw * 2) => 10 cycles out of the 20 cycles that are
available for a single byte operation at 2Mbaud: 80 cycles / byte
transfer time, but full duplex => 40 cycles byte. One operation to
move USART <-> memory and one memory <-> USB controller so 20 cycles
/ byte op, but can be slightly asymmetric if one faster than the
other...

IIRC currently it is 9 cycles/byte (+ per packet overhead but that is
not per byte but per 32 or 64 bytes) for the USB <-> memory transfers
and 20 for USART->memory ISR and like 25 for memory->USART ISR.


>
> In that case my work was interesting, but doesn't apply because you're
> right- there's not enough register pressure to utilize the lower registers.
>

--
Urja Rannikko

David Holmes

unread,
Jun 19, 2015, 2:58:12 AM6/19/15
to devel...@arduino.cc

I got it working. Under the preference setting I had selected external program only

David Holmes

Urja Rannikko

unread,
Jun 19, 2015, 8:17:26 PM6/19/15
to devel...@arduino.cc
If you want to add the IDs whilst keeping the compilation default as
the demo application,
feel free to make a pull request with them on github. I mean they're
not a secret since you can look them up online...

AFAIK since I'm not Arduino.cc I'm not supposed to release firmware
that actually uses
the official Arduino IDs - and also the original source code didnt
include the IDs and linux doesnt care so I didnt touch them.

--
Urja Rannikko
Reply all
Reply to author
Forward
0 new messages