Всегда работа с CAN была простой, но что-то пошло не так (в устройстве на КДПВ)…
В последнее время мне часто удается использовать микроконтроллер STM32H750VB, и вот в одном устройстве понадобилось задействовать шину CAN, но первая же попытка, которую я предпринял показала всю мою самоуверенность дала странный результат. Ниже описана история
Итак, сначала о схемотехнике. На КДПВ зеленым обведен, понятно дело, сам виновник — микроконтроллер, тут ничего сложного с CAN нет — он подключен согласно
В качестве физического уровня применен MAX3051 (ну нравится он мне..), вот примерно так:
Ранее вместо 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 ) они видны как пики:
Итак, после этого эксперимента стало понятно, что схема работает верно, контроллер 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 вообще, что можно отнести к плюсам. А к минусам… А к ним можно отнести потерянное время (контроллер удалось пристроить в другой проект) и понимание того, что современные контроллеры глючат со страшной силой тоже по-современному (или это тоже плюс?).
Автор: Олег Морозов