0
\$\begingroup\$

I have written a simple program that should send "NO MESSAGE!" to the serial monitor if there are no CAN messages or "A NEW MESSAGE ARRIVED!" and then the CAN message itself through UART2 when there are CAN messages. My idea is that since all car sensors are connected to the same CAN bus then I should be able to read all the messages live if i set all the CAN filter bits to 0, I don't know if that's possible or I have to send requests for specific PID and wait for a response (this is my first design question).

I connected my device to my car's OBD2 port and expected to see a swarm of CAN messages as soon as I turned on the engine, instead all i saw was "no message!" over and over again.

I am aware that there are so many things that could cause the said problem so I am going to explain everything that I have done hoping that someone could tell me what's the problem.

I have tried the loopback mode and I was able to both receive and send CAN messages but that doesn't really tell me much since CAN RX and CAN TX are internally connected so the fact that I can send and receive messages using loopback mode does not mean that the MCP2551 IC that I am using is faulty or not and currently it's the only IC i have and cannot try other ICs. So the first possible problem could be a faulty IC.

I have also seen in many online schematics that people place a 120 ohm resistor between CANH and CANL but they say it's only required if my MCU is a terminal node. I don't know if it is considered a terminal node when i connect it to the OBD2 port or not so I did NOT put a resistor there (will try it on my second try).

I do not have a scope capable of scoping my car's OBD2 port to see if there are signals on CANH and CANL wires although it could be the cause i highly doubt it. here is the picture of my car's OBD port and its connections: OBD2 front view

OBD2 top view

You can clearly see that pins 6 and 14 have wires so my car does support CAN and an unknown protocol (pins 1 and 9).

Next there is the baud rate. According to this website: this there are 4 flavors of CAN datalink layers:

500kbps with standard (11 bit) identifiers
250kbps with standard (11 bit) identifiers
500kbps with extended (29 bit) identifiers
250kbps with extended (29 bit) identifiers

So far I have only used the 500kbps with 11 bit identifiers. Does it matter if it is standard or extended when I’m just sniffing packets?

Here is init CAN setting:

    static void MX_CAN1_Init(void) {
    
      hcan1.Instance = CAN1;   
 hcan1.Init.Prescaler = 5;  
 hcan1.Init.Mode = CAN_MODE_NORMAL; 
 hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
 hcan1.Init.TimeSeg1 = CAN_BS1_15TQ;  
 hcan1.Init.TimeSeg2 = CAN_BS2_2TQ; 
 hcan1.Init.TimeTriggeredMode = DISABLE;  
 hcan1.Init.AutoBusOff = DISABLE; 
 hcan1.Init.AutoWakeUp = DISABLE; 
 hcan1.Init.AutoRetransmission = DISABLE;  
 hcan1.Init.ReceiveFifoLocked = DISABLE;   
 hcan1.Init.TransmitFifoPriority = DISABLE;   

if (HAL_CAN_Init(&hcan1) != HAL_OK)   {
        Error_Handler();   
}

}

Which according to CUBEMX should give me a 500kbit/sec baud rate since my APB1 clock rate is 45 MHz. Which brings me to my third question, how does one change CAN baud rate in the middle of the program? I’m thinking de-init CAN, create another CAN init function with 250 kbit/sec settings and initialize CAN again, is there an easier way?

here is my clock tree's pic: enter image description here

Here is the main function:

            int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_CAN1_Init();
  /* USER CODE BEGIN 2 */
  CAN1_Config();
  if(HAL_CAN_Start(&hcan1) != HAL_OK)
    {
      Error_Handler();
    }
  CAN1_Rx();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Here is my CAN1_config() function:

void CAN1_Config(void)
{
  CAN_FilterTypeDef CAN1_CONF;

  CAN1_CONF.FilterActivation = ENABLE;
  CAN1_CONF.FilterBank = 0;
  CAN1_CONF.FilterFIFOAssignment = CAN_RX_FIFO0;
  CAN1_CONF.FilterIdHigh = 0X0000;
  CAN1_CONF.FilterIdLow = 0X0000;
  CAN1_CONF.FilterMaskIdHigh = 0X0000;
  CAN1_CONF.FilterMaskIdLow = 0X0000;
  CAN1_CONF.FilterMode = CAN_FILTERMODE_IDMASK;
  CAN1_CONF.FilterScale = CAN_FILTERSCALE_32BIT;

  if(HAL_CAN_ConfigFilter(&hcan1, &CAN1_CONF) != HAL_OK)
  {
      Error_Handler();
  }

}

Here is my CAN1_RX() function:

void CAN1_Rx(void)
{
    uint8_t CAN_Rx_Msg[5];
    CAN_RxHeaderTypeDef CAN1_RxHeader;
    while(1)
    {
        if(! HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0))
        {
            HAL_UART_Transmit(&huart2, (unsigned char *)"NO MESSAGE!\n", strlen("NO MESSAGE!\n"), HAL_MAX_DELAY);
            HAL_Delay(1500);
        }

        else
        {
            HAL_UART_Transmit(&huart2, (unsigned char *)"MESSAGE ARRIVED!\n", strlen("MESSAGE ARRIVED!\n"), HAL_MAX_DELAY);
            HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &CAN1_RxHeader, CAN_Rx_Msg);
            HAL_UART_Transmit(&huart2, (unsigned char *)CAN_Rx_Msg, sizeof(CAN_Rx_Msg), HAL_MAX_DELAY);
        }
    }

}

