По реализации работы CAN протокола на МК STM32 публикаций в интернете достаточно много. Но довольно тяжело настроить готовый код под свое устройство, особенно если пытаемся это сделать впервые.

В рамках данной статьи я постараюсь максимально подробно разобрать программный код и подробно описать "подводные камни", с которыми может впервые столкнуться новичок.

 

Физическая связь

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

Большинство контроллеров семейства STM32 имеют одно или несколько модулей CAN. Каждый порт - это две ножки: TX - для передачи данных и RX - для приема данных с шины.  CAN-порт для удобства можно перенаправить на другие ножки контроллера, это может облегчить разводку платы. Например в stm32f103c8 "по умолчанию" это пины PA11 (CAN_RX) и PA12 (CAN_TX), но их можно перенаправить на пины PB8 и PB9 соответственно.

Для связи микроконтроллера с шиной недостаточно настроить ножки и подцепить их к CAN - в лучше случае может выгореть порт контроллера, а в худшем и все наше устройство целиком. Для подсоединения контроллера к шине требуется еще один электронный компонент - приемопередатчик физического уровня сети (трансивер), как правило -это 8-ми контактная микросхема в корпусе SOIC-8 или DIP-8. также встречаются в корпусах SOT23-8 и SOIC-14, но намного реже.

Трансивер в виде отдельной микросхемы является необходимостью. Объясняется это, прежде всего, высокими требованиями к его надежности и рабочим характеристикам, поскольку работает он с цепями, физически выходящими за пределы устройства (в данном случае это сама шина CAN). А условия, в которых находятся эти цепи, зачастую не определены: например, сильные магнитные поля или пролегающие рядом силовые высоковольтные цепи. Более того, при необходимости гальванической развязки ее удобнее всего осуществлять именно между трансивером и контроллером CAN-сети.

Данный материал посвящен больше программной части, поэтому более подробно о "железной" составляющей проекта, о видах трансиверах и вариантах их использования, я опишу в отдельной статье.

 

Программная часть

Поняв как это все работает физически, приступим к рассмотрению программной части.

Программную часть можно разделить на несколько частей:

• Настройка порта и инициализация CAN шины;
• Настройка фильтрации сообщений;
• Прием сообщений;
• Передача сообщений.

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

 Разберем теперь каждый этап подробнее.

 

Настройка порта

Приведу пример настройки портов и  инициализации CAN шины в микроконтроллере STM32F103

Листинг 1. Настройка портов:
	/******************************************************************************
	Определение настройки CAN
	******************************************************************************/
	#define CAN1_ReMap // Закоментировать, если нет ремапинга портов

	#ifndef CAN1_ReMap
		#define CAN1_GPIO_PORT        GPIOA
		#define CAN1_RX_SOURCE        GPIO_Pin_11              // RX-порт
		#define CAN1_TX_SOURCE        GPIO_Pin_12              // TX-порт
		#define CAN1_Periph        RCC_APB2Periph_GPIOA        // Порт перифирии
	#else
		#define CAN1_GPIO_PORT        GPIOB
		#define CAN1_RX_SOURCE        GPIO_Pin_8               // RX-порт
		#define CAN1_TX_SOURCE        GPIO_Pin_9               // TX-порт
		#define CAN1_Periph        RCC_APB2Periph_GPIOB        // Порт перифирии
	#endif



	GPIO_InitTypeDef GPIO_InitStructure;
	// CAN GPIOs configuration
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);           // включаем тактирование AFIO
	RCC_APB2PeriphClockCmd(CAN1_Periph, ENABLE);                   // включаем тактирование порта

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);           // включаем тактирование CAN-шины

	// Настраиваем CAN RX pin
	GPIO_InitStructure.GPIO_Pin   = CAN1_RX_SOURCE;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(CAN1_GPIO_PORT, &GPIO_InitStructure);

	// Настраиваем CAN TX pin
	GPIO_InitStructure.GPIO_Pin   = CAN1_TX_SOURCE;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(CAN1_GPIO_PORT, &GPIO_InitStructure);

	#ifdef CAN1_ReMap
		GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);         // Переносим Can1 на PB8, PB9
	#endif

 

