CANbus bitrate and frame loss

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
Post Reply
artur
Posts: 12
Joined: Thu Dec 08, 2016 8:05 pm

CANbus bitrate and frame loss

Post by artur » Wed Jan 23, 2019 10:05 pm

Hi, I am having trouble with CAN frames getting lost when sending them back-to-back.

The setup consists of two pyboards interconnected via CAN bus through CAN transceivers (TI SN65HVD231)

For easy reproduction of this problem, copy the following code into the main.py of each board and on the REPL run slave() on one board and master() on the other. After few seconds hit Ctrl-C and compare the count of sent vs received frames.

Results for different bitrates:

125k (prescaler=16, sjw=1, bs1=14, bs2=6) - works OK
250k (prescaler=8, sjw=1, bs1=14, bs2=6) - does not work at all
500k (prescaler=4, sjw=1, bs1=14, bs2=6) - 0.2% frames lost (not received)
1M (prescaler=2, sjw=1, bs1=14, bs2=6) - 0.6% frames lost (not received)

Code: Select all

import pyb
can = pyb.CAN(1, mode=pyb.CAN.NORMAL, prescaler=2, sjw=1, bs1=14, bs2=6)
can.setfilter(0, pyb.CAN.LIST16, 0, (123, 124, 125, 126))

def master():
    count = 0
    try:
        while True:
            can.send("message!", 123, timeout=1000)
            count += 1
    except KeyboardInterrupt:
        print(count)

def slave():
    count = 0
    try:
        while True:
            if can.any(0):
                can.recv(0)
                count += 1
    except KeyboardInterrupt:
        print(count)

jickster
Posts: 629
Joined: Thu Sep 07, 2017 8:57 pm

Re: CANbus bitrate and frame loss

Post by jickster » Thu Jan 24, 2019 2:11 am

Did you put terminating resistors?


Sent from my iPhone using Tapatalk Pro

artur
Posts: 12
Joined: Thu Dec 08, 2016 8:05 pm

Re: CANbus bitrate and frame loss

Post by artur » Thu Jan 24, 2019 8:34 am

Here is the setup. (This setup implementes a redundant bus, but only one is used for this test, e.g. the upper one):

Image

artur
Posts: 12
Joined: Thu Dec 08, 2016 8:05 pm

Re: CANbus bitrate and frame loss

Post by artur » Thu Jan 24, 2019 10:37 pm

The loss of frames is due to the receiving pyboard not able to process all frames. When adding delay between sending of frames, this improves. For example, with 200 us, the 500 kbps has no frames lost, and with 300 us also the 1 Mbps rate has no frame lost.

I should also add that a connected CAN sniffer (usb2can) is able to receive all frames, at 1 Mpbs without delay. So obviously the bottleneck is the receiving side.

Can the swj, bs1, and bs2 settings be changed to improve receiving? I tried some changes, but none improved the situation.

Below are the code snippets for one-way sending (with optional delay) and ping-pong (where each node sends a frame after one was received).

one-way:

Code: Select all

import pyb
import time

