Difference between revisions of "STM32 CAN"

From Stm32World Wiki
Jump to navigation Jump to search
 
(9 intermediate revisions by the same user not shown)
Line 5: Line 5:
  
 
== Loopback Example ==
 
== Loopback Example ==
 +
 +
The [[CAN]] peripheral on [[STM32]] MCU's can be configured to run in loopback mode.  This makes it possible to test CAN programming without actually hooking the device up to a physical CAN bus.
 +
 +
The source for this example is here: [https://github.com/STM32World/firmware/tree/master/mcustm32f405_can_loopback https://github.com/STM32World/firmware/tree/master/mcustm32f405_can_loopback]
  
 
=== CubeMX Config ===
 
=== CubeMX Config ===
Line 11: Line 15:
  
 
<div class="res-img">
 
<div class="res-img">
[[File:Can loopback example clock.png|1200px]]
+
[[File:Can loopback example clock.png]]
 +
</div>
 +
 
 +
Notice the APB1 bus is running at 42 MHz.  The CAN peripherals are connected to this bus.
 +
 
 +
<div class="res-img">
 +
[[File:Can loopback example pinout.png]]
 
</div>
 
</div>
 +
 +
We can now configure the CAN1 periopheral:
 +
 +
{| width=100%
 +
|-
 +
| valign=top | [[File:Can loopback example can1.png]]
 +
| valign=top | [[File:Can loopback example can nvic.png]]
 +
|}
 +
 +
=== Code ===
 +
 +
First let's drop a few global variables to handle the CAN messages:
 +
 +
<pre>
 +
/* USER CODE BEGIN PV */
 +
 +
CAN_TxHeaderTypeDef TxHeader;
 +
CAN_RxHeaderTypeDef RxHeader;
 +
 +
uint32_t TxMailbox;
 +
 +
uint8_t TxData[8];
 +
uint8_t RxData[8];
 +
 +
uint32_t msg_count = 0;
 +
 +
/* USER CODE END PV */
 +
</pre>
 +
 +
Second, we create a callback to receive messages:
 +
 +
<pre>
 +
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
 +
{
 +
    DBG("HAL_CAN_RxFifo0MsgPendingCallback");
 +
    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) {
 +
        DBG("Got message %lu - id = 0x%04lx len = 0x%lx, data=%02x%02x%02x%02x%02x%02x%02x%02x", msg_count + 1, RxHeader.StdId, RxHeader.DLC, RxData[0], RxData[1], RxData[2], RxData[3], RxData[4], RxData[5], RxData[6], RxData[7]);
 +
    }
 +
    msg_count++;
 +
}
 +
</pre>
 +
 +
We can now initialize the [[CAN]] peripheral and the filters:
 +
 +
<pre>
 +
  /* USER CODE BEGIN 2 */
 +
 +
    DBG("\n\n\n\n\n---------------------\nCAN Loopback Starting");
 +
 +
    CAN_FilterTypeDef canfilterconfig;
 +
 +
    canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
 +
    canfilterconfig.FilterBank = 12;  // anything between 0 to SlaveStartFilterBank
 +
    canfilterconfig.FilterFIFOAssignment = CAN_RX_FIFO0;
 +
    //canfilterconfig.FilterIdHigh = 0x103<<5;
 +
    canfilterconfig.FilterIdHigh = 0x0000;
 +
    canfilterconfig.FilterIdLow = 0x0000;
 +
    //canfilterconfig.FilterMaskIdHigh = 0x1<<13;
 +
    canfilterconfig.FilterMaskIdHigh = 0x0;
 +
    canfilterconfig.FilterMaskIdLow = 0x0;
 +
    canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
 +
    canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
 +
    canfilterconfig.SlaveStartFilterBank = 13;  // 13 to 27 are assigned to slave CAN (CAN 2) OR 0 to 12 are assgned to CAN1
 +
 +
    HAL_CAN_ConfigFilter(&hcan1, &canfilterconfig);
 +
 +
    HAL_CAN_Start(&hcan1);
 +
 +
    HAL_CAN_ActivateNotification(
 +
            &hcan1,
 +
            CAN_IT_TX_MAILBOX_EMPTY |
 +
            CAN_IT_RX_FIFO0_MSG_PENDING |
 +
            CAN_IT_RX_FIFO0_FULL |
 +
            CAN_IT_RX_FIFO0_OVERRUN |
 +
            CAN_IT_RX_FIFO1_MSG_PENDING |
 +
            CAN_IT_RX_FIFO1_FULL |
 +
            CAN_IT_RX_FIFO1_OVERRUN |
 +
            CAN_IT_WAKEUP |
 +
            CAN_IT_SLEEP_ACK |
 +
            CAN_IT_ERROR_WARNING |
 +
            CAN_IT_ERROR_PASSIVE |
 +
            CAN_IT_BUSOFF |
 +
            CAN_IT_LAST_ERROR_CODE |
 +
            CAN_IT_ERROR
 +
        );
 +
 +
  /* USER CODE END 2 */
 +
</pre>
 +
 +
In the main loop we can send a message with the current timestamp:
 +
 +