В определениях сразу предусмотрим возможность переопределения портов обмена с CAN шиной. Если нам не нужен ремап, то строка "#define CAN1_ReMap" должна быть закомментирована, а если же мы используем переадресацию, то просто раскомментируем эту строку и CAN будет настроен на работу через порт GPIOB на пинах PB8 и PB9.

Следует обратить внимание на то, что если в Вашем микроконтроллере несколько CAN устройств, то настройка переадресации, как и настройка CAN в целом, может немного отличаться.

Далее в процедуре инициализации мы включаем тактирование альтернативных функций, CAN-шины и порта, на котором будет "висеть" наша шина, иначе у нас ничего не заработает, так как контроллер не будет знать, что эту периферию нужно активировать.

Затем настраиваем ножки контроллера: 

Для CAN RX настраиваем режим работы как GPIO_Mode_IPU - вход с подтяжкой к питанию,
а для CAN TX настраиваем режим работы как GPIO_Mode_AF_PP - выход с двумя состояниями (Push-Pull) для альтернативных функций.

Если у нас включена переадресация, то компилятор добавит в код еще и команду ремапинга портов шины.

В принципе это вся настройка пинов, теперь нам осталось сделать инициализацию самого устройства.

 

Инициализация CAN

Определяем структуру описания CAN, затем "отключаем" его для настройки.

Нам необходимо задать несколько параметров протокола, а также режим работы и тайминги.

Листинг 2. Настройка CAN:
	// Инициализация шины
	CAN_InitTypeDef CAN_InitStructure;

	CAN_DeInit(CAN1);
	CAN_StructInit(&CAN_InitStructure);

	// CAN cell init
	CAN_InitStructure.CAN_TTCM = DISABLE;
	CAN_InitStructure.CAN_ABOM = ENABLE;
	CAN_InitStructure.CAN_AWUM = ENABLE;
	CAN_InitStructure.CAN_NART = DISABLE;
	CAN_InitStructure.CAN_RFLM = DISABLE;
	CAN_InitStructure.CAN_TXFP = ENABLE;
	CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
	CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
	CAN_InitStructure.CAN_BS1 = CAN_BS1_13tq;
	CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
	CAN_InitStructure.CAN_Prescaler = 50;
	CAN_Init(CAN1, &CAN_InitStructure);

Здесь мы немного подробнее рассмотрим настройки инициализации.

Начнем с параметров работы CAN:

Таб. 1. Параметры CAN
Параметр Расшифровка Пояснение
CAN_TTCM Enable or disable the time triggered communication mode.
Включение или отключение режима Time Triggered Mode.

Этот параметр влияет на включение таймера.
Внутренний 16-битный таймер используется для привязки метки времени к каждому принятому и отправленному сообщению. Этот таймер начинает счет с момента разрешения работы контроллера CAN.
Этот счетчик может быть сброшен приложением или автоматически после приема в последний mailbox, когда установлен Time Triggered Mode

CAN_ABOM Enable or disable the automatic bus-off management.
Включение или отключение автоматического отключения шины.

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

CAN_AWUM Enable or disable the automatic wake-up mode.
Включение или отключение автоматического пробуждения устройства по сигналу с CAN-шины. 
При включении этого параметра устройство будет автоматически просыпаться, но следует обратить внимание что на активацию устройства требуется некоторое время и первый пакет, переданный по шине, может быть утерян.

CAN_NART Enable or disable the no-automatic retransmission mode.
Включение или отключение режима проверки получения пакета.

Если этот режим включен, то микроконтроллер при передаче кадра не будет проверять подтверждение получения пакета всеми устройством на шине.
Если выключен - то, при передаче кадра, микроконтроллер будет слушать шину на предмет получения подтверждения от всех устройства о том, что пакет получен и, если не получен хотя бы одним устройтсвом, то будет пытаться отправить повторно до тех пор, пока все устройства не подтвердят получение пакета.
Другими словами: При включенном параметре передал и забыл, при выключенном - передал и проверил, если не смог отправить, то пытается еще раз, пока не сможет отправить.
Если устройство не подключено к шине или если неправильно настроены тайминги, то при выключении этого параметра контроллер будет бесконечно пытаться отправлять пакет в шину.

CAN_RFLM Enable or disable the Receive FIFO Locked mode.
Включение или отключение режима блокировки Receive FIFO

