Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/Tools/AP_Bootloader/can.cpp
Views: 1798
/*1This program is free software: you can redistribute it and/or modify2it under the terms of the GNU General Public License as published by3the Free Software Foundation, either version 3 of the License, or4(at your option) any later version.56This program is distributed in the hope that it will be useful,7but WITHOUT ANY WARRANTY; without even the implied warranty of8MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9GNU General Public License for more details.1011You should have received a copy of the GNU General Public License12along with this program. If not, see <http://www.gnu.org/licenses/>.13*/14/*15CAN bootloader support16*/17#include <AP_HAL/AP_HAL.h>18#include <hal.h>19#if HAL_USE_CAN == TRUE || HAL_NUM_CAN_IFACES20#include <AP_Math/AP_Math.h>21#include <AP_Math/crc.h>22#include <canard.h>23#include "support.h"24#include <dronecan_msgs.h>25#include "can.h"26#include "bl_protocol.h"27#include <drivers/stm32/canard_stm32.h>28#include "app_comms.h"29#include <AP_HAL_ChibiOS/hwdef/common/watchdog.h>30#include <stdio.h>31#include <AP_HAL_ChibiOS/CANIface.h>32#include <AP_CheckFirmware/AP_CheckFirmware.h>3334static CanardInstance canard;35static uint32_t canard_memory_pool[4096/4];36#ifndef HAL_CAN_DEFAULT_NODE_ID37#define HAL_CAN_DEFAULT_NODE_ID CANARD_BROADCAST_NODE_ID38#endif39static uint8_t initial_node_id = HAL_CAN_DEFAULT_NODE_ID;4041// can config for 1MBit42static uint32_t baudrate = 1000000U;4344#if HAL_USE_CAN45static CANConfig cancfg = {46CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,470 // filled in below48};49// pipelining is not faster when using ChibiOS CAN driver50#define FW_UPDATE_PIPELINE_LEN 151#else52ChibiOS::CANIface can_iface[HAL_NUM_CAN_IFACES];53#endif5455#ifndef CAN_APP_VERSION_MAJOR56#define CAN_APP_VERSION_MAJOR 257#endif58#ifndef CAN_APP_VERSION_MINOR59#define CAN_APP_VERSION_MINOR 060#endif61#ifndef CAN_APP_NODE_NAME62#define CAN_APP_NODE_NAME "org.ardupilot." CHIBIOS_BOARD_NAME63#endif6465#ifdef EXT_FLASH_SIZE_MB66static_assert(EXT_FLASH_SIZE_MB == 0, "DroneCAN bootloader cannot support external flash");67#endif6869static uint8_t node_id_allocation_transfer_id;70static uavcan_protocol_NodeStatus node_status;71static uint32_t send_next_node_id_allocation_request_at_ms;72static uint8_t node_id_allocation_unique_id_offset;7374static void processTx(void);7576// keep up to 4 transfers in progress77#ifndef FW_UPDATE_PIPELINE_LEN78#define FW_UPDATE_PIPELINE_LEN 479#endif8081#if CH_CFG_USE_MUTEXES == TRUE82static HAL_Semaphore can_mutex;83#endif8485static struct {86uint32_t rtt_ms;87uint32_t ofs;88uint8_t node_id;89uint8_t path[sizeof(uavcan_protocol_file_Path::path.data)+1];90uint8_t sector;91uint32_t sector_ofs;92uint8_t transfer_id;93uint8_t idx;94struct {95uint8_t tx_id;96uint32_t sent_ms;97uint32_t offset;98bool have_reply;99uavcan_protocol_file_ReadResponse pkt;100} reads[FW_UPDATE_PIPELINE_LEN];101uint16_t erased_to;102} fw_update;103104/*105get cpu unique ID106*/107static void readUniqueID(uint8_t* out_uid)108{109uint8_t len = sizeof(uavcan_protocol_dynamic_node_id_Allocation::unique_id.data);110memset(out_uid, 0, len);111memcpy(out_uid, (const void *)UDID_START, MIN(len,12));112}113114/*115simple 16 bit random number generator116*/117static uint16_t get_randomu16(void)118{119static uint32_t m_z = 1234;120static uint32_t m_w = 76542;121m_z = 36969 * (m_z & 0xFFFFu) + (m_z >> 16);122m_w = 18000 * (m_w & 0xFFFFu) + (m_w >> 16);123return ((m_z << 16) + m_w) & 0xFFFF;124}125126127/**128* Returns a pseudo random integer in a given range129*/130static uint32_t get_random_range(uint16_t range)131{132return get_randomu16() % range;133}134135/*136handle a GET_NODE_INFO request137*/138static void handle_get_node_info(CanardInstance* ins,139CanardRxTransfer* transfer)140{141uint8_t buffer[UAVCAN_PROTOCOL_GETNODEINFO_RESPONSE_MAX_SIZE];142uavcan_protocol_GetNodeInfoResponse pkt {};143144node_status.uptime_sec = AP_HAL::millis() / 1000U;145146pkt.status = node_status;147pkt.software_version.major = CAN_APP_VERSION_MAJOR;148pkt.software_version.minor = CAN_APP_VERSION_MINOR;149150readUniqueID(pkt.hardware_version.unique_id);151152// use hw major/minor for APJ_BOARD_ID so we know what fw is153// compatible with this hardware154pkt.hardware_version.major = APJ_BOARD_ID >> 8;155pkt.hardware_version.minor = APJ_BOARD_ID & 0xFF;156157char name[strlen(CAN_APP_NODE_NAME)+1];158strcpy(name, CAN_APP_NODE_NAME);159pkt.name.len = strlen(CAN_APP_NODE_NAME);160memcpy(pkt.name.data, name, pkt.name.len);161162uint16_t total_size = uavcan_protocol_GetNodeInfoResponse_encode(&pkt, buffer, true);163164canardRequestOrRespond(ins,165transfer->source_node_id,166UAVCAN_PROTOCOL_GETNODEINFO_SIGNATURE,167UAVCAN_PROTOCOL_GETNODEINFO_ID,168&transfer->transfer_id,169transfer->priority,170CanardResponse,171&buffer[0],172total_size);173}174175/*176send a read for a fw update177*/178static bool send_fw_read(uint8_t idx)179{180auto &r = fw_update.reads[idx];181r.tx_id = fw_update.transfer_id;182r.have_reply = false;183184uavcan_protocol_file_ReadRequest pkt {};185pkt.path.path.len = strlen((const char *)fw_update.path);186pkt.offset = r.offset;187memcpy(pkt.path.path.data, fw_update.path, pkt.path.path.len);188189uint8_t buffer[UAVCAN_PROTOCOL_FILE_READ_REQUEST_MAX_SIZE];190uint16_t total_size = uavcan_protocol_file_ReadRequest_encode(&pkt, buffer, true);191192if (canardRequestOrRespond(&canard,193fw_update.node_id,194UAVCAN_PROTOCOL_FILE_READ_SIGNATURE,195UAVCAN_PROTOCOL_FILE_READ_ID,196&fw_update.transfer_id,197CANARD_TRANSFER_PRIORITY_HIGH,198CanardRequest,199&buffer[0],200total_size) > 0) {201// mark it as having been sent202r.sent_ms = AP_HAL::millis();203return true;204}205return false;206}207208/*209send a read for a fw update210*/211static void send_fw_reads(void)212{213const uint32_t now = AP_HAL::millis();214215for (uint8_t i=0; i<FW_UPDATE_PIPELINE_LEN; i++) {216const uint8_t idx = (fw_update.idx+i) % FW_UPDATE_PIPELINE_LEN;217const auto &r = fw_update.reads[idx];218if (r.have_reply) {219continue;220}221if (r.sent_ms != 0 && now - r.sent_ms < 10+2*MAX(250,fw_update.rtt_ms)) {222// waiting on a response223continue;224}225if (!send_fw_read(idx)) {226break;227}228}229}230231/*232erase up to at least the given sector number233*/234static void erase_to(uint16_t sector)235{236if (sector < fw_update.erased_to) {237return;238}239flash_func_erase_sector(sector);240fw_update.erased_to = sector+1;241242/*243pre-erase any non-erased pages up to end of flash. This puts all244the load of erasing at the start of flashing which is much245faster than flashing as we go on boards with small flash246sectors. We stop at the first already erased page so we don't247end up wasting time erasing already erased pages when the248firmware is much smaller than the total flash size249*/250while (flash_func_sector_size(fw_update.erased_to) != 0 &&251!flash_func_is_erased(fw_update.erased_to)) {252flash_func_erase_sector(fw_update.erased_to);253fw_update.erased_to++;254}255}256257/*258handle response to file read for fw update259*/260static void handle_file_read_response(CanardInstance* ins, CanardRxTransfer* transfer)261{262if (transfer->source_node_id != fw_update.node_id) {263return;264}265/*266match the response to a sent request267*/268uint8_t idx = 0;269bool found = false;270for (idx=0; idx<FW_UPDATE_PIPELINE_LEN; idx++) {271const auto &r = fw_update.reads[idx];272if (r.tx_id == transfer->transfer_id) {273found = true;274break;275}276}277if (!found) {278// not a current transfer, we may be getting long delays279fw_update.rtt_ms = MIN(3000, fw_update.rtt_ms+250);280return;281}282if (uavcan_protocol_file_ReadResponse_decode(transfer, &fw_update.reads[idx].pkt)) {283return;284}285fw_update.reads[idx].have_reply = true;286uint32_t rtt = MIN(3000,MAX(AP_HAL::millis() - fw_update.reads[idx].sent_ms, 25));287fw_update.rtt_ms = uint32_t(0.9 * fw_update.rtt_ms + 0.1 * rtt);288289while (fw_update.reads[fw_update.idx].have_reply) {290auto &r = fw_update.reads[fw_update.idx];291if (r.offset != fw_update.ofs) {292// bad sequence293r.have_reply = false;294r.sent_ms = 0;295break;296}297const auto &pkt = r.pkt;298const uint16_t len = pkt.data.len;299const uint16_t len_words = (len+3U)/4U;300const uint8_t *buf = (uint8_t *)pkt.data.data;301uint32_t buf32[len_words] {};302memcpy((uint8_t*)buf32, buf, len);303304if (fw_update.ofs == 0) {305flash_set_keep_unlocked(true);306}307308const uint32_t sector_size = flash_func_sector_size(fw_update.sector);309if (sector_size == 0) {310// firmware is too big311fw_update.node_id = 0;312flash_write_flush();313flash_set_keep_unlocked(false);314node_status.vendor_specific_status_code = uint8_t(check_fw_result_t::FAIL_REASON_BAD_LENGTH_APP);315break;316}317if (fw_update.sector_ofs == 0) {318erase_to(fw_update.sector);319}320if (fw_update.sector_ofs+len > sector_size) {321erase_to(fw_update.sector+1);322}323if (!flash_write_buffer(fw_update.ofs, buf32, len_words)) {324continue;325}326327fw_update.ofs += len;328fw_update.sector_ofs += len;329if (fw_update.sector_ofs >= flash_func_sector_size(fw_update.sector)) {330fw_update.sector++;331fw_update.sector_ofs -= sector_size;332}333334if (len < sizeof(uavcan_protocol_file_ReadResponse::data.data)) {335fw_update.node_id = 0;336flash_write_flush();337flash_set_keep_unlocked(false);338const auto ok = check_good_firmware();339node_status.vendor_specific_status_code = uint8_t(ok);340if (ok == check_fw_result_t::CHECK_FW_OK) {341jump_to_app();342}343return;344}345346r.have_reply = false;347r.sent_ms = 0;348r.offset += FW_UPDATE_PIPELINE_LEN*sizeof(uavcan_protocol_file_ReadResponse::data.data);349send_fw_read(fw_update.idx);350processTx();351352fw_update.idx = (fw_update.idx + 1) % FW_UPDATE_PIPELINE_LEN;353}354355// show offset number we are flashing in kbyte as crude progress indicator356node_status.vendor_specific_status_code = 1 + (fw_update.ofs / 1024U);357}358359/*360handle a begin firmware update request. We start pulling in the file data361*/362static void handle_begin_firmware_update(CanardInstance* ins, CanardRxTransfer* transfer)363{364if (fw_update.node_id == 0) {365uavcan_protocol_file_BeginFirmwareUpdateRequest pkt;366if (uavcan_protocol_file_BeginFirmwareUpdateRequest_decode(transfer, &pkt)) {367return;368}369if (pkt.image_file_remote_path.path.len > sizeof(fw_update.path)-1) {370return;371}372memset(&fw_update, 0, sizeof(fw_update));373for (uint8_t i=0; i<FW_UPDATE_PIPELINE_LEN; i++) {374fw_update.reads[i].offset = i*sizeof(uavcan_protocol_file_ReadResponse::data.data);375}376memcpy(fw_update.path, pkt.image_file_remote_path.path.data, pkt.image_file_remote_path.path.len);377fw_update.path[pkt.image_file_remote_path.path.len] = 0;378fw_update.node_id = pkt.source_node_id;379if (fw_update.node_id == 0) {380fw_update.node_id = transfer->source_node_id;381}382}383384uint8_t buffer[UAVCAN_PROTOCOL_FILE_BEGINFIRMWAREUPDATE_RESPONSE_MAX_SIZE];385uavcan_protocol_file_BeginFirmwareUpdateResponse reply {};386reply.error = UAVCAN_PROTOCOL_FILE_BEGINFIRMWAREUPDATE_RESPONSE_ERROR_OK;387388uint32_t total_size = uavcan_protocol_file_BeginFirmwareUpdateResponse_encode(&reply, buffer, true);389canardRequestOrRespond(ins,390transfer->source_node_id,391UAVCAN_PROTOCOL_FILE_BEGINFIRMWAREUPDATE_SIGNATURE,392UAVCAN_PROTOCOL_FILE_BEGINFIRMWAREUPDATE_ID,393&transfer->transfer_id,394transfer->priority,395CanardResponse,396&buffer[0],397total_size);398}399400static void handle_allocation_response(CanardInstance* ins, CanardRxTransfer* transfer)401{402// Rule C - updating the randomized time interval403send_next_node_id_allocation_request_at_ms =404AP_HAL::millis() + UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MIN_REQUEST_PERIOD_MS +405get_random_range(UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MAX_FOLLOWUP_DELAY_MS);406407if (transfer->source_node_id == CANARD_BROADCAST_NODE_ID)408{409node_id_allocation_unique_id_offset = 0;410return;411}412413struct uavcan_protocol_dynamic_node_id_Allocation msg;414if (uavcan_protocol_dynamic_node_id_Allocation_decode(transfer, &msg)) {415return;416}417418// Obtaining the local unique ID419uint8_t my_unique_id[sizeof(uavcan_protocol_dynamic_node_id_Allocation::unique_id.data)];420readUniqueID(my_unique_id);421422// Matching the received UID against the local one423if (memcmp(msg.unique_id.data, my_unique_id, msg.unique_id.len) != 0) {424node_id_allocation_unique_id_offset = 0;425return; // No match, return426}427428if (msg.unique_id.len < sizeof(msg.unique_id.data)) {429// The allocator has confirmed part of unique ID, switching to the next stage and updating the timeout.430node_id_allocation_unique_id_offset = msg.unique_id.len;431send_next_node_id_allocation_request_at_ms -= UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MIN_REQUEST_PERIOD_MS;432} else if (msg.node_id != CANARD_BROADCAST_NODE_ID) { // new ID valid? (if not we will time out and start over)433// Allocation complete - copying the allocated node ID from the message434canardSetLocalNodeID(ins, msg.node_id);435}436}437438/**439* This callback is invoked by the library when a new message or request or response is received.440*/441static void onTransferReceived(CanardInstance* ins,442CanardRxTransfer* transfer)443{444/*445* Dynamic node ID allocation protocol.446* Taking this branch only if we don't have a node ID, ignoring otherwise.447*/448if (canardGetLocalNodeID(ins) == CANARD_BROADCAST_NODE_ID) {449if (transfer->transfer_type == CanardTransferTypeBroadcast &&450transfer->data_type_id == UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_ID) {451handle_allocation_response(ins, transfer);452}453return;454}455456switch (transfer->data_type_id) {457case UAVCAN_PROTOCOL_GETNODEINFO_ID:458handle_get_node_info(ins, transfer);459break;460461case UAVCAN_PROTOCOL_FILE_BEGINFIRMWAREUPDATE_ID:462handle_begin_firmware_update(ins, transfer);463break;464465case UAVCAN_PROTOCOL_FILE_READ_ID:466handle_file_read_response(ins, transfer);467break;468469case UAVCAN_PROTOCOL_RESTARTNODE_ID:470NVIC_SystemReset();471break;472}473}474475476/**477* This callback is invoked by the library when it detects beginning of a new transfer on the bus that can be received478* by the local node.479* If the callback returns true, the library will receive the transfer.480* If the callback returns false, the library will ignore the transfer.481* All transfers that are addressed to other nodes are always ignored.482*/483static bool shouldAcceptTransfer(const CanardInstance* ins,484uint64_t* out_data_type_signature,485uint16_t data_type_id,486CanardTransferType transfer_type,487uint8_t source_node_id)488{489(void)source_node_id;490491if (canardGetLocalNodeID(ins) == CANARD_BROADCAST_NODE_ID) {492/*493* If we're in the process of allocation of dynamic node ID, accept only relevant transfers.494*/495if ((transfer_type == CanardTransferTypeBroadcast) &&496(data_type_id == UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_ID))497{498*out_data_type_signature = UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_SIGNATURE;499return true;500}501return false;502}503504switch (data_type_id) {505case UAVCAN_PROTOCOL_GETNODEINFO_ID:506*out_data_type_signature = UAVCAN_PROTOCOL_GETNODEINFO_SIGNATURE;507return true;508case UAVCAN_PROTOCOL_FILE_BEGINFIRMWAREUPDATE_ID:509*out_data_type_signature = UAVCAN_PROTOCOL_FILE_BEGINFIRMWAREUPDATE_SIGNATURE;510return true;511case UAVCAN_PROTOCOL_RESTARTNODE_ID:512*out_data_type_signature = UAVCAN_PROTOCOL_RESTARTNODE_SIGNATURE;513return true;514case UAVCAN_PROTOCOL_FILE_READ_ID:515*out_data_type_signature = UAVCAN_PROTOCOL_FILE_READ_SIGNATURE;516return true;517default:518break;519}520521return false;522}523524#if HAL_USE_CAN525static void processTx(void)526{527static uint8_t fail_count;528for (const CanardCANFrame* txf = NULL; (txf = canardPeekTxQueue(&canard)) != NULL;) {529CANTxFrame txmsg {};530txmsg.DLC = txf->data_len;531memcpy(txmsg.data8, txf->data, 8);532txmsg.EID = txf->id & CANARD_CAN_EXT_ID_MASK;533txmsg.IDE = 1;534txmsg.RTR = 0;535if (canTransmit(&CAND1, CAN_ANY_MAILBOX, &txmsg, TIME_IMMEDIATE) == MSG_OK) {536canardPopTxQueue(&canard);537fail_count = 0;538} else {539// just exit and try again later. If we fail 8 times in a row540// then start discarding to prevent the pool filling up541if (fail_count < 8) {542fail_count++;543} else {544canardPopTxQueue(&canard);545}546return;547}548}549}550551static void processRx(void)552{553CANRxFrame rxmsg {};554while (canReceive(&CAND1, CAN_ANY_MAILBOX, &rxmsg, TIME_IMMEDIATE) == MSG_OK) {555CanardCANFrame rx_frame {};556557#ifdef HAL_GPIO_PIN_LED_BOOTLOADER558palToggleLine(HAL_GPIO_PIN_LED_BOOTLOADER);559#endif560const uint64_t timestamp = AP_HAL::micros64();561memcpy(rx_frame.data, rxmsg.data8, 8);562rx_frame.data_len = rxmsg.DLC;563if(rxmsg.IDE) {564rx_frame.id = CANARD_CAN_FRAME_EFF | rxmsg.EID;565} else {566rx_frame.id = rxmsg.SID;567}568canardHandleRxFrame(&canard, &rx_frame, timestamp);569}570}571#else572// Use HAL CAN interface573static void processTx(void)574{575static uint8_t fail_count;576for (const CanardCANFrame* txf = NULL; (txf = canardPeekTxQueue(&canard)) != NULL;) {577AP_HAL::CANFrame txmsg {};578txmsg.dlc = txf->data_len;579memcpy(txmsg.data, txf->data, 8);580txmsg.id = (txf->id | AP_HAL::CANFrame::FlagEFF);581// push message with 1s timeout582bool send_ok = false;583for (uint8_t i=0; i<HAL_NUM_CAN_IFACES; i++) {584send_ok |= (can_iface[i].send(txmsg, AP_HAL::micros64() + 1000000, 0) > 0);585}586if (send_ok) {587canardPopTxQueue(&canard);588fail_count = 0;589} else {590// just exit and try again later. If we fail 8 times in a row591// then start discarding to prevent the pool filling up592if (fail_count < 8) {593fail_count++;594} else {595canardPopTxQueue(&canard);596}597return;598}599}600}601602static void processRx(void)603{604AP_HAL::CANFrame rxmsg;605while (true) {606bool got_pkt = false;607for (uint8_t i=0; i<HAL_NUM_CAN_IFACES; i++) {608bool read_select = true;609bool write_select = false;610can_iface[i].select(read_select, write_select, nullptr, 0);611if (!read_select) {612continue;613}614#ifdef HAL_GPIO_PIN_LED_BOOTLOADER615palToggleLine(HAL_GPIO_PIN_LED_BOOTLOADER);616#endif617CanardCANFrame rx_frame {};618619//palToggleLine(HAL_GPIO_PIN_LED);620uint64_t timestamp;621AP_HAL::CANIface::CanIOFlags flags;622can_iface[i].receive(rxmsg, timestamp, flags);623memcpy(rx_frame.data, rxmsg.data, 8);624rx_frame.data_len = rxmsg.dlc;625rx_frame.id = rxmsg.id;626canardHandleRxFrame(&canard, &rx_frame, timestamp);627got_pkt = true;628}629if (!got_pkt) {630break;631}632}633}634#endif //#if HAL_USE_CAN635636/*637wrapper around broadcast638*/639static void canard_broadcast(uint64_t data_type_signature,640uint16_t data_type_id,641uint8_t &transfer_id,642uint8_t priority,643const void* payload,644uint16_t payload_len)645{646#if CH_CFG_USE_MUTEXES == TRUE647WITH_SEMAPHORE(can_mutex);648#endif649canardBroadcast(&canard,650data_type_signature,651data_type_id,652&transfer_id,653priority,654payload,655payload_len);656}657658659/*660handle waiting for a node ID661*/662static void can_handle_DNA(void)663{664if (canardGetLocalNodeID(&canard) != CANARD_BROADCAST_NODE_ID) {665return;666}667668if (AP_HAL::millis() < send_next_node_id_allocation_request_at_ms) {669return;670}671672send_next_node_id_allocation_request_at_ms =673AP_HAL::millis() + UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MIN_REQUEST_PERIOD_MS +674get_random_range(UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MAX_FOLLOWUP_DELAY_MS);675676// Structure of the request is documented in the DSDL definition677// See http://uavcan.org/Specification/6._Application_level_functions/#dynamic-node-id-allocation678uint8_t allocation_request[CANARD_CAN_FRAME_MAX_DATA_LEN - 1];679allocation_request[0] = (uint8_t)(CANARD_BROADCAST_NODE_ID << 1U);680681if (node_id_allocation_unique_id_offset == 0) {682allocation_request[0] |= 1; // First part of unique ID683}684685uint8_t my_unique_id[sizeof(uavcan_protocol_dynamic_node_id_Allocation::unique_id.data)];686readUniqueID(my_unique_id);687688static const uint8_t MaxLenOfUniqueIDInRequest = 6;689uint8_t uid_size = (uint8_t)(sizeof(uavcan_protocol_dynamic_node_id_Allocation::unique_id.data) - node_id_allocation_unique_id_offset);690if (uid_size > MaxLenOfUniqueIDInRequest) {691uid_size = MaxLenOfUniqueIDInRequest;692}693694memmove(&allocation_request[1], &my_unique_id[node_id_allocation_unique_id_offset], uid_size);695696// Broadcasting the request697canard_broadcast(UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_SIGNATURE,698UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_ID,699node_id_allocation_transfer_id,700CANARD_TRANSFER_PRIORITY_LOW,701&allocation_request[0],702(uint16_t) (uid_size + 1));703704// Preparing for timeout; if response is received, this value will be updated from the callback.705node_id_allocation_unique_id_offset = 0;706}707708static void send_node_status(void)709{710uint8_t buffer[UAVCAN_PROTOCOL_NODESTATUS_MAX_SIZE];711node_status.uptime_sec = AP_HAL::millis() / 1000U;712713uint32_t len = uavcan_protocol_NodeStatus_encode(&node_status, buffer, true);714715static uint8_t transfer_id; // Note that the transfer ID variable MUST BE STATIC (or heap-allocated)!716717canard_broadcast(UAVCAN_PROTOCOL_NODESTATUS_SIGNATURE,718UAVCAN_PROTOCOL_NODESTATUS_ID,719transfer_id,720CANARD_TRANSFER_PRIORITY_LOW,721buffer,722len);723}724725726/**727* This function is called at 1 Hz rate from the main loop.728*/729static void process1HzTasks(uint64_t timestamp_usec)730{731canardCleanupStaleTransfers(&canard, timestamp_usec);732733if (canardGetLocalNodeID(&canard) != CANARD_BROADCAST_NODE_ID) {734node_status.mode = fw_update.node_id?UAVCAN_PROTOCOL_NODESTATUS_MODE_SOFTWARE_UPDATE:UAVCAN_PROTOCOL_NODESTATUS_MODE_MAINTENANCE;735send_node_status();736}737}738739void can_set_node_id(uint8_t node_id)740{741initial_node_id = node_id;742}743744// check for a firmware update marker left by app745bool can_check_update(void)746{747bool ret = false;748#if HAL_RAM_RESERVE_START >= 256749struct app_bootloader_comms *comms = (struct app_bootloader_comms *)HAL_RAM0_START;750if (comms->magic == APP_BOOTLOADER_COMMS_MAGIC && comms->my_node_id != 0) {751can_set_node_id(comms->my_node_id);752fw_update.node_id = comms->server_node_id;753for (uint8_t i=0; i<FW_UPDATE_PIPELINE_LEN; i++) {754fw_update.reads[i].offset = i*sizeof(uavcan_protocol_file_ReadResponse::data.data);755}756memcpy(fw_update.path, comms->path, sizeof(uavcan_protocol_file_Path::path.data)+1);757ret = true;758// clear comms region759memset(comms, 0, sizeof(struct app_bootloader_comms));760}761#endif762#if defined(CAN1_BASE) && defined(RCC_APB1ENR_CAN1EN)763// check for px4 fw update. px4 uses the filter registers in CAN1764// to communicate with the bootloader. This only works on CAN1765if (!ret && stm32_was_software_reset()) {766uint32_t *fir = (uint32_t *)(CAN1_BASE + 0x240);767struct PACKED app_shared {768union {769uint64_t ull;770uint32_t ul[2];771uint8_t valid;772} crc;773uint32_t signature;774uint32_t bus_speed;775uint32_t node_id;776} *app = (struct app_shared *)&fir[4];777/* we need to enable the CAN peripheral in order to look at778the FIR registers.779*/780RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;781static const uint32_t app_signature = 0xb0a04150;782if (app->signature == app_signature &&783app->node_id > 0 && app->node_id < 128) {784// crc is in reversed word order in FIR registers785uint32_t sig[3];786memcpy((uint8_t *)&sig[0], (const uint8_t *)&app->signature, sizeof(sig));787const uint64_t crc = crc_crc64(sig, 3);788const uint32_t *crc32 = (const uint32_t *)&crc;789if (crc32[0] == app->crc.ul[1] &&790crc32[1] == app->crc.ul[0]) {791// reset signature so we don't get in a boot loop792app->signature = 0;793// setup node ID794can_set_node_id(app->node_id);795// and baudrate796baudrate = app->bus_speed;797ret = true;798}799}800}801#endif802return ret;803}804805void can_start()806{807node_status.vendor_specific_status_code = uint8_t(check_good_firmware());808node_status.mode = UAVCAN_PROTOCOL_NODESTATUS_MODE_MAINTENANCE;809810#if HAL_USE_CAN811// calculate optimal CAN timings given PCLK1 and baudrate812CanardSTM32CANTimings timings {};813canardSTM32ComputeCANTimings(STM32_PCLK1, baudrate, &timings);814cancfg.btr = CAN_BTR_SJW(0) |815CAN_BTR_TS2(timings.bit_segment_2-1) |816CAN_BTR_TS1(timings.bit_segment_1-1) |817CAN_BTR_BRP(timings.bit_rate_prescaler-1);818canStart(&CAND1, &cancfg);819#else820for (uint8_t i=0; i<HAL_NUM_CAN_IFACES; i++) {821can_iface[i].init(baudrate, AP_HAL::CANIface::NormalMode);822}823#endif824canardInit(&canard, (uint8_t *)canard_memory_pool, sizeof(canard_memory_pool),825onTransferReceived, shouldAcceptTransfer, NULL);826827if (initial_node_id != CANARD_BROADCAST_NODE_ID) {828canardSetLocalNodeID(&canard, initial_node_id);829}830831send_next_node_id_allocation_request_at_ms =832AP_HAL::millis() + UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MIN_REQUEST_PERIOD_MS +833get_random_range(UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MAX_FOLLOWUP_DELAY_MS);834835if (stm32_was_watchdog_reset()) {836node_status.vendor_specific_status_code = uint8_t(check_fw_result_t::FAIL_REASON_WATCHDOG);837}838}839840841void can_update()842{843// do one loop of CAN support. If we are doing a firmware update844// then loop until it is finished845do {846processTx();847processRx();848can_handle_DNA();849static uint32_t last_1Hz_ms;850uint32_t now = AP_HAL::millis();851if (now - last_1Hz_ms >= 1000) {852last_1Hz_ms = now;853process1HzTasks(AP_HAL::micros64());854}855if (fw_update.node_id != 0) {856send_fw_reads();857}858#if CH_CFG_ST_FREQUENCY >= 1000000859// give a bit of time for background processing860chThdSleepMicroseconds(200);861#endif862} while (fw_update.node_id != 0);863}864865// printf to CAN LogMessage for debugging866void can_vprintf(const char *fmt, va_list ap)867{868// only on H7 for now, where we have plenty of flash869#if defined(STM32H7)870uavcan_protocol_debug_LogMessage pkt {};871uint8_t buffer[UAVCAN_PROTOCOL_DEBUG_LOGMESSAGE_MAX_SIZE];872uint32_t n = vsnprintf((char*)pkt.text.data, sizeof(pkt.text.data), fmt, ap);873pkt.text.len = MIN(n, sizeof(pkt.text.data));874875uint32_t len = uavcan_protocol_debug_LogMessage_encode(&pkt, buffer, true);876static uint8_t logmsg_transfer_id;877878canard_broadcast(UAVCAN_PROTOCOL_DEBUG_LOGMESSAGE_SIGNATURE,879UAVCAN_PROTOCOL_DEBUG_LOGMESSAGE_ID,880logmsg_transfer_id,881CANARD_TRANSFER_PRIORITY_LOW,882buffer,883len);884#endif // defined(STM32H7)885}886887// printf to CAN LogMessage for debugging888void can_printf(const char *fmt, ...)889{890// only on H7 for now, where we have plenty of flash891#if defined(STM32H7)892va_list ap;893va_start(ap, fmt);894can_vprintf(fmt, ap);895va_end(ap);896#endif // defined(STM32H7)897}898899void can_printf_severity(uint8_t severity, const char *fmt, ...)900{901// only on H7 for now, where we have plenty of flash902#if defined(STM32H7)903va_list ap;904va_start(ap, fmt);905can_vprintf(fmt, ap);906va_end(ap);907#endif908}909910911#endif // HAL_USE_CAN912913914