<pre>
 +
  /* USER CODE BEGIN WHILE */
 +
 +
    uint32_t now = 0, last_blink = 0, last_tx = 0;
 +
 +
    while (1) {
 +
 +
        now = HAL_GetTick();
 +
 +
        if (now - last_tx >= 100) {
 +
 +
            TxHeader.DLC = 4;
 +
            TxHeader.ExtId = 0;
 +
            TxHeader.IDE = CAN_ID_STD;
 +
            TxHeader.RTR = CAN_RTR_DATA;
 +
            TxHeader.StdId = 0x601;
 +
            //TxHeader.TransmitGlobalTime = DISABLE;
 +
 +
            if (HAL_CAN_AddTxMessage(&hcan1, &TxHeader, (uint8_t *)&now, &TxMailbox) != HAL_OK)
 +
            {
 +
                Error_Handler();
 +
            }
 +
 +
            last_tx = now;
 +
        }
 +
 +
        if (now - last_blink >= 500) {
 +
 +
            HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
 +
 +
            last_blink = now;
 +
 +
        }
 +
 +
    /* USER CODE END WHILE */
 +
</pre>
 +
 +
== Real CAN bus example ==
 +
 +
To be added
  
 
== Miscellaneous Links ==
 
== Miscellaneous Links ==
  
 
To be added
 
To be added

Latest revision as of 13:15, 23 December 2023

Introduction

Most STM32 MCUs are equipped with one or more CAN peripherals.

Loopback Example

The CAN peripheral on STM32 MCU's can be configured to run in loopback mode. This makes it possible to test CAN programming without actually hooking the device up to a physical CAN bus.

The source for this example is here: https://github.com/STM32World/firmware/tree/master/mcustm32f405_can_loopback

CubeMX Config

The clock is configured to maximum speed using the external crystal:

Can loopback example clock.png

Notice the APB1 bus is running at 42 MHz. The CAN peripherals are connected to this bus.

Can loopback example pinout.png

We can now configure the CAN1 periopheral:

Can loopback example can1.png Can loopback example can nvic.png

Code

First let's drop a few global variables to handle the CAN messages:

/* USER CODE BEGIN PV */

CAN_TxHeaderTypeDef TxHeader;
CAN_RxHeaderTypeDef RxHeader;

uint32_t TxMailbox;

uint8_t TxData[8];
uint8_t RxData[8];

uint32_t msg_count = 0;

/* USER CODE END PV */

Second, we create a callback to receive messages:

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    DBG("HAL_CAN_RxFifo0MsgPendingCallback");
    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) {
        DBG("Got message %lu - id = 0x%04lx len = 0x%lx, data=%02x%02x%02x%02x%02x%02x%02x%02x", msg_count + 1, RxHeader.StdId, RxHeader.DLC, RxData[0], RxData[1], RxData[2], RxData[3], RxData[4], RxData[5], RxData[6], RxData[7]);
    }
    msg_count++;
}

We can now initialize the CAN peripheral and the filters:

  /* USER CODE BEGIN 2 */

    DBG("\n\n\n\n\n---------------------\nCAN Loopback Starting");

    CAN_FilterTypeDef canfilterconfig;

    canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
    canfilterconfig.FilterBank = 12;  // anything between 0 to SlaveStartFilterBank
    canfilterconfig.FilterFIFOAssignment = CAN_RX_FIFO0;
    //canfilterconfig.FilterIdHigh = 0x103<<5;
    canfilterconfig.FilterIdHigh = 0x0000;
    canfilterconfig.FilterIdLow = 0x0000;
    //canfilterconfig.FilterMaskIdHigh = 0x1<<13;
    canfilterconfig.FilterMaskIdHigh = 0x0;
    canfilterconfig.FilterMaskIdLow = 0x0;
    canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
    canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
    canfilterconfig.SlaveStartFilterBank = 13;  // 13 to 27 are assigned to slave CAN (CAN 2) OR 0 to 12 are assgned to CAN1

    HAL_CAN_ConfigFilter(&hcan1, &canfilterconfig);

    HAL_CAN_Start(&hcan1);

    HAL_CAN_ActivateNotification(
            &hcan1,
            CAN_IT_TX_MAILBOX_EMPTY |
            CAN_IT_RX_FIFO0_MSG_PENDING |
            CAN_IT_RX_FIFO0_FULL |
            CAN_IT_RX_FIFO0_OVERRUN |
            CAN_IT_RX_FIFO1_MSG_PENDING |
            CAN_IT_RX_FIFO1_FULL |
            CAN_IT_RX_FIFO1_OVERRUN |
            CAN_IT_WAKEUP |
            CAN_IT_SLEEP_ACK |
            CAN_IT_ERROR_WARNING |
            CAN_IT_ERROR_PASSIVE |
            CAN_IT_BUSOFF |
            CAN_IT_LAST_ERROR_CODE |
            CAN_IT_ERROR
        );

  /* USER CODE END 2 */

In the main loop we can send a message with the current timestamp:

  /* USER CODE BEGIN WHILE */

    uint32_t now = 0, last_blink = 0, last_tx = 0;

    while (1) {

        now = HAL_GetTick();

        if (now - last_tx >= 100) {

            TxHeader.DLC = 4;
            TxHeader.ExtId = 0;
            TxHeader.IDE = CAN_ID_STD;
            TxHeader.RTR = CAN_RTR_DATA;
            TxHeader.StdId = 0x601;
            //TxHeader.TransmitGlobalTime = DISABLE;

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

            last_tx = now;
        }

        if (now - last_blink >= 500) {

            HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);

            last_blink = now;

        }

    /* USER CODE END WHILE */

Real CAN bus example

To be added

Miscellaneous Links

To be added