0 - при переполнении RX_FIF0 прием не прерывается (3 сообщения до заполнения FIFO), каждый новый пакет затирает предыдущий
1 - при переполнении RX_FIF0 прием прерывается (3 сообщения до заполнения FIFO), новые пакеты отбрасываются до освобождения RX_FIFO.
(Спасибо maxx_ir  за подсказку)
CAN_TXFP Enable or disable the transmit FIFO priority.
Включение или отключение приоритета передачи FIFO

Включение этого параметра определяет, в каком порядке сообщения будут отправляться в шину.
Если параметр включен, то сообщения отправляются в хронологическом порядке: FIFO - First Input First Output - Первый пришел - первым ушел.
Если же выключен, то пакеты передаются в зависимости от приоритета ID пакета. Т.е. пакет с более высоким приоритетом будет отправлен раньше.
 

 

Для первого запуска и тестирования устройства нам будет достаточно включить режим "CAN_NART". При подключении осциллографа к шине, если устройство нормально функционирует, мы увидим всплески активности в зависимости от того, как часто передаем данные по шине.

Если же "CAN_NART" выключен, то при включенном параметре "CAN_ABOM" будет предпринято всего 128 попыток передать пакет, а потом микроконтроллер отключит модуль Can (если Вы не будете обрабатывать ошибки и сбрасывать счетчики ошибок). При выключенном "CAN_ABOM" автоматического отключения модуля Can не произойдет.

Если же устройство не будет получать подтверждение о получении пакетов, то осциллограф нарисует "расческу", так как устройство будет бесконечно пытаться передать пакеты, пока не получит подтверждения. 

 

Следующий параметр CAN_Mode определяет в каком режиме контроллер будет работать с CAN-шиной:

Таб. 2. Режим работы контроллера
Параметр Расшифровка Пояснение
CAN_Mode_Normal

Normal -нормальный режим работы При этом параметре МК будет работать в обычном (нормальном) режиме работы. Данные будут передаваться и читаться из шины.

CAN_Mode_LoopBack

LoopBack - слушать себя При выборе режима LoopBack, контроллер будет передавать данные в шину и слушать себя же одновременно.
Это равносильно тому, что в USART мы бы замкнули ножку TX на ножку RX.
Но пакеты из шины доходить до контроллера не будут.

CAN_Mode_Silent

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

CAN_Mode_Silent_LoopBack 

Это объединенные режимы Silent и LoopBack - слушать только себя В данном режиме все пакеты будут полностью крутится внутри контроллера без выхода в общую шину.
Из шины соответственно ни один пакет данных не дойдет до устройства.
Этот режим идеален для отладки устройства. При включении его мы можем как передать данные в шину, так и обработать те данные, которые мы же и отправили, при этом не имея физического подключения к шине.

 

Повторюсь, если Вы только начинаете работать с CAN и у Вас нет готового устройства с трансивером, Вы можете научится работать с шиной на любой отладочной плате с микроконтроллером STM32, который поддерживает CAN. Для этого достаточно выбрать при настройке режим работы CAN_Mode_Silent_LoopBack или CAN_Mode_LoopBack. В этих режимах Вы сможете отправлять пакеты данных и принимать их же на одном устройстве.

 

 

Настройка таймингов

Синхронизация и тайминги в CAN — отдельный, важный и сложный вопрос. Однако, благодаря сложности и продуманности становится не так важна возможная рассинхронизация и нестабильность тактовых частот узлов сети, и связь становится возможной даже в тяжёлых условиях.

Вкратце разберем как выглядит передача одного бита информации при передаче сообщения в CAN шине.

Всё время делится на кванты длиной t_q, и номинальная длительность бита равна 1+BS1+BS2 квантов. Захват значения бита происходит на границе BS1 и BS2. В процессе приёма приёмник определяет, в какой из временных периодов произошёл перепад сигнала (т.е. начало приёма нового бита). В норме перепад должен произойти на границе SYNC и BS1, если он произошёл раньше — контроллер уменьшает BS1, если позже — увеличивает BS2 на величину SJW (от 1 до 4 квантов времени). Таким образом, происходит постоянная пересинхронизация с частотой других приёмников.

В нашем примере мы настраиваем тайминги с учетом того, что перефирия настроена на частоту 8MHz. С помощью калькулятора таймингов, выбираем оптимальные под нашу шину и микроконтроллер. 

Скорость шины настраивается с помощью прескалера. При указанных параметрах тайминга и CAN_Prescaler равным 50 - скорости передачи по шине составит 10 Кбит.

Для изменения скорости передачи, в нашем примере достаточно изменить CAN_Prescaler, например при значении равном "1", скорость передачи составит 500 Кбит, при "2" - 250 Кбит, ну и так далее. В примере кода, предоставленном во вложении к данной статье, расписаны все значения CAN_Prescaler, доступные для данного проекта. В принципе можно добится скорости вплоть до 1 Мбита.

За настройку таймингов при инициализации CAN устройства отвечают четыре параметра:

Таб. 3. Параметры настройки таймингов
Параметр Расшифровка Пояснение
CAN_SJW Размер SJW SJW (reSynchronization Jump Width) определяет максимальное количество квантов времени, на которое может быть увеличено или уменьшено количество квантов времени битовых сегментов. Возможные значения этого показателя от 1-го до 4-х квантов.
CAN_BS1 Длина сегмента фазы 1 BS1 (Bit Segment 1) - определяет местоположение точки захвата (Sample Point). Он включает в себя Prop_Seg и PHASE_SEG1 стандарта CAN. Его продолжительность программируется от 1 до 16 квантов времени.
CAN_BS2 Длина сегмента фазы 2 BS2 (Bit Segment 2) - определяет местоположение точки передачи. Он представляет собой PHASE_SEG2 стандарта CAN. Его продолжительность программируется от 1 до 8 квантов времени.
CAN_Prescaler Множитель Множитель, из значения которого рассчитывается размер кванта времени. Рассчитывается исходя от частоты работы периферии микроконтроллера.
Важно не путать с частотой работы самого контроллера!

 

Для тестирования достаточно будет использовать параметры настройки таймингов, предложеные в примере. Более подробно о том что такое тайминги, а также как их правильно выставить описано в отдельной статье (STM32. Настройка таймингов работы CAN).

 

Настройка фильтрации пакетов CAN

Все входящие сообщения проходят через фильтр. Фильтр можно настроить на отслеживание как какого-то конкретного сообщения так и группы - настройка маски. Благодаря этому уменьшается нагрузка на процессор, так как ему не приходится самому отфильтровывать ненужные сообщения — это делается на аппаратном уровне.

Листинг 3. Настройка фильтрации пакетов (без ограничений)
	// CAN filter init
	CAN_FilterInitTypeDef CAN_FilterInitStructure;
	CAN_FilterInitStructure.CAN_FilterNumber = 1;
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
	CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
	CAN_FilterInit(&CAN_FilterInitStructure);

В рамках данной публикации я не буду подробно описывать настройку работы с фильтрами, это материал для отдельной статьи (см. Почтовые ящики. Фильтры пакетов CAN). Скажу лишь, что для начала достаточно установить фильтры без ограничений, а впоследствии можно самостоятельно их настроить  под свои требования. 

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

 

Настройка прерываний на прием сообщений CAN

С прерываниями дела обстоят ни чуть не сложнее, чем со структурой инициализации. Для начала надо настроить и проинициализировать контроллер прерываний (NVIC — Nested vectored interrupt controller). В архитектуре STM32 каждому прерыванию можно выставить свой приоритет для случаев, когда возникает несколько прерываний одновременно. Поэтому NVIC представляет нам несколько вариантов формирования приоритетных групп. Я не буду вдаваться в подробности, в нашем случае это не актуально, так как у нас на данный момент настроено лишь одно прерывание на обработку пакетов CAN/

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

Листинг 4. Настройка обработчика прерываний
	// CAN FIFO0 message pending interrupt enable
	CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);

	// NVIC Configuration
	// Enable CAN1 RX0 interrupt IRQ channel
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

Следует обратить внимание, что прерывание для CAN шины и USB одно и тоже. Если Вы будете использовать оба устройства в своей разработке, то необходимо более тщательно подойти к обработке прерываний от них. Могут возникнуть проблемы вплоть до полного отказа одного из устройств.

В параметре "NVIC_IRQChannel" мы указываем, какое именно прерывание мы инициализируем. Константа "USB_LP_CAN1_RX0_IRQn" обозначает канал, отвечающий за прерывания, связанные с CAN1. Найдя ее определение в файле stm32f10x.h, вы увидите еще множество констант (ADC1_IRQn, TIM1_TRG_COM_TIM17_IRQn и др.), обозначающих прерывания от других периферийных устройств. 

Следующими двумя строками мы указываем приоритет прерываний (максимальные значения этих двух параметров определяются выбранной приоритетной группой). Затем указываем, что прерывание активно.

Последняя строка, собственно, включает использование прерывания. 

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

 

Получение данных из шины

Для получения данных из шины CAN используется прерывание. 

В микроконтроллерах STM32 пакеты из шины обрабатываются на уровне железа. После получения контроллером сообщения, он пропускает его через фильтры и только после этого помещает их в мэйлбокс. И уже только после того, как сообщение помещено в почтовый ящик, происходит вызов прерывания на получение сообщения.

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

Рассмотрим вариант обработки прерывания на основе тестового сообщения сети. Идентификаторы команд указаны в заголовочном файле can.h (во вложении к статье). Но Вы можете использовать и любые свои.

Листинг 5. Обработчик прерываний
void USB_LP_CAN1_RX0_IRQHandler(void)
{
	CanRxMsg RxMessage;

	// Обнулим данные пакета
	RxMessage.DLC =     0x00;
	RxMessage.ExtId =     0x00;
	RxMessage.FMI =     0x00;
	RxMessage.IDE =     0x00;
	RxMessage.RTR =     0x00;
	RxMessage.StdId =     0x00;
	RxMessage.Data [0] = 0x00;
	RxMessage.Data [1] = 0x00;
	RxMessage.Data [2] = 0x00;
	RxMessage.Data [3] = 0x00;
	RxMessage.Data [4] = 0x00;
	RxMessage.Data [5] = 0x00;
	RxMessage.Data [6] = 0x00;
	RxMessage.Data [7] = 0x00;

	if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)                 // Проверим почтовый ящик
	{
		CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);

		// Проверим тип заголовка сообщения
		if (RxMessage.IDE == CAN_Id_Standard) {
			if (RxMessage.StdId == CAN_CMD_Test_Send) {      // Если получили тестовое сообщение
				CAN_Send_Ok();
			}
			if (RxMessage.StdId == CAN_CMD_Test_Ok) {        // Если получили подтверждение получения
			// Вставляем любой свой код, например мигание диодом
			}
		}
	}
}

Для начала мы объявляем переменную и заполняем ее нулями. Если этого не сделать, то сообщение будет заполнено "мусором", что может нам несколько усложнить жизнь в случае появления ошибок.

После этого проверяем статус нашего почтового ящика и, если есть сообщения, обработаем их.

Затем проверим формат кадра: Если стандартный, то проверяем параметр "StdID", но а если расширенный, то данные команды будут в параметре "ExtId".

После получения тестового сообщения, мы в ответ отсылаем подтверждение получения.

Здесь мы не рассматриваем передачу блока данных (но в тестовом сообщении мы их передавали). Для проверки наличия данных в пакете, необходимо проверить параметр сообщения "DLC",  если его значение больше нуля, то мы можем получить ровно столько байт, сколько указано в этом параметре. данные хранятся в массиве "Data[0..7]".

 

Отправка данных в шину

Для отправки тестового пакета в шину напишем отдельную функцию, которая будет вызываться из основного цикла программы несколько раз в секунду.

Листинг 6. Отправка данных в шину
void CAN_Send_Test(void)
{
	CanTxMsg TxMessage;
	TxMessage.StdId = CAN_CMD_Test_Send;             // Команда шины

	TxMessage.ExtId = 0x00;                          // Расширенную команду указывать нет смысла

	TxMessage.IDE = CAN_Id_Standard;                 // Формат кадра
	TxMessage.RTR = CAN_RTR_DATA;                    // Тип сообщения
	TxMessage.DLC = 3;                               // Длина блока данных 3 - передадим три байта

	TxMessage.Data[0] = 0x00;                        // Байт данных №1
	TxMessage.Data[1] = 0x01;                        // Байт данных №2
	TxMessage.Data[2] = 0x02;                        // Байт данных №3

	CAN_Transmit(CAN1, &TxMessage);
}

