Настройка модуля CAN на микроконтроллере STM32F103. Часть 1

Заказал как-то в Китае такую вот отладочную плату. На ней имеется USB/UART конвертер на микросхеме PL2303, две кнопки для экспериментов, кнопка питания, кнопка сброса, два светодиода для экспериментов, светодиод питания, стабилизатор на 3.3В, два разъема мини-USB, разъем JTAG для подключения отладчика, например J-Link. В комплекте также шел USB-кабель, изображенный на фото. Плата питается от USB-порта компьютера.

Отладочная плата STM32F103

Сегодня я попробую написать драйвер для модуля CAN и поделиться своими наработками.

Сразу скажу, что разжевывать информация что такое CAN шина не буду, но приведу пару документов, где можно почитать о CAN шине на русском языке:

1986ВЕ9х, К1986ВЕ9х и MDR32F9Qx - это Российские аналоги микроконтроллеров STM32. Но все же модуль CAN, описанный в документации, немного отличается от модуля CAN для STM32.

Еще одно важное замечание: изначально на плате нет драйвера CAN-шины (микросхемы, согласующей уровни сигналов шины и цифровые уровни микроконтроллера). Я запаял микросхему MCP2551, но есть и другие аналоги, подходящие по ногам.

Итак начнем. Проект я буду создавать в среде CooCox IDE версии 1.7.8. Здесь сразу можно выбрать нужные библиотеки и они автоматически добавятся в проект. Из всего изобилия я выбираю только CMSIS core и CMSIS_boot. Признаться четсно, библиотека от STM32 stm32f10x_stdperiph_lib мне не нравится из-за своей избыточности, хотя я частенько подсматриваю код ее исходников, когда разбираюсь с каким-либо новым модулем. Возможно наш драйвер не будет сильно отличаться от уже готового драйвера библиотеки StdPeriph, но я считают, что материал лучше запоминается, когда все пробуешь делать сам с нуля.

Выбор библиотек в CooCox IDE

CMSIS core содержит описание всех регистров микроконтроллера, а также обработчики прерываний по умолчанию и т.д. В CMSIS_boot содержатся все необходимы функции для инициализации контроллера, те, что выполняются до запуска функции main. Здесь происходит настройка системы тактирования. Очень приятная вещь, контроллер  уже сразу настраивается на тактирование от внешнего генератора 8МГц и частота шины SYSCLK выбирается максимальная 72 МГц. При желании можно покопаться в файле system_stm32f10x.c и настроить другую частоту, а также выбрать другой источник тактирования.

Вообще можно написать и свою функцию настройки системы тактирования RCC. Обычно я пользуюсь для этого двумя программами-калькуляторами. Первая - это STM32generator. В ней можно настраивать еще и DMA, USART, порты ввода-вывода, а также она может генерировать сразу готовый код программы.

Вторая - программа с оффициального сайт ST Electronics STM32CubeMX. Это визуальный конструктор программы для STM32 микроконтроллера, она позволяет выбрать необходимые модули, настроить их конфигурации и генерирует готовые проекты в самых популярных IDE на основе конечно же StdPeriph_lib. Она нам еще понадобится для расчета скорости CAN. А пока пример настройки системы RCC. Что в ней очень удобно, так это то, что можно сразу же вбить в поле HCLK нужную частоту и программа автоматически просчитает все остальные настройки RCC. 

Настройка RCC в программе STM32CubeMX

В нашем проекте мы не будем перенастраивать систему тактирования и остановимся на частоте SYSCLK = 72 МГц.

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

Как обычно начинаем с инициализации модуля CAN1. Здесь нужно выполнить следующие действия:

  • Разрешить тактирование модуля CAN1;
  • Войти в режим инициализации (после сброса модуль находится в спящем режиме);
  • Настроить скорость передачи данных в регистре CAN_BTR;
  • Выбрать режим работы модуля один из трех (обычный, только приема, режим петли);
  • По необходимости включить/выключить дополнительные функции, такие как автоматическое пробуждение при появлении на шине данных, блокировка FIFO при переполнении и другие.
  • Выйти из режима инициализации.

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

Самым сложным пунктом здесь является настройка скорости передачи. Но на самом деле сложного ничего нет, нужно руководствоваться несколькими правилами. Как известно, максимальная скорость передачи CAN-шины может достигать 1 Мбит/с. Т.е. время минимального битового интервала определяется по формуле:

TBIT = 1/Скорость передачи

Выходит, что для скорость передачи данных 1 Мбит/с битовый интервал долже быть равен 1 мкс (или 1000 нс).

Битовый интервал рабивается еще на более мелкие интервалы, называемые квантами TQ (Time Quanta). Обязательным условием правильной настройки бит тайминга (скорости передачи) является равенство TBIT = 8...25TQ. Из мануала следует, что битовый интервал состоит из трех частей:

  • SYNC_SEG;
  • BIT SEGMENT 1;
  • BIT SEGMENT 2;

Настройка таймингов CAN1 модуля STM32

Что это такое можете почитать подробно в мануале на контроллер. Из илюстрации видно, что SYNC_SEG всегда равен 1TQ, а сумма (BS1 + BS2) = 7...24TQ. При этом BS1 = 1..16TQ, BS2 = 1...8TQ.

BRP - это делитель входной частоты. С помощью него мы получаем нужный битовый интервал. 

Как известно из даташита модуль CAN1 тактируется от шины PCLK1, частота которой не может быть выше 36 МГц. По умолчанию в CMSIS boot именно эта частота и настраивается. От нее мы и будет вести все расчеты, в которых нам снова поможет STM32CubeMX. 

Выбираем Pinout->Periphrerals->CAN->Mater Mode.

Переходим на вкладку Configuration и нажимам кнопку CAN

Настройка бит тайминга CAN шины в STM32CubeMX

Допустим мы хотим получить скорость передачи данных 1 Мбит/с. Тогда нужно настроить битовый интервал длительностью 1000 нс. Устанавливаем делитель шины тактирования PCLK1 = 36МГц/4, т.е. BRP = 4, тогда время одного кванта получится около 111 нс. Отсюда получаем, что битовый интервал равен 9TQ. SYNC_SEG 1TQ. Остается (BS1+BS2) = 8TQ. Устанавливаем их значение по 4TQ. В своем проект я создал таблицу настроек для скоростей передачи 1 Мбит/с, 500 кбит/с, 250 кбит/с, 125 кбит/с и 62,5 кбит/с. Вот какие временный настройки я получил:

//******************************************************************************
// Структура настройки бит тайминга
//******************************************************************************
typedef struct {

	u32 BRP		:10;
	u32			:6;
	u32 TS1		:4;
	u32 TS2		:3;
	u32			:1;
	u32 SJW		:2;
	u32			:6;

} t_can_timing;

//******************************************************************************
// Настройки для разных скоростей передачи (для частоты APB1 = 36 МГц)
//******************************************************************************
const static t_can_timing baud_rate_settings[] = {

	[CAN_BUS_BR1M] = {.BRP = 3, .TS1 = 3, .TS2 = 3, .SJW = 0},
	[CAN_BUS_BR500K] = {.BRP = 7, .TS1 = 3, .TS2 = 3, .SJW = 0},
	[CAN_BUS_BR250K] = {.BRP = 15, .TS1 = 3, .TS2 = 3, .SJW = 0},
	[CAN_BUS_BR125K] = {.BRP = 31, .TS1 = 3, .TS2 = 3, .SJW = 0},
	[CAN_BUS_BR62500] = {.BRP = 63, .TS1 = 3, .TS2 = 3, .SJW = 0},
};

Структура t_can_timing не случайно имеет такой формат. Он совпадает с форматом регистра CAN_BTR за исключением полей выбора режима работы. Необходимо также заметить, что от всех коэффициентов, полученных в STM32CubeMX отнимается единица.

Если фронт принимаемого сигнала отклоняется от Sync_Seg, длительность BS1 может быть увеличена, а BS2 уменьшена, чтобы в следующий раз фронт прошел в нужном месте. Величина изменения BS1 и BS2 варьируется в зависимости от значения отклонения фронта, но не превышает значения Synchronization Jump Width (SJW).
 
И наконец привожу код функции инициализации модуля CAN1:
//******************************************************************************
// Инициализация модуля CAN
//******************************************************************************
t_can_opstatus can_init(t_can_bus_speed baudrate, t_can_mode mode){

	u32 tmo;

	//Тактирование CAN
	RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;

	//Запрос режима инициализации
	CAN1->MCR |= CAN_MCR_INRQ;

	//Ожидание установки режима инициализации
	tmo = CAN_INIT_MODE_TMO;
	while(tmo-- && (CAN1->MSR & CAN_MSR_INAK) != CAN_MSR_INAK);
	//Проверка статуса
	if((CAN1->MSR & CAN_MSR_INAK) != CAN_MSR_INAK){
		return CAN_OPSTATUS_TMO;
	}

	//Дополнительные настройки
	CAN1->MCR &= ~CAN_MCR_TTCM;		//Time triggered communication mode ВЫКЛ
	CAN1->MCR &= ~CAN_MCR_ABOM;		//ВЫКЛ автоматический выход из bus-off
	CAN1->MCR &= ~CAN_MCR_AWUM;		//Авто. выход из спящего режима ВЫКЛ
	CAN1->MCR &= ~CAN_MCR_NART;		//Модуль будет отправлять сообщение, пока не отправится
	CAN1->MCR &= ~CAN_MCR_RFLM;		//Фифо не лочится, если оно заполнено и принято лишнее сообщение
	CAN1->MCR &= ~CAN_MCR_TXFP;		//Приоритет передачи сообщений определяется идентификаторами

	//Настройка скорости CAN-шины
	CAN1->BTR = *(u32*)&baud_rate_settings[baudrate];
	//Режим работы модуля
	CAN1->BTR &= ~(CAN_BTR_SILM | CAN_BTR_LBKM);
	CAN1->BTR |= (mode & 0x3) << 30;

	//Выход из режима инициализации
	CAN1->MCR &= ~CAN_MCR_INRQ;

	tmo = CAN_INIT_MODE_TMO;
	while(tmo-- && (CAN1->MSR & CAN_MSR_INAK) == CAN_MSR_INAK);
	//Проверка статуса
	if((CAN1->MSR & CAN_MSR_INAK) == CAN_MSR_INAK){
		return CAN_OPSTATUS_TMO;
	}

	return CAN_OPSTATUS_OK;
}

Переход в режим инициализации происходит установкой бита INRQ в регистре CAN_MCR, при этом сбрасывается бит спящего режима SLEEP. Проверка перехода в режим инициализации осуществляется в регистра CAN_MSR, установкой бита  INAK. Выход из режима инициализации осуществляется сбросом бита INRQ .

 

Продолжение следует...