STM32 CAN bus library addition (#482)

* CAN-bus library for STM32

This adds CAN-bus library for STM32. Mainly for STM32F4, but others can be added later.

* CAN2 and F446 support added for STM32 CAN library

Adds support for second can interface and F446 bit timings.

* Fix STM32 CAN pin selection and STM32F1 support

The previous way to select CAN pins wasn't actually working, so this fixes that problem and also the F1 support too.

* Fix building for Mega/Teensy

* Update cancomms.ino

* Update speeduino.ino

Co-authored-by: Pasi Kemppainen <pazi88@users.noreply.github.com>
Co-authored-by: Josh Stewart <josh@noisymime.org>
This commit is contained in:
Pasi Kemppainen 2020-12-01 23:29:10 +02:00 committed by GitHub
parent 4825f573b2
commit 8d262bd8fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 616 additions and 12 deletions

View File

@ -276,6 +276,20 @@ void ignitionSchedule8Interrupt(HardwareTimer*);
*/
#if defined(ARDUINO_BLACK_F407VE)
//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
STM32_CAN Can0 (_CAN1,DEF);
/*
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;
#endif
#endif //CORE_STM32

View File

@ -286,7 +286,7 @@ void sendcanValues(uint16_t offset, uint16_t packetLength, byte cmd, byte portTy
void can_Command()
{
//int currentcanCommand = inMsg.id;
#if defined(CORE_TEENSY)
#if defined(CORE_TEENSY) || defined(ARDUINO_ARCH_STM32)
// currentStatus.canin[12] = (inMsg.id);
if ( (inMsg.id == uint16_t(configPage9.obd_address + 0x100)) || (inMsg.id == 0x7DF))
{
@ -355,7 +355,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) //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(CORE_TEENSY) || defined(ARDUINO_ARCH_STM32) //Scope guarding this for now, but this needs a bit of a rethink for how it can be handled better across multiple archs
outMsg.id = (canaddress);
outMsg.len = 8;
outMsg.buf[0] = 0x0B ; //11;
@ -376,7 +376,7 @@ void sendCancommand(uint8_t cmdtype, uint16_t canaddress, uint8_t candata1, uint
#endif
}
#ifdef CORE_TEENSY35
#if defined(CORE_TEENSY35) || defined(ARDUINO_ARCH_STM32)
// This routine builds the realtime data into packets that the obd requesting device can understand. This is only used by teensy and stm32 with onboard canbus
void obd_response(uint8_t PIDmode, uint8_t requestedPIDlow, uint8_t requestedPIDhigh)
{

View File

@ -212,7 +212,7 @@ void initialiseAll()
//Setup the calibration tables
loadCalibration();
#if defined(CORE_TEENSY35)
#if defined(CORE_TEENSY35) || defined(ARDUINO_ARCH_STM32)
configPage9.intcan_available = 1; // device has internal canbus
//Teensy uses the Flexcan_T4 library to use the internal canbus
//enable local can interface

View File

@ -126,7 +126,7 @@ void loop()
}
}
#endif
#if defined(CORE_TEENSY35)
#if defined(CORE_TEENSY35) || defined(ARDUINO_ARCH_STM32)
//currentStatus.canin[12] = configPage9.enable_intcan;
if (configPage9.enable_intcan == 1) // use internal can module
{
@ -138,13 +138,7 @@ void loop()
//Can0.read(inMsg);
//currentStatus.canin[12] = inMsg.buf[5];
//currentStatus.canin[13] = inMsg.id;
}
}
#endif
#if defined(CORE_STM32)
else if (configPage9.enable_intcan == 1) // can module enabled
{
//check local can module
}
}
#endif

View File

@ -0,0 +1,504 @@
#if defined(ARDUINO_ARCH_STM32)
#include "STM32_CAN.h"
uint8_t STM32_CAN::CANMsgAvail()
{
if (_channel == _CAN1) {
// Check for pending FIFO 0 messages
return CAN1->RF0R & 0x3UL;
}
#if defined(CAN2)
if (_channel == _CAN2) {
// Check for pending FIFO 0 messages
return CAN2->RF0R & 0x3UL;
}
#endif
}
void STM32_CAN::CANSetGpio(GPIO_TypeDef * addr, uint8_t index, uint8_t speed )
{
#if defined(STM32F4xx)
uint8_t _index2 = index * 2;
uint8_t _index4 = index * 4;
uint8_t ofs = 0;
uint8_t setting;
if (index > 7) {
_index4 = (index - 8) * 4;
ofs = 1;
}
uint32_t mask;
mask = 0xF << _index4;
addr->AFR[ofs] &= ~mask; // Reset alternate function
setting = 0x9; // AF9
mask = setting << _index4;
addr->AFR[ofs] |= mask; // Set alternate function
mask = 0x3 << _index2;
addr->MODER &= ~mask; // Reset mode
setting = 0x2; // Alternate function mode
mask = setting << _index2;
addr->MODER |= mask; // Set mode
mask = 0x3 << _index2;
addr->OSPEEDR &= ~mask; // Reset speed
setting = speed;
mask = setting << _index2;
addr->OSPEEDR |= mask; // Set speed
mask = 0x1 << index;
addr->OTYPER &= ~mask; // Reset Output push-pull
mask = 0x3 << _index2;
addr->PUPDR &= ~mask; // Reset port pull-up/pull-down
#endif
}
void STM32_CAN::CANSetFilter(uint8_t index, uint8_t scale, uint8_t mode, uint8_t fifo, uint32_t bank1, uint32_t bank2) {
if (index > 27) return;
CAN1->FA1R &= ~(0x1UL<<index); // Deactivate filter
if (scale == 0) {
CAN1->FS1R &= ~(0x1UL<<index); // Set filter to Dual 16-bit scale configuration
} else {
CAN1->FS1R |= (0x1UL<<index); // Set filter to single 32 bit configuration
}
if (mode == 0) {
CAN1->FM1R &= ~(0x1UL<<index); // Set filter to Mask mode
} else {
CAN1->FM1R |= (0x1UL<<index); // Set filter to List mode
}
if (fifo == 0) {
CAN1->FFA1R &= ~(0x1UL<<index); // Set filter assigned to FIFO 0
} else {
CAN1->FFA1R |= (0x1UL<<index); // Set filter assigned to FIFO 1
}
CAN1->sFilterRegister[index].FR1 = bank1; // Set filter bank registers1
CAN1->sFilterRegister[index].FR2 = bank2; // Set filter bank registers2
CAN1->FA1R |= (0x1UL<<index); // Activate filter
}
void STM32_CAN::begin()
{
if (_channel == _CAN1)
{
// CAN1
RCC->APB1ENR |= 0x2000000UL; // Enable CAN1 clock
}
#if defined(CAN2)
else if (_channel == _CAN2)
{
// CAN2
RCC->APB1ENR |= 0x4000000UL; // Enable CAN2 clock
}
#endif
SetTXRX();
if (_channel == _CAN1)
{
CAN1->MCR |= 0x1UL; // Require CAN1 to Initialization mode
while (!(CAN1->MSR & 0x1UL)); // Wait for Initialization mode
//CAN1->MCR = 0x51UL; // Hardware initialization(No automatic retransmission)
CAN1->MCR = 0x41UL; // Hardware initialization(With automatic retransmission)
}
#if defined(CAN2)
else if (_channel == _CAN2)
{
// CAN2
CAN2->MCR |= 0x1UL; // Require CAN2 to Initialization mode
while (!(CAN2->MSR & 0x1UL)); // Wait for Initialization mode
//CAN2->MCR = 0x51UL; // Hardware initialization(No automatic retransmission)
CAN2->MCR = 0x41UL; // Hardware initialization(With automatic retransmission)
}
#endif
}
int STM32_CAN::write(CAN_message_t &CAN_tx_msg)
{
volatile int count = 0;
uint32_t out = 0;
if (CAN_tx_msg.flags.extended == true) { // Extended frame format
out = ((CAN_tx_msg.id & CAN_EXT_ID_MASK) << 3U) | STM32_CAN_TIR_IDE;
}
else { // Standard frame format
out = ((CAN_tx_msg.id & CAN_STD_ID_MASK) << 21U);
}
// Remote frame
if (CAN_tx_msg.flags.remote == true) {
out |= STM32_CAN_TIR_RTR;
}
if (_channel == _CAN1)
{
CAN1->sTxMailBox[0].TDTR &= ~(0xF);
CAN1->sTxMailBox[0].TDTR |= CAN_tx_msg.len & 0xFUL;
CAN1->sTxMailBox[0].TDLR = (((uint32_t) CAN_tx_msg.buf[3] << 24) |
((uint32_t) CAN_tx_msg.buf[2] << 16) |
((uint32_t) CAN_tx_msg.buf[1] << 8) |
((uint32_t) CAN_tx_msg.buf[0] ));
CAN1->sTxMailBox[0].TDHR = (((uint32_t) CAN_tx_msg.buf[7] << 24) |
((uint32_t) CAN_tx_msg.buf[6] << 16) |
((uint32_t) CAN_tx_msg.buf[5] << 8) |
((uint32_t) CAN_tx_msg.buf[4] ));
// Send Go
CAN1->sTxMailBox[0].TIR = out | STM32_CAN_TIR_TXRQ;
// Wait until the mailbox is empty
while(CAN1->sTxMailBox[0].TIR & 0x1UL && count++ < 1000000);
if (CAN1->sTxMailBox[0].TIR & 0x1UL) {
//Serial.println("transmit Fail");
return -1; // transmit failed
}
else {
return 1; // transmit ok
}
}
#if defined(CAN2)
if (_channel == _CAN2)
{
CAN2->sTxMailBox[0].TDTR &= ~(0xF);
CAN2->sTxMailBox[0].TDTR |= CAN_tx_msg.len & 0xFUL;
CAN2->sTxMailBox[0].TDLR = (((uint32_t) CAN_tx_msg.buf[3] << 24) |
((uint32_t) CAN_tx_msg.buf[2] << 16) |
((uint32_t) CAN_tx_msg.buf[1] << 8) |
((uint32_t) CAN_tx_msg.buf[0] ));
CAN2->sTxMailBox[0].TDHR = (((uint32_t) CAN_tx_msg.buf[7] << 24) |
((uint32_t) CAN_tx_msg.buf[6] << 16) |
((uint32_t) CAN_tx_msg.buf[5] << 8) |
((uint32_t) CAN_tx_msg.buf[4] ));
// Send Go
CAN2->sTxMailBox[0].TIR = out | STM32_CAN_TIR_TXRQ;
// Wait until the mailbox is empty
while(CAN2->sTxMailBox[0].TIR & 0x1UL && count++ < 1000000);
if (CAN2->sTxMailBox[0].TIR & 0x1UL) {
//Serial.println("transmit Fail");
return -1; // transmit failed
}
else {
return 1; // transmit ok
}
}
#endif
}
int STM32_CAN::read(CAN_message_t &CAN_rx_msg)
{
if (CANMsgAvail()) {
if(_channel == _CAN1) {
uint32_t id = CAN1->sFIFOMailBox[0].RIR;
if ((id & STM32_CAN_RIR_IDE) == 0) { // Standard frame format
CAN_rx_msg.flags.extended = false;
CAN_rx_msg.id = (CAN_STD_ID_MASK & (id >> 21U));
}
else { // Extended frame format
CAN_rx_msg.flags.extended = true;
CAN_rx_msg.id = (CAN_EXT_ID_MASK & (id >> 3U));
}
if ((id & STM32_CAN_RIR_RTR) == 0) { // Data frame
CAN_rx_msg.flags.remote = false;
}
else { // Remote frame
CAN_rx_msg.flags.remote = true;
}
CAN_rx_msg.len = (CAN1->sFIFOMailBox[0].RDTR) & 0xFUL;
CAN_rx_msg.buf[0] = 0xFFUL & CAN1->sFIFOMailBox[0].RDLR;
CAN_rx_msg.buf[1] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 8);
CAN_rx_msg.buf[2] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 16);
CAN_rx_msg.buf[3] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 24);
CAN_rx_msg.buf[4] = 0xFFUL & CAN1->sFIFOMailBox[0].RDHR;
CAN_rx_msg.buf[5] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 8);
CAN_rx_msg.buf[6] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 16);
CAN_rx_msg.buf[7] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 24);
CAN_rx_msg.bus = 1;
CAN1->RF0R |= 0x20UL;
} // end CAN1
#if defined(CAN2)
if(_channel == _CAN2) {
uint32_t id = CAN2->sFIFOMailBox[0].RIR;
if ((id & STM32_CAN_RIR_IDE) == 0) { // Standard frame format
CAN_rx_msg.flags.extended = false;
CAN_rx_msg.id = (CAN_STD_ID_MASK & (id >> 21U));
}
else { // Extended frame format
CAN_rx_msg.flags.extended = true;
CAN_rx_msg.id = (CAN_EXT_ID_MASK & (id >> 3U));
}
if ((id & STM32_CAN_RIR_RTR) == 0) { // Data frame
CAN_rx_msg.flags.remote = false;
}
else { // Remote frame
CAN_rx_msg.flags.remote = true;
}
CAN_rx_msg.len = (CAN2->sFIFOMailBox[0].RDTR) & 0xFUL;
CAN_rx_msg.buf[0] = 0xFFUL & CAN2->sFIFOMailBox[0].RDLR;
CAN_rx_msg.buf[1] = 0xFFUL & (CAN2->sFIFOMailBox[0].RDLR >> 8);
CAN_rx_msg.buf[2] = 0xFFUL & (CAN2->sFIFOMailBox[0].RDLR >> 16);
CAN_rx_msg.buf[3] = 0xFFUL & (CAN2->sFIFOMailBox[0].RDLR >> 24);
CAN_rx_msg.buf[4] = 0xFFUL & CAN2->sFIFOMailBox[0].RDHR;
CAN_rx_msg.buf[5] = 0xFFUL & (CAN2->sFIFOMailBox[0].RDHR >> 8);
CAN_rx_msg.buf[6] = 0xFFUL & (CAN2->sFIFOMailBox[0].RDHR >> 16);
CAN_rx_msg.buf[7] = 0xFFUL & (CAN2->sFIFOMailBox[0].RDHR >> 24);
CAN_rx_msg.bus = 2;
CAN2->RF0R |= 0x20UL;
} // END CAN2
#endif
return 1; // message read
}
else {
return 0; // no messages available
}
}
void STM32_CAN::setBaudRate(uint32_t baud)
{
//there must be better way to do this, but for now it's what it is.
int bitrate;
switch(baud)
{
case 50000:
bitrate = 0;
break;
case 100000:
bitrate = 1;
break;
case 125000:
bitrate = 2;
break;
case 250000:
bitrate = 3;
break;
case 500000:
bitrate = 4;
break;
case 1000000:
bitrate = 5;
break;
default:
//Other baud rates aren't supported
break;
}
// Set bit rates
if (_channel == _CAN1)
{
CAN1->BTR &= ~(((0x03) << 24) | ((0x07) << 20) | ((0x0F) << 16) | (0x1FF));
CAN1->BTR |= (((can_configs[bitrate].TS2-1) & 0x07) << 20) | (((can_configs[bitrate].TS1-1) & 0x0F) << 16) | ((can_configs[bitrate].BRP-1) & 0x1FF);
// Configure Filters to default values
CAN1->FMR |= 0x1UL; // Set to filter initialization mode
CAN1->FMR &= 0xFFFFC0FF; // Clear CAN2 start bank
// bxCAN has 28 filters.
// These filters are used for both CAN1 and CAN2.
// STM32F405 has CAN1 and CAN2, so CAN2 filters are offset by 14
CAN1->FMR |= 0xE00; // Start bank for the CAN2 interface
// Set filter 0
// Single 32-bit scale configuration
// Two 32-bit registers of filter bank x are in Identifier Mask mode
// Filter assigned to FIFO 0
// Filter bank register to all 0
CANSetFilter(0, 1, 0, 0, 0x0UL, 0x0UL);
// Set filter 14
// Single 32-bit scale configuration
// Two 32-bit registers of filter bank x are in Identifier Mask mode
// Filter assigned to FIFO 0
// Filter bank register to all 0
CANSetFilter(14, 1, 0, 0, 0x0UL, 0x0UL);
}
#if defined(CAN2)
else if (_channel == _CAN2)
{
CAN2->BTR &= ~(((0x03) << 24) | ((0x07) << 20) | ((0x0F) << 16) | (0x1FF));
CAN2->BTR |= (((can_configs[bitrate].TS2-1) & 0x07) << 20) | (((can_configs[bitrate].TS1-1) & 0x0F) << 16) | ((can_configs[bitrate].BRP-1) & 0x1FF);
// Configure Filters to default values
CAN2->FMR |= 0x1UL; // Set to filter initialization mode
CAN2->FMR &= 0xFFFFC0FF; // Clear CAN2 start bank
// bxCAN has 28 filters.
// These filters are used for both CAN1 and CAN2.
// STM32F405 has CAN1 and CAN2, so CAN2 filters are offset by 14
CAN2->FMR |= 0xE00; // Start bank for the CAN2 interface
// Set filter 0
// Single 32-bit scale configuration
// Two 32-bit registers of filter bank x are in Identifier Mask mode
// Filter assigned to FIFO 0
// Filter bank register to all 0
CANSetFilter(0, 1, 0, 0, 0x0UL, 0x0UL);
// Set filter 14
// Single 32-bit scale configuration
// Two 32-bit registers of filter bank x are in Identifier Mask mode
// Filter assigned to FIFO 0
// Filter bank register to all 0
CANSetFilter(14, 1, 0, 0, 0x0UL, 0x0UL);
}
#endif
if (_channel == _CAN1)
{
CAN1->FMR &= ~(0x1UL); // Deactivate initialization mode
uint16_t TimeoutMilliseconds = 1000;
bool can1 = false;
CAN1->MCR &= ~(0x1UL); // Require CAN1 to normal mode
// Wait for normal mode
// If the connection is not correct, it will not return to normal mode.
for (uint16_t wait_ack = 0; wait_ack < TimeoutMilliseconds; wait_ack++) {
if ((CAN1->MSR & 0x1UL) == 0) {
can1 = true;
break;
}
delayMicroseconds(1000);
}
if (can1) {
//Serial.println("CAN1 initialize ok");
} else {
//Serial.println("CAN1 initialize fail!!");
}
}
#if defined(CAN2)
else if (_channel == _CAN2)
{
CAN2->FMR &= ~(0x1UL); // Deactivate initialization mode
uint16_t TimeoutMilliseconds = 1000;
bool can2 = false;
CAN2->MCR &= ~(0x1UL); // Require CAN2 to normal mode
// Wait for normal mode
// If the connection is not correct, it will not return to normal mode.
for (uint16_t wait_ack = 0; wait_ack < TimeoutMilliseconds; wait_ack++) {
if ((CAN2->MSR & 0x1UL) == 0) {
can2 = true;
break;
}
delayMicroseconds(1000);
}
//Serial.print("can2=");
//Serial.println(can2);
if (can2) {
//Serial.println("CAN2 initialize ok");
} else {
//Serial.println("CAN2 initialize fail!!");
}
}
#endif
}
void STM32_CAN::enableFIFO(bool status)
{
//Nothing to do here. The FIFO is on by default.
}
void STM32_CAN::SetTXRX()
{
if (_channel == _CAN1) // CAN1
{
#if defined(STM32F4xx)
//PD0/PD1 as default. PA11/PA12 is for USB so that can't be used if native USB connection is in use.
if (_pins == DEF) {
RCC->AHB1ENR |= 0x8; // Enable GPIOD clock
CANSetGpio(GPIOD, 1); // Set PD1
CANSetGpio(GPIOD, 0); // Set PD0
}
//PB8/PB9 are alternative pins.
if (_pins == ALT) {
RCC->AHB1ENR |= 0x2; // Enable GPIOB clock
CANSetGpio(GPIOB, 9); // Set PB9
CANSetGpio(GPIOB, 8); // Set PB8
}
//PA11/PA12 are second alternative pins, but it can't be used if native USB connection is in use.
if (_pins == ALT2) {
RCC->AHB1ENR |= 0x1; // Enable GPIOA clock
CANSetGpio(GPIOA, 12); // Set PA12
CANSetGpio(GPIOA, 11); // Set PA11
}
#elif defined(STM32F1xx)
//PA11/PA12 as default, because those are only ones available on all F1 models.
if (_pins == DEF) {
AFIO->MAPR &= 0xFFFF9FFF; // reset CAN remap
// CAN_RX mapped to PA11, CAN_TX mapped to PA12
GPIOA->CRH &= ~(0xFF000UL); // Configure PA12(0b0000) and PA11(0b0000)
// 0b0000
// MODE=00(Input mode)
// CNF=00(Analog mode)
GPIOA->CRH |= 0xB8FFFUL; // Configure PA12(0b1011) and PA11(0b1000)
// 0b1011
// MODE=11(Output mode, max speed 50 MHz)
// CNF=10(Alternate function output Push-pull
// 0b1000
// MODE=00(Input mode)
// CNF=10(Input with pull-up / pull-down)
GPIOA->ODR |= 0x1UL << 12; // PA12 Upll-up
}
//PB8/PB9 are alternative pins.
if (_pins == ALT) {
AFIO->MAPR |= 0x00004000; // set CAN remap
// CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)
RCC->APB2ENR |= 0x8UL; // Enable GPIOB clock
GPIOB->CRH &= ~(0xFFUL); // Configure PB9(0b0000) and PB8(0b0000)
// 0b0000
// MODE=00(Input mode)
// CNF=00(Analog mode)
GPIOB->CRH |= 0xB8UL; // Configure PB9(0b1011) and PB8(0b1000)
// 0b1011
// MODE=11(Output mode, max speed 50 MHz)
// CNF=10(Alternate function output Push-pull
// 0b1000
// MODE=00(Input mode)
// CNF=10(Input with pull-up / pull-down)
GPIOB->ODR |= 0x1UL << 8; // PB8 Upll-up
}
#endif
}
#if defined(CAN2)
else if (_channel == _CAN2) // CAN2
{
//PB5/PB6 as default
if (_pins == DEF) {
RCC->AHB1ENR |= 0x2; // Enable GPIOB clock
CANSetGpio(GPIOB, 6); // Set PB6
CANSetGpio(GPIOB, 5); // Set PB5
}
//PB12/PB13 are alternative pins.
if (_pins == ALT) {
RCC->AHB1ENR |= 0x2; // Enable GPIOB clock
CANSetGpio(GPIOB, 13); // Set PB13
CANSetGpio(GPIOB, 12); // Set PB12
}
}
#endif
}
#endif

View File

@ -0,0 +1,92 @@
/*
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
things from Teensy FlexCAN library to make it compatible with the CAN features that exist in speeduino for Teensy.
Link to the nopnop2002 repository:
https://github.com/nopnop2002/Arduino-STM32-CAN
*/
#ifndef STM32_CAN_H
#define STM32_CAN_H
#if defined(ARDUINO_ARCH_STM32)
#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/
typedef struct CAN_message_t {
uint32_t id = 0; // can identifier
uint16_t timestamp = 0; // time when message arrived
uint8_t idhit = 0; // filter that id came from
struct {
bool extended = 0; // identifier is extended (29-bit)
bool remote = 0; // remote transmission request packet type
bool overrun = 0; // message overrun
bool reserved = 0;
} flags;
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
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, ALT2,} CAN_PINS;
#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
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);
uint8_t CANMsgAvail();
void SetTXRX();
public:
STM32_CAN(const CAN_CHANNEL channel, CAN_PINS pins) : _channel (channel), _pins (pins) { };
void begin();
void setBaudRate(uint32_t baud);
int write(CAN_message_t &CAN_tx_msg);
int read(CAN_message_t &CAN_rx_msg);
void enableFIFO(bool status = 1);
};
#endif
#endif