adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.13k stars 1.22k forks source link

SAMD21-Port: Uart loses characters at 115200 Bit/s #4320

Open wohai opened 3 years ago

wohai commented 3 years ago

Tested on itsybits M0 and QT Py M0. The uart loses characters during reception at 115200 bit/s.

A PC with serial interface is connected to RX of the device and sends 26 characters (a-z) every 300ms. The attached test code reads the data and prints the result, the receive buffer is set to 64 bytes. Th test code waits for the begin of a data packet (while com.in_waiting == 0) and then reads all data it can get with a 100ms timeout. This code works fine with Version 5.3.1 but loses characters on version 6.x.x (tested with stable 6.1.0 and newest build).

Related forum post

Sample output:

b'abcdefghiklmnopqtuvwxyz'
b'abcdefghijlmnopqrsuvwxyz'
b'abcdefghijlmnopqruvwxyz'
b'abcdefghijlmnopqrsuvwxyz'
b'abcdefghijlmnopqrtuvwxyz'
b'abcdefghijlmnopqruvwxyz'

Test code (runs on PC and on itsybitsy M0):

import sys

COMPORT = 'COM4'

if sys.platform.startswith('Atmel'):
    import time
    import busio
    import board
    print('test receiver')
    with busio.UART(
      board.TX,
      board.RX,
      baudrate=115200,
      receiver_buffer_size=64,
      timeout=0.1
    ) as com:
        while True:
            while com.in_waiting == 0:
                time.sleep(0.01)
            data = com.read()
            print(data)
else:
    import time
    import serial
    print('test transmitter')
    with serial.Serial(COMPORT, 115200, timeout=1) as tx:
        data = bytes(range(97,123))
        while True:
            print('sending')
            tx.write(data)
            time.sleep(0.3)
DavePutz commented 3 years ago

I am not sure why CP 5.3.1 is working, but I think I can offer some insight here. The Cortex M0 UART has a 16-byte FIFO buffer. You will note that in your output the first 16 characters (up through the 'q') are correct. Your code has a .01 second loop sleep, which is 10 milliseconds. At a baud rate of 115200, the 26 characters in your test take up about 1.8 milliseconds. So, if those characters arrive while you are sleeping and the FIFO is full... Note that if you reduce the sleep time significantly (i.e. " time.sleep(0.0005)" then CP 6.2 works fine.

dhalbert commented 3 years ago

In 6.x, time.sleep() was changed so that it does a WFI instruction, which simply hangs waiting for an interrupt, in this case either a timer tick (about a msec) or a UART-related interrupt. I don't know whether some behavior of the UART interrupt routine has changed, but was going to take a look at some point.

In your test program, it's best not to sleep at all, as @DavePutz implied. But is the test simulating some larger program which is now suffering dropped characters?

wohai commented 3 years ago

The program I'm working on does not really need sleep, in the first version I used sleep as placeholder for other tasks, but I will go on without sleep. I was not aware that time.sleep goes really into a sleep mode which has impact on interrupt handling, I read about the new alarm module which handles light sleep and deep sleep and thought only this is for the hardware sleep modes. Thank you for the investigation.

tannewt commented 3 years ago

I was not aware that time.sleep goes really into a sleep mode which has impact on interrupt handling

time.sleep doesn't impact interrupt handling because it is woken up by interrupts. The sleep time might be different though because we switched from systick to the RTC for timekeeping.

Generally, UART dropping characters can be solved by increasing the buffer size.

jraynes commented 3 years ago

@tannewt I'm having a similar problem and can't seem to initialize uart library with a buffer size greater than 64:

>>> uart = busio.UART(board.TX,board.RX,baudrate=9600,bits=8, parity=None, stop=1, timeout=10, receiver_buffer_size=96)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Could not initialize UART

I'm talking to a Simcom 7080G LTE Modem and basic AT commands work fine at 64 byte buffer but when I try to enable GPS and output current location the beginning of the output gets cut off:

>>> uart.write(b'at+cgnsinf\r\n');uart.readline()
12
b'6634,-75.1806,65.657,0.00,,0,,0.9,1.2,0.8,,14,,4.8,8.0\r\n'
tannewt commented 3 years ago

@jraynes Does 128 work? Maybe it needs to be a power of two.

jraynes commented 3 years ago

@tannewt it does thank you! Would be nice if the error message was a bit more helpful than "could not initialize" here

tannewt commented 3 years ago

@jraynes can you make a separate issue for improving the error message? That isn't the same as this issue of missed characters.