pierremolinaro / acanfd-stm32

STM32 G4 and STM32 H7 CANFD Driver
MIT License
11 stars 1 forks source link

Request: Add example of using 'ACANFD_STM32_from_cpp.h' in other C++ Files #2

Open Mitchy6 opened 3 months ago

Mitchy6 commented 3 months ago

Having an issue with accessing the fdcani elements in other C++ files Complier indicates that the elements (fdcan1, fdcan2, fdcan3) are undefined; however CANMessage elements are defined. Currently including in main, and in the other file.

Likely something simple; but curious if there's something fundamental that I'm missing. Would it be possible to offer an example or code snip?

Using a G474 as the base hardware. No issues using the library if all functions are kept within the main loop/ino

Thanks.

chriskinal commented 3 months ago

How are you encapsulating the ACANFD library in these other c++ files? In a class file? Another .ino in the same directory as the main .ino? Or? What IDE are you using?

Mitchy6 commented 3 months ago

Attempting to use it much like the Flexcan_T4 library which has the 'can0' (or whatever name is defined) exposed external to the main function, so any class is able to call and send on a particular can interface, without requiring to be added into a class or sub function.

A quick way that seems to work is if I define the following: extern ACANFD_STM32 fdcan1; This allows the .ino file to contain the setup of the busses, while allowing a 'CANManager' class to handle sending, from there I can use the class function to send via any other classes.

I'm just hoping to simply this a bit, allowing the setup to occur in the main .ino, and then just call 'fdcan1.tryToSendReturnStatusFD(message)' from whatever class requires adding a message to the send buffer.

I'll experiment a bit more, it's entirely possible that adding the 'extern ACANFD_STM32 fdcan1;' definition will work, as the fdcan1 interface should then be globally accessible.

Hope all this makes sense. And I'm likely reaching the limits of my current C++ knowledge.

chriskinal commented 3 months ago

Yes, I am pushing the limits of my C++ knowledge as well. I was looking at encapsulating it into an application specific class file with simplified send / receive functions. That way when I create an instance of the app specific class file it will automatically setup the bus for that instance. Then I can use the simplified app specific functions anywhere in the app to send / receive data from the CANbus.

chriskinal commented 3 months ago

Here is an example where I created a class file to encapsulate the library and provide simple send / receive functions. Hope this helps. There is also an example of using the class at the bottom.

ACANFD_AOG.h // Wrapper class for ACANFD_STM32 for use in AOG with the STM32H723ZG MCU. // Copyright 2024 by chriskinal // Use is hereby granted under the MIT license.

ifndef ACANFD_AOG_h

define ACANFD_AOG_h

ifndef ARDUINO_NUCLEO_H723ZG

error This sketch runs on NUCLEO-H723ZG Nucleo-144 board

endif

include "Arduino.h"

class ACANFD_AOG {

public: void begin(int can_bus, int can_id, int can_speed ); void can_send( uint8_t can_data[] ); bool can_recv( uint8_t* outBytes);

private: uint8_t _can_bus; uint16_t _can_id; int _can_speed; };

endif


ACANFD_AOG.cpp // Wrapper class for ACANFD_STM32 for use in AOG with the STM32H723ZG MCU. // Copyright 2024 by chriskinal // Use is hereby granted under the MIT license.

include

include

static const uint32_t FDCAN1_MESSAGE_RAM_WORD_SIZE = 2560; static const uint32_t FDCAN2_MESSAGE_RAM_WORD_SIZE = 2560;

include

//ACANFD_AOG::ACANFD_AOG() {}