Здесь все просто: объявляем переменную TxMessage, заполняем данные для отправки и выполняем команду передачи данных в шину.

Теперь немного подробнее о параметрах сообщения:

Таб. 4. Параметры сообщения
Параметр Расшифровка Пояснение
StdId Сообщение стандартного кадра Стандартная команда, которую мы передаем в шину. Длина команды не может превышать 11 бит. 

ExtId Сообщение расширенного кадра Расширенная команда, которую мы передаем в шину. Длина составляет 29 бит.

IDE Формат кадра Здесь мы указываем, кокой формат кадра мы используем: CAN_Id_Standard или CAN_Id_Extended.
Исходя из того, какой формат кадра мы выбрали, нам нужно заполнять команду StdId или ExtId соответственно. 

RTR Тип сообщения Есть два типа сообщений: CAN_RTR_Data и CAN_RTR_Remote. На сегодняшний момент использование CAN_RTR_Remote сведено к минимуму, в некоторых протоколах вообще не используется.
CAN_RTR_Data - передача данных,
CAN_RTR_Remote - передача управляющего сообщения без блока данных, в этом случае параметр DLC всегда равен нулю.

DLC Длина блока данных Здесь мы указываем сколько байт данных мы передадим в сообщении. Можем указать максимум 8 байт.

Data[0..7] Байты данных с 1-го по 7-й Этот блок заполняем байтами, которые хотим передать в пакете. 
Необходимо заполнить столько байт, сколько указали в параметре DLC.

 

Хочется еще раз обратить внимание на параметр формата кадра "IDE".  Если мы выбираем "CAN_Id_Standard", то заполнять должны параметр"StdId", а "ExtId"  - должен быть равен нулю. Соответственно если выбираем "CAN_Id_Extended" , то обнуляем "StdId", а заполняем "ExtId" .

Стандартный формат кадра имеет длину в 44 бита, а формат расширенного кадра - 64 бита. Но полезная нагрузка одного пакета выше у расширенного, так как на 64 бита он имеет 29 бит полезной информации, а в стандартном на 44 бита всего 11 бит полезной нагрузки.

Стандарт CAN позволяет одновременно гонять по линии как расширенные, так и стандартные пакеты. Если нам нужно просто отправлять общую команду в шину, например команду установки режима охраны, то выгоднее пользоваться стандартными пакетами, но если же мы гоняем данные, то правильнее будет использовать расширенный пакет, причем в заголовок сообщения можно поместить например 11 бит команды и 18 бит данных.

 

Поиск ошибок

В режиме Silent_LoopBack работоспособность легко проверить с помощью отладчика, установив брикпоинты на функции отправки данных и в процедуре обработки прерывания на получение пакета.

В обычном режиме - желательно иметь под рукой осциллограф, так как достаточно сложно без него определить откуда вылезла ошибка - железо или код. Также стоит обратить внимание на устройство, на котором выполняется тестирование: Если это STM Discovery, то есть вероятность того, что на пинах CAN висит дополнительная обвязка отладочной платы, которая может искажать обмен данных с трансивером. Лучше всего использовать отладочные платы с минимальным "обвесом".

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

Для своих сетей Вы можете настраивать тайминги на свой вкус и требования "скоростного режима", главное не отходить от стандарта и следить, чтобы на всех устройствах тайминги были одинаковыми.

 

Заключение

В этой статье я постарался подробно описать как настроить CAN на микроконтроллере STM32F103. Чтобы не очень перегружать информацией, часть материала с более подробным материалом оформил в отдельных статьях.

Во вложениях к статье приведен полный код программы, рассматриваемый в статье. Код протестирован в режимах Silent_LoopBack и Normal - полностью рабочий.

Если у Вас есть какие либо замечания по статье или предложения - прошу в коменты. 

  

Вложения:
ФайлОписаниеРазмер файла:
Скачать этот файл (CAN_protocol_stm32f103_test.zip)CAN_protocol_stm32f103_test.zipТестовая прошивка214 Кб

Комментарии  

#226 Админ 26.08.2020 21:57
Цитирую Алексей:
Поясните начинающему...

Странный e-mail у начинающего 8)
Но так и быть...
Разницы, в каком файле, нет. Это культура программирования.
В данных примерах для удобства восприятия все лежит в main.c (за исключением библиотек)
Цитировать
#227 Алексей 26.08.2020 22:05
авточип это мой домен..)) Использую только для емаил.Тоесть написали в в файл и ошибок компиляции не получаем?))
Цитировать
#228 Админ 26.08.2020 22:08
Цитирую Алексей:
Тоесть написали в в файл и ошибок компиляции не получаем?))

Если зайти через компьютер, то на этой странице будет вложение с примером.
Кроме текста в мэйн ещё нужно подключить библиотеки
Цитировать
#229 Алексей 26.08.2020 22:20
Спасибо.Качнул пример. Буду изучать этот тест.
Цитировать
#230 Виталий 01.10.2020 12:22
Помогите народ. stm32f105rb. При включении тихого режима при посылке одного сообщения в контроллер по кан1 колбек приема входит в ступор. Постоянно переполнение буффера. Что делать, пока не могу понять.
Цитировать
#231 Андрей 01.10.2020 12:54
Цитирую Виталий:
Помогите народ. stm32f105rb. При включении тихого режима при посылке одного сообщения в контроллер по кан1 колбек приема входит в ступор. Постоянно переполнение буффера. Что делать, пока не могу понять.

Эм, а что такое "Тихий режим"? Если его не включать, то проблем с CAN1 не возникает?
Что значит "входит в ступор"? Не может выйти из прерывания?
Что значит "переполнение буфера"? В микроконтроллере есть встроенные mailbox-ы для хранения принятых сообщений до тех пор, пока они не будут прочитаны или скопированы куда-то ещё до тех пор, пока не получат подтверждения о получении сообщения.
Цитировать
#232 Админ 01.10.2020 16:51
Я думаю, у него стоит повтор отправки в случае ошибки, следовательно после первой же неудачной ошибки забивает ящики.
Виталий, включи CAN_NART и должно убрать забивание, но для поиска ошибки данных маловато.
Цитировать
#233 Виталий 02.10.2020 11:26
Спасибо всем за ответы. Картина такая. Нужен мне Silent mode. Включаю его и при первом входящем пакете преривание постоянно появляется, хотя я пакети не шлю ему.
void vCAN_RxFifo0MsgPendingCallback()
{
while ((phcan->Instance->RF0R & CAN_RF0R_FMP0) != 0)
{ // message pending FIFO0
HAL_CAN_GetRxMessage(phcan, CAN_RX_FIFO0, &RxMessage, RxData);
}
FMP0_St = phcan->Instance->RF0R; //= 0 Вроде норм
if(phcan->Instance->RF0R & 3)
{
FMP0_St2 = phcan->Instance->RF0R; //=1 а тут уже не норм за несколько тактов.
phcan->Instance->RF0R |= CAN_RF0R_RFOM0;
}
}
//MSR: RX, SAMP, RXM флаги постоянно виставляються, как будто приходит сообщение, но ничего же не приходит.
Цитировать
#234 Виталий 02.10.2020 11:33
Я уже отключил прерывание и все читаю в основном потоке по флагам, картина та же самая. В Silent mode tx откобчен от внешней шыны, но некоторые данные всетаки передает ядру кан, вот думаю тут проблема.
Цитировать
#235 Виталий 02.10.2020 11:38
Цитирую Андрей:
Цитирую Виталий:
Помогите народ. stm32f105rb. При включении тихого режима при посылке одного сообщения в контроллер по кан1 колбек приема входит в ступор. Постоянно переполнение буффера. Что делать, пока не могу понять.

Эм, а что такое "Тихий режим"? Если его не включать, то проблем с CAN1 не возникает?
Что значит "входит в ступор"? Не может выйти из прерывания?
Что значит "переполнение буфера"? В микроконтроллере есть встроенные mailbox-ы для хранения принятых сообщений до тех пор, пока они не будут прочитаны или скопированы куда-то ещё до тех пор, пока не получат подтверждения о получении сообщения.

В Normal Mode все работает как часики.
Цитировать
#236 Андрей 02.10.2020 15:42
Возможно, правда, надо включить флаг NART, он отключает повторную отправку. Если контроллер считает, что не удалось отправить сообщение, он будет пытаться отправить его снова и снова. Не знаю, как в Silent определяется успешность отправки, но в целом отправка считается успешной, если удалось отправить заголовок, т.е. шина была свободна и первые 11-29 бит ушли без коллизий (см. доминантные/рецессивные биты).
Цитировать
#237 Виталий 02.10.2020 17:54
Цитирую Андрей:
Возможно, правда, надо включить флаг NART, он отключает повторную отправку. Если контроллер считает, что не удалось отправить сообщение, он будет пытаться отправить его снова и снова. Не знаю, как в Silent определяется успешность отправки, но в целом отправка считается успешной, если удалось отправить заголовок, т.е. шина была свободна и первые 11-29 бит ушли без коллизий (см. доминантные/рецессивные биты).

NART Не помог(
Цитировать
#238 Андрей 02.10.2020 18:19
Предлагаю скачать библиотеку Standart Peripheral Library или HAL, там есть примеры работы CAN в режиме LoobBack и найти 10 отличий с вашим проектом. Скомпилируйте пример от ST, убедитесь что он (не)работает.
Также можете кинуть ссылку на ваш проект на GitHub, будет время - посмотрю.

https://my.st.com/content/my_st_com/en/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32-standard-peripheral-libraries.html
Цитировать
#239 Админ 02.10.2020 19:10
Да можно не мучиться и скачать пример к этой статье. Там есть и библиотека и исходный пример.
Как включить silent, думаю объяснять не надо.
Скачивание доступно с десктопной версии сайта
Все примеры проверены и рабочие
Цитировать
#240 Виталий 05.10.2020 12:17
Цитирую Андрей:
Предлагаю скачать библиотеку Standart Peripheral Library или HAL, там есть примеры работы CAN в режиме LoobBack и найти 10 отличий с вашим проектом. Скомпилируйте пример от ST, убедитесь что он (не)работает.
Также можете кинуть ссылку на ваш проект на GitHub, будет время - посмотрю.

https://my.st.com/content/my_st_com/en/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32-standard-peripheral-libraries.html

https://github.com/Vitalii81/CAN.git
Еще не работал я с github.
Цитировать
#241 Виталий 05.10.2020 12:18
Цитирую Виталий:
Цитирую Андрей:
Предлагаю скачать библиотеку Standart Peripheral Library или HAL, там есть примеры работы CAN в режиме LoobBack и найти 10 отличий с вашим проектом. Скомпилируйте пример от ST, убедитесь что он (не)работает.
Также можете кинуть ссылку на ваш проект на GitHub, будет время - посмотрю.

https://my.st.com/content/my_st_com/en/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32-standard-peripheral-libraries.html

https://github.com/Vitalii81/My-Studies.git
Еще не работал я с github.
Цитировать
#242 Виталий 06.10.2020 14:05
Вобщем братцы разобрался. Мой косяк, опыта с каном нету, поетому не понял что на другом конце включена авторетрасляция, она и лупила мне сообщения, ведь в Silent mode бита подтверждения в ответ нету. Спасибо всем за участие)))
Цитировать
#243 Volodymyr 31.03.2021 12:32
Всем привет!
А довести до готового устройсва, за оплату, кто-то берется?
ТЗ примерно такое.
Если на КАНе появился такой-то сигнал, то ответ такой-то через такое-то время. Или усложняем, если на КАНе то-то и другой дискретньій сигнал равен нулю, то отвечаем в КАН то-то и на каком-то пине ставим единицу. Никаких математических обработок не нужно. Или, усложним ТЗ. Запоминаем несколько сигналов с КАНа, и отдаем их в КАН по какому-то условию в КАНе.
Спасибо.
Цитировать
#244 Дмитрий 20.10.2023 16:41
Error[Pe144]: a value of type "void *" cannot be used to initialize an entity of type "void (*)(void)" C:\Projing\CAN_protocol_stm32f103_test\cmsis_boot\startup\startup_stm32f10x_md.с
Подскажите пожалуйста как эту ошибку победить?
Цитировать