Всегда работа с CAN была простой, но что-то пошло не так (в устройстве на КДПВ)…

image

В последнее время мне часто удается использовать микроконтроллер STM32H750VB, и вот в одном устройстве понадобилось задействовать шину CAN, но первая же попытка, которую я предпринял показала всю мою самоуверенность дала странный результат. Ниже описана история

Итак, сначала о схемотехнике. На КДПВ зеленым обведен, понятно дело, сам виновник — микроконтроллер, тут ничего сложного с CAN нет — он подключен согласно

image

В качестве физического уровня применен MAX3051 (ну нравится он мне..), вот примерно так:

image

Ранее вместо STM32H750VB в такой же системе был STM32F107, но старичок не справился с задачами и было решено заменить его на модный и молодежный более современный.

Но вот незадача — у старого микроконтроллера был bxCAN, а в новом уже FDCAN. Хотя отличия и есть, но с точки зрения кода (да и работы — устойства-то на шине старые): замена — это очень просто. Для желающих можно сравнить:
Было Стало
MX_CAN1_Init();
MX_FDCAN1_Init();
    hcan1.Instance = CAN1;
    hcan1.Init.Prescaler = 4;
    hcan1.Init.Mode = CAN_MODE_NORMAL;
    hcan1.Init.SyncJumpWidth = CAN_SJW_4TQ;
    hcan1.Init.TimeSeg1 = CAN_BS1_15TQ;
    hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
    hcan1.Init.TimeTriggeredMode = DISABLE;
    hcan1.Init.AutoBusOff = ENABLE;
    hcan1.Init.AutoWakeUp = DISABLE;
    hcan1.Init.AutoRetransmission = DISABLE;
    hcan1.Init.ReceiveFifoLocked = DISABLE;
    hcan1.Init.TransmitFifoPriority = DISABLE;
  hfdcan1.Instance = FDCAN1;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_NO_BRS;
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
  hfdcan1.Init.AutoRetransmission = ENABLE;
  hfdcan1.Init.TransmitPause = DISABLE;
  hfdcan1.Init.ProtocolException = ENABLE;
  hfdcan1.Init.NominalPrescaler = 1;
  hfdcan1.Init.NominalSyncJumpWidth = 3;
  hfdcan1.Init.NominalTimeSeg1 = 11;
  hfdcan1.Init.NominalTimeSeg2 = 4;
  hfdcan1.Init.DataPrescaler = 1;
  hfdcan1.Init.DataSyncJumpWidth = 3;
  hfdcan1.Init.DataTimeSeg1 = 11;
  hfdcan1.Init.DataTimeSeg2 = 4;
  hfdcan1.Init.MessageRAMOffset = 64;
  hfdcan1.Init.StdFiltersNbr = 0;
  hfdcan1.Init.ExtFiltersNbr = 0;
  hfdcan1.Init.RxFifo0ElmtsNbr = 4;
  hfdcan1.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8;
  hfdcan1.Init.RxFifo1ElmtsNbr = 4;
  hfdcan1.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_8;
  hfdcan1.Init.RxBuffersNbr = 4;
  hfdcan1.Init.RxBufferSize = FDCAN_DATA_BYTES_8;
  hfdcan1.Init.TxEventsNbr = 4;
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK) {

В общем, косметические различия. И мне казалось, что и заработает все сразу и правильно.

Однако, сразу не заработало…

Контроллер CAN не мог выставить доминантный уровень и переходил в отказ состояние Bus-Off, также никаких данных с шины не принималось (притом, что другое устройство на шине стабильно посылало пакеты два раза в секунду).

Что ж, подумал я, пришла очередь отладки, и припаял проводки на линии CAN-RX и CAN-TX (собственно просмотр самой шины выглядел логично — устройство молчало, а подключенное другое устройство посылало пакеты, как договаривались).

После этого вначале был включен режим FDCAN_MODE_BUS_MONITORING. И, о чудо, сразу увиделись пакеты от шины! (В этом режиме контроллер CAN только слушает данные, но ничего не передает). Так, замечательно…

Далее был включен режим FDCAN_MODE_EXTERNAL_LOOPBACK ( в этом режиме, наоборот, слушаем только себя, но зато все передаем в шину). И на линиях CAN_RX и CAN_TX появились все пакеты данных — как отправляемые самим устройством, так и принимаемые с шины, вот на
рисунке ниже (серым данные TX от микроконтроллера, оранжевым данные линии RX ) они видны как пики:

image

Итак, после этого эксперимента стало понятно, что схема работает верно, контроллер CAN в микропроцессоре может как принимать, так и передавать данные.

Однако при попытке одновременно как принимать, так и передавать данные все равно система становилась Bus-Off c ошибкой в регистре контроля ошибок (FDCAN protocol status register (FDCAN_PSR)) LEC[2:0] = 5 — а это означает из даташита Bit0Error: During the transmission of a message (or acknowledge bit, or active error
flag, or overload flag), the device wanted to send a dominant level (data or identifier bit
logical value 0), but the monitored bus value was recessive…

После двух дней мучений (понятно, в чем ошибка, но не понятно, как исправить) и вдумчивого исследования даташита, errata и кучи стороннего кода и мануалов пришло просветление понимание — все я делаю правильно, но не работает!

Что же, подумал я, может дело в технике, и… заменил сам микроконтроллер (на другой из той же партии). И… оно заработало! Ну то есть вот вообще без плясок с бубном проблем, с исходным кодом и как положено с первого раза.

Краткие итоги


Видимо, попался такой вот хитробракованный экземпляр. Но зато удалось сильно глубже вникнуть в работу FDCAN вообще, что можно отнести к плюсам. А к минусам… А к ним можно отнести потерянное время (контроллер удалось пристроить в другой проект) и понимание того, что современные контроллеры глючат со страшной силой тоже по-современному (или это тоже плюс?).