Introduction
The Controller Area Network (CAN) bus is a robust vehicle bus standard designed to allow microcontrollers and devices to communicate with each other in applications without a host computer. It is widely used in automotive and industrial environments due to its high resilience to interference and ability to function over long distances. In this blog post, we will delve into the technical implementation of the CAN protocol on microcontrollers, specifically focusing on the practical setup, programming, and debugging of a CAN network.
CAN Protocol Basics
The CAN protocol was designed to prioritize reliability and speed in harsh environments. A key feature of CAN is message arbitration, which ensures that in case of simultaneous transmission, the message with the highest priority will be sent first without collision. This is managed through non-destructive bitwise arbitration.
Key Features:
- Multi-master communication: Any device can initiate communication.
- Error detection and signaling: Increased reliability.
- Priority-based message arbitration: Critical messages take precedence.
Hardware Setup
For this demonstration, we will use two STM32F446RE microcontrollers connected via a CAN transceiver to each CAN node. Each STM32 board is programmed to send and receive messages, emulating a real-world scenario where multiple devices communicate over CAN.
Components Required:
- 2 x STM32F446RE Nucleo boards
- 2 x SN65HVD230 CAN bus transceivers
- Jumper wires
- Breadboard
Wiring Diagram
[STM32] -- [SN65HVD230] -- [CAN_H, CAN_L] -- [SN65HVD230] -- [STM32]
Software Implementation
We will configure one STM32 as a CAN sender and the other as a receiver. The setup involves initializing the CAN peripheral, setting up message filters (for the receiver), and coding the transmission and reception logic.
Initialize CAN:
void CAN_Init(void) {
CAN_HandleTypeDef CanHandle;
CAN_FilterConfTypeDef sFilterConfig;
// Basic CAN configuration
CanHandle.Instance = CAN1;
CanHandle.Init.TimeTriggeredMode = DISABLE;
CanHandle.Init.AutoBusOff = DISABLE;
CanHandle.Init.AutoWakeUp = DISABLE;
CanHandle.Init.AutoRetransmission = ENABLE;
CanHandle.Init.ReceiveFifoLocked = DISABLE;
CanHandle.Init.TransmitFifoPriority = DISABLE;
HAL_CAN_Init(&CanHandle);
// Configure filters
sFilterConfig.FilterNumber = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
HAL_CAN_ConfigFilter(&CanHandle, &sFilterConfig);
}
Sending Messages:
void CAN_Send(CAN_HandleTypeDef *hcan, uint32_t id, uint8_t *data, uint8_t length) {
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailbox;
TxHeader.StdId = id;
TxHeader.ExtId = 0x01;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.IDE = CAN_ID_STD;
TxHeader.DLC = length;
HAL_CAN_AddTxMessage(hcan, &TxHeader, data, &TxMailbox);
}
Debugging Challenges
During initial testing, messages were not being received, leading to extensive debugging. It was discovered that:
- Filter Configuration: Initially misconfigured, not allowing any messages through. Adjusting the filter masks corrected this.
- Electrical Noise: Adding terminators at each end of the CAN bus reduced errors significantly.
Conclusion
Implementing and debugging CAN communications in embedded systems can be complex but rewarding. This guide not only walks through the setup and programming but also addresses common pitfalls and their solutions. With the above implementation, you can develop robust CAN-based networks suitable for various applications, particularly in automotive and industrial systems.