/*! * @file can.c * @author Francesco Gritti * @brief can peripheral driver implementation * * This files implements all CAN bus funcitonalities * * * */ #include "mcu.h" #ifdef use_can #include "can.h" #include #include //#define CAN_IMPLEMENT_FIFO //#define CAN_FIFO_DEPTH #define CAN_MINIMAL_IMPLEMENTATION /* CAN bus Flasher - flashare il mcu con del nuovo codice dalla periferica can bus. - funzionare correttamente con altri dispositivi connessi al bus - error detection / retransmission STRUTTURA CODICE - è necessario che TUTTO il codice eseguito durante la fase di programmazione sia caricato in RAM => Anche l' Interrupt Handler, il codice per inviare dati sul CAN e qualsiasi di queste cose. Durante la fase di programmazione è fondamentale assicurarsi che non vengano lanciati interrupt o altro da periferiche (a parte il CAN) => DISABILITO INTERRUPT QUANDO ENTRO IN ROUTINE PROGRAMMAZIONE PROCEDURA 1) il rpogrammatore invia il comando PROGRAMMING_INIT con payload l'ID del dispositivo che si vuole riprogrammare 2) Il dispositivo corrispondente entra nella routine can_flasher_SRAM mentre tutti gli altri smettono di inviare comandi sul can bus dal momento che questo potrebbe interferire o rallentare la procedura di caricamento del nuovo firmware 3) il programmatore invia ogni un pacchetto da 8 byte alla volta e attende l'ACK del dispositivo che si sta riprogrammando. Questo è necessario poichè lo standard CAN richiede che ogni dispositivo connesso alla rete invii un messaggio di acknowledge quando riceve un messaggio con CRC corretto, anche se non è tra i suoi filtri. 3-b) in caso di errore, sia che venga ricevuto un NACK al messaggio CAN, sia che NON venga ricevuto alcun messaggio da parte del sispositivo in programmazione si prova a reinviare il blocco dati per un numero N di volte 4) una volta inviato l'intero firmware, il programmator einvia il comando PROGRAMMING_EXIT con cui notifica al dispositivo di resettarsi e a tutti gli altri di riprendere le loro normali funzioni * */ #ifdef CAN_IMPLEMENT_FIFO FIFO_CREATE(canMessage_t, CAN_TxFIFO, CAN_FIFO_DEPTH); #endif /* Static functions */ static u8 s_mailbox_transmit (u8 mbx, u16 ID, u8 * data, u8 len, canIdExtension idType, canDataRemote dataRemote); static u8 s_abort_mailbox_transmit (u8 mbx); static void s_can_read_message (u8 mailbox); __attribute__ ((weak)) void CAN_TxOverride (void) {} __attribute__ ((weak)) void CAN_busError (void) {} __attribute__ ((weak)) void CAN_onNACK (canMessage_t *message) {(void) message;} __attribute__ ((weak)) void can_message_received (canMessage_t *message) {(void) message;} canNetworkStatus_t can_get_network_status () { canNetworkStatus_t netStat = { .TEC = (CAN->ESR & CAN_ESR_TEC_Msk) >> CAN_ESR_TEC_Pos, .REC = (CAN->ESR & CAN_ESR_REC_Msk) >> CAN_ESR_REC_Pos, .error_warning = (CAN->ESR & CAN_ESR_BOFF_Msk)>> CAN_ESR_BOFF_Pos, .error_passive = (CAN->ESR & CAN_ESR_EPVF_Msk)>> CAN_ESR_EPVF_Pos, .bus_off = (CAN->ESR & CAN_ESR_EWGF_Msk)>> CAN_ESR_EWGF_Pos, .LEC = (CAN->ESR & CAN_ESR_LEC_Msk) >> CAN_ESR_LEC_Pos, }; return netStat; } void CEC_CAN_IRQHandler (void) { // check if there are messages to be read in FIFO0 and FIFO1 u8 nMessagesFIFO0 = (CAN->RF0R & CAN_RF0R_FMP0_Msk) >> CAN_RF0R_FMP0_Pos; u8 nMessagesFIFO1 = (CAN->RF1R & CAN_RF1R_FMP1_Msk) >> CAN_RF1R_FMP1_Pos; for (; nMessagesFIFO0>0; nMessagesFIFO0--) { s_can_read_message (0); CAN->RF0R |= CAN_RF0R_RFOM0; } for (; nMessagesFIFO1>0; nMessagesFIFO1--) { s_can_read_message (1); CAN->RF1R |= CAN_RF1R_RFOM1; } #ifdef CAN_IMPLEMENT_FIFO // check if one of the mailbox has completed transmission if ((CAN->TSR & (CAN_TSR_TXOK0 | CAN_TSR_TXOK1 | CAN_TSR_TXOK2)) != 0) { // clear all mailboxes status flag CAN->TSR |= CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2; if (!FIFO_isEmpty (&CAN_TxFIFO)) { // get the message from FIFO canMessage_t *message = (canMessage_t*) FIFO_Pop (&CAN_TxFIFO); // make sure pointer is valid if (message != 0) { u8 mbxCode = (CAN->TSR & (CAN_TSR_CODE_Msk)) >> CAN_TSR_CODE_Pos; // make sure that a mailbox is actually free. If it is, it is the one identified // by mbxCode if ((CAN->TSR & (CAN_TSR_TME0_Msk | CAN_TSR_TME1_Msk | CAN_TSR_TME2_Msk)) != 0) { s_mailbox_transmit (mbxCode,message->id, message->data, message->data_len, message->std_ext_id, message->data_remote_frame); } else { FIFO_Append (&CAN_TxFIFO, (u8*)message); } } } } #endif } void s_can_read_message (u8 mailbox) { canMessage_t rxMessage; rxMessage.std_ext_id = (CAN->sFIFOMailBox[mailbox].RIR & CAN_RI0R_IDE) != 0; rxMessage.data_remote_frame = (CAN->sFIFOMailBox[mailbox].RIR & CAN_RI0R_RTR) != 0; rxMessage.id = (rxMessage.std_ext_id == canSTD_ID ? (CAN->sFIFOMailBox[mailbox].RIR & CAN_RI0R_STID) >> CAN_RI0R_STID_Pos : (CAN->sFIFOMailBox[mailbox].RIR & CAN_RI0R_EXID) >> CAN_RI0R_EXID_Pos); rxMessage.data_len = (CAN->sFIFOMailBox[mailbox].RDTR & CAN_RDT0R_DLC_Msk) >> CAN_RDT0R_DLC_Pos; rxMessage.data [0] = (CAN->sFIFOMailBox[mailbox].RDLR & CAN_RDL0R_DATA0) >> CAN_RDL0R_DATA0_Pos; rxMessage.data [1] = (CAN->sFIFOMailBox[mailbox].RDLR & CAN_RDL0R_DATA1) >> CAN_RDL0R_DATA1_Pos; rxMessage.data [2] = (CAN->sFIFOMailBox[mailbox].RDLR & CAN_RDL0R_DATA2) >> CAN_RDL0R_DATA2_Pos; rxMessage.data [3] = (CAN->sFIFOMailBox[mailbox].RDLR & CAN_RDL0R_DATA3) >> CAN_RDL0R_DATA3_Pos; rxMessage.data [4] = (CAN->sFIFOMailBox[mailbox].RDHR & CAN_RDH0R_DATA4) >> CAN_RDH0R_DATA4_Pos; rxMessage.data [5] = (CAN->sFIFOMailBox[mailbox].RDHR & CAN_RDH0R_DATA5) >> CAN_RDH0R_DATA5_Pos; rxMessage.data [6] = (CAN->sFIFOMailBox[mailbox].RDHR & CAN_RDH0R_DATA6) >> CAN_RDH0R_DATA6_Pos; rxMessage.data [7] = (CAN->sFIFOMailBox[mailbox].RDHR & CAN_RDH0R_DATA7) >> CAN_RDH0R_DATA7_Pos; can_message_received (&rxMessage); } i8 can_init (void) { RCC_CAN1_CLK_ENABLE(); // configure GPIO GPIO_AF_PP (CAN_TX); GPIO_AF_PP (CAN_RX); GPIO_SET_AFRH (CAN_TX, 0x04); GPIO_SET_AFRH (CAN_RX, 0x04); // enter initialization mode u32 startTick = flib_GetTick(); CAN->MCR |= CAN_MCR_INRQ; // wait 10ms for the CAN peripheral to enter initialization mode // otherwise, return error while ((CAN->MSR & CAN_MSR_INAK) == 0U) { if((flib_GetTick() - startTick) > 10) return CAN_INIT_ERROR; } CAN->MCR &= ~CAN_MCR_SLEEP; //CAN_BTR_LBKM CAN->BTR = ((CAN_SJW-1) << CAN_BTR_SJW_Pos) | ((CAN_SEG2-1) << CAN_BTR_TS2_Pos) | ((CAN_SEG1-1) << CAN_BTR_TS1_Pos) | ((CAN_PRESCALER-1) << CAN_BTR_BRP_Pos); // enable interrupt on error and on FIFO 0/1 not empty and TX request complete (abort or transmit) CAN->IER = CAN_IER_ERRIE | CAN_IER_FMPIE1 | CAN_IER_FMPIE0 | CAN_IER_TMEIE; //CAN->MSR |= CAN_MSR_ERRI; // enable interrupt request NVIC_SetPriority(CEC_CAN_IRQn, 2); NVIC_EnableIRQ(CEC_CAN_IRQn); // enter normal mode. Do not wait for peripheral acknowledge since if there // are transmissions going on on the bus it might take a while for the // peripheral to enter normal mode and it is not necessary to wait CAN->MCR &= ~CAN_MCR_INRQ; return CAN_INIT_OK; } void can_init_minimal (void) { RCC_CAN1_CLK_ENABLE(); // configure GPIO GPIO_AF_PP (CAN_TX); GPIO_AF_PP (CAN_RX); GPIO_SET_AFRH (CAN_TX, 0x04); GPIO_SET_AFRH (CAN_RX, 0x04); CAN->MCR |= CAN_MCR_INRQ; while ((CAN->MSR & CAN_MSR_INAK) == 0U); CAN->MCR &= ~CAN_MCR_SLEEP; //CAN_BTR_LBKM CAN->BTR = ((CAN_SJW-1) << CAN_BTR_SJW_Pos) | ((CAN_SEG2-1) << CAN_BTR_TS2_Pos) | ((CAN_SEG1-1) << CAN_BTR_TS1_Pos) | ((CAN_PRESCALER-1) << CAN_BTR_BRP_Pos); // enable interrupt on error and on FIFO 0/1 not empty and TX request complete (abort or transmit) CAN->IER = CAN_IER_ERRIE | CAN_IER_FMPIE1 | CAN_IER_FMPIE0 | CAN_IER_TMEIE; // enable interrupt request NVIC_SetPriority(CEC_CAN_IRQn, 2); NVIC_EnableIRQ(CEC_CAN_IRQn); CAN->MCR &= ~CAN_MCR_INRQ; // wait for peripheral to enter normal mode } i8 can_transmit_f32 (u16 ID, f32 data, canIdExtension idType, canDataRemote dataRemote, canTxOverrideBehaviorE override) { f32 localData = data; u8 *buffer = (u8*) (&localData); return can_transmit (ID, buffer, 4, idType, dataRemote, override); } i8 can_transmit_u32 (u16 ID, u32 data, canIdExtension idType, canDataRemote dataRemote, canTxOverrideBehaviorE override) { u32 localData = data; u8 *buffer = (u8*) (&localData); return can_transmit (ID, buffer, 4, idType, dataRemote, override); } f32 can_extract_f32 (u8 *payload) { f32* data = (f32*) (payload); return *data; } u32 can_extract_u32 (u8 *payload) { u32* data = (u32*) (payload); return *data; } // with 16bit registers only STD IDs can be mapped u8 can_config_filter_mask_mode_16bit (u8 filterId, canFIFO assignedFIFO, canDataRemote dataRemote, u16 id1, u16 id2, u16 mask1, u16 mask2) { if (filterId >= N_FILTERS) return 0; u32 filterBitMask = (u32)(1 << filterId); // deactivate filter CAN->FA1R &= ~filterBitMask; // enter filter configuration mode CAN->FMR |= CAN_FMR_FINIT; // set mask mode CAN->FM1R &= ~ filterBitMask; // set scale mode to 16 bit CAN->FS1R &= ~ filterBitMask; u32 mask1_reg = ((u32)mask1 << 5); u32 id1_reg = ((u32) id1 << 5); u32 mask2_reg = ((u32)mask2 << 5); u32 id2_reg = ((u32) id2 << 5); if (dataRemote == canREMOTE_FRAME) { mask1_reg |= (1 << 4); id1_reg |= (1 << 4); mask2_reg |= (1 << 4); id2_reg |= (1 << 4); } CAN->sFilterRegister [filterId].FR1 = (mask1_reg << 16) | id1_reg; CAN->sFilterRegister [filterId].FR2 = (mask2_reg << 16) | id2_reg; // assign FIFO if (assignedFIFO == canFIFO_0) CAN->FFA1R &= ~filterBitMask; else CAN->FFA1R |= filterBitMask; CAN->FMR &= ~CAN_FMR_FINIT; return 1; } // can map both STD and EXTENDED IDs u8 can_config_filter_mask_mode_32bit (u8 filterId, canFIFO assignedFIFO, canIdExtension extendedId, canDataRemote dataRemote, u32 id, u32 mask) { if (filterId >= N_FILTERS) return 0; u32 filterBitMask = (u32)(1 << filterId); // deactivate filter CAN->FA1R &= ~filterBitMask; // enter filter configuration mode CAN->FMR |= CAN_FMR_FINIT; // set mask mode CAN->FM1R &= ~ filterBitMask; // set scale mode to 32 bit CAN->FS1R |= filterBitMask; if (extendedId == canSTD_ID) { CAN->sFilterRegister [filterId].FR1 = ((u32)id << 21); CAN->sFilterRegister [filterId].FR2 = ((u32)mask << 21); } else { CAN->sFilterRegister [filterId].FR1 = ((u32)id << 3) | (1 << 2); CAN->sFilterRegister [filterId].FR2 = ((u32)mask << 3) | (1 << 2); if (dataRemote == canREMOTE_FRAME) { CAN->sFilterRegister [filterId].FR1 |= (1<<1); CAN->sFilterRegister [filterId].FR2 |= (1<<1); } } // assign FIFO if (assignedFIFO == canFIFO_0) CAN->FFA1R &= ~filterBitMask; else CAN->FFA1R |= filterBitMask; CAN->FMR &= ~CAN_FMR_FINIT; return 1; } u8 can_config_filter_list_mode_16bit (u8 filterId, canFIFO assignedFIFO, canDataRemote dataRemote, u16 id1, u16 id2, u16 id3, u16 id4) { if (filterId >= N_FILTERS) return 0; u32 filterBitMask = (u32)(1 << filterId); CAN->FA1R &= ~filterBitMask; // deactivate filter CAN->FMR |= CAN_FMR_FINIT; // enter filter configuration mode CAN->FM1R |= filterBitMask; // set list mode CAN->FS1R &= ~ filterBitMask; // set scale mode to 16 bit u32 id1_reg = ((u32)id1 << 5); u32 id2_reg = ((u32)id2 << 5); u32 id3_reg = ((u32)id3 << 5); u32 id4_reg = ((u32)id4 << 5); if (dataRemote == canREMOTE_FRAME) { id1_reg |= (1 << 4); id2_reg |= (1 << 4); id3_reg |= (1 << 4); id4_reg |= (1 << 4); } CAN->sFilterRegister [filterId].FR1 = (id1_reg << 16) | id2_reg; CAN->sFilterRegister [filterId].FR2 = (id3_reg << 16) | id4_reg; // assign FIFO if (assignedFIFO == canFIFO_0) CAN->FFA1R &= ~filterBitMask; else CAN->FFA1R |= filterBitMask; CAN->FMR &= ~CAN_FMR_FINIT; return 1; } u8 can_config_filter_list_mode_32bit (u8 filterId, canFIFO assignedFIFO, canIdExtension extendedId, canDataRemote dataRemote, u32 id1, u32 id2) { if (filterId >= N_FILTERS) return 0; u32 filterBitMask = (u32)(1 << filterId); CAN->FA1R &= ~filterBitMask; // deactivate filter CAN->FMR |= CAN_FMR_FINIT; // enter filter configuration mode CAN->FM1R |= filterBitMask; // set list mode CAN->FS1R |= filterBitMask; // set scale mode to 32 bit if (extendedId == canSTD_ID) { CAN->sFilterRegister [filterId].FR1 = (id1 << 21); CAN->sFilterRegister [filterId].FR2 = (id2 << 21); } else { CAN->sFilterRegister [filterId].FR1 = (id1 << 3) | (1 << 2); CAN->sFilterRegister [filterId].FR2 = (id2 << 3) | (1 << 2); if (dataRemote == canREMOTE_FRAME) { CAN->sFilterRegister [filterId].FR1 |= (1<<1); CAN->sFilterRegister [filterId].FR2 |= (1<<1); } } // assign FIFO if (assignedFIFO == canFIFO_0) CAN->FFA1R &= ~filterBitMask; else CAN->FFA1R |= filterBitMask; CAN->FMR &= ~CAN_FMR_FINIT; return 1; } void can_enable_filter (u8 filterId) { if (filterId >= N_FILTERS) return; CAN->FA1R |= (u32)(1<= N_FILTERS) return; CAN->FA1R &= ~ (u32)(1< 8 || len == 0) return -1; // check if the mailbox addressed by u8 mbxCode = (CAN->TSR & (CAN_TSR_CODE_Msk)) >> CAN_TSR_CODE_Pos; // check if at least one mailbox is free. In this case, its id is the one // previously read from CAN_TSR if ((CAN->TSR & (CAN_TSR_TME0_Msk | CAN_TSR_TME1_Msk | CAN_TSR_TME2_Msk)) != 0) { s_mailbox_transmit (mbxCode, ID, data, len, idType, dataRemote); return CAN_TX_OK; } // in case none of the mailbox is free, if OVERRIDE LOW PRIO flag was set, look // for the mailbox with the message of lowest priority, abort its transmission // and request the transmission of the new message. The mailbox code with lowest // priority is the one read from CAN_TSR else if (override == CAN_OVERRIDE_LOW_PRIO) { if (s_abort_mailbox_transmit (mbxCode) && s_mailbox_transmit ((mbxCode-1), ID, data, len, idType, dataRemote)) { return CAN_TX_OK; } return CAN_TX_ERROR; } else if (override == CAN_OVERRIDE_IF_HIGHER_PRIO) { // get lowest priority mailbox message ID type (STD or extended) u8 mbxIdType = (CAN->sTxMailBox [mbxCode].TIR & CAN_TI0R_IDE_Msk) >> CAN_TI0R_IDE_Pos; // make sure the two messages have the same ID type (STD or extended) if (mbxIdType == idType) { // standard address case if (mbxIdType == 0) { u16 stdId = (CAN->sTxMailBox [mbxCode].TIR & CAN_TI0R_STID_Msk) >> CAN_TI0R_STID_Pos; // in case the message for which transmission was requested has higher priority, // abort transmission of the previous and request transmission of the current one if (ID < stdId) { if (s_abort_mailbox_transmit (mbxCode) && s_mailbox_transmit (mbxCode-1, ID, data, len, idType, dataRemote)) { return CAN_TX_OK; } return CAN_TX_ERROR; } } // extended address else { u32 extID = (CAN->sTxMailBox [mbxCode].TIR & CAN_TI0R_EXID_Msk) >> CAN_TI0R_EXID_Pos; if (ID < extID) { if (s_abort_mailbox_transmit (mbxCode) && s_mailbox_transmit (mbxCode-1, ID, data, len, idType, dataRemote)) { return CAN_TX_OK; } return CAN_TX_ERROR; } } } } else if (override == CAN_OVERRIDE_ID) { // look for the given ID in the transmit mailboxes for (u8 mbx=0; mbx <3; mbx++) { u8 mbxExtId = (CAN->sTxMailBox [mbxCode].TIR & CAN_TI0R_IDE_Msk) >> CAN_TI0R_IDE_Pos; if (mbxExtId == idType) { if (mbxExtId) { u32 extId = (CAN->sTxMailBox [mbxCode].TIR & CAN_TI0R_EXID_Msk) >> CAN_TI0R_EXID_Pos; if (extId == ID) { if (s_abort_mailbox_transmit (mbxCode) && s_mailbox_transmit (mbxCode-1, ID, data, len, idType, dataRemote)) { return CAN_TX_OK; } return CAN_TX_ERROR; } } else { u16 stdId = (CAN->sTxMailBox [mbxCode].TIR & CAN_TI0R_STID_Msk) >> CAN_TI0R_STID_Pos; if (stdId == ID) { if (s_abort_mailbox_transmit (mbxCode) && s_mailbox_transmit (mbxCode-1, ID, data, len, idType, dataRemote)) { return CAN_TX_OK; } return CAN_TX_ERROR; } } } } } // if none of the previous transmits were successful, append the message to the FIFO // disable CAN interrupt so that ISR cannot change FIFO content while updating here // NOTE: it is important disable all CAN interrupt since inside ISR the flag for TX // complete is always checked, even if the actual source if the interrupt was another // flag. NVIC_DisableIRQ(CEC_CAN_IRQn); #ifdef CAN_IMPLEMENT_FIFO if (!FIFO_isFull (&CAN_TxFIFO)) { // construct the message canMessage_t message = { .std_ext_id = idType, .data_remote_frame = dataRemote, .id = ID, .data_len = len }; memcpy (message.data, data, len); FIFO_Append (&CAN_TxFIFO, (u8*)&message); NVIC_EnableIRQ(CEC_CAN_IRQn); return CAN_TX_OK; } else { NVIC_EnableIRQ(CEC_CAN_IRQn); CAN_TxOverride (); return CAN_TX_ERROR; } #endif return CAN_TX_ERROR; } void can_enable_automatic_retransmission (void) { // write to MCR register can be done outside initialization mode CAN->MCR &= ~CAN_MCR_NART; } void can_desable_automatic_retransmission (void) { CAN->MCR |= CAN_MCR_NART; } u8 s_abort_mailbox_transmit (u8 mbx) { u32 abortMAsk = 0; u32 testMask = 0; switch (mbx) { case 1: abortMAsk = CAN_TSR_ABRQ0_Msk; testMask = CAN_TSR_TME0_Msk; break; case 2: abortMAsk = CAN_TSR_ABRQ1_Msk; testMask = CAN_TSR_TME1_Msk; break; case 3: abortMAsk = CAN_TSR_ABRQ2_Msk; testMask = CAN_TSR_TME2_Msk; break; default: return 0; } CAN->TSR |= abortMAsk; u32 startTick = flib_GetTick (); while ((CAN->TSR & testMask) == 0) { if((flib_GetTick() - startTick) > 10) return 0; // flag error } return 1; } u8 s_mailbox_transmit (u8 mbx, u16 ID, u8 * data, u8 len, canIdExtension idType, canDataRemote dataRemote) { // make sure the mailbox is actually free if (mbx >= 3 || (CAN->TSR & (CAN_TSR_TME0_Msk << mbx)) == 0) return 0; CAN->sTxMailBox[mbx].TIR = ID << (idType == canSTD_ID ? CAN_TI0R_STID_Pos : CAN_TI0R_EXID_Pos); if (idType == canEXTENDED_ID) CAN->sTxMailBox[mbx].TIR |= CAN_TI0R_IDE_Msk; if (dataRemote == canREMOTE_FRAME) CAN->sTxMailBox[mbx].TIR |= CAN_TI0R_RTR_Msk; // set data length register CAN->sTxMailBox[mbx].TDTR = (len & 0x0f); // reset data registers CAN->sTxMailBox[mbx].TDLR = 0x0000; CAN->sTxMailBox[mbx].TDHR = 0x0000; CAN->sTxMailBox[mbx].TDLR |= data[0]; if (len > 1) CAN->sTxMailBox[mbx].TDLR |= (data[1] << 8); if (len > 2) CAN->sTxMailBox[mbx].TDLR |= (data[2] << 16); if (len > 3) CAN->sTxMailBox[mbx].TDLR |= (data[3] << 24); if (len > 4) CAN->sTxMailBox[mbx].TDHR |= data[4]; if (len > 5) CAN->sTxMailBox[mbx].TDHR |= (data[5] << 8); if (len > 6) CAN->sTxMailBox[mbx].TDHR |= (data[6] << 16); if (len > 7) CAN->sTxMailBox[mbx].TDHR |= (data[7] << 24); // request transmission CAN->sTxMailBox[mbx].TIR |= CAN_TI0R_TXRQ_Msk; return 1; } #endif