#ifndef INCLUDED_OWLNET_CAN_H #define INCLUDED_OWLNET_CAN_H #include "OtfError.h" //---------------------------------------------------------------------------- // configuration // these must be defined in main application //---------------------------------------------------------------------------- // also needs this definition: //#define OWLNET_MHZ 4 // MHz for CanBus module // activate/deactivate CAN //#define CAN_ACTIVE //#undef CAN_ACTIVE // CAN message queue size [number of messages] //#define CanMsgQSize 10 #ifndef CanMsgQSize #define CanMsgQSize 10 #endif //---------------------------------------------------------------------------- // DECLARATIONS //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // CAN Message //---------------------------------------------------------------------------- // has moved to OCP //---------------------------------------------------------------------------- // global data //---------------------------------------------------------------------------- extern UBYTE8 CanRxCount; // is incremented with each received msg and // is reset with 1 Hz extern UBYTE8 CanTxCount; // dito extern UBYTE8 CanTxCountPrev; // the CanTxCount value of preceding 1Hz cycle //---------------------------------------------------------------------------- // CAN Message Queue //---------------------------------------------------------------------------- // queue management: // - CanMsgQReadNext == CanMsgQSize indicates empty queue // - CanMsgQReadNext != CanMsgQSize: points to next element to be read // - CanMsgQReadNext == CanMsgQWriteNext indicates full queue // - CanMsgQWriteNext != CanMsgQReadNext points to next writeable element extern UBYTE8 CanMsgQReadNext; extern UBYTE8 CanMsgQWriteNext; #ifdef CAN_ACTIVE extern dtOcpMsg CanQ[CanMsgQSize]; #endif //---------------------------------------------------------------------------- // functions //---------------------------------------------------------------------------- // sending and receiving extern void CanInit(void); extern void CanSend(dtOcpMsg *pCanMsg); extern UBYTE8 CanCanSend(void); // returns TRUE if a send buffer is currently available ("we can send") extern UBYTE8 CanRecv(dtOcpMsg *pCanMsg); // queue management extern void CanMsgQInit(void); // initializes queue indices extern dtOcpMsg *CanMsgGetHead(void); // returns ptr to queue head (0 if no msg available) extern void CanMsgRemoveHead(void); // deletes head entry extern dtOcpMsg *CanMsgGetTail(void); // prepares to add msg at tail (allocates a slot) and returns pointer // or returns 0 if overflow // if successful: // - fill location where pointer points to // - call CanMsgAddTail to vaildate the entry // - if CanMsgAddTail is not called, nothing is added extern void CanMsgAddTail(void); // record has been added at tail via pointer (see CanMsgGetTail) extern void CanMsg2Ascii(dtOcpMsg *pCan, UBYTE8 *pAsc); // converts a CAN msg to ASCII format // up to 27 chars + NULL byte: "08112233.8 003456789ABCDEF0" //---------------------------------------------------------------------------- // IMPLEMENTATION //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // global data //---------------------------------------------------------------------------- #ifdef CAN_ACTIVE UBYTE8 CanRxCount; // is incremented with each received msg and // reset after App1s call UBYTE8 CanTxCount; // dito UBYTE8 CanTxCountPrev; // the CanTxCount value of preceding 1Hz cycle //---------------------------------------------------------------------------- // CAN Message Queue //---------------------------------------------------------------------------- UBYTE8 CanMsgQReadNext; UBYTE8 CanMsgQWriteNext; dtOcpMsg CanQ[CanMsgQSize]; #endif //---------------------------------------------------------------------------- // functions //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // sending and receiving //---------------------------------------------------------------------------- #ifdef CAN_ACTIVE void CanInit(void) { // initialize variables CanRxCount = 0; CanTxCount = 0; CanMsgQInit(); // request configuration mode cancon = 10011110b; // poll until in configuration mode while ((canstat & 10000000b) == 0) { nop(); } // setup port pins set_bit(trisb,3); clear_bit(trisb,2); clear_bit(portb,2); // setup configuration registers #ifdef OWLNET_4_MHZ brgcon1 = 11000001b; // for 50kbit/s at 4 MHZ clock; SJW=4 #endif #ifdef OWLNET_8_MHZ brgcon1 = 11000011b; // for 50kbit/s at 8 MHZ clock; SJW=4 #endif #ifdef OWLNET_16_MHZ brgcon1 = 11000111b; // for 50kbit/s at 16 MHZ clock; SJW=4 #endif #ifdef OWLNET_32_MHZ brgcon1 = 11001111b; // for 50kbit/s at 32 MHZ clock; SJW=4 #endif brgcon2 = 11011110b; // seg2 programmable, sample 3 times, phase1 = 4, prop = 7 brgcon3 = 00000111b; // no filter, phase2=8 ciocon = 00100000b; // drive CanTX high when recessive; no cancap // interrupts //>>> // receive buffer control rxb0con = 01000100b; // extended frames only, enable double buffer // rxb0con = 01100100b; // all msgs (also with errors), enable double buffer rxb1con = 01000000b; // extended frames only // acceptance filters and masks: accept all to RXB0 rxm0sidh = 0; rxm0sidl = 0; rxm0eidh = 0; rxm0eidl = 0; rxm1sidh = 11111111b; rxm1sidl = 11111111b; rxm1eidh = 11111111b; rxm1eidl = 11111111b; rxf0sidh = 0; rxf0sidl = 00001000b; // EXIDEN bit must be set rxf0eidh = 0; rxf0eidl = 0; rxf1sidh = 0; rxf1sidl = 0; rxf1eidh = 0; rxf1eidl = 0; rxf2sidh = 0; rxf2sidl = 0; rxf2eidh = 0; rxf2eidl = 0; rxf3sidh = 0; rxf3sidl = 0; rxf3eidh = 0; rxf3eidl = 0; rxf4sidh = 0; rxf4sidl = 0; rxf4eidh = 0; rxf4eidl = 0; rxf5sidh = 0; rxf5sidl = 0; rxf5eidh = 0; rxf5eidl = 0; /* cancon = 01011110b;// loopback mode // poll until active while ((canstat & 11100000b) != 01000000b) { nop(); } */ // now activate cancon = 00000000b; // poll until active while ((canstat & 11100000b) != 0) { nop(); } } UBYTE8 CanRecv(dtOcpMsg *pCanMsg) { UBYTE8 RecvCount = 0; UBYTE8 a, b; if (test_bit(pir3,RXB0IF)) { if (test_bit(rxb0con,RXFUL)) { //receive from rxb0 //>>>to do: use WIN bits and access bank instead of RXB0 directly // fill CanMsg struct with data from RXB0 pCanMsg->Can.Eid[3] = (rxb0sidh >> 3) & 00011111b; a = (rxb0sidh & 00000111b) << 5; b = (rxb0sidl & 11100000b) >> 3; pCanMsg->Can.Eid[2] = a | b | (rxb0sidl & 00000011b); pCanMsg->Can.Eid[1] = rxb0eidh; pCanMsg->Can.Eid[0] = rxb0eidl; pCanMsg->Can.DlcAndRtr = rxb0dlc; pCanMsg->Can.Data[0] = rxb0d0; pCanMsg->Can.Data[1] = rxb0d1; pCanMsg->Can.Data[2] = rxb0d2; pCanMsg->Can.Data[3] = rxb0d3; pCanMsg->Can.Data[4] = rxb0d4; pCanMsg->Can.Data[5] = rxb0d5; pCanMsg->Can.Data[6] = rxb0d6; pCanMsg->Can.Data[7] = rxb0d7; ++RecvCount; // release RXB0 clear_bit(rxb0con,RXFUL); } clear_bit(pir3,RXB0IF); } if (RecvCount == 0&& test_bit(pir3,RXB1IF)) { if (test_bit(rxb1con,RXFUL)) { //receive from rxb1 //>>>to do: use WIN bits and access bank to share code with RXB0 receive // fill CanMsg struct with data from rxb1 pCanMsg->Can.Eid[3] = (rxb1sidh >> 3) & 00011111b; a = (rxb1sidh & 00000111b) << 5; b = (rxb1sidl & 11100000b) >> 3; pCanMsg->Can.Eid[2] = a | b | (rxb1sidl & 00000011b); pCanMsg->Can.Eid[1] = rxb1eidh; pCanMsg->Can.Eid[0] = rxb1eidl; pCanMsg->Can.DlcAndRtr = rxb1dlc; pCanMsg->Can.Data[0] = rxb1d0; pCanMsg->Can.Data[1] = rxb1d1; pCanMsg->Can.Data[2] = rxb1d2; pCanMsg->Can.Data[3] = rxb1d3; pCanMsg->Can.Data[4] = rxb1d4; pCanMsg->Can.Data[5] = rxb1d5; pCanMsg->Can.Data[6] = rxb1d6; pCanMsg->Can.Data[7] = rxb1d7; ++RecvCount; // release RXB1 clear_bit(rxb1con,RXFUL); } clear_bit(pir3,RXB1IF); } CanRxCount += RecvCount; // inbound swap if (RecvCount) { OcpSwap(pCanMsg); } return RecvCount; } UBYTE8 CanCanSend(void) // returns TRUE if a send buffer is currently available ("we can send") { if (test_bit(txb0con, TXREQ) == 0) { return 1; } else if (test_bit(txb1con, TXREQ) == 0) { return 2; } else if (test_bit(txb2con, TXREQ) == 0) { return 3; } return 0; } void CanSend(dtOcpMsg *pCanMsg) // Sends a CAN message. If send buffer is full, older message is discarded. // Uses the 3 can send buffers as send queue: // - If empty, txb0 is filled with high prio. // - Else, if empty, txb1 is filled with middle prio. // - Else, if empty, txb2 is filled with low prio. // - If also txb2 is already full, the old message is discarded and replaced by the new one. { UBYTE8 i, a, b; // be optimistic CanTxCount++; // outbbound swap OcpSwap(pCanMsg); // first, try buffer 0 if (test_bit(txb0con, TXREQ) == 0) { // priority = 3 (highest) set_bit(txb0con, TXPRI1); set_bit(txb0con, TXPRI0); // always send Extended Identifier a = (pCanMsg->Can.Eid[3] & 00011111b) << 3; b = (pCanMsg->Can.Eid[2] & 11100000b) >> 5; txb0sidh = a | b; a = (pCanMsg->Can.Eid[2] & 00011100b) << 3; b = pCanMsg->Can.Eid[2] & 00000011b; txb0sidl = a | b | 00001000b; txb0eidh = pCanMsg->Can.Eid[1]; txb0eidl = pCanMsg->Can.Eid[0]; // data length code and RTR bit txb0dlc = pCanMsg->Can.DlcAndRtr; // data bytes txb0d0 = pCanMsg->Can.Data[0]; txb0d1 = pCanMsg->Can.Data[1]; txb0d2 = pCanMsg->Can.Data[2]; txb0d3 = pCanMsg->Can.Data[3]; txb0d4 = pCanMsg->Can.Data[4]; txb0d5 = pCanMsg->Can.Data[5]; txb0d6 = pCanMsg->Can.Data[6]; txb0d7 = pCanMsg->Can.Data[7]; // request sending the message set_bit(txb0con, TXREQ); } // if buffer 0 busy, try buffer 1 else if (test_bit(txb1con, TXREQ) == 0) { // priority = 2 (3=highest, 0=lowest) set_bit(txb1con, TXPRI1); clear_bit(txb1con, TXPRI0); // always send Extended Identifier a = (pCanMsg->Can.Eid[3] & 00011111b) << 3; b = (pCanMsg->Can.Eid[2] & 11100000b) >> 5; txb1sidh = a | b; a = (pCanMsg->Can.Eid[2] & 00011100b) << 3; b = pCanMsg->Can.Eid[2] & 00000011b; txb1sidl = a | b | 00001000b; txb1eidh = pCanMsg->Can.Eid[1]; txb1eidl = pCanMsg->Can.Eid[0]; // data length code and RTR bit txb1dlc = pCanMsg->Can.DlcAndRtr; // data bytes txb1d0 = pCanMsg->Can.Data[0]; txb1d1 = pCanMsg->Can.Data[1]; txb1d2 = pCanMsg->Can.Data[2]; txb1d3 = pCanMsg->Can.Data[3]; txb1d4 = pCanMsg->Can.Data[4]; txb1d5 = pCanMsg->Can.Data[5]; txb1d6 = pCanMsg->Can.Data[6]; txb1d7 = pCanMsg->Can.Data[7]; // request sending the message set_bit(txb1con, TXREQ); } // if buffer 1 busy, use buffer 2 else { // abort pending message if (test_bit(txb2con, TXREQ)) { clear_bit(txb2con, TXREQ); // have been too optimistic CanTxCount--; bOtfErrorMask |= OTF_E_CANSEND_OVERFLOW; } // priority = 1 (3=highest, 0=lowest) clear_bit(txb1con, TXPRI1); set_bit(txb1con, TXPRI0); // always send Extended Identifier a = (pCanMsg->Can.Eid[3] & 00011111b) << 3; b = (pCanMsg->Can.Eid[2] & 11100000b) >> 5; txb2sidh = a | b; a = (pCanMsg->Can.Eid[2] & 00011100b) << 3; b = pCanMsg->Can.Eid[2] & 00000011b; txb2sidl = a | b | 00001000b; txb2eidh = pCanMsg->Can.Eid[1]; txb2eidl = pCanMsg->Can.Eid[0]; // data length code and RTR bit txb2dlc = pCanMsg->Can.DlcAndRtr; // data bytes txb2d0 = pCanMsg->Can.Data[0]; txb2d1 = pCanMsg->Can.Data[1]; txb2d2 = pCanMsg->Can.Data[2]; txb2d3 = pCanMsg->Can.Data[3]; txb2d4 = pCanMsg->Can.Data[4]; txb2d5 = pCanMsg->Can.Data[5]; txb2d6 = pCanMsg->Can.Data[6]; txb2d7 = pCanMsg->Can.Data[7]; // request sending the message set_bit(txb2con, TXREQ); } } //---------------------------------------------------------------------------- // queue management //---------------------------------------------------------------------------- void CanMsgQInit(void) { CanMsgQReadNext = CanMsgQSize; CanMsgQWriteNext = 0; } dtOcpMsg *CanMsgGetHead(void) // returns ptr to queue head { if (CanMsgQReadNext != CanMsgQSize) { return &(CanQ[CanMsgQReadNext]); } return (dtOcpMsg *)0; } void CanMsgRemoveHead(void) // deletes head entry { if (CanMsgQReadNext != CanMsgQSize) { // not empty // check for rewinding pointer if (++CanMsgQReadNext >= CanMsgQSize) { CanMsgQReadNext = 0; } // check if empty if (CanMsgQReadNext == CanMsgQWriteNext) { // now empty CanMsgQReadNext = CanMsgQSize; CanMsgQWriteNext = 0; } } } dtOcpMsg *CanMsgGetTail(void) // prepares to add msg at tail (allocates a slot) and returns pointer // or returns 0 if overflow // if successful: // - fill location where pointer points to // - call CanMsgAddTail to vaildate the entry // - if CanMsgAddTail is not called, nothing is added { UBYTE8 c; if (CanMsgQWriteNext == CanMsgQReadNext) { // queue full, cannot add bOtfErrorMask |= OTF_E_CANRECV_OVERFLOW; return (dtOcpMsg *)0; } return &(CanQ[CanMsgQWriteNext]); // store record here and then call CanMsgAddTail } void CanMsgAddTail(void) // record has been added at tail via pointer (see CanMsgGetTail) { if (++CanMsgQWriteNext >= CanMsgQSize) { CanMsgQWriteNext = 0; } if (CanMsgQReadNext == CanMsgQSize) { // no longer empty CanMsgQReadNext = 0; } } void CanMsg2Ascii(dtOcpMsg *pCan, UBYTE8 *pAsc) // converts a CAN msg to ASCII format // up to 27 chars + NULL byte: "08112233.8 003456789ABCDEF0" // { UBYTE8 i; // "08112233.8 003456789ABCDEF0" for (i = 0; i < 4; ++i) { *pAsc++ = HexNibble((pCan->Can.Eid[3-i]) >> 4); *pAsc++ = HexNibble( pCan->Can.Eid[3-i] ); } if (test_bit(pCan->Can.DlcAndRtr, CAN_RTR_BIT_NO)) { *pAsc++ = 'R'; } else { *pAsc++ = '.'; } *pAsc++ = HexNibble( pCan->Can.DlcAndRtr & 0x0f ); *pAsc++ = ' '; for (i = 0; i < (pCan->Can.DlcAndRtr & 0x0f); ++i) { *pAsc++ = HexNibble((pCan->Can.Data[i]) >> 4); *pAsc++ = HexNibble( pCan->Can.Data[i] ); } *pAsc++ = 0; } UBYTE8 Ascii2CanMsg(UBYTE8 *pAsc, dtOcpMsg *pCan) // converts an ASCII msg to CAN msg if ASCII is in this format: // "08112233.8 003456789ABCDEF0" // --eid---RD dddddddddddddddd // eid = extended CAN identifier // R = RTR bit, .=0 or R=1 // D = D = data length code 0..8 // dd = data bytes // returns 0 if no errors { UBYTE8 hi, lo, dl, i; UBYTE8 Error = 0; // read EID for (i = 0; i < 4; ++i) { hi = ReadasHexNibble(*pAsc); if (hi == 0xff) Error = 1; pAsc++; lo = ReadasHexNibble(*pAsc); if (lo == 0xff) Error = 1; pAsc++; pCan->Can.Eid[3-i] = (hi << 4) | lo; } // read RTR bit if (*pAsc == 'R') { hi = 01000000b; // RTR is in bit 6 } else if (*pAsc == '.'){ hi = 00000000b; } else { Error = 1; } pAsc++; // read DLC lo = ReadasHexNibble(*pAsc); if (lo == 0xff) Error = 1; pCan->Can.DlcAndRtr = hi | lo; pAsc++; pAsc++; // read data bytes if (lo <= 8) { dl = lo; for (i = 0; i < dl; ++i) { hi = ReadasHexNibble(*pAsc); if (hi == 0xff) Error = 1; pAsc++; lo = ReadasHexNibble(*pAsc); if (lo == 0xff) Error = 1; pAsc++; pCan->Can.Data[i] = (hi << 4) | lo; } } return Error; } #endif // CAN_ACTIVE #endif // INCLUDED_OWLNET_CAN_H