Application note 0565-02: Generic-CANHAL Driver

This document describes the implementation of the port CANopen driver on a generic CAN abstraction layer. The aim is to simplify the porting of the CANopen driver to any underlaying CAN-HW. The new CANopen driver “Generic-CANHAL” is based on this generic CAN abstraction layer.

 

This new CAN abstraction layer consists of two parts. The first layer (CANHAL) includes the generic API function and the second layer consists of the target-specific implementation (target adaptation).

There is an available target adaption with the STM32H743x and example projects for the IDE Atollic TrueSTUDIO (based on GCC).

General layers model:

 

 Notes to the layers:

Layer name

Who takes care of the implementation

Description

Application/

Example

Customer

User application

The delivery from port includes several example applications (e.g. s1, s2).

 

port CANopen Library

Separate delivery from port

(source folder: canopen)

 

HW independent CANopen Library from port

port CANopenDriver /

Generic-CANHAL

Delivery from port

(source folders:

“drivers/co_generic”,

”drivers/shar_src”,

“drivers/shar_inc”)

General connection layer between CANopen Library and target based on the CAN abstraction layer

This layer includes the usual driver API for a CANopen application additionally with necessary CPU/board initializations.

 

port CANopenDriver /

CANHAL

Delivery from port

(sources: “drivers/canhal/canhal.[c,h]”)

Generic fixed CAN API called by the new CANopen Generic driver.

port CANopenDriver /

 

CAN Target Adaption

Customer

(port provides the API functions as template).

Delivery from port

(sources: “drivers/canhal/cantarget.[c,h]”)

 

Specific implementation of the API for the target.

The code contains additional "Todo’s" to help.

Vendor HAL-Layer

Supplier of the HAL-Layer, normally the CPU- manufacturer

The Vendor HAL-Layer allows the access to the CAN controller via HAL-Layer functions, e.g. STM32CubeH7 - HAL-layer.

But it is also possible to access directly to the CAN controller registers without Vendor HAL-Layer.

CAN-HW /Controller

HW (CPU, CAN-interface manufacturer)

Possible are also external CAN interfaces.

 

 

Supported features with the current CAN abstraction layer implementing:

  • available for CANopen Library configurations multi-line and single-line

  • BasicCAN (all CAN-IDs received), FullCAN (HW-Filtering) is optional possible,

but not yet implemented in the available target adaption (STM32H7xx)

  • 11+29bit CAN-IDs can be transmitted or received with variable data length

  • remote frames (RTR) can be transmitted or received

  • Bare metall (without any operating system support)

 

Folder sructure of driver package “Generic-CANHAL” :

The delivery consists of two parts. The first part contains all the generic driver sources without HW access.

The second part is our porting  to the STM32H7xx target with realy HW access, in particular also to the CAN abstraction layer.

Part1: 0565_178_CO_DP_Generic_CANHAL_Driver_1_xx.zip                                      (xx = version string)

Part2: 0565_178_CO_DP_Generic_CANHAL_Driver_2_xx.zip

If both *.zip archives are unpacked in the same directory, the following structure is created.

Folder structure with comments (sources of the second delivery part marked here in blue):

  • -:/

--- |-- canopen                                 (CANopen Library, not part from this driver deliverys)

     |

     |-- drivers

     |         |

     |         |-- co_generic                   (CPU/Board adaption folder (not CAN), in generic without HW-access)

     |         |-- co_stm32H7xx           (CPU/Board adaption folder (not CAN), with STM32H7xx HW-access)

     |         |

     |         |-- co_canhal

     |         |           |-- co_canhal.c,.h                                 (CAN abstraction layer API - CANHAL)

     |         |           |-- co_cantarget.c,.h                            (CAN abstraction layer API – target adaption template)

     |         |  |-- co_cantartget_stm32h7xx.c,.h   (CAN abstraction layer API – target STM32H743x)

     |         |                                                                            

     |         |-- shar_inc                                                          (shared driver includes)

     |         |            |-- can_generic_canhal.h                    (internal access to the CAN abstraction layer API)

     |         |            |-- cpu_generic.h

     |         |            |-- …                      

     |         |            |-- cpu_stm32_h7.h                           (CPU porting to STM32H7xx)

     |         |

     |         |-- shar_src                                                      (shared driver sources, especially timer service part)

     |                      |-- can_generic_canhal.c                    (internal access to the CAN abstraction layer API)

     |                      |-- cpu_generic.c

     |                      |-- …

     |                      |-- cpu_stm32_h7.c                            (CPU porting to STM32H7xx)

     |        

