'; viAPItag.display("pw_18622"); } else { document.getElementById("div-vi-1725427021").innerHTML = '

'; viAPItag.display("pw_18620"); } })

CAN Protocol in STM32

This tutorial will cover the Basic Can protocol in STM32. Here we will see how to communicate between two STM32 MCUs 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.

Below is the image showing the Standard CAN Frame.

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.

I am using CAN1 for this tutorial.

We also need to enable the pull up for the RX pin as shown below.


The Connection between F446 and F103 is shown below.



How to Modify the CAN Data Frame

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

CAN_TxHeaderTypeDef   TxHeader;
uint8_t               TxData[8];
uint32_t              TxMailbox;

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;

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 the function HAL_CAN_AddTxMessage. It have the following parameters

You can see above the data on the TX Line.

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;
  }
}
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.

Subscribe
Notify of

34 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
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.

×