pcan_pro_x/Src/pcanpro_fd_protocol.c

728 lines
20 KiB
C

#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include "pcanpro_can.h"
#include "pcan_usbpro_fw.h"
#include "pcanfd_usb_fw.h"
#include "pcanpro_timestamp.h"
#include "pcanpro_protocol.h"
#include "pcanpro_led.h"
#include "pcanpro_usbd.h"
#include "usb_device.h"
#define CAN_CHANNEL_MAX (2)
struct pcan_usbfd_fw_info
{
uint16_t size_of; /* sizeof this */
uint16_t type; /* type of this structure */
uint8_t hw_type; /* Type of hardware (HW_TYPE_xxx) */
uint8_t bl_version[3]; /* Bootloader version */
uint8_t hw_version; /* Hardware version (PCB) */
uint8_t fw_version[3]; /* Firmware version */
uint32_t dev_id[2]; /* "device id" per CAN */
uint32_t ser_no; /* S/N */
uint32_t flags; /* special functions */
uint8_t unk[8];
} __attribute__ ((packed));
static struct
{
uint32_t device_nr;
uint32_t last_bus_load_update;
uint32_t last_time_sync;
uint32_t last_time_flush;
uint8_t can_drv_loaded;
uint8_t lin_drv_loaded;
struct
{
/* config */
uint8_t silient;
uint8_t bus_active;
uint8_t loopback;
uint8_t err_mask;
uint32_t channel_nr;
uint16_t opt_mask;
uint8_t led_is_busy;
/* slow speed */
struct ucan_timing_slow slow_br;
/* can fd , data fast speed */
struct ucan_timing_fast fast_br;
/* clock */
uint32_t can_clock;
uint32_t nominal_qt;
uint32_t data_qt;
uint32_t tx_time_ns;
uint32_t rx_time_ns;
uint32_t bus_load;
}
can[CAN_CHANNEL_MAX];
}
pcan_device =
{
.device_nr = 0xFFFFFFFF,
.can[0] =
{
.channel_nr = 0xFFFFFFFF,
.can_clock = 80000000u
},
.can[1] =
{
.channel_nr = 0xFFFFFFFF,
.can_clock = 80000000u
},
};
#define PCAN_USB_DATA_BUFFER_SIZE 2048
static uint8_t resp_buffer[2][PCAN_USB_DATA_BUFFER_SIZE];
static uint8_t drv_load_packet[16];
static uint16_t data_pos = 0;
static uint8_t data_buffer[PCAN_USB_DATA_BUFFER_SIZE];
void *pcan_data_alloc_buffer( uint16_t type, uint16_t size )
{
uint16_t aligned_size = (size+(4-1))&(~(4-1));
if( sizeof( data_buffer ) < (aligned_size+data_pos+4) )
return (void*)0;
struct ucan_msg *pmsg = (void*)&data_buffer[data_pos];
pmsg->size = aligned_size;
pmsg->type = type;
pmsg->ts_low = pcan_timestamp_us();
pmsg->ts_high = 0;
data_pos += aligned_size;
return pmsg;
}
static struct t_m2h_fsm resp_fsm[2] =
{
[0] = {
.state = 0,
.ep_addr = PCAN_USB_EP_CMDIN,
.pdbuf = resp_buffer[0],
.dbsize = PCAN_USB_DATA_BUFFER_SIZE,
},
[1] = {
.state = 0,
.ep_addr = PCAN_USB_EP_MSGIN_CH1,
.pdbuf = resp_buffer[1],
.dbsize = PCAN_USB_DATA_BUFFER_SIZE,
}
};
/* low level requests */
uint8_t pcan_protocol_device_setup( USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req )
{
switch( req->bRequest )
{
case USB_VENDOR_REQUEST_INFO:
switch( req->wValue )
{
case USB_VENDOR_REQUEST_wVALUE_INFO_FIRMWARE:
{
static struct pcan_usbfd_fw_info fwi =
{
.size_of = sizeof( struct pcan_usbfd_fw_info ),
.type = 2,
.hw_type = 1,
#if ( PCAN_X6 == 1 )
.bl_version = { 1, 1, 0 },
.hw_version = 3,
#else
.bl_version = { 2, 1, 0 }, /* bootloader v > 2 support massstorage mode */
.hw_version = 2,
#endif
.fw_version = { 3, 2, 0 },
.dev_id[0] = 0xFFFFFFFF,
.dev_id[1] = 0xFFFFFFFF,
.ser_no = 0xFFFFFFFF,
.flags = 0x00000000,
.unk = {
0x01, /* cmd_out */
0x81, /* cmd_in */
0x02, /* write */
0x03, /* write */
0x82, /* read */
0x00,
0x00,
0x00
}
};
/* windows/linux has different struct size */
fwi.size_of = req->wLength;
fwi.dev_id[0] = pcan_device.can[0].channel_nr;
fwi.dev_id[1] = pcan_device.can[1].channel_nr;
fwi.ser_no = pcan_device.device_nr;
return USBD_CtlSendData( pdev, (void*)&fwi, fwi.size_of );
}
default:
assert(0);
break;
}
break;
case USB_VENDOR_REQUEST_FKT:
switch( req->wValue )
{
case USB_VENDOR_REQUEST_wVALUE_SETFKT_BOOT:
break;
case USB_VENDOR_REQUEST_wVALUE_SETFKT_INTERFACE_DRIVER_LOADED:
{
USBD_CtlPrepareRx( pdev, drv_load_packet, 16 );
return USBD_OK;
}
break;
default:
assert(0);
break;
}
break;
case USB_VENDOR_REQUEST_ZERO:
break;
default:
USBD_CtlError( pdev, req );
return USBD_FAIL;
}
return USBD_FAIL;
}
void pcan_ep0_receive( void )
{
if( drv_load_packet[0] == 0 )
{
pcan_flush_ep( PCAN_USB_EP_MSGIN_CH1 );
pcan_flush_ep( PCAN_USB_EP_CMDIN );
pcan_device.can_drv_loaded = drv_load_packet[1];
pcan_led_set_mode( LED_STAT, LED_MODE_ON, 0 );
}
else
pcan_device.lin_drv_loaded = drv_load_packet[1];
}
int pcan_protocol_rx_frame( uint8_t channel, struct t_can_msg *pmsg )
{
static const uint8_t pcan_fd_len2dlc[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */
9, 9, 9, 9, /* 9 - 12 */
10, 10, 10, 10, /* 13 - 16 */
11, 11, 11, 11, /* 17 - 20 */
12, 12, 12, 12, /* 21 - 24 */
13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */
14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */
14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */
15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */
15, 15, 15, 15, 15, 15, 15, 15 /* 57 - 64 */
};
struct ucan_rx_msg *pcan_msg = pcan_data_alloc_buffer( UCAN_MSG_CAN_RX, sizeof(struct ucan_rx_msg) + pmsg->size );
if( !pcan_msg )
return -1;
if( !(pmsg->flags & MSG_FLAG_ECHO) )
{
uint32_t nqt = pcan_device.can[channel].nominal_qt;
uint32_t dqt = pcan_device.can[channel].data_qt;
pcan_device.can[channel].rx_time_ns += pcan_can_msg_time( pmsg, nqt, dqt );
if( !pcan_device.can[channel].led_is_busy )
{
pcan_led_set_mode( channel ? LED_CH1_RX:LED_CH0_RX, LED_MODE_BLINK_FAST, 237 );
}
}
if( pmsg->size > CAN_PAYLOAD_MAX_SIZE )
pmsg->size = CAN_PAYLOAD_MAX_SIZE;
pcan_msg->channel_dlc = UCAN_MSG_CHANNEL_DLC( channel, pcan_fd_len2dlc[pmsg->size] );
pcan_msg->client = pmsg->dummy;
pcan_msg->flags = 0;
pcan_msg->tag_low = 0;
pcan_msg->tag_high = 0;
/* we support only regular frames */
if( pmsg->flags & MSG_FLAG_RTR )
pcan_msg->flags |= UCAN_MSG_RTR;
if( pmsg->flags & MSG_FLAG_EXT )
pcan_msg->flags |= UCAN_MSG_EXT_ID;
if( pmsg->flags & MSG_FLAG_FD )
{
pcan_msg->flags |= UCAN_MSG_EXT_DATA_LEN;
if( pmsg->flags & MSG_FLAG_BRS )
pcan_msg->flags |= UCAN_MSG_BITRATE_SWITCH;
if( pmsg->flags & MSG_FLAG_ESI )
pcan_msg->flags |= UCAN_MSG_ERROR_STATE_IND;
}
if( pmsg->flags & MSG_FLAG_ECHO )
{
pcan_msg->flags |= UCAN_MSG_API_SRR | UCAN_MSG_HW_SRR;
pcan_msg->ts_low = pcan_timestamp_us();
}
else
{
pcan_msg->ts_low = pmsg->timestamp;
}
pcan_msg->can_id = pmsg->id;
memcpy( pcan_msg->d, pmsg->data, pmsg->size );
return 0;
}
int pcan_protocol_tx_frame_cb( uint8_t channel, struct t_can_msg *pmsg )
{
if( pmsg->flags & MSG_FLAG_ECHO )
{
(void)pcan_protocol_rx_frame( channel, pmsg );
}
if( !pcan_device.can[channel].led_is_busy )
{
pcan_led_set_mode( channel ? LED_CH1_TX:LED_CH0_TX, LED_MODE_BLINK_FAST, 237 );
}
uint32_t nqt = pcan_device.can[channel].nominal_qt;
uint32_t dqt = pcan_device.can[channel].data_qt;
pcan_device.can[channel].tx_time_ns += pcan_can_msg_time( pmsg, nqt, dqt );
return 0;
}
int pcan_protocol_tx_frame( struct ucan_tx_msg *pmsg )
{
static const uint8_t pcan_fd_dlc2len[] =
{
0, 1, 2, 3, 4, 5, 6, 7,
8, 12, 16, 20, 24, 32, 48, 64
};
struct t_can_msg msg = { 0 };
uint8_t channel;
channel = UCAN_MSG_CHANNEL(pmsg);
if( channel >= CAN_CHANNEL_MAX )
return -1;
msg.id = pmsg->can_id;
/* CAN-FD frame */
if( pmsg->flags & UCAN_MSG_EXT_DATA_LEN )
{
msg.flags |= MSG_FLAG_FD;
if( pmsg->flags & UCAN_MSG_BITRATE_SWITCH )
msg.flags |= MSG_FLAG_BRS;
if( pmsg->flags & UCAN_MSG_ERROR_STATE_IND )
msg.flags |= MSG_FLAG_ESI;
msg.size = pcan_fd_dlc2len[UCAN_MSG_DLC(pmsg)];
}
else
{
msg.size = UCAN_MSG_DLC(pmsg);
}
if( msg.size > sizeof( msg.data ) )
return -1;
/* TODO: process UCAN_MSG_SINGLE_SHOT, UCAN_MSG_HW_SRR, UCAN_MSG_ERROR_STATE_IND */
if( pmsg->flags & UCAN_MSG_RTR )
msg.flags |= MSG_FLAG_RTR;
if( pmsg->flags & UCAN_MSG_EXT_ID )
msg.flags |= MSG_FLAG_EXT;
if( pmsg->flags & (UCAN_MSG_API_SRR|UCAN_MSG_HW_SRR) )
{
msg.flags |= MSG_FLAG_ECHO;
msg.dummy = pmsg->client;
}
memcpy( msg.data, pmsg->d, msg.size );
msg.timestamp = pcan_timestamp_us();
if( pcan_can_write( channel, &msg ) < 0 )
{
/* TODO: tx queue overflow ? */
;
}
return 0;
}
static int pcan_protocol_send_status( uint8_t channel, uint8_t status )
{
struct ucan_status_msg *ps = pcan_data_alloc_buffer( UCAN_MSG_STATUS, sizeof( struct ucan_status_msg ) );
if( !ps )
return -1;
ps->channel_p_w_b = channel&0x0f;
ps->channel_p_w_b |= (status&0x0f)<<4;
return 0;
}
int pcan_protocol_set_baudrate( uint8_t channel, struct t_can_bitrate *pbitrate, struct t_can_bitrate *pdata_bitrate )
{
#define PCAN_STM32_SYSCLK_HZ (24000000u)
uint32_t bitrate, pcan_bitrate, pcan_brp;
struct t_can_bitrate *pcur;
if( pbitrate )
pcur = pbitrate;
else if( pdata_bitrate )
pcur = pdata_bitrate;
else
return -1;
pcan_brp = ((PCAN_STM32_SYSCLK_HZ/1000000u)*pcur->brp)/(pcan_device.can[channel].can_clock/1000000u);
pcan_bitrate = (((PCAN_STM32_SYSCLK_HZ)/pcan_brp)/(1/*tq*/ + pcur->tseg1 + pcur->tseg2 ));
bitrate = (((pcan_device.can[channel].can_clock)/pcur->brp)/(1/*tq*/ + pcur->tseg1 + pcur->tseg2 ));
(void)pcan_bitrate;
(void)pcan_brp;
pcan_can_set_bitrate( channel, bitrate, ( pcur == pdata_bitrate ) );
if( pcur == pdata_bitrate )
{
pcan_device.can[channel].data_qt = 1000000000u/bitrate;
}
else
{
pcan_device.can[channel].nominal_qt = 1000000000u/bitrate;
}
return 0;
}
static void pcan_protocol_process_cmd( uint8_t *ptr, uint16_t size )
{
struct ucan_command *pcmd = (void*)ptr;
while( size >= sizeof( struct ucan_command ) )
{
switch( UCAN_CMD_OPCODE( pcmd ) )
{
case UCAN_CMD_NOP:
break;
case UCAN_CMD_RESET_MODE:
{
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
pcan_can_set_bus_active( UCAN_CMD_CHANNEL(pcmd) , 0 );
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].bus_active = 0;
/* update ISO mode only on inactive bus */
uint8_t bus_iso_mode = ( pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].opt_mask & UCAN_OPTION_ISO_MODE ) != 0;
pcan_can_set_iso_mode( UCAN_CMD_CHANNEL(pcmd), bus_iso_mode );
}
}
break;
case UCAN_CMD_NORMAL_MODE:
{
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
if( pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].silient )
{
pcan_can_set_silent( UCAN_CMD_CHANNEL(pcmd) , 0 );
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].silient = 0;
}
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].bus_active = 1;
pcan_can_set_bus_active( UCAN_CMD_CHANNEL(pcmd) , 1 );
pcan_protocol_send_status( UCAN_CMD_CHANNEL(pcmd), 0 );
}
}
break;
case UCAN_CMD_LISTEN_ONLY_MODE:
{
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
pcan_can_set_silent( UCAN_CMD_CHANNEL(pcmd) , 1 );
pcan_can_set_bus_active( UCAN_CMD_CHANNEL(pcmd) , 1 );
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].silient = 1;
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].bus_active = 1;
pcan_protocol_send_status( UCAN_CMD_CHANNEL(pcmd), 0 );
}
}
break;
case UCAN_CMD_TIMING_SLOW:
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
struct t_can_bitrate br;
struct ucan_timing_slow *ptiming = (void*)pcmd;
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].slow_br = *ptiming;
br.brp = ptiming->brp + 1;
br.tseg1 = ptiming->tseg1 + 1;
br.tseg2 = ptiming->tseg2 + 1;
br.sjw = (ptiming->sjw_t & 0x0f)+1;
(void)((ptiming->sjw_t&0x80) != 0);
pcan_protocol_set_baudrate( UCAN_CMD_CHANNEL(pcmd), &br, 0 );
}
break;
/* only for CAN-FD */
case UCAN_CMD_TIMING_FAST:
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
struct t_can_bitrate br;
struct ucan_timing_fast *ptiming = (void*)pcmd;
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].fast_br = *ptiming;
br.brp = ptiming->brp + 1;
br.tseg1 = ptiming->tseg1 + 1;
br.tseg2 = ptiming->tseg2 + 1;
br.sjw = (ptiming->sjw & 0x0f)+1;
pcan_protocol_set_baudrate( UCAN_CMD_CHANNEL(pcmd), 0, &br );
}
break;
case UCAN_CMD_SET_STD_FILTER:
if( UCAN_CMD_CHANNEL(pcmd) >= CAN_CHANNEL_MAX )
break;
pcan_can_set_filter_mask( UCAN_CMD_CHANNEL(pcmd), 0, 0, 0, 0 );
break;
case UCAN_CMD_RESERVED2:
break;
case UCAN_CMD_FILTER_STD:
if( UCAN_CMD_CHANNEL(pcmd) >= CAN_CHANNEL_MAX )
break;
pcan_can_set_filter_mask( UCAN_CMD_CHANNEL(pcmd), 0, 0, 0, 0 );
break;
case UCAN_CMD_TX_ABORT:
break;
case UCAN_CMD_WR_ERR_CNT:
break;
case UCAN_CMD_SET_EN_OPTION:
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
struct ucan_option *popt = (void*)pcmd;
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].opt_mask |= popt->mask;
}
break;
case UCAN_CMD_CLR_DIS_OPTION:
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
struct ucan_option *popt = (void*)pcmd;
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].opt_mask &= ~popt->mask;
}
break;
case UCAN_CMD_SET_ERR_GEN1:
break;
case UCAN_CMD_SET_ERR_GEN2:
break;
case UCAN_CMD_DIS_ERR_GEN:
break;
case UCAN_CMD_RX_BARRIER:
break;
case UCAN_CMD_SET_ERR_GEN_S:
break;
case UCAN_USB_CMD_CLK_SET:
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
struct ucan_usb_clock *pclock = (void*)pcmd;
switch( pclock->mode )
{
default:
case UCAN_USB_CLK_80MHZ:
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].can_clock = 80000000u;
break;
case UCAN_USB_CLK_60MHZ:
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].can_clock = 60000000u;
break;
case UCAN_USB_CLK_40MHZ:
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].can_clock = 40000000u;
break;
case UCAN_USB_CLK_30MHZ:
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].can_clock = 30000000u;
break;
case UCAN_USB_CLK_24MHZ:
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].can_clock = 24000000u;
break;
case UCAN_USB_CLK_20MHZ:
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].can_clock = 20000000u;
break;
}
}
break;
case UCAN_USB_CMD_LED_SET:
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
struct ucan_usb_led *pled = (void*)pcmd;
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].led_is_busy = pled->mode;
pcan_led_set_mode( UCAN_CMD_CHANNEL(pcmd), pled->mode, 0xFFFFFFFF );
}
break;
case UCAN_USB_CMD_DEVID_SET:
{
struct ucan_usb_device_id *pdevid = (void*)pcmd;
if( UCAN_CMD_CHANNEL(pcmd) < CAN_CHANNEL_MAX )
{
pcan_device.can[UCAN_CMD_CHANNEL(pcmd)].channel_nr = pdevid->device_id;
}
break;
}
case 0x87: /* CAN FD ISO MODE, 0xff - enable, 0x55 - disable */
{
/* do nothing here */
break;
}
case UCAN_CMD_END_OF_COLLECTION:
return;
default:
assert( 0 );
break;
}
size -= sizeof( struct ucan_command );
++pcmd;
}
}
void pcan_protocol_process_data( uint8_t ep, uint8_t *ptr, uint16_t size )
{
if( ep == 1 )
{
pcan_protocol_process_cmd( ptr, size );
return;
}
/* message data ? */
struct ucan_msg *pmsg = 0;
while( size )
{
if( size < 4 )
break;
pmsg = (void*)ptr;
if( !pmsg->size || !pmsg->type )
break;
if( size < pmsg->size )
break;
size -= pmsg->size;
ptr += pmsg->size;
switch( pmsg->type )
{
//to host only
//UCAN_MSG_ERROR:
//UCAN_MSG_BUSLOAD:
case UCAN_MSG_CAN_TX:
pcan_protocol_tx_frame( (struct ucan_tx_msg *)pmsg );
break;
case UCAN_MSG_CAN_TX_PAUSE:
/* TODO: */
break;
case UCAN_CMD_END_OF_COLLECTION:
case 0xffff:
return;
default:
assert( 0 );
break;
}
}
}
void pcan_protocol_init( void )
{
pcan_can_init_ex( CAN_BUS_1, 500000 );
pcan_can_set_filter_mask( CAN_BUS_1, 0, 0, 0, 0 );
pcan_can_init_ex( CAN_BUS_2, 500000 );
pcan_can_set_filter_mask( CAN_BUS_2, 0, 0, 0, 0 );
pcan_can_set_iso_mode( CAN_BUS_1, 0 );
pcan_can_set_iso_mode( CAN_BUS_2, 0 );
pcan_can_install_rx_callback( CAN_BUS_1, pcan_protocol_rx_frame );
pcan_can_install_rx_callback( CAN_BUS_2, pcan_protocol_rx_frame );
pcan_can_install_tx_callback( CAN_BUS_1, pcan_protocol_tx_frame_cb );
pcan_can_install_tx_callback( CAN_BUS_2, pcan_protocol_tx_frame_cb );
}
void pcan_protocol_poll( void )
{
uint32_t ts_ms = pcan_timestamp_millis();
uint32_t ts_us = pcan_timestamp_us();
pcan_can_poll();
/* flush data */
if( data_pos > 0 )
{
/* endmark */
*(uint32_t*)&data_buffer[data_pos] = 0x00000000;
uint16_t flush_size = data_pos + 4;
/* align to 64 */
flush_size += (64-1);
flush_size &= ~(64-1);
int res = pcan_flush_data( &resp_fsm[1], data_buffer, flush_size );
if( res )
{
data_pos = 0;
pcan_device.last_time_flush = ts_us;
}
}
#if 0
else
{
ts_us -= pcan_device.last_time_flush;
if( pcan_device.last_time_flush && ( ts_us > 800 ) )
{
int res = pcan_flush_data( &resp_fsm[1], 0, 0 );
if( res )
{
pcan_device.last_time_flush = 0;
}
}
}
#endif
/* timesync part */
if( !pcan_device.can_drv_loaded )
return;
/* update bus load each 250ms */
if( ( ts_ms - pcan_device.last_bus_load_update ) >= 250u )
{
pcan_device.last_bus_load_update = ts_ms;
for( int i = 0; i < CAN_CHANNEL_MAX; i++ )
{
uint32_t total_ns = pcan_device.can[i].tx_time_ns + pcan_device.can[i].rx_time_ns;
/* get bus in percents 0 - 100% */
pcan_device.can[i].bus_load = total_ns / (250000000u/100u);
pcan_device.can[i].tx_time_ns = 0;
pcan_device.can[i].rx_time_ns = 0;
}
}
if( ( ts_ms - pcan_device.last_time_sync ) < 1000u )
return;
struct ucan_usb_ts_msg *pts = pcan_data_alloc_buffer( UCAN_USB_MSG_CALIBRATION, sizeof( struct ucan_usb_ts_msg ) );
if( !pts )
return;
pts->usb_frame_index = pcan_usb_frame_number();
pts->unused = 0;
pcan_device.last_time_sync = ts_ms;
for( int i = 0; i < CAN_CHANNEL_MAX; i++ )
{
if( !(pcan_device.can[i].opt_mask & UCAN_OPTION_BUSLOAD) )
continue;
if( !pcan_device.can[i].bus_active )
continue;
struct ucan_bus_load_msg *pbs = pcan_data_alloc_buffer( UCAN_MSG_BUSLOAD, sizeof( struct ucan_bus_load_msg ) );
if( !pbs )
return;
pbs->channel = i;
/* 0 ... 100% => 0 ... 4095 */
pbs->bus_load = (pcan_device.can[i].bus_load*4095u)/100u;
}
}