CAN Protocol in STM32

This tutorial will cover the Basic Can protocol in STM32. Here we will see, how to communicate between two STM32 boards using the CAN protocol. Of course we would need 2 can transceivers (at least) to do that, and that’s why I am using MCP2551 can transceivers.

A Little info about the CAN Protocol

I am not going to explain every small detail here, instead we will just focus on some important things. For more details about the Protocol, you can google it.

CAN (Controlled Area Network) Protocol is a way of communication between different devices, but under certain rules. These rules must be followed when a message is transmitted over the CAN bus. Here we are going to see these rules.

Shown below is the Standard CAN Frame

  • Here, Identifier is the ID of the transmitting Device
  • RTR (Remote Transmission Request) Specifies if the data is Remote frame or Data frame
  • IDE specifies if we are using Standard ID or Extended ID
  • r is the Reserved bit
  • DLC specifies the data length in Bytes
  • Data Field is where we can send the data, which should be upto 8 bytes
  • Checksum and DEL are the CRC data and it’s Delimiter
  • ACK and DEL is the acknowledgment bit and it’s Delimiter

In this Tutorial, we will see upto the Data Field only. The CRC and ACK will be handled by the HAL Library.



Connection and Configuration

The CubeMX Configuration is as shown below

  • Here the BAUD RATE is set to 500000 bps. You can try different different combinations for Prescalar and Time Quanta to achieve this.
  • The Operating Mode is NORMAL Mode
  • Pins PA11 and PA12 are set as CAN_RX and CAN_TX

Update regarding new version of IDE

NOTE:- You should also pull the RX pin HIGH using the Internal PULL-UP.

The Connection between F446 and F103 is shown below.

  • Here the Tx and Rx from the Transceivers are connected to PA12 and PA11 of the Respective controllers
  • CANH and CANL are connected to each other
  • Also there is 120 ohms Resistance at each node. This is very important, or else you will not get the data.


How to Modify the CAN Data Frame

To do this, we will define some variable, where we can store the header and the data.

CAN_TxHeaderTypeDef   TxHeader;

uint8_t               TxData[8];

uint32_t              TxMailbox;
  • Here TxHeader will be used to store the header information, like RTR, DLC, etc. This is type CAN_TxHeaderTypeDef
  • TxData is used to store the data, that we are going to transmit over the CAN bus
  • TxMailbox is the mailbox, which will be sent to the CAN bus

Now we will store the required values in the TxHeader, and in the TxData.

TxHeader.IDE = CAN_ID_STD;
TxHeader.StdId = 0x446;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.DLC = 2;

TxData[0] = 50;  
TxData[1] = 0xAA;
  • Here CAN_ID_STD means that we are using the Standard ID (not extended)
  • 0x446 is the Identifier. This is the ID of the Transmitter, and it should be maximum 11 bit wide
  • CAN_RTR_DATA indicates that we are sending a data frame
  • DLC is the Length of data bytes, and here we are sending 2 data Bytes
  • Now we will store the 2 data bytes in the TxData array

We have the information ready to be transmitted, and now we will finally transmit it on the CAN bus

if (HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox) != HAL_OK)
{
   Error_Handler ();
}

This can be done by using HAL_CAN_AddTxMessage. It have the following parameters

  • hcan1 is the instance of the CAN, that we are using
  • TxHeader is the Header of the message
  • TxData is the Data field
  • TxMailbox is the mailbox, which will carry the header and data message

You can see above the data on the TX Line.

  • Identifier is 0x446, the STD ID of the transmitter
  • Control Field is 0x2, it contains DLC, RTR, IDE
  • 2 Bytes of data Field
  • And at last there is CRC Value, which was added by the HAL

This message is sent to the CAN bus, and now all the CAN devices on this bus will sort of receive this message. I said sort of, because whether to receive the message or not, depends on the Filter Configuration for each device.

If the message satisfies the conditions as per the FILTER, only then it will be allowed to pass.






Filter Configuration

In order to reduce CPU Load to filter out messages, the STM32 have the Filters built inside the CAN peripheral. Let’s Check them out

  CAN_FilterTypeDef canfilterconfig;

  canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
  canfilterconfig.FilterBank = 18;  // which filter bank to use from the assigned ones
  canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
  canfilterconfig.FilterIdHigh = 0x446<<5;
  canfilterconfig.FilterIdLow = 0;
  canfilterconfig.FilterMaskIdHigh = 0x446<<5;
  canfilterconfig.FilterMaskIdLow = 0x0000;
  canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
  canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
  canfilterconfig.SlaveStartFilterBank = 20;  // how many filters to assign to the CAN1 (master can)

  HAL_CAN_ConfigFilter(&hcan1, &canfilterconfig);
  1. FilterActivation specifies if we want to enable Filters or not. Obviously we have to enable them
  2. SlaveStartFilterBank specifies How many Filter Banks do we want to assign to CAN1. Basically the controllers with dual CAN peripheral have 28 Filter Banks, which can be distributed between these 2 CAN. Here I am assigning 20 Filter Banks to the CAN1, and Rest to the CAN 2.
    • This parameter is useless for the controllers with single CAN peripheral. And these Controllers have 14 Filter Banks ( 0 to 13)
  3. FilterBank specifies which Filter Bank do we want to use for the filter Process. Here I have assigned 20 Banks for CAN 1, and I can only choose Out of these 20 Banks. So I am choosing Bank number 18.
    • In case of Single CAN Peripheral, you can choose any value between 0 to 13
  4. FilterFIFOAssignment specifies which FIFO are we going to use for the Receive message. Generally we have 2 FIFOs ( FIFO 0, and FIFO 1). I am choosing FIFO 0
  1. FilterMode specifies which type of Filter do we want to use. We have 2 types of filters in STM32. MASK MODE, where the Mask register will be used to compare some particular bits in the ID register to the incoming ID. And the LIST MODE, where the incoming ID is directly compared with the ID set in the ID Register.
    • I am using MASK Mode here, as It seems to be more useful
  2. FilterScale specifies If we want to use one 32 bit Filter Register, or 2 16 bit Filter Registers.
    • I am using one 32 Bit Register here.
  3. FilterIdHigh is the Higher 16 Bits of the ID register. The value set in this register will be compared to the incoming Identifier.
    • Here I have decided to only compare the STD ID of the incoming message, and that’s why I am shifting the value by 5. The STD ID starts from 5th bit in the ID HIGH Register
  4. FilterMaskIdHigh is the Higher 16 Bits of the MASK register. The value set in this register will enable the comparison of that particular bit in the ID register to that of the incoming ID.

The Last 2 points might be hard to understand, so I would suggest that you watch the video below. It could be better explained with the working example, and that’s shown in the VIDEO.

Check out the Video Below



Receiving DATA

We will use the interrupt for the RX FIFO, so whenever a message is passed through the Filter an interrupt will be triggered.

First of all We will enable the CAN1 RX0 interrupt in the CubeMX


Now Inside the main Function, we will Activate the Notification for the Received message.

  if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
  {
	  Error_Handler();
  }

Here we will choose CAN_IT_RX_FIFO0_MSG_PENDING. This would trigger the interrupt, whenever there is some pending message in the RX_FIFO 0. Once the interrupt is triggered, a callback function will be called. In this case, it will be HAL_CAN_RxFifo0MsgPendingCallback

CAN_RxHeaderTypeDef   RxHeader;

uint8_t               RxData[8];

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
  if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
  {
    Error_Handler();
  }

  if ((RxHeader.StdId == 0x103))
  {
	  datacheck = 1;
  }
}
  • Here we will Receive the message from RX_FIFO 0.
  • The message Header will be stored in the RxHeader, and the data will be stored in RxData.
  • We can do further checks, like if the message was received from the ID 0x103, then the datacheck flag will be set.
  • Later in the while loop we can perform some actions based on this flag
if (datacheck)
{
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}

For example, If the Flag is set, The LED will turn ON.




RESULT

The result here is hard to put in images, so I would suggest that you watch the video for more detailed working.

Check out the Video Below










Info

You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

34 Comments. Leave new

  • Mohammadreza Mehrdad
    December 3, 2023 7:00 PM

    Hello,
    Thanks for your useful tutorials.

    I am going to use Extended CAN of stm32 but I could not find how I can do it. Is it possible you help and tell me which part of the code need to be changed.

    Regards,
    Mehrdad

    Reply
  • Olá amigo, qualo modelo das placas, a menor é a BluePill e a outra é que núcleo?

    Reply
  • Hi, I am using stm32f407 I tried your code but there is no data is received,i don’t know what I
    Missed

    Reply
  • Hey man! Big thanks, your code examples are a great help, especially this one. Video tutorials on youtube are great too. (if only not for the robotic voice-over, but I understand how hard it is to make a proper voice-over)

    Reply
  • super ra kaasi

    Reply
  • Thank you very much for this helpful tutorial.
    I also watched the vid on youtube for the can one master multiple slaves.
    Do I understand right: I can only tell a controller to which other controllers (IDs) to listen to, but I cannot tell the master to send a message to a certain slave?
    So all slaves listen to the master, and the master message passes the filter of all slaves, and I have to pack a “address information” into the data part of the message, which every slave has to check then?
    I would be very happy if you could clarify this for me 🙂

    Reply
  • Thamanoon Kedwiriyakarn
    July 6, 2022 9:13 AM

    Thank you very much Sir.

    Reply
  • I have to establish CAN bus communication between two stm32f407VET6 boards and it doesn’t work for me, can I directly connect tx to tx and rx to rx? because I understand that the boards have two integrated can, I do not have the MCP2551, I have two MCP2515

    Reply
    • You need can transceivers, you can’t directly connect them.
      MCP2515 is SPI to CAN, and this tutorial does not cover that.

      Reply
  • Hi, I am using STM32F103C8 and I have tried your code and setup. However, your code is working on “loopback mode” and not working in “Normal Mode”. I don’t know why, it won’t transmit and wont receive message. I believe there is other people commented this problem on your youtube (CAN Normal Mode) video but has yet get an answer, also there are many other facing this issue as well (stack forum or st community) both also do not have answer. If anyone reading this and have solution please share it here. Thank you.
    Extra Info:
    If I configure in loopback mode, I can transmit message from MCU to other CAN analyzer device and to internal Rx (I verified by oscilloscope and CAN analyzer device successfully received.)

    Reply
    • Do you using STM32F103C8 2 board right ? I have problem same with you.

      Reply
      • I am not using STM32F103C8 blue pill board. Mine is just the microcontroller(STM32F103C8T6) itself. I owned a STM32F303 Nucleo board as well and Have tried controllertech’s code and it is working.
        Also list of configurations I have tried on STM32F103:
        1) adjusting the sample point to ~80%
        2) tried internal clk and external clk
        3) FIFO0 & FIFO1
        4) different baudrate
        5) CAN port remapping
        6) CAN Config Filter ID etc
        7) check on hardware wiring, connection etc
        8) new or spare STM32F103
        9) ST’s example code in STM32Cube_FW_F1_V1.X.X
        Unfortunately, non of them is working. At this point I think STM32F103C8 CAN peripheral is broken. I wish I have a blue pill, I believe STM32F103 on blue pill will not have this issue.
        Also If anyone has solution please share it here Thank you.

        Reply
        • Finally I get it to work. The problem is Vcc. I am supplying 3.3V to transceiver which is not enough. The transceiver I am using required Vcc in range of 4.7 – 5.2 V. Lesson learned, read datasheet thoroughly.

          Reply
          • Thanks for sharing!

            Your comment helped me to realize where the past 6 hours have gone.

            I am using MCP2562 and it has 2 power supplies, Vio and Vdd. I thought both are 3.3V, but it turns out that Vdd should be 5V.

            Lesson learned – read carefully the documentation before soldering 😉

      • can i see your program and setting on stm cube??

        Reply
  • Hello,

    ı want to know, which is id transmitted message to me? How can ı lean?

    “HAL_CAN_AddTxMessage” methods accept uint8_t data, can ı transmit array? 

    In my scenario I have a master and 5 slaves. 5 slaves are sending messages to master. And I want to know, which slave sent the message to the master?

    Thank you for this post.

    Reply
    • in the received callback, you can check it by using ID = RxHeader.StdID;
      the ID will be stored in the ID variable

      Reply
  • Нужны stm32 can

    Reply
  • Do you have any good sources on what modifications need to be made to use the FDCAN peripheral on the H7 processors? I can see some signals making it back and forth between the two boards but they analyze as Errors and after two exchanges back and forth the CAN State get stuck in BUSY.

    Reply
    • All fixed. The CAN was BUSY because I was forgetting to call GetMessage and the buffer I’m guessing was full. Using this tutorial mixed with the example project from HAL on FDCAN worked great. Thanks!

      Reply
  • Lakshminarayana
    February 10, 2022 6:43 PM

    Hello Thanks for the tutorial it was helpful but i have one problem, i can only receive the data in Blue Pill but the data sent by the Bull pill is not received at F4 controller and i am using CAN BUS 2 for the communication i need as all suggested by you. but still no way i could achieve it.

    could you please guide me what wrong i am doing.

    Thanks
    With regards
    Lakshminarayana KS

    Reply
  • super bro

    Reply
  • Hey, This worked well.

    Now is it possible to use multiple addresses with a device to transmit data?
    That is the slave has multiple addresses for a single device, each address has different data values. Now the master accesses the slave with multiple addresses according to the required data values.

    For example:- The master requires the data”time” in the slave, the master uses the particular address(0x102) for the data “time”.If the master requires the data “Brightness” in the slave, the master uses the particular address(0x103) for the data “Brightness”.Likewise….

    Can you give me a solution to work out this in STM32F103C8T6?

    Reply
  • I use CAN1 for F407VG, it works normal but use CAN2 it does not receive data. I dont know why?

    Reply
    • check out the filter configuration properly. There are different filters provided for both the CANs. I have mentioned that in the first video

      Reply
      • I don’t use the filter configuration for CAN2, it sends data normally but still does not receive data.

        Reply
  • VAN-NHI NGUYEN
    August 1, 2021 2:21 PM

    Hello, i don’t know what the value 0x443 in STD ID is for, while on the receiver side we check the value 0x103.

    Reply
    • I have made the necessary corrections. Actually I was trying to show multiple IDs, you should watch the video to understand it

      Reply
  • Hi Admin,
    Thank you so much for your tutorials…!
    I am STM32F103 CAN (MCP2561 as CAN transceiver) with 500kbps and CAN BUS ANALYZER to check the CAN msgs.
    Here am sending 3 CAN msgs for every 100ms but some times some msgs are missing so that am getting the CAN cycle time as 200ms and I have followed the same procedure as above but some times my CAN msgs are missing.
    what I have to do eliminate this error ?
    Thanks again

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

keyboard_arrow_up

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add controllerstech.com to your ad blocking whitelist or disable your adblocking software.

×