Цель этой статьи — рассказать о моём опыте модификации автомобиля и экспериментах с шиной CAN.
- С чего всё началось
- Шина CAN
- Интеграция
- Функциональность
- Всё вместе
- Как я добавил функции автомобилю по шине CAN, не умея программировать
- С чего всё началось
- Шина CAN
- Интеграция
- Функциональность
- Всё вместе
- CAN-USB адаптер из stm32vldiscovery
- Попытка сделать всё по-быстрому
- Пишем тестовую прошивку, для проверки USB
- Создаём новый проект в Cube
- ОК, смотрим как оно работает
- Подстраиваемся под CAN Hacker
- Добавим интерфейс CAN к нашему проекту
- Добавим Timestamp
- Добавим возможность отправки сообщений в CAN
- Заключение
- 🔥 Видео
Видео:Магия CAN-шиныСкачать
С чего всё началось
Сначала я решил добавить фронтальную камеру в свой 2017 Chevrolet Cruze. Поскольку у автомобиля уже есть заводская камера заднего вида, то на высоком уровне нужно было выяснить две вещи:
- Способ передачи видео с фронтальной камеры, которую я добавлю.
- Способ отображения на экране картинки с камеры заднего вида в любое время.
Видеочасть была простой. Из предыдущего опыта я знал, что можно сделать видеомикшер на реле.
Запуск на экране оказался более сложным, и после некоторого расследования я пришёл к выводу, что машина должна подавать сигнал от камеры заднего вида на экран через какую-то шину данных.
Видео:MCP2515, контроллер CAN шины с интерфейсом SPIСкачать
Шина CAN
У Chevrolet две разные шины данных. Первая — это стандартная CAN, быстрая (500 Кбит/с) и надёжная, она используется для критических данных. Вторая — то, что GM называет LAN (GMLAN), более старая и медленная шина (33,3 Кбит/с), которая используется для данных, не связанных с безопасностью.
Мне нужен был способ прослушивать трафик по CAN, то есть снифер. Для этой цели невероятно полезно устройство PCAN.
Peak Can
Peak Can (PCAN) представляет собой USB-устройство, способное перехватывать и передавать сообщения. Благодаря программному обеспечению Pcan View можно начинать работу без особого обучения.
Поскольку камера заднего вида менее важна для безопасности, чем другие компоненты, я предположил, что искомые данные, скорее всего, будут на шине GMLAN.
Самая простая точка доступа — разъём OBD2. Я подключил Peak Can к шине GMLAN, запустил программное обеспечение — и сразу началось прослушивание трафика.
Видео:Универсальный CAN адаптер MFD207CAN-UN (часть 2)Скачать
Интеграция
Цель состояла в том, чтобы перепроектировать вызов камеры заднего вида. Для этого с включённым снифером я повёл машину задним ходом, чтобы она включила дисплей, а затем несколько раз попробовал парковаться. На протяжении всего этого процесса я заметил один ID с сообщениями, которые последовательно имитировали мои действия.
Тогда я припарковался и через Pcan View попытался передать то же самое сообщение, которое я видел, когда включался и выключался дисплей. В мгновение ока я уже взаимодействовал с шиной.
Передача сообщения через PCAN
Впрочем, я не планировал постоянно ездить с ноутбуком. Нужен был способ автоматизировать эти функции — и здесь пригодилась Arduino. Возможность напрямую получать питание 12V в сочетании с большим количеством ресурсов и поддержки в интернете сделала этот выбор очевидным.
В дополнение к Arduino для завершения проекта мне понадобилось два компонента: модуль CAN и модуль реле. По сути, Arduino — это мозг, запускающий и выполняющий код. Модуль CAN предоставляет возможность взаимодействовать с шиной данных, а реле обеспечивает питание фронтальной камеры, а также действует как видеомикшер между ней и камерой заднего вида.
Модуль mcp2515 (сверху), Arduino Uno (посередине), модуль реле (снизу)
После добавления и настройки соответствующих библиотек Arduino установил связь с автомобилем.
Прослушивание трафика через Arduino
Поскольку я уже знал, что могу запустить дисплей, то начал думать о том, КАК это сделать. Первоначальная идея состояла в том, чтобы установить на панели специальную кнопку мгновенного вызова, но я начал думать: «А что ЕЩЁ в сети можно использовать в качестве триггера?»
В ходе экспериментов я обнаружил, что по шине GMLAN также передаются сообщения с ID, соответствующим кнопке «Отмена круиз-контроля». Это было идеально, потому что круиз-контроль включается на скоростях более 65 км/ч, когда я буду использовать переднюю камеру, а на скоростях ниже 15 км/ч будет включаться камера заднего вида, чтобы помочь с парковкой, так что они никогда не будут перекрываться. После написания некоторого кода я смог заставить Arduino распознать, когда нажимается кнопка отмены круиз-контроля.
Распознавание однократного нажатия кнопки
Однако я не хотел, чтобы камера активировалась каждый раз, когда я отменяю круиз-контроль, поэтому я решил, что лучший подход — превратить её (по сути) в многофункциональную кнопку. Камера активируется только в том случае, если кнопка «дважды нажата».
После долгого уикенда изучения функции millis и отладки кода я успешно запрограммировал распознавание двойного нажатия.
Распознавание двойного нажатия
И когда я привязал его к своим командам для управления дисплеем, у меня собралась довольно крутая небольшая утилита.
Видео:CAN шина👏 Как это работаетСкачать
Функциональность
Теперь у меня была возможность включать и выключать дисплей, но оставалась одна проблема — что насчёт камеры заднего вида? Мне нужно было, чтобы они с фронтальной камерой работали вместе, словно их так настроили на заводе.
На блок-схеме я изобразил, как я это представляю.
Я быстро понял, что для такой системы нужно в любой момент времени знать состояние трёх переменных:
- Модуль передней камеры: водитель включил или выключил его?
- Дисплей камеры: изображение на дисплее включено или выключено?
- Задний ход: автомобиль в реверсе или нет?
Не имея опыта программирования, это было очень сложно сделать, и я всё свободное время думал о разных подходах.
В конце концов, я добился успеха!
Теперь я смог реализовать операционную логику, которая контролирует реле.
На протяжении всего процесса я всё больше узнавал об Arduino и заметил, что версия Nano способна делать всё, что нужно, при этом у неё меньший размер и более низкая цена. Она идеально подходит для постоянной установки в автомобиль. Я разработал модель и распечатал на 3D-принтере корпус для размещения компонентов в качестве компактного блока для установки.
Видео:Can Bus - что это такое ? Зачем нужен ? Как настроить ?Скачать
Всё вместе
Наконец настал день, когда я увидел результаты. Хотя нужно ещё повозиться с таймингом, но было приятно видеть, что модуль корректно работает.
Включение/выключение режима парковки, включение/выключение фронтальной камеры, автоматическое переключение на камеру заднего вида и автоматическое переключение обратно
В целом, этот опыт меня многому научил и открыл глаза на возможности интеграции непосредственно с шиной CAN. Довольно удивительно, чего можно достичь соединением по двум проводам.
Видео:Простая проверка CAN шины. Сканер не видит автомобиль через OBD2. Как правильно выбрать изоленту.Скачать
Как я добавил функции автомобилю по шине CAN, не умея программировать
Цель этой статьи — рассказать о моём опыте модификации автомобиля и экспериментах с шиной CAN.
Видео:Компьютерная диагностика авто. K-линия и CAN шинаСкачать
С чего всё началось
Сначала я решил добавить фронтальную камеру в свой 2017 Chevrolet Cruze. Поскольку у автомобиля уже есть заводская камера заднего вида, то на высоком уровне нужно было выяснить две вещи:
- Способ передачи видео с фронтальной камеры, которую я добавлю.
- Способ отображения на экране картинки с камеры заднего вида в любое время.
Видеочасть была простой. Из предыдущего опыта я знал, что можно сделать видеомикшер на реле.
Читайте также: Назначение шины управления мпс
Запуск на экране оказался более сложным, и после некоторого расследования я пришёл к выводу, что машина должна подавать сигнал от камеры заднего вида на экран через какую-то шину данных.
Видео:Установка бесконтактного считывателя CAN-шины Eurosens InCANСкачать
Шина CAN
У Chevrolet две разные шины данных. Первая — это стандартная CAN, быстрая (500 Кбит/с) и надёжная, она используется для критических данных. Вторая — то, что GM называет LAN (GMLAN), более старая и медленная шина (33,3 Кбит/с), которая используется для данных, не связанных с безопасностью.
Мне нужен был способ прослушивать трафик по CAN, то есть снифер. Для этой цели невероятно полезно устройство PCAN.
Peak Can
Peak Can (PCAN) представляет собой USB-устройство, способное перехватывать и передавать сообщения. Благодаря программному обеспечению Pcan View можно начинать работу без особого обучения.
Поскольку камера заднего вида менее важна для безопасности, чем другие компоненты, я предположил, что искомые данные, скорее всего, будут на шине GMLAN.
Самая простая точка доступа — разъём OBD2. Я подключил Peak Can к шине GMLAN, запустил программное обеспечение — и сразу началось прослушивание трафика.
Видео:Универсальный CAN адаптер MFD207CAN-UN (часть 1)Скачать
Интеграция
Цель состояла в том, чтобы перепроектировать вызов камеры заднего вида. Для этого с включённым снифером я повёл машину задним ходом, чтобы она включила дисплей, а затем несколько раз попробовал парковаться. На протяжении всего этого процесса я заметил один ID с сообщениями, которые последовательно имитировали мои действия.
Тогда я припарковался и через Pcan View попытался передать то же самое сообщение, которое я видел, когда включался и выключался дисплей. В мгновение ока я уже взаимодействовал с шиной.
Передача сообщения через PCAN
Впрочем, я не планировал постоянно ездить с ноутбуком. Нужен был способ автоматизировать эти функции — и здесь пригодилась Arduino. Возможность напрямую получать питание 12V в сочетании с большим количеством ресурсов и поддержки в интернете сделала этот выбор очевидным.
В дополнение к Arduino для завершения проекта мне понадобилось два компонента: модуль CAN и модуль реле. По сути, Arduino — это мозг, запускающий и выполняющий код. Модуль CAN предоставляет возможность взаимодействовать с шиной данных, а реле обеспечивает питание фронтальной камеры, а также действует как видеомикшер между ней и камерой заднего вида.
Модуль mcp2515 (сверху), Arduino Uno (посередине), модуль реле (снизу)
После добавления и настройки соответствующих библиотек Arduino установил связь с автомобилем.
Прослушивание трафика через Arduino
Поскольку я уже знал, что могу запустить дисплей, то начал думать о том, КАК это сделать. Первоначальная идея состояла в том, чтобы установить на панели специальную кнопку мгновенного вызова, но я начал думать: «А что ЕЩЁ в сети можно использовать в качестве триггера?»
В ходе экспериментов я обнаружил, что по шине GMLAN также передаются сообщения с ID, соответствующим кнопке «Отмена круиз-контроля». Это было идеально, потому что круиз-контроль включается на скоростях более 65 км/ч, когда я буду использовать переднюю камеру, а на скоростях ниже 15 км/ч будет включаться камера заднего вида, чтобы помочь с парковкой, так что они никогда не будут перекрываться. После написания некоторого кода я смог заставить Arduino распознать, когда нажимается кнопка отмены круиз-контроля.
Распознавание однократного нажатия кнопки
Однако я не хотел, чтобы камера активировалась каждый раз, когда я отменяю круиз-контроль, поэтому я решил, что лучший подход — превратить её (по сути) в многофункциональную кнопку. Камера активируется только в том случае, если кнопка «дважды нажата».
После долгого уикенда изучения функции millis и отладки кода я успешно запрограммировал распознавание двойного нажатия.
Распознавание двойного нажатия
И когда я привязал его к своим командам для управления дисплеем, у меня собралась довольно крутая небольшая утилита.
Видео:CAN-Bus адаптер для VW Audi Seat Skoda Peugeot Citroen Mercedes BMW Ford - магнитола будет работать!Скачать
Функциональность
Теперь у меня была возможность включать и выключать дисплей, но оставалась одна проблема — что насчёт камеры заднего вида? Мне нужно было, чтобы они с фронтальной камерой работали вместе, словно их так настроили на заводе.
На блок-схеме я изобразил, как я это представляю.
Я быстро понял, что для такой системы нужно в любой момент времени знать состояние трёх переменных:
- Модуль передней камеры: водитель включил или выключил его?
- Дисплей камеры: изображение на дисплее включено или выключено?
- Задний ход: автомобиль в реверсе или нет?
Не имея опыта программирования, это было очень сложно сделать, и я всё свободное время думал о разных подходах.
В конце концов, я добился успеха!
Теперь я смог реализовать операционную логику, которая контролирует реле.
На протяжении всего процесса я всё больше узнавал об Arduino и заметил, что версия Nano способна делать всё, что нужно, при этом у неё меньший размер и более низкая цена. Она идеально подходит для постоянной установки в автомобиль. Я разработал модель и распечатал на 3D-принтере корпус для размещения компонентов в качестве компактного блока для установки.
Видео:Arduino CAN Monitor (простейший монитор шины CAN)Скачать
Всё вместе
Наконец настал день, когда я увидел результаты. Хотя нужно ещё повозиться с таймингом, но было приятно видеть, что модуль корректно работает.
Включение/выключение режима парковки, включение/выключение фронтальной камеры, автоматическое переключение на камеру заднего вида и автоматическое переключение обратно
В целом, этот опыт меня многому научил и открыл глаза на возможности интеграции непосредственно с шиной CAN. Довольно удивительно, чего можно достичь соединением по двум проводам.
Видео:Универсальная плата CAN шиныСкачать
CAN-USB адаптер из stm32vldiscovery
При разработке устройств с CAN-интерфейсом желательно иметь удобный инструмент для отслеживания сообщений в сети. Для RS232/485 существует множество бюджетных USB адаптеров и куча разнообразного софта, а вот для CAN мне не удалось найти недорогое готовое решение.
В то же самое время на сайтах автолюбителей находились самодельные устройства для подключения к CAN шине автомобиля. Одними из готовых проектов были USB<>CAN Bus Interface (CAN Hacker), реализованный на Atmega+SJA1000 и проект STM32-CAN-Busadapter, реализованный на STM32F105. Работают они с программой CAN Hacker, которая выглядит достаточно удобной для работы. Беглый анализ протокола команд по USB показал, что эти устройства представляются COM портом, и дальнейшее общение происходит в виде передачи команд из ASCII символов.
В закромах была найдена плата STM32VLDiscovery, которая и стала объектом испытаний. На ней мы будем повторять «STM32-CAN-Busadapter».
Первым делом придётся заменить микроконтроллер STM32F100, установленный на STM32VLDiscovery. Дело в том, что одновременная работа CAN и USB в серии F1 возможна только в микроконтроллерах STM32F105/107. Хорошо, что у STM заявлена pin-to-pin совместимость микроконтроллеров различных серий, но выполненном в одинаковом корпусе.
Читайте также: Can шина мерседес w140
В местном магазине были приобретены:
1. STM32F105RBT6 297 руб.
2. PCA82C250T 115 руб.
3. TJA1055T 138 руб.
4. PBS-40, 2шт. 114 руб.
Макетная плата «с дырочками 2,54» уже давно ждала своего часа.
Попытка сделать всё по-быстрому
Стираем пыль с STM32VLDiscovery, проверяем, что она ещё работает, загрузив Demo-проект. Перепаиваем контроллер, проверяем, что пересадка прошла успешно, загрузив тот же самый проект.
С сайта проекта STM32-CAN-Busadapter загружаем (требуется регистрация) бинарный файл прошивки и при помощи «STM32 ST-LINK Utility» зашиваем в наш контроллер.
Упрощённая схема выглядит так. Более подробная — на сайте проекта STM32-CAN-Busadapter.
Припаиваем USB D+,D-,Vbus в соответствии со схемой. Добавляем джампер/переключатель на PA2, у автора названый «Bootloader».
Включаем и . ничего не работает, устройство по USB не определяется, совсем. При любых положениях «Bootloader».
Вспоминаем, что для определения подключения по USB необходимо линию D+ подтянуть на 5В через 1,5КОм резистор. После этого наше устройство начинает определяться как «неизвестное устройство» с vid/pid 0000.
Дальше было несколько часов попыток разобраться, что же происходит, и принято решение написать тестовую прошивку для проверки USB подключения.
Пишем тестовую прошивку, для проверки USB
Для написания тестовой прошивки используем STM32CubeMX, что позволит нам по-быстрому состряпать тестовую прошивку. По утверждениям ST и дистрибьютеров, использование CubeMX — это «модно, стильно, молодёжно», надо же когда-то попробовать разобраться с этим Cub-ом.
С сайта STMicroelectronic скачиваем STM32CubeMX. Версия периодически обновляется, у меня v4.7.0.
В установленном Cube входим в «Help»->«Install New Libraries» и устанавливаем «Firmware Package for Family STM32F1» (у меня V1.0.0).
В «Help»->«Updater Settings» можно посмотреть «Repository Folder» — место, куда скачалась наша «Firmware Package», там лежат в том числе и примеры с исходниками для различных отладочных плат.
Создаём новый проект в Cube
MCU — STM32F105RBTx.
В «Configuration»->«Peripheals»->«RCC» выбираем тактирование от внешнего кварцевого резонатора, HSE устанавливаем в «Crystal/Ceramic Resonator».
В «Configuration»->«Peripheals»->«USB_OTG_FS» выбираем режим «Device_Only», и устанавливаем галочку «Activate_VBUS» — для автоматического определения момента подключения к USB. После этого у нас автоматически назначаться ножки PA9, PA11 и PA12 на работу с USB.
В «Configuration»->«MiddleWares»->«USB_DEVICE»->«Class For FS IP» выбираем «Communication Device Class (Virtual Port Com)».
Дальше на закладке «Clock Configuration» настроим систему тактирования для нашего микроконтроллера. Подсмотреть готовые значения коэффициентов PLL и Prescaler-ов можно в примерах, посмотрев процедуру SystemClock_Config. У нас должна получиться такая «картина»:
Теперь можно сгенерировать проект для компиляции.
Перед первой генерацией запросит ввести название, место хранения проекта и IDE для которой будет формироваться проект. Я выбрал Keil 4, как более привычный. Возможны варианты Keil5, Keil4, IAR, TrueStudio, SW4STM32. После генерации нажимаем «Open Project» и открывается наша среда разработки. Ничего не меняя компилируем и загружаем.
И…, оно работает. Определилось устройство, драйвера нашлись на сайте ST. Теперь в «Диспетчере устройств» видим «STMicroelectronics Virtual COM Port (COM4)».
Далее было потрачено какое-то время, чтобы понять, почему железо работает, а чужая прошивка нет. В итоге было замечено, что бинарные файлы выглядят по-разному.
Помню, что в начале программы идут вектора прерываний и в нашей прошивке мы видим что-то похожее, а в скачанной прошивке данные совсем не похожи на команды перехода по адресам.
Более того, гугль подсказал, что первые 4 байта прошивки — адрес стека, следующие 4 байта — адрес первой команды программы.
Написал автору STM32-CAN-Busadapter. Описал, что прошивка «битая», не работает, что первые байты не такие, как должны быть. Andreas мне ответил. Написал, что прошивка-то рабочая, но требует его фирменного загрузчика. К письму был приложен .hex файл «personally version only for you».
ОК, смотрим как оно работает
Допаиваем микросхемы трансиверов CAN, получаем вот такую «красоту». Подтягивающий резистор к D+ линии USB можно убрать, он есть внутри микроконтроллера.
Прошиваем, запускаем CAN Hacker, изучаем. Здесь нам понадобится подключение к какой-нибудь CAN сети. У меня это была плата openmcu с STM32F107 контроллером, которая выдавала CAN посылки. Поигравшись с программой «CAN Hacker», понял, что штука подходящая, есть режимы monitor и tracer — сообщения выводятся или в таблицу или в список по мере поступления.
Вот небольшое видео, не моё.
Теперь можно попробовать написать свою прошивку для адаптера. Тем более заготовка прошивки у нас уже есть.
Пишем свою прошивку.
Открываем в нашей IDE проект, сгенерированный Cube и дописываем недостающие куски кода.
Основное правило — писать в промежутках между
/* USER CODE BEGIN… */
и
/* USER CODE END… */
Иначе всё, что написано вне таких специально отведённых мест будет нещадно перезаписано Cub-ом при следующей генерации проекта.
Для начала сделаем эхо: всё, что отправили в наш виртуальный COM порт получаем обратно.
При приёме данных по USB вызывается процедура CDC_Receive_FS (uint8_t* Buf, uint32_t *Len) в файле «usbd_cdc_if.c«.
Добавим отправку обратно всего, что получили.
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
Компилируем, загружаем. Открываем любой терминальной программой наш виртуальный COM-порт. Параметры порта (скорость, чётность) могут быть любыми. Убеждаемся, что эхо работает.
Подстраиваемся под CAN Hacker
Теперь начнём реализацию протокола для работы с программой «CAN Hacker». Сам протокол можно посмотреть на странице проекта USB<>CAN Bus Interface (CAN Hacker), в файле «описание», или поискать на просторах интернета по названию «Lawicel Protokol».
Программой USBTrace был подсмотрен процесс инициализации адаптера.
Необходимо ответить на команду «запрос версии», на все остальные запросы просто отвечаем «ОК» (0x0D).
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
USBD_CDC_SetTxBuffer(hUsbDevice_0, (uint8_t*)&UserTxBufferFS[0], num_bytes);
USBD_CDC_TransmitPacket(hUsbDevice_0);
return (USBD_OK);
/* USER CODE END 6 */
>
После этого программа «CAN Hacker» сможет «увидеть» наш адаптер.
Добавим интерфейс CAN к нашему проекту
В Cube устанавливаем «Configuration»->«Peripheals»->«CAN1» галочку «Master mode». На закладке «Configuration» «CAN1» настраиваем скорость и разрешаем прерывание по приёму:
Генерируем проект в Cube, открываем в IDE.
В «main.c» необходимо добавить буферы для CAN, настроить фильтр входящих сообщений и добавить процедуру HAL_CAN_RxCpltCallback. Эта процедура будет вызываться из прерывания по приёму CAN. Название, разумеется, может быть только таким, т.к. именно оно прописано в «недрах» проекта, сгенерированного Cub-ом. Всё что приняли по CAN будем пересылать в USB, в соответствии с протоколом. Например по CAN с адреса 0x123 приняли 8 байт данных 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88, запаковываем это в посылку для USB «t12381122334455667788» добавляем в конце символ 0x0D и отправляем в наш виртуальный СОМ порт на ПК.
Читайте также: Шины диски для опель мокка
буферы для приёма/передачи
/* USER CODE BEGIN PV */
static CanTxMsgTypeDef can1TxMessage;
static CanRxMsgTypeDef can1RxMessage;
/* USER CODE END PV */
процедура, вызываемая при приёме посылки
/* USER CODE BEGIN 0 */
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* CanHandle)
pRxMsg->StdId,\
CanHandle->pRxMsg->DLC,\
CanHandle->pRxMsg->Data[0],\
CanHandle->pRxMsg->Data[1],\
CanHandle->pRxMsg->Data[2],\
CanHandle->pRxMsg->Data[3],\
CanHandle->pRxMsg->Data[4],\
CanHandle->pRxMsg->Data[5],\
CanHandle->pRxMsg->Data[6],\
CanHandle->pRxMsg->Data[7]\
);
CDC_Transmit_FS(buf,num_bytes); // отправляем в USB то, что получили по CAN
HAL_CAN_Receive_IT(&hcan1, CAN_FIFO0); //ждём следующую посылку
>
/* USER CODE END 0 */
для использования внешней функции CDC_Transmit_FS подключим .h файл
/* USER CODE BEGIN Includes */
#include «usbd_cdc_if.h»
/* USER CODE END Includes */
в основном цикле main добавим инициализацию буферов и настроим приёмный фильтр
/* USER CODE BEGIN 2 */
hcan1.pTxMsg = &can1TxMessage;
hcan1.pRxMsg = &can1RxMessage;
/* USER CODE END 2 */
// настраиваем фильтр — приём всех посылок
CAN_FilterConfTypeDef canFilterConfig;
canFilterConfig.FilterNumber = 0;
canFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
canFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
canFilterConfig.FilterIdHigh = 0x0000;
canFilterConfig.FilterIdLow = 0x0000;
canFilterConfig.FilterMaskIdHigh = 0x0000 Реализация кольцевого буфера
буфер для отправки USB у нас уже определён
uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];
в «usb_cdc_if.c» определим указатели в этот буфер
uint32_t ptrWriteUserTxBufferFS = 0; //запись в буфер при приёме из CAN
uint32_t ptrReadUserTxBufferFS = 0; //чтение из буфера, отправка в USB
/* USER CODE END 1 */
опишем процедуры добавления данных в буфер и отправки из буфера
к сожалению, нормального места в «usb_cdc_if.c» не нашлось, поэтому пришлось пихать в секцию «USBD_CDC_Private_Macros»
/* USER CODE BEGIN 2 */
extern uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];
extern uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];
extern uint8_t interface_state;
extern USBD_HandleTypeDef *hUsbDevice_0;
if(ptrReadUserTxBufferFS != ptrWriteUserTxBufferFS)
ptrWriteUserTxBufferFS) // сделать кольцо?
else
__enable_irq();
if(interface_state != 1) return(1); //если интерфейс не сконфигурирован, то отправлять не будем
if(USBD_CDC_TransmitPacket(hUsbDevice_0) == USBD_OK)
>
>
return(0);
>
/* USER CODE END 2 */
в «main.c» добавим переменную
uint8_t interface_state = 0;
пока от CAN Hacker не придёт команда «O» — переход в рабочий режим из настроечного, не будем ничего слать в USB, т.к. считаем что интерфейс ещё не сконфигурирован
заменим в прерывании CAN прямую отправку в USB на добавление в буфер отправки
CDC_Transmit_FS на CDC_add_buf_to_transmit
и добавим периодический опрос буфера для отправки
while (1)
/* USER CODE END 3 */
Компилируем, загружаем. Видим, что теперь в CAN Hacker отображаются все сообщения, без пропусков.
Добавим Timestamp
В протоколе CAN Hаcker предусмотрены «отметки времени» для каждого сообщения. Диапазон значений 0..60000 мс.
Используем для этого TIM1.
В Cub-е, в «Configuration»->«Peripheals»->«TIM1» выбираем «Clock source»=«Internal Clock».
Для настройки частоты тактирования таймера 1000 Гц (1мс). Придётся понизить частоту тактирования APB2.
Убеждаемся, что от APB2 не тактируется ничего для нас важного по быстродействию.
Из «reference manual» на «STM32F1»:
Видим, что от APB2 тактируются таймер1, порты ввода-вывода, АЦП, SPI, USART, и мы можем смело понижать частоту APB2.
В Cub-е, на закладке «Clock Configuration» устанавливаем «APB2 Prescaler» равным 8, получаем частоту тактирования таймера 18 МГц.
На закладке «Configuration»->«TIM1» устанавливаем
Prescaler(PSC — 16 bit value) = 18000
Counter period (AutoReload Register) = 60000
Генерируем код, открываем в IDE.
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* CanHandle)
pRxMsg->StdId,\
CanHandle->pRxMsg->DLC,\
CanHandle->pRxMsg->Data[0],\
CanHandle->pRxMsg->Data[1],\
CanHandle->pRxMsg->Data[2],\
CanHandle->pRxMsg->Data[3],\
CanHandle->pRxMsg->Data[4],\
CanHandle->pRxMsg->Data[5],\
CanHandle->pRxMsg->Data[6],\
CanHandle->pRxMsg->Data[7],\
time
);
if(interface_state == 1) CDC_add_buf_to_transmit(buf,num_bytes);
Опять, вроде работает, но при интенсивном обмене в CAN обмен по USB «затыкается».
На этот раз виновата процедура sprintf, которая долго выполняется в прерывании CAN.
Перепишем формирование посылки из CAN в USB без использования sprintf.
uint8_t halfbyte_to_hexascii(uint8_t _halfbyte)
= 10) return(‘A’ + _halfbyte — 10);
else return(‘0’ + _halfbyte);
>
uint8_t hexascii_to_halfbyte(uint8_t _ascii)
= ‘0’) && (_ascii = ‘a’) && (_ascii = ‘A’) && (_ascii изменим процедуру HAL_CAN_RxCpltCallback
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* CanHandle)
pRxMsg->StdId,\
CanHandle->pRxMsg->DLC,\
CanHandle->pRxMsg->Data[0],\
CanHandle->pRxMsg->Data[1],\
CanHandle->pRxMsg->Data[2],\
CanHandle->pRxMsg->Data[3],\
CanHandle->pRxMsg->Data[4],\
CanHandle->pRxMsg->Data[5],\
CanHandle->pRxMsg->Data[6],\
CanHandle->pRxMsg->Data[7],\
time
);
*/
num_bytes = 0;
buf[num_bytes++] = ‘t’;
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->StdId)>>8);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->StdId)>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->StdId));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->DLC));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[0])>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[0]));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[1])>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[1]));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[2])>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[2]));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[3])>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[3]));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[4])>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[4]));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[5])>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[5]));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[6])>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[6]));
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[7])>>4);
buf[num_bytes++] = halfbyte_to_hexascii((CanHandle->pRxMsg->Data[7]));
buf[num_bytes++] = halfbyte_to_hexascii((time)>>12);
buf[num_bytes++] = halfbyte_to_hexascii((time)>>8);
buf[num_bytes++] = halfbyte_to_hexascii((time)>>4);
buf[num_bytes++] = halfbyte_to_hexascii((time)>>0);
buf[num_bytes++] = ‘\r’;
if(interface_state == 1) CDC_add_buf_to_transmit(buf,num_bytes);
Некоторые эксперименты по быстродействию на приём: в CAN формируем 1000000 посылок с адреса 0x321 и в CAN Hacker смотрим сколько из них примем.
Скорость 500 Кбит/сек, посылки без перерывов, потери 0,2%:
Скорость 1 Мбит/сек, посылки без перерывов, потери 50%:
Скорость 1 Мбит/сек, по две посылки каждые 1 мс, потери 0%:
По-моему неплохой результат.
Добавим возможность отправки сообщений в CAN
В файле «usbd_cdc_if.c», в процедуру USB CDC_Receive_FS добавим:
hcan1.pTxMsg->StdId = hexascii_to_halfbyte(Buf[i++]);
hcan1.pTxMsg->StdId = (hcan1.pTxMsg->StdId StdId = (hcan1.pTxMsg->StdId DLC = hexascii_to_halfbyte(Buf[i++]);
tmp_byte = hexascii_to_halfbyte(Buf[i++]); tmp_byte = (tmp_byte Data[0] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf[i++]); tmp_byte = (tmp_byte Data[1] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf[i++]); tmp_byte = (tmp_byte Data[2] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf[i++]); tmp_byte = (tmp_byte Data[3] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf[i++]); tmp_byte = (tmp_byte Data[4] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf[i++]); tmp_byte = (tmp_byte Data[5] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf[i++]); tmp_byte = (tmp_byte Data[6] = tmp_byte;
tmp_byte = hexascii_to_halfbyte(Buf[i++]); tmp_byte = (tmp_byte Data[7] = tmp_byte;
HAL_CAN_Transmit(&hcan1, 10);
num_bytes = sprintf((char*)UserTxBufferFS,»\r»);
break;
Компилируем, загружаем, проверяем, работает.
Заключение
На этом, пожалуй, можно остановиться. Рубрика ведь «Сделай сам» называется. Если кто захочет, сможет самостоятельно добавить поддержку разных скоростей CAN, работу с 29 битными расширенными идентификаторами, фильтры сообщений, remote frames.
Хочу сказать, что принцип работы через ASCII команды мне понравился. В будущем планирую реализовать USB-SPI, USB-I2C функционал. Например, настраиваем наш виртуальный COM порт на 115200 бод — работаем с CAN, настраиваем на 57600 — работаем с I2C, настраиваем на 9600 — работаем с SPI. Разумеется, при работе с SPI или I2C «CAN Hacker» уже нельзя будет использовать и придётся какой-то свой протокол придумывать.
Готовый проект к данной статье можно скачать по ссылке.
- Свежие записи
- Нужно ли менять пружины при замене амортизаторов
- Скрипят амортизаторы на машине что делать
- Из чего состоит стойка амортизатора передняя
- Чем стянуть пружину амортизатора без стяжек
- Для чего нужны амортизаторы в автомобиле
🔥 Видео
Как работает LIN шина автомобиля. K-Line L-Line шины данных. Лин шина автомобиля. Lin-bus networkСкачать
Кан шина, что это? Поймет школьник! принцип работыСкачать
Подключение к CAN-шине через разъем OBDIIСкачать
Выбор CANBUS в Магнитоле на Андроиде (если есть канбус)Скачать
Как проверить CAN шину Используем симулятор ElectudeСкачать
Автомобиль не запускается, сканер не подключается: на примере FORD FOCUS 3, CAN шина (Видео 92)Скачать
Экспресс диагностика CAN шины на автомобиле. №21Скачать
Как настроить Canbus на Android магнитоле . Правильное подключение .Скачать