Skip to content

Instantly share code, notes, and snippets.

@Arman92
Last active June 5, 2023 20:29
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save Arman92/154e2540847b32c44c29 to your computer and use it in GitHub Desktop.
Save Arman92/154e2540847b32c44c29 to your computer and use it in GitHub Desktop.
CAN-Bus ECU reading with STM32F429 (STM32Fxxx)

Reading out ECU CAN bus using a STM32FXXX device (tested on STM32F429-Discovery)

The problem

configuring the SJW, BS1, BS2 and Prescaler to achieve a desired baudrate and the Sample Point to be a correct value for different CAN higher-layers (J1939, CANOpen, etc.)

The solution

I did not find any suitable reference that says what is the best sampling point for different protocols, so I decided to test each possible sample point, see if one of them works, and then choose it as the right solution.

The equation of CAN bus baudrate for STM32 MCUs is: ** Baudrate = APB1Clock / (Prescaler * (SJW + BS1 + BS2)) **

If you are not getting any CAN frames and you think it's because of your baudrate, then by pressing a button (firing up an external interrupt), CANWorker class will find another combiniation of SJW, BS1, BS2 and Prescaler, you need to wait a second or two, if nothing received yet, press the button again until you get CAN frames.

This example is inspired from the STM Cube's examples (STM324xG_EVAL/Examples/CAN/CAN_Networking). The codes may not work right away, as it's just a gist not a repo. but the idea is pretty simple and codes are handy, it helped me getting data from a Renault Kerax and a Mercedes Axor.

