pac55xx: implemented CAN module interface for qorvo pac55xx.

This commit is contained in:
Kevin Stefanik
2020-02-13 18:39:54 -05:00
committed by Karl Palsson
parent cb83273416
commit 245761f894
3 changed files with 768 additions and 0 deletions

View File

@@ -0,0 +1,307 @@
/**
* @brief CAN definitions for the Qorvo PAC55xx series of microcontrollers.
*
* @addtogroup PAC55xx_can CAN
* @ingroup PAC55xx_defines
* @author Kevin Stefanik <kevin@allocor.tech>
* LGPL License Terms @ref lgpl_license
* @date February 13, 2020
*
* Definitions in this file come from the PAC55XX Family User Guide Rev 1.23
* by Active-Semi dated November 19, 2019.
*
* Note: all memory-mapped writes must be performed using 32-bit registers.
* Any 8-bit memory-mapped registers below may only be used to read.
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2020 Kevin Stefanik <kevin@allocor.tech>
*
* 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/>.
*/
#ifndef LIBOPENCM3_PAC55XX_CAN_H_
#define LIBOPENCM3_PAC55XX_CAN_H_
#include <libopencm3/pac55xx/memorymap.h>
#include <libopencm3/cm3/common.h>
/**@{*/
/**
* @defgroup can_isr_sr_cmr_mr CAN ISR/SR/CMR/MR Registers
* @{*/
/** This is the 32-bit memory mapped read/write accessor for:
* - ISR - bits 31:24 - Interrupt Status/ACK Register RW, default 00h
* - SR - bits 23:16 - Status Register RO, default 00h
* - CMR - bits 15:8 - Command RW, default 00h
* - MR - bits 7:0 - Mode RW, default 04h
* When writing, be sure to use CAN_ISR_SR_CMR_MR_SET and CAN_ISR_SR_CMR_MR_CLEAR
* so as to avoid inadvertently Acknowledging an ISR bit. Writing '1' to one
* of the ISR bits when it is triggered/set will ACK/clear the bit.
*/
#define CAN_ISR_SR_CMR_MR(can_base) MMIO32((can_base) + 0x0000)
#define CAN_ISR_SR_CMR_MR_SET(can_base, bits) (CAN_ISR_SR_CMR_MR(can_base) = \
(CAN_ISR_SR_CMR_MR(can_base) & 0x00FFFFFF) | (bits))
#define CAN_ISR_SR_CMR_MR_CLEAR(can_base, bits) (CAN_ISR_SR_CMR_MR(can_base) = \
(CAN_ISR_SR_CMR_MR(can_base) & 0x00FFFFFF) & ~(bits))
/**@}*/
/**
* @defgroup can_btr01_rmc_imr CAN BTR1/BTR0/RMC/IMR Registers
* @{*/
/** This is the 32-bit memory mapped read/write accessor for:
* - BTR1 - bits 31:24 - Bus Timing 1 Register RW, default 00h
* - BTR0 - bits 23:16 - Bus Timing 0 Register RW, default 00h
* - RMC - bits 15:8 - Receive Message Counter RO, default 00h
* - IMR - bits 7:0 - Interrupt Mask Register RW, default 00h
*/
#define CAN_BTR1_BTR0_RMC_IMR(can_base) MMIO32((can_base) + 0x0004)
/**@}*/
/** CAN Transmit Buffer Register RW, default 00000000h */
#define CAN_TXBUF(can_base) MMIO32((can_base) + 0x0008)
/** CAN Receive Buffer Register RO, default 00000000h */
#define CAN_RXBUF(can_base) MMIO32((can_base) + 0x000C)
/** CAN Acceptance Code Register RW, default 00000000h */
#define CAN_ACR(can_base) MMIO32((can_base) + 0x0010)
/** CAN Acceptance Mask Register RW, default 00000000h */
#define CAN_AMR(can_base) MMIO32((can_base) + 0x0014)
/**
* @defgroup can_alc_txrxerr_ecc CAN ALC/TXERR/RXERR/ECC Registers
* @{*/
#define CAN_ALC_TXERR_RXERR_ECC(can_base) MMIO32((can_base) + 0x0018)
/** CAN Error Code Capture Register RO, default 00h */
#define CAN_ECC(can_base) (CAN_ALC_TXERR_RXERR_ECC(can_base) & 0xFF)
/** CAN RX Error Counter Register RO, default 00h */
#define CAN_RXERR(can_base) ((CAN_ALC_TXERR_RXERR_ECC(can_base) >> 8) & 0xFF)
/** CAN TX Error Counter Register RO, default 00h */
#define CAN_TXERR(can_base) ((CAN_ALC_TXERR_RXERR_ECC(can_base) >> 16) & 0xFF)
/** CAN Arbitration Lost Code Capture Register RO, default 00h */
#define CAN_ALC(can_base) ((CAN_ALC_TXERR_RXERR_ECC(can_base) >> 24) & 0xFF)
/**@}*/
/** CAN Mode Register bit definitions. This register controls high level modes of the CAN peripheral.
* @defgroup can_mr_bits CAN Mode Register
* @{*/
/** AFM: Acceptance Filter Mode */
#define CAN_MR_AFM BIT0
/** LOM: Listen only mode */
#define CAN_MR_LOM BIT1
/** RM: Reset Mode */
#define CAN_MR_RM BIT2
/**@}*/
/** CAN Command Register. This register commands the CAN peripheral to either transmit or abort.
* @defgroup can_cmr_bits CAN Command Register
* @{*/
/** AT: Abort transmission */
#define CAN_CMR_AT BIT9
/** TR: Transmit Request */
#define CAN_CMR_TR BIT10
/**@}*/
/** CAN Status Register. This register provides read-only status of the CAN peripheral.
* @defgroup can_sr_bits CAN Status Register
* @{*/
/** BS: Bus Off Status */
#define CAN_SR_BS BIT16
/** ES: Error Status */
#define CAN_SR_ES BIT17
/** TS: Transmit Status */
#define CAN_SR_TS BIT18
/** RS: Receive Status */
#define CAN_SR_RS BIT19
/** TBS: Transmit Buffer Status */
#define CAN_SR_TBS BIT21
/** DSO: Data Overrun Status */
#define CAN_SR_DSO BIT22
/** RBS: Receive Buffer Status */
#define CAN_SR_RBS BIT23
/**@}*/
/** CAN Interrupt Status Register bit definitions.
* - 1: interrupt triggered
* - 0: no interrupt
* - Writing a 1 to a triggered interrupt clears the bit.
* @defgroup can_isr_bits CAN Interrupt Status Register
* @{*/
/** DOI: Data Overflow Interrupt */
#define CAN_ISR_DOI BIT24
/** BEI: Bus Error Interrupt */
#define CAN_ISR_BEI BIT25
/** TI: Transmit Interrupt */
#define CAN_ISR_TI BIT26
/** RI: Receive Interrupt */
#define CAN_ISR_RI BIT27
/** EPI: Error Passive Interrupt */
#define CAN_ISR_EPI BIT28
/** EWI: Error Warning Interrupt */
#define CAN_ISR_EWI BIT29
/** ALI: Arbitration Lost Interrupt */
#define CAN_ISR_ALI BIT30
/** This is a helper to acknowledge an ISR */
#define CAN_ISR_ACKNOWLEDGE(can_base, isr) CAN_ISR_SR_CMR_MR_SET(can_base, ((isr) & 0x7F000000))
/**@}*/
/** CAN Interrupt Mask Register bit definitions.
* 0: disables/masks interrupt
* 1: enables interrupt
* @defgroup can_imr_bits CAN Mask Register
* @{*/
/** DOIM: DOI Interrupt Mask */
#define CAN_IMR_DOIM BIT0
/** BEIM: BEI Interrupt Mask */
#define CAN_IMR_BEIM BIT1
/** TIM: TI Interrupt Mask */
#define CAN_IMR_TIM BIT2
/** RIM: RI Interrupt Mask */
#define CAN_IMR_RIM BIT3
/** EPIM: EPI Interrupt Mask */
#define CAN_IMR_EPIM BIT4
/** EWIM: EWI Interrupt Mask */
#define CAN_IMR_EWIM BIT5
/** ALIM: ALI Interrupt Mask */
#define CAN_IMR_ALIM BIT6
/**@}*/
/** CAN Receive Message Counter Register bit definitions.
* @defgroup can_rmc_bits CAN Receive Message Counter Register.
* @{*/
#define CAN_RMC(can_base) ((CAN_BTR1_BTR0_RMC_IMR(can_base) >> 8) & 0x1F)
/**@}*/
/** CAN Bus Timing 0 Register bit definitions.
* @defgroup can_btr0_bits CAN Bus Timing 0 Register.
* @{*/
#define CAN_BTR0_BRP_MASK (0x3F)
#define CAN_BTR0_BRP_SHIFT 16
#define CAN_BTR0_BRP(val) (((val) & CAN_BTR0_BRP_MASK) << CAN_BTR0_BRP_SHIFT)
#define CAN_BTR0_SJW_MASK (0x03)
#define CAN_BTR0_SJW_SHIFT 22
#define CAN_BTR0_SJW(val) (((val) & CAN_BTR0_SJW_MASK) << CAN_BTR0_SJW_SHIFT)
/**@}*/
/** CAN Bus Timing 1 Register bit definitions.
* @defgroup can_btr1_bits CAN Bus Timing 1 Register
* @{*/
#define CAN_BTR1_TSEG1_MASK (0x0F)
#define CAN_BTR1_TSEG1_SHIFT 24
#define CAN_BTR1_TSEG1(val) (((val) & CAN_BTR1_TSEG1_MASK) << CAN_BTR1_TSEG1_SHIFT)
#define CAN_BTR1_TSEG2_MASK (0x07)
#define CAN_BTR1_TSEG2_SHIFT 28
#define CAN_BTR1_TSEG2(val) (((val) & CAN_BTR1_TSEG2_MASK) << CAN_BTR1_TSEG2_SHIFT)
#define CAN_BTR1_SAM BIT31
/**@}*/
/** CAN Error Code Capture Register bit definitions.
* @defgroup can_ecc_bits CAN Error Code Capture Register
* @{*/
/** BER: Bit error ocurred */
#define CAN_ECC_BER BIT0
/** STFER: Stuff error occurred */
#define CAN_ECC_STFER BIT1
/** CRCER: CRC error occurred */
#define CAN_ECC_CRCER BIT2
/** FRMER: Form error occurred */
#define CAN_ECC_FRMER BIT3
/** ACKER: ACK error occurred */
#define CAN_ECC_ACKER BIT4
/** EDIR: Direction of transfer 0:TX, 1:RX */
#define CAN_ECC_EDIR BIT5
/** TXWRN: set when CAN_TXERR >= 96 */
#define CAN_ECC_TXWRN BIT6
/** RXWRN: set when CAN_RXERR >= 96 */
#define CAN_ECC_RXWRN BIT7
/**@}*/
/** CAN Acceptance Code/Mask Register. This is used for filtering messages.
* Mask value of 1 ignores the bit. Mask value of 0 checks the bit.
* @defgroup can_acr_bits CAN Acceptance Code Register
* @{*/
#define CAN_ACR_DUAL_DB_UPPER 0x000F0000U /* 19:16 */
#define CAN_ACR_DUAL_DB_LOWER 0x0000000FU /* 3:0 */
#define CAN_ACR_DUAL_ID1 0xFFE00000U /* 31:21 */
#define CAN_ACR_DUAL_ID2 0x0000FFE0U /* 15:5 */
#define CAN_ACR_DUAL_RTR1 0x00100000U /* 20 */
#define CAN_ACR_DUAL_RTR2 0x00000010U /* 4 */
#define CAN_ACR_SINGLE_STD_ID 0xFFE00000U /* 31:21 */
#define CAN_ACR_SINGLE_STD_RTR 0x00100000U /* 20 */
#define CAN_ACR_SINGLE_STD_DB1 0x0000FF00U /* 15:8 */
#define CAN_ACR_SINGLE_STD_DB2 0x000000FFU /* 7:0 */
#define CAN_ACR_SINGLE_EXT_ID 0xFFFFFFF8U /* 31:3 */
#define CAN_ACR_SINGLE_EXT_RTR 0x00000004U /* 2 */
/**@}*/
/**
* @defgroup can_bit_masks CAN Miscellaneous Bit Masks
* @{*/
#define CAN_BITS_2_0 (0x07)
#define CAN_BITS_3_0 (0x0F)
#define CAN_BITS_4_0 (0x1F)
#define CAN_BITS_7_3 (0xF8)
#define CAN_BITS_10_3 (0x07F8)
#define CAN_BITS_12_5 (0x00001FE0U)
#define CAN_BITS_20_13 (0x001FE000U)
#define CAN_BITS_28_21 (0x1FE00000U)
#define CAN_BITS_15_8 (0x0000FF00U)
#define CAN_BITS_23_16 (0x00FF0000U)
#define CAN_BITS_31_24 (0xFF000000U)
#define CAN_BITS_23_21 (0x00E00000U)
/**@}*/
/**@}*/
BEGIN_DECLS
/** CAN Application Programming Interface.
* @addtogroup can_api CAN Peripheral API
* @ingroup peripheral_apis
@{*/
void can_enable(uint32_t canport);
void can_disable(uint32_t canport);
void can_init(uint32_t canport, bool listen_only, uint32_t sjw,
uint32_t tseg1, uint32_t tseg2,
bool sam3, uint32_t brp);
void can_filter_clear(uint32_t canport);
void can_filter_dual(uint32_t canport, uint32_t id1, uint32_t id1_mask,
uint32_t id2, uint32_t id2_mask,
uint8_t db, uint8_t db_mask);
void can_filter_single_std(uint32_t canport, uint32_t id, uint32_t id_mask,
uint8_t db1, uint8_t db1_mask,
uint8_t db2, uint8_t db2_mask);
void can_filter_single_std_rtr(uint32_t canport, uint32_t id, uint32_t id_mask,
uint8_t db1, uint8_t db1_mask,
uint8_t db2, uint8_t db2_mask);
void can_filter_single_ext(uint32_t canport, uint32_t id, uint32_t id_mask);
void can_filter_single_ext_rtr(uint32_t canport, uint32_t id, uint32_t id_mask);
void can_enable_irq(uint32_t canport, uint8_t imr);
void can_disable_irq(uint32_t canport, uint8_t imr);
bool can_transmit_std(uint32_t canport, uint32_t id, bool rtr, uint8_t length,
const uint8_t *data);
bool can_transmit_ext(uint32_t canport, uint32_t id, bool rtr, uint8_t length,
const uint8_t *data);
void can_abort_transmit(uint32_t canport);
void can_receive(uint32_t canport, uint32_t *id, bool *ext, bool *rtr, uint8_t *length,
uint8_t *data);
/**@}*/
END_DECLS
#endif /* LIBOPENCM3_PAC55XX_CAN_H_ */