--- |-- examples                                                     (example projects generated by the IDE Atollic TrueSTUDIO)

                |-- s1_canhal_generic                            (generic slave single-line example)

                |-- s4_canhal_generic                          (generic slave multi-line example)

|-- s1_canhal_stm32H7xx                                (slave single-line example ported on STM32H7xx)

|-- s2_canhal_stm32H7xx                                (slave single-line example ported on STM32H7xx)

|-- s3_canhal_stm32H7xx                                (slave single-line example ported on STM32H7xx)

|-- m1_canhal_stm32H7xx                              (master single-line example ported on STM32H7xx)

                |-- s4_canhal_stm32H7xx                (slave single-line example ported on STM32H7xx)

 

If the customer wants to develop his own driver, he can use the generic sources as a template. The sources marked in blue for the STM32H7xx can used as a reference for the own development. These must be adapted or created for a new target.

 Short API description of the target adaptation layer functions to be implemented:

(file cantarget.[c,.h]):

/****************************************************************************/ /* prototypes */ /****************************************************************************/ /****************************************************************************/ /** Initialize target CAN board peripherals  * This function sets up the target board for used CAN lines (e.g. CAN clock, CAN pins). May not be necessary if this has already been done elsewhere in the project.  * \return CO_OK - success \return others - fail  */ RET_T coCAN_targetCanBoardInit(     void );   /****************************************************************************/ /** Initialize CAN peripherals  * This function sets up the target CAN interface.  * \return CO_OK - success \return others - fail  */ RET_T coCAN_targetCanInit(     UNSIGNED8 canLine                /**< [in] number of CAN line */ ); /****************************************************************************/ /** Get CAN filter max.  * This function gets the maximal possible filter count. We need 1 filter for every receive message ID to be received. Max. value = 0 means filtering not supported - BasicCAN mode.  * \return filter_count  */ UNSIGNED32 coCAN_targetCanGetFilterMax(     UNSIGNED8 canLine                    /**< [in] number of CAN line */ ); /****************************************************************************/ /** Start CAN interface  * This function starts the target CAN with a given line number.  * \return CO_OK - success \return others - fail  */ RET_T coCAN_targetCanStart(     UNSIGNED8 canLine              /**< [in] number of CAN line */ ); /****************************************************************************/ /** Stop CAN interface  * This function stops the target CAN with a given line number. After this stop command the CAN interface is still initialized, but transmit or receive operations are not possible.  * \return CO_OK - success \return others - fail  */ RET_T coCAN_targetCanStop(     UNSIGNED8 canLine            /**< [in] number of CAN line */ ); /****************************************************************************/ /** CAN target driver set receive filter  * CAN target driver set receive filter function. This function is required if FullCAN is to be implemented. If function is not implemented only BasicCAN (all messages are received) is available.  * \return CAN_OK - success \return others - fail  */ RET_T coCAN_targetCanSetFilter(     UNSIGNED8 canLine,               /**< [in] number of CAN line */     CO_CAN_FILTER_T filter           /**< [in] filter struct */ ); /****************************************************************************/ /** Set bitrate to CAN peripheries  * This function sets up the CAN bit rate in the target CAN.  * \return CO_OK - success \return others - fail (bitrate not supported)  */ RET_T coCAN_targetCanSetBitrate(     UNSIGNED8 canLine,              /**< [in] number of CAN line */     UNSIGNED32 bitrateindex       /**< [in] index for the desired CAN bit rate                                   from bit timing table 0 according to CiA-305 */ ); /****************************************************************************/ /** CAN driver send  * CAN driver send function.  * \return CO_OK - success \return others - fail  */ RET_T coCAN_targetCanSend(     UNSIGNED8 canLine,               /**< [in] number of CAN line */     CO_CANHAL_MSG_T *p_can_msg       /**< [in] CAN message for transmission */ ); /****************************************************************************/ /** CAN target driver interrupt handler  * This function handles the CAN interrupt(s) for RX messages, TX message done, CAN controller state changed. Usually we are using the same handler function for every CAN interrupt. The callback function coCANHAL_targetCanDriverCallback() of the CANHAL layer must be called for every event.  * \return nothing  */ void coCAN_targetCanInterrupt(     UNSIGNED8 canLine                    /**< [in] number of CAN line */ ); The received data or status events received in the interrupt handler must be transferred to the CANHAL via the following callback: /* prototype for the callback function of CANHAL layer, which shall be called by the interrupt handler */ void coCANHAL_targetCanDriverCallback(     UNSIGNED8 canLine,          /**< [in] number of CAN line */     CO_CAN_EVENT_T event,       /**< [in] trigger event for interrupt */     CO_CANHAL_MSG_T *pData);    /**< [in] received data */ Some examples for calling the callback functions: /* callback function to upper layer with receive message data */ coCANHAL_targetCanDriverCallback(canLine, CO_CAN_EVENT_RX, &can_msg); /* callback function to upper layer, TX message done */ coCANHAL_targetCanDriverCallback(canLine, CO_CAN_EVENT_TXC, NULL); /* callback function to upper layer, ERR_PASSIV occurred */ coCANHAL_targetCanDriverCallback(canLine, CO_CAN_EVENT_ERR_PASSIVE, NULL); /* callback function to upper layer, CAN overrun occurred */ coCANHAL_targetCanDriverCallback(canLine, CO_CAN_EVENT_ERR_OVERRUN, NULL); Used data types and parameters for implementing the target layer API: /**< CAN bit rate */ /* These index values shall be used for argument bitrateindex for function coCAN_targetCanSetBitrate(). */ #define CO_CAN_BAUDRATE_UNKNOWN   0xFFu #define CO_CAN_BAUDRATE_10        0x00u #define CO_CAN_BAUDRATE_20        0x01u #define CO_CAN_BAUDRATE_50        0x02u #define CO_CAN_BAUDRATE_100       0x03u #define CO_CAN_BAUDRATE_125       0x04u #define CO_CAN_BAUDRATE_250       0x05u #define CO_CAN_BAUDRATE_500       0x06u #define CO_CAN_BAUDRATE_800       0x07u #define CO_CAN_BAUDRATE_1000      0x08u #define CO_CAN_BAUDRATE_INDEX    CO_CAN_BAUDRATE_125   /* default used 125 kbit/s */ /****************************************************************************/ /* enums */ /****************************************************************************/ /**< CAN event */ typedef enum {     CO_CAN_EVENT_RX = 0,                        /**< message received */     CO_CAN_EVENT_TXC,                           /**< message transmitted */     CO_CAN_EVENT_ERR_BUSOFF,                    /**< bus is off */     CO_CAN_EVENT_ERR_PASSIVE,                   /**< no acknowledge */     CO_CAN_EVENT_ERR_ACTIVE,                    /**< bus ok */     CO_CAN_EVENT_ERR_OVERRUN                    /**< received message lost */ } CO_CAN_EVENT_T; /****************************************************************************/ /* structures */ /****************************************************************************/ /**< CAN message buffer */ typedef struct {     UNSIGNED32 messageID;                       /**< message identifier */     BOOL_T rtrFlag;                             /**< Remote Transmission requested */     BOOL_T extendedFlag;                        /**< extended identifier */     UNSIGNED8 canLine;                          /**< number of line */     UNSIGNED8 dataLen;                          /**< length of data in byte */     UNSIGNED8 data[CO_CAN_DATA_LEN];            /**< message data */ } CO_CANHAL_MSG_T; /**< CAN Filter type */ typedef struct {     UNSIGNED32 messageID;             /**< filtered message identifier */     BOOL_T rtrFlag;                   /**< filter RTR frames */     BOOL_T extendedFlag;              /**< filter frames with extended identifier */     UNSIGNED8 canLine;                /**< number of CAN line */ } CO_CAN_FILTER_T;