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:
parent
4825f573b2
commit
8d262bd8fc
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
@ -141,12 +141,6 @@ void loop()
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(CORE_STM32)
|
||||
else if (configPage9.enable_intcan == 1) // can module enabled
|
||||
{
|
||||
//check local can module
|
||||
}
|
||||
#endif
|
||||
|
||||
//Displays currently disabled
|
||||
// if (configPage2.displayType && (mainLoopCount & 255) == 1) { updateDisplay();}
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue