diff --git a/conf/modules/can.xml b/conf/modules/can.xml index a0075dd494..b7b4f3bd79 100644 --- a/conf/modules/can.xml +++ b/conf/modules/can.xml @@ -11,12 +11,11 @@ mcu - uavcan
- + diff --git a/conf/modules/uavcan.xml b/conf/modules/uavcan.xml index 511f05c14c..9398dbad77 100644 --- a/conf/modules/uavcan.xml +++ b/conf/modules/uavcan.xml @@ -10,6 +10,9 @@ + + can +
@@ -37,4 +40,3 @@
- diff --git a/sw/airborne/arch/chibios/mcu_periph/can_arch.c b/sw/airborne/arch/chibios/mcu_periph/can_arch.c index 8d922f7003..0af6052333 100644 --- a/sw/airborne/arch/chibios/mcu_periph/can_arch.c +++ b/sw/airborne/arch/chibios/mcu_periph/can_arch.c @@ -85,11 +85,23 @@ static void can_thd_rx(void* arg) { snprintf(thd_name, 10, "can%d_rx", cas->if_index); chRegSetThreadName(thd_name); + event_listener_t rxe; + chEvtRegister(&cas->cand->error_event, &rxe, EVENT_MASK(1)); + + struct pprzaddr_can addr = { .can_ifindex = cas->if_index }; while(!chThdShouldTerminateX()) { + + eventmask_t evts = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_IMMEDIATE); + // receive error + if (evts & EVENT_MASK(1)) { + chEvtGetAndClearFlags(&rxe); + canp->nb_errors++; + } + CANRxFrame rx_frame; msg_t status = canReceiveTimeout(cas->cand, CAN_ANY_MAILBOX, &rx_frame, chTimeMS2I(50)); if(status == MSG_OK) { @@ -110,7 +122,7 @@ static void can_thd_rx(void* arg) { .can_id = id, .len = can_dlc_to_len(rx_frame.DLC), .flags = 0, - .timestamp = get_sys_time_msec(), + .timestamp = TIME_I2US(chVTGetSystemTimeX()) }; if(rx_frame.FDF) { @@ -142,9 +154,9 @@ int can_transmit_frame(struct pprzcan_frame* txframe, struct pprzaddr_can* addr) } if(txframe->can_id & CAN_FRAME_EFF) { frame.common.XTD = 1; - frame.ext.EID = txframe->can_id & CAN_EID_MASK + frame.ext.EID = txframe->can_id & CAN_EID_MASK; } else { - frame.std.SID = txframe->can_id & CAN_SID_MASK + frame.std.SID = txframe->can_id & CAN_SID_MASK; } memcpy(frame.data8, txframe->data, txframe->len); @@ -308,4 +320,4 @@ static bool canConfigureIface(struct can_arch_periph* cas) cas->cfg.btr = CAN_BTR_SJW(0) | CAN_BTR_TS1(bs1 - 1) | CAN_BTR_TS2(bs2 - 1) | CAN_BTR_BRP(prescaler - 1); #endif return true; -} \ No newline at end of file +} diff --git a/sw/airborne/arch/chibios/modules/uavcan/uavcan.c b/sw/airborne/arch/chibios/modules/uavcan/uavcan.c index 559125df8b..73624b5314 100644 --- a/sw/airborne/arch/chibios/modules/uavcan/uavcan.c +++ b/sw/airborne/arch/chibios/modules/uavcan/uavcan.c @@ -25,6 +25,8 @@ */ #include "uavcan.h" +#include "mcu_periph/can.h" +#include "modules/core/threads.h" #ifndef UAVCAN_NODE_ID #define UAVCAN_NODE_ID 100 @@ -45,17 +47,10 @@ static uavcan_event *uavcan_event_hd = NULL; #define UAVCAN_CAN1_BAUDRATE UAVCAN_BAUDRATE #endif -static THD_WORKING_AREA(uavcan1_rx_wa, 1024 * 2); -static THD_WORKING_AREA(uavcan1_tx_wa, 1024 * 2); struct uavcan_iface_t uavcan1 = { - .can_driver = &CAND1, + .can_net = {.can_ifindex = 1}, .can_baudrate = UAVCAN_CAN1_BAUDRATE, - .can_cfg = {0}, - .thread_rx_wa = uavcan1_rx_wa, - .thread_rx_wa_size = sizeof(uavcan1_rx_wa), - .thread_tx_wa = uavcan1_tx_wa, - .thread_tx_wa_size = sizeof(uavcan1_tx_wa), .node_id = UAVCAN_CAN1_NODE_ID, .transfer_id = 0, .initialized = false @@ -71,117 +66,56 @@ struct uavcan_iface_t uavcan1 = { #define UAVCAN_CAN2_BAUDRATE UAVCAN_BAUDRATE #endif -static THD_WORKING_AREA(uavcan2_rx_wa, 1024 * 2); -static THD_WORKING_AREA(uavcan2_tx_wa, 1024 * 2); struct uavcan_iface_t uavcan2 = { - .can_driver = &CAND2, + .can_net = {.can_ifindex = 2}, .can_baudrate = UAVCAN_CAN2_BAUDRATE, - .can_cfg = {0}, - .thread_rx_wa = uavcan2_rx_wa, - .thread_rx_wa_size = sizeof(uavcan2_rx_wa), - .thread_tx_wa = uavcan2_tx_wa, - .thread_tx_wa_size = sizeof(uavcan2_tx_wa), .node_id = UAVCAN_CAN2_NODE_ID, .transfer_id = 0, .initialized = false }; #endif -/* - * REMOVE ME - This is only here because the can driver is not yet used - */ -void can_init(void) { } -/* - * Receiver thread. - */ -static THD_FUNCTION(uavcan_rx, p) -{ - event_listener_t el; - CANRxFrame rx_msg; +static void can_frame_cb(struct pprzcan_frame* rx_msg, UNUSED struct pprzaddr_can* src_addr, void* user_data) { + struct uavcan_iface_t *iface = (struct uavcan_iface_t *)user_data; + + pprz_mtx_lock(&iface->mutex); + CanardCANFrame rx_frame; - struct uavcan_iface_t *iface = (struct uavcan_iface_t *)p; - - chRegSetThreadName("uavcan_rx"); - chEvtRegister(&iface->can_driver->rxfull_event, &el, EVENT_MASK(0)); - while (true) { - if (chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(100)) == 0) { - continue; - } - chMtxLock(&iface->mutex); - - // Wait until a CAN message is received - while (canReceive(iface->can_driver, CAN_ANY_MAILBOX, &rx_msg, TIME_IMMEDIATE) == MSG_OK) { - // Process message. - const uint32_t timestamp = TIME_I2US(chVTGetSystemTimeX()); - memcpy(rx_frame.data, rx_msg.data8, 8); - rx_frame.data_len = rx_msg.DLC; -#if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2) - if (rx_msg.common.XTD) { - rx_frame.id = CANARD_CAN_FRAME_EFF | rx_msg.ext.EID; - } else { - rx_frame.id = rx_msg.std.SID; - } -#else - if (rx_msg.IDE) { - rx_frame.id = CANARD_CAN_FRAME_EFF | rx_msg.EID; - } else { - rx_frame.id = rx_msg.SID; - } -#endif - - // Let canard handle the frame - canardHandleRxFrame(&iface->canard, &rx_frame, timestamp); - } - chMtxUnlock(&iface->mutex); + memcpy(rx_frame.data, rx_msg->data, 8); + rx_frame.data_len = rx_msg->len; + if (rx_msg->can_id & CAN_FRAME_EFF) { + rx_frame.id = CANARD_CAN_FRAME_EFF | (rx_msg->can_id & CAN_EID_MASK); + } else { + rx_frame.id = rx_msg->can_id & CAN_SID_MASK; } - chEvtUnregister(&iface->can_driver->rxfull_event, &el); + + // Let canard handle the frame + canardHandleRxFrame(&iface->canard, &rx_frame, rx_msg->timestamp); + + pprz_mtx_unlock(&iface->mutex); } /* * Transmitter thread. */ -static THD_FUNCTION(uavcan_tx, p) +static void uavcan_tx(void* p) { - event_listener_t txc, txe, txr; struct uavcan_iface_t *iface = (struct uavcan_iface_t *)p; uint8_t err_cnt = 0; - chRegSetThreadName("uavcan_tx"); - chEvtRegister(&iface->can_driver->txempty_event, &txc, EVENT_MASK(0)); - chEvtRegister(&iface->can_driver->error_event, &txe, EVENT_MASK(1)); - chEvtRegister(&iface->tx_request, &txr, EVENT_MASK(2)); - while (true) { - eventmask_t evts = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(100)); - // Succesfull transmit - if (evts == 0) { - continue; - } + pprz_bsem_wait(&iface->bsem); - // Transmit error - if (evts & EVENT_MASK(1)) { - chEvtGetAndClearFlags(&txe); - continue; - } - - chMtxLock(&iface->mutex); + pprz_mtx_lock(&iface->mutex); for (const CanardCANFrame *txf = NULL; (txf = canardPeekTxQueue(&iface->canard)) != NULL;) { - CANTxFrame tx_msg; - memcpy(tx_msg.data8, txf->data, 8); -#if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2) - tx_msg.DLC = txf->data_len; // TODO fixme for FDCAN (>8 bytes) - tx_msg.ext.EID = txf->id & CANARD_CAN_EXT_ID_MASK; - tx_msg.common.XTD = 1; - tx_msg.common.RTR = 0; -#else - tx_msg.DLC = txf->data_len; - tx_msg.EID = txf->id & CANARD_CAN_EXT_ID_MASK; - tx_msg.IDE = CAN_IDE_EXT; - tx_msg.RTR = CAN_RTR_DATA; -#endif - if (!canTryTransmitI(iface->can_driver, CAN_ANY_MAILBOX, &tx_msg)) { + struct pprzcan_frame tx_msg; + memcpy(tx_msg.data, txf->data, 8); + tx_msg.len = txf->data_len; + tx_msg.can_id = (txf->id & CANARD_CAN_EXT_ID_MASK) | CAN_FRAME_EFF; + + if (!can_transmit_frame(&tx_msg, &iface->can_net)) { err_cnt = 0; canardPopTxQueue(&iface->canard); } else { @@ -193,13 +127,13 @@ static THD_FUNCTION(uavcan_tx, p) } // Timeout - just exit and try again later - chMtxUnlock(&iface->mutex); + pprz_mtx_unlock(&iface->mutex); chThdSleepMilliseconds(++err_cnt); - chMtxLock(&iface->mutex); + pprz_mtx_lock(&iface->mutex); continue; } } - chMtxUnlock(&iface->mutex); + pprz_mtx_unlock(&iface->mutex); } } @@ -239,133 +173,14 @@ static bool shouldAcceptTransfer(const CanardInstance *ins __attribute__((unused return false; } -/** - * Try to compute the timing registers for the can interface and set the configuration - */ -static bool uavcanConfigureIface(struct uavcan_iface_t *iface) -{ - if (iface->can_baudrate < 1) { - return false; - } - - // Hardware configurationn -#if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2) - const uint32_t pclk = STM32_FDCANCLK; -#else - const uint32_t pclk = STM32_PCLK1; -#endif - static const int MaxBS1 = 16; - static const int MaxBS2 = 8; - - /* - * Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG - * CAN in Automation, 2003 - * - * According to the source, optimal quanta per bit are: - * Bitrate Optimal Maximum - * 1000 kbps 8 10 - * 500 kbps 16 17 - * 250 kbps 16 17 - * 125 kbps 16 17 - */ - const int max_quanta_per_bit = (iface->can_baudrate >= 1000000) ? 10 : 17; - static const int MaxSamplePointLocation = 900; - - /* - * Computing (prescaler * BS): - * BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual - * BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified - * let: - * BS = 1 + BS1 + BS2 -- Number of time quanta per bit - * PRESCALER_BS = PRESCALER * BS - * ==> - * PRESCALER_BS = PCLK / BITRATE - */ - const uint32_t prescaler_bs = pclk / iface->can_baudrate; - -// Searching for such prescaler value so that the number of quanta per bit is highest. - uint8_t bs1_bs2_sum = max_quanta_per_bit - 1; - while ((prescaler_bs % (1 + bs1_bs2_sum)) != 0) { - if (bs1_bs2_sum <= 2) { - return false; // No solution - } - bs1_bs2_sum--; - } - - const uint32_t prescaler = prescaler_bs / (1 + bs1_bs2_sum); - if ((prescaler < 1U) || (prescaler > 1024U)) { - return false; // No solution - } - - /* - * Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. - * We need to find the values so that the sample point is as close as possible to the optimal value. - * - * Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) - * {{bs2 -> (1 + bs1)/7}} - * - * Hence: - * bs2 = (1 + bs1) / 7 - * bs1 = (7 * bs1_bs2_sum - 1) / 8 - * - * Sample point location can be computed as follows: - * Sample point location = (1 + bs1) / (1 + bs1 + bs2) - * - * Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: - * - With rounding to nearest - * - With rounding to zero - */ -// First attempt with rounding to nearest - uint8_t bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; - uint8_t bs2 = bs1_bs2_sum - bs1; - uint16_t sample_point_permill = 1000 * (1 + bs1) / (1 + bs1 + bs2); - -// Second attempt with rounding to zero - if (sample_point_permill > MaxSamplePointLocation) { - bs1 = (7 * bs1_bs2_sum - 1) / 8; - bs2 = bs1_bs2_sum - bs1; - sample_point_permill = 1000 * (1 + bs1) / (1 + bs1 + bs2); - } - - /* - * Final validation - * Helpful Python: - * def sample_point_from_btr(x): - * assert 0b0011110010000000111111000000000 & x == 0 - * ts2,ts1,brp = (x>>20)&7, (x>>16)&15, x&511 - * return (1+ts1+1)/(1+ts1+1+ts2+1) - * - */ - if ((iface->can_baudrate != (pclk / (prescaler * (1 + bs1 + bs2)))) || (bs1 < 1) || (bs1 > MaxBS1) || (bs2 < 1) - || (bs2 > MaxBS2)) { - return false; - } - - // Configure the interface -#if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2) - iface->can_cfg.NBTP = (0 << FDCAN_NBTP_NSJW_Pos) | ((bs1 - 1) << FDCAN_NBTP_NTSEG1_Pos) | (( - bs2 - 1) << FDCAN_NBTP_NTSEG2_Pos) | ((prescaler - 1) << FDCAN_NBTP_NBRP_Pos); - iface->can_cfg.CCCR = FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE; -#else - iface->can_cfg.mcr = CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP; - iface->can_cfg.btr = CAN_BTR_SJW(0) | CAN_BTR_TS1(bs1 - 1) | CAN_BTR_TS2(bs2 - 1) | CAN_BTR_BRP(prescaler - 1); -#endif - return true; -} /** * Initialize uavcan interface */ static void uavcanInitIface(struct uavcan_iface_t *iface) { - // First try to configure abort if failed - if (!uavcanConfigureIface(iface)) { - return; - } - - // Initialize mutexes/events for multithread locking - chMtxObjectInit(&iface->mutex); - chEvtObjectInit(&iface->tx_request); + pprz_mtx_init(&iface->mutex); + pprz_bsem_init(&iface->bsem, true); // Initialize canard canardInit(&iface->canard, iface->canard_memory_pool, sizeof(iface->canard_memory_pool), @@ -373,13 +188,12 @@ static void uavcanInitIface(struct uavcan_iface_t *iface) // Update the uavcan node ID canardSetLocalNodeID(&iface->canard, iface->node_id); - // Start the can interface - canStart(iface->can_driver, &iface->can_cfg); - // Start the receiver and transmitter thread - chThdCreateStatic(iface->thread_rx_wa, iface->thread_rx_wa_size, NORMALPRIO + 8, uavcan_rx, (void *)iface); - chThdCreateStatic(iface->thread_tx_wa, iface->thread_tx_wa_size, NORMALPRIO + 7, uavcan_tx, (void *)iface); - iface->initialized = true; + can_register_callback(can_frame_cb, &iface->can_net, (void *)iface); + + if(!pprz_thread_create(&iface->thread_tx, 2048, "uavcan_tx", NORMALPRIO+7, uavcan_tx, (void *)iface)) { + iface->initialized = true; + } } /** @@ -388,25 +202,9 @@ static void uavcanInitIface(struct uavcan_iface_t *iface) void uavcan_init(void) { #if UAVCAN_USE_CAN1 -#if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2) - // Configure the RAM - uavcan1.can_cfg.RXF0C = (32 << FDCAN_RXF0C_F0S_Pos) | (0 << FDCAN_RXF0C_F0SA_Pos); - uavcan1.can_cfg.RXF1C = (32 << FDCAN_RXF1C_F1S_Pos) | (128 << FDCAN_RXF1C_F1SA_Pos); - uavcan1.can_cfg.TXBC = (32 << FDCAN_TXBC_TFQS_Pos) | (256 << FDCAN_TXBC_TBSA_Pos); - uavcan1.can_cfg.TXESC = 0x000; // 8 Byte mode only (4 words per message) - uavcan1.can_cfg.RXESC = 0x000; // 8 Byte mode only (4 words per message) -#endif uavcanInitIface(&uavcan1); #endif #if UAVCAN_USE_CAN2 -#if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2) - // Configure the RAM - uavcan2.can_cfg.RXF0C = (32 << FDCAN_RXF0C_F0S_Pos) | (384 << FDCAN_RXF0C_F0SA_Pos); - uavcan2.can_cfg.RXF1C = (32 << FDCAN_RXF1C_F1S_Pos) | (512 << FDCAN_RXF1C_F1SA_Pos); - uavcan2.can_cfg.TXBC = (32 << FDCAN_TXBC_TFQS_Pos) | (640 << FDCAN_TXBC_TBSA_Pos); - uavcan2.can_cfg.TXESC = 0x000; // 8 Byte mode only (4 words per message) - uavcan2.can_cfg.RXESC = 0x000; // 8 Byte mode only (4 words per message) -#endif uavcanInitIface(&uavcan2); #endif } @@ -434,12 +232,11 @@ void uavcan_broadcast(struct uavcan_iface_t *iface, uint64_t data_type_signature uint16_t payload_len) { if (!iface->initialized) { return; } - - chMtxLock(&iface->mutex); + pprz_mtx_lock(&iface->mutex); canardBroadcast(&iface->canard, data_type_signature, data_type_id, &iface->transfer_id, priority, payload, payload_len); - chMtxUnlock(&iface->mutex); - chEvtBroadcast(&iface->tx_request); + pprz_mtx_unlock(&iface->mutex); + pprz_bsem_signal(&iface->bsem); } diff --git a/sw/airborne/arch/chibios/modules/uavcan/uavcan.h b/sw/airborne/arch/chibios/modules/uavcan/uavcan.h index 2920324034..1b392873c4 100644 --- a/sw/airborne/arch/chibios/modules/uavcan/uavcan.h +++ b/sw/airborne/arch/chibios/modules/uavcan/uavcan.h @@ -26,24 +26,25 @@ #ifndef MODULES_UAVCAN_ARCH_H #define MODULES_UAVCAN_ARCH_H -#include #include #include +#include "mcu_periph/can.h" +#include "modules/core/threads.h" /** uavcan interface structure */ struct uavcan_iface_t { - CANDriver *can_driver; + struct pprzaddr_can can_net; uint32_t can_baudrate; - CANConfig can_cfg; event_source_t tx_request; - mutex_t mutex; - void *thread_rx_wa; - void *thread_tx_wa; - void *thread_uavcan_wa; - size_t thread_rx_wa_size; - size_t thread_tx_wa_size; - size_t thread_uavcan_wa_size; + + pprz_mutex_t mutex; + pprz_thread_t thread_tx; + // void *thread_tx_wa; + // void *thread_uavcan_wa; + // size_t thread_tx_wa_size; + // size_t thread_uavcan_wa_size; + pprz_bsem_t bsem; uint8_t node_id; CanardInstance canard; diff --git a/sw/airborne/mcu_periph/can.c b/sw/airborne/mcu_periph/can.c index e0041c1dda..bb761c2e7b 100644 --- a/sw/airborne/mcu_periph/can.c +++ b/sw/airborne/mcu_periph/can.c @@ -29,6 +29,16 @@ struct can_periph can1 = { .fd = 1, + .nb_errors = 0, + .callbacks = {0}, + .callback_user_data = {0} +}; +#endif + +#if USE_CAN2 +struct can_periph can2 = { + .fd = 2, + .nb_errors = 0, .callbacks = {0}, .callback_user_data = {0} }; diff --git a/sw/airborne/mcu_periph/can.h b/sw/airborne/mcu_periph/can.h index 72c3b46c63..b079b36964 100644 --- a/sw/airborne/mcu_periph/can.h +++ b/sw/airborne/mcu_periph/can.h @@ -73,7 +73,7 @@ struct pprzcan_frame { socketcan_id_t can_id; uint8_t len; uint8_t flags; // CAN FD specific flags - uint32_t timestamp; // timestamp in ms. + uint32_t timestamp; // timestamp in us. uint8_t data[SOCKETCAN_MAX_DLEN]; }; @@ -89,6 +89,7 @@ typedef void(* can_rx_frame_callback_t)(struct pprzcan_frame* rxframe, struct pp struct can_periph { void* arch_struct; int fd; + uint32_t nb_errors; can_rx_frame_callback_t callbacks[CAN_NB_CALLBACKS_MAX]; void* callback_user_data[CAN_NB_CALLBACKS_MAX]; }; diff --git a/sw/airborne/modules/core/slcan.c b/sw/airborne/modules/core/slcan.c index 651af13cbb..def2371e03 100644 --- a/sw/airborne/modules/core/slcan.c +++ b/sw/airborne/modules/core/slcan.c @@ -258,10 +258,11 @@ void slcan_can_rx_cb(struct pprzcan_frame* rxframe, struct pprzaddr_can* src_add } if(slcan->timestamp) { - slcan->tx_buf[idx++] = nibble2hex((rxframe->timestamp >> 12)); - slcan->tx_buf[idx++] = nibble2hex((rxframe->timestamp >> 8)); - slcan->tx_buf[idx++] = nibble2hex((rxframe->timestamp >> 4)); - slcan->tx_buf[idx++] = nibble2hex((rxframe->timestamp >> 0)); + uint16_t timestamp_ms = rxframe->timestamp / 1000; + slcan->tx_buf[idx++] = nibble2hex((timestamp_ms >> 12)); + slcan->tx_buf[idx++] = nibble2hex((timestamp_ms >> 8)); + slcan->tx_buf[idx++] = nibble2hex((timestamp_ms >> 4)); + slcan->tx_buf[idx++] = nibble2hex((timestamp_ms >> 0)); } slcan->tx_buf[idx++] = '\r';