Difference between revisions of "STM32 CAN"
(→Code) |
|||
(3 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 27: | Line 31: | ||
| valign=top | [[File:Can loopback example can nvic.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 == | == Real CAN bus example == |
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:
Notice the APB1 bus is running at 42 MHz. The CAN peripherals are connected to this bus.
We can now configure the CAN1 periopheral:
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