void ACANFD_AOG::begin(int can_bus, int can_id, int can_speed ) { uint32_t errorCode; _can_id = can_id;

Serial.print ("Initializing CANbus #") ;
Serial.println(can_bus);

ACANFD_STM32_Settings settings (can_speed * 1000, DataBitRateFactor::x1) ;

Serial.print ("Bit Rate prescaler: ") ;
Serial.println (settings.mBitRatePrescaler) ;
Serial.print ("Arbitration Phase segment 1: ") ;
Serial.println (settings.mArbitrationPhaseSegment1) ;
Serial.print ("Arbitration Phase segment 2: ") ;
Serial.println (settings.mArbitrationPhaseSegment2) ;
Serial.print ("Arbitration SJW: ") ;
Serial.println (settings.mArbitrationSJW) ;
Serial.print ("Actual Arbitration Bit Rate: ") ;
Serial.print (settings.actualArbitrationBitRate ()) ;
Serial.println (" bit/s") ;
Serial.print ("Arbitration Sample point: ") ;
Serial.print (settings.arbitrationSamplePointFromBitStart ()) ;
Serial.println ("%") ;
Serial.print ("Exact Arbitration Bit Rate ? ") ;
Serial.println (settings.exactArbitrationBitRate () ? "yes" : "no") ;
Serial.print ("Data Phase segment 1: ") ;
Serial.println (settings.mDataPhaseSegment1) ;
Serial.print ("Data Phase segment 2: ") ;
Serial.println (settings.mDataPhaseSegment2) ;
Serial.print ("Data SJW: ") ;
Serial.println (settings.mDataSJW) ;
Serial.print ("Actual Data Bit Rate: ") ;
Serial.print (settings.actualDataBitRate ()) ;
Serial.println (" bit/s") ;
Serial.print ("Data Sample point: ") ;
Serial.print (settings.dataSamplePointFromBitStart ()) ;
Serial.println ("%") ;
Serial.print ("Exact Data Bit Rate ? ") ;
Serial.println (settings.exactDataBitRate () ? "yes" : "no") ;

// // Interface set to normal mode
settings.mModuleMode = ACANFD_STM32_Settings::NORMAL_FD ;

// // Reject standard frames that do not match any filter
settings.mNonMatchingStandardFrameReception = ACANFD_STM32_FilterAction::FIFO0 ;

// // Allocate FIFO 1
settings.mHardwareRxFIFO1Size = 10 ; // By default, 0
settings.mDriverReceiveFIFO1Size = 10 ; // By default, 0

if ( can_bus == 1 )
{
     errorCode = fdcan1.beginFD(settings);
}
if ( can_bus == 2 )
{
    errorCode = fdcan2.beginFD(settings) ;
}

Serial.print ("Message RAM required minimum size: ") ;
Serial.print (fdcan1.messageRamRequiredMinimumSize ()) ;
Serial.println (" words") ;

Serial.print("CANbus #");
Serial.print(can_bus);
if (0 == errorCode) {
    Serial.println (" OK!") ;
}else{
    Serial.print (" Error can configuration: 0x") ;
    Serial.println (errorCode, HEX) ;
}

}

void ACANFD_AOG::can_send( uint8_t can_data[] ) { if (fdcan1.sendBufferNotFullForIndex (0)) { CANFDMessage frame; frame.id = _can_id; frame.ext = true; frame.type = CANFDMessage::CAN_DATA; frame.len = 8; for (uint8_t i=0 ; i<frame.len ; i++) { frame.data [i] = can_data[i] ;} const uint32_t sendStatus = fdcan1.tryToSendReturnStatusFD (frame); if (sendStatus != 0) { Serial.print ("Sent error 0x") ; Serial.println (sendStatus) ; } Serial.println("Frame Sent"); delay(100); } }

bool ACANFD_AOG::can_recv( uint8_t* outBytes ) { bool message_received = false; CANFDMessage frame ; if (fdcan1.receiveFD0 (frame)) { if ( frame.ext ) { //Serial.println(frame.id); for ( int i=0; i<frame.len; i++){ outBytes[i] = frame.data[i]; } } Serial.println("Frame Received"); message_received = true; }

return message_received;

}


Usage example

include

include

uint8_t test_data[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};

ACANFD_AOG keya_bus;

void setup() { Serial.begin (115200) ; Serial.println("Begin setup ..."); keya_bus.begin( 1, 1, 250); }

void loop() {

//keya_bus.can_send(test_data);
uint8_t keyaRvc[8];
if (keya_bus.can_recv(keyaRvc)){
    for (int i = 0; i < 8; ++i){
        Serial.println(keyaRvc[i], HEX);
    }
}
delay(100);

}