View File

@@ -35,6 +35,7 @@ TGT_CFLAGS += $(DEBUG_FLAGS)
TGT_CFLAGS += $(STANDARD_FLAGS)
ARFLAGS = rcs
OBJS += can.o
OBJS += gpio.o
VPATH += ../cm3

460
lib/pac55xx/can.c Normal file
View File

@@ -0,0 +1,460 @@
/**
* @addtogroup can_api CAN Peripheral API
* @ingroup peripheral_apis
* @brief <b>PAC55xxxx CAN Driver</b>
* @author @htmlonly &copy; @endhtmlonly 2020 Kevin Stefanik <kevin@allocor.tech>
* @date February 13, 2020
*
* This library supports the CAN module in the PAC55xx SoC from Qorvo.
*
* Note: Acceptance Code Mask Register values of 1 indicate the filter is to
* ignore the bit. However, standard CAN driver APIs use a positive logic for the
* mask. The implementations in this file inverts masks as appropriate to
* the mask to make this more portable/intuitive.
*
* LGPL License Terms @ref lgpl_license
*/
/*
* This file is part of the libopencm3 project.
*
* 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/pac55xx/can.h>
#include <libopencm3/cm3/common.h>
/*---------------------------------------------------------------------------*/
/** @brief CAN Enable
Enable the CAN peripheral and its associated FIFOs/counters/interrupts.
@param[in] canport Unsigned int32. CAN block register base address.
*/
void can_enable(uint32_t canport) {
CAN_ISR_SR_CMR_MR_CLEAR(canport, CAN_MR_RM);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Disable
Disable the CAN peripheral and all associated FIFOs/counters/interrupts.
@param[in] canport Unsigned int32. CAN block register base address.
*/
void can_disable(uint32_t canport) {
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_RM);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Init
Initialize the selected CAN peripheral block.
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] listen_only bool. Enable listen only mode.
@param[in] sjw Unsigned int32. Resynchronization time quanta jump width.
@param[in] tseg1 Unsigned int32. Time segment 1 time quanta width.
@param[in] tseg2 Unsigned int32. Time segment 2 time quanta width.
@param[in] sam3 bool. Use best 2 out of 3 samples.
@param[in] brp Unsigned int32. Baud rate prescaler.
*/
void can_init(uint32_t canport, bool listen_only, uint32_t sjw,
uint32_t tseg1, uint32_t tseg2,
bool sam3, uint32_t brp) {
/* Put CAN module in reset and clear out ISR/SR/CMR/MR */
CAN_ISR_SR_CMR_MR(canport) = CAN_MR_RM;
/* Setup single filter scheme */
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
/* enable listen-only mode */
if (listen_only) {
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_LOM);
}
/* Set Baud Rate Prescaler, sync jump width, tseg1/2 */
CAN_BTR1_BTR0_RMC_IMR(canport) = CAN_BTR0_BRP(brp) | CAN_BTR0_SJW(sjw)
| CAN_BTR1_TSEG1(tseg1) | CAN_BTR1_TSEG2(tseg2);
if (sam3) {
/* enable sample bus 3 times */
CAN_BTR1_BTR0_RMC_IMR(canport) |= CAN_BTR1_SAM;
}
/* Filter: Accept incoming messages with any identifier */
CAN_ACR(canport) = 0;
/* Note: when mask bits are 1, the bits are ignored */
CAN_AMR(canport) = 0xFFFFFFFFu;
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Filter Clear
Clear the message filters to receive all messages.
@param[in] canport Unsigned int32. CAN block register base address.
*/
void can_filter_clear(uint32_t canport) {
/* Filter: Accept incoming messages with any identifier */
CAN_ACR(canport) = 0;
/* Note: when mask bits are 1, the bits are ignored */
CAN_AMR(canport) = 0xFFFFFFFFu;
/* Setup single filter scheme */
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Dual Filter Standard Frame
Notes:
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
the bit. However standard CAN driver APIs use a positive logic for the mask.
So this function inverts the mask to make this more portable/intuitive.
- Register definition byte order is opposite what is shown in Rev 1.23 of
the PAC55XX Family User Guide. Since both data and ID values cross byte
boundaries, the bswap32 function is used to correct for the discrepancy.
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] id1 Unsigned int32. CAN ID 1. Only bits 10:0 are used.
@param[in] id1_mask Unsigned int32. CAN ID 1 mask. Only bits 10:0 are used.
@param[in] id2 Unsigned int32. CAN ID 2. Only bits 10:0 are used.
@param[in] id2_mask Unsigned int32. CAN ID 2 mask. Only bits 10:0 are used.
@param[in] db bool. CAN first data byte value.
@param[in] db_mask bool. CAN first data byte mask.
*/
void can_filter_dual(uint32_t canport, uint32_t id1, uint32_t id1_mask,
uint32_t id2, uint32_t id2_mask,
uint8_t db, uint8_t db_mask) {
/* set value */
uint32_t word = ((id1 << 21) & CAN_ACR_DUAL_ID1)
| ((id2 << 5) & CAN_ACR_DUAL_ID2)
| ((db << 12) & CAN_ACR_DUAL_DB_UPPER) | (db & CAN_ACR_DUAL_DB_LOWER);
CAN_ACR(canport) = __builtin_bswap32(word);
/* set mask */
word = ((~id1_mask << 21) & CAN_ACR_DUAL_ID1)
| ((~id2_mask << 5) & CAN_ACR_DUAL_ID2)
| ((~db_mask << 12) & CAN_ACR_DUAL_DB_UPPER)
| ((~db_mask) & CAN_ACR_DUAL_DB_LOWER)
| CAN_ACR_DUAL_RTR1 | CAN_ACR_DUAL_RTR2;
CAN_AMR(canport) = __builtin_bswap32(word);
/* 0: dual filter */
CAN_ISR_SR_CMR_MR_CLEAR(canport, CAN_MR_AFM);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Filter Single Standard Frame
Notes:
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
the bit. However standard CAN driver APIs use a positive logic for the mask.
So this function inverts the mask to make this more portable/intuitive.
- Register definition byte order is opposite what is shown in Rev 1.23 of
the PAC55XX Family User Guide. Since both data and ID values cross byte
boundaries, the bswap32 function is used to correct for the discrepancy.
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] id Unsigned int32. CAN ID. Only bits 10:0 are used.
@param[in] id_mask Unsigned int32. CAN ID mask. Only bits 10:0 are used.
@param[in] db1 bool. CAN first data byte value.
@param[in] db1_mask bool. CAN first data byte mask.
@param[in] db2 bool. CAN second data byte value.
@param[in] db2_mask bool. CAN second data byte mask.
*/
void can_filter_single_std(uint32_t canport, uint32_t id, uint32_t id_mask,
uint8_t db1, uint8_t db1_mask,
uint8_t db2, uint8_t db2_mask) {
/* set value */
uint32_t word = ((id << 21) & CAN_ACR_SINGLE_STD_ID)
| ((db1 << 8) & CAN_ACR_SINGLE_STD_DB1)
| ((db2 << 0) & CAN_ACR_SINGLE_STD_DB2);
CAN_ACR(canport) = __builtin_bswap32(word);
/* set mask */
word = ((~id_mask << 21) & CAN_ACR_SINGLE_STD_ID)
| CAN_ACR_SINGLE_STD_RTR | CAN_ACR_DUAL_DB_UPPER
| ((~db1_mask << 8) & CAN_ACR_SINGLE_STD_DB1)
| ((~db2_mask << 0) & CAN_ACR_SINGLE_STD_DB2);
CAN_AMR(canport) = __builtin_bswap32(word);
/* 1: single filter */
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Filter Single Standard Frame w/RTR set
Notes:
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
the bit. However standard CAN driver APIs use a positive logic for the mask.
So this function inverts the mask to make this more portable/intuitive.
- Register definition byte order is opposite what is shown in Rev 1.23 of
the PAC55XX Family User Guide. Since both data and ID values cross byte
boundaries, the bswap32 function is used to correct for the discrepancy.
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] id Unsigned int32. CAN ID. Only bits 10:0 are used.
@param[in] id_mask Unsigned int32. CAN ID mask. Only bits 10:0 are used.
@param[in] db1 bool. CAN first data byte value.
@param[in] db1_mask bool. CAN first data byte mask.
@param[in] db2 bool. CAN second data byte value.
@param[in] db2_mask bool. CAN second data byte mask.
*/
void can_filter_single_std_rtr(uint32_t canport, uint32_t id, uint32_t id_mask,
uint8_t db1, uint8_t db1_mask,
uint8_t db2, uint8_t db2_mask) {
/* set value */
uint32_t word = ((id << 21) & CAN_ACR_SINGLE_STD_ID)
| CAN_ACR_SINGLE_STD_RTR | ((db1 << 8) & CAN_ACR_SINGLE_STD_DB1)
| ((db2 << 0) & CAN_ACR_SINGLE_STD_DB2);
CAN_ACR(canport) = __builtin_bswap32(word);
/* set mask */
word = ((~id_mask << 21) & CAN_ACR_SINGLE_STD_ID)
| ((~db1_mask << 8) & CAN_ACR_SINGLE_STD_DB1)
| ((~db2_mask << 0) & CAN_ACR_SINGLE_STD_DB2);
CAN_AMR(canport) = __builtin_bswap32(word);
/* 1: single filter */
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Filter Single Extended Frame
Notes:
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
the bit. However standard CAN driver APIs use a positive logic for the mask.
So this function inverts the mask to make this more portable/intuitive.
- Register definition byte order is opposite what is shown in Rev 1.23 of
the PAC55XX Family User Guide. Since both data and ID values cross byte
boundaries, the bswap32 function is used to correct for the discrepancy.
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] id Unsigned int32. CAN ID. Only bits 28:0 are used.
@param[in] id_mask Unsigned int32. CAN ID mask. Only bits 28:0 are used.
*/
void can_filter_single_ext(uint32_t canport, uint32_t id, uint32_t id_mask) {
/* set value */
uint32_t word = ((id << 3) & CAN_ACR_SINGLE_EXT_ID);
CAN_ACR(canport) = __builtin_bswap32(word);
/* set mask */
word = ((~id_mask << 3) & CAN_ACR_SINGLE_EXT_ID) | CAN_ACR_SINGLE_EXT_RTR;
CAN_AMR(canport) = __builtin_bswap32(word);
/* 1: single filter */
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Filter Single Extended Frame w/RTR set
Notes:
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
the bit. However standard CAN driver APIs use a positive logic for the mask.
So this function inverts the mask to make this more portable/intuitive.
- Register definition byte order is opposite what is shown in Rev 1.23 of
the PAC55XX Family User Guide. Since both data and ID values cross byte
boundaries, the bswap32 function is used to correct for the discrepancy.
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] id Unsigned int32. CAN ID. Only bits 28:0 are used.
@param[in] id_mask Unsigned int32. CAN ID mask. Only bits 28:0 are used.
*/
void can_filter_single_ext_rtr(uint32_t canport, uint32_t id, uint32_t id_mask) {
/* set value */
uint32_t word = ((id << 3) & CAN_ACR_SINGLE_EXT_ID) | CAN_ACR_SINGLE_EXT_RTR;
CAN_ACR(canport) = __builtin_bswap32(word);
/* set mask */
word = ((~id_mask << 3) & CAN_ACR_SINGLE_EXT_ID);
CAN_AMR(canport) = __builtin_bswap32(word);
/* 1: single filter */
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Enable IRQ
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] irq Unsigned int8. IRQ bit(s).
*/
void can_enable_irq(uint32_t canport, uint8_t irq) {
/* set to 1 (not masked) to enable */
CAN_BTR1_BTR0_RMC_IMR(canport) |= (uint32_t)irq;
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Disable IRQ
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] irq Unsigned int8. IRQ bit(s).
*/
void can_disable_irq(uint32_t canport, uint8_t irq) {
/* set to 0 (masked) to disable */
CAN_BTR1_BTR0_RMC_IMR(canport) &= ~(uint32_t)irq;
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Transmit Standard Frame
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] id Unsigned int32. Message ID bits 10:0 used.
@param[in] rtr bool. Remote Request bit value.
@param[in] length Unsigned int8. Message payload length.
@param[in] data Unsigned int8[]. Message payload data.
@returns true if able to transmit, false otherwise.
*/
bool can_transmit_std(uint32_t canport, uint32_t id, bool rtr, uint8_t length,
const uint8_t *data) {
/* if TBS is 0, then not ready to transmit */
if ((CAN_ISR_SR_CMR_MR(canport) & CAN_SR_TBS) == 0) {
return false;
}
uint32_t word = (length & CAN_BITS_3_0)
| (rtr ? BIT6 : 0) /* DLC/RTR/FF ==> 7:0 */
| ((id & CAN_BITS_10_3) << 5) /* ID 10:3 ==> 15:8 */
| ((id & CAN_BITS_2_0) << 21) /* ID 2:0 ==> 23:21 */
| (((length > 0) ? data[0] : 0) << 24);
CAN_TXBUF(canport) = word;
if (length > 1) {
word = (data[1] << 0) | (data[2] << 8)
| (data[3] << 16) | (data[4] << 24);
CAN_TXBUF(canport) = word;
}
if (length > 5) {
word = (data[5] << 0) | (data[6] << 8) | (data[7] << 16);
CAN_TXBUF(canport) = word;
}
/* Request transmit */
CAN_ISR_SR_CMR_MR_SET(canport, CAN_CMR_TR);
return true;
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Transmit Extended Frame
@param[in] canport Unsigned int32. CAN block register base address.
@param[in] id Unsigned int32. Message ID bits 28:0 used.
@param[in] rtr bool. Remote Request bit value.
@param[in] length Unsigned int8. Message payload length, 0-8.
@param[in] data Unsigned int8[]. Message payload data.
@returns true if able to transmit, false otherwise.
*/
bool can_transmit_ext(uint32_t canport, uint32_t id, bool rtr, uint8_t length,
const uint8_t *data) {
/* if TBS is 0, then not ready to transmit */
if ((CAN_ISR_SR_CMR_MR(canport) & CAN_SR_TBS) == 0) {
return false;
}
uint32_t word = (length & CAN_BITS_3_0)
| (rtr ? BIT6 : 0) | BIT7 /* DLC/RTR/FF ==> 7:0 */
| ((id & CAN_BITS_28_21) >> 13) /* ID 28:21 ==> 15:8 */
| ((id & CAN_BITS_20_13) << 3) /* ID 20:13 ==> 23:16 */
| ((id & CAN_BITS_12_5) << 19); /* ID 12:5 ==> 31:24 */
CAN_TXBUF(canport) = word; /* write first 32-bit word to FIFO */
word = ((id & CAN_BITS_4_0) << 3); /* ID 4:0 ==> 7:3 */
if (length > 0) {
word |= (data[0] << 8) | (data[1] << 16) | (data[2] << 24);
}
/* for extended frame, always write second 32-bit word to FIFO */
CAN_TXBUF(canport) = word;
if (length > 3) {
word = (data[3] << 0) | (data[4] << 8)
| (data[5] << 16) | (data[6] << 24);
CAN_TXBUF(canport) = word;
}
if (length > 7) {
word = data[7];
CAN_TXBUF(canport) = word;
}
/* Request transmit */
CAN_ISR_SR_CMR_MR_SET(canport, CAN_CMR_TR);
return true;
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Abort Transmit
Aborts the current transmission.
@param[in] canport Unsigned int32. CAN block register base address.
*/
void can_abort_transmit(uint32_t canport) {
CAN_ISR_SR_CMR_MR_SET(canport, CAN_CMR_AT);
}
/*---------------------------------------------------------------------------*/
/** @brief CAN Receive Message
If no data is in the RX buffer, id and length are set to 0.
@param[in] canport Unsigned int32. CAN block register base address.
@param[out] id Unsigned int32 pointer. Message ID.
@param[out] ext bool pointer. The message ID is extended.
@param[out] rtr bool pointer. Remote Request bit value.
@param[out] length Unsigned int8 pointer. Length of message payload.
@param[out] data Unsigned int8[]. Message payload data, min length 8.
*/
void can_receive(uint32_t canport, uint32_t *id, bool *ext, bool *rtr, uint8_t *length,
uint8_t *data) {
if ((CAN_ISR_SR_CMR_MR(canport) & CAN_ISR_RI) == 0 || CAN_RMC(canport) == 0) {
*id = 0;
*length = 0;
return; /* empty RX FIFO */
}
uint32_t can_buffer = CAN_RXBUF(canport); /* read 32-bit word */
uint8_t rx_length = can_buffer & CAN_BITS_3_0;
bool is_extended = can_buffer & BIT7;
if (ext) {
*ext = is_extended;
}
if (rtr) {
*rtr = can_buffer & BIT6;
}
if (length) {
*length = rx_length;
}
uint32_t _id;
if (is_extended) {
/* Parse extended message ID from RXBUF */
_id = ((can_buffer & CAN_BITS_15_8) << 13) /* ID 28:21 <== 15:8 */
| ((can_buffer & CAN_BITS_23_16) >> 3) /* ID 20:13 <== 23:16 */
| ((can_buffer & CAN_BITS_31_24) >> 19); /* ID 12:5 <== 31:24 */
can_buffer = CAN_RXBUF(canport);
_id |= ((can_buffer & CAN_BITS_7_3) >> 3); /* ID 4:0 <== 7:3 */
/* Parse extended message data from RXBUF */
data[0] = can_buffer >> 8;
data[1] = can_buffer >> 16;
data[2] = can_buffer >> 24;
if (rx_length > 3) {
can_buffer = CAN_RXBUF(canport);
data[3] = can_buffer;
data[4] = can_buffer >> 8;
data[5] = can_buffer >> 16;
data[6] = can_buffer >> 24;
}
if (rx_length > 7) {
can_buffer = CAN_RXBUF(canport);
data[7] = can_buffer;
}
} else {
/* Parse standard message ID from RXBUF */
_id = ((can_buffer & CAN_BITS_15_8) >> 5) /* ID 10:3 <== 15:8 */
| ((can_buffer & CAN_BITS_23_21) >> 21); /* ID 2:0 <== 23:21 */
/* Parse standard message data from RXBUF */
data[0] = can_buffer >> 24;
if (rx_length > 1) {
can_buffer = CAN_RXBUF(canport);
data[1] = can_buffer;
data[2] = can_buffer >> 8;
data[3] = can_buffer >> 16;
data[4] = can_buffer >> 24;
if (rx_length > 5) {
/* buffer contains data5,data6,data7 */
can_buffer = CAN_RXBUF(canport);
data[5] = can_buffer;
data[6] = can_buffer >> 8;
data[7] = can_buffer >> 16;
}
}
}
if (id) {
*id = _id;
}
/*
* Write 1 to acknowledge/clear the interrupt
* Note: ensure not to let the other interrupt masks be written as 1, so as
* to avoid acknowledging them.
* Note: CAN_ISR_RI is already high, but we still write '1' to it to clear it.
*/
CAN_ISR_ACKNOWLEDGE(canport, CAN_ISR_RI);
return;
}