diff --git a/include/libopencm3/stm32/fdcan.h b/include/libopencm3/stm32/fdcan.h new file mode 100644 index 00000000..61ac87d0 --- /dev/null +++ b/include/libopencm3/stm32/fdcan.h @@ -0,0 +1,892 @@ +/** @defgroup fdcan_defines FDCAN Defines + +@ingroup STM32G_defines + +@brief libopencm3 Defined Constants and Types for STM32 FD-CAN + +@author @htmlonly © @endhtmlonly 2021 Eduard Drusa + +LGPL License Terms @ref lgpl_license +*/ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2021 Eduard Drusa + * + * 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 . + */ + + + +#ifndef LIBOPENCM3_FDCAN_H +#define LIBOPENCM3_FDCAN_H + +#include +#include + +/** @{ */ + +/* FDCAN block base addresses. Used in functions to identify FDCAN block being manipulated. */ + +/** @defgroup fdcan_block FDCAN block base addresses + * @{ + */ +#define CAN1 FDCAN1_BASE +#define CAN2 FDCAN2_BASE +#define CAN3 FDCAN3_BASE +/**@}*/ + + +/** @defgroup fdcan_fifo Named constants for FIFOs + * @{ + */ +#define FDCAN_FIFO0 0 +#define FDCAN_FIFO1 1 +/**@}*/ + + +/** @defgroup FDCAN registers file in each FDCAN block. */ + +#define FDCAN_CREL(can_base) MMIO32(can_base + 0x0000) +#define FDCAN_ENDN(can_base) MMIO32(can_base + 0x0004) +#define FDCAN_DBTP(can_base) MMIO32(can_base + 0x000C) +#define FDCAN_TEST(can_base) MMIO32(can_base + 0x0010) +#define FDCAN_RWD(can_base) MMIO32(can_base + 0x0014) +#define FDCAN_CCCR(can_base) MMIO32(can_base + 0x0018) +#define FDCAN_NBTP(can_base) MMIO32(can_base + 0x001C) +#define FDCAN_TSCC(can_base) MMIO32(can_base + 0x0020) +#define FDCAN_TSCV(can_base) MMIO32(can_base + 0x0024) +#define FDCAN_TOCC(can_base) MMIO32(can_base + 0x0028) +#define FDCAN_TOCV(can_base) MMIO32(can_base + 0x002C) +#define FDCAN_ECR(can_base) MMIO32(can_base + 0x0040) +#define FDCAN_PSR(can_base) MMIO32(can_base + 0x0044) +#define FDCAN_TDCR(can_base) MMIO32(can_base + 0x0048) +#define FDCAN_IR(can_base) MMIO32(can_base + 0x0050) +#define FDCAN_IE(can_base) MMIO32(can_base + 0x0054) +#define FDCAN_ILS(can_base) MMIO32(can_base + 0x0058) +#define FDCAN_ILE(can_base) MMIO32(can_base + 0x005C) +#define FDCAN_RXGFC(can_base) MMIO32(can_base + 0x0080) +#define FDCAN_XIDAM(can_base) MMIO32(can_base + 0x0084) +#define FDCAN_HPMS(can_base) MMIO32(can_base + 0x0088) + +/** Generic access to Rx FIFO status registers. + * @param can_base FDCAN block base address @ref fdcan_block + * @param fifo_id ID of FIFO, 0 or 1 + */ +#define FDCAN_RXFIS(can_base, fifo_id) MMIO32(can_base + 0x0090 + (8 * fifo_id)) +#define FDCAN_RXF0S(can_base) FDCAN_RXFIS(can_base, 0) +#define FDCAN_RXF1S(can_base) FDCAN_RXFIS(can_base, 1) + +/** Generic access to Rx FIFO acknowledge registers. + * @param can_base FDCAN block base address @ref fdcan_block + * @param fifo_id ID of FIFO, 0 or 1 + */ +#define FDCAN_RXFIA(can_base, fifo_id) MMIO32(can_base + 0x0094 + (8 * fifo_id)) +#define FDCAN_RXF0A(can_base) FDCAN_RXFIA(can_base, 0) +#define FDCAN_RXF1A(can_base) FDCAN_RXFIA(can_base, 1) + +#define FDCAN_TXBC(can_base) MMIO32(can_base + 0x00C0) +#define FDCAN_TXFQS(can_base) MMIO32(can_base + 0x00C4) +#define FDCAN_TXBRP(can_base) MMIO32(can_base + 0x00C8) +#define FDCAN_TXBAR(can_base) MMIO32(can_base + 0x00CC) +#define FDCAN_TXBCR(can_base) MMIO32(can_base + 0x00D0) +#define FDCAN_TXBTO(can_base) MMIO32(can_base + 0x00D4) +#define FDCAN_TXBCF(can_base) MMIO32(can_base + 0x00D8) +#define FDCAN_TXBTIE(can_base) MMIO32(can_base + 0x00DC) +#define FDCAN_TXBCIE(can_base) MMIO32(can_base + 0x00E0) +#define FDCAN_TXEFS(can_base) MMIO32(can_base + 0x00E4) +#define FDCAN_TXEFA(can_base) MMIO32(can_base + 0x00E8) +#define FDCAN_CKDIV(can_base) MMIO32(can_base + 0x0100) + +/* DAY[7:0]: FDCAN core revision date */ +#define FDCAN_CREL_DAY_SHIFT 0 +#define FDCAN_CREL_DAY_MASK 0xFF + +/* MON[7:0]: FDCAN core revision month */ +#define FDCAN_CREL_MON_SHIFT 8 +#define FDCAN_CREL_MON_MASK 0xFF + +/* YEAR[3:0]: FDCAN core revision year */ +#define FDCAN_CREL_YEAR_SHIFT 16 +#define FDCAN_CREL_YEAR_MASK 0xF + +/* SUBSTEP[3:0]: FDCAN core release sub stepping */ +#define FDCAN_CREL_SUBSTEP_SHIFT 20 +#define FDCAN_CREL_SUBSTEP_MASK 0xF + +/* STEP[3:0]: FDCAN core release stepping */ +#define FDCAN_CREL_STEP_SHIFT 24 +#define FDCAN_CREL_STEP_MASK 0xF + +/* REL[3:0]: FDCAN core release number */ +#define FDCAN_CREL_REL_SHIFT 28 +#define FDCAN_CREL_REL_MASK 0xF + + +/* DSJW[3:0]: Synchronization jump width */ +#define FDCAN_DBTP_DSJW_SHIFT 0 +#define FDCAN_DBTP_DSJW_MASK 0xF + +/* DTSEG2[3:0]: Data time segment after sample point */ +#define FDCAN_DBTP_DTSEG2_SHIFT 4 +#define FDCAN_DBTP_DTSEG2_MASK 0xF + +/* DTSEG1[4:0]: Data time segment before sample point */ +#define FDCAN_DBTP_DTSEG1_SHIFT 8 +#define FDCAN_DBTP_DTSEG1_MASK 0x1F + +/* DBRP[4:0]: Data bit rate prescaler */ +#define FDCAN_DBTP_DBRP_SHIFT 16 +#define FDCAN_DBTP_DBRP_MASK 0x1F + +#define FDCAN_DBTP_TDC (1 << 23) + +#define FDCAN_TEST_LBCK (1 << 4) +/* TX[1:0]: Control of transmit pin */ +#define FDCAN_TEST_TX_SHIFT 5 +#define FDCAN_TEST_TX_MASK 0x3 + +#define FDCAN_TEST_RX (1 << 7) + +/* WDC[7:0]: RAM watchdog configuration */ +#define FDCAN_RWD_WDC_SHIFT 0 +#define FDCAN_RWD_WDC_MASK 0xFF + +/* WDV[7:0]: RAM watchdog actual value */ +#define FDCAN_RWD_WDV_SHIFT 7 +#define FDCAN_RWD_WDV_MASK 0xFF + +/** @defgroup fdcan_cccr FDCAN CC control register bits + * @{ + */ +#define FDCAN_CCCR_INIT (1 << 0) +#define FDCAN_CCCR_CCE (1 << 1) +#define FDCAN_CCCR_ASM (1 << 2) +#define FDCAN_CCCR_CSA (1 << 3) +#define FDCAN_CCCR_CSR (1 << 4) +#define FDCAN_CCCR_MON (1 << 5) +#define FDCAN_CCCR_DAR (1 << 6) +#define FDCAN_CCCR_TEST (1 << 7) +#define FDCAN_CCCR_FDOE (1 << 8) +#define FDCAN_CCCR_BRSE (1 << 9) +#define FDCAN_CCCR_PXHD (1 << 12) +#define FDCAN_CCCR_EFBI (1 << 13) +#define FDCAN_CCCR_TXP (1 << 14) +#define FDCAN_CCCR_NISO (1 << 15) +/**@}*/ + +/** Timeout for FDCAN_CCCR register INIT bit to accept set value. + * + * This timeout is required because FDCAN uses two different clocks + * feeding two different portions of block. There can be slight delay + * based on how clocks are set up. While amount of FDCAN_clk / + * FDCAN_pclk combinations is high and clock speeds may vary a lot, + * following value has been choosen as sane default. You are free to + * use any timeout value you want. + */ +#define FDCAN_CCCR_INIT_TIMEOUT 0x0000FFFF + +/* NTSEG2[6:0]: Nominal timing segment after sample point length */ +#define FDCAN_NBTP_NTSEG2_SHIFT 0 +#define FDCAN_NBTP_NTSEG2_MASK 0x7F + +/* NTSEG1[7:0]: Nominal timing segment before sample point length */ +#define FDCAN_NBTP_NTSEG1_SHIFT 8 +#define FDCAN_NBTP_NTSEG1_MASK 0xFF + +/* NBRP[8:0]: Norminal timing bit rate prescaler */ +#define FDCAN_NBTP_NBRP_SHIFT 16 +#define FDCAN_NBTP_NBRP_MASK 0x1FF + +/* NSJW[6:0]: Norminal timing resynchronization jumb width*/ +#define FDCAN_NBTP_NSJW_SHIFT 25 +#define FDCAN_NBTP_NSJW_MASK 0x7F + +/* TSS[1:0]: Timestamp select */ +#define FDCAN_TSCC_TSS_SHIFT 0 +#define FDCAN_TSCC_TSS_MASK 0x3 + +/* TCP[3:0]: Timestamp counter prescaler */ +#define FDCAN_TSCC_TCP_SHIFT 16 +#define FDCAN_TSCC_TCP_MASK 0xF + + +/* TSC[15:0]: Timestamp counter value */ +#define FDCAN_TSCV_TSC_SHIFT 0 +#define FDCAN_TSCV_TSC_MASK 0xFFFF + +#define FDCAN_TOCC_ETOC (1 << 0) +/* TOS[1:0]: Timeout select */ +#define FDCAN_TOCC_TOS_SHIFT 1 +#define FDCAN_TOCC_TOS_MASK 0x3 + +/* TOP[15:0]: Timeout period */ +#define FDCAN_TOCC_TOP_SHIFT 16 +#define FDCAN_TOCC_TOP_MASK 0xFFFF + +/* TOC[15:0]: Timeout counter */ +#define FDCAN_TOCV_TOC_SHIFT 0 +#define FDCAN_TOCV_TOC_MASK 0xFFFF + +/* TEC[7:0]: Transmit error counter */ +#define FDCAN_ECR_TEC_SHIFT 0 +#define FDCAN_ECR_TEC_MASK 0xFF + +/* REC[6:0]: Receive error counter */ +#define FDCAN_ECR_REC_SHIFT 8 +#define FDCAN_ECR_REC_MASK 0x7F + +#define FDCAN_ECR_RP (1 << 15) +/* CEL[7:0]: CAN error logging */ +#define FDCAN_ECR_CEL_SHIFT 16 +#define FDCAN_ECR_CEL_MASK 0xFF + + +/* LEC[2:0]: Last error code */ +#define FDCAN_PSR_LEC_SHIFT 0 +#define FDCAN_PSR_LEC_MASK 0x7 + +/* ACT[1:0]: CAN block activity */ +#define FDCAN_PSR_ACT_SHIFT 3 +#define FDCAN_PSR_ACT_MASK 0x3 + +#define FDCAN_PSR_EP (1 << 5) +#define FDCAN_PSR_EW (1 << 6) +#define FDCAN_PSR_BO (1 << 7) +/* DLEC[2:0]: Last error code in data section */ +#define FDCAN_PSR_DLEC_SHIFT 8 +#define FDCAN_PSR_DLEC_MASK 0x7 + +#define FDCAN_PSR_RESI (1 << 11) + +/* the what? */ +#define FDCAN_PSR_RBRSRESI1 (1 << 12) +#define FDCAN_PSR_REDL (1 << 13) +#define FDCAN_PSR_PXE (1 << 14) + +/* TDCV[6:0]: Transmitter delay compensation value */ +#define FDCAN_PSR_TDCV_SHIFT 16 +#define FDCAN_PSR_TDCV_MASK 0x7F + +/* TDCF[6:0]: Transmitter delay compensation filter window length */ +#define FDCAN_TDCR_TDCF_SHIFT 0 +#define FDCAN_TDCR_TDCF_MASK 0x7F + +/* TDCO[6:0]: Transmitter delay compensation offset */ +#define FDCAN_TDCR_TDCO_SHIFT 8 +#define FDCAN_TDCR_TDCO_MASK 0x7F + +/** @defgroup fdcan_ir FDCAN interrupt register flags + * @{ + */ +#define FDCAN_IR_RF0N (1 << 0) +#define FDCAN_IR_RF0F (1 << 1) +#define FDCAN_IR_RF0L (1 << 2) +#define FDCAN_IR_RF1N (1 << 3) +#define FDCAN_IR_RF1F (1 << 4) +#define FDCAN_IR_RF1L (1 << 5) +#define FDCAN_IR_HPM (1 << 6) +#define FDCAN_IR_TC (1 << 7) +#define FDCAN_IR_TCF (1 << 8) +#define FDCAN_IR_TFE (1 << 9) +#define FDCAN_IR_TEFN (1 << 10) +#define FDCAN_IR_TEFF (1 << 11) +#define FDCAN_IR_TEFL (1 << 12) +#define FDCAN_IR_TSW (1 << 13) +#define FDCAN_IR_MRAF (1 << 14) +#define FDCAN_IR_TOO (1 << 15) +#define FDCAN_IR_ELO (1 << 16) +#define FDCAN_IR_EP (1 << 17) +#define FDCAN_IR_EW (1 << 18) +#define FDCAN_IR_BO (1 << 19) +#define FDCAN_IR_WDI (1 << 20) +#define FDCAN_IR_PEA (1 << 21) +#define FDCAN_IR_PED (1 << 22) +#define FDCAN_IR_ARA (1 << 23) +/**@}*/ + +/** @defgroup fdcan_ie FDCAN interrupt enable flags + * @{ + */ +#define FDCAN_IE_RF0NE (1 << 0) +#define FDCAN_IE_RF0FE (1 << 1) +#define FDCAN_IE_RF0LE (1 << 2) +#define FDCAN_IE_RF1NE (1 << 3) +#define FDCAN_IE_RF1FE (1 << 4) +#define FDCAN_IE_RF1LE (1 << 5) +#define FDCAN_IE_HPME (1 << 6) +#define FDCAN_IE_TCE (1 << 7) +#define FDCAN_IE_TCFE (1 << 8) +#define FDCAN_IE_TFEE (1 << 9) +#define FDCAN_IE_TEFNE (1 << 10) +#define FDCAN_IE_TEFFE (1 << 11) +#define FDCAN_IE_TEFLE (1 << 12) +#define FDCAN_IE_TSWE (1 << 13) +#define FDCAN_IE_MRAFE (1 << 14) +#define FDCAN_IE_TOOE (1 << 15) +#define FDCAN_IE_ELOE (1 << 16) +#define FDCAN_IE_EPE (1 << 17) +#define FDCAN_IE_EWE (1 << 18) +#define FDCAN_IE_BOE (1 << 19) +#define FDCAN_IE_WDIE (1 << 20) +#define FDCAN_IE_PEAE (1 << 21) +#define FDCAN_IE_PEDE (1 << 22) +#define FDCAN_IE_ARAE (1 << 23) +/**@}*/ + +/** @defgroup fdcan_ils FDCAN_ILS interrupt line select flags + * @{ + */ +#define FDCAN_ILS_RxFIFO0 (1 << 0) +#define FDCAN_ILS_RxFIFO1 (1 << 1) +#define FDCAN_ILS_SMSG (1 << 2) +#define FDCAN_ILS_TFERR (1 << 3) +#define FDCAN_ILS_MISC (1 << 4) +#define FDCAN_ILS_BERR (1 << 5) +#define FDCAN_ILS_PERR (1 << 6) +/**@}*/ + +#define FDCAN_ILE_INT0 (1 << 0) +#define FDCAN_ILE_INT1 (1 << 1) + +#define FDCAN_RXGFC_RRFE (1 << 0) +#define FDCAN_RXGFC_RRFS (1 << 1) +/* ANFE[1:0]: Accept non-matching frames w/ extended ID */ +#define FDCAN_RXGFC_ANFE_SHIFT 2 +#define FDCAN_RXGFC_ANFE_MASK 0x3 + +/* ANFS[1:0]: Accept non-matching frames w/ standard ID */ +#define FDCAN_RXGFC_ANFS_SHIFT 4 +#define FDCAN_RXGFC_ANFS_MASK 0x3 + +#define FDCAN_RXGFC_F1OM (1 << 8) +#define FDCAN_RXGFC_F0OM (1 << 9) +/* LSS[4:0]: List size of standard ID filters */ +#define FDCAN_RXGFC_LSS_SHIFT 16 +#define FDCAN_RXGFC_LSS_MASK 0x1F + +/* LSE[3:0]: List size of extended ID filters */ +#define FDCAN_RXGFC_LSE_SHIFT 24 +#define FDCAN_RXGFC_LSE_MASK 0xF + + +/* EIDM[28:0]: Extended ID mask for filtering */ +#define FDCAN_XIDAM_EIDM_SHIFT 0 +#define FDCAN_XIDAM_EIDM_MASK 0x1FFFFFFF + + +/* BIDX[2:0]: Buffer index */ +#define FDCAN_HPMS_BIDX_SHIFT 0 +#define FDCAN_HPMS_BIDX_MASK 0x7 + +/* MSI[1:0]: Message storage indicator */ +#define FDCAN_HPMS_MSI_SHIFT 6 +#define FDCAN_HPMS_MSI_MASK 0x3 + +/* FIDX[4:0]: Filter index */ +#define FDCAN_HPMS_FIDX_SHIFT 8 +#define FDCAN_HPMS_FIDX_MASK 0x1F + +#define FDCAN_HPMS_FLS (1 << 15) + +/* Fill level of Rx FIFOs */ +#define FDCAN_RXFIFO_FL_SHIFT 0 +#define FDCAN_RXFIFO_FL_MASK 0xF + +/* Get index of Rx FIFOs */ +#define FDCAN_RXFIFO_GI_SHIFT 8 +#define FDCAN_RXFIFO_GI_MASK 0x3 + +/* Put index of Rx FIFOs */ +#define FDCAN_RXFIFO_PI_SHIFT 16 +#define FDCAN_RXFIFO_PI_MASK 0x3 + +#define FDCAN_RXFIFO_FF (1 << 24) +#define FDCAN_RXFIFO_RFL (1 << 25) + +/* F0FL[3:0]: Fill level of Rx FIFO 0 */ +#define FDCAN_RXF0S_F0FL_SHIFT FDCAN_RXFIFO_FL_SHIFT +#define FDCAN_RXF0S_F0FL_MASK FDCAN_RXFIFO_FL_MASK + +/* F0GI[1:0]: Get index of Rx FIFO 0 */ +#define FDCAN_RXF0S_F0GI_SHIFT FDCAN_RXFIFO_GI_SHIFT +#define FDCAN_RXF0S_F0GI_MASK FDCAN_RXFIFO_GI_MASK + +/* F0PI[1:0]: Put index of Rx FIFO 0 */ +#define FDCAN_RXF0S_F0PI_SHIFT FDCAN_RXFIFO_PI_SHIFT +#define FDCAN_RXF0S_F0PI_MASK FDCAN_RXFIFO_PI_MASK + +#define FDCAN_RXF0S_F0F FDCAN_RXFIFO_FF +#define FDCAN_RXF0S_RF0L FDCAN_RXFIFO_RFL + +/* Rx FIFOs acknowledge index */ +#define FDCAN_RXFIFO_AI_SHIFT 0 +#define FDCAN_RXFIFO_AI_MASK 0x7 + +/* R0AI[2:0]: Rx FIFO 0 acknowledge index */ +#define FDCAN_RXF0A_R0AI_SHIFT FDCAN_RXFIFO_AI_SHIFT +#define FDCAN_RXF0A_R0AI_MASK FDCAN_RXFIFO_AI_MASK + +/* F1FL[3:1]: Fill level of Rx FIFO 1 */ +#define FDCAN_RXF1S_F1FL_SHIFT FDCAN_RXFIFO_FL_SHIFT +#define FDCAN_RXF1S_F1FL_MASK FDCAN_RXFIFO_FL_MASK + +/* F1GI[1:1]: Get index of Rx FIFO 1 */ +#define FDCAN_RXF1S_F1GI_SHIFT FDCAN_RXFIFO_GI_SHIFT +#define FDCAN_RXF1S_F1GI_MASK FDCAN_RXFIFO_GI_MASK + +/* F1PI[1:1]: Put index of Rx FIFO 1 */ +#define FDCAN_RXF1S_F1PI_SHIFT FDCAN_RXFIFO_PI_SHIFT +#define FDCAN_RXF1S_F1PI_MASK FDCAN_RXFIFO_PI_MASK + +#define FDCAN_RXF1S_F1F FDCAN_RXFIFO_FF +#define FDCAN_RXF1S_RF1L FDCAN_RXFIFO_RFL + +/* R1AI[2:0]: Rx FIFO 1 acknowledge index */ +#define FDCAN_RXF1A_R1AI_SHIFT FDCAN_RXFIFO_AI_SHIFT +#define FDCAN_RXF1A_R1AI_MASK FDCAN_RXFIFO_AI_MASK + +#define FDCAN_TXBC_TFQM (1 << 24) + +/* TFFL[2:0]: Tx FIFO free level */ +#define FDCAN_TXFQS_TFFL_SHIFT 0 +#define FDCAN_TXFQS_TFFL_MASK 0x7 + +/* TFGI[1:0]: Tx FIFO get index */ +#define FDCAN_TXFQS_TFGI_SHIFT 0 +#define FDCAN_TXFQS_TFGI_MASK 0x3 + +/* TFQPI[1:0]: Tx FIFO put index */ +#define FDCAN_TXFQS_TFQPI_SHIFT 0 +#define FDCAN_TXFQS_TFQPI_MASK 0x3 + +#define FDCAN_TXFQS_TFQF (1 << 0) + +/** @defgroup fdcan_txbrp FDCAN_TXBRP Transmit request pending bits + * @{ + */ +#define FDCAN_TXBRP_TRP0 (1 << 0) +#define FDCAN_TXBRP_TRP1 (1 << 1) +#define FDCAN_TXBRP_TRP2 (1 << 2) +/**@}*/ + +/** @defgroup fdcan_txbar FDCAN_TXBAR Transmit buffer add request bits + * @{ + */ +#define FDCAN_TXBAR_AR0 (1 << 0) +#define FDCAN_TXBAR_AR1 (1 << 1) +#define FDCAN_TXBAR_AR2 (1 << 2) +/**@}*/ + +/** @defgroup fdcan_txbcr FDCAN_TXBCR Transmit buffer cancel request bits + * @{ + */ +#define FDCAN_TXBCR_CR0 (1 << 0) +#define FDCAN_TXBCR_CR1 (1 << 1) +#define FDCAN_TXBCR_CR2 (1 << 2) +/**@}*/ + +/** @defgroup fdcan_txbto FDCAN_TXBTO Transmit buffer transfer occured bits + * @{ + */ +#define FDCAN_TXBTO_TO0 (1 << 0) +#define FDCAN_TXBTO_TO1 (1 << 1) +#define FDCAN_TXBTO_TO2 (1 << 2) +/**@}*/ + +/** @defgroup fdcan_txbcf FDCAN_TXBCF Transmit buffer cancellation finished bits + * @{ + */ +#define FDCAN_TXBCF_CF0 (1 << 0) +#define FDCAN_TXBCF_CF1 (1 << 1) +#define FDCAN_TXBCF_CF2 (1 << 2) +/**@}*/ + +/** @defgroup fdcan_txbtie FDCAN_TXBTIE Transmit interrupt enable bits + * + * Each bit enables or disables transmit interrupt for transmit buffer + * slot. + * @{ + */ +#define FDCAN_TXBTIE_TIE0 (1 << 0) +#define FDCAN_TXBTIE_TIE1 (1 << 1) +#define FDCAN_TXBTIE_TIE2 (1 << 2) +/**@}*/ + +/** @defgroup fdcan_txbcie FDCAN_TXBCIE Transmit cancelled interrupt enable bits + * + * Each bit enables or disables transmit cancelled interrupt for transmit buffer + * slot. + * @{ + */ +#define FDCAN_TXBCIE_CFIE0 (1 << 0) +#define FDCAN_TXBCIE_CFIE1 (1 << 1) +#define FDCAN_TXBCIE_CFIE2 (1 << 2) +/**@}*/ + +/* EFFL[2:0]: Event FIFO fill level*/ +#define FDCAN_TXEFS_EFFL_SHIFT 0 +#define FDCAN_TXEFS_EFFL_MASK 0x7 + +/* EFG[1:0]: Event FIFO get index */ +#define FDCAN_TXEFS_EFGI_SHIFT 8 +#define FDCAN_TXEFS_EFGI_MASK 0x3 + +/* EFPI[1:0]: Event FIFO put index */ +#define FDCAN_TXEFS_EFPI_SHIFT 16 +#define FDCAN_TXEFS_EFPI_MASK 0x3 + +#define FDCAN_TXEFS_EFF (1 << 24) +#define FDCAN_TXEFS_TEF (1 << 25) + +/* EFAI[1:0]: Event FIFO acknowledge index */ +#define FDCAN_TXEFA_EFAI_SHIFT 0 +#define FDCAN_TXEFA_EFAI_MASK 0x3 + + +/* PDIV[3:0]: Input clock divider */ +#define FDCAN_CKDIV_PDIV_SHIFT 0 +#define FDCAN_CKDIV_PDIV_MASK 0xF + +/* --- FD-CAN memory block defines------------------------------------------ */ + +/** Structure describing standard ID filter. + * Standard ID filter is composed of one 32bit value. + * This region of memory cannot be accessed in quantities less than 32bits. + */ +struct fdcan_standard_filter { + /** Aggregate of filter type, filter action and two IDs */ + uint32_t type_id1_conf_id2; +}; + +#define FDCAN_SFT_SHIFT 30 +#define FDCAN_SFT_MASK 0x3 + +/** @defgroup fdcan_sft Standard ID filter match type + * + * Matching strategy for standard ID filters. + * @{ + */ +/** Filter matches all messages in range from id1 to id2. */ +#define FDCAN_SFT_RANGE 0x0 + +/** Filter matches messages with id1 or id2 */ +#define FDCAN_SFT_DUAL 0x1 + +/** Filter matches messages which match id1 after being unmasked + * using id2. */ +#define FDCAN_SFT_ID_MASK 0x2 + +/** Disable this filter. */ +#define FDCAN_SFT_DISABLE 0x3 +/**@}*/ + +#define FDCAN_SFEC_SHIFT 27 +#define FDCAN_SFEC_MASK 0x7 + +/** @defgroup fdcan_sfec Standard ID filter action + * + * Defines possible actions for standard ID filters. All actions except + * of @ref FDCAN_SFEC_PRIO cause filter matching to terminate immediately + * with desired outcome. FDCAN_SFEC_PRIO sets priority flag for message + * and continues processing remaining filters. + * @{ + */ + +/** Filter is disabled. No matchin occurrs. */ +#define FDCAN_SFEC_DISABLE 0x0 + +/** Put message into FIFO0 */ +#define FDCAN_SFEC_FIFO0 0x1 + +/** Put message into FIFO1 */ +#define FDCAN_SFEC_FIFO1 0x2 + +/** Reject message */ +#define FDCAN_SFEC_REJECT 0x3 + +/** Treat message as priority message (and continue processing further rules) */ +#define FDCAN_SFEC_PRIO 0x4 + +/** Treat message as priority and put it into FIFO0 */ +#define FDCAN_SFEC_PRIO_FIFO0 0x5 + +/** Treat message as priority and put it into FIFO1 */ +#define FDCAN_SFEC_PRIO_FIFO1 0x6 +/**@}*/ + +/** Amount of standard filters allocated in Message RAM + * This number may vary between devices. 28 is value valid + * for STM32G4 + **/ +#define FDCAN_SFT_MAX_NR 28 + +/* SFEC = 0x7 is unused */ + +#define FDCAN_SFID1_SHIFT 16 +#define FDCAN_SFID1_MASK 0x7FF + +#define FDCAN_SFID2_SHIFT 0 +#define FDCAN_SFID2_MASK 0x7FF + +/** Structure describing extended ID filters. + * Extended ID filter is composed of two 32bit values. + * This region of memory cannot be accessed in quantities less than 32bits. + */ +struct fdcan_extended_filter { + /** Aggregate of filter action and extended ID */ + uint32_t conf_id1; + /** Aggregate of filter type and extended ID or mask */ + uint32_t type_id2; +}; + +#define FDCAN_EFEC_SHIFT 29 +#define FDCAN_EFEC_MASK 0x7 + +/** @defgroup fdcan_efec Extended ID filter action + * + * These are possible actions, extended filter can have. If filter is + * disabled, then no matching is performed. All other actions except of + * @ref FDCAN_EFEC_PRIO cause matching to terminate with required outcome. + * FDCAN_EFEC_PRIO marks message as priority and continues matching. + * @{ + */ + +/** Disable this filter. */ +#define FDCAN_EFEC_DISABLE 0x0 + +/** Put message into FIFO0 */ +#define FDCAN_EFEC_FIFO0 0x1 + +/** Put message into FIFO1 */ +#define FDCAN_EFEC_FIFO1 0x2 + +/** Reject message */ +#define FDCAN_EFEC_REJECT 0x3 + +/** Treat message as priority message (and continue processing further rules) */ +#define FDCAN_EFEC_PRIO 0x4 + +/** Treat message as priority and put it into FIFO0 */ +#define FDCAN_EFEC_PRIO_FIFO0 0x5 + +/** Treat message as priority and put it into FIFO1 */ +#define FDCAN_EFEC_PRIO_FIFO1 0x6 +/**@}*/ + +#define FDCAN_EFID1_SHIFT 0 +#define FDCAN_EFID1_MASK 0x1FFFFFFF + +#define FDCAN_EFT_SHIFT 30 +#define FDCAN_EFT_MASK 0x3 + +/** @defgroup fdcan_eft Extended ID filter match type + * + * Matching strategy for extended ID filters. + * @{ + */ +/** Filter matches all messages in range from id1 to id2. */ +#define FDCAN_EFT_RANGE 0x0 + +/** Filter matches messages with id1 or id2 */ +#define FDCAN_EFT_DUAL 0x1 + +/** Filter matches messages which match id1 after being unmasked + * using id2. */ +#define FDCAN_EFT_ID_MASK 0x2 + +/** Similar to @ref FDCAN_EFT_RANGE except of ignoring global mask + * set using @ref FDCAN_XIDAM register. + */ +#define FDCAN_EFT_RANGE_NOXIDAM 0x3 +/**@}*/ + +#define FDCAN_EFID2_SHIFT 0 +#define FDCAN_EFID2_MASK 0x1FFFFFFF + +/** Amount of extended filters allocated in Message RAM + * This number may vary between devices. 8 is value valid + * for STM32G4 + **/ +#define FDCAN_EFT_MAX_NR 8 + +/** Structure describing receive FIFO element. + * Receive FIFO element consists of 2 32bit values for header + * and 16 32bit values for message payload. + * This area of memory can only be accessed in 32bit quantities + */ +struct fdcan_rx_fifo_element { + /** Aggregate of message identifier and flags. */ + uint32_t identifier_flags; + /** Aggregate of filter match ID, transfer format, DLC and timestamp */ + uint32_t filt_fmt_dlc_ts; + /** Message payload data */ + uint32_t data[64 / sizeof(uint32_t)]; +}; + +/** Structure describing transmit event element. + * Transmit event element consists of 2 32bit values. + * This area of memory can only be accessed in 32bit quantities + */ +struct fdcan_tx_event_element { + /** Aggregate of message identifier and flags. */ + uint32_t identifier_flags; + + /** Aggregate of event ID, transfer format, DLC and timestamp */ + uint32_t evt_fmt_dlc_ts; +}; + +/** Structure describing transmit buffer element. + * Transmit buffer consists of 2 32bit values for header + * and 16 32bit values for message payload. + * This area of memory can only be accessed in 32bit quantities + */ +struct fdcan_tx_buffer_element { + /** Aggregate of message identifier and flags. */ + uint32_t identifier_flags; + + /** Aggregate of event ID, transfer format and DLC */ + uint32_t evt_fmt_dlc_res; + /** Message payload data */ + uint32_t data[64 / sizeof(uint32_t)]; +}; + +/** @defgroup fdcan_fifo_flags FIFO / buffer flags + * @{ + */ +#define FDCAN_FIFO_ESI (1 << 31) +#define FDCAN_FIFO_XTD (1 << 20) +#define FDCAN_FIFO_RTR (1 << 29) +#define FDCAN_FIFO_EFC (1 << 23) +#define FDCAN_FIFO_FDF (1 << 21) +#define FDCAN_FIFO_BRS (1 << 20) +/**@}*/ + +#define FDCAN_FIFO_EID_SHIFT 0 +#define FDCAN_FIFO_EID_MASK 0x1FFFFFFF + +#define FDCAN_FIFO_SID_SHIFT 18 +#define FDCAN_FIFO_SID_MASK 0x7FF + +#define FDCAN_FIFO_DLC_SHIFT 16 +#define FDCAN_FIFO_DLC_MASK 0xF + +#define FDCAN_FIFO_MM_SHIFT 24 +#define FDCAN_FIFO_MM_MASK 0xFF + +#define FDCAN_FIFO_ANMF (1 << 31) +#define FDCAN_FIFO_FIDX_SHIFT 24 +#define FDCAN_FIFO_FIDX_MASK 0x7F + +#define FDCAN_FIFO_RXTS_SHIFT 0 +#define FDCAN_FIFO_RXTS_MASK 0xFFFF + +/** Message RAM layout for one FDCAN block. + * There are as many memory blocks as there are FDCAN blocks + */ +struct fdcan_message_ram { + /* List of standard ID filters */ + struct fdcan_standard_filter lfssa[FDCAN_SFT_MAX_NR]; + + /* List of extended ID filters */ + struct fdcan_extended_filter lfesa[FDCAN_EFT_MAX_NR]; + + /* Buffer area for two receive FIFOs each having space for three messages */ + struct fdcan_rx_fifo_element rx_fifo[2][3]; + + /* Buffer area for transmit event buffers */ + struct fdcan_tx_event_element tx_event[3]; + + /* Buffer area for transmitted messages. May act either as FIFO or as queue + * depending on configuration + */ + struct fdcan_tx_buffer_element tx_buffer[3]; +}; + + +/* --- FD-CAN error returns ------------------------------------------------- */ + +/** FDCAN error return values + */ +enum fdcan_error { + /** No error. Operation finished successfully */ + FDCAN_E_OK, + + /** Value provided was out of range */ + FDCAN_E_OUTOFRANGE, + + /** Timeout waiting for FDCAN block to accept INIT bit change */ + FDCAN_E_TIMEOUT, + + /** Value provided was invalid (FIFO index, FDCAN block base address, length, etc.) */ + FDCAN_E_INVALID, + + /** Device is busy: Transmit buffer is full, unable to queue additional message or device + * is outside of INIT mode and cannot perform desired operation. */ + FDCAN_E_BUSY, + + /** Receive buffer is empty, unable to read any new message */ + FDCAN_E_NOTAVAIL +}; + +/**@}*/ + +/* --- FD-CAN functions ----------------------------------------------------- */ + +BEGIN_DECLS + +int fdcan_init(uint32_t canport, uint32_t timeout); + +void fdcan_set_can(uint32_t canport, bool auto_retry_disable, bool rx_fifo_locked, + bool tx_queue_mode, bool silent, uint32_t n_sjw, uint32_t n_ts1, uint32_t n_ts2, + uint32_t n_br_presc); + +void fdcan_set_fdcan(uint32_t canport, bool brs_enable, bool fd_op_enable, + uint32_t f_sjw, uint32_t f_ts1, uint32_t f_ts2, uint32_t f_br_presc); + +void fdcan_set_test(uint32_t canport, bool testing, bool loopback); + +void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt); + +int fdcan_start(uint32_t canport, uint32_t timeout); + +int fdcan_get_init_state(uint32_t canport); + +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); + +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); + +void fdcan_enable_irq(uint32_t canport, uint32_t irq); +void fdcan_disable_irq(uint32_t canport, uint32_t irq); + +int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr, + bool fdcan_fmt, bool btr_switch, uint8_t length, const uint8_t *data); + +int fdcan_receive(uint32_t canport, uint8_t fifo, bool release, uint32_t *id, + bool *ext, bool *rtr, uint8_t *fmi, uint8_t *length, + uint8_t *data, uint16_t *timestamp); + +void fdcan_release_fifo(uint32_t canport, uint8_t fifo); + +bool fdcan_available_tx(uint32_t canport); +bool fdcan_available_rx(uint32_t canport, uint8_t fifo); + +END_DECLS + + +#endif diff --git a/lib/stm32/common/fdcan.c b/lib/stm32/common/fdcan.c new file mode 100644 index 00000000..30e6956a --- /dev/null +++ b/lib/stm32/common/fdcan.c @@ -0,0 +1,779 @@ +/** @defgroup fdcan_file FDCAN peripheral API + * + * @ingroup peripheral_apis + * + * @brief libopencm3 STM32 FDCAN + * + * @version 1.0.0 + * + * @author @htmlonly © @endhtmlonly 2021 Eduard Drusa + * + * 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 + * + * 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 . + */ + +#include +#include +#include + + +/* --- FD-CAN internal functions -------------------------------------------- */ + +/** Routine implementing FDCAN_CCCR's INIT bit manipulation. + * + * This routine will change INIT bit and wait for it to actually + * change its value. If change won't happen before timeout, + * error is signalized. If INIT bit already has value which + * should be set, this function will return immediately. + * + * @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. + * @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) +{ + uint32_t expected; + uint32_t wait_ack; + + if (set) { + if ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT) { + /* Already there, sir */ + return FDCAN_E_OK; + } + + FDCAN_CCCR(canport) |= FDCAN_CCCR_INIT; + expected = FDCAN_CCCR_INIT; + } else { + if ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == 0) { + /* Already there, sir */ + return FDCAN_E_OK; + } + + FDCAN_CCCR(canport) &= ~FDCAN_CCCR_INIT; + expected = 0; + } + + /* Wait, until INIT bit is acknowledged */ + wait_ack = timeout; + while ((wait_ack--) && + ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == expected)); + + if ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == expected) { + return FDCAN_E_OK; + } else { + return FDCAN_E_TIMEOUT; + } +} + +/** Return ID of next free Tx buffer. + * + * Examines transmit buffer allocation in message RAM + * and returns ID of buffer, which is free. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @returns Non-negative number ID of Tx buffer which is free, + * or FDCAN_E_BUSY if no Tx buffer is available + */ +static int fdcan_get_free_txbuf(uint32_t canport) +{ + if ((FDCAN_TXBRP(canport) & FDCAN_TXBRP_TRP0) == 0) { + return 0; + } else if ((FDCAN_TXBRP(canport) & FDCAN_TXBRP_TRP1) == 0) { + return 1; + } else if ((FDCAN_TXBRP(canport) & FDCAN_TXBRP_TRP2) == 0) { + return 2; + } + + return FDCAN_E_BUSY; +} + +/** Returns fill state and next available get index from receive FIFO. + * + * Examines FDCAN receive FIFO and returns fill status of FIFO and ID of + * next message available for reading. If fill status is 0 (FIFO is empty), + * then get index is undefined. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] fifo_id ID of fifo queried (currently 0 or 1) + * @param [out] get_index Address of buffer where next get index will be stored + * @param [out] pending_frames Address of buffer where amount of pending frame will be stored. + */ +static void fdcan_get_fill_rxfifo(uint32_t canport, uint8_t fifo_id, unsigned *get_index, + unsigned *pending_frames) +{ + *get_index = (FDCAN_RXFIS(canport, fifo_id) >> FDCAN_RXFIFO_GI_SHIFT) + & FDCAN_RXFIFO_GI_MASK; + + *pending_frames = (FDCAN_RXFIS(canport, fifo_id) >> FDCAN_RXFIFO_FL_SHIFT) + & FDCAN_RXFIFO_FL_MASK; +} + +/** Obtain address of FDCAN Message RAM for certain FDCAN block. + * + * @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. + */ +static struct fdcan_message_ram *fdcan_get_msgram_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))); + } + + return NULL; +} + +/** Converts frame length to DLC value. + * + * Works for both CAN and FDCAN frame lengths. If length + * is invalid value, then returns 0xFF. + * + * @param [in] length intended frame payload length in bytes + * @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) +{ + if (length <= 8) { + return length; + } else if (length <= 24) { + if ((length % 4) != 0) { + return 0xFF; + } + return 8 + ((length - 8) / 4); + } else { + if ((length % 16) != 0) { + return 0xFF; + } + return 11 + (length / 16); + } +} + +/** Converts DLC value into frame payload length. + * + * Works for both CAN and FDCAN DLC values. + * + * @param [in] dlc DLC value + * @returns data payload length in bytes + */ +static uint8_t fdcan_dlc_to_length(uint32_t dlc) +{ + if (dlc <= 8) { + return dlc; + } else if (dlc <= 12) { + return 8 + ((dlc - 8) * 4); + } else { + return 16 + ((dlc - 12) * 16); + } +} + +/* --- FD-CAN functions ----------------------------------------------------- */ + +/** @ingroup fdcan_file */ +/**@{ + * */ + +/** Put FDCAN block into INIT mode for setup + * + * Initialize the selected CAN peripheral block. This function will switch CAN block + * into initialization mode. CAN block is then left in initialization mode in order to + * perform setup, which can't be adjusted once FDCAN block is started. It is mandatory + * to call at least @ref fdcan_set_can function to configure basic timing values for + * CAN 2.0 operation. Functions which only have effect, if FDCAN block is in INIT mode + * are: + * * @ref fdcan_set_can + * * @ref fdcan_set_fdcan + * * @ref fdcan_init_filter + * * @ref fdcan_set_test + * + * 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. + * @returns Operation error status. See @ref fdcan_error. + */ +int fdcan_init(uint32_t canport, uint32_t timeout) +{ + if (fdcan_cccr_init_cfg(canport, true, timeout) != 0) { + return FDCAN_E_TIMEOUT; + } + + FDCAN_CCCR(canport) |= FDCAN_CCCR_CCE; + + return FDCAN_E_OK; +} + +/** Set essential FDCAN block parameters for plain CAN operation + * + * Allows configuration of prescalers and essential transmit and FIFO behavior + * used during transmission in plain CAN 2.0 mode. In this mode FDCAN frame format + * is not available nor is possible to use fast bitrates. + * This function does neither enable FD-CAN mode after reset nor disable it + * after re-entering INIT mode of previously configured block. Timing values set + * here are valid for both arbitration phase of all frames and for data phase of + * both CAN and FDCAN frames, which don't use bitrate switching. This function can + * only be called after FDCAN block has been switched into INIT mode. + * It is possible to receive FDCAN frames even if FDCAN block is configured only using + * this function as long as bitrate switching is not used. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] auto_retry_disable Disable automatic frame retransmission on error + * @param [in] rx_fifo_locked Enable FIFO locked mode. Upon FIFO overflow all received + * messages are discarded. + * @param [in] tx_queue_mode Enable transmission queue mode. Otherwise transmission + * works in FIFO mode. + * @param [in] silent Enable silent mode. Transmitter stays recessive all the time. + * @param [in] n_sjw Resynchronization time quanta jump width + * @param [in] n_ts1 Time segment 1 time quanta + * @param [in] n_ts2 Time segment 2 time quanta + * @param [in] n_br_presc Arbitration phase / CAN mode bitrate prescaler + */ +void fdcan_set_can(uint32_t canport, bool auto_retry_disable, bool rx_fifo_locked, + bool tx_queue_mode, bool silent, uint32_t n_sjw, uint32_t n_ts1, uint32_t n_ts2, + uint32_t n_br_presc) +{ + FDCAN_NBTP(canport) = (n_sjw << FDCAN_NBTP_NSJW_SHIFT) + | (n_ts1 << FDCAN_NBTP_NTSEG1_SHIFT) + | (n_ts2 << FDCAN_NBTP_NTSEG2_SHIFT) + | (n_br_presc << FDCAN_NBTP_NBRP_SHIFT); + + if (tx_queue_mode) { + FDCAN_TXBC(canport) |= FDCAN_TXBC_TFQM; + } else { + FDCAN_TXBC(canport) &= ~FDCAN_TXBC_TFQM; + } + + if (auto_retry_disable) { + FDCAN_CCCR(canport) |= FDCAN_CCCR_DAR; + } else { + FDCAN_CCCR(canport) &= ~FDCAN_CCCR_DAR; + } + + if (silent) { + FDCAN_CCCR(canport) |= FDCAN_CCCR_MON; + } else { + 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; + } + +} + +/** 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. + * It is safe to call this function on previously configured block in order + * to enable/disable/change FDCAN mode parameters. Non-FDCAN parameters won't + * be affected. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] brs_enable Enable FDCAN bitrate switching for fast mode operation + * @param [in] fd_op_enable Enable transmission of FDCAN-formatted frames + * @param [in] f_sjw Resynchronization time quanta jump width in fast mode + * @param [in] f_ts1 Time segment 1 time quanta in fast mode + * @param [in] f_ts2 Time segment 2 time quanta in fast mode + * @param [in] f_br_presc Fast mode operation bitrate prescaler + */ +void fdcan_set_fdcan(uint32_t canport, bool brs_enable, bool fd_op_enable, + uint32_t f_sjw, uint32_t f_ts1, uint32_t f_ts2, uint32_t f_br_presc) +{ + FDCAN_DBTP(canport) = (f_sjw << FDCAN_DBTP_DSJW_SHIFT) + | (f_ts1 << FDCAN_DBTP_DTSEG1_SHIFT) + | (f_ts2 << FDCAN_DBTP_DTSEG2_SHIFT) + | (f_br_presc << FDCAN_DBTP_DBRP_SHIFT); + + if (fd_op_enable) { + FDCAN_CCCR(canport) |= FDCAN_CCCR_FDOE; + } else { + FDCAN_CCCR(canport) &= ~FDCAN_CCCR_FDOE; + } + + if (brs_enable) { + FDCAN_CCCR(canport) |= FDCAN_CCCR_BRSE; + } else { + FDCAN_CCCR(canport) &= ~FDCAN_CCCR_BRSE; + } +} + +/** Set FDCAN block testing features. + * + * Configures self-test functions of FDCAN block. It is safe to call + * this function on fully configured interface. This function can + * only be called after FDCAN block is put into INIT mode. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] testing Enables testing mode of FDCAN block + * @param [in] loopback Enables transmission loopback + */ +void fdcan_set_test(uint32_t canport, bool testing, bool loopback) +{ + if (testing) { + FDCAN_CCCR(canport) |= FDCAN_CCCR_TEST; + if (loopback) { + FDCAN_TEST(canport) |= FDCAN_TEST_LBCK; + } else { + FDCAN_TEST(canport) &= ~FDCAN_TEST_LBCK; + } + } else { + FDCAN_CCCR(canport) &= ~FDCAN_CCCR_TEST; + /* FDCAN_TEST is automatically reset to default values by + * FDCAN at this point */ + } +} + +/** 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. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @returns 1 if FDCAN block is in INIT mode, 0 if it is already started. + */ +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 + * have its own set of filtering rules. It is only possible to configure as + * many filters as was configured previously using @ref fdcan_init_filter. + * + * @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] 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. + */ +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); + + /* id_list_mode and action are passed unguarded. Simply use + * defines and it will be OK. id1 and id2 are masked for + * correct size, because it is way too simple to pass ID + * larger than 11 bits unintentionally. It then leads to all + * kind of extremely weird errors while excessive ID bits + * overflow into flags. This tends to be extremely time + * consuming to debug. + */ + ram->lfssa[nr].type_id1_conf_id2 = + (id_list_mode << FDCAN_SFT_SHIFT) + | (action << FDCAN_SFEC_SHIFT) + | ((id1 & FDCAN_SFID1_MASK) << FDCAN_SFID1_SHIFT) + | ((id2 & FDCAN_SFID2_MASK) << FDCAN_SFID2_SHIFT); + + return; +} + +/** Configure filter rule for extended ID frames. + * + * Sets up filter rule for frames having extended ID. Each FDCAN block can + * have its own set of filtering rules. It is only possible to configure as + * many filters as was configured previously using @ref fdcan_init_filter. + * + * @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. + * @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. + */ +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); + + ram->lfesa[nr].conf_id1 = + (action << FDCAN_EFEC_SHIFT) + | ((id1 & FDCAN_EFID1_MASK) << FDCAN_EFID1_SHIFT); + + ram->lfesa[nr].type_id2 = + (id_list_mode << FDCAN_EFT_SHIFT) + | ((id2 & FDCAN_EFID2_MASK) << FDCAN_EFID2_SHIFT); +} + +/** Transmit Message using FDCAN + * + * @param [in] canport CAN block register base. See @ref fdcan_block. + * @param [in] id Message ID + * @param [in] ext Extended message ID + * @param [in] rtr Request transmit + * @param [in] fdcan_fmt Use FDCAN format + * @param [in] btr_switch Switch bitrate for data portion of frame + * @param [in] length Message payload length. Must be valid CAN or FDCAN frame length + * @param [in] data Message payload data + * @returns int 0, 1 or 2 on success and depending on which outgoing mailbox got + * selected. Otherwise returns error code. For error codes, see @ref fdcan_error. + */ +int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr, + bool fdcan_fmt, bool btr_switch, uint8_t length, const uint8_t *data) +{ + int mailbox; + uint32_t dlc, flags = 0; + + mailbox = fdcan_get_free_txbuf(canport); + + if (mailbox == FDCAN_E_BUSY) { + return mailbox; + } + + struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport); + + /* Early check: if FDCAN message lentgh is > 8, it must be + * a multiple of 4 *and* fdcan format must be enabled. + */ + dlc = fdcan_length_to_dlc(length); + + if (dlc == 0xFF) { + return FDCAN_E_INVALID; + } + + if (ext) { + ram->tx_buffer[mailbox].identifier_flags = FDCAN_FIFO_XTD + | ((id & FDCAN_FIFO_EID_MASK) << FDCAN_FIFO_EID_SHIFT); + } else { + ram->tx_buffer[mailbox].identifier_flags = + (id & FDCAN_FIFO_SID_MASK) << FDCAN_FIFO_SID_SHIFT; + } + + if (rtr) { + ram->tx_buffer[mailbox].identifier_flags |= FDCAN_FIFO_RTR; + } + + if (fdcan_fmt) { + flags |= FDCAN_FIFO_FDF; + } + + if (btr_switch) { + flags |= FDCAN_FIFO_BRS; + } + + ram->tx_buffer[mailbox].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]); + } + + FDCAN_TXBAR(canport) |= 1 << mailbox; + + return mailbox; +} + +/** Receive Message from FDCAN FIFO + * + * Reads one message from receive FIFO. Returns message ID, type of ID, message length + * and message payload. It is mandatory to provide valid pointers to suitably sized buffers + * for these outputs. Additionally, it is optinally possible to provide non-zero pointer to + * obtain filter identification, request of transmission flag and message timestamp. + * If pointers provided for optional outputs are NULL, then no information is returned + * for given pointer. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block + * @param [in] fifo_id FIFO id. + * @param [in] release Release the FIFO automatically after copying data out + * @param [out] id Returned message ID. Mandatory. + * @param [out] ext Returned type of the message ID (true if extended). Mandatory. + * @param [out] rtr Returnes flag if request of transmission was requested. Optional. + * @param [out] fmi Returned ID of the filter which matched this frame. Optional. + * @param [out] length Length of message payload in bytes. Mandatory. + * @param [out] data Buffer for storage of message payload data. Mandatory. + * @param [out] timestamp Returned timestamp of received frame. Optional. + * @returns Operation error status. See @ref fdcan_error. + */ +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) + & FDCAN_FIFO_DLC_MASK; + + len = fdcan_dlc_to_length(dlc); + + *length = len; + if ((fifo[get_index].identifier_flags & FDCAN_FIFO_XTD) == FDCAN_FIFO_XTD) { + *ext = true; + *id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_EID_SHIFT) + & FDCAN_FIFO_EID_MASK; + } else { + *ext = false; + *id = (fifo[get_index].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) + & FDCAN_FIFO_RXTS_MASK; + } + + if (fmi) { + *fmi = (uint8_t) (fifo[get_index].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); + } + + for (unsigned int q = 0; q < len; q += 4) { + *((uint32_t *) &data[q]) = fifo[get_index].data[q / 4]; + } + + if (release) { + FDCAN_RXFIA(canport, fifo_id) |= get_index << FDCAN_RXFIFO_AI_SHIFT; + } + + return FDCAN_E_OK; +} + +/** Release receive oldest FIFO entry. + * + * This function will mask oldest entry in FIFO as released making + * space for another received frame. This function can be used if + * fdcan_receive was called using release = false. If used in other + * case, then messages can get lost. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] fifo_id ID of FIFO where release should be performed (0 or 1) + */ +void fdcan_release_fifo(uint32_t canport, uint8_t fifo_id) +{ + unsigned pending_frames, get_index; + + get_index = (FDCAN_RXFIS(canport, fifo_id) >> FDCAN_RXFIFO_GI_SHIFT) + & FDCAN_RXFIFO_GI_SHIFT; + + pending_frames = (FDCAN_RXFIS(canport, fifo_id) >> FDCAN_RXFIFO_FL_SHIFT) + & FDCAN_RXFIFO_FL_SHIFT; + + if (pending_frames) { + FDCAN_RXFIA(canport, fifo_id) |= get_index << FDCAN_RXFIFO_AI_SHIFT; + } +} + +/** Enable IRQ from FDCAN block. + * + * This routine configures FDCAN to enable certain IRQ. + * Each FDCAN block supports two IRQs. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] irq number of IRQ to be enabled (currently 0 or 1) + */ +void fdcan_enable_irq(uint32_t canport, uint32_t irq) +{ + FDCAN_ILE(canport) |= irq & (FDCAN_ILE_INT0 | FDCAN_ILE_INT1); +} + +/** Disable IRQ from FDCAN block. + * + * This routine configures FDCAN to disable certain IRQ. + * Each FDCAN block supports two IRQs. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] irq number of IRQ to be enabled (currently 0 or 1) + */ +void fdcan_disable_irq(uint32_t canport, uint32_t irq) +{ + FDCAN_ILE(canport) &= ~(irq & (FDCAN_ILE_INT0 | FDCAN_ILE_INT1)); +} + +/** Check if there is free transmit buffer. + * + * @param [in] canport FDCAN port. See @ref fdcan_block. + * @returns true if there is at least one free transmit buffer for new message + * to be sent, false otherwise. + */ +bool fdcan_available_tx(uint32_t canport) +{ + return (fdcan_get_free_txbuf(canport) != FDCAN_E_BUSY); +} + +/** Tell if there is message waiting in receive FIFO. + * + * @param [in] canport FDCAN port. See @ref fdcan_block. + * @param [in] fifo Rx FIFO number, 0 or 1 + * @returns true if there is at least one message waiting in given receive FIFO, + * false otherwise. + */ +bool fdcan_available_rx(uint32_t canport, uint8_t fifo) +{ + unsigned pending_frames; + + pending_frames = (FDCAN_RXFIS(canport, fifo) >> FDCAN_RXFIFO_FL_SHIFT) + & FDCAN_RXFIFO_FL_MASK; + + return (pending_frames != 0); +} + +/**@}*/ + + diff --git a/lib/stm32/g4/Makefile b/lib/stm32/g4/Makefile index ddaddb73..63a13e81 100644 --- a/lib/stm32/g4/Makefile +++ b/lib/stm32/g4/Makefile @@ -40,6 +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 += 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