Finally I will draw a quick schematic of my circuit (I can upload real pictures should you guys require it of both the circuit and the OBD2 plug that I am connecting to my car)

enter image description here

I put the 10 k resistor on pin 8 to control slew rate (found it in an online diagram) also I don't know if i must put a 120 ohm resistor between CANH and CANL.

Sorry for the long post and the terrible schematics, this website's tool is just too fancy so I used paint instead :D

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Welcome to EE.SE. I advice you to try again with the “too fancy” tool. \$\endgroup\$
    – winny
    Commented Apr 9, 2021 at 6:49
  • \$\begingroup\$ There are many open source projects for ARM processors, including STM32 on Github. Have you looked their code implementation? Further they also share a PC program for decoding CAN messages. \$\endgroup\$ Commented Apr 9, 2021 at 7:08

1 Answer 1

0
\$\begingroup\$

For general trouble-shooting, please see What are the most common causes of CAN bus communication errors? The solution to several of the problems you are having is described there.

I have also seen in many online schematics that people place a 120 ohm resistor between CANH and CANL but they say it's only required if my MCU is a terminal node. I don't know if it is considered a terminal node when i connect it to the OBD2 port or not so I did NOT put a resistor there (will try it on my second try).

Yes it will be a terminal since it's a point to point communication. You should always have 60 ohm between CANH and CANL. If you have 120 ohm between them and you didn't add a resistor, that means there's just 1 resistor in the other end and you need to add one on your side.

I do not have a scope capable of scoping my car's OBD2 port to see if there are signals on CANH and CANL wires

Tough luck. You shouldn't be doing projects with CAN then, or electronics in general...

Next there is the baud rate.

Measure it with your scope. Oh right...

Does it matter if it is standard or extended when I’m just sniffing packets?

It depends. Some CAN listener tools require you to set either standard or extended. You can't really do CAN projects without a CAN listener tool either.

If by sniffing you mean your own code, then it matters too, of course. But mostly when doing message filtering.

Here is init CAN setting:

Baudrate is obtained by counting the total number of time quanta (tq). baudrate = total_tq * clock tick. You say that you have 45MHz clock and divide it by 5, so the CAN controller is clocked with 9MHz. Since you appear to have 18 tq, then 18* (1/9MHz) = 500kbps so that's the correct baudrate.

Your sample point is at (1+15)/18 tq = 88.8% which is fine. The optimal recommendation is 87.5%.

However, your clock accuracy matters. Where is this 45MHz coming from, internal RC oscillator or external quartz? You should have a clock accuracy < 1%.

how does one change CAN baud rate in the middle of the program

I don't know the ST controller specifically so I can't help you there, but generally you have to disable the controller and/or put it in some configuration state, since changing baudrate on the fly isn't a common scenario for CAN.

However, changing baudrate over and over with "brute force" until something fits is not how you design baudrate-detecting systems. Instead connect the CAN Rx line to an input capture pin of your timer hardware, then simply measure the baudrate if you expect to find bus traffic before you start, which seems to be the case here (?).

Finally I will draw a quick schematic of my circuit

(There is a schematic tool integrated with this site, no need for MS Paint.)

You have MCP2551 pin 8 "RS" wrong and this is a big problem. Read about operating modes in the datasheet. You have set the part in sleep mode. You should tie pin 8 to ground to enable "high speed mode". "High speed" meaning normal CAN bus mode, as opposed to more exotic stuff with slope control etc.

I put the 10 k resistor on pin 8 to control slew rate

That would be pin 5. Read the datasheet instead of sketchy online sources. At 500kbps you will unlikely benefit from this anyway, just drop that attempt. Tie pin 8 to ground and leave pin 5 floating.

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for your answer :) i am new to electronics and this is my first ever project , i wil buy a scope which can monitor can or get some sort of a can listener. The thing is that i want the final product to be able to detect the protocol + the flavor of the car and then gather data accordingly which is why even if i do measure my car's baud rate it would still mean little(the product will be specific to my car). Regarding the resistor between CANH and CANL have i understood you correctly that i should put a 60 ohm resistor?(there is 60 ohm on the other side). i will attach clock tree pic \$\endgroup\$
    – illidan
    Commented Apr 9, 2021 at 8:57
  • \$\begingroup\$ @illidan Usually when you buy a CAN-to-USB adapter you get a PC software for listening with it. Good enough for hobbyist use at least. As for scopes, they usually can decode CAN nowadays, but that isn't necessary either. Manually decoding CAN frames isn't often necessary. You mostly use the scope to check that the hardware is ok - look for error frames or measure voltage levels, baudrate etc. Any old scope can do that, doesn't have to be something fancy. \$\endgroup\$
    – Lundin
    Commented Apr 9, 2021 at 9:10
  • \$\begingroup\$ @illidan "Regarding the resistor between CANH and CANL have i understood you correctly that i should put a 60 ohm resistor?(there is 60 ohm on the other side). i will attach clock tree pic" No you should measure 60 ohm between CANH and CANL if your bus is healthy. You need two resistors, one in each end. Then parallel resistance gives you 1/120 + 1/120 = 1/60. Now if you already measure 60 ohm without adding a resistor of your own, it means you are sitting on a "stub" and don't need to add a resistor. However, you should keep wires short in that case. \$\endgroup\$
    – Lundin
    Commented Apr 9, 2021 at 9:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.