diff --git a/README.md b/README.md index 4fe0444..f40ade5 100644 --- a/README.md +++ b/README.md @@ -19,16 +19,27 @@ STM32 core: https://github.com/stm32duino/Arduino_Core_STM32 ## How to use. To use this library, CAN module needs to be enabled in HAL drivers. If PIO is used, it's enough -to add -DHAL_CAN_MODULE_ENABLED as build flag. With Arduino IDE it's easiest to create hal_conf_extra.h -file -to same folder with sketch and haven #define HAL_CAN_MODULE_ENABLED there. See examples for this. +to add `-DHAL_CAN_MODULE_ENABLED` as build flag. With Arduino IDE it's easiest to create `hal_conf_extra.h` file +to same folder with sketch and haven `#define HAL_CAN_MODULE_ENABLED` there. See examples for this. ### Setup The sketch needs include in the header to use the library. -``` +```Cpp #include "STM32_CAN.h" ``` Use constructor to set which CAN interface and pins to use. +```Cpp +STM32_CAN Can1( CAN1 ); //by peripheral. Uses first pin defined in arduino PeripheralPins maps +STM32_CAN Can1( PA11 /* rx pin */, PA12 /* tx pin */); //by arduino digital pin number. Finds matching peripheral automatically +STM32_CAN Can1( PA_11, PA_12 ); //by PinName. Finds matching peripheral automatically ``` +**Note**: Digital pin and PinName may not be mixed or one will be implicitly converted and possibly causing unintended behavior. PinNames are defined with an underscore between port and pin. Digital pin numbers are aliased with port and pinnumber without an underscore. +Can convert between the two with `digitalPinToPinName()` and `pinNametoDigitalPin()`. + +Tx pin may be defined as `PNUM_NOT_DEFINED` (digital pin number) or `NC` (PinName). Then only Rx is setup, allowing a listen only mode. + +The original method by choosing from 3 sets of fixed pin combinations can still be used as well for compatibility. +```Cpp STM32_CAN Can1( CAN1, DEF ); ``` Depending on the STM32 model, there is CAN1, CAN2 and CAN3 available. @@ -45,11 +56,98 @@ The pins are: - ALT: PB3/4 In constructor you can optionally set TX and RX buffer sizes. Default is 16 messages. +```Cpp +STM32_CAN Can1 (PA_11, PA_12, RX_SIZE_256, TX_SIZE_256); +``` + +#### Additional settings +Following settings may be changed before starting the driver with `begin()`. +```Cpp +setIRQPriority(uint32_t preemptPriority, uint32_t subPriority); // default: lowest prio, 0 +setAutoRetransmission(bool enabled); //default: true +setRxFIFOLock(bool fifo0locked); //default: false +setTxBufferMode(TX_BUFFER_MODE mode); //default: FIFO +setTimestampCounter(bool enabled); //default: false +setMode(MODE mode); //default: NORMAL +setAutoBusOffRecovery(bool enabled); //default: false +``` +**Note**: `setTimestampCounter()` should always be set `false`. Feature is marked as non functioning in Erratas. + +**Note**: begin() will overwrite `setAutoRetransmission()` setting (`false` by default). + +#### Start / Stop bus + +Before using library commands, begin must be called. +```Cpp +Can1.begin(); +``` +Optionally automatic retransmission can be enabled with begin. +```Cpp +Can1.begin(true); +``` +Set baud rate by using. +```Cpp +Can1.setBaudRate(500000); +``` +500 kbit/s in this case. + +`setBaudRate` may be called before or after begin. Bus start once both are called. + +It may also be called while running to change baudrate. + +Call +```Cpp +Can1.end(); +``` +to stop the bus and release all resources. Will be implicitly called by destructor when freeing object. +Only one STM32_CAN instance can control a CAN Peripheral at one time. It reserves the peripheral from begin() to end(). + +#### Filters +Filters may only be set after bus is started (`begin()` & `setBaudRate()` was called). + +By default bank 0 will receive CAN messages with all IDs. But using filters, we can set the CAN to receive +only specific message IDs. The CAN peripheral has many different methods of defining filters for messages. +These functions help setting up filters: +```Cpp +bool setFilterSingleMask(uint8_t bank_num, uint32_t id, uint32_t mask, IDE std_ext); +bool setFilterDualID (uint8_t bank_num, uint32_t id1, uint32_t id2, IDE std_ext1, IDE std_ext2); +bool setFilterDualMask (uint8_t bank_num, uint32_t id1, uint32_t mask1, IDE std_ext1, uint32_t id2, uint32_t mask2, IDE std_ext2); +bool setFilterQuadID (uint8_t bank_num, uint32_t id1, IDE std_ext1, uint32_t id2, IDE std_ext2, uint32_t id3, IDE std_ext3, uint32_t id4, IDE std_ext4); +``` +The simplest one is `setFilterSingleMask`. The others allow setting multiple filters into a single filter bank. When using the functions `setFilterDualMask` and `setFilterQuadID` for extended ids, the 15 LSBs of the ID are always masked out. + +Example: we want to receive only messages with standard ID 0x153 and extended ID 0x613, so we use bank 0 and 1. +```Cpp +Can1.setFilterSingleMask( 0, 0x153, 0x7FF, STD); +Can1.setFilterSingleMask( 1, 0x613, 0x1FFFFFFF, EXT); +``` +First number is the bank number. Second is message ID. 3rd ID mask. And last one defines ID type: `AUTO`, `STD` or `EXT`. + + +**Note**: optional args of filter settings allow storing messages into the non-default RxFIFO. `read()` currently only reads from the default FIFO, so messages will not be accessible if sent to other FIFO. + +**Note**: the following function allowed setting filters in the past. This function was bugged and did only work as long as optional args filter_mode and filter_scale where not changed. +Its behavior was kept in the broken state for compatibility. It has a 4th arg for ID type, defaults to `AUTO`. +```Cpp +bool setFilter(uint8_t bank_num, uint32_t filter_id, uint32_t mask) ``` -STM32_CAN Can1 (CAN1, ALT_2, RX_SIZE_256, TX_SIZE_256); + +To disable a filter bank call (`true` to re-enable it) +```Cpp +Can1.setFilter(bank_num, false); ``` -There is structure to contain CAN packets and info about those. + + +The amount of available filter banks can be queried with: +```Cpp +Can1.getFilterBankCount() ``` +**Note**: Single CAN devices have 14, dual CAN devices 28. Driver does split the 28 into 14 for each CAN instance. + + +### Main loop +The library defines this structure to describe a CAN message. +```Cpp typedef struct CAN_message_t { uint32_t id = 0; // can identifier uint16_t timestamp = 0; // time when message arrived @@ -67,41 +165,20 @@ typedef struct CAN_message_t { bool seq = 0; // sequential frames } CAN_message_t; ``` -In sketch. -``` -static CAN_message_t CAN_msg; -``` -Before using library commands, begin must be called. -``` -Can1.begin(); -``` -Optionally automatic retransmission can be enabled with begin. -``` -Can1.begin(true); -``` -Set baud rate by using. -``` -Can1.setBaudRate(500000); -``` -500 kbit/s in this case. -By default bank 0 will receive CAN messages with all IDs. But using filters, we can set the CAN to receive -only specific message IDs. Ie. if we want to receive only message IDs 0x153 and 0x613, so we use bank 0 and 1. -``` -Can1.setFilter( 0, 0x153, 0x1FFFFFFF ); -Can1.setFilter( 1, 0x613, 0x1FFFFFFF ); +Define variable to store a message +```Cpp +CAN_message_t CAN_msg; ``` -First number is the bank number. Second is message ID. And last one is mask. -### Main loop To read CAN messages from buffer: -``` -Can1.read(CAN_msg) +```Cpp +Can1.read(CAN_msg); ``` This will return true if there is messages available on receive buffer. To write messages to send queue. -``` +```Cpp Can1.write(CAN_msg); ``` This will return true if there room in the send queue. diff --git a/STM32_CAN.cpp b/STM32_CAN.cpp index b2b8080..7e7daec 100644 --- a/STM32_CAN.cpp +++ b/STM32_CAN.cpp @@ -1,295 +1,547 @@ #include "STM32_CAN.h" +#include "core_debug.h" + +#if defined(HAL_CEC_MODULE_ENABLED) && defined(STM32_CAN1_SHARED_WITH_CEC) +/** Pointer to CEC_HandleTypeDef structure that contains + * the configuration information for the specified CEC. + * Application have to declare them properly to be able to call + * the HAL_CEC_IRQHandler(). + */ +extern CEC_HandleTypeDef * phcec; +#endif + +#define STM32_CAN_SINGLE_CAN_FILTER_COUNT 14 +#define STM32_CAN_DUAL_CAN_FILTER_COUNT 28 +#define STM32_CAN_CAN2_FILTER_OFFSET 14 + +//Max value, lowest priority +#define MAX_IRQ_PRIO_VALUE ((1UL << __NVIC_PRIO_BITS) - 1UL) + constexpr Baudrate_entry_t STM32_CAN::BAUD_RATE_TABLE_48M[]; constexpr Baudrate_entry_t STM32_CAN::BAUD_RATE_TABLE_45M[]; -static STM32_CAN* _CAN1 = nullptr; -static CAN_HandleTypeDef hcan1; uint32_t test = 0; + +typedef enum { +#ifdef CAN1 + CAN1_INDEX, +#endif #ifdef CAN2 -static STM32_CAN* _CAN2 = nullptr; -static CAN_HandleTypeDef hcan2; + CAN2_INDEX, #endif #ifdef CAN3 -static STM32_CAN* _CAN3 = nullptr; -static CAN_HandleTypeDef hcan3; + CAN3_INDEX, #endif + CAN_NUM, + CAN_UNKNOWN = 0xFFFF +} can_index_t; -STM32_CAN::STM32_CAN( CAN_TypeDef* canPort, CAN_PINS pins, RXQUEUE_TABLE rxSize, TXQUEUE_TABLE txSize ) { +static stm32_can_t * canObj[CAN_NUM] = {NULL}; - if (_canIsActive) { return; } +stm32_can_t *get_can_obj(CAN_HandleTypeDef *hcan) +{ + stm32_can_t *obj; + obj = (stm32_can_t *)((char *)hcan - offsetof(stm32_can_t, handle)); + return (obj); +} - sizeRxBuffer=rxSize; - sizeTxBuffer=txSize; +can_index_t get_can_index(CAN_TypeDef *instance) +{ + can_index_t index = CAN_UNKNOWN; +#if defined(CAN1) + if (instance == CAN1) { + index = CAN1_INDEX; + } +#endif +#if defined(CAN2) + if (instance == CAN2) { + index = CAN2_INDEX; + } +#endif +#if defined(CAN3) + if (instance == CAN3) { + index = CAN3_INDEX; + } +#endif + if (index == CAN_UNKNOWN) { + Error_Handler(); + } + return index; +} + +bool STM32_CAN::allocatePeripheral(CAN_TypeDef *instance) +{ + can_index_t index = get_can_index(instance); + if(index >= CAN_NUM) + { + return false; + } + if(canObj[index]) + { + //bus already in use by other instance + Error_Handler(); + return false; + } + _can.handle.Instance = instance; + //register with global, we own this instance now + canObj[index] = &_can; + return true; +} +bool STM32_CAN::freePeripheral() +{ + if (_can.handle.Instance == nullptr) return false; + can_index_t index = get_can_index(_can.handle.Instance); + if(index >= CAN_NUM) + { + return false; + } + if(canObj[index] == &_can) + { + canObj[index] = nullptr; + _can.handle.Instance = nullptr; + return true; + } + Error_Handler(); + return false; +} + +bool STM32_CAN::hasPeripheral() +{ + if (_can.handle.Instance == nullptr) return false; + can_index_t index = get_can_index(_can.handle.Instance); + if(index >= CAN_NUM) + { + return false; + } + return canObj[index] == &_can; +} + +STM32_CAN::STM32_CAN(uint32_t rx, uint32_t tx, RXQUEUE_TABLE rxSize, TXQUEUE_TABLE txSize) + : sizeRxBuffer(rxSize), sizeTxBuffer(txSize), + preemptPriority(MAX_IRQ_PRIO_VALUE), subPriority(0) +{ + this->rx = digitalPinToPinName(rx); + this->tx = digitalPinToPinName(tx); + init(); +} + +STM32_CAN::STM32_CAN(PinName rx, PinName tx, RXQUEUE_TABLE rxSize, TXQUEUE_TABLE txSize) + : rx(rx), tx(tx), sizeRxBuffer(rxSize), sizeTxBuffer(txSize), + preemptPriority(MAX_IRQ_PRIO_VALUE), subPriority(0) +{ + init(); +} + +STM32_CAN::STM32_CAN( CAN_TypeDef* canPort, RXQUEUE_TABLE rxSize, TXQUEUE_TABLE txSize ) + : sizeRxBuffer(rxSize), sizeTxBuffer(txSize), + preemptPriority(MAX_IRQ_PRIO_VALUE), subPriority(0) +{ + //get first matching pins from map + rx = pinmap_find_pin(canPort, PinMap_CAN_RD); + tx = pinmap_find_pin(canPort, PinMap_CAN_TD); + init(); +} + +//lagacy pin config for compatibility +STM32_CAN::STM32_CAN( CAN_TypeDef* canPort, CAN_PINS pins, RXQUEUE_TABLE rxSize, TXQUEUE_TABLE txSize ) + : rx(NC), tx(NC), sizeRxBuffer(rxSize), sizeTxBuffer(txSize), + preemptPriority(MAX_IRQ_PRIO_VALUE), subPriority(0) +{ if (canPort == CAN1) { - _CAN1 = this; - n_pCanHandle = &hcan1; + switch(pins) + { + case DEF: + rx = PA_11; + tx = PA_12; + break; + case ALT: + rx = PB_8; + tx = PB_9; + break; + #if defined(__HAL_RCC_GPIOD_CLK_ENABLE) + case ALT_2: + rx = PD_0; + tx = PD_1; + break; + #endif + } } - #ifdef CAN2 - if( canPort == CAN2) +#ifdef CAN2 + else if(canPort == CAN2) { - _CAN2 = this; - n_pCanHandle = &hcan2; + switch(pins) + { + case DEF: + rx = PB_12; + tx = PB_13; + break; + case ALT: + rx = PB_5; + tx = PB_6; + break; + } } - #endif - #ifdef CAN3 - if (canPort == CAN3) +#endif +#ifdef CAN3 + else if(canPort == CAN3) + { + switch(pins) + { + case DEF: + rx = PA_8; + tx = PA_15; + break; + case ALT: + rx = PB_3; + tx = PB_4; + break; + } + } +#endif + init(); +} + +STM32_CAN::~STM32_CAN() +{ + end(); +} + +void STM32_CAN::init(void) +{ + _can.__this = (void*)this; + _can.handle.Instance = nullptr; + baudrate = 0UL; + filtersInitialized = false; + + setTimestampCounter(false); + setAutoBusOffRecovery(false); + _can.handle.Init.AutoWakeUp = DISABLE; + setRxFIFOLock(false); + setTxBufferMode(TX_BUFFER_MODE::FIFO); + setMode(MODE::NORMAL); + setAutoRetransmission(true); +} + +CAN_TypeDef * STM32_CAN::getPeripheral() +{ + CAN_TypeDef * canPort_rx = (CAN_TypeDef *) pinmap_peripheral(rx, PinMap_CAN_RD); + CAN_TypeDef * canPort_tx = (CAN_TypeDef *) pinmap_peripheral(tx, PinMap_CAN_TD); + if ((canPort_rx != canPort_tx && canPort_tx != NP) || canPort_rx == NP) + { + //ensure pins relate to same peripheral OR only Rx is set/valid + // rx only can be used as listen only but needs a 3rd node for valid ACKs + + // do not allow Tx only since that would break arbitration + return NP; + } + + #ifdef STM32F1xx + /** AF remapping on the F1 platform only possible in pairs + * Verify that both pins use the same remapping + * Only enforced if both pins are used, in Rx only Tx pin is not set to AF*/ + if(canPort_rx != NP && canPort_tx != NP) { - _CAN3 = this; - n_pCanHandle = &hcan3; + uint32_t rx_func = pinmap_function(rx, PinMap_CAN_RD); + uint32_t tx_func = pinmap_function(tx, PinMap_CAN_TD); + uint32_t rx_afnum = STM_PIN_AFNUM(rx_func); + uint32_t tx_afnum = STM_PIN_AFNUM(tx_func); + if(rx_afnum != tx_afnum) + { + //ERROR + return NP; + } } #endif - _canPort = canPort; - _pins = pins; + //clear tx pin in case it was set but does not match a peripheral + if(canPort_tx == NP) + tx = NC; + + return canPort_rx; +} + +/**------------------------------------------------------------- + * setup functions + * no effect after begin() + * ------------------------------------------------------------- + */ +void STM32_CAN::setIRQPriority(uint32_t preemptPriority, uint32_t subPriority) +{ + //NOTE: limiting the IRQ prio, but not accounting for group setting + this->preemptPriority = min(preemptPriority, MAX_IRQ_PRIO_VALUE); + this->subPriority = min(subPriority, MAX_IRQ_PRIO_VALUE); +} + +void STM32_CAN::setAutoRetransmission(bool enabled) +{ + _can.handle.Init.AutoRetransmission = enabled ? (ENABLE) : (DISABLE); +} + +void STM32_CAN::setRxFIFOLock(bool fifo0locked, bool fifo1locked) +{ + (void)fifo1locked; + _can.handle.Init.ReceiveFifoLocked = fifo0locked ? (ENABLE) : (DISABLE); +} + +void STM32_CAN::setTxBufferMode(TX_BUFFER_MODE mode) +{ + _can.handle.Init.TransmitFifoPriority = (FunctionalState)mode; +} + +void STM32_CAN::setTimestampCounter(bool enabled) +{ + _can.handle.Init.TimeTriggeredMode = enabled ? (ENABLE) : (DISABLE); +} + +void STM32_CAN::setMode(MODE mode) +{ + _can.handle.Init.Mode = mode; } +void STM32_CAN::enableLoopBack( bool yes ) { + setMode(yes ? MODE::LOOPBACK : MODE::NORMAL); +} + +void STM32_CAN::enableSilentMode( bool yes ) { + setMode(yes ? MODE::SILENT : MODE::NORMAL); +} + +void STM32_CAN::enableSilentLoopBack( bool yes ) { + setMode(yes ? MODE::SILENT_LOOPBACK : MODE::NORMAL); +} + +void STM32_CAN::setAutoBusOffRecovery(bool enabled) +{ + _can.handle.Init.AutoBusOff = enabled ? (ENABLE) : (DISABLE); +} + +/**------------------------------------------------------------- + * lifecycle functions + * setBaudRate may be called before or after begin + * ------------------------------------------------------------- + */ // Init and start CAN void STM32_CAN::begin( bool retransmission ) { // exit if CAN already is active if (_canIsActive) return; - _canIsActive = true; + auto instance = getPeripheral(); + if(instance == NP) + { + //impossible pinconfig, done here + return; + } + if(!allocatePeripheral(instance)) + { + //peripheral already in use + return; + } - GPIO_InitTypeDef GPIO_InitStruct; + _canIsActive = true; initializeBuffers(); + + /** + * NOTE: enabling the internal pullup of RX pin + * in case no external circuitry (CAN Transceiver) is connected this will ensure a valid recessive level. + * A valid recessive level on RX is needed to leave init mode and enter normal mode. + * This is even the case if loopback is enabled where the input is internally connected. + * This lets loopback only tests without external circuitry sill function. + */ + uint32_t rx_func = pinmap_function(rx, PinMap_CAN_RD); + pin_function(rx, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, STM_PIN_AFNUM(rx_func))); + if(tx != NC) + { + pin_function(tx, pinmap_function(tx, PinMap_CAN_TD)); + } // Configure CAN - if (_canPort == CAN1) + if (_can.handle.Instance == CAN1) { //CAN1 __HAL_RCC_CAN1_CLK_ENABLE(); - if (_pins == ALT) - { - __HAL_RCC_GPIOB_CLK_ENABLE(); - #if defined(__HAL_RCC_AFIO_CLK_ENABLE) // Some MCUs like F1xx uses AFIO to set pins, so if there is AFIO defined, we use that. - __HAL_AFIO_REMAP_CAN1_2(); // To use PB8/9 pins for CAN1. - __HAL_RCC_AFIO_CLK_ENABLE(); - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // If AFIO is used, there doesn't seem to be "very high" option for speed, so we use "high" -setting. - GPIO_InitStruct.Pin = GPIO_PIN_8; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - GPIO_InitStruct.Pin = GPIO_PIN_9; - #else // Without AFIO, this is the way to set the pins for CAN. - #if defined(GPIO_AF8_CAN1) // Depending on the MCU used, this can be AF8, AF4 or AF9 - GPIO_InitStruct.Alternate = GPIO_AF8_CAN1; - #elif defined(GPIO_AF4_CAN) - GPIO_InitStruct.Alternate = GPIO_AF4_CAN; - #else - GPIO_InitStruct.Alternate = GPIO_AF9_CAN1; - #endif - GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9; - #if defined(GPIO_SPEED_FREQ_VERY_HIGH) - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - #else - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - #endif - #endif - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - } - if (_pins == DEF) - { - __HAL_RCC_GPIOA_CLK_ENABLE(); - GPIO_InitStruct.Pull = GPIO_NOPULL; - #if defined(__HAL_RCC_AFIO_CLK_ENABLE) - __HAL_AFIO_REMAP_CAN1_1(); // To use PA11/12 pins for CAN1. - __HAL_RCC_AFIO_CLK_ENABLE(); - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Pin = GPIO_PIN_11; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - GPIO_InitStruct.Pin = GPIO_PIN_12; - #else - #if defined(GPIO_AF4_CAN) - GPIO_InitStruct.Alternate = GPIO_AF4_CAN; - #else - GPIO_InitStruct.Alternate = GPIO_AF9_CAN1; - #endif - GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12; - #if defined(GPIO_SPEED_FREQ_VERY_HIGH) - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - #else - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - #endif - #endif - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - } - - #if defined(__HAL_RCC_GPIOD_CLK_ENABLE) // not all MCU variants have port GPIOD available - if (_pins == ALT_2) - { - __HAL_RCC_GPIOD_CLK_ENABLE(); - GPIO_InitStruct.Pull = GPIO_NOPULL; - #if defined(__HAL_RCC_AFIO_CLK_ENABLE) - __HAL_AFIO_REMAP_CAN1_3(); // To use PD0/1 pins for CAN1. - __HAL_RCC_AFIO_CLK_ENABLE(); - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Pin = GPIO_PIN_0; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); - GPIO_InitStruct.Pin = GPIO_PIN_1; - #else - #if defined(GPIO_AF4_CAN) - GPIO_InitStruct.Alternate = GPIO_AF4_CAN; - #else - GPIO_InitStruct.Alternate = GPIO_AF9_CAN1; - #endif - GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; - #if defined(GPIO_SPEED_FREQ_VERY_HIGH) - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - #else - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - #endif - #endif - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); - } - #endif - + #ifdef CAN1_IRQn_AIO + // NVIC configuration for CAN1 common interrupt + HAL_NVIC_SetPriority(CAN1_IRQn_AIO, preemptPriority, subPriority); + HAL_NVIC_EnableIRQ(CAN1_IRQn_AIO); + #else + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + /** CAN Tx and Rx0 blocked by USB, only using Rx1 */ + HAL_NVIC_SetPriority(CAN1_RX1_IRQn, preemptPriority, subPriority); + HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn); + #else // NVIC configuration for CAN1 Reception complete interrupt - HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 15, 0); // 15 is lowest possible priority + HAL_NVIC_SetPriority(CAN1_RX0_IRQn, preemptPriority, subPriority); HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn ); // NVIC configuration for CAN1 Transmission complete interrupt - HAL_NVIC_SetPriority(CAN1_TX_IRQn, 15, 0); // 15 is lowest possible priority + HAL_NVIC_SetPriority(CAN1_TX_IRQn, preemptPriority, subPriority); HAL_NVIC_EnableIRQ(CAN1_TX_IRQn); + #endif + #endif /** else defined(CAN1_IRQn_AIO) */ - n_pCanHandle->Instance = CAN1; + _can.bus = 1; } #ifdef CAN2 - else if (_canPort == CAN2) + else if (_can.handle.Instance == CAN2) { //CAN2 __HAL_RCC_CAN1_CLK_ENABLE(); // CAN1 clock needs to be enabled too, because CAN2 works as CAN1 slave. __HAL_RCC_CAN2_CLK_ENABLE(); - if (_pins == ALT) - { - __HAL_RCC_GPIOB_CLK_ENABLE(); - #if defined(__HAL_RCC_AFIO_CLK_ENABLE) - __HAL_AFIO_REMAP_CAN2_ENABLE(); // To use PB5/6 pins for CAN2. Don't ask me why this has different name than for CAN1. - __HAL_RCC_AFIO_CLK_ENABLE(); - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Pin = GPIO_PIN_5; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - GPIO_InitStruct.Pin = GPIO_PIN_6; - #else - GPIO_InitStruct.Alternate = GPIO_AF9_CAN2; - GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - #endif - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - } - if (_pins == DEF) { - __HAL_RCC_GPIOB_CLK_ENABLE(); - #if defined(__HAL_RCC_AFIO_CLK_ENABLE) - __HAL_AFIO_REMAP_CAN2_DISABLE(); // To use PB12/13 pins for CAN2. - __HAL_RCC_AFIO_CLK_ENABLE(); - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Pin = GPIO_PIN_12; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - GPIO_InitStruct.Pin = GPIO_PIN_13; - #else - GPIO_InitStruct.Alternate = GPIO_AF9_CAN2; - GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - #endif - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - } // NVIC configuration for CAN2 Reception complete interrupt - HAL_NVIC_SetPriority(CAN2_RX0_IRQn, 15, 0); // 15 is lowest possible priority + HAL_NVIC_SetPriority(CAN2_RX0_IRQn, preemptPriority, subPriority); HAL_NVIC_EnableIRQ(CAN2_RX0_IRQn ); // NVIC configuration for CAN2 Transmission complete interrupt - HAL_NVIC_SetPriority(CAN2_TX_IRQn, 15, 0); // 15 is lowest possible priority + HAL_NVIC_SetPriority(CAN2_TX_IRQn, preemptPriority, subPriority); HAL_NVIC_EnableIRQ(CAN2_TX_IRQn); - n_pCanHandle->Instance = CAN2; + _can.bus = 2; } #endif #ifdef CAN3 - else if (_canPort == CAN3) + else if (_can.handle.Instance == CAN3) { //CAN3 __HAL_RCC_CAN3_CLK_ENABLE(); - if (_pins == ALT) - { - __HAL_RCC_GPIOB_CLK_ENABLE(); - GPIO_InitStruct.Alternate = GPIO_AF11_CAN3; - GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - } - if (_pins == DEF) - { - __HAL_RCC_GPIOA_CLK_ENABLE(); - GPIO_InitStruct.Alternate = GPIO_AF11_CAN3; - GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_15; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - } // NVIC configuration for CAN3 Reception complete interrupt - HAL_NVIC_SetPriority(CAN3_RX0_IRQn, 15, 0); // 15 is lowest possible priority + HAL_NVIC_SetPriority(CAN3_RX0_IRQn, preemptPriority, subPriority); HAL_NVIC_EnableIRQ(CAN3_RX0_IRQn ); // NVIC configuration for CAN3 Transmission complete interrupt - HAL_NVIC_SetPriority(CAN3_TX_IRQn, 15, 0); // 15 is lowest possible priority + HAL_NVIC_SetPriority(CAN3_TX_IRQn, preemptPriority, subPriority); HAL_NVIC_EnableIRQ(CAN3_TX_IRQn); - n_pCanHandle->Instance = CAN3; + _can.bus = 3; + } +#endif + + setAutoRetransmission(retransmission); + + filtersInitialized = false; + + //try to start in case baudrate was set earlier + setBaudRate(baudrate); +} + +void STM32_CAN::end() +{ + if(!hasPeripheral()) + { + return; + } + + stop(); + + disableMBInterrupts(); + + if (_can.handle.Instance == CAN1) + { + __HAL_RCC_CAN1_CLK_DISABLE(); + } +#ifdef CAN2 + else if (_can.handle.Instance == CAN2) + { + __HAL_RCC_CAN2_CLK_DISABLE(); + //only disable CAN1 clock if its not used + if(canObj[CAN1_INDEX] == nullptr) + { + __HAL_RCC_CAN1_CLK_DISABLE(); + } + } +#endif +#ifdef CAN3 + else if (_can.handle.Instance == CAN3) + { + __HAL_RCC_CAN3_CLK_DISABLE(); } #endif - n_pCanHandle->Init.TimeTriggeredMode = DISABLE; - n_pCanHandle->Init.AutoBusOff = DISABLE; - n_pCanHandle->Init.AutoWakeUp = DISABLE; - if (retransmission){ n_pCanHandle->Init.AutoRetransmission = ENABLE; } - else { n_pCanHandle->Init.AutoRetransmission = DISABLE; } - n_pCanHandle->Init.ReceiveFifoLocked = DISABLE; - n_pCanHandle->Init.TransmitFifoPriority = ENABLE; - n_pCanHandle->Init.Mode = CAN_MODE_NORMAL; + /** un-init pins, enable tx PULLUP for weak driving of recessive state */ + pin_function(rx, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, GPIO_AF_NONE)); + if(tx != NC) + pin_function(tx, STM_PIN_DATA(STM_MODE_INPUT, GPIO_PULLUP, GPIO_AF_NONE)); + + freeBuffers(); + + freePeripheral(); + + _canIsActive = false; } void STM32_CAN::setBaudRate(uint32_t baud) { + baudrate = baud; + + if(!hasPeripheral()) + { + return; + } // Calculate and set baudrate - calculateBaudrate( n_pCanHandle, baud ); + if(!calculateBaudrate( baud )) + { + return; + } + + // (re)-start + stop(); + start(); +} +void STM32_CAN::start() +{ // Initializes CAN - HAL_CAN_Init( n_pCanHandle ); + HAL_CAN_Init( &_can.handle ); initializeFilters(); // Start the CAN peripheral - HAL_CAN_Start( n_pCanHandle ); + HAL_CAN_Start( &_can.handle ); + + // Activate CAN notifications + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + HAL_CAN_ActivateNotification( &_can.handle, CAN_IT_RX_FIFO1_MSG_PENDING); + #else + HAL_CAN_ActivateNotification( &_can.handle, CAN_IT_RX_FIFO0_MSG_PENDING); + HAL_CAN_ActivateNotification( &_can.handle, CAN_IT_TX_MAILBOX_EMPTY); + #endif +} - // Activate CAN RX notification - HAL_CAN_ActivateNotification( n_pCanHandle, CAN_IT_RX_FIFO0_MSG_PENDING); +void STM32_CAN::stop() +{ + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + HAL_CAN_DeactivateNotification( &_can.handle, CAN_IT_RX_FIFO1_MSG_PENDING); + #else + HAL_CAN_DeactivateNotification( &_can.handle, CAN_IT_RX_FIFO0_MSG_PENDING); + HAL_CAN_DeactivateNotification( &_can.handle, CAN_IT_TX_MAILBOX_EMPTY); + #endif - // Activate CAN TX notification - HAL_CAN_ActivateNotification( n_pCanHandle, CAN_IT_TX_MAILBOX_EMPTY); + /** Calls Stop internally, clears all errors */ + HAL_CAN_DeInit( &_can.handle ); } + +/**------------------------------------------------------------- + * post begin(), setup filters, data transfer + * ------------------------------------------------------------- + */ + bool STM32_CAN::write(CAN_message_t &CAN_tx_msg, bool sendMB) { bool ret = true; uint32_t TxMailbox; CAN_TxHeaderTypeDef TxHeader; + if(!_can.handle.Instance) return false; - __HAL_CAN_DISABLE_IT(n_pCanHandle, CAN_IT_TX_MAILBOX_EMPTY); + #if !defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) + __HAL_CAN_DISABLE_IT(&_can.handle, CAN_IT_TX_MAILBOX_EMPTY); + #endif if (CAN_tx_msg.flags.extended == 1) // Extended ID when CAN_tx_msg.flags.extended is 1 { @@ -314,7 +566,7 @@ bool STM32_CAN::write(CAN_message_t &CAN_tx_msg, bool sendMB) TxHeader.TransmitGlobalTime = DISABLE; - if(HAL_CAN_AddTxMessage( n_pCanHandle, &TxHeader, CAN_tx_msg.buf, &TxMailbox) != HAL_OK) + if(HAL_CAN_AddTxMessage( &_can.handle, &TxHeader, CAN_tx_msg.buf, &TxMailbox) != HAL_OK) { /* in normal situation we add up the message to TX ring buffer, if there is no free TX mailbox. But the TX mailbox interrupt is using this same function to move the messages from ring buffer to empty TX mailboxes, so for that use case, there is this check */ @@ -327,132 +579,283 @@ bool STM32_CAN::write(CAN_message_t &CAN_tx_msg, bool sendMB) } else { ret = false; } } - __HAL_CAN_ENABLE_IT(n_pCanHandle, CAN_IT_TX_MAILBOX_EMPTY); + + #if !defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) + __HAL_CAN_ENABLE_IT(&_can.handle, CAN_IT_TX_MAILBOX_EMPTY); + #endif return ret; } bool STM32_CAN::read(CAN_message_t &CAN_rx_msg) { bool ret; - __HAL_CAN_DISABLE_IT(n_pCanHandle, CAN_IT_RX_FIFO0_MSG_PENDING); + if(!_can.handle.Instance) return false; + + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + __HAL_CAN_DISABLE_IT(&_can.handle, CAN_IT_RX_FIFO1_MSG_PENDING); + #else + __HAL_CAN_DISABLE_IT(&_can.handle, CAN_IT_RX_FIFO0_MSG_PENDING); +#endif + ret = removeFromRingBuffer(rxRing, CAN_rx_msg); - __HAL_CAN_ENABLE_IT(n_pCanHandle, CAN_IT_RX_FIFO0_MSG_PENDING); + + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + __HAL_CAN_ENABLE_IT(&_can.handle, CAN_IT_RX_FIFO1_MSG_PENDING); + #else + __HAL_CAN_ENABLE_IT(&_can.handle, CAN_IT_RX_FIFO0_MSG_PENDING); +#endif return ret; } +uint8_t STM32_CAN::getFilterBankCount(IDE std_ext) +{ + (void)std_ext; + if(_can.handle.Instance == nullptr) return 0; + #ifdef CAN2 + if(_can.handle.Instance == CAN1) + { + return STM32_CAN_CAN2_FILTER_OFFSET; + } + if(_can.handle.Instance == CAN2) + { + return STM32_CAN_DUAL_CAN_FILTER_COUNT - STM32_CAN_CAN2_FILTER_OFFSET; + } + #endif + return STM32_CAN_SINGLE_CAN_FILTER_COUNT; +} + +static uint32_t format32bitFilter(uint32_t id, IDE std_ext, bool mask) +{ + uint32_t id_reg; + if (std_ext == AUTO) + { + std_ext = (id <= 0x7FF) ? STD : EXT; + } + if (std_ext == STD) + { + id <<= 18; + } + id_reg = id << 3; + //set IDE bit + if (mask || std_ext == EXT) + { + id_reg |= (1 << 2); + } + return id_reg; +} + +static uint32_t format16bitFilter(uint32_t id, IDE std_ext, bool mask) +{ + uint32_t id_reg; + if (std_ext == AUTO) + { + std_ext = (id <= 0x7FF) ? STD : EXT; + } + if (std_ext == STD) + { + id <<= 18; + } + //set STID + id_reg = (id >> (18-5)) & 0xFFE0UL; + //set EXTI [17:15] + id_reg |= (id >> (18-3)) & 0x003UL; + //set IDE bit + if (mask || std_ext == EXT) + { + id_reg |= (1 << 3); + } + return id_reg; +} + +bool STM32_CAN::setFilter(uint8_t bank_num, bool enabled, FILTER_ACTION action) +{ + CAN_TypeDef *can_ip = _can.handle.Instance; + if(!_can.handle.Instance) return false; + /** CAN2 shares filter banks with CAN1 + * Driver allocates equal amount to each + * Filter Banks located at CAN1 base address + */ + #ifdef CAN2 + if(_can.handle.Instance == CAN2) + { + can_ip = CAN1; + bank_num += STM32_CAN_CAN2_FILTER_OFFSET; + } + #endif + + uint32_t filternbrbitpos = (uint32_t)1 << (bank_num & 0x1FU); + + /* Initialisation mode for the filter */ + SET_BIT(can_ip->FMR, CAN_FMR_FINIT); + + /* Filter Deactivation */ + CLEAR_BIT(can_ip->FA1R, filternbrbitpos); + + /* Filter FIFO assignment */ + switch (action) + { + case FILTER_ACTION::STORE_FIFO0: + CLEAR_BIT(can_ip->FFA1R, filternbrbitpos); + break; + case FILTER_ACTION::STORE_FIFO1: + SET_BIT(can_ip->FFA1R, filternbrbitpos); + break; + } + + /* Filter activation */ + if(enabled) + { + SET_BIT(can_ip->FA1R, filternbrbitpos); + } + /* Leave the initialisation mode for the filter */ + CLEAR_BIT(can_ip->FMR, CAN_FMR_FINIT); + return true; +} + +bool STM32_CAN::setFilterSingleMask(uint8_t bank_num, uint32_t id, uint32_t mask, IDE std_ext, FILTER_ACTION action, bool enabled) +{ + uint32_t id_reg = format32bitFilter(id, std_ext, false); + uint32_t mask_reg = format32bitFilter(mask, std_ext, true); + return setFilterRaw(bank_num, id_reg, mask_reg, CAN_FILTERMODE_IDMASK, CAN_FILTERSCALE_32BIT, action, enabled); +} + +bool STM32_CAN::setFilterDualID(uint8_t bank_num, uint32_t id1, uint32_t id2, IDE std_ext1, IDE std_ext2, FILTER_ACTION action, bool enabled) +{ + uint32_t id = format32bitFilter(id1, std_ext1, false); + uint32_t mask = format32bitFilter(id2, std_ext2, false); + return setFilterRaw(bank_num, id, mask, CAN_FILTERMODE_IDLIST, CAN_FILTERSCALE_32BIT, action, enabled); +} + +bool STM32_CAN::setFilterDualMask(uint8_t bank_num, uint32_t id1, uint32_t mask1, IDE std_ext1, uint32_t id2, uint32_t mask2, IDE std_ext2, FILTER_ACTION action, bool enabled) +{ + uint32_t id = (uint32_t)format16bitFilter(id1, std_ext1, false) | (((uint32_t)format16bitFilter(mask1, std_ext1, true)) << 16); + uint32_t mask = (uint32_t)format16bitFilter(id2, std_ext2, false) | (((uint32_t)format16bitFilter(mask2, std_ext2, true)) << 16); + return setFilterRaw(bank_num, id, mask, CAN_FILTERMODE_IDMASK, CAN_FILTERSCALE_16BIT, action, enabled); +} + +bool STM32_CAN::setFilterQuadID(uint8_t bank_num, uint32_t id1, IDE std_ext1, uint32_t id2, IDE std_ext2, uint32_t id3, IDE std_ext3, uint32_t id4, IDE std_ext4, FILTER_ACTION action, bool enabled) +{ + uint32_t id = (uint32_t)format16bitFilter(id1, std_ext1, false) | (((uint32_t)format16bitFilter(id2, std_ext2, false)) << 16); + uint32_t mask = (uint32_t)format16bitFilter(id3, std_ext3, false) | (((uint32_t)format16bitFilter(id4, std_ext4, false)) << 16); + return setFilterRaw(bank_num, id, mask, CAN_FILTERMODE_IDLIST, CAN_FILTERSCALE_16BIT, action, enabled); +} + bool STM32_CAN::setFilter(uint8_t bank_num, uint32_t filter_id, uint32_t mask, IDE std_ext, uint32_t filter_mode, uint32_t filter_scale, uint32_t fifo) +{ + /** NOTE: legacy, this function only implemented 32 bit scaling mode in mask mode, other modes will be broken*/ + if(filter_scale != CAN_FILTERSCALE_32BIT) + { + core_debug("WARNING: legacy function only implements 32 bit filter scale. Filter will be broken!\n"); + } + if(filter_scale != CAN_FILTERMODE_IDMASK) + { + core_debug("WARNING: legacy function only implements ID Mask mode. Filter will be broken!\n"); + } + /** re-implement broken implementation for legacy behaviour */ + uint32_t id_reg = format32bitFilter(filter_id, std_ext, false); + uint32_t mask_reg = format32bitFilter(mask, std_ext, true); + FILTER_ACTION action = (fifo==CAN_FILTER_FIFO0) ? FILTER_ACTION::STORE_FIFO0 : FILTER_ACTION::STORE_FIFO1; + return !setFilterRaw(bank_num, id_reg, mask_reg, filter_mode, filter_scale, action); +} + +bool STM32_CAN::setFilterRaw(uint8_t bank_num, uint32_t id, uint32_t mask, uint32_t filter_mode, uint32_t filter_scale, FILTER_ACTION action, bool enabled) { CAN_FilterTypeDef sFilterConfig; + if(!_can.handle.Instance) return false; sFilterConfig.FilterBank = bank_num; sFilterConfig.FilterMode = filter_mode; sFilterConfig.FilterScale = filter_scale; - sFilterConfig.FilterFIFOAssignment = fifo; - sFilterConfig.FilterActivation = ENABLE; - - if (std_ext == STD || (std_ext == AUTO && filter_id <= 0x7FF)) + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + if(action == FILTER_ACTION::STORE_FIFO0) { - // Standard ID can be only 11 bits long - sFilterConfig.FilterIdHigh = (uint16_t) (filter_id << 5); - sFilterConfig.FilterIdLow = 0; - sFilterConfig.FilterMaskIdHigh = (uint16_t) (mask << 5); - sFilterConfig.FilterMaskIdLow = CAN_ID_EXT; + core_debug("WARNING: RX0 IRQ is blocked by USB Driver. Events only handled by polling and RX1 events!\n"); } - else + #endif + switch (action) { - // Extended ID - sFilterConfig.FilterIdLow = (uint16_t) (filter_id << 3); - sFilterConfig.FilterIdLow |= CAN_ID_EXT; - sFilterConfig.FilterIdHigh = (uint16_t) (filter_id >> 13); - sFilterConfig.FilterMaskIdLow = (uint16_t) (mask << 3); - sFilterConfig.FilterMaskIdLow |= CAN_ID_EXT; - sFilterConfig.FilterMaskIdHigh = (uint16_t) (mask >> 13); + case FILTER_ACTION::STORE_FIFO0: + sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; + break; + case FILTER_ACTION::STORE_FIFO1: + sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO1; + break; } + sFilterConfig.FilterActivation = enabled ? ENABLE : DISABLE; - // Enable filter - if (HAL_CAN_ConfigFilter( n_pCanHandle, &sFilterConfig ) != HAL_OK) + sFilterConfig.FilterIdLow = id & 0xFFFFUL; + sFilterConfig.FilterIdHigh = id >> 16; + sFilterConfig.FilterMaskIdLow = mask & 0xFFFFUL; + sFilterConfig.FilterMaskIdHigh = mask >> 16; + + #ifdef CAN2 + sFilterConfig.SlaveStartFilterBank = STM32_CAN_CAN2_FILTER_OFFSET; + if(_can.handle.Instance == CAN2) { - return 1; + sFilterConfig.FilterBank += STM32_CAN_CAN2_FILTER_OFFSET; + if(sFilterConfig.FilterBank >= STM32_CAN_DUAL_CAN_FILTER_COUNT) + return false; } else + #endif + if(sFilterConfig.FilterBank >= STM32_CAN_SINGLE_CAN_FILTER_COUNT) { - return 0; + return false; } + // Enable filter + return (HAL_CAN_ConfigFilter( &_can.handle, &sFilterConfig ) == HAL_OK); } + +/**------------------------------------------------------------- + * Teensy FlexCAN compatibility functions + * ------------------------------------------------------------- + */ + void STM32_CAN::setMBFilter(CAN_BANK bank_num, CAN_FLTEN input) { - CAN_FilterTypeDef sFilterConfig; - sFilterConfig.FilterBank = uint8_t(bank_num); - if (input == ACCEPT_ALL) { sFilterConfig.FilterActivation = ENABLE; } - else { sFilterConfig.FilterActivation = DISABLE; } - - HAL_CAN_ConfigFilter(n_pCanHandle, &sFilterConfig); + setFilter(bank_num, (input == ACCEPT_ALL)); } void STM32_CAN::setMBFilter(CAN_FLTEN input) { - CAN_FilterTypeDef sFilterConfig; - uint8_t max_bank_num = 27; - uint8_t min_bank_num = 0; - #ifdef CAN2 - if (_canPort == CAN1){ max_bank_num = 13;} - else if (_canPort == CAN2){ min_bank_num = 14;} - #endif - for (uint8_t bank_num = min_bank_num ; bank_num <= max_bank_num ; bank_num++) + for (uint8_t bank_num = 0 ; bank_num < STM32_CAN_SINGLE_CAN_FILTER_COUNT ; bank_num++) { - sFilterConfig.FilterBank = bank_num; - if (input == ACCEPT_ALL) { sFilterConfig.FilterActivation = ENABLE; } - else { sFilterConfig.FilterActivation = DISABLE; } - HAL_CAN_ConfigFilter(n_pCanHandle, &sFilterConfig); + setFilter(bank_num, (input == ACCEPT_ALL)); } } bool STM32_CAN::setMBFilterProcessing(CAN_BANK bank_num, uint32_t filter_id, uint32_t mask, IDE std_ext) { // just convert the MB number enum to bank number. - return setFilter(uint8_t(bank_num), filter_id, mask, std_ext); + return !setFilterSingleMask(uint8_t(bank_num), filter_id, mask, std_ext); } bool STM32_CAN::setMBFilter(CAN_BANK bank_num, uint32_t id1, IDE std_ext) { // by setting the mask to 0x1FFFFFFF we only filter the ID set as Filter ID. - return setFilter(uint8_t(bank_num), id1, 0x1FFFFFFF, std_ext); + return !setFilterSingleMask(uint8_t(bank_num), id1, 0x1FFFFFFF, std_ext); } bool STM32_CAN::setMBFilter(CAN_BANK bank_num, uint32_t id1, uint32_t id2, IDE std_ext) { - // if we set the filter mode as IDLIST, the mask becomes filter ID too. So we can filter two totally independent IDs in same bank. - return setFilter(uint8_t(bank_num), id1, id2, AUTO, CAN_FILTERMODE_IDLIST, std_ext); + return !setFilterDualID(uint8_t(bank_num), id1, id2, std_ext, std_ext); } -// TBD, do this using "setFilter" -function void STM32_CAN::initializeFilters() { - CAN_FilterTypeDef sFilterConfig; - // We set first bank to accept all RX messages - sFilterConfig.FilterBank = 0; - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; - sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; - sFilterConfig.FilterIdHigh = 0x0000; - sFilterConfig.FilterIdLow = 0x0000; - sFilterConfig.FilterMaskIdHigh = 0x0000; - sFilterConfig.FilterMaskIdLow = 0x0000; - sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; - sFilterConfig.FilterActivation = ENABLE; - #ifdef CAN2 - // Filter banks from 14 to 27 are for Can2, so first for Can2 is bank 14. This is not relevant for devices with only one CAN - if (_canPort == CAN1) - { - sFilterConfig.SlaveStartFilterBank = 14; - } - if (_canPort == CAN2) + if(filtersInitialized) return; + filtersInitialized = true; + + /** Let everything in by default */ + setFilterRaw(0, 0UL, 0UL, CAN_FILTERMODE_IDMASK, CAN_FILTERSCALE_32BIT, + FILTER_ACTION::CAN_FILTER_DEFAULT_ACTION, true); + + /** turn off all other filters that might sill be setup from before */ + for (uint8_t bank_num = 1 ; bank_num < STM32_CAN_SINGLE_CAN_FILTER_COUNT ; bank_num++) { - sFilterConfig.FilterBank = 14; + setFilter(bank_num, false); } - #endif - - HAL_CAN_ConfigFilter(n_pCanHandle, &sFilterConfig); } void STM32_CAN::initializeBuffers() @@ -473,6 +876,21 @@ void STM32_CAN::initializeBuffers() initRingBuffer(rxRing, rx_buffer, sizeRxBuffer); } +void STM32_CAN::freeBuffers() +{ + txRing.head = 0; + txRing.tail = 0; + txRing.buffer = nullptr; + delete[] tx_buffer; + tx_buffer = nullptr; + + rxRing.head = 0; + rxRing.tail = 0; + rxRing.buffer = nullptr; + delete[] rx_buffer; + rx_buffer = nullptr; +} + void STM32_CAN::initRingBuffer(RingbufferTypeDef &ring, volatile CAN_message_t *buffer, uint32_t size) { ring.buffer = buffer; @@ -538,8 +956,8 @@ uint32_t STM32_CAN::ringBufferCount(RingbufferTypeDef &ring) return((uint32_t)entries); } -void STM32_CAN::setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescaler, uint8_t timeseg1, - uint8_t timeseg2, uint8_t sjw) +void STM32_CAN::setBaudRateValues(uint16_t prescaler, uint8_t timeseg1, + uint8_t timeseg2, uint8_t sjw) { uint32_t _SyncJumpWidth = 0; uint32_t _TimeSeg1 = 0; @@ -659,39 +1077,39 @@ void STM32_CAN::setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescal } _Prescaler = prescaler; - CanHandle->Init.SyncJumpWidth = _SyncJumpWidth; - CanHandle->Init.TimeSeg1 = _TimeSeg1; - CanHandle->Init.TimeSeg2 = _TimeSeg2; - CanHandle->Init.Prescaler = _Prescaler; + _can.handle.Init.SyncJumpWidth = _SyncJumpWidth; + _can.handle.Init.TimeSeg1 = _TimeSeg1; + _can.handle.Init.TimeSeg2 = _TimeSeg2; + _can.handle.Init.Prescaler = _Prescaler; } template -bool STM32_CAN::lookupBaudrate(CAN_HandleTypeDef *CanHandle, int baud, const T(&table)[N]) { +bool STM32_CAN::lookupBaudrate(int baud, const T(&table)[N]) { for (size_t i = 0; i < N; i++) { if (baud != (int)table[i].baudrate) { continue; } /* for the best chance at interoperability, use the widest SJW possible */ - setBaudRateValues(CanHandle, table[i].prescaler, table[i].timeseg1, table[i].timeseg2, 4); + setBaudRateValues(table[i].prescaler, table[i].timeseg1, table[i].timeseg2, 4); return true; } return false; } -void STM32_CAN::calculateBaudrate(CAN_HandleTypeDef *CanHandle, int baud) +bool STM32_CAN::calculateBaudrate(int baud) { uint8_t bs1; uint8_t bs2; uint16_t prescaler; - const uint32_t frequency = getAPB1Clock(); + const uint32_t frequency = getCanPeripheralClock(); if (frequency == 48000000) { - if (lookupBaudrate(CanHandle, baud, BAUD_RATE_TABLE_48M)) return; + if (lookupBaudrate(baud, BAUD_RATE_TABLE_48M)) return true; } else if (frequency == 45000000) { - if (lookupBaudrate(CanHandle, baud, BAUD_RATE_TABLE_45M)) return; + if (lookupBaudrate(baud, BAUD_RATE_TABLE_45M)) return true; } /* this loop seeks a precise baudrate match, with the sample point positioned @@ -719,105 +1137,90 @@ void STM32_CAN::calculateBaudrate(CAN_HandleTypeDef *CanHandle, int baud) if (baud_cur != baud) continue; - setBaudRateValues(CanHandle, prescaler, bs1, bs2, 4); - return; + setBaudRateValues(prescaler, bs1, bs2, 4); + return true; } } } /* uhoh, failed to calculate an acceptable baud rate... */ + return false; } -uint32_t STM32_CAN::getAPB1Clock() +uint32_t STM32_CAN::getCanPeripheralClock() { - RCC_ClkInitTypeDef clkInit; - uint32_t flashLatency; - HAL_RCC_GetClockConfig(&clkInit, &flashLatency); - - uint32_t hclkClock = HAL_RCC_GetHCLKFreq(); - uint8_t clockDivider = 1; - switch (clkInit.APB1CLKDivider) - { - case RCC_HCLK_DIV1: - clockDivider = 1; - break; - case RCC_HCLK_DIV2: - clockDivider = 2; - break; - case RCC_HCLK_DIV4: - clockDivider = 4; - break; - case RCC_HCLK_DIV8: - clockDivider = 8; - break; - case RCC_HCLK_DIV16: - clockDivider = 16; - break; - default: - // should not happen - break; - } - - uint32_t apb1Clock = hclkClock / clockDivider; - - return apb1Clock; + //All bxCAN get clocked by APB1 / PCLK1 + return HAL_RCC_GetPCLK1Freq(); } void STM32_CAN::enableMBInterrupts() { - if (n_pCanHandle->Instance == CAN1) + if (_can.handle.Instance == CAN1) { + #ifdef CAN1_IRQn_AIO + HAL_NVIC_EnableIRQ(CAN1_IRQn_AIO); + #else + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn); + #else HAL_NVIC_EnableIRQ(CAN1_TX_IRQn); + HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn); + #endif + #endif /** else defined(CAN1_IRQn_AIO) */ } #ifdef CAN2 - else if (n_pCanHandle->Instance == CAN2) + else if (_can.handle.Instance == CAN2) { HAL_NVIC_EnableIRQ(CAN2_TX_IRQn); + HAL_NVIC_EnableIRQ(CAN2_RX0_IRQn); } #endif #ifdef CAN3 - else if (n_pCanHandle->Instance == CAN3) + else if (_can.handle.Instance == CAN3) { HAL_NVIC_EnableIRQ(CAN3_TX_IRQn); + HAL_NVIC_EnableIRQ(CAN3_RX0_IRQn); } #endif } void STM32_CAN::disableMBInterrupts() { - if (n_pCanHandle->Instance == CAN1) + if (_can.handle.Instance == CAN1) { + #ifdef CAN1_IRQn_AIO + #if defined(HAL_CEC_MODULE_ENABLED) && defined(STM32_CAN1_SHARED_WITH_CEC) + //only disable if cec instance is not set/used + if(!phcec) + #endif + { + HAL_NVIC_DisableIRQ(CAN1_IRQn_AIO); + } + #else + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn); + #else HAL_NVIC_DisableIRQ(CAN1_TX_IRQn); + HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn); + #endif + #endif /** else defined(CAN1_IRQn_AIO) */ } #ifdef CAN2 - else if (n_pCanHandle->Instance == CAN2) + else if (_can.handle.Instance == CAN2) { HAL_NVIC_DisableIRQ(CAN2_TX_IRQn); + HAL_NVIC_DisableIRQ(CAN2_RX0_IRQn); } #endif #ifdef CAN3 - else if (n_pCanHandle->Instance == CAN3) + else if (_can.handle.Instance == CAN3) { HAL_NVIC_DisableIRQ(CAN3_TX_IRQn); + HAL_NVIC_DisableIRQ(CAN3_RX0_IRQn); } #endif } -void STM32_CAN::enableLoopBack( bool yes ) { - if (yes) { n_pCanHandle->Init.Mode = CAN_MODE_LOOPBACK; } - else { n_pCanHandle->Init.Mode = CAN_MODE_NORMAL; } -} - -void STM32_CAN::enableSilentMode( bool yes ) { - if (yes) { n_pCanHandle->Init.Mode = CAN_MODE_SILENT; } - else { n_pCanHandle->Init.Mode = CAN_MODE_NORMAL; } -} - -void STM32_CAN::enableSilentLoopBack( bool yes ) { - if (yes) { n_pCanHandle->Init.Mode = CAN_MODE_SILENT_LOOPBACK; } - else { n_pCanHandle->Init.Mode = CAN_MODE_NORMAL; } -} - void STM32_CAN::enableFIFO(bool status) { //Nothing to do here. The FIFO is on by default. This is just to work with code made for Teensy FlexCan. @@ -830,181 +1233,162 @@ void STM32_CAN::enableFIFO(bool status) // There is 3 TX mailboxes. Each one has own transmit complete callback function, that we use to pull next message from TX ringbuffer to be sent out in TX mailbox. extern "C" void HAL_CAN_TxMailbox0CompleteCallback( CAN_HandleTypeDef *CanHandle ) { + stm32_can_t * canObj = get_can_obj(CanHandle); + STM32_CAN * _can = (STM32_CAN *)canObj->__this; CAN_message_t txmsg; - // use correct CAN instance - if (CanHandle->Instance == CAN1) - { - if (_CAN1->removeFromRingBuffer(_CAN1->txRing, txmsg)) - { - _CAN1->write(txmsg, true); - } - } -#ifdef CAN2 - else if (CanHandle->Instance == CAN2) - { - if (_CAN2->removeFromRingBuffer(_CAN2->txRing, txmsg)) - { - _CAN2->write(txmsg, true); - } - } -#endif -#ifdef CAN3 - else if (CanHandle->Instance == CAN3) + + if (_can->removeFromRingBuffer(_can->txRing, txmsg)) { - if (_CAN3->removeFromRingBuffer(_CAN3->txRing, txmsg)) - { - _CAN3->write(txmsg, true); - } + _can->write(txmsg, true); } -#endif } extern "C" void HAL_CAN_TxMailbox1CompleteCallback( CAN_HandleTypeDef *CanHandle ) { + stm32_can_t * canObj = get_can_obj(CanHandle); + STM32_CAN * _can = (STM32_CAN *)canObj->__this; CAN_message_t txmsg; - // use correct CAN instance - if (CanHandle->Instance == CAN1) - { - if (_CAN1->removeFromRingBuffer(_CAN1->txRing, txmsg)) - { - _CAN1->write(txmsg, true); - } - } -#ifdef CAN2 - else if (CanHandle->Instance == CAN2) - { - if (_CAN2->removeFromRingBuffer(_CAN2->txRing, txmsg)) - { - _CAN2->write(txmsg, true); - } - } -#endif -#ifdef CAN3 - else if (CanHandle->Instance == CAN3) + + if (_can->removeFromRingBuffer(_can->txRing, txmsg)) { - if (_CAN3->removeFromRingBuffer(_CAN3->txRing, txmsg)) - { - _CAN3->write(txmsg, true); - } + _can->write(txmsg, true); } -#endif } extern "C" void HAL_CAN_TxMailbox2CompleteCallback( CAN_HandleTypeDef *CanHandle ) { + stm32_can_t * canObj = get_can_obj(CanHandle); + STM32_CAN * _can = (STM32_CAN *)canObj->__this; CAN_message_t txmsg; - // use correct CAN instance - if (CanHandle->Instance == CAN1) - { - if (_CAN1->removeFromRingBuffer(_CAN1->txRing, txmsg)) - { - _CAN1->write(txmsg, true); - } - } -#ifdef CAN2 - else if (CanHandle->Instance == CAN2) - { - if (_CAN2->removeFromRingBuffer(_CAN2->txRing, txmsg)) - { - _CAN2->write(txmsg, true); - } - } -#endif -#ifdef CAN3 - else if (CanHandle->Instance == CAN3) + + if (_can->removeFromRingBuffer(_can->txRing, txmsg)) { - if (_CAN3->removeFromRingBuffer(_CAN3->txRing, txmsg)) - { - _CAN3->write(txmsg, true); - } + _can->write(txmsg, true); } -#endif } // This is called by RX0_IRQHandler when there is message at RX FIFO0 buffer +#if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) +extern "C" void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *CanHandle) +#else extern "C" void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *CanHandle) +#endif { + stm32_can_t * canObj = get_can_obj(CanHandle); + STM32_CAN * _can = (STM32_CAN *)canObj->__this; CAN_message_t rxmsg; CAN_RxHeaderTypeDef RxHeader; //bool state = Disable_Interrupts(); // move the message from RX FIFO0 to RX ringbuffer - if (HAL_CAN_GetRxMessage( CanHandle, CAN_RX_FIFO0, &RxHeader, rxmsg.buf ) == HAL_OK) + #if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) + const uint32_t fifo = CAN_RX_FIFO1; + #else + const uint32_t fifo = CAN_RX_FIFO0; + #endif + do { - if ( RxHeader.IDE == CAN_ID_STD ) + if (HAL_CAN_GetRxMessage( CanHandle, fifo, &RxHeader, rxmsg.buf ) == HAL_OK) { - rxmsg.id = RxHeader.StdId; - rxmsg.flags.extended = 0; - } - else - { - rxmsg.id = RxHeader.ExtId; - rxmsg.flags.extended = 1; - } + if ( RxHeader.IDE == CAN_ID_STD ) + { + rxmsg.id = RxHeader.StdId; + rxmsg.flags.extended = 0; + } + else + { + rxmsg.id = RxHeader.ExtId; + rxmsg.flags.extended = 1; + } - rxmsg.flags.remote = RxHeader.RTR; - rxmsg.mb = RxHeader.FilterMatchIndex; - rxmsg.timestamp = RxHeader.Timestamp; - rxmsg.len = RxHeader.DLC; + rxmsg.flags.remote = RxHeader.RTR; + rxmsg.mb = RxHeader.FilterMatchIndex; + rxmsg.timestamp = RxHeader.Timestamp; + rxmsg.len = RxHeader.DLC; - // use correct ring buffer based on CAN instance - if (CanHandle->Instance == CAN1) - { - rxmsg.bus = 1; - _CAN1->addToRingBuffer(_CAN1->rxRing, rxmsg); - } -#ifdef CAN2 - else if (CanHandle->Instance == CAN2) - { - rxmsg.bus = 2; - _CAN2->addToRingBuffer(_CAN2->rxRing, rxmsg); - } -#endif -#ifdef CAN3 - else if (CanHandle->Instance == CAN3) - { - rxmsg.bus = 3; - _CAN3->addToRingBuffer(_CAN3->rxRing, rxmsg); + rxmsg.bus = canObj->bus; + _can->addToRingBuffer(_can->rxRing, rxmsg); } -#endif - } + } while(HAL_CAN_GetRxFifoFillLevel(CanHandle, fifo)); //Enable_Interrupts(state); } +#ifdef CAN1_IRQHandler_AIO + +extern "C" void CAN1_IRQHandler_AIO(void) +{ + if(canObj[CAN1_INDEX]) { + HAL_CAN_IRQHandler(&canObj[CAN1_INDEX]->handle); + } + #if defined(HAL_CEC_MODULE_ENABLED) && defined(STM32_CAN1_SHARED_WITH_CEC) + if(phcec) + { + HAL_CEC_IRQHandler(phcec); + } + #endif +} + +#else + // RX IRQ handlers +#if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) +/** If USB blocks TX and RX0 IRQs, will use RX1 by default*/ +extern "C" void CAN1_RX1_IRQHandler(void) +#else extern "C" void CAN1_RX0_IRQHandler(void) +#endif { - HAL_CAN_IRQHandler(&hcan1); + if(canObj[CAN1_INDEX]) { + HAL_CAN_IRQHandler(&canObj[CAN1_INDEX]->handle); + } } #ifdef CAN2 extern "C" void CAN2_RX0_IRQHandler(void) { - HAL_CAN_IRQHandler(&hcan2); + if(canObj[CAN2_INDEX]) { + HAL_CAN_IRQHandler(&canObj[CAN2_INDEX]->handle); + } } #endif #ifdef CAN3 extern "C" void CAN3_RX0_IRQHandler(void) { - HAL_CAN_IRQHandler(&hcan3); + if(canObj[CAN3_INDEX]) { + HAL_CAN_IRQHandler(&canObj[CAN3_INDEX]->handle); + } } #endif // TX IRQ handlers +#if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && defined(STM32_CAN_USB_WORKAROUND_POLLING) +/** If USB blocks TX and RX0 IRQs, need to poll for Tx events*/ +extern "C" void STM32_CAN_Poll_IRQ_Handler(void) +#else extern "C" void CAN1_TX_IRQHandler(void) +#endif { - HAL_CAN_IRQHandler(&hcan1); + if(canObj[CAN1_INDEX]) { + HAL_CAN_IRQHandler(&canObj[CAN1_INDEX]->handle); + } } #ifdef CAN2 extern "C" void CAN2_TX_IRQHandler(void) { - HAL_CAN_IRQHandler(&hcan2); + if(canObj[CAN2_INDEX]) { + HAL_CAN_IRQHandler(&canObj[CAN2_INDEX]->handle); + } } #endif #ifdef CAN3 extern "C" void CAN3_TX_IRQHandler(void) { - HAL_CAN_IRQHandler(&hcan3); + if(canObj[CAN3_INDEX]) { + HAL_CAN_IRQHandler(&canObj[CAN3_INDEX]->handle); + } } #endif + +#endif /* CAN1_IRQHandler_AIO */ + diff --git a/STM32_CAN.h b/STM32_CAN.h index d42519c..5df758a 100644 --- a/STM32_CAN.h +++ b/STM32_CAN.h @@ -19,23 +19,131 @@ to same folder with sketch and haven #define HAL_CAN_MODULE_ENABLED there. See e #ifndef STM32_CAN_H #define STM32_CAN_H -// couple of workarounds +#include + +/** Handling special cases for IRQ Handlers */ +#if defined(STM32F0xx) +#if defined(STM32F042x6) || defined(STM32F072xB) || defined(STM32F091xC) || defined(STM32F098xx) + + /** + * NOTE: STM32F0 share IRQ Handler with HDMI CEC + * and there is only a single IRQ Handler not 4 + * CEC_CAN_IRQn | CEC_CAN_IRQHandler + * + * define all in one alias for IRQn and Handler + */ + #define CAN1_IRQn_AIO CEC_CAN_IRQn + #define CAN1_IRQHandler_AIO CEC_CAN_IRQHandler + /** + * NOTE: CAN IRQ is shared with CEC + * To use CEC with CAN declare: + * CEC_HandleTypeDef * phcec; + * and point to your CEC handle. + * Internal IRQ Handler will call CEC Handler as well. + */ + #define STM32_CAN1_SHARED_WITH_CEC + +#endif +#endif + +#if defined(STM32F1xx) +#if defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) +/** + * NOTE: STM32F103xx uses shared IRQ Handler with USB + * USB_HP_CAN1_TX_IRQn | USB_HP_CAN1_TX_IRQHandler + * USB_LP_CAN1_RX0_IRQn | USB_LP_CAN1_RX0_IRQHandler + * + * the CMSIS files define already aliases for the CAN *_IRQHandler and *_IRQn + * conforming with standard naming convention: + * CAN1_TX_IRQn | CAN1_TX_IRQHandler + * CAN1_RX0_IRQn | CAN1_RX0_IRQHandler + * + * when USB is enabled the USBDevice driver also implements these making a concurrent use impossible. + * + * Following are unaffected: + * CAN1_RX1_IRQn | CAN1_RX1_IRQHandler + * CAN1_SCE_IRQn | CAN1_SCE_IRQHandler + */ + +#ifdef USBCON +#define STM32_CAN1_TX_RX0_BLOCKED_BY_USB +#endif + +#endif +#endif + #if defined(STM32F3xx) - #define GPIO_AF9_CAN1 GPIO_AF9_CAN - #define CAN1_RX0_IRQn CAN_RX0_IRQn - #define CAN1_TX_IRQn CAN_TX_IRQn - #define GPIO_SPEED_FREQ_VERY_HIGH GPIO_SPEED_FREQ_HIGH - #define CAN1_TX_IRQHandler CAN_TX_IRQHandler +#if defined(STM32F302x8) || defined(STM32F302xC) || defined(STM32F302xE)\ + || defined(STM32F303xC) || defined(STM32F303xE) + + /** + * NOTE: STM32F3 with USB share IRQ Handler with it + * USB_HP_CAN_TX_IRQn | USB_HP_CAN_TX_IRQHandler + * USB_LP_CAN_RX0_IRQn | USB_LP_CAN_RX0_IRQHandler + * + * the CMSIS files define already aliases for the CAN *_IRQHandler and *_IRQn + * missing peripheral index: + * CAN_TX_IRQn | CAN_TX_IRQHandler + * CAN_RX0_IRQn | CAN_RX0_IRQHandler + * + * when USB is enabled the USBDevice driver also implements these making a concurrent use impossible. + * + * Following are unaffected: + * CAN_RX1_IRQn | CAN_RX1_IRQHandler + * CAN_SCE_IRQn | CAN_SCE_IRQHandler + * + * define more aliases with peripheral index + */ + + #define CAN1_TX_IRQn USB_HP_CAN_TX_IRQn + #define CAN1_RX0_IRQn USB_LP_CAN_RX0_IRQn + #define CAN1_RX1_IRQn CAN_RX1_IRQn + #define CAN1_SCE_IRQn CAN_SCE_IRQn + + #define CAN1_TX_IRQHandler USB_HP_CAN_TX_IRQHandler + #define CAN1_RX0_IRQHandler USB_LP_CAN_RX0_IRQHandler + #define CAN1_RX1_IRQHandler CAN_RX1_IRQHandler + #define CAN1_SCE_IRQHandler CAN_SCE_IRQHandler + + /** NOTE: USE_USB_INTERRUPT_REMAPPED may be used to use + * different USB IRQs and not block the CAN IRQ handlers */ + #if defined(USBCON) && !defined(USE_USB_INTERRUPT_REMAPPED) + #define STM32_CAN1_TX_RX0_BLOCKED_BY_USB + #endif + +#elif defined(STM32F303x8) || defined(STM32F328xx) || defined(STM32F334x8)\ + || defined(STM32F358xx) || defined(STM32F373xC)\ + || defined(STM32F378xx) || defined(STM32F398xx) + + /** + * NOTE: STM32F3 without USB define symbols without peripheral index + * define more aliases with peripheral index + */ + + #define CAN1_TX_IRQn CAN_TX_IRQn + #define CAN1_RX0_IRQn CAN_RX0_IRQn + #define CAN1_RX1_IRQn CAN_RX1_IRQn + #define CAN1_SCE_IRQn CAN_SCE_IRQn + + #define CAN1_TX_IRQHandler CAN_TX_IRQHandler #define CAN1_RX0_IRQHandler CAN_RX0_IRQHandler + #define CAN1_RX1_IRQHandler CAN_RX1_IRQHandler + #define CAN1_SCE_IRQHandler CAN_SCE_IRQHandler +#endif #endif -#if defined(STM32F0xx) - #define CAN1_TX_IRQn CEC_CAN_IRQn - #define CAN1_RX0_IRQn CEC_CAN_IRQn - #define CAN1_RX0_IRQHandler CEC_CAN_IRQHandler +#if defined(STM32_CAN1_TX_RX0_BLOCKED_BY_USB) && !defined(STM32_CAN_USB_WORKAROUND_POLLING) +#error "USB and CAN interrupts are shared on the F1/F3 platform, driver is not compatible with USBDevice of Arduino core. Can define STM32_CAN_USB_WORKAROUND_POLLING to disable error msg and call STM32_CAN_Poll_IRQ_Handler to poll for Tx IRQ events. Only use FIFO 1." +#elif defined(USBCON) && defined(STM32_CAN_USB_WORKAROUND_POLLING) +#warning "CAN IRQ Handler is used by USBDevice driver, call STM32_CAN_Poll_IRQ_Handler() frequently to handle CAN events." +extern "C" void STM32_CAN_Poll_IRQ_Handler(void); +#define CAN_FILTER_DEFAULT_FIFO CAN_FILTER_FIFO1 +#define CAN_FILTER_DEFAULT_ACTION STORE_FIFO1 +#else +#define CAN_FILTER_DEFAULT_FIFO CAN_FILTER_FIFO0 +#define CAN_FILTER_DEFAULT_ACTION STORE_FIFO0 #endif -#include // This struct is directly copied from Teensy FlexCAN library to retain compatibility with it. Not all are in use with STM32. // Source: https://github.com/tonton81/FlexCAN_T4/ @@ -138,28 +246,116 @@ typedef enum IDE { AUTO = 2 } IDE; +typedef struct { + void * __this; + CAN_HandleTypeDef handle; + uint32_t bus; +} stm32_can_t; + class STM32_CAN { public: + enum MODE { + NORMAL = CAN_MODE_NORMAL, + SILENT = CAN_MODE_SILENT, + SILENT_LOOPBACK = CAN_MODE_SILENT_LOOPBACK, + LOOPBACK = CAN_MODE_LOOPBACK + }; + + enum FILTER_ACTION { + STORE_FIFO0, + STORE_FIFO1, + }; + + enum TX_BUFFER_MODE { + FIFO = ENABLE, /** Sequencial transfers order */ + QUEUE = DISABLE /** Sequence based on msg ID priorites. Only effects hardware queue. */ + }; + + // Default buffer sizes are set to 16. But this can be changed by using constructor in main code. + STM32_CAN(uint32_t rx, uint32_t tx = PNUM_NOT_DEFINED, RXQUEUE_TABLE rxSize = RX_SIZE_16, TXQUEUE_TABLE txSize = TX_SIZE_16); + STM32_CAN(PinName rx, PinName tx = NC, RXQUEUE_TABLE rxSize = RX_SIZE_16, TXQUEUE_TABLE txSize = TX_SIZE_16); + STM32_CAN(CAN_TypeDef* canPort, RXQUEUE_TABLE rxSize = RX_SIZE_16, TXQUEUE_TABLE txSize = TX_SIZE_16); + //legacy for compatibility STM32_CAN(CAN_TypeDef* canPort, CAN_PINS pins, RXQUEUE_TABLE rxSize = RX_SIZE_16, TXQUEUE_TABLE txSize = TX_SIZE_16); + ~STM32_CAN(); +/**------------------------------------------------------------- + * setup functions + * no effect after begin() + * ------------------------------------------------------------- + */ + void setIRQPriority(uint32_t preemptPriority, uint32_t subPriority); + + /** send message again on arbitration failure */ + void setAutoRetransmission(bool enabled); + + /** If locked incoming msg is dropped when fifo is full, + * when unlocked last msg in fifo is overwritten + * 2nd arg has no effect, setting effects both fifos */ + void setRxFIFOLock(bool fifo0locked, bool fifo1locked = true); + void setTxBufferMode(TX_BUFFER_MODE mode); + void setTimestampCounter(bool enabled); + + void setMode(MODE mode); + void enableLoopBack(bool yes = 1); + void enableSilentMode(bool yes = 1); + void enableSilentLoopBack(bool yes = 1); + + void setAutoBusOffRecovery(bool enabled); + +/**------------------------------------------------------------- + * lifecycle functions + * setBaudRate may be called before or after begin + * ------------------------------------------------------------- + */ // Begin. By default the automatic retransmission is enabled. If it causes problems, use begin(false) to disable it. void begin(bool retransmission = false); + void end(void); + void setBaudRate(uint32_t baud); + +/**------------------------------------------------------------- + * post begin(), setup filters, data transfer + * ------------------------------------------------------------- + */ bool write(CAN_message_t &CAN_tx_msg, bool sendMB = false); bool read(CAN_message_t &CAN_rx_msg); - // Manually set STM32 filter bank parameters - bool setFilter(uint8_t bank_num, uint32_t filter_id, uint32_t mask, IDE = AUTO, uint32_t filter_mode = CAN_FILTERMODE_IDMASK, uint32_t filter_scale = CAN_FILTERSCALE_32BIT, uint32_t fifo = CAN_FILTER_FIFO0); - // Teensy FlexCAN style "set filter" -functions + + /** returns number of available filter banks. If hasSharedFilterBanks() is false counts may differ by id type. */ + uint8_t getFilterBankCount(IDE std_ext = STD); + /** returns if filter count and index are shared (true) or dedicated per id type (false) */ + bool hasSharedFilterBanks() { + return true; + } + + /** + * Manually set STM32 filter bank parameters + * These return true on success + */ + /** set filter state and action, keeps filter rules intact */ + bool setFilter(uint8_t bank_num, bool enabled, FILTER_ACTION action = CAN_FILTER_DEFAULT_ACTION); + bool setFilterSingleMask(uint8_t bank_num, uint32_t id, uint32_t mask, IDE std_ext, FILTER_ACTION action = CAN_FILTER_DEFAULT_ACTION, bool enabled = true); + bool setFilterDualID(uint8_t bank_num, uint32_t id1, uint32_t id2, IDE std_ext1, IDE std_ext2, FILTER_ACTION action = CAN_FILTER_DEFAULT_ACTION, bool enabled = true); + bool setFilterDualMask(uint8_t bank_num, uint32_t id1, uint32_t mask1, IDE std_ext1, uint32_t id2, uint32_t mask2, IDE std_ext2, FILTER_ACTION action = CAN_FILTER_DEFAULT_ACTION, bool enabled = true); + bool setFilterQuadID(uint8_t bank_num, uint32_t id1, IDE std_ext1, uint32_t id2, IDE std_ext2, uint32_t id3, IDE std_ext3, uint32_t id4, IDE std_ext4, FILTER_ACTION action = CAN_FILTER_DEFAULT_ACTION, bool enabled = true); + bool setFilterRaw(uint8_t bank_num, uint32_t id, uint32_t mask, uint32_t filter_mode, uint32_t filter_scale, FILTER_ACTION action = CAN_FILTER_DEFAULT_ACTION, bool enabled = true); + /** Legacy, broken! Only works correctly for 32 bit mask mode + * Retruns true on Error, false on Success (like Teensy functions, opposite of STM32 function) + */ + bool setFilter(uint8_t bank_num, uint32_t filter_id, uint32_t mask, IDE = AUTO, uint32_t filter_mode = CAN_FILTERMODE_IDMASK, uint32_t filter_scale = CAN_FILTERSCALE_32BIT, uint32_t fifo = CAN_FILTER_DEFAULT_FIFO); + +/**------------------------------------------------------------- + * Teensy FlexCAN compatibility functions + * ------------------------------------------------------------- + * These return false on success + */ bool setMBFilterProcessing(CAN_BANK bank_num, uint32_t filter_id, uint32_t mask, IDE = AUTO); void setMBFilter(CAN_FLTEN input); /* enable/disable traffic for all MBs (for individual masking) */ void setMBFilter(CAN_BANK bank_num, CAN_FLTEN input); /* set specific MB to accept/deny traffic */ bool setMBFilter(CAN_BANK bank_num, uint32_t id1, IDE = AUTO); /* input 1 ID to be filtered */ bool setMBFilter(CAN_BANK bank_num, uint32_t id1, uint32_t id2, IDE = AUTO); /* input 2 ID's to be filtered */ - void enableLoopBack(bool yes = 1); - void enableSilentMode(bool yes = 1); - void enableSilentLoopBack(bool yes = 1); void enableFIFO(bool status = 1); void enableMBInterrupts(); void disableMBInterrupts(); @@ -183,19 +379,27 @@ class STM32_CAN { uint16_t sizeTxBuffer; private: + void init(void); + CAN_TypeDef * getPeripheral(void); + bool allocatePeripheral(CAN_TypeDef *instance); + bool freePeripheral(void); + bool hasPeripheral(void); + void start(void); + void stop(void); void initializeFilters(); bool isInitialized() { return rx_buffer != 0; } void initRingBuffer(RingbufferTypeDef &ring, volatile CAN_message_t *buffer, uint32_t size); void initializeBuffers(void); + void freeBuffers(void); bool isRingBufferEmpty(RingbufferTypeDef &ring); uint32_t ringBufferCount(RingbufferTypeDef &ring); template - bool lookupBaudrate(CAN_HandleTypeDef *CanHandle, int Baudrate, const T(&table)[N]); - void calculateBaudrate(CAN_HandleTypeDef *CanHandle, int Baudrate); - void setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescaler, uint8_t timeseg1, - uint8_t timeseg2, uint8_t sjw); - uint32_t getAPB1Clock(void); + bool lookupBaudrate(int Baudrate, const T(&table)[N]); + bool calculateBaudrate(int Baudrate); + void setBaudRateValues(uint16_t prescaler, uint8_t timeseg1, + uint8_t timeseg2, uint8_t sjw); + uint32_t getCanPeripheralClock(void); volatile CAN_message_t *rx_buffer = nullptr; volatile CAN_message_t *tx_buffer = nullptr; @@ -240,10 +444,17 @@ class STM32_CAN { }; bool _canIsActive = false; - CAN_PINS _pins; - CAN_HandleTypeDef *n_pCanHandle; - CAN_TypeDef* _canPort; + uint32_t baudrate; + bool filtersInitialized; + + PinName rx; + PinName tx; + + uint32_t preemptPriority; + uint32_t subPriority; + + stm32_can_t _can; };