STM32H7: Implement basic FDCAN support
Add stm32h7 support for FDCAN peripheral. Source level compatibility is provided with stm32g4. Additional features of stm32h7 such as configurable buffers are supported. Implementation offers feature parity with stm32g4 implementation.
This commit is contained in:
committed by
Karl Palsson
parent
0d72e6739c
commit
32354846bd
@@ -1,22 +1,3 @@
|
||||
/** @defgroup fdcan_file FDCAN peripheral API
|
||||
*
|
||||
* @ingroup peripheral_apis
|
||||
*
|
||||
* @brief <b>libopencm3 STM32 FDCAN</b>
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @author @htmlonly © @endhtmlonly 2021 Eduard Drusa <ventyl86 at netkosice dot sk>
|
||||
*
|
||||
* Devices can have up to three FDCAN peripherals residing in one FDCAN block. The peripherals
|
||||
* support both CAN 2.0 A and B standard and Bosch FDCAN standard. FDCAN frame format and
|
||||
* bitrate switching is supported. The peripheral has several filters for incoming messages that
|
||||
* can be distributed between two FIFOs and three transmit mailboxes. For transmitted messages
|
||||
* it is possible to opt for event notification once message is transmitted.
|
||||
*
|
||||
* LGPL License Terms @ref lgpl_license
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
@@ -53,11 +34,11 @@
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] set new value of INIT, true means set
|
||||
* @param [in] timeout Amount of busyloop cycles, function will wait for FDCAN
|
||||
* to switch it's state. If set to 0, then function returns immediately.
|
||||
* to switch it's state. If set to 0, then function returns immediately.
|
||||
* @returns FDCAN_E_OK on success, FDCAN_E_TIMEOUT if INIT bit value
|
||||
* didn't change before timeout has expired.
|
||||
*/
|
||||
static int fdcan_cccr_init_cfg(uint32_t canport, bool set, uint32_t timeout)
|
||||
int fdcan_cccr_init_cfg(uint32_t canport, bool set, uint32_t timeout)
|
||||
{
|
||||
uint32_t expected;
|
||||
uint32_t wait_ack;
|
||||
@@ -135,43 +116,71 @@ static void fdcan_get_fill_rxfifo(uint32_t canport, uint8_t fifo_id, unsigned *g
|
||||
& FDCAN_RXFIFO_FL_MASK;
|
||||
}
|
||||
|
||||
/** Obtain address of FDCAN Message RAM for certain FDCAN block.
|
||||
/** Returns standard filter start address in message RAM
|
||||
*
|
||||
* @param [in] canport identification of FDCAN block. See @ref fdcan_block.
|
||||
* @return Address of Message RAM for given FDCAN block or null pointer
|
||||
* if FDCAN block identification is invalid.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @returns Base address of standard filter configuration block.
|
||||
*/
|
||||
static struct fdcan_message_ram *fdcan_get_msgram_addr(uint32_t canport)
|
||||
struct fdcan_standard_filter *fdcan_get_flssa_addr(uint32_t canport)
|
||||
{
|
||||
/* This piece of code may look wrong, after one examines
|
||||
* STM32G4 datasheet and/or g4/memorymap.h. There are three
|
||||
* memory regions defined for FDCANx_RAM_BASE. They are 0x400
|
||||
* bytes apart as per chapter 2.2.2 of [RM0440].
|
||||
*
|
||||
* It turns out, that these addresses are not in line with what
|
||||
* is specified later in chapter 44.3.3 of [RM0440]. There it is
|
||||
* stated, that message RAMs are packed and in case of multiple
|
||||
* FDCAN blocks, message RAM for n-th FDCAN starts at address
|
||||
* end of (n-1)-th block + 4 (explicitly, offset 0x354).
|
||||
*
|
||||
* It turns out, that this statement is also false! In fact FDCAN
|
||||
* message RAMs are packed tightly and n-th block starts immediately
|
||||
* after (n-1)-th block ends. Thus offset is going to be computed
|
||||
* using formula:
|
||||
*
|
||||
* FDCAN1_RAM_BASE + (block_id * sizeof(struct fdcan_message_ram))
|
||||
*/
|
||||
if (canport == CAN1) {
|
||||
return (struct fdcan_message_ram *) (FDCAN1_RAM_BASE + 0);
|
||||
} else if (canport == CAN2) {
|
||||
return (struct fdcan_message_ram *)
|
||||
(FDCAN1_RAM_BASE + sizeof(struct fdcan_message_ram));
|
||||
} else if (canport == CAN3) {
|
||||
return (struct fdcan_message_ram *)
|
||||
(FDCAN1_RAM_BASE + (2 * sizeof(struct fdcan_message_ram)));
|
||||
}
|
||||
struct fdcan_standard_filter *lfssa = (struct fdcan_standard_filter *)
|
||||
(CAN_MSG_BASE + FDCAN_LFSSA_OFFSET(canport));
|
||||
return lfssa;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
/** Returns extended filter start address in message RAM
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @returns Base address of extended filter configuration block.
|
||||
*/
|
||||
struct fdcan_extended_filter *fdcan_get_flesa_addr(uint32_t canport)
|
||||
{
|
||||
struct fdcan_extended_filter *lfesa = (struct fdcan_extended_filter *)
|
||||
(CAN_MSG_BASE + FDCAN_LFESA_OFFSET(canport));
|
||||
return lfesa;
|
||||
}
|
||||
|
||||
/** Returns FIFO start address in message RAM
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] fifo_id ID of FIFO whose address is requested
|
||||
* @returns Base address of FIFO block.
|
||||
*/
|
||||
struct fdcan_rx_fifo_element *fdcan_get_rxfifo_addr(uint32_t canport,
|
||||
unsigned fifo_id, unsigned element_id)
|
||||
{
|
||||
struct fdcan_rx_fifo_element *rxfifo = (struct fdcan_rx_fifo_element *)
|
||||
(CAN_MSG_BASE + FDCAN_RXFIFO_OFFSET(canport, fifo_id)
|
||||
+ (element_id * fdcan_get_fifo_element_size(canport, fifo_id))
|
||||
);
|
||||
return rxfifo;
|
||||
}
|
||||
|
||||
/** Returns transmit event start address in message RAM
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @returns Base address of transmit event block.
|
||||
*/
|
||||
struct fdcan_tx_event_element *fdcan_get_txevt_addr(uint32_t canport)
|
||||
{
|
||||
struct fdcan_tx_event_element *rxfifo = (struct fdcan_tx_event_element *)
|
||||
(CAN_MSG_BASE + FDCAN_TXEVT_OFFSET(canport));
|
||||
return rxfifo;
|
||||
}
|
||||
|
||||
/** Returns transmit buffer start address in message RAM
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @returns Base address of transmit buffer block.
|
||||
*/
|
||||
struct fdcan_tx_buffer_element *fdcan_get_txbuf_addr(uint32_t canport, unsigned element_id)
|
||||
{
|
||||
struct fdcan_tx_buffer_element *rxfifo = (struct fdcan_tx_buffer_element *)
|
||||
(CAN_MSG_BASE + FDCAN_TXBUF_OFFSET(canport)
|
||||
+ (element_id * fdcan_get_txbuf_element_size(canport))
|
||||
);
|
||||
|
||||
return rxfifo;
|
||||
}
|
||||
|
||||
/** Converts frame length to DLC value.
|
||||
@@ -183,7 +192,7 @@ static struct fdcan_message_ram *fdcan_get_msgram_addr(uint32_t canport)
|
||||
* @returns DLC value representing lengths or 0xFF if length cannot
|
||||
* be encoded into DLC format (applies only to FDCAN frame lengths)
|
||||
*/
|
||||
static uint32_t fdcan_length_to_dlc(uint8_t length)
|
||||
uint32_t fdcan_length_to_dlc(uint8_t length)
|
||||
{
|
||||
if (length <= 8) {
|
||||
return length;
|
||||
@@ -207,7 +216,7 @@ static uint32_t fdcan_length_to_dlc(uint8_t length)
|
||||
* @param [in] dlc DLC value
|
||||
* @returns data payload length in bytes
|
||||
*/
|
||||
static uint8_t fdcan_dlc_to_length(uint32_t dlc)
|
||||
uint8_t fdcan_dlc_to_length(uint32_t dlc)
|
||||
{
|
||||
if (dlc <= 8) {
|
||||
return dlc;
|
||||
@@ -237,13 +246,13 @@ static uint8_t fdcan_dlc_to_length(uint32_t dlc)
|
||||
* * @ref fdcan_init_filter
|
||||
* * @ref fdcan_set_test
|
||||
*
|
||||
* You can check if FDCAN block is in INIT mode or it is started using
|
||||
* You can check if FDCAN block is in INIT mode or it is started using
|
||||
* @ref fdcan_get_init_state.
|
||||
*
|
||||
* @param[in] canport CAN register base address. See @ref fdcan_block.
|
||||
* @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
|
||||
* confirming that it entered INIT mode. If set to 0, function will return
|
||||
* immediately.
|
||||
* confirming that it entered INIT mode. If set to 0, function will
|
||||
* return immediately.
|
||||
* @returns Operation error status. See @ref fdcan_error.
|
||||
*/
|
||||
int fdcan_init(uint32_t canport, uint32_t timeout)
|
||||
@@ -309,16 +318,11 @@ void fdcan_set_can(uint32_t canport, bool auto_retry_disable, bool rx_fifo_locke
|
||||
FDCAN_CCCR(canport) &= ~FDCAN_CCCR_MON;
|
||||
}
|
||||
|
||||
if (rx_fifo_locked) {
|
||||
FDCAN_RXGFC(canport) &= ~(FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM);
|
||||
} else {
|
||||
FDCAN_RXGFC(canport) |= FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM;
|
||||
}
|
||||
|
||||
fdcan_set_fifo_locked_mode(canport, rx_fifo_locked);
|
||||
}
|
||||
|
||||
/** Set FDCAN block parameters for FDCAN transmission
|
||||
*
|
||||
*
|
||||
* Enables and configures parameters related to FDCAN transmission. This function
|
||||
* allows configuration of bitrate switching, FDCAN frame format and fast mode
|
||||
* timing. This function can only be called if FDCAN block is in INIT mode.
|
||||
@@ -381,36 +385,6 @@ void fdcan_set_test(uint32_t canport, bool testing, bool loopback)
|
||||
}
|
||||
}
|
||||
|
||||
/** Enable FDCAN operation after FDCAN block has been set up.
|
||||
*
|
||||
* This function will disable FDCAN configuration effectively
|
||||
* allowing FDCAN to sync up with the bus. After calling this function
|
||||
* it is not possible to reconfigure amount of filter rules, yet
|
||||
* it is possible to configure rules themselves. FDCAN block operation
|
||||
* state can be checked using @ref fdcan_get_init_state.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
|
||||
* confirming that it left INIT mode. If set to 0, function will return
|
||||
* immediately.
|
||||
* @returns Operation error status. See @ref fdcan_error.
|
||||
* @note If this function returns with timeout, it usually means that
|
||||
* FDCAN_clk is not set up properly.
|
||||
*/
|
||||
int fdcan_start(uint32_t canport, uint32_t timeout)
|
||||
{
|
||||
/* Error here usually means, that FDCAN_clk is not set up
|
||||
* correctly, or at all. This usually can't be seen above
|
||||
* when INIT is set to 1, because default value for INIT is
|
||||
* 1 as long as one has FDCAN_pclk configured properly.
|
||||
**/
|
||||
if (fdcan_cccr_init_cfg(canport, false, timeout) != 0) {
|
||||
return FDCAN_E_TIMEOUT;
|
||||
}
|
||||
|
||||
return FDCAN_E_OK;
|
||||
}
|
||||
|
||||
/** Return current FDCAN block operation state.
|
||||
*
|
||||
* This function effectively returns value of FDCAN_CCCR's INIT bit.
|
||||
@@ -422,59 +396,6 @@ int fdcan_get_init_state(uint32_t canport)
|
||||
return ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT);
|
||||
}
|
||||
|
||||
/** Configure amount of filters and initialize filtering block.
|
||||
*
|
||||
* This function allows to configure global amount of filters present.
|
||||
* FDCAN block will only ever check as many filters as this function configures.
|
||||
* Function will also clear all filter blocks to zero values. This function
|
||||
* can be only called after @ref fdcan_init has already been called and
|
||||
* @ref fdcan_start has not been called yet as registers holding filter
|
||||
* count are write-protected unless FDCAN block is in INIT mode. It is possible
|
||||
* to reconfigure filters (@ref fdcan_set_std_filter and @ref fdcan_set_ext_filter)
|
||||
* after FDCAN block has already been started.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] std_filt requested amount of standard ID filter rules (0-28)
|
||||
* @param [in] ext_filt requested amount of extended ID filter rules (0-8)
|
||||
*/
|
||||
void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
|
||||
{
|
||||
struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
|
||||
|
||||
/* Only perform initialization of message RAM if there are
|
||||
* any filters required
|
||||
*/
|
||||
if (std_filt > 0) {
|
||||
FDCAN_RXGFC(canport) =
|
||||
(FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT))
|
||||
| (std_filt << FDCAN_RXGFC_LSS_SHIFT);
|
||||
|
||||
|
||||
for (int q = 0; q < FDCAN_SFT_MAX_NR; ++q) {
|
||||
ram->lfssa[q].type_id1_conf_id2 = 0;
|
||||
}
|
||||
} else {
|
||||
/* Reset filter count to zero */
|
||||
FDCAN_RXGFC(canport) =
|
||||
(FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT));
|
||||
}
|
||||
|
||||
if (ext_filt > 0) {
|
||||
FDCAN_RXGFC(canport) =
|
||||
(FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT))
|
||||
| (ext_filt << FDCAN_RXGFC_LSE_SHIFT);
|
||||
|
||||
for (int q = 0; q < FDCAN_EFT_MAX_NR; ++q) {
|
||||
ram->lfesa[q].conf_id1 = 0;
|
||||
ram->lfesa[q].type_id2 = 0;
|
||||
}
|
||||
} else {
|
||||
/* Reset filter count to zero */
|
||||
FDCAN_RXGFC(canport) =
|
||||
(FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
/** Configure filter rule for standard ID frames.
|
||||
*
|
||||
* Sets up filter rule for frames having standard ID. Each FDCAN block can
|
||||
@@ -483,20 +404,20 @@ void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] nr number of filter to be configured
|
||||
* @param [in] id_list_mode Mode in which id1 and id2 are used to match the rule.
|
||||
* See @ref fdcan_sft.
|
||||
* @param [in] id_list_mode Mode in which id1 and id2 are used to match the rule.
|
||||
* See @ref fdcan_sft.
|
||||
* @param [in] id1 standard ID for matching. Used as exact value, lower bound or bit
|
||||
* pattern depending on matching mode selected
|
||||
* @param [in] id2 standard ID or bitmask. Used as exact value, upper bound or bit mask
|
||||
* depending on matching mode selected
|
||||
* @param [in] action Action performed if filtering rule matches frame ID.
|
||||
* See @ref fdcan_sfec.
|
||||
* @param [in] action Action performed if filtering rule matches frame ID.
|
||||
* See @ref fdcan_sfec.
|
||||
*/
|
||||
void fdcan_set_std_filter(uint32_t canport, uint32_t nr,
|
||||
uint8_t id_list_mode, uint32_t id1, uint32_t id2,
|
||||
uint8_t action)
|
||||
{
|
||||
struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
|
||||
struct fdcan_standard_filter *lfssa = fdcan_get_flssa_addr(canport);
|
||||
|
||||
/* id_list_mode and action are passed unguarded. Simply use
|
||||
* defines and it will be OK. id1 and id2 are masked for
|
||||
@@ -506,7 +427,7 @@ void fdcan_set_std_filter(uint32_t canport, uint32_t nr,
|
||||
* overflow into flags. This tends to be extremely time
|
||||
* consuming to debug.
|
||||
*/
|
||||
ram->lfssa[nr].type_id1_conf_id2 =
|
||||
lfssa[nr].type_id1_conf_id2 =
|
||||
(id_list_mode << FDCAN_SFT_SHIFT)
|
||||
| (action << FDCAN_SFEC_SHIFT)
|
||||
| ((id1 & FDCAN_SFID1_MASK) << FDCAN_SFID1_SHIFT)
|
||||
@@ -524,25 +445,25 @@ void fdcan_set_std_filter(uint32_t canport, uint32_t nr,
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] nr number of filter to be configured
|
||||
* @param [in] id_list_mode mode in which id1 and id2 are used to match the rule.
|
||||
* See @ref fdcan_eft.
|
||||
* See @ref fdcan_eft.
|
||||
* @param [in] id1 extended ID for matching. Used as exact value, lower bound or bit
|
||||
* pattern depending on matching mode selected
|
||||
* @param [in] id2 extended ID or bitmask. Used as exact value, upper bound or bit mask
|
||||
* depending on matching mode selected
|
||||
* @param [in] action Action performed if filtering rule matches frame ID.
|
||||
* See @ref fdcan_efec.
|
||||
* See @ref fdcan_efec.
|
||||
*/
|
||||
void fdcan_set_ext_filter(uint32_t canport, uint32_t nr,
|
||||
uint8_t id_list_mode, uint32_t id1, uint32_t id2,
|
||||
uint8_t action)
|
||||
{
|
||||
struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
|
||||
struct fdcan_extended_filter *lfesa = fdcan_get_flesa_addr(canport);
|
||||
|
||||
ram->lfesa[nr].conf_id1 =
|
||||
lfesa[nr].conf_id1 =
|
||||
(action << FDCAN_EFEC_SHIFT)
|
||||
| ((id1 & FDCAN_EFID1_MASK) << FDCAN_EFID1_SHIFT);
|
||||
|
||||
ram->lfesa[nr].type_id2 =
|
||||
lfesa[nr].type_id2 =
|
||||
(id_list_mode << FDCAN_EFT_SHIFT)
|
||||
| ((id2 & FDCAN_EFID2_MASK) << FDCAN_EFID2_SHIFT);
|
||||
}
|
||||
@@ -572,7 +493,7 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr,
|
||||
return mailbox;
|
||||
}
|
||||
|
||||
struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
|
||||
struct fdcan_tx_buffer_element *tx_buffer = fdcan_get_txbuf_addr(canport, mailbox);
|
||||
|
||||
/* Early check: if FDCAN message lentgh is > 8, it must be
|
||||
* a multiple of 4 *and* fdcan format must be enabled.
|
||||
@@ -584,15 +505,15 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr,
|
||||
}
|
||||
|
||||
if (ext) {
|
||||
ram->tx_buffer[mailbox].identifier_flags = FDCAN_FIFO_XTD
|
||||
tx_buffer->identifier_flags = FDCAN_FIFO_XTD
|
||||
| ((id & FDCAN_FIFO_EID_MASK) << FDCAN_FIFO_EID_SHIFT);
|
||||
} else {
|
||||
ram->tx_buffer[mailbox].identifier_flags =
|
||||
tx_buffer->identifier_flags =
|
||||
(id & FDCAN_FIFO_SID_MASK) << FDCAN_FIFO_SID_SHIFT;
|
||||
}
|
||||
|
||||
if (rtr) {
|
||||
ram->tx_buffer[mailbox].identifier_flags |= FDCAN_FIFO_RTR;
|
||||
tx_buffer->identifier_flags |= FDCAN_FIFO_RTR;
|
||||
}
|
||||
|
||||
if (fdcan_fmt) {
|
||||
@@ -603,11 +524,11 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr,
|
||||
flags |= FDCAN_FIFO_BRS;
|
||||
}
|
||||
|
||||
ram->tx_buffer[mailbox].evt_fmt_dlc_res =
|
||||
tx_buffer->evt_fmt_dlc_res =
|
||||
(dlc << FDCAN_FIFO_DLC_SHIFT) | flags;
|
||||
|
||||
for (int q = 0; q < length; q += 4) {
|
||||
ram->tx_buffer[mailbox].data[q / 4] = *((uint32_t *) &data[q]);
|
||||
tx_buffer->data[q / 4] = *((uint32_t *) &data[q]);
|
||||
}
|
||||
|
||||
FDCAN_TXBAR(canport) |= 1 << mailbox;
|
||||
@@ -640,52 +561,49 @@ int fdcan_receive(uint32_t canport, uint8_t fifo_id, bool release, uint32_t *id,
|
||||
bool *ext, bool *rtr, uint8_t *fmi, uint8_t *length,
|
||||
uint8_t *data, uint16_t *timestamp)
|
||||
{
|
||||
const struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
|
||||
|
||||
const struct fdcan_rx_fifo_element *fifo;
|
||||
|
||||
unsigned pending_frames, get_index, dlc, len;
|
||||
|
||||
fdcan_get_fill_rxfifo(canport, fifo_id, &get_index, &pending_frames);
|
||||
|
||||
fifo = ram->rx_fifo[fifo_id];
|
||||
|
||||
if (pending_frames == 0) {
|
||||
return FDCAN_E_NOTAVAIL;
|
||||
}
|
||||
|
||||
dlc = (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_DLC_SHIFT)
|
||||
const struct fdcan_rx_fifo_element *fifo = fdcan_get_rxfifo_addr(canport,
|
||||
fifo_id, get_index);
|
||||
|
||||
dlc = (fifo->filt_fmt_dlc_ts >> FDCAN_FIFO_DLC_SHIFT)
|
||||
& FDCAN_FIFO_DLC_MASK;
|
||||
|
||||
len = fdcan_dlc_to_length(dlc);
|
||||
|
||||
*length = len;
|
||||
if ((fifo[get_index].identifier_flags & FDCAN_FIFO_XTD) == FDCAN_FIFO_XTD) {
|
||||
if ((fifo->identifier_flags & FDCAN_FIFO_XTD) == FDCAN_FIFO_XTD) {
|
||||
*ext = true;
|
||||
*id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_EID_SHIFT)
|
||||
*id = (fifo->identifier_flags >> FDCAN_FIFO_EID_SHIFT)
|
||||
& FDCAN_FIFO_EID_MASK;
|
||||
} else {
|
||||
*ext = false;
|
||||
*id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_SID_SHIFT)
|
||||
*id = (fifo->identifier_flags >> FDCAN_FIFO_SID_SHIFT)
|
||||
& FDCAN_FIFO_SID_MASK;
|
||||
}
|
||||
|
||||
if (timestamp) {
|
||||
*timestamp = (uint16_t) (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_RXTS_SHIFT)
|
||||
*timestamp = (uint16_t) (fifo->filt_fmt_dlc_ts >> FDCAN_FIFO_RXTS_SHIFT)
|
||||
& FDCAN_FIFO_RXTS_MASK;
|
||||
}
|
||||
|
||||
if (fmi) {
|
||||
*fmi = (uint8_t) (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_MM_SHIFT)
|
||||
*fmi = (uint8_t) (fifo->filt_fmt_dlc_ts >> FDCAN_FIFO_MM_SHIFT)
|
||||
& FDCAN_FIFO_MM_MASK;
|
||||
}
|
||||
|
||||
if (rtr) {
|
||||
*rtr = ((fifo[get_index].identifier_flags & FDCAN_FIFO_RTR) == FDCAN_FIFO_RTR);
|
||||
*rtr = ((fifo->identifier_flags & FDCAN_FIFO_RTR) == FDCAN_FIFO_RTR);
|
||||
}
|
||||
|
||||
for (unsigned int q = 0; q < len; q += 4) {
|
||||
*((uint32_t *) &data[q]) = fifo[get_index].data[q / 4];
|
||||
*((uint32_t *) &data[q]) = fifo->data[q / 4];
|
||||
}
|
||||
|
||||
if (release) {
|
||||
@@ -40,7 +40,7 @@ OBJS += crs_common_all.o
|
||||
OBJS += dac_common_all.o dac_common_v2.o
|
||||
OBJS += dma_common_l1f013.o
|
||||
OBJS += dmamux.o
|
||||
OBJS += fdcan.o
|
||||
OBJS += fdcan.o fdcan_common.o
|
||||
OBJS += flash.o flash_common_all.o flash_common_f.o flash_common_idcache.o
|
||||
OBJS += gpio_common_all.o gpio_common_f0234.o
|
||||
OBJS += opamp_common_all.o opamp_common_v2.o
|
||||
|
||||
187
lib/stm32/g4/fdcan.c
Normal file
187
lib/stm32/g4/fdcan.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/** @addtogroup fdcan_file FDCAN peripheral API
|
||||
*
|
||||
* @ingroup peripheral_apis
|
||||
*
|
||||
* @brief <b>libopencm3 STM32 FDCAN</b>
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @author @htmlonly © @endhtmlonly 2021 Eduard Drusa <ventyl86 at netkosice dot sk>
|
||||
*
|
||||
* Devices can have up to three FDCAN peripherals residing in one FDCAN block. The peripherals
|
||||
* support both CAN 2.0 A and B standard and Bosch FDCAN standard. FDCAN frame format and
|
||||
* bitrate switching is supported. The peripheral has several filters for incoming messages that
|
||||
* can be distributed between two FIFOs and three transmit mailboxes. For transmitted messages
|
||||
* it is possible to opt for event notification once message is transmitted.
|
||||
*
|
||||
* LGPL License Terms @ref lgpl_license
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2021 Eduard Drusa <ventyl86 at netkosice dot sk>
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libopencm3/stm32/fdcan.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* --- FD-CAN functions ----------------------------------------------------- */
|
||||
|
||||
/** @ingroup fdcan_file */
|
||||
/**@{
|
||||
* */
|
||||
|
||||
/** Returns actual size of FIFO entry in FIFO for given CAN port and FIFO.
|
||||
*
|
||||
* Obtains value of FIFO entry length. For G4 it returns constant value as
|
||||
* G4 has FIFO element length hardcoded.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block. Unused.
|
||||
* @param [in] fifo_id ID of FIFO whole length is queried. Unused.
|
||||
* @returns Length of FIFO entry length covering frame header and frame payload.
|
||||
*/
|
||||
unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id)
|
||||
{
|
||||
/* Silences compiler. Variables are present for API compatibility
|
||||
* with STM32H7
|
||||
*/
|
||||
(void) (canport);
|
||||
(void) (fifo_id);
|
||||
return sizeof(struct fdcan_rx_fifo_element);
|
||||
}
|
||||
|
||||
/** Returns actual size of transmit entry in transmit queue/FIFO for given CAN port.
|
||||
*
|
||||
* Obtains value of entry length in transmit queue/FIFO. For G4 it returns constant value
|
||||
* as G4 has transmit buffer entries of fixed length.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block. Unused.
|
||||
* @param [in] fifo_id ID of FIFO whole length is queried. Unused.
|
||||
* @returns Length of FIFO entry length covering frame header and frame payload.
|
||||
*/
|
||||
unsigned fdcan_get_txbuf_element_size(uint32_t canport)
|
||||
{
|
||||
/* Silences compiler. Variables are present for API compatibility
|
||||
* with STM32H7
|
||||
*/
|
||||
(void) (canport);
|
||||
return sizeof(struct fdcan_tx_buffer_element);
|
||||
}
|
||||
|
||||
/** Configure amount of filters and initialize filtering block.
|
||||
*
|
||||
* This function allows to configure global amount of filters present.
|
||||
* FDCAN block will only ever check as many filters as this function configures.
|
||||
* Function will also clear all filter blocks to zero values. This function
|
||||
* can be only called after @ref fdcan_init has already been called and
|
||||
* @ref fdcan_start has not been called yet as registers holding filter
|
||||
* count are write-protected unless FDCAN block is in INIT mode. It is possible
|
||||
* to reconfigure filters (@ref fdcan_set_std_filter and @ref fdcan_set_ext_filter)
|
||||
* after FDCAN block has already been started.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] std_filt requested amount of standard ID filter rules (0-28)
|
||||
* @param [in] ext_filt requested amount of extended ID filter rules (0-8)
|
||||
*/
|
||||
void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
|
||||
{
|
||||
struct fdcan_standard_filter *lfssa = fdcan_get_flssa_addr(canport);
|
||||
struct fdcan_extended_filter *lfesa = fdcan_get_flesa_addr(canport);
|
||||
|
||||
/* Only perform initialization of message RAM if there are
|
||||
* any filters required
|
||||
*/
|
||||
if (std_filt > 0) {
|
||||
FDCAN_RXGFC(canport) =
|
||||
(FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT))
|
||||
| (std_filt << FDCAN_RXGFC_LSS_SHIFT);
|
||||
|
||||
|
||||
for (int q = 0; q < FDCAN_SFT_MAX_NR; ++q) {
|
||||
lfssa[q].type_id1_conf_id2 = 0;
|
||||
}
|
||||
} else {
|
||||
/* Reset filter count to zero */
|
||||
FDCAN_RXGFC(canport) =
|
||||
(FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT));
|
||||
}
|
||||
|
||||
if (ext_filt > 0) {
|
||||
FDCAN_RXGFC(canport) =
|
||||
(FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT))
|
||||
| (ext_filt << FDCAN_RXGFC_LSE_SHIFT);
|
||||
|
||||
for (int q = 0; q < FDCAN_EFT_MAX_NR; ++q) {
|
||||
lfesa[q].conf_id1 = 0;
|
||||
lfesa[q].type_id2 = 0;
|
||||
}
|
||||
} else {
|
||||
/* Reset filter count to zero */
|
||||
FDCAN_RXGFC(canport) =
|
||||
(FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
/** Enable FDCAN operation after FDCAN block has been set up.
|
||||
*
|
||||
* This function will disable FDCAN configuration effectively
|
||||
* allowing FDCAN to sync up with the bus. After calling this function
|
||||
* it is not possible to reconfigure amount of filter rules, yet
|
||||
* it is possible to configure rules themselves. FDCAN block operation
|
||||
* state can be checked using @ref fdcan_get_init_state.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
|
||||
* confirming that it left INIT mode. If set to 0, function will return
|
||||
* immediately.
|
||||
* @returns Operation error status. See @ref fdcan_error.
|
||||
* @note If this function returns with timeout, it usually means that
|
||||
* FDCAN_clk is not set up properly.
|
||||
*/
|
||||
int fdcan_start(uint32_t canport, uint32_t timeout)
|
||||
{
|
||||
/* Error here usually means, that FDCAN_clk is not set up
|
||||
* correctly, or at all. This usually can't be seen above
|
||||
* when INIT is set to 1, because default value for INIT is
|
||||
* 1 as long as one has FDCAN_pclk configured properly.
|
||||
**/
|
||||
if (fdcan_cccr_init_cfg(canport, false, timeout) != 0) {
|
||||
return FDCAN_E_TIMEOUT;
|
||||
}
|
||||
|
||||
return FDCAN_E_OK;
|
||||
}
|
||||
|
||||
/** Configure FDCAN FIFO lock mode
|
||||
*
|
||||
* This function allows to choose between locked and overewrite mode of FIFOs. In locked mode,
|
||||
* whenever FIFO is full and new frame arrives, which would normally been stored into given
|
||||
* FIFO, then frame is dropped. If overwrite mode is active, then most recent message in FIFO
|
||||
* is rewritten by frame just received.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] locked true activates locked mode, false activates overwrite mode
|
||||
*/
|
||||
void fdcan_set_fifo_locked_mode(uint32_t canport, bool locked)
|
||||
{
|
||||
if (locked) {
|
||||
FDCAN_RXGFC(canport) &= ~(FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM);
|
||||
} else {
|
||||
FDCAN_RXGFC(canport) |= FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM;
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@@ -39,6 +39,7 @@ ARFLAGS = rcs
|
||||
|
||||
OBJS += dac_common_all.o dac_common_v2.o
|
||||
OBJS += exti_common_all.o
|
||||
OBJS += fdcan.o fdcan_common.o
|
||||
OBJS += flash_common_all.o flash_common_f.o flash_common_f24.o
|
||||
OBJS += fmc_common_f47.o
|
||||
OBJS += gpio_common_all.o gpio_common_f0234.o
|
||||
|
||||
473
lib/stm32/h7/fdcan.c
Normal file
473
lib/stm32/h7/fdcan.c
Normal file
@@ -0,0 +1,473 @@
|
||||
/** @addtogroup fdcan_file FDCAN peripheral API
|
||||
*
|
||||
* @ingroup peripheral_apis
|
||||
*
|
||||
* @brief <b>libopencm3 STM32 FDCAN</b>
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @author @htmlonly © @endhtmlonly 2021 Eduard Drusa <ventyl86 at netkosice dot sk>
|
||||
*
|
||||
* Device is equipped with two FDCAN peripherals residing in one FDCAN block. The peripherals
|
||||
* support both CAN 2.0 A and B standard and Bosch FDCAN standard. FDCAN frame format and
|
||||
* bitrate switching is supported. The peripheral has several filters for incoming messages that
|
||||
* can be distributed between two FIFOs and transmit buffer all of configurable amount of
|
||||
* entries. For transmitted messages it is possible to opt for event notification once message
|
||||
* is transmitted.
|
||||
*
|
||||
* The FDCAN peripheral present in STM32 H7 is a superset of FDCAN peripheral found in other MCUs
|
||||
* such as STM32 G4. It allows more fine-grade control over buffer and filter allocation and
|
||||
* supports TTCAN on CAN1, etc. This driver provides source-level backwards compatible
|
||||
* implementation of driver, which allows build of unmodified software originally written for
|
||||
* STM32 G4 on STM32 H7.
|
||||
*
|
||||
* LGPL License Terms @ref lgpl_license
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2021 Eduard Drusa <ventyl86 at netkosice dot sk>
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libopencm3/stm32/fdcan.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define FDCAN_LSS_COUNT(can_base) \
|
||||
((FDCAN_SIDFC(can_base) >> FDCAN_SIDFC_LSS_SHIFT) & FDCAN_SIDFC_LSS_MASK)
|
||||
|
||||
#define FDCAN_LSE_COUNT(can_base) \
|
||||
((FDCAN_XIDFC(can_base) >> FDCAN_XIDFC_LSE_SHIFT) & FDCAN_XIDFC_LSE_MASK)
|
||||
|
||||
/* --- FD-CAN functions ----------------------------------------------------- */
|
||||
|
||||
/** @ingroup fdcan_file */
|
||||
/**@{
|
||||
* */
|
||||
|
||||
/** Returns actual size of FIFO entry in FIFO for given CAN port and FIFO.
|
||||
*
|
||||
* Obtains value of FIFO entry length. This value covers both fixed-size frame
|
||||
* header block and payload buffer. User can configure payload buffer size individually
|
||||
* for each FIFO and designated RX buffer.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] fifo_id ID of FIFO whole length is queried.
|
||||
* @returns Length of FIFO entry length covering frame header and frame payload.
|
||||
*/
|
||||
unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id)
|
||||
{
|
||||
unsigned element_size;
|
||||
if (fifo_id == 0) {
|
||||
element_size = FDCAN_RXESC(canport) >> FDCAN_RXESC_F0DS_SHIFT;
|
||||
} else {
|
||||
element_size = FDCAN_RXESC(canport) >> FDCAN_RXESC_F1DS_SHIFT;
|
||||
}
|
||||
|
||||
/* Mask is unshifted and at this point, element_size is unshifted too */
|
||||
return 8 + fdcan_dlc_to_length((element_size & FDCAN_RXESC_F0DS_MASK) | 0x7);
|
||||
}
|
||||
|
||||
/** Returns actual size of transmit entry in transmit queue/FIFO for given CAN port.
|
||||
*
|
||||
* Obtains value of entry length in transmit queue/FIFO. This value covers both
|
||||
* fixed-sized frame header block and payload buffer. User can configure payload buffer
|
||||
* size.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] fifo_id ID of FIFO whole length is queried.
|
||||
* @returns Length of FIFO entry length covering frame header and frame payload.
|
||||
*/
|
||||
unsigned fdcan_get_txbuf_element_size(uint32_t canport)
|
||||
{
|
||||
unsigned element_size;
|
||||
element_size = (FDCAN_TXESC(canport) >> FDCAN_TXESC_TBDS_SHIFT) & FDCAN_TXESC_TBDS_MASK;
|
||||
return 8 + fdcan_dlc_to_length((element_size & FDCAN_TXESC_TBDS_MASK) | 0x7);
|
||||
}
|
||||
|
||||
/** Initialize allocation of standard filter block in CAN message RAM.
|
||||
*
|
||||
* Allows specifying size of standard filtering block (in term of available filtering
|
||||
* rules and filter base address within CAN message RAM. Note, that there are no limitations
|
||||
* nor checking on address provided. It is possible to share whole filtering block or
|
||||
* portion of it between multiple CAN interfaces.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] flssa standard filtering block start address offset in message RAM
|
||||
* @param [in] lss amount of standard filters
|
||||
*/
|
||||
void fdcan_init_std_filter_ram(uint32_t canport, uint32_t flssa, uint8_t lss)
|
||||
{
|
||||
FDCAN_SIDFC(canport) = flssa << FDCAN_SIDFC_FLSSA_SHIFT
|
||||
| lss << FDCAN_SIDFC_LSS_SHIFT;
|
||||
}
|
||||
|
||||
/** Initialize allocation of extended filter block in CAN message RAM.
|
||||
*
|
||||
* Allows specifying size of extended filtering block (in term of available filtering
|
||||
* rules and filter base address within CAN message RAM. Note, that there are no limitations
|
||||
* nor checking on address provided. It is possible to share whole filtering block or
|
||||
* portion of it between multiple CAN interfaces.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] flesa extended filtering block start address offset in message RAM
|
||||
* @param [in] lse amount of extended filters
|
||||
*/
|
||||
void fdcan_init_ext_filter_ram(uint32_t canport, uint32_t flesa, uint8_t lse)
|
||||
{
|
||||
FDCAN_XIDFC(canport) = flesa << FDCAN_XIDFC_FLESA_SHIFT
|
||||
| lse << FDCAN_XIDFC_LSE_SHIFT;
|
||||
}
|
||||
|
||||
/** Initialize allocation of FIFO block in CAN message RAM.
|
||||
*
|
||||
* Allows specifying size of FIFO block (in term of available messages in FIFO
|
||||
* and FIFO base address within CAN message RAM. Note, that there are no limitations
|
||||
* nor checking on address provided.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] fifo_id ID of fifo being configured
|
||||
* @param [in] fxsa FIFO block start address offset in message RAM
|
||||
* @param [in] fxs amount of entries allocated in FIFO
|
||||
*/
|
||||
void fdcan_init_fifo_ram(uint32_t canport, unsigned fifo_id, uint32_t fxsa, uint8_t fxs)
|
||||
{
|
||||
FDCAN_RXFIC(canport, fifo_id) = (FDCAN_RXFIC(canport, fifo_id)
|
||||
& ~(
|
||||
(FDCAN_RXFIC_FIS_MASK << FDCAN_RXFIC_FIS_SHIFT)
|
||||
| (FDCAN_RXFIC_FISA_MASK << FDCAN_RXFIC_FISA_SHIFT)
|
||||
))
|
||||
| (fxs << FDCAN_RXFIC_FIS_SHIFT)
|
||||
| (fxsa & (FDCAN_RXFIC_FISA_MASK << FDCAN_RXFIC_FISA_SHIFT));
|
||||
}
|
||||
|
||||
/** Initialize allocation of transmit event block in CAN message RAM.
|
||||
*
|
||||
* Allows specifying size of transmit event block (in term of allocated events and block
|
||||
* base address within CAN message RAM. Note, that there are no limitations
|
||||
* nor checking on address provided.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] fifo_id ID of fifo being configured
|
||||
* @param [in] fxsa FIFO block start address offset in message RAM
|
||||
* @param [in] fxs amount of entries allocated in FIFO
|
||||
*/
|
||||
void fdcan_init_tx_event_ram(uint32_t canport, uint32_t tesa, uint8_t tes)
|
||||
{
|
||||
FDCAN_TXEFC(canport) = (FDCAN_TXEFC(canport)
|
||||
& ~(
|
||||
(FDCAN_TXEFC_EFS_MASK << FDCAN_TXEFC_EFS_SHIFT)
|
||||
| (FDCAN_TXEFC_EFSA_MASK << FDCAN_TXEFC_EFSA_SHIFT)
|
||||
))
|
||||
| (tes << FDCAN_TXEFC_EFS_SHIFT)
|
||||
| (tesa & (FDCAN_TXEFC_EFSA_MASK << FDCAN_TXEFC_EFSA_SHIFT));
|
||||
}
|
||||
|
||||
/** Initialize allocation of transmit queue block in CAN message RAM.
|
||||
*
|
||||
* Allows specifying size of transmit queue block (in term of allocated buffers and block
|
||||
* base address within CAN message RAM. Note, that there are no limitations
|
||||
* nor checking on address provided.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] fifo_id ID of fifo being configured
|
||||
* @param [in] fxsa FIFO block start address offset in message RAM
|
||||
* @param [in] fxs amount of entries allocated in FIFO
|
||||
*/
|
||||
void fdcan_init_tx_buffer_ram(uint32_t canport, uint32_t tbsa, uint8_t tbs)
|
||||
{
|
||||
FDCAN_TXBC(canport) = (FDCAN_TXBC(canport)
|
||||
& ~(
|
||||
(FDCAN_TXBC_TFQS_MASK << FDCAN_TXBC_TFQS_SHIFT)
|
||||
| (FDCAN_TXBC_TBSA_MASK << FDCAN_TXBC_TBSA_SHIFT)
|
||||
))
|
||||
| (tbs << FDCAN_TXBC_TFQS_SHIFT)
|
||||
| (tbsa & (FDCAN_TXBC_TBSA_MASK << FDCAN_TXBC_TBSA_SHIFT));
|
||||
}
|
||||
|
||||
/** Initialize size of data fields in reception buffers.
|
||||
*
|
||||
* Configures maximum size of message payload, which can be stored in different
|
||||
* reception buffers. Each buffer can have maximum payload size configured independently.
|
||||
* Buffers can only be configured to sizes which are valid sizes of FDCAN payload. Sizes smaller
|
||||
* than 8 are automatically padded to 8.
|
||||
* @note If you change these values, then content of reception buffers is invalidated. You may
|
||||
* also want to recalculate base addresses of objects in message RAM as their size might have
|
||||
* changed.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] rxbuf maximum storable payload size of dedicated RX buffer
|
||||
* @param [in] rxfifo0 maximum storable payload size of RX FIFO 0
|
||||
* @param [in] rxfifo1 maximum storable payload size of RX FIFO 1
|
||||
* @returns operation return status. See @ref fdcan_error.
|
||||
*/
|
||||
int fdcan_set_rx_element_size(uint32_t canport, uint8_t rxbuf, uint8_t rxfifo0, uint8_t rxfifo1)
|
||||
{
|
||||
unsigned rxbufdlc = fdcan_length_to_dlc(rxbuf);
|
||||
unsigned rxfifo0dlc = fdcan_length_to_dlc(rxfifo0);
|
||||
unsigned rxfifo1dlc = fdcan_length_to_dlc(rxfifo1);
|
||||
|
||||
if (rxbufdlc == 0xFF || rxfifo0dlc == 0xFF || rxfifo1dlc == 0xFF) {
|
||||
return FDCAN_E_INVALID;
|
||||
}
|
||||
|
||||
/* RXESC fields use format of FDCAN DLC, albeit using only
|
||||
* three LSBs. DLC < 8 is always allocated as 8 bytes and is always
|
||||
* encoded as 0.
|
||||
*/
|
||||
if (rxbufdlc <= 8) {
|
||||
rxbufdlc = 0;
|
||||
} else {
|
||||
rxbufdlc &= ~(1 << 4);
|
||||
}
|
||||
|
||||
if (rxfifo0dlc <= 8) {
|
||||
rxfifo0dlc = 0;
|
||||
} else {
|
||||
rxfifo0dlc &= ~(1 << 4);
|
||||
}
|
||||
|
||||
if (rxfifo1dlc <= 8) {
|
||||
rxfifo1dlc = 0;
|
||||
} else {
|
||||
rxfifo1dlc &= ~(1 << 4);
|
||||
}
|
||||
|
||||
FDCAN_RXESC(canport) = rxbufdlc << FDCAN_RXESC_RBDS_SHIFT
|
||||
| rxfifo1dlc << FDCAN_RXESC_F1DS_SHIFT
|
||||
| rxfifo0dlc << FDCAN_RXESC_F0DS_SHIFT;
|
||||
|
||||
return FDCAN_E_OK;
|
||||
}
|
||||
|
||||
/** Initialize size of data fields in transmit buffers.
|
||||
*
|
||||
* Configures maximum size of message payload, which can be stored either in dedicated
|
||||
* transmit buffer or into transmit queue/FIFO. One size is applied both to transmit buffer
|
||||
* and transmit queue / FIFO. Buffers can only be configured to sizes which are valid sizes
|
||||
* of FDCAN payload. Sizes smaller than 8 are automatically padded to 8 bytes.
|
||||
* @note If you change these values, then content of transmission buffers is invalidated. You may
|
||||
* also want to recalculate base addresses of objects in message RAM as their size might have
|
||||
* changed.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] txbuf maximum storable payload size of TX buffer / FIFO / queue
|
||||
* @returns operation return status. See @ref fdcan_error.
|
||||
*/
|
||||
int fdcan_set_tx_element_size(uint32_t canport, uint8_t txbuf)
|
||||
{
|
||||
unsigned txbufdlc = fdcan_length_to_dlc(txbuf);
|
||||
|
||||
if (txbufdlc == 0xFF) {
|
||||
return FDCAN_E_INVALID;
|
||||
}
|
||||
|
||||
if (txbufdlc <= 8) {
|
||||
txbufdlc = 0;
|
||||
} else {
|
||||
txbufdlc &= ~(1 << 4);
|
||||
}
|
||||
|
||||
FDCAN_TXESC(canport) = txbufdlc << FDCAN_TXESC_TBDS_SHIFT;
|
||||
|
||||
return FDCAN_E_OK;
|
||||
}
|
||||
|
||||
/** Configure amount of filters and initialize filtering block.
|
||||
*
|
||||
* This function allows to configure global amount of filters present.
|
||||
* FDCAN block will only ever check as many filters as this function configures.
|
||||
* Function will also clear all filter blocks to zero values. This function
|
||||
* can be only called after @ref fdcan_init has already been called and
|
||||
* @ref fdcan_start has not been called yet as registers holding filter
|
||||
* count are write-protected unless FDCAN block is in INIT mode. It is possible
|
||||
* to reconfigure filters (@ref fdcan_set_std_filter and @ref fdcan_set_ext_filter)
|
||||
* after FDCAN block has already been started.
|
||||
*
|
||||
* This function is provided for source level compatibility with code written for
|
||||
* STM32G4. As an alternative, you can use @ref fdcan_init_std_filter_ram() and
|
||||
* @ref fdcan_init_ext_filter_ram() to have more control over filtering configuration.
|
||||
* Note that if you do so, your code won't be compatible with STM32G4 controllers.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] std_filt requested amount of standard ID filter rules (0-28)
|
||||
* @param [in] ext_filt requested amount of extended ID filter rules (0-8)
|
||||
*/
|
||||
void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
|
||||
{
|
||||
struct fdcan_standard_filter *lfssa;
|
||||
struct fdcan_extended_filter *lfesa;
|
||||
|
||||
int can_id = FDCAN_BLOCK_ID(canport);
|
||||
|
||||
int lfsofs = 0;
|
||||
int lfeofs = 0;
|
||||
int lfssize = std_filt * sizeof(struct fdcan_standard_filter);
|
||||
int lfesize = ext_filt * sizeof(struct fdcan_extended_filter);
|
||||
|
||||
if (can_id == 0) {
|
||||
lfsofs = 0;
|
||||
lfeofs = lfssize;
|
||||
} else {
|
||||
lfsofs = CAN_MSG_BASE + CAN_MSG_SIZE - lfssize;
|
||||
lfeofs = lfsofs - lfesize;
|
||||
}
|
||||
|
||||
fdcan_init_std_filter_ram(canport, lfsofs, 28);
|
||||
fdcan_init_ext_filter_ram(canport, lfeofs, 8);
|
||||
|
||||
lfssa = fdcan_get_flssa_addr(canport);
|
||||
lfesa = fdcan_get_flesa_addr(canport);
|
||||
|
||||
/* Only perform initialization of message RAM if there are
|
||||
* any filters required
|
||||
*/
|
||||
if (std_filt > 0) {
|
||||
for (int q = 0; q < 28; ++q) {
|
||||
lfssa[q].type_id1_conf_id2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_filt > 0) {
|
||||
for (int q = 0; q < 8; ++q) {
|
||||
lfesa[q].conf_id1 = 0;
|
||||
lfesa[q].type_id2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Enable FDCAN operation after FDCAN block has been set up.
|
||||
*
|
||||
* This function will disable FDCAN configuration effectively
|
||||
* allowing FDCAN to sync up with the bus. After calling this function
|
||||
* it is not possible to reconfigure amount of filter rules, yet
|
||||
* it is possible to configure rules themselves. FDCAN block operation
|
||||
* state can be checked using @ref fdcan_get_init_state.
|
||||
*
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
|
||||
* confirming that it left INIT mode. If set to 0, function will return
|
||||
* immediately.
|
||||
* @returns Operation error status. See @ref fdcan_error.
|
||||
* @note If this function returns with timeout, it usually means that
|
||||
* FDCAN_clk is not set up properly.
|
||||
*/
|
||||
int fdcan_start(uint32_t canport, uint32_t timeout)
|
||||
{
|
||||
/* This block detects obviously invalid configuration of
|
||||
* RX FIFOs and TX buffers. Such situation may happen
|
||||
* if code originally targeted for STM32G4 is compiled for
|
||||
* STM32H7. This code is not aware of H7 abilities and
|
||||
* leaves this stuff unconfigured. We pick up here and
|
||||
* assume, that the code wants the FDCAN to behave as if
|
||||
* it was STM32G4. Therefore we will configure two three entry
|
||||
* long RX FIFOs, three entry long TX event buffer and three
|
||||
* entry long TX FIFO/queue here.
|
||||
*/
|
||||
if (FDCAN_RXFIFO_OFFSET(canport, 0) == 0
|
||||
&& FDCAN_RXFIFO_OFFSET(canport, 1) == 0
|
||||
&& FDCAN_TXBUF_OFFSET(canport) == 0
|
||||
&& FDCAN_TXEVT_OFFSET(canport) == 0
|
||||
&& FDCAN_RXESC(canport) == 0
|
||||
&& FDCAN_TXESC(canport) == 0
|
||||
) {
|
||||
/* These sizes are fixed based on what G4 contains. */
|
||||
int fifo0_size = 3 * sizeof(struct fdcan_rx_fifo_element);
|
||||
int fifo1_size = fifo0_size;
|
||||
int txevt_size = 3 * sizeof(struct fdcan_tx_event_element);
|
||||
int txbuf_size = 3 * sizeof(struct fdcan_tx_buffer_element);
|
||||
|
||||
fdcan_set_rx_element_size(canport, 0, 64, 64);
|
||||
fdcan_set_tx_element_size(canport, 64);
|
||||
|
||||
/* At this point we simply assume that FLSSA and FLESA were
|
||||
* set up by calling fdcan_init_filter(). It they weren't,
|
||||
* there just won't be allocated any space for them.
|
||||
* That's not a problem as after fdcan_start returns, it is
|
||||
* not possible to configure filter amount anymore.
|
||||
*
|
||||
* Default approach is to configure CAN1 to occupy bottom
|
||||
* of message RAM and CAN1 to occupy top of message RAM keeping
|
||||
* large unused space in between. This ensures that even if someone
|
||||
* starts playing with CAN1, "implicit" configuration of CAN2 done
|
||||
* here won't break things magically.
|
||||
*/
|
||||
if (FDCAN_BLOCK_ID(canport) == 0) {
|
||||
/* CAN1 will use bottom-up layout with
|
||||
* FLSSA < FLESA < F0SA < F1SA < TXESA < TXBSA
|
||||
* Rx buffer is not used.
|
||||
*/
|
||||
fdcan_init_fifo_ram(canport, 0,
|
||||
FDCAN_LFESA_OFFSET(canport)
|
||||
+ FDCAN_LSE_COUNT(canport) * sizeof(struct fdcan_extended_filter),
|
||||
3);
|
||||
fdcan_init_fifo_ram(canport, 1,
|
||||
FDCAN_RXFIFO_OFFSET(canport, 0) + fifo0_size, 3);
|
||||
fdcan_init_tx_event_ram(canport,
|
||||
FDCAN_RXFIFO_OFFSET(canport, 1) + fifo1_size, 3);
|
||||
fdcan_init_tx_buffer_ram(canport,
|
||||
FDCAN_TXEVT_OFFSET(canport) + txevt_size, 3);
|
||||
} else {
|
||||
/* LFESA might be uninitialized. In such case
|
||||
* we forge it's address at the end of MSG RAM
|
||||
*/
|
||||
int lfesa_offs = FDCAN_LFESA_OFFSET(canport);
|
||||
if (lfesa_offs == 0) {
|
||||
lfesa_offs = CAN_MSG_SIZE;
|
||||
}
|
||||
/* CAN2 will use top-down layout with
|
||||
* TXBSA < TXESA < F1SA < F0SA < FLESA < FLSSA
|
||||
* Rx buffer is not used.
|
||||
* This arrangement should ensure that even if
|
||||
* CAN1 was configured manually, CAN2 default
|
||||
* configuration should not break CAN1.
|
||||
*/
|
||||
fdcan_init_fifo_ram(canport, 0, lfesa_offs - fifo0_size, 3);
|
||||
fdcan_init_fifo_ram(canport, 1,
|
||||
FDCAN_RXFIFO_OFFSET(canport, 0) - fifo1_size, 3);
|
||||
fdcan_init_tx_event_ram(canport,
|
||||
FDCAN_RXFIFO_OFFSET(canport, 1) - txevt_size, 3);
|
||||
fdcan_init_tx_buffer_ram(canport,
|
||||
FDCAN_TXEVT_OFFSET(canport) - txbuf_size, 3);
|
||||
}
|
||||
|
||||
}
|
||||
/* Error here usually means, that FDCAN_clk is not set up
|
||||
* correctly, or at all. This usually can't be seen above
|
||||
* when INIT is set to 1, because default value for INIT is
|
||||
* 1 as long as one has FDCAN_pclk configured properly.
|
||||
**/
|
||||
if (fdcan_cccr_init_cfg(canport, false, timeout) != 0) {
|
||||
return FDCAN_E_TIMEOUT;
|
||||
}
|
||||
|
||||
return FDCAN_E_OK;
|
||||
}
|
||||
|
||||
/** Configure FDCAN FIFO lock mode
|
||||
*
|
||||
* This function allows to choose between locked and overewrite mode of FIFOs. In locked mode,
|
||||
* whenever FIFO is full and new frame arrives, which would normally been stored into given
|
||||
* FIFO, then frame is dropped. If overwrite mode is active, then most recent message in FIFO
|
||||
* is rewritten by frame just received.
|
||||
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
|
||||
* @param [in] locked true activates locked mode, false activates overwrite mode
|
||||
*/
|
||||
void fdcan_set_fifo_locked_mode(uint32_t canport, bool locked)
|
||||
{
|
||||
if (locked) {
|
||||
FDCAN_RXF0C(canport) &= ~(FDCAN_RXF0C_F0OM);
|
||||
FDCAN_RXF1C(canport) &= ~(FDCAN_RXF1C_F1OM);
|
||||
} else {
|
||||
FDCAN_RXF0C(canport) |= FDCAN_RXF0C_F0OM;
|
||||
FDCAN_RXF1C(canport) |= FDCAN_RXF1C_F1OM;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user