#include "CANWorker.h"
#define CAN_BAUDRATE_TEST_TIMEOUT 10000 //ms
CAN_HandleTypeDef _canHandle;
bool receivedAnyCANFrame = false;
bool flag = false;
bool busy = false;
CANWorker::CANWorker ()
{
// I used STM32F429-Discovery blue button for jumping to next baudrate solution, use another GPIO (or another method)
//that suits you.
BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);
EXTI0_IRQHandler_Config();
/*##- Start the Reception process and enable reception interrupt #########*/
if(HAL_CAN_Receive_IT(&_canHandle, CAN_FIFO0) != HAL_OK)
{
/* Reception Error */
Error_Handler();
}
int counter = 0;
while (true)
{
// check if the External interrupt of USER_KEY is occured (by checking the flag)
if (counter >= (CAN_BAUDRATE_TEST_TIMEOUT / 300) && !receivedAnyCANFrame && flag)
{
INFO("CAN BAUDRATE TEST timed out, trying another baudrate\r\n");
flag = false;
counter = 0;
receivedAnyCANFrame = false;
if (!busy)
calculateAndInitBaudrate(250000); // change to your desired baudrate (typically 250kbs or 500kbs)
}
else
{
HAL_Delay(300);
counter++;
}
}
bool CANWorker::init_HAL_CAN()
{
CAN_FilterConfTypeDef sFilterConfig;
static CanTxMsgTypeDef TxMessage;
static CanRxMsgTypeDef RxMessage;
/*##-1- Configure the CAN peripheral #######################################*/
_canHandle.Instance = CAN1;
_canHandle.pTxMsg = &TxMessage;
_canHandle.pRxMsg = &RxMessage;
_canHandle.Init.TTCM = DISABLE; //Non time trigger communication mode //
_canHandle.Init.ABOM = DISABLE; //The software automatically Bus-off management //
_canHandle.Init.AWUM = DISABLE; //Sleep mode wake by software (clear CAN-> MCR SLEEP) (automatic wake-up mode)//
_canHandle.Init.NART = DISABLE; //Disable automatic transfer message (non-automatic retransmission mode)//
_canHandle.Init.RFLM = DISABLE; //The message is not locked, the new cover the old //
_canHandle.Init.TXFP = DISABLE; // Priority is determined by the message identifier //
_canHandle.Init.Mode = CAN_MODE_NORMAL;
_canHandle.Init.SJW = CAN_SJW_2TQ;
_canHandle.Init.BS1 = CAN_BS1_3TQ;
_canHandle.Init.BS2 = CAN_BS2_3TQ;
_canHandle.Init.Prescaler = 16;
if(HAL_CAN_Init(&_canHandle) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
return false;
}
// The below CAN filter does not actually filer any CAN frame, and this is what we want right now!
/*##-2- Configure the CAN Filter ###########################################*/
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 = 0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
if(HAL_CAN_ConfigFilter(&_canHandle, &sFilterConfig) != HAL_OK)
{
/* Filter configuration Error */
Error_Handler();
return false;
}
return true;
}
void CANWorker::Error_Handler()
{
while(1)
{
BSP_LED_Toggle(LED3);
HAL_Delay(100);
}
}
void CANWorker::calculateAndInitBaudrate(int baudrate)
{
busy = true;
bool shouldBrake = false;
uint32_t frequency = getAPB1Clock();
for (; sjw <= 4 && !shouldBrake; )
{
for (; bs1 <= 16 && !shouldBrake; )
{
for (; bs2 <= 8 && !shouldBrake; )
{
for (; prescaler <= 1024 && !shouldBrake; )
{
int calcBaudrate = (int)(frequency / (prescaler * (sjw + bs1 + bs2)));
if (calcBaudrate == baudrate)
{
if (sjw == 1)
_canHandle.Init.SJW = CAN_SJW_1TQ;
else if (sjw == 2)
_canHandle.Init.SJW = CAN_SJW_2TQ;
else if (sjw == 3)
_canHandle.Init.SJW = CAN_SJW_3TQ;
else if (sjw == 4)
_canHandle.Init.SJW = CAN_SJW_4TQ;
if (bs1 == 1)
_canHandle.Init.BS1 = CAN_BS1_1TQ;
else if (bs1 == 2)
_canHandle.Init.BS1 = CAN_BS1_2TQ;
else if (bs1 == 3)
_canHandle.Init.BS1 = CAN_BS1_3TQ;
else if (bs1 == 4)
_canHandle.Init.BS1 = CAN_BS1_4TQ;
else if (bs1 == 5)
_canHandle.Init.BS1 = CAN_BS1_5TQ;
else if (bs1 == 6)
_canHandle.Init.BS1 = CAN_BS1_6TQ;
else if (bs1 == 7)
_canHandle.Init.BS1 = CAN_BS1_7TQ;
else if (bs1 == 8)
_canHandle.Init.BS1 = CAN_BS1_8TQ;
else if (bs1 == 9)
_canHandle.Init.BS1 = CAN_BS1_9TQ;
else if (bs1 == 10)
_canHandle.Init.BS1 = CAN_BS1_10TQ;
else if (bs1 == 11)
_canHandle.Init.BS1 = CAN_BS1_11TQ;
else if (bs1 == 12)
_canHandle.Init.BS1 = CAN_BS1_12TQ;
else if (bs1 == 13)
_canHandle.Init.BS1 = CAN_BS1_13TQ;
else if (bs1 == 14)
_canHandle.Init.BS1 = CAN_BS1_14TQ;
else if (bs1 == 15)
_canHandle.Init.BS1 = CAN_BS1_15TQ;
else if (bs1 == 16)
_canHandle.Init.BS1 = CAN_BS1_16TQ;
if (bs2 == 1)
_canHandle.Init.BS2 = CAN_BS2_1TQ;
else if (bs2 == 2)
_canHandle.Init.BS2 = CAN_BS2_2TQ;
else if (bs2 == 3)
_canHandle.Init.BS2 = CAN_BS2_2TQ;
else if (bs2 == 4)
_canHandle.Init.BS2 = CAN_BS2_2TQ;
_canHandle.Init.Prescaler = prescaler;
shouldBrake = true;
// below lines are my own serial class, replace them with yours
//serial->puts("new config applied\r\n");
//serial->puts("sjw: ");
//serial->print(sjw);
//serial->puts(" bs1: ");
//serial->print(bs1);
//serial->puts(" bs2: ");
//serial->print(bs2);
//serial->puts(" prescaler: ");
//serial->print(prescaler);
//serial->puts("\r\n");
}
prescaler++;
}
if (!shouldBrake)
{
prescaler = 1;
bs2++;
}
}
if (!shouldBrake)
{
bs2 = 1;
bs1++;
}
}
if (!shouldBrake)
{
bs1 = 1;
sjw++;
}
}
busy = false;
if (HAL_CAN_Init(&_canHandle) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
}
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* CanHandle)
{
receivedAnyCANFrame = true;
// below lines are my own serial class, replace them with yours
serial->print("StdId:");
serial->print((int)CanHandle->pRxMsg->StdId);
serial->print(",");
serial->print((int)CanHandle->pRxMsg->ExtId);
serial->print(",");
serial->print((int)CanHandle->pRxMsg->DLC);
serial->print(",");
serial->print((int)CanHandle->pRxMsg->Data[0]);
serial->putc(' ');
serial->print((int)CanHandle->pRxMsg->Data[1]);
serial->putc(' ');
serial->print((int)CanHandle->pRxMsg->Data[2]);
serial->putc(' ');
serial->print((int)CanHandle->pRxMsg->Data[3]);
serial->putc(' ');
serial->print((int)CanHandle->pRxMsg->Data[4]);
serial->putc(' ');
serial->print((int)CanHandle->pRxMsg->Data[5]);
serial->putc(' ');
serial->print((int)CanHandle->pRxMsg->Data[6]);
serial->putc(' ');
serial->print((int)CanHandle->pRxMsg->Data[7]);
serial->print("\r\n");
BSP_LED_Toggle(LED3);
/* Receive the next CAN Frame*/
if(HAL_CAN_Receive_IT(CanHandle, CAN_FIFO0) != HAL_OK)
{
/* Reception Error */
//Error_Handler();
}
}
uint32_t CANWorker::getAPB1Clock()
{
RCC_ClkInitTypeDef clkInit;
uint32_t flashLatency;
HAL_RCC_GetClockConfig(&clkInit, &flashLatency);
uint32_t hclkClock = HAL_RCC_GetHCLKFreq();
uint8_t clockDivider = 1;
if (clkInit.APB1CLKDivider == RCC_HCLK_DIV1)
clockDivider = 1;
if (clkInit.APB1CLKDivider == RCC_HCLK_DIV2)
clockDivider = 2;
if (clkInit.APB1CLKDivider == RCC_HCLK_DIV4)
clockDivider = 4;
if (clkInit.APB1CLKDivider == RCC_HCLK_DIV8)
clockDivider = 8;
if (clkInit.APB1CLKDivider == RCC_HCLK_DIV16)
clockDivider = 16;
uint32_t apb1Clock = hclkClock / clockDivider;
return apb1Clock;
}
/**
* @brief Configures EXTI Line0 (connected to PA0 pin) in interrupt mode
* @param None
* @retval None
*/
static void EXTI0_IRQHandler_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == KEY_BUTTON_PIN)
{
flag = true;
}
}
#include <inttypes.h>
#include "stm32f429xx.h"
#include "stm32f4xx_hal.h"
class CANWorker
{
private:
int sjw = 1;
int bs1 = 1;
int bs2 = 1;
int prescaler = 1;
bool init_HAL_CAN();
uint32_t getAPB1Clock();
void Error_Handler();
public:
CANWorker();
void calculateAndInitBaudrate(int baudrate);
};
/* User can use this section to tailor CANx instance used and associated
resources */
/* Definition for CANx clock resources */
#define CANx CAN1
#define CANx_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE()
#define CANx_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define CANx_FORCE_RESET() __HAL_RCC_CAN1_FORCE_RESET()
#define CANx_RELEASE_RESET() __HAL_RCC_CAN1_RELEASE_RESET()
/* Definition for USARTx Pins */
#define CANx_TX_PIN GPIO_PIN_12
#define CANx_TX_GPIO_PORT GPIOA
#define CANx_TX_AF GPIO_AF9_CAN1
#define CANx_RX_PIN GPIO_PIN_11
#define CANx_RX_GPIO_PORT GPIOA
#define CANx_RX_AF GPIO_AF9_CAN1
/* Definition for USARTx's NVIC */
//#define CANx_RX_IRQn CAN2_RX0_IRQn
//#define CANx_RX_IRQHandler CAN2_RX0_IRQHandler
#define CANx_RX_IRQn1 CAN1_RX0_IRQn
#define CANx_RX_IRQHandler1 CAN1_RX0_IRQHandler
#include "defines.h"
/**
* @brief CAN MSP Initialization
* This function configures the hardware resources used in this example:
* - Peripheral's clock enable
* - Peripheral's GPIO Configuration
* - NVIC configuration for DMA interrupt request enable
* @param hcan: CAN handle pointer
* @retval None
*/
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
GPIO_InitTypeDef GPIO_InitStruct;
/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* CAN1 Periph clock enable */
CANx_CLK_ENABLE();
/* Enable GPIO clock ****************************************/
CANx_GPIO_CLK_ENABLE();
/*##-2- Configure peripheral GPIO ##########################################*/
/* CAN1 TX GPIO pin configuration */
GPIO_InitStruct.Pin = CANx_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = CANx_TX_AF;
HAL_GPIO_Init(CANx_TX_GPIO_PORT, &GPIO_InitStruct);
/* CAN1 RX GPIO pin configuration */
GPIO_InitStruct.Pin = CANx_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = CANx_RX_AF;
HAL_GPIO_Init(CANx_TX_GPIO_PORT, &GPIO_InitStruct);
/*##-3- Configure the NVIC #################################################*/
/* NVIC configuration for CAN1 Reception complete interrupt */
HAL_NVIC_SetPriority(CANx_RX_IRQn1, 1, 0);
HAL_NVIC_EnableIRQ(CANx_RX_IRQn1);
}
/**
* @brief CAN MSP De-Initialization
* This function frees the hardware resources used in this example:
* - Disable the Peripheral's clock
* - Revert GPIO to their default state
* @param hcan: CAN handle pointer
* @retval None
*/
void HAL_CAN_MspDeInit(CAN_HandleTypeDef *hcan)
{
/*##-1- Reset peripherals ##################################################*/
CANx_FORCE_RESET();
CANx_RELEASE_RESET();
/*##-2- Disable peripherals and GPIO Clocks ################################*/
/* De-initialize the CAN1 TX GPIO pin */
HAL_GPIO_DeInit(CANx_TX_GPIO_PORT, CANx_TX_PIN);
/* De-initialize the CAN1 RX GPIO pin */
HAL_GPIO_DeInit(CANx_RX_GPIO_PORT, CANx_RX_PIN);
/*##-4- Disable the NVIC for CAN reception #################################*/
HAL_NVIC_DisableIRQ(CANx_RX_IRQn1);
}
#include "defines.h"
extern CAN_HandleTypeDef _canHandle;
/**
* @brief This function handles CAN1 RX0 interrupt request.
* @param None
* @retval None
*/
void CAN1_RX0_IRQHandler(void)
{
HAL_CAN_IRQHandler(&_canHandle);
}
/**
* @brief This function handles CAN2 RX0 interrupt request.
* @param None
* @retval None
*/
void CAN2_RX0_IRQHandler(void)
{
HAL_CAN_IRQHandler(&_canHandle);
}
/**
* @brief This function handles CAN1 RX1 interrupt request.
* @param None
* @retval None
*/
void CAN1_RX1_IRQHandler(void)
{
HAL_CAN_IRQHandler(&_canHandle);
}
/**
* @brief This function handles CAN2 RX1 interrupt request.
* @param None
* @retval None
*/
void CAN2_RX1_IRQHandler(void)
{
HAL_CAN_IRQHandler(&_canHandle);
}
/**
* @brief This function handles CAN1 TX interrupt request.
* @param None
* @retval None
*/
void CAN1_TX_IRQHandler(void)
{
HAL_CAN_IRQHandler(&_canHandle);
}
/**
* @brief This function handles CAN2 TX interrupt request.
* @param None
* @retval None
*/
void CAN2_TX_IRQHandler(void)
{
HAL_CAN_IRQHandler(&_canHandle);
}
@Hecot
Copy link

Hecot commented Aug 31, 2016

Dear Arman!

Thanks for the code - worked great for me! I changed the logic and make it self repeating until it get a CAN message with StdID != 0, then jump in a while (1) loop and switch on the LED. So I can stop the Debugger and read the values from the .init Register - easier than writing it to a serial console.

@emrecnulkr
Copy link

Hi dear,

Can you add keil uvision project ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment