736 lines
19 KiB
C
736 lines
19 KiB
C
|
/*!
|
||
|
* @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 <fifo.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//#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<<filterId);
|
||
|
}
|
||
|
void can_disable_filter (u8 filterId) {
|
||
|
|
||
|
if (filterId >= N_FILTERS)
|
||
|
return;
|
||
|
CAN->FA1R &= ~ (u32)(1<<filterId);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
i8 can_transmit (u16 ID, u8 * data, u8 len, canIdExtension idType, canDataRemote dataRemote, canTxOverrideBehaviorE override) {
|
||
|
|
||
|
if (idType != canSTD_ID && idType != canEXTENDED_ID)
|
||
|
return -1;
|
||
|
if (dataRemote != canDATA_FRAME && dataRemote != canREMOTE_FRAME)
|
||
|
return -1;
|
||
|
if (len > 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 <CODE>
|
||
|
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 <CODE>
|
||
|
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
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|