Привет, сегодня мы будем настраивать отправку данных с помощью CAN (Controller Area Network). В интернете много информации о том, как настроить CAN использую HAL библиотеку, а в случае использования CMSIS информация обрывочна, по этой причине решил рассказать о своем опыте работы. Принцип работы CAN-сети в этой статье разбирать не будем т.к. на просторах интернета существует большой объем материла (особенно мне нравится как написано тут) на эту тему, но по ходу повествования будем останавливаться на некоторых нюансах. Ставим себе задачу: заставить контроллер периодически отправлять кадры в CAN-сеть с скоростью передачи 250 кБит/с, со стандартной длиной идентификатора (11 бит) с полем данных размеров 8 байт. Использовать будем микроконтроллер STM32F103C8T6 на отладочной плате, называемой в народе "Blue Pill" (рис. 1а). Также нам понадобится два приемопередачика (ПП) (по англицки "transceiver") для CAN-шины. Я использую 2 готовые платы с SN65HVD230 на борту (рис. 1б). На рис. 1в представлена схема этой платы . Для написания прошивки я буду использовать Keil uVision v5. Отладку и демонстрацию работы будем производить с помощью осциллографа и логического анализатора. Соберем, не побоюсь этого слова, испытательный стенд. Соединяем выводы ПП с выводами Blue Pill: CAN TX -> PA12  СAN RX -> PA11 Соединяем выводы ПП CAN_H и CAN_L между собой. Далее соединяем линии питания. В итоге должно получиться что-то подобное схеме на рис. 2. Желтый провод идет к входу логического анализатора. Напишем встраиваемое программное обеспечение для МК. Открываем Keil (или другую удобную для вас среду разработки: IAR, Eclipse/CubeIDE и др., главное, чтобы был установлен CMSIS), создаем проект и настраиваем для работы с нашей «Blue Pill». Если у Вас это вызывает затруднения, то в помощь статья. Настроем систему тактирования. Смотрим в тех. спецификацию (datasheet) (рис. 3). Будем использовать высокоскоростной внутренний тактовый генератор (HSI). Причины почему именно его нет, да и это не является темой статьи. Главное, настроить так чтобы на шине APB1 была частота 36 МГц. Если все же интересно как настраивать систему тактирования, то можно ввести в поисковике «stm32 cmsis rcc», в интернете информации огромное количество, или почитать Reference Manual (бред, конечно, но вдруг поможет 😊). Здесь я лишь приведу текст функции настройки тактирования с комментариями, она не идеальна, но на данном этапе с задачей справляется: Помещаем прототип функции в начало, само тело прячем в подвал, чтобы не мешалось. В функции main вызываем rcc_init().  Создадим функцию и uint8_t can_init(void). Далее текст в теле функции. Включим тактирование CAN: CAN RX и TX по умолчанию находятся на выводах PA11 и PA12, соответственно (см. 31 стр. тех. спецификации). Включаем тактирование порта A: Настраиваем выводы на альтернативную функцию. Настроим сразу и прием (CAN Rx) , но в этой части он на не понадобится: Теперь нам нужен master control register (MCR) Переводим CAN в режим инициализации: Отключим режим автоматической ретрансляции. Ретрансляция сообщения происходит при отсутствии подтверждения сообщения, а т.к. в нашей стенде нам принимать сообщение нечем, следовательно, подтвердить прием тоже некому. Кстати, забегая вперед, когда не приходит подтверждение получения сообщения то в CAN error status register (CAN_ESR) в битах 4…6 LEC[2:0]: (Last error code) возникает Acknowledgment Error. При этом поднимаются 4 и 5 бит (0b011). Настроим чтобы CAN автоматически выходил из спящего режима (Automatic Wakeup Mode): Настроим скорость передачи данных. Помним, мы поставили себе задачу, что скорость составляет 250 кБит/с, т.е длительность одного бита: T_bit = 1/(250*1000) = 4 мкс при этом частота шины APB1 f_APB1 = 36 МГц. Далее я приведу рисунок (рис. 4, взял тут), который пояснит некоторые термины и понятия которые будем использовать далее  В нашем случае: System Clock = f_APB1 = 36 МГц. CAN System Clock это частота после пред делителя. Теперь посмотрим на CAN Bit Period. Видим, что один БИТ состоит из 4 частей. Первая часть всегда длительностью 1 квант. Вторая и третья часть объединяются в первый сегмент. Далее идет точка «захвата» бита (Sample Point) и второй сегмент бита. Количество квантов в сегментах выбирается так чтобы точка «захвата» находилась в районе 87,5% длительности бита для протоколов CANopen и DeviceNet и 75% для ARINC 825. Наш вариант 87,5 %. Теперь у нас два пути: воспользоваться специальным калькулятором. либо составить и систему из двух простых уравнений и решить её. Пойдем по первому пути. Вводим данные и нажимаем «Request Table». Ах да, еще есть величина ширина скачка синхронизации (SJW - Synchronization Jump Width) регулирует битовую синхронизацию по мере необходимости. На расчет она не влияет.  В результате получим таблицу: Выбираем 2-ю строку исходя из положения точки «захвата» и получаем, что 1 сегменту у нас длиной 13 квантов, а 2-ой – 2 кванта времени, t_Q = 1/(16*250000) = 250 нс, а пред делитель равен 9. Более того, нам даже подсказывают, что нужно записать в регистр CAN_BTR, мы его рассмотрим чуть более детально. Кстати, можно было и не указывать желаемую скорость передачи, тогда бы нам калькулятор выдал значения для наиболее часто используемых скоростей. Решаем задачку как в школе: Дано:Скорость передачи, bps = 250 кБит/сТочка захвата на 87,5 % длины бита, sp = 0,875Частота шины APB1, f_APB1 = 36 МГц Решение:Знаем, что длительность бита равна:(x+y+1)*t_Q = 1/bps (1)t_Q - длительность кванта времениСчитаем, что пред делитель равен 1 (PreSc = 1), тогда:t_Q = PreSc/f_APB1 = 27,78 нс НайтиДлительность 1 и 2 сегментов (x и y, соответственно) Решаем систему ур-ий (1) и (2), получаем: x = 125, y = 18, но если посмотреть в ref. manual, то можно заметить, что x (длительность 1-ого сегмента) может принимать значение от 1 до 16. В цикле увеличиваем значение пред делителя на 1 по порядку и решаем систему уравнений для каждого случая. Выбираем наиболее подходящий для нас вариант. Ответ: PreSc = 9, x = 13, y = 2 Настроим скорость передачи, для этого пойдем в CAN bit timing register (CAN_BTR). Сбросим биты раздела Baud rate prescaler (BRP[9:0]) и установим: BRP[9:0] = PreSc - 1 = 8: Сбросим биты, отвечающие за 1 временной сегмент и установим: TS1[3:0] = 13 – 1 = 12: Сбросим биты, отвечающие за 2 временной сегмент и установим: TS2[2:0] = 2 – 1 = 1: Биты 24…25 SJW[1:0] не трогаем их состояние т. о. получим что ширина скачка синхронизации t_SJW = 2t_Q = 500 нс. Также в этом регистре можно настроить CAN в режим отладки (Loop back mode, Silent mode), но это не наш случай.  Настроим почтовый ящик номер 0 предназначенный для отправки (transmit mailbox 0). Чтобы добраться до него в структуре типа CAN_TypeDef есть вложенная структура sTxMailBox типа CAN_TxMailBox_TypeDef.  Сообщения, которые будем отправлять из этого ящика содержат данные (data frame), а не запрос (remote frame), сообщим об этом контроллеру: Будем использовать стандартный идентификатор для кадра: Отчистим идентификатор и поставим какой душе вздумается (в диапазоне от 0 до 3777): Теперь сообщим сколько байт полезной информации будет передавать в одном кадре. CAN позволяет передать от 1 до 8. Эх, гулять так гулять, будем передавать 8. Определим это значение в начале файла, т.к. это значение нам еще пригодится в функции отправки сообщения:  и передадим это значение в регистр:  Все, с настройкой закончили. Естественно, дальше нужно будет настроить и прием, функцию будем допиливать, но это отдельная история. Нас пока интересует передача. Переходим из режима инициализации в «нормальный» режим (normal mode). Завершаем функцию. ссылка на полный код в конце статьи. Теперь создаем функцию для отправки сообщения. Наша функция помимо того, что будет отправлять данные, будет делить их на кадры в соответствии с размером и проверять, возникли ли ошибки отправки. Передавать в функцию будем указатель на адрес начала передаваемых данных, и количество байт которое необходимо передать. Нам понадобятся 2 счетчика i и j. Первый используем для перебора байтов, второй в случае, если размер кадра будет меньше количество передаваемых байт. Проверим есть ли запросы на передачу для 0 почтового ящика, эта информация хранится в CAN transmit status register (CAN_TSR) а именно в 26 бите (TME0). Если есть, ждем, и если ждать придется слишком долго, то функция вернет 1.  Обнуляем данные, которые лежат в регистрах с данными чтобы не отправить случайно какой-нибудь мусор: Далее пишем цикл, который постепенно заполняет каждый байт в регистрах данных TDLR и TDHR и делит данные на кадры если нужно передать больше байт чем настроено в DATA_LENGTH_CODE (мы настроили на 8 байт): Далее добавляем запрос на передачу оставшихся данных и проверяем, что данные отправились без ошибок или возвращаем код ошибки (мы про этот регистр упоминали ранее): Теперь в main до бесконечного цикла вызываем функцию настройки can_init(), инициализируем тестовую строку и переменную для счетчика, который будет использоваться для задержки, а то поднимать таймеры в этой статье не будем, дабы не раздувать объем). В цикле вызываем функцию и создаем простенькую задержку: Все, наша программа готова.  Cобираем проект, прошиваем, подключаем осциллограф и/или логический анализатор. Вот что показывает осциллограф: На осциллограммах в первом случае отправляется 10 байт т.о. сообщение делится на 2 кадра. Во втором случае мы сообщение уменьшили до размера 8 байт – 1 кадр. И в третьем случаем мы длину кадра уменьшили до 4 байт, а длину сообщения вернули к 10 байтам – 3 кадра. Распознать вручную такую осциллограмму весьма затруднительно, для этого используем логически анализатор (я пользуюсь LWLA1034 в паре с ПО – PulseView) (рис. 6). Видно, что данные отправляются как нужно (по таблице ASCII A = 0x41, B = 0x42 и т. д.), идентификатор какой записали (0x556), частота соответствует желаемой (250 кБит/с). Нет подтверждения, что отправленные данные получены, но ему и взяться неоткуда. Кстати посмотрим, что у нас записано в CAN error status register, а именно какой последний код ошибки. Зайдем в Keil в режиме отладки: LEC = 3 – ошибка подтверждения как и ожидалось. На этом всё! Задача выполнена, ура! Полный текст программы и проект в Keil можно скачать с github. Если это будет кому-нибудь интересно, то напишу продолжение про настройку CAN через cmsis на прием.