Controller Area Network (CAN) is one of the most common protocols in the Embedded Systems world. Through this article my intention is to take you with me on my journey of learning CAN protocol using the STM32 eco system. I use STM32 here simply because I have an STM32F407-Disco board at my disposal. Please note that this article wouldn’t teach you CAN protocol, rather, it refers to materials that already exist in the Internet and builds upon that knowledge to implement basic communication scheme on actual hardware.
The main use case for CAN is in the automotive industry. It is one of the most common communication protocols used in modern automobiles. Most of the sensors transmit their data through the CAN bus that is connected to the control unit of the automobile. You can find hundreds of articles about these use cases in the Internet.
This article is the first in a series. I plan to expand on this foundation and implement a mock CAN network in the coming weeks.
Board bring upAs the first step of any embedded project, it’s a best practice to write a simple program to blink an LED. This is one of the most powerful programs, because it proves that your compiler, programmer and the board works properly. Therefore, naturally, this was my first step.
Learning CAN protocolIt’s very difficult to start learning how to use a protocol without understanding the basics of the protocol. One doesn’t need to understand all the nitty-gritties of the protocol, however, a basic understanding is a must to be ready for what comes next in datasheets and reference manuals. I referred the Introduction to the Controller Area Network (CAN) document by Texas Instruments to get the basic understanding. Once I could understand the basic concepts, I had a look at the bxCAN implementation in the STM32F407 reference manual. It explains the mechanics of the STM32 adaptation and realization of the CAN protocol.
CAN interface configurationAfterwards, I enabled the CAN interface using the STM32CubeMX software. I selected CAN1 and it automatically configured the required GPIO pins. The only other configuration I changed was the operating mode under the CAN Advanced Parameters configuration. I set it to “Loopback combined with Silent”. This mode is designed to test CAN configuration of nodes without interrupting any network that they are connected to. The configuration is explained in detail in the section 32.5 of the reference manual. My idea is to make use of the loopback mode to test the implementation without depending on any external components.
Understanding the STM32Cube HAL API for CANOne thing I really like about the STM32Cube HAL is their API documentation. All the relevant information is usually available at the begining of the source file. In this case, for CAN API, all the information is available in the stm32f4xx_hal_can.c file. If I summarize the required APIs, Define HAL_CAN_MspInit()
Auto generated in stm32f4xx_hal_msp.c Called by HAL_CAN_Init() GPIO configuration and clock configuration is autogenerated inside this functionCall HAL_CAL_Init()
This function is called within the autogenerated MX_CAN1_Init() in main.cConfigure RX filters using HAL_CAN_ConfigFilter()
This function call is NOT auto generatedStart the CAN peripheral by calling HAL_CAN_Start()
This function call is NOT auto generatedUse a function to transmit a CAN frame
This function call is NOT auto generatedUse a function to recieve a CAN frame
This function call is NOT auto generated
First two steps are already done by the STM32CubeMx software. Therefore, we only need to manually implement the last four steps in the sequence above.
Filter configurationThis is the most confusing configuration in this example. I’ll try to explain the simplest filtering scheme here. However, before we start talking about the filtering we need to briefly look at the format of a CAN frame. In the above image, the CAN frame is the physical data that transmits through the network. Once the bxCAN peripheral receives it, the standard ID, extended ID, Identifier Extension bit, and Remote Transmission Request bit are extracted from the message. These form the identifier of the CAN frame as recognized by the bxCAN implementation. The identifier register contains the value that the peripheral compares against this received packet. However, there is another register called the mask register that is used as a bit mask to select what are the must match bits and what are the don’t care bits. In the simplest filter configuration, we can set the mask register to be 0xFFFFFFFF such that all the bits in the extracted identifier gets compared with all the bits of the filter identifier register. In the diagram below, I attempted to summarize this whole operation using two logic AND operations. If value1 and value2 are equal, that is considered a match in the filter. Assume the STDID is 0x02 and all other IDs are 0. In that case the maching filter value should be 0x0040 0000. If the filter mask register is 0xFFFF FFFF, then our peripheral would recieve this message without any issue. With this information let’s have a look at the API.