DELAY = 0 * 100  # delay in us between frames
BR = 1000  # bitrate in Kbps
can = pyb.CAN(1, mode=pyb.CAN.NORMAL, prescaler=2000//BR, sjw=1, bs1=14, bs2=6)
can.setfilter(0, pyb.CAN.LIST16, 0, (123, 124, 125, 126))

def m():
    count = 0
    start = time.ticks_us()
    try:
        while True:
            if DELAY:
                pyb.udelay(DELAY)
            can.send("message!", 123, timeout=1000)
            count += 1
    except KeyboardInterrupt:
        print("frames:", count)
        duration = time.ticks_diff(time.ticks_us(), start) / 1000
        print("%s kbps" % (count * 8 * 8 / duration))

def s():
    count = 0
    start = time.ticks_us()
    try:
        while True:
            if can.any(0):
                can.recv(0)
                count += 1
    except KeyboardInterrupt:
        master_count = int(input("frames from master: "))
        print("frames:", count)
        loss = (100 * (master_count - count) / master_count)
        print("loss (%):", loss)

ping-pong:

Code: Select all

import pyb
import time

BR = 1000  # bitrate in Kbps
can = pyb.CAN(1, mode=pyb.CAN.NORMAL, prescaler=2000//BR, sjw=1, bs1=14, bs2=6)
can.setfilter(0, pyb.CAN.LIST16, 0, (123, 124, 125, 126))

def m():
    count = 0
    start = time.ticks_us()
    try:
        while True:
            can.send("message!", 123, timeout=1000)
            count += 1
            while not can.any(0):
                pass
            while can.any(0):
                can.recv(0)
                count += 1
    except KeyboardInterrupt:
        print("frames:", count)
        duration = time.ticks_diff(time.ticks_us(), start) / 1000
        print("%s kbps" % (count * 8 * 8 / duration))

def s():
    count = 0
    start = None
    try:
        while True:
            if can.any(0):
                if start is None:
                    start = time.ticks_us()
                can.recv(0)
                count += 1
                can.send("message!", 123, timeout=1000)
                count += 1
    except KeyboardInterrupt:
        print("frames:", count)

artur
Posts: 12
Joined: Thu Dec 08, 2016 8:05 pm

Re: CANbus bitrate and frame loss

Post by artur » Fri Feb 01, 2019 12:38 pm

What puzzles me however is that there is real frame loss at high baud rates (>=500 kbps) when sending non-stop.

For a frame to be placed on the CAN bus, it must be acknowledged by at least one other node (that sends an active ACK bit). Obviously the CAN controller of the receiving pyboard must have sent such ACK, but then fails to further transfer the frame into its fifo buffer.

Should this not raise an Exception?

chrismas9
Posts: 152
Joined: Wed Jun 25, 2014 10:07 am

Re: CANbus bitrate and frame loss

Post by chrismas9 » Sat Feb 02, 2019 12:10 am

This may not affect packet loss but I see two hardware issues that may affect reliability.

There is no GND connection between the two devices on the bus. Without a GND you rely on the ESD clamp diodes to try and maintain the signals within the common mode range which is not reliable.

There is no termination on pin 8 Rs. This is an undefined mode on the 231 but will probably give a slew rate of less than 2 V/uS which is not enough for 1 mbaud.

jickster
Posts: 629
Joined: Thu Sep 07, 2017 8:57 pm

Re: CANbus bitrate and frame loss

Post by jickster » Sat Feb 02, 2019 3:20 am

artur wrote:What puzzles me however is that there is real frame loss at high baud rates (>=500 kbps) when sending non-stop.

For a frame to be placed on the CAN bus, it must be acknowledged by at least one other node (that sends an active ACK bit). Obviously the CAN controller of the receiving pyboard must have sent such ACK, but then fails to further transfer the frame into its fifo buffer.

Should this not raise an Exception?

You’re assuming a lot about the implementation of this driver.

The fact that you only stop losing frames if you introduce a delay is confirmation that there is no buffer implemented.


Sent from my iPhone using Tapatalk Pro

artur
Posts: 12
Joined: Thu Dec 08, 2016 8:05 pm

Re: CANbus bitrate and frame loss

Post by artur » Tue Feb 05, 2019 8:49 pm

jickster wrote:
Sat Feb 02, 2019 3:20 am
The fact that you only stop losing frames if you introduce a delay is confirmation that there is no buffer implemented.
You are right. Handling the frame reception in a callback rather than in the main loop solves the issue. Now there is no frame loss even for 1 Mbps.

Code: Select all

def s():
    global count
    data = [0, 0, 0, memoryview(bytearray(8))]

    def callback(bus, reason):
        global count
        count += 1
        bus.recv(0, list=data)
        if reason == 0:
            pass
        elif reason == 1:
            pass  # fifo full
        elif reason == 2:
            # fifo overflow
            raise Exception("fifo overflow")

    can.rxcallback(0, callback)
    try:
        while True:
            pass
    except KeyboardInterrupt:
        master_count = int(input("frames from master: "))
        print("frames:", count)
        loss = (100 * (master_count - count) / master_count)
        print("loss (%):", loss)

Post Reply