#include "pcan_can.h" #include "pcan_led.h" #include "pcan_packet.h" #include "pcan_timestamp.h" #include "pcan_usb.h" #include "punker.h" #include #include #include #define SJA1000_BASICCAN 0 #define SJA1000_PELICAN 1 static struct { uint8_t bus_active; uint8_t ext_vcc_state; uint8_t led_mode; uint16_t last_timestamp_sync; uint16_t last_flush; struct { /* error handling related */ uint8_t err; uint8_t ecc; uint8_t rx_err; uint8_t tx_err; /* config */ uint8_t btr0; uint8_t btr1; uint8_t silient; uint8_t loopback; uint8_t err_mask; uint32_t quartz_freq; } can; uint8_t sja1000_shadow[6]; uint8_t sja1000_ic_mode; uint8_t device_id; uint32_t device_serial; } pcan_device = { .device_id = 0xFF, .device_serial = 0xFFFFFFFF, .can = { .quartz_freq = PCAN_USB_CRYSTAL_HZ, .loopback = 0, }, }; #define BLOCK_SIZE (64) #define HEADER_SIZE (2) #define PCAN_MAX_RECORD_SIZE (BLOCK_SIZE * 14) typedef struct { /* raw data array */ uint8_t buffer[PCAN_MAX_RECORD_SIZE]; /* ts counter, need to provide correct timestamp correction ( 2 or 1 byte ) */ uint16_t ts_counter; uint16_t pos; } PCAN_RECORD_BUFFER_EX; static uint8_t temp_data_buffer[PCAN_MAX_RECORD_SIZE] = { 0 }; static struct t_m2h_fsm data_fsm = { .state = 0, .ep_addr = PCAN_USB_EP_MSGIN, .pdbuf = temp_data_buffer, .dbsize = sizeof(temp_data_buffer), }; static PCAN_RECORD_BUFFER_EX pcan_records; void pcan_sja1000_write(uint8_t reg_addr, uint8_t value) { if (pcan_device.sja1000_ic_mode == SJA1000_PELICAN) { reg_addr &= 0x7F; switch (reg_addr) { case 6: pcan_device.sja1000_shadow[2] = value; break; case 7: pcan_device.sja1000_shadow[3] = value; break; case 14: pcan_device.sja1000_shadow[4] = value; break; case 15: pcan_device.sja1000_shadow[5] = (value == 0xFF) ? 0x7F : value; break; case 16: pcan_device.sja1000_shadow[0] = value; break; case 20: pcan_device.sja1000_shadow[1] = value; break; case 31: if ((value & 0x80) == 0) { pcan_device.sja1000_ic_mode = SJA1000_BASICCAN; } break; } } else if (pcan_device.sja1000_ic_mode == SJA1000_BASICCAN) { reg_addr &= 0x1F; switch (reg_addr) { case 4: pcan_device.sja1000_shadow[0] = value; break; case 5: pcan_device.sja1000_shadow[1] = value; break; case 6: pcan_device.sja1000_shadow[2] = value; break; case 7: pcan_device.sja1000_shadow[3] = value; break; case 31: if ((value & 0x80) != 0) { pcan_device.sja1000_ic_mode = SJA1000_PELICAN; } break; } } } uint8_t pcan_sja1000_read(uint8_t reg_addr) { if (pcan_device.sja1000_ic_mode == SJA1000_PELICAN) { reg_addr &= 0x7F; switch (reg_addr) { case 0: return 0x01; case 6: return pcan_device.sja1000_shadow[2]; case 7: return pcan_device.sja1000_shadow[3]; case 14: return pcan_device.sja1000_shadow[4]; case 15: return pcan_device.sja1000_shadow[5]; case 16: return pcan_device.sja1000_shadow[0]; case 20: return pcan_device.sja1000_shadow[1]; case 31: return 0x80; /* PeliCAN mode */ default: return 0; } } else if (pcan_device.sja1000_ic_mode == SJA1000_BASICCAN) { reg_addr &= 0x1F; switch (reg_addr) { case 0: return 0x01; case 4: return pcan_device.sja1000_shadow[0]; case 5: return pcan_device.sja1000_shadow[1]; case 6: return pcan_device.sja1000_shadow[2]; case 7: return pcan_device.sja1000_shadow[3]; case 31: return 0x00; /* BasicCAN mode */ default: return 0; } } return 0; } void pcan_record_write_header(uint8_t *p) { p[0] = 0x02; /* record header */ p[1] = 0x00; /* record count */ } void pcan_record_buffer_reset(PCAN_RECORD_BUFFER_EX *prec) { pcan_record_write_header(prec->buffer); prec->ts_counter = 0; prec->pos = HEADER_SIZE; } /* request free slot for specific amount of data */ uint8_t *pcan_record_buffer_request(PCAN_RECORD_BUFFER_EX *prec, uint16_t size) { if (size > (BLOCK_SIZE - HEADER_SIZE)) return 0; uint16_t unused = BLOCK_SIZE - (prec->pos & (BLOCK_SIZE - 1)); /* allocate new slot ? */ if (unused < size) { /* align data to next slot bound */ uint8_t next = (prec->pos + (BLOCK_SIZE - 1)) & (~((BLOCK_SIZE - 1))); if ((next + HEADER_SIZE) >= PCAN_MAX_RECORD_SIZE) return 0; memset(&prec->buffer[next], 0x00, BLOCK_SIZE); pcan_record_write_header(&prec->buffer[next]); next += 2; prec->pos = next; /* reset ts_counter for next buffer */ prec->ts_counter = 0; } if ((prec->pos + size) > PCAN_MAX_RECORD_SIZE) return (void *)0; return &prec->buffer[prec->pos]; } void pcan_record_buffer_commit(PCAN_RECORD_BUFFER_EX *prec, uint16_t size) { uint16_t pos = prec->pos & (~(BLOCK_SIZE - 1)); /* increse record count */ ++prec->buffer[pos + 1]; prec->pos += size; } uint16_t pcan_record_buffer_flush(PCAN_RECORD_BUFFER_EX *prec) { uint16_t ts_ticks = pcan_timestamp_ticks(); int res, flush_size; if (prec->pos <= HEADER_SIZE) { /* try to send ZLP, original 800 */ if (pcan_device.last_flush && (((uint16_t)(ts_ticks - pcan_device.last_flush)) >= PCAN_TICKS_FROM_US(800))) { res = pcan_flush_data(&data_fsm, 0, 0); if (res) { pcan_device.last_flush = 0; } } return 0; } flush_size = (prec->pos + (BLOCK_SIZE - 1)) & (~((BLOCK_SIZE - 1))); res = pcan_flush_data(&data_fsm, prec->buffer, flush_size); if (res) { pcan_record_buffer_reset(prec); pcan_device.last_flush = ts_ticks; return 1; } return 0; } #if 0 static void pcan_bus_event( PCAN_RECORD_BUFFER_EX *prec, uint8_t flags ) { uint8_t *ptr, sl, pos = 0; ptr = pcan_record_buffer_request( prec, 6 ); if( !ptr ) return; /* sl */ sl = PCAN_USB_STATUSLEN_INTERNAL | 0x03; pack_u8( ptr, sl ); ptr += 1; pos += 1; /* function */ pack_u8( ptr, PCAN_USB_REC_BUSEVT ); ptr += 1; pos += 1; /* number 0x80, 0x00 */ pack_u8( ptr, flags ); ptr += 1; pos += 1; /* ecc */ pack_u8( ptr, pcan_device.can.ecc ); ptr += 1; pos += 1; /* rx error counter */ pack_u8( ptr, pcan_device.can.rx_err ); ptr += 1; pos += 1; /* tx error counter */ pack_u8( ptr, pcan_device.can.tx_err ); ptr += 1; pos += 1; /* add new record */ pcan_record_buffer_commit( prec, pos ); } #endif #define BUFFER_MINIMAL_GAP_SIZE (16u) static void pcan_rx_can_frame(PCAN_RECORD_BUFFER_EX *prec, const can_message_t *msg) { uint8_t *ptr, pos = 0; ptr = pcan_record_buffer_request(prec, BUFFER_MINIMAL_GAP_SIZE); /* wow ! too fast too furious !!! */ if (!ptr) { if (!pcan_record_buffer_flush(prec)) { pcan_device.can.err |= PCAN_USB_ERROR_RXQOVR; return; } ptr = pcan_record_buffer_request(prec, BUFFER_MINIMAL_GAP_SIZE); /* o_0 */ if (!ptr) return; } /* recheck dlc for abnormal frames... */ uint8_t sl = msg->dlc > 8 ? 8 : msg->dlc; if (msg->flags & CAN_FLAG_EXTID) { sl |= PCAN_USB_STATUSLEN_EXT_ID; } if (msg->flags & CAN_FLAG_RTR) { sl |= PCAN_USB_STATUSLEN_RTR; } pack_u8(ptr, sl); ptr += 1; pos += 1; if (sl & PCAN_USB_STATUSLEN_EXT_ID) { uint32_t id = (msg->id & 0x1FFFFFFF) << 3; if (msg->flags & CAN_FLAG_ECHO) { id |= 0x01; } pack_u32(ptr, id); ptr += 4; pos += 4; } else { uint16_t id = (msg->id & 0x7FF) << 5; if (msg->flags & CAN_FLAG_ECHO) { id |= 0x01; } pack_u16(ptr, id); ptr += 2; pos += 2; } /* timestamp to ticks, 1 tick is 42.666us */ uint16_t timestamp16 = msg->timestamp; if (prec->ts_counter == 0) { pack_u16(ptr, timestamp16); ptr += 2; pos += 2; } else { pack_u8(ptr, timestamp16 & 0xFF); ptr += 1; pos += 1; } ++prec->ts_counter; if (!(sl & PCAN_USB_STATUSLEN_RTR)) { memcpy(ptr, msg->data, msg->dlc); ptr += msg->dlc; pos += msg->dlc; } if (msg->flags & CAN_FLAG_ECHO) { pack_u8(ptr, msg->dummy); ptr += 1; pos += 1; } /* add new record */ pcan_record_buffer_commit(prec, pos); } static void pcan_rx_message(can_message_t *msg) { if (!pcan_device.bus_active) return; if ((msg->flags & CAN_FLAG_ECHO) == 0) { pcan_led_set_mode(LED_CH0_RX, LED_MODE_BLINK_FAST, 237); } pcan_rx_can_frame(&pcan_records, msg); } static void pcan_can_error(uint8_t err, uint8_t rx_err, uint8_t tx_err) { pcan_device.can.rx_err = rx_err; pcan_device.can.tx_err = tx_err; if (err & CAN_ERROR_FLAG_BUSOFF) { pcan_device.can.err |= PCAN_USB_ERROR_BUS_OFF; } } #define WAIT_FOR_TXSLOTS 1 static uint8_t pcan_decode_data_frame(uint8_t *ptr, uint16_t size, uint8_t flags) { can_message_t msg = { 0 }; uint8_t rec_len = flags & PCAN_USB_STATUSLEN_DLC; /* return back frame to PC */ uint8_t srr_flag = 0; if (!pcan_device.bus_active) return 0; if (flags & PCAN_USB_STATUSLEN_EXT_ID) { uint32_t id; if (size < sizeof(uint32_t)) return 0; id = unpack_u32(ptr); srr_flag = id & 0x01; id >>= 3; msg.flags = CAN_FLAG_EXTID; msg.id = id; ptr += sizeof(uint32_t); size -= sizeof(uint32_t); } else { uint16_t id; if (size < sizeof(uint16_t)) return 0; id = unpack_u16(ptr); srr_flag = id & 0x01; id >>= 5; msg.id = id; ptr += sizeof(uint16_t); size -= sizeof(uint16_t); } /* check for MAX DLC */ msg.dlc = rec_len < 8 ? rec_len : 8; if (size < rec_len) return 0; if (!(flags & PCAN_USB_STATUSLEN_RTR)) { memcpy(msg.data, ptr, msg.dlc); ptr += rec_len; size -= rec_len; } else { msg.flags |= CAN_FLAG_RTR; } /* self receive flag ? */ if (srr_flag) { if (size < sizeof(uint8_t)) return 0; msg.dummy = unpack_u8(ptr); ptr += sizeof(uint8_t); size -= sizeof(uint8_t); } msg.timestamp = pcan_timestamp_ticks(); pcan_led_set_mode(LED_CH0_TX, LED_MODE_BLINK_FAST, 237); #if WAIT_FOR_TXSLOTS const uint16_t ts_poll = pcan_timestamp_ticks(); /* need more time to send data... ? */ while (pcan_can_send_message(&msg) < 0) { /* USB will get NACK and we will not miss other data */ pcan_can_poll(); uint16_t ts_diff = pcan_timestamp_ticks() - ts_poll; /* we can't tramsit couse bus off or timeout ? */ if ((pcan_device.can.err & PCAN_USB_ERROR_BUS_OFF) || (ts_diff >= PCAN_TICKS_FROM_US(1000000u))) { /* tx buffer overflow, drop all data */ pcan_device.can.err |= PCAN_USB_ERROR_TXFULL; return size; } } #else if (pcan_can_send_message(&msg) < 0) { /* tx buffer overflow, drop all data */ pcan_device.can.err |= PCAN_USB_ERROR_TXFULL; return size; } #endif /* return back to PC */ if (srr_flag) { msg.flags |= CAN_FLAG_ECHO; pcan_rx_message(&msg); } return size; } static void pcan_set_bitrate(uint8_t *ptr) { /* decode BTR1 */ volatile uint8_t tseg1 = (ptr[0] & 0xF) + 1; volatile uint8_t tseg2 = ((ptr[0] >> 4) & 0x7) + 1; /* decode BTR0 */ volatile uint16_t brp = (ptr[1] & 0x3f) + 1; volatile uint8_t sjw = ((ptr[1] >> 6) & 0x3) + 1; uint32_t bitrate = (((PCAN_USB_CRYSTAL_HZ / 2) / brp) / (1 /*tq*/ + tseg1 + tseg2)); (void)bitrate; pcan_can_set_bitrate(brp, tseg1, tseg2, sjw); } /* create */ void pcan_timesync_event(PCAN_RECORD_BUFFER_EX *prec) { uint8_t sl = PCAN_USB_STATUSLEN_INTERNAL | 2 /* record data size */; uint8_t *ptr, pos = 0; ptr = pcan_record_buffer_request(prec, 5); if (!ptr) return; uint16_t timestamp16 = pcan_timestamp_ticks(); pack_u8(ptr, sl); ptr += 1; pos += 1; /* function */ pack_u8(ptr, PCAN_USB_REC_TS); ptr += 1; pos += 1; /* number */ pack_u8(ptr, 1); ptr += 1; pos += 1; /* timestamp */ pack_u16(ptr, timestamp16); ptr += 2; pos += 2; /* add new record */ pcan_record_buffer_commit(prec, pos); /* error record */ pos = 0; ptr = pcan_record_buffer_request(prec, 3); if (!ptr) return; pcan_device.can.err &= pcan_device.can.err_mask; pack_u8(ptr, PCAN_USB_STATUSLEN_INTERNAL); ptr += 1; pos += 1; pack_u8(ptr, PCAN_USB_REC_ERROR); /* f */ ptr += 1; pos += 1; pack_u8(ptr, pcan_device.can.err); /* n */ ptr += 1; pos += 1; /* add new record */ pcan_record_buffer_commit(prec, pos); /* clean errors */ pcan_device.can.err = 0x00; } void pcan_protocol_process_command(uint8_t *ptr, uint16_t size) { PCAN_USB_PARAM *cmd = (void *)ptr; if (size < PCAN_USB_MSG_HEADER_LEN) return; switch (cmd->number) { case PCAN_USB_EX0: switch (cmd->function) { /* set mass storage mode */ case PCAN_USB_SETCAN2FLASH: break; } break; case PCAN_USB_EX3: switch (cmd->function) { /* CAN silient control*/ case PCAN_USB_SET_SILENT_MODE: pcan_device.can.silient = cmd->param[0]; pcan_can_set_silent(pcan_device.can.silient); break; } break; case PCAN_USB_SET: switch (cmd->function) { /* BTR0 BTR1 */ case PCAN_USB_CMD_BITRATE: pcan_device.can.btr0 = cmd->param[1]; pcan_device.can.btr1 = cmd->param[0]; pcan_set_bitrate(cmd->param); break; /* set CAN on/off*/ case PCAN_USB_CMD_BUS: pcan_device.bus_active = cmd->param[0]; pcan_led_set_mode(LED_STAT, pcan_device.bus_active ? LED_MODE_BLINK_SLOW : LED_MODE_OFF, 0); pcan_can_set_bus_active(pcan_device.bus_active); if (pcan_device.bus_active) { /* provide first timesync event */ pcan_device.last_timestamp_sync = pcan_timestamp_ticks(); pcan_timesync_event(&pcan_records); } /* led state */ pcan_led_set_mode(LED_CH0_TX, pcan_device.bus_active ? LED_MODE_OFF : LED_MODE_ON, 0); pcan_led_set_mode(LED_CH0_RX, pcan_device.bus_active ? LED_MODE_OFF : LED_MODE_ON, 0); break; /* set device id 0-255 */ case PCAN_USB_CMD_DEVID: pcan_device.device_id = cmd->param[0]; break; /* unknown windows driver call */ case PCAN_USB_CMD_CFG: break; /* sja1000 reg write */ case PCAN_USB_CMD_REGISTER: pcan_sja1000_write(cmd->param[0], cmd->param[1]); break; /* ext VCC on/off */ case PCAN_USB_CMD_EXT_VCC: pcan_device.ext_vcc_state = cmd->param[0]; break; /* error frame mask */ case PCAN_USB_CMD_ERR_FR: pcan_device.can.err_mask = cmd->param[0]; break; /* set led mode */ case PCAN_USB_CMD_LED: pcan_device.led_mode = cmd->param[0]; break; } break; case PCAN_USB_GET: switch (cmd->function) { /* BTR0 BTR1 */ case PCAN_USB_CMD_BITRATE: cmd->param[1] = pcan_device.can.btr0; cmd->param[0] = pcan_device.can.btr1; pcan_usb_send_command_buffer(cmd, sizeof(PCAN_USB_PARAM)); break; /* quarts freq in Mhz */ case PCAN_USB_CMD_CLOCK: { cmd->param[0] = pcan_device.can.quartz_freq / 1000000u; pcan_usb_send_command_buffer(cmd, sizeof(PCAN_USB_PARAM)); } break; /* get device id */ case PCAN_USB_CMD_DEVID: { /* default: 255 */ cmd->param[0] = pcan_device.device_id; pcan_usb_send_command_buffer(cmd, sizeof(PCAN_USB_PARAM)); } break; /* serial */ case PCAN_USB_CMD_SN: { /* default: 4 bytes FFFFFFFF */ cmd->param[0] = (pcan_device.device_serial >> 0x18) & 0xFF; cmd->param[1] = (pcan_device.device_serial >> 0x10) & 0xFF; cmd->param[2] = (pcan_device.device_serial >> 0x08) & 0xFF; cmd->param[3] = (pcan_device.device_serial >> 0x00) & 0xFF; pcan_usb_send_command_buffer(cmd, sizeof(PCAN_USB_PARAM)); } break; /* sja1000 reg read */ case PCAN_USB_CMD_REGISTER: cmd->param[1] = pcan_sja1000_read(cmd->param[0]); pcan_usb_send_command_buffer(cmd, sizeof(PCAN_USB_PARAM)); break; /* get led */ case PCAN_USB_CMD_LED: cmd->param[0] = pcan_device.led_mode; pcan_usb_send_command_buffer(cmd, sizeof(PCAN_USB_PARAM)); break; } break; default: assert(0); break; } } void pcan_protocol_process_data(uint8_t *ptr, uint16_t size) { uint8_t msg_prefix, msg_rec_count; /* invalid frame header len ? */ if (size < PCAN_USB_MSG_HEADER_LEN) return; msg_prefix = *ptr++; msg_rec_count = *ptr++; (void)msg_prefix; /* avoid warning */ size -= PCAN_USB_MSG_HEADER_LEN; for (uint8_t i = 0; i < msg_rec_count; i++) { uint16_t eated = 0, sl; if (size < 1) return; sl = unpack_u8(ptr); ptr += sizeof(uint8_t); size -= sizeof(uint8_t); ; /* status, error frame */ if (sl & PCAN_USB_STATUSLEN_INTERNAL) { /* ??? abnormal, device support only CAN data frame */ eated = 0; } /* regular CAN data frame */ else { eated = pcan_decode_data_frame(ptr, size, sl); } /* ? error indicator */ if (!eated) return; eated = size - eated; if (size < eated) return; ptr += eated; size -= eated; } } void pcan_protocol_init(void) { pcan_device.sja1000_ic_mode = SJA1000_PELICAN; pcan_record_buffer_reset(&pcan_records); pcan_can_init(); pcan_can_install_rx_callback(pcan_rx_message); pcan_can_install_error_callback(pcan_can_error); } void pcan_protocol_poll(void) { uint16_t ts_ms = pcan_timestamp_ticks(); if (pcan_device.bus_active) { /* each ~1000 ms */ if ((((uint16_t)(ts_ms - pcan_device.last_timestamp_sync)) >= PCAN_TICKS_FROM_US(1000000u))) { pcan_device.last_timestamp_sync = ts_ms; pcan_timesync_event(&pcan_records); } } pcan_record_buffer_flush(&pcan_records); pcan_can_poll(); }