New CAN-bus library for STM32 (#725)

* Use new CAN-library

* Fix CAN3 pin number

* Fix building on other platforms
This commit is contained in:
Pasi Kemppainen 2021-12-23 04:46:13 +02:00 committed by GitHub
parent d0bbb24563
commit 34116ad223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1040 additions and 535 deletions

View File

@ -65,7 +65,7 @@ framework = arduino
board = black_f407ve
lib_deps = stm32duino/STM32duino RTC
board_build.core = stm32
build_flags = -std=gnu++11 -UBOARD_MAX_IO_PINS -DENABLE_HWSERIAL2 -DENABLE_HWSERIAL3 -DUSBCON -DHAL_PCD_MODULE_ENABLED -DUSBD_USE_CDC
build_flags = -std=gnu++11 -UBOARD_MAX_IO_PINS -DENABLE_HWSERIAL2 -DENABLE_HWSERIAL3 -DUSBCON -DHAL_PCD_MODULE_ENABLED -DUSBD_USE_CDC -DHAL_CAN_MODULE_ENABLED
upload_protocol = dfu
debug_tool = stlink
monitor_speed = 115200

View File

@ -320,20 +320,12 @@ void ignitionSchedule8Interrupt(HardwareTimer*);
***********************************************************************************************************
* CAN / Second serial
*/
#if defined(STM32F407xx) || defined(STM32F103xB) || defined(STM32F405xx)
#if HAL_CAN_MODULE_ENABLED
#define NATIVE_CAN_AVAILABLE
//HardwareSerial CANSerial(PD6, PD5);
#include <src/STM32_CAN/STM32_CAN.h>
//This activates CAN1 interface on STM32, but it's named as Can0, because that's how Teensy implementation is done
extern STM32_CAN Can0;
/*
Second CAN interface is also available if needed or it can be used also as primary CAN interface.
for STM32F4 the default CAN1 pins are PD0 & PD1. Alternative (ALT) pins are PB8 & PB9 and ALT2 pins are PA11 and PA12:
for STM32F4 the default CAN2 pins are PB5 & PB6. Alternative (ALT) pins are PB12 & PB13.
for STM32F1 the default CAN1 pins are PA11 & PA12. Alternative (ALT) pins are PB8 & PB9.
Example of using CAN2 as secondary CAN bus with alternative pins:
STM32_CAN Can1 (_CAN2,ALT);
*/
static CAN_message_t outMsg;
static CAN_message_t inMsg;

View File

@ -6,10 +6,15 @@
#include "scheduler.h"
#include "HardwareTimer.h"
#if defined(STM32F407xx) || defined(STM32F103xB) || defined(STM32F405xx)
#define NATIVE_CAN_AVAILABLE
#if HAL_CAN_MODULE_ENABLED
//This activates CAN1 interface on STM32, but it's named as Can0, because that's how Teensy implementation is done
STM32_CAN Can0 (_CAN1,DEF);
STM32_CAN Can0 (CAN1, ALT_2, RX_SIZE_256, TX_SIZE_16);
/*
These CAN interfaces and pins are available for use, depending on the chip/package:
Default CAN1 pins are PA11 and PA12. Alternative (ALT) pins are PB8 & PB9 and ALT_2 pins are PD0 & PD1.
Default CAN2 pins are PB12 & PB13. Alternative (ALT) pins are PB5 & PB6.
Default CAN3 pins are PA8 & PA15. Alternative (ALT) pins are PB3 & PB4.
*/
#endif
#if defined(SRAM_AS_EEPROM)

View File

@ -433,7 +433,7 @@ void sendCancommand(uint8_t cmdtype, uint16_t canaddress, uint8_t candata1, uint
//send to truecan send routine
//canaddress == speeduino canid, candata1 == canin channel dest, paramgroup == can address to request from
//This section is to be moved to the correct can output routine later
#if defined(CORE_TEENSY) || defined(STM32F407xx) || defined(STM32F103xB) || defined(STM32F405xx) //Scope guarding this for now, but this needs a bit of a rethink for how it can be handled better across multiple archs
#if defined(NATIVE_CAN_AVAILABLE)
outMsg.id = (canaddress);
outMsg.len = 8;
outMsg.buf[0] = 0x0B ; //11;

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,22 @@
/*
This is CAN library for STM32 to be used in Speeduino engine management system by pazi88.
The library is created because at least currently (year 2020) there is no CAN library in the STM32 core.
This library is mostly based on the STM32 CAN examples by nopnop2002 and it has been combined with few
This is universal CAN library for STM32 that was made to be used with Speeduino EFI.
It should support all STM32 MCUs that are also supported in stm32duino Arduino_Core_STM32 and supports up to 3x CAN busses.
The library is created because at least currently (year 2021) there is no official CAN library in the STM32 core.
This library is based on several STM32 CAN example libraries linked below and it has been combined with few
things from Teensy FlexCAN library to make it compatible with the CAN features that exist in speeduino for Teensy.
Link to the nopnop2002 repository:
Links to repositories that have helped with this:
https://github.com/nopnop2002/Arduino-STM32-CAN
https://github.com/J-f-Jensen/libraries/tree/master/STM32_CAN
https://github.com/jiauka/STM32F1_CAN
STM32 core: https://github.com/stm32duino/Arduino_Core_STM32
*/
#if HAL_CAN_MODULE_ENABLED
#ifndef STM32_CAN_H
#define STM32_CAN_H
#if defined(STM32F407xx) || defined(STM32F1xx) || defined(STM32F405xx)
#include <Arduino.h>
#define STM32_CAN_TIR_TXRQ (1U << 0U) // Bit 0: Transmit Mailbox Request
#define STM32_CAN_RIR_RTR (1U << 1U) // Bit 1: Remote Transmission Request
#define STM32_CAN_RIR_IDE (1U << 2U) // Bit 2: Identifier Extension
#define STM32_CAN_TIR_RTR (1U << 1U) // Bit 1: Remote Transmission Request
#define STM32_CAN_TIR_IDE (1U << 2U) // Bit 2: Identifier Extension
#define CAN_EXT_ID_MASK 0x1FFFFFFFU
#define CAN_STD_ID_MASK 0x000007FFU
// 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/
@ -37,65 +33,152 @@ typedef struct CAN_message_t {
uint8_t len = 8; // length of data
uint8_t buf[8] = { 0 }; // data
int8_t mb = 0; // used to identify mailbox reception
uint8_t bus = 1; // used to identify where the message came from when events() is used. CAN(1) and CAN(2) in use
uint8_t bus = 1; // used to identify where the message came (CAN1, CAN2 or CAN3)
bool seq = 0; // sequential frames
} CAN_message_t;
typedef const struct
{
uint8_t TS2;
uint8_t TS1;
uint8_t BRP;
} CAN_bit_timing_config_t;
typedef enum CAN_PINS {DEF, ALT, ALT_2,} CAN_PINS;
//STM32 has only 3 TX mailboxes
typedef enum CAN_MAILBOX {
typedef enum RXQUEUE_TABLE {
RX_SIZE_2 = (uint16_t)2,
RX_SIZE_4 = (uint16_t)4,
RX_SIZE_8 = (uint16_t)8,
RX_SIZE_16 = (uint16_t)16,
RX_SIZE_32 = (uint16_t)32,
RX_SIZE_64 = (uint16_t)64,
RX_SIZE_128 = (uint16_t)128,
RX_SIZE_256 = (uint16_t)256,
RX_SIZE_512 = (uint16_t)512,
RX_SIZE_1024 = (uint16_t)1024
} RXQUEUE_TABLE;
typedef enum TXQUEUE_TABLE {
TX_SIZE_2 = (uint16_t)2,
TX_SIZE_4 = (uint16_t)4,
TX_SIZE_8 = (uint16_t)8,
TX_SIZE_16 = (uint16_t)16,
TX_SIZE_32 = (uint16_t)32,
TX_SIZE_64 = (uint16_t)64,
TX_SIZE_128 = (uint16_t)128,
TX_SIZE_256 = (uint16_t)256,
TX_SIZE_512 = (uint16_t)512,
TX_SIZE_1024 = (uint16_t)1024
} TXQUEUE_TABLE;
/* Teensy FlexCAN uses Mailboxes for different RX filters, but in STM32 there is Filter Banks. These work practically same way,
so the Filter Banks are named as mailboxes in "setMBFilter" -functions, to retain compatibility with Teensy FlexCAN library.
*/
typedef enum CAN_BANK {
MB0 = 0,
MB1 = 1,
MB2 = 2
} CAN_MAILBOX;
MB2 = 2,
MB3 = 3,
MB4 = 4,
MB5 = 5,
MB6 = 6,
MB7 = 7,
MB8 = 8,
MB9 = 9,
MB10 = 10,
MB11 = 11,
MB12 = 12,
MB13 = 13,
MB14 = 14,
MB15 = 15,
MB16 = 16,
MB17 = 17,
MB18 = 18,
MB19 = 19,
MB20 = 20,
MB21 = 21,
MB22 = 22,
MB23 = 23,
MB24 = 24,
MB25 = 25,
MB26 = 26,
MB27 = 27
} CAN_BANK;
#ifndef CAN2
typedef enum CAN_CHANNEL {_CAN1,} CAN_CHANNEL;
#elif defined CAN2
typedef enum CAN_CHANNEL {_CAN1, _CAN2,} CAN_CHANNEL;
#endif
//Bit timings depend on the APB1 clock speed and need to be calculated based on that.
//APB1 at 42MHz:
#if defined(STM32F407xx) || defined(STM32F405xx)
CAN_bit_timing_config_t can_configs[6] = {{2, 12, 56}, {2, 12, 28}, {2, 13, 21}, {2, 11, 12}, {2, 11, 6}, {1, 5, 6}};
//APB1 at 36MHz
#elif defined(STM32F1xx)
CAN_bit_timing_config_t can_configs[6] = {{2, 13, 45}, {2, 15, 20}, {2, 13, 18}, {2, 13, 9}, {2, 15, 4}, {2, 15, 2}};
//APB1 at 45MHz
#elif defined(STM32F446xx)
CAN_bit_timing_config_t can_configs[6] = {{2, 12, 60}, {2, 12, 30}, {2, 12, 24}, {2, 12, 12}, {2, 12, 6}, {1, 7, 5}};
//If support for more APB1 clock speeds is needed, use this calculator: http://www.bittiming.can-wiki.info/
#endif
typedef enum CAN_FLTEN {
ACCEPT_ALL = 0,
REJECT_ALL = 1
} CAN_FLTEN;
class STM32_CAN {
const CAN_CHANNEL _channel;
const CAN_PINS _pins;
private:
void CANSetGpio(GPIO_TypeDef * addr, uint8_t index, uint8_t speed = 3);
void CANSetFilter(uint8_t index, uint8_t scale, uint8_t mode, uint8_t fifo, uint32_t bank1, uint32_t bank2);
void writeTxMailbox(uint8_t mb_num, CAN_message_t &CAN_tx_msg);
uint8_t CANMsgAvail();
void SetTXRX();
public:
STM32_CAN(const CAN_CHANNEL channel, CAN_PINS pins) : _channel (channel), _pins (pins) { };
void begin();
// Default buffer sizes are set to 16. But this can be changed by using constructor in main code.
STM32_CAN(CAN_TypeDef* canPort, CAN_PINS pins, RXQUEUE_TABLE rxSize = RX_SIZE_16, TXQUEUE_TABLE txSize = TX_SIZE_16);
// Begin. By default the automatic retransmission is enabled. If it causes problems, use begin(false) to disable it.
void begin(bool retransmission = true);
void setBaudRate(uint32_t baud);
int write(CAN_message_t &CAN_tx_msg); // use any available mailbox for transmitting
int write(CAN_MAILBOX mb_num, CAN_message_t &CAN_tx_msg); // use a single mailbox for transmitting
int read(CAN_message_t &CAN_rx_msg);
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, 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
bool setMBFilterProcessing(CAN_BANK bank_num, uint32_t filter_id, uint32_t mask);
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); /* input 1 ID to be filtered */
bool setMBFilter(CAN_BANK bank_num, uint32_t id1, uint32_t id2); /* 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();
// These are public because these are also used from interupts.
typedef struct RingbufferTypeDef {
volatile uint16_t head;
volatile uint16_t tail;
uint16_t size;
volatile CAN_message_t *buffer;
} RingbufferTypeDef;
RingbufferTypeDef rxRing;
RingbufferTypeDef txRing;
bool addToRingBuffer(RingbufferTypeDef &ring, const CAN_message_t &msg);
bool removeFromRingBuffer(RingbufferTypeDef &ring, CAN_message_t &msg);
protected:
uint16_t sizeRxBuffer;
uint16_t sizeTxBuffer;
private:
void initializeFilters();
bool isInitialized() { return rx_buffer != 0; }
void initRingBuffer(RingbufferTypeDef &ring, volatile CAN_message_t *buffer, uint32_t size);
void initializeBuffers(void);
bool isRingBufferEmpty(RingbufferTypeDef &ring);
uint32_t ringBufferCount(RingbufferTypeDef &ring);
void calculateBaudrate(CAN_HandleTypeDef *CanHandle, int Baudrate);
uint32_t getAPB1Clock(void);
volatile CAN_message_t *rx_buffer;
volatile CAN_message_t *tx_buffer;
bool _canIsActive = false;
CAN_PINS _pins;
CAN_HandleTypeDef *n_pCanHandle;
CAN_TypeDef* _canPort;
};
static STM32_CAN* _CAN1 = nullptr;
static CAN_HandleTypeDef hcan1;
#ifdef CAN2
static STM32_CAN* _CAN2 = nullptr;
static CAN_HandleTypeDef hcan2;
#endif
#ifdef CAN3
static STM32_CAN* _CAN3 = nullptr;
static CAN_HandleTypeDef hcan3;
#endif
#endif
#endif
#endif