STM32F042-Drivers-Pub/can.c
Francesco Gritti fe64f5342d initial commit
2024-05-02 12:18:04 +02:00

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