//***************************************************************************** // (C) Automotive Lighting Reutlingen GmbH // Tuebinger Strasse 123, 72762 Reutlingen, Germany // // Automotive Lighting Reutlingen GmbH owns all the rights to this work. // This work shall not be copied, reproduced, used, modified, transferred // or its information shall not be disclosed without the prior written // authorization of Automotive Lighting Reutlingen GmbH. //***************************************************************************** //----------------------------------------------------------------------------- /// \file ISO15765.c /// /// \brief /// /// \descr /// /// Additional information can be found in the design description (Link: /// MDDD) /// /// \author Ramona-Andreea Bolboaca (f33613c) /// mailTo:ramona-andreea.bolboaca(at)magnetimarelli.com //----------------------------------------------------------------------------- #include #include #include #include #include #include #if ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD) #include #elif ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CAN) == CFG_ISO15765_COM_TYPE_CAN) #ifndef CFG_TYP_PRJ2 #include #elif (CFG_TYP_PRJ2 != CFG_Prj_LED_6_2) #include #endif #endif ///////////////////////////////////////////////////////////////////// STATIC_AL uint8 arCF_Frame[CF_MAX_FRAME_SIZE]; // frame buffer STATIC_AL uint8 uCF_SN; // frame sequence number STATIC_AL uint16 uCF_DL; // complete data length STATIC_AL uint16 uCF_Size; // actual data length STATIC_AL uint8 uCF_STmin; // own STmin parameter, send to peer STATIC_AL uint8 uCF_BS; // own BS parameter, send to peer STATIC_AL uint8 uCF_BScnt; // own BS counter, internal STATIC_AL uint8 uFC_STmin; // STmin parameter, received by FC frame STATIC_AL uint8 uFC_FS; // FS parameter, received by FC frame #if (((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CAN) == CFG_ISO15765_COM_TYPE_CAN) || \ ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD)) STATIC_AL uint8 uFC_BS; // BS parameter, received by FC frame #else STATIC_AL boolean boCFNeeded = FALSE; #endif #ifndef PADDING_BYTE #define PADDING_BYTE 0xFFU #endif #ifdef UNIT_TEST boolean boUtRetVal = TRUE; #endif ////////////////////////////////////////////////////////////////////////// // timer functions typedef struct { uint16 uID; TIMER tTimer; void (*fnc)(void); } TTIMER; STATIC_AL TTIMER arTimers[MAX_TIMERS]; #if (((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CAN) == CFG_ISO15765_COM_TYPE_CAN) || \ ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD)) STATIC_AL void ISO15765TimerTX_FC(void); #endif STATIC_AL void ISO15765TimerTX_CF(void); STATIC_AL void ISO15765TimerRX_CF(void); STATIC_AL boolean ISO15765TimerStart(uint16 ID, uint16 Period, void(*fnc)(void)); STATIC_AL boolean ISO15765TimerStop (uint8 ID); STATIC_AL boolean LowerSend (uint8* data, uint16 length); STATIC_AL boolean UpperSend (uint8* data, volatile uint16 length); STATIC_AL void UpperError(uint8 error); // timer IDs #define TX_CF 1U #define TX_FC 2U #define RX_CF 3U ////////////////////////////////////////////////////////////////////////// // protocol definitions #define NPCI_SINGLE_FRAME 0x00U #define NPCI_FIRST_FRAME 0x10U #define NPCI_CONSECUTIVE_FRAME 0x20U #define NPCI_FLOW_CONTROL 0x30U #define NPCI_ERROR_FRAME 0x40U // CAN-FD - maximum payload 64 bytes #if ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD) #define BUFFER_SIZE 64U #if defined(EXT_ADR) // extended addressing #define SF_DATALENGTH 61U #define SF_DATALENGTH_SHORT 6U #define FF_DATALENGTH 61U #define CF_DATALENGTH 62U #define FC_DATALENGTH 3U #define EXT_STARTBYTE 1U #define N_SA 0U #else // normal addressing #define SF_DATALENGTH 62U #define SF_DATALENGTH_SHORT 7U #define FF_DATALENGTH 62U #define CF_DATALENGTH 63U #define FC_DATALENGTH 3U #define EXT_STARTBYTE 0U #endif // CAN - maximum payload 8 bytes #else #define BUFFER_SIZE 8U #if defined(EXT_ADR) // extended addressing #define SF_DATALENGTH 6U #define FF_DATALENGTH 5U #define CF_DATALENGTH 6U #define FC_DATALENGTH 3U #define EXT_STARTBYTE 1U #define N_SA 0U #else // normal addressing #define SF_DATALENGTH 7U #define FF_DATALENGTH 6U #define CF_DATALENGTH 7U #define FC_DATALENGTH 3U #define EXT_STARTBYTE 0U #endif #endif // timing #define N_As 1000U #define N_Ar 1000U #define N_Bs 1000U #define N_Cr 1000U //----------------------------------------------------------------------------- /// \brief ISO15765Init /// /// \descr /// /// \param - /// /// \return void //----------------------------------------------------------------------------- void ISO15765Init(void) { uCF_DL = 0; uCF_STmin = STmin; uCF_BS = BS; // init timer (void)memset(arTimers, 0, sizeof(TTIMER) * MAX_TIMERS); } //----------------------------------------------------------------------------- /// \brief ISO15765Send /// /// \descr /// /// \param - /// /// \return boolean //----------------------------------------------------------------------------- // transmit data to lower layer boolean ISO15765Send(uint8* data, uint16 length) { boolean boRet; #if ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_LIN) == CFG_ISO15765_COM_TYPE_LIN) boCFNeeded = FALSE; #endif if (length > 4095U) { // TP protocol can handle max 4095 bytes // data size too big boRet = FALSE; } else if (length <= SF_DATALENGTH) { // send SF #if ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD) arCF_Frame[0] = NPCI_SINGLE_FRAME; arCF_Frame[1] = (uint8)length & 0xFFU; (void)memcpy(&arCF_Frame[2], data, length); boRet = LOWER_TRANSMIT(arCF_Frame, length + 2); #else arCF_Frame[0] = (NPCI_SINGLE_FRAME | ((uint8)length & 0x0FU)); (void)memcpy(&arCF_Frame[1], data, length); boRet = LOWER_TRANSMIT(arCF_Frame, length + 1U); #endif } else { // send FF uint8 buf[CF_DATALENGTH + 1U]; buf[0] = (NPCI_FIRST_FRAME | ((uint8)(length >> 8U) & 0x0FU)); buf[1] = (uint8)length; (void)memcpy(&buf[2], data, FF_DATALENGTH); (void)memcpy(&arCF_Frame[0], data, length); uCF_SN = 1U; // init sequence number uCF_DL = FF_DATALENGTH; // FF data length uCF_BScnt = 0U; // init block counter uCF_Size = length; // init length if ((LOWER_TRANSMIT(buf, FF_DATALENGTH + 2U)) == TRUE) { // sending to lower layer was successful // start timer for FC reception check #if (((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CAN) == CFG_ISO15765_COM_TYPE_CAN) || \ ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD)) (void)ISO15765TimerStart(TX_FC, N_Bs, ISO15765TimerTX_FC); #else boCFNeeded = TRUE; #endif boRet = TRUE; } #ifndef UNIT_TEST else { // FF could not be send uCF_DL = 0; boRet = FALSE; } #endif // !UNIT_TEST } return boRet; } //----------------------------------------------------------------------------- /// \brief ISO15765SendCF /// /// \descr /// /// \param - /// /// \return boolean //----------------------------------------------------------------------------- STATIC_AL boolean ISO15765SendCF(void) { boolean boRet = FALSE; uint8 buf[CF_DATALENGTH + 1U]; #if ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_LIN) == CFG_ISO15765_COM_TYPE_LIN) boCFNeeded= FALSE; #endif buf[0] = (NPCI_CONSECUTIVE_FRAME | (uCF_SN++ & 0x0FU)); (void)memcpy(&buf[1], &arCF_Frame[uCF_DL], CF_DATALENGTH); const uint16 unTransmitSize = ((uint16)(uCF_Size - uCF_DL) >= (uint16)CF_DATALENGTH) ? (uint16)(CF_DATALENGTH + 1U) : (uint16)((uint32)((uint32)uCF_Size + 1U) - (uint32)uCF_DL); if ((LOWER_TRANSMIT(buf, unTransmitSize)) == TRUE) { // sending to lower layer was successful uCF_DL += CF_DATALENGTH; // check if frame is complete if (uCF_DL >= uCF_Size) { // frame completely sent uCF_DL = 0; } else { #if (((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CAN) == CFG_ISO15765_COM_TYPE_CAN) || \ ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD)) // check BS if ((uCF_BScnt++ >= uFC_BS) && (uFC_BS != 0U)) { // block completely sent - wait for FC from receiver // trigger timer for next FC reception (void)ISO15765TimerStart(TX_FC, N_Bs, ISO15765TimerTX_FC); } else { // trigger timer for next CF frame (void)ISO15765TimerStart(TX_CF, uFC_STmin, ISO15765TimerTX_CF); } #else boCFNeeded = TRUE; #endif } boRet = TRUE; } #ifndef UNIT_TEST else { // transmission error on lower layer - abort uCF_DL = 0; // inform upper layer UPPER_ERROR(ERROR_TX); boRet = FALSE; } #endif // !UNIT_TEST return boRet; } //----------------------------------------------------------------------------- /// \brief ISO15765SendCFAbort /// /// \descr /// /// \param - /// /// \return void //----------------------------------------------------------------------------- STATIC_AL void ISO15765SendCFAbort(void) { uCF_DL = 0; // inform upper layer UPPER_ERROR(ERROR_TX); } //----------------------------------------------------------------------------- /// \brief ISO15765SendFC /// /// \descr /// /// \param - /// /// \return boolean //----------------------------------------------------------------------------- STATIC_AL boolean ISO15765SendFC(uint8 FS) { uint8 data[3]; data[0] = (NPCI_FLOW_CONTROL | (FS & 0x0FU)); data[1] = uCF_BS; data[2] = uCF_STmin; return LOWER_TRANSMIT(data, 3); } //----------------------------------------------------------------------------- /// \brief ISO15765Receive /// /// \descr /// /// \param - /// /// \return void //----------------------------------------------------------------------------- // receive function for data from lower layer void ISO15765Receive(uint8* data, volatile uint16 length) { #if defined(EXT_ADR) // use extended addressing if (data[N_SA] != (uint8)EXT_ADR_SRC) { // ignore invalid source address return; } #endif // check the NPCI type switch(data[EXT_STARTBYTE] & 0xF0U) { case NPCI_SINGLE_FRAME : { // single frame received - just check length and pass to upper layer uint8 SF_DL = data[EXT_STARTBYTE] & 0x0FU; uCF_DL = 0; #if ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD) if (SF_DL != 0u) { // CANFD_DL<=8 if ((SF_DL > SF_DATALENGTH_SHORT) || (length <= SF_DL)) { // error - frame length wrong, discard frame } else { // frame is okay (void)UPPER_TRANSMIT(&data[EXT_STARTBYTE + 1], SF_DL); // strip NPCI } } else { // CANFD_DL > 8 uint8 SF_DL_FD = data[2] & 0xFFU; if ((SF_DL_FD > SF_DATALENGTH) || (length <= SF_DL_FD)) { // error - frame length wrong, discard frame } else { // frame is okay (void)UPPER_TRANSMIT(&data[EXT_STARTBYTE + 2], SF_DL_FD); // strip NPCI } } #else // PRQA S 3415 1 // operation performed correctly if ((SF_DL > SF_DATALENGTH) || (length <= SF_DL)) { // error - frame length wrong, discard frame } else { // frame is okay (void)UPPER_TRANSMIT(&data[EXT_STARTBYTE + 1U], SF_DL); // strip NPCI } #endif break; } case NPCI_FIRST_FRAME : { // first frame received uCF_DL = (uint16)(((uint16)data[EXT_STARTBYTE] & 0x0FU) << 8U) + (uint16)(data[EXT_STARTBYTE + 1U]); if ((uCF_DL < (SF_DATALENGTH + 1U))) { // error - frame length too small, discard frame uCF_DL = 0; break; } // frame is okay - copy to buffer (void)memcpy(arCF_Frame, &data[EXT_STARTBYTE + 2U], FF_DATALENGTH); // strip NPCI uCF_Size = FF_DATALENGTH; // init size uCF_SN = 1; // init SN (next expected seq number) uCF_BScnt = 0; // init block counter // send FC (void)ISO15765SendFC(0U); // trigger timeout for next CF frame reception (void)ISO15765TimerStart(RX_CF, N_Cr, ISO15765TimerRX_CF); break; } case NPCI_CONSECUTIVE_FRAME : { // consecutive frame received uint8 SN; // kill timer (void)ISO15765TimerStop(RX_CF); if (uCF_DL == 0U) { // no CF expected break; } // check sequence number SN = data[EXT_STARTBYTE] & 0x0FU; #ifdef WORKAROUND_SAME_CAN_TWICE if (SN != uCF_SN) { if ((SN == uCF_SN - 1) || (SN == 0xF) && (uCF_SN == 0x0)){ //ignore that case to prevent hangs break; } else { // error - wrong sequence number, discard frame and cancel reception uCF_DL = 0U; UPPER_ERROR(ERROR_RX); break; } } else { // generate next SN uCF_SN = ((uCF_SN+1U) & 0x0FU); } #else if (SN != uCF_SN) { // error - wrong sequence number, discard frame and cancel reception uCF_DL = 0U; UPPER_ERROR(ERROR_RX); break; } else { // generate next SN uCF_SN = ((uCF_SN+1U) & 0x0FU); //++uCF_SN & 0x0FU; } #endif // frame is okay // check buffer space if (uCF_Size + length < CF_MAX_FRAME_SIZE) { // append new data to buffer (void)memcpy(&arCF_Frame[uCF_Size], &data[EXT_STARTBYTE + 1U], length < CF_DATALENGTH ? length : CF_DATALENGTH); // strip NPCI uCF_Size += (length < CF_DATALENGTH ? length : CF_DATALENGTH); } else { // buffer overflow - the CF is accepted, but silently discarded but the upper layer should get informed UPPER_ERROR(ERROR_OVERFLOW); } // frame done? if (uCF_Size >= uCF_DL) { // frame complete - send frame to upper layer (void)UPPER_TRANSMIT(arCF_Frame, uCF_DL); uCF_DL = 0; } else { if (uFC_BS != 0U) { if (++uCF_BScnt >= uCF_BS) { // complete block received, send FC uCF_BScnt = 0; (void)ISO15765SendFC(0U); } } // restart timer (void)ISO15765TimerStart(RX_CF, N_Cr, ISO15765TimerRX_CF); } break; } case NPCI_FLOW_CONTROL : { // flow control frame received // kill surveillance timer (void)ISO15765TimerStop(TX_FC); // FC frame is too short || FS format error - abort if ((length < FC_DATALENGTH) || ((data[EXT_STARTBYTE + 0U] & 0x0FU) > 1U)) { break; } // frame is okay - store values #if defined (UNIT_TEST) uFC_FS = data[EXT_STARTBYTE + 1U] & 0x01U; // 0 = CTS (ContinueToSend), 1 = WT (Wait) #else uFC_FS = data[EXT_STARTBYTE + 0U] & 0x01U; // 0 = CTS (ContinueToSend), 1 = WT (Wait) #endif // UNIT_TEST #if (((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CAN) == CFG_ISO15765_COM_TYPE_CAN) || \ ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD)) uFC_BS = data[EXT_STARTBYTE + 1U]; #endif uFC_STmin = data[EXT_STARTBYTE + 2U]; uCF_BScnt = 0U; // reset block counter if (uFC_FS == 0U) { // CTS set - send next consecutive frame (void)ISO15765TimerStart(TX_CF, uFC_STmin, ISO15765TimerTX_CF); } break; } default : // unknown N_PCI type break; } // PRQA S 5330 3 // Program architecture and readability // PRQA S 5310 2 // Program architecture and readability // PRQA S 5316 1 // Program architecture and readability } #if ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_LIN) == CFG_ISO15765_COM_TYPE_LIN) //----------------------------------------------------------------------------- /// \brief ISO15765_PrepareConsecutiveFrame /// /// \descr /// /// \param - /// /// \return boolean //----------------------------------------------------------------------------- boolean ISO15765_PrepareConsecutiveFrame(void) { boolean boRes; if (boCFNeeded == TRUE) { boRes = TRUE; (void) ISO15765SendCF(); } else { boRes = FALSE; } return boRes; } #endif //----------------------------------------------------------------------------- /// \brief ISO15765TimerTX_CF /// /// \descr /// /// \param - /// /// \return void //----------------------------------------------------------------------------- STATIC_AL void ISO15765TimerTX_CF(void) { // STmin expired, send next consecutive frame (void) ISO15765SendCF(); } #if (((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CAN) == CFG_ISO15765_COM_TYPE_CAN) || \ ((ISO15765_COM_TYPE & CFG_ISO15765_COM_TYPE_CANFD) == CFG_ISO15765_COM_TYPE_CANFD)) //----------------------------------------------------------------------------- /// \brief ISO15765TimerTX_FC /// /// \descr /// /// \param - /// /// \return void //----------------------------------------------------------------------------- STATIC_AL void ISO15765TimerTX_FC(void) { // waiting for FC frame expired, this is an error condition // the upper layer needs to be informed that the receiver didn't answer UPPER_ERROR(ERROR_TX); ISO15765SendCFAbort(); } #endif //----------------------------------------------------------------------------- /// \brief ISO15765TimerRX_CF /// /// \descr /// /// \param - /// /// \return void //----------------------------------------------------------------------------- STATIC_AL void ISO15765TimerRX_CF(void) { // waiting for next CF frame from sender expired, this is an error condition // the upper layer needs to be informed that the sender has a timeout UPPER_ERROR(ERROR_RX); ISO15765SendCFAbort(); } /////////////////////////////////////////////////////////////////////////////// // // T I M E R S E C T I O N // /////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- /// \brief ISO15765TimerStart /// /// \descr /// /// \param - /// /// \return boolean //----------------------------------------------------------------------------- STATIC_AL boolean ISO15765TimerStart(uint16 ID, uint16 Period, void(*fnc)(void)) { // check if timer is already running boolean boRet = FALSE; uint8 i; for (i = 0; i < MAX_TIMERS; i++) { if (arTimers[i].uID == ID) { // yes, timer is running - restart arTimers[i].tTimer = TimerStart(UTILTIME_MSEC(Period)); boRet = TRUE; break; } } if(boRet == FALSE) // timer not created yet - find next free slot { for (i = 0; i < MAX_TIMERS; i++) { if (arTimers[i].uID == 0U) { // found empty slot arTimers[i].tTimer = TimerStart(UTILTIME_MSEC(Period)); arTimers[i].fnc = fnc; arTimers[i].uID = ID; // set ID last to ensure that all other params are valid boRet = TRUE; break; } } } // if no empty slot found return FALSE return boRet; } //----------------------------------------------------------------------------- /// \brief ISO15765TimerStop /// /// \descr /// /// \param - /// /// \return boolean //----------------------------------------------------------------------------- STATIC_AL boolean ISO15765TimerStop(uint8 ID) { boolean boRet = FALSE; uint8 i; for (i = 0; i < MAX_TIMERS; i++) { if (arTimers[i].uID == ID) { // remove timer arTimers[i].uID = 0; boRet = TRUE; break; } } // return FALSE if ID not found return boRet; } //----------------------------------------------------------------------------- /// \brief ISO15765TimerService /// /// \descr /// /// \param - /// /// \return void //----------------------------------------------------------------------------- // this must be called every ms void ISO15765TimerService(void) { // check Timers and trigger according callback uint8 i; for (i = 0; i < MAX_TIMERS; i++) { if (arTimers[i].uID != 0U) { if((TimerTimeout(arTimers[i].tTimer)) == TRUE) { // timer is elapsed - execute callback arTimers[i].uID = 0; // kill timer before function call - may be restarted in fnc arTimers[i].fnc(); } } } } ////////////////////////////////////////////////////////////////////////// // // WRAPPER // ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- /// \brief LowerSend /// /// \descr /// /// \param - /// /// \return boolean //----------------------------------------------------------------------------- STATIC_AL boolean LowerSend(uint8* data, uint16 length) { uint8 buf[BUFFER_SIZE] = { 0 }; #ifdef PADDING // clear buffer // padding is useless here //(void)memset(buf, (int)PADDING_BYTE, BUFFER_SIZE); #endif #if defined(EXT_ADR) // use extended addressing buf[0] = EXT_ADR_TRG; (void)memcpy(&buf[1], data, length); length++; #else // normal addressing (void)memcpy(buf, data, length); #endif SEND_OVER_COMMUNICATION_BUS(buf, length); #ifdef UNIT_TEST return boUtRetVal; #else return TRUE; #endif } //----------------------------------------------------------------------------- /// \brief UpperSend /// /// \descr /// /// \param - /// /// \return boolean //----------------------------------------------------------------------------- STATIC_AL boolean UpperSend(uint8* data, volatile uint16 length) { // get buffer PduInfoType* PduInfoPtr; (void)ComBuf_RxProvide(ISO15765_USED_COMBUF_ID, &PduInfoPtr); if (PduInfoPtr->SduLength >= length) { (void)memcpy(PduInfoPtr->SduDataPtr, data, length); PduInfoPtr->SduLength = length; } ComBuf_RxIndication(ISO15765_USED_COMBUF_ID, NTFRSLT_OK); return TRUE; } //----------------------------------------------------------------------------- /// \brief UpperError /// /// \descr /// /// \param - /// /// \return void //----------------------------------------------------------------------------- STATIC_AL void UpperError(uint8 error) { // PRQA S 3112 1 // parameter not used at the moment (void) error; // not used ComBuf_RxIndication(ISO15765_USED_COMBUF_ID, NTFRSLT_E_NOT_OK); }