STM32 CAN

From Stm32World Wiki
Jump to navigation Jump to search

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