Spark Logic

CAN Bus Library for STM32F103

watswat5 Mon May 04, 2015 9:19 pm

I’m looking for a working CAN library for the stm32f103. I know it has the hardware for it, but looking at some example code (found here: https://www.olimex.com/Products/Duino/S … e-hardware), I’m totally lost.

Can someone create a CAN library similar to the MCP2515 library for Arduino? That library is intuitive and easy to use, even for users who know almost nothing about CAN.

The MCP2515 Library can be found here: https://github.com/coryjfowler/MCP_CAN_lib

Replies

macbeth Wed Jul 26, 2017 2:06 pm

I’d definitely vote for merging Phono’s patch into master as well.

But I still have 2 more general questions (and this seems to be the thread where the CAN-geeks hang out)

1: Is there a way to reset the communication in case of a temporary short circuit of the CAN bus? Even when I try to escape the blocking while loop in CANsend I never again get mbx == CAN_TX_NO_MBX

CAN_TX_MBX CANsend(CanMsg *pmsg) {
uint32 ct=0;
CAN_TX_MBX mbx;
digitalWrite(BOARD_LED,1);
do
mbx = canBus.send(pmsg);
while (mbx == CAN_TX_NO_MBX && ct++<20000);
digitalWrite(BOARD_LED,0);
return mbx;
}

tjb12345 Sun Aug 06, 2017 8:28 am

I am trying to get Phono’s library working on a MCP2551 and a blue pill (STM32F103C8), I have it connected to a active CANBUS, and I have successfully received and sent message using this same chip with an Arduino Due. I am trying to use the same code that And_Ru posted a while back (modified slightly to just send a message every second. However it fails and get stuck in the mbx = CAN_TX_NO_MBX loop. Assuming this is because it can’t successful transmit the messages, so they remain in the TX Mailbox, and it gets full after 3 messages. Here is my connections:

MCP2551 Blue Pill
TXD…………PB9
VSS…………GND
VDD………..5V
RXD……….PB8
Vref………Nothing
CAN H……to CAN H on CANBUS
CAN L……to CAN L on CANBUS
Rs……….to GND (through a 4.7k resistor….tried directly to GND, but the 4.7k is what use on the DUE and that works)

I have a FTDI setup on Serial1 to monitor the status, using PA2

The MPC2551 is a 5V transceiver, so on the DUE I use a logic level converter, and that works great. I tried with and without on the Blue Pill since PB8 and PB9 are 5V tolerant. I’m running at 250kbps on the baud, and I have that configured in the setup. I can’t tell what I have going on different that what And_Ru has in his video, but there must be something. Any ideas? I can post code if needed.

RogerClark Sun Aug 06, 2017 10:27 am

[Phono – Thu Jul 20, 2017 12:02 pm] –
@RogerClark: I understand that you are not experienced enough to support the HardwareCAN library, and thus, you did not include it in your last version.
However, I would like to request you to include in the next version of the core the few changes that are required to be able to include the library in a project. These changes are simple and cannot add a threat to the stability of the core.
Thanks in advance.

Is someone going to create a PR for this change?

tjb12345 Mon Aug 07, 2017 9:16 pm

Update:

been working on this yet, and I got it to send 1 message, and then it freezes. I received the first message on my Due. I tried different serial ports and no serial ports, but it still only sends 1 before freezing. I also added the digitalWrite in the main loop that fires every second to make sure it wasn’t just failing to send. It does not continue to blink. It is also not geting stuck in the No MBX loop anymore, as I put the digitalWrite there (with a small delay to make sure I could see the blink.)

Anyone out there? :?

EDIT: re-reading above posts and saw that jongjejung had the same issue, however its not clear what was wrong in his case. I definitely have 5V at the MPC2551.

#include <HardwareCAN.h>
/*
* Uses STM32duino with Phono patch. Must add 33 and 95 CAN speeds
*/
#define BPIN 0
#define SPIN 1
byte msgD0 ; // variable to be used in the example.
uint32_t lastSent = 0;
int count = 1;
int ledState = 0;
// Instanciation of CAN interface
HardwareCAN canBus(CAN1_BASE);
CanMsg msg ;

void CANSetup(void)
{
CAN_STATUS Stat ;

// Initialize CAN module
//Serial1.println("test1");
canBus.map(CAN_GPIO_PB8_PB9);
// Serial1.println("test2");// This setting is already wired in the Olimexino-STM32 board
Stat = canBus.begin(CAN_SPEED_250, CAN_MODE_NORMAL); // Other speeds go from 125 kbps to 1000 kbps. CAN allows even more choices.
// Serial1.print("Status: ");
// Serial1.println(Stat);
//Serial1.println("test3");
canBus.filter(0, 0, 0);
canBus.set_irq_mode(); // Use irq mode (recommended), so the handling of incoming messages
// will be performed at ease in a task or in the loop. The software fifo is 16 cells long,
// allowing at least 15 ms before processing the fifo is needed at 125 kbps
Stat = canBus.status();
if (Stat != CAN_OK){
// Serial1.println("CAN initialization Failed");
/* Your own error processing here */ ; // Initialization failed
}

}

// Send one frame. Parameter is a pointer to a frame structure (above), that has previously been updated with data.
// If no mailbox is available, wait until one becomes empty. There are 3 mailboxes.
CAN_TX_MBX CANsend(CanMsg *pmsg) // Should be moved to the library?!
{
CAN_TX_MBX mbx;

do
{

mbx = canBus.send(pmsg) ;
// Serial1.print("MBX: ");
// Serial1.println(mbx);
#ifdef USE_MULTITASK
vTaskDelay( 1 ) ; // Infinite loops are not multitasking-friendly
#endif

}
while(mbx == CAN_TX_NO_MBX) ;
// Waiting outbound frames will eventually be sent, unless there is a CAN bus failure.
return mbx ;
}

// Send message
// Prepare and send a frame containing some value
void SendCANmessage(long id=0x001, byte dlength=8, byte d0=0x00, byte d1=0x00, byte d2=0x00, byte d3=0x00, byte d4=0x00, byte d5=0x00, byte d6=0x00, byte d7=0x00)
{

// Initialize the message structure
// A CAN structure includes the following fields:
msg.IDE = CAN_ID_EXT; // Indicates a standard identifier ; CAN_ID_EXT would mean this frame uses an extended identifier
msg.RTR = CAN_RTR_DATA; // Indicated this is a data frame, as opposed to a remote frame (would then be CAN_RTR_REMOTE)
msg.ID = id ; // Identifier of the frame : 0-2047 (0-0x3ff) for standard idenfiers; 0-0x1fffffff for extended identifiers
msg.DLC = dlength; // Number of data bytes to follow

// Prepare frame : send something
msg.Data[0] = d0 ;
msg.Data[1] = d1 ;
msg.Data[2] = d2 ;
msg.Data[3] = d3 ;
msg.Data[4] = d4 ;
msg.Data[5] = d5 ;
msg.Data[6] = d6 ;
msg.Data[7] = d7 ;

Serial2.println("before CANSend");

CANsend(&msg) ; // Send this frame
// Serial1.println("after CANSend");

}

// The application program starts here
int bState = 0; // variable for reading the pushbutton status
int sState = 0; // variable for reading the switch status
byte st = 0x31; // buttot 1 on the CD30MP3

void setup() {
// put your setup code here, to run once:
Serial2.begin(115200);
// Serial1.println("before CANSetup");
CANSetup(); // Initialize the CAN module and prepare the message structures.
pinMode(PC13, OUTPUT);
// pinMode(BPIN, INPUT); // input for hardware button
// pinMode(SPIN, INPUT); // input for hardware switch

// Serial1.println("Hello World!");
msgD0 = 0x01;
delay(500);
}

void loop() {
// bState = digitalRead(BPIN);
// sState = digitalRead(SPIN);
//Serial1.println("loop");
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
uint32_t currentMillis = millis();
if ((currentMillis - lastSent) >= 1000) {
digitalWrite(PC13, ledState);
ledState != ledState;
lastSent = currentMillis;
uint32_t msgID = 0x1CFFEEF0 ;

// Serial1.print("start CAN Message: ");
// Serial1.println(count);

SendCANmessage(msgID, 3, 0x01, 0x6f, count) ;
count++;
}

// check if the switch is high.
// if it is:

// try to read message and output to serial
CanMsg *r_msg;
if ((r_msg = canBus.recv()) != NULL){
// Serial1.print(r_msg->ID);
// Serial1.print("#");
// Serial1.print(r_msg->Data[0]);
// Serial1.print(".");
// Serial1.print(r_msg->Data[1]);
// Serial1.print(".");
// Serial1.print(r_msg->Data[2]);
// Serial1.print(".");
// Serial1.print(r_msg->Data[3]);
// Serial1.print(".");
// Serial1.print(r_msg->Data[4]);
// Serial1.print(".");
// Serial1.print(r_msg->Data[5]);
// Serial1.print(".");
// Serial1.print(r_msg->Data[6]);
// Serial1.print(".");
// Serial1.println(r_msg->Data[7]);

canBus.free();
}

}

Phono Wed Aug 09, 2017 8:31 pm

Been in vacations for several weeks, thus my silence.
@macbeth
Question 1 :
you can insert in the loop that processes incoming messages the following code:
if ( canBus.Port->ESR & CAN_ESR_BOFF )
CANSetup() ;

tjb12345 Thu Aug 10, 2017 4:20 am

I did go through the changes.h file, and I thought I had them all, but I might have missed it. Its very possible that I overlooked one or got mixed up, as I copied and moves the core files when I was trying different things. Knowing me I might have changed ones that weren’t in the Arduino folder.

I double check tomorrow and report back.

Thanks much.

tjb12345 Fri Aug 11, 2017 8:49 pm

[Phono – Wed Aug 09, 2017 8:31 pm] –
@tjb12345:
looks like the transmit interrupt is not activated. Did you perform the changes in the two core files as described in the file changes.h of the example of the HardwareCAN library?

@all: these changes in the core files are what I request RogerClark to include in the root to allow for integration of the library!

So I cleaned out the STM32 core files and started over, downloading the branch from RogerClark’s repo that he pulled in from your fork

this link: https://github.com/rogerclarkmelbourne/ … og-patch-1

Went through the changes.h file, but they were all made already, with one exception. (Im assuming since I am pulling from your fork?)

the only difference (unless im blind) is the last CAN_RX0_IRQ_Handler function. in the changes.h file it returns 1, but the core file had return 0. I changed it but it still gets hung.

I just got an Olimexino board delivered today, so im going to try with that as well to eliminate hardware, but I can take this same MPC2551 and hook it to my Due, and it works on that.

2.3.1) inserted 12 lines, position 186
// JMD : default ISRs of CAN, to be overridden if HardwareCAN library is used in sketch
void __attribute__((weak)) USB_HP_CAN_TX_IRQHandler(void)
{ ; } // Dummy ISR

void __irq_usb_hp_can_tx(void)
{
USB_HP_CAN_TX_IRQHandler () ;
}

uint8 __attribute__((weak)) CAN_RX0_IRQ_Handler(void)
{ return 1 ; } // Dummy ISR

tjb12345 Mon Aug 14, 2017 2:41 pm

So I decided to try testing in Loopback mode, to eliminate the MCP2551 from the setup. I tested in loopback mode on the blue pill, and on the Olimexino (STM32F103RBT6) board that I just got. Script works no problem on the that board. waits 5 seconds, sends 1 message, and then returns to the loops and just flashes led (not using delay() here).

The Blue pill sends the message and then freezes. I have replaced the 1.5k R10 resistor on the D+ line, and tried to see if there was other soldering issues, but I can’t find any. Tried it on a different board as well with same results. I know they are cheap clones, so maybe all the ones i have have something wrong with them.

For now I am going to continue my prototyping with the Olimexino-STM32 board, but I would like to use the blue pill if possible, as they are cheaper and smaller, and I already have a CAN transceiver board designed that matches up to it.

Phono Mon Aug 14, 2017 8:11 pm

Oops!
There is an error in my file changes.h.

the dummy interrupt service routine in C:\ArduinoForSTM32\arduino-1.6.9\hardware\Arduino_STM32-master\STM32F1\cores\maple\libmaple\usb\stm32f1\usb.c
should read
uint8 __attribute__((weak)) CAN_RX0_IRQ_Handler(void)
{ return 0 ; } // Dummy ISR
If you change it to 1, you crash the usb operation when the HardwareCAN library is not used.

I have only used the olimexino STM32 board, and it works. I did not try with another board. When you can, check with the olimexino board and tell me the result.

tjb12345 Mon Aug 14, 2017 8:26 pm

I was working on this right now, and was struggling to get the USB working on my olimexino STM32 board….was able to upload to is and everything, but couldn’t get it to stay showing as a COM Port, that change fixed it right away, I have the BlinkNcount sketch running on it now. Going to move forward and try to get CANBUS function along with Serial on a hardware serial port with my FTDI (need serial to read the messages for prototyping.) If I can get this working, then I’m going to go back to trying it on the Blue Pill….

Thanks for the fix!

tjb12345 Tue Aug 15, 2017 1:59 am

I got the Olimexino STM32 working to send and receive CAN Messages like I want. The high speed is going to be a nice improvement over the Uno. I would still like to get the BluePill working but there must be some slight difference that is causing my problem.
To recap:

It successfully sends 1 message, but then freezes (or gets stuck in a loop somewhere). I am using a MPC2551 Transceiver, which is verified to have 5 volts supplied to it. The code doesn’t work in Loopback Mode either though, so I am pretty sure it’s either the BP hardware, or the something in the STMF103C Generic Code (it works with the Maple (rev 3) code for the Olimexino board).

BP has been modified with 1.5k resistor in place of the 10k at R10. I hit the USB pins with the soldering iron to make sure there wasn’t a bad solder (not using USB though, uploading via serial to USART1 (PA9 and PA10). I was able to get the USB to work on the BP as well, running the BlinkNCount example, so that leads me to believe that the hardware is OK.

One difference is I used the USB to upload the CAN sketch to the Olimexino, but then used D7 , D8 for the Serial adapter to print the messages. on the Blue Pill, I am uploading right from the serial adapter, would that make any difference?

I am convinced that this will work, as it seems that someone else had it working earlier in the thread.

RogerClark Tue Aug 15, 2017 3:52 am

CAN and USB share hardware resources in the MCU.

You can’t simply run CAN and expect USB to still run, unless you modify the USB system to co-exist

tjb12345 Tue Aug 15, 2017 1:37 pm

[RogerClark – Tue Aug 15, 2017 3:52 am] –
CAN and USB share hardware resources in the MCU.

You can’t simply run CAN and expect USB to still run, unless you modify the USB system to co-exist

I’m not trying to use both…that why I’m uploading and running Serial through USART1 and an FTDI adapter. I only used the USB on the BlinkNCount Example to make sure that part actually worked.

Similar with the Olimexino, I used USB to upload the CAN sketch, but did not try to use SerialUSB to communicate with the serial monitor.

I’m just trying to figure out why when loaded onto a generic STM32F103C8 that it behaves differently than a STM32103RB. I do agree that the USB is probably whats

RogerClark Tue Aug 15, 2017 9:18 pm

how do you upload using the bootloader?

Did you select the serial uploaded option, then export the binary and then manually run DFU from the command line?

If you select bootloader upload from the menu in the IDE it enables all the USB subsystem code so that the sketch can be automatically triggered via USB to upload again the next time.

tjb12345 Tue Aug 15, 2017 9:54 pm

For the Generic, I am just choosing Serial from the “Upload Method” and I have my FTDI adapter hooked up to PA9 and PA10. It uploads fine that way, but maybe it doesn’t include the code to for the interrupt?

RogerClark Tue Aug 15, 2017 9:56 pm

Yes. If you choose Serial upload, the compile options remove all USB code, however most other options include the USB code

tjb12345 Fri Aug 18, 2017 10:02 pm

[RogerClark – Tue Aug 15, 2017 9:56 pm] –
Yes. If you choose Serial upload, the compile options remove all USB code, however most other options include the USB code

I knew there was going to be some little thing I was doing wrong……I got it working now! In loopback mode anyway, but I’m confident it will work other ways as well.

I am still not having luck uploading with USB after I flashed the PC13 Generic Bootloader, but I am able to program it using my ST-Link Tool and that option in the upload method, and It doesn’t make on difference to me anyway, since I can’t use USB for serial anyway.

I also tried adding a line to the boards.txt file like this

genericSTM32F103C.menu.upload_method.serialMethod.build.upload_flags=-DSERIAL_USB

RogerClark Fri Aug 18, 2017 10:10 pm

You can upload via the USB bootloader, and have Serial USB disabled in the core, however you will have to manually reset to upload, as the IDE uses USB to trigger the board into the bootloader in order to upload

But, I think there are more than one Define you may need to change

And_Ru Sun Sep 03, 2017 9:50 pm

1) I know I’m late, but if you have only first message sent to CAN and then hang up, it is good to check if receiver party works well (receive and acknowledge the message).

2) to remain USB serial in the board I just added the check of one pin in the setup:

Serial1.begin(115200);
pinMode(28, INPUT); // B12 = 16+12 = 28
digitalWrite(28, LOW);
delay(50);
Serial1.println("pin B12 set to input mode");

usbMode = digitalRead(28);

Serial1.print("pin B12 is ");
Serial1.println(usbMode);

if (usbMode)
{
Serial1.println("Entering USB mode");
Serial.begin(9600);
Serial.println("Entering USB mode");
while (1)
{
Serial.print(".");
Serial1.print(".");
digitalWrite(PC13, (!digitalRead(PC13)));
delay(1000);
}
}
else
{
digitalWrite(PC13, PC13OFF);
Serial1.println("Starting CAN module ...");
CANSetup() ; // Initialize the CAN module and prepare the message structures.
}
Serial1.println("Setup finished, going to loop()");

Akash Tue Sep 12, 2017 6:55 am

Hello friends, I am using olimexino STM32 board with CAN bus ANalyzer. From HardwareCAN library is suitable for sending message but we can not filter message by list,it can only read all message. for doing filter list, what should I need to change below??

CAN_STATUS can_filter(CAN_Port* CANx, uint8 filter_idx, CAN_FIFO fifo, CAN_FILTER_SCALE scale, CAN_FILTER_MODE mode, uint32 fr1, uint32 fr2)
{
uint32 mask = ((uint32)0x00000001) << filter_idx;

CANx->FMR |= CAN_FMR_FINIT; // Initialization mode for the filter
CANx->FA1R &= ~mask; // Deactivation filter

if (scale == CAN_FILTER_32BIT)
CANx->FS1R |= mask;
else
CANx->FS1R &= ~mask;

CANx->sFilterRegister[filter_idx].FR1 = fr1;
CANx->sFilterRegister[filter_idx].FR2 = fr2;

if (mode == CAN_FILTER_MASK)
CANx->FM1R &= ~mask;
else
CANx->FM1R |= mask;

if (fifo == CAN_FIFO0)
CANx->FFA1R &= ~mask;
else
CANx->FFA1R |= mask;

CANx->FA1R |= mask;
CANx->FMR &= ~CAN_FMR_FINIT;
return CAN_OK;
}

Sign in to reply