Код инициализации модуля CAN
Что бы модуль CAN начал работать, его нужно инициализировать.
Делается это просто, растолкав биты и байты в регистры, CAN тот час оживает.
Если вы знаете, что и куда толкать, CAN будет послушен вам железобетонно.
Теперь давайте научимся это делать.
Пример написан для CAN1 процессора STM32F407.
Он должен работать и на всей линейке STM. Модуль в камнях идентичный. Для настройки CAN2 просто замените слова
CAN1 на CAN2 и обратите внимание, как настраиваются банки фильтров входящих сообщений.
Для инициализации модуля нужно выполнить несколько простых шагов.
- настроить порт I/O
- затактировать CAN1 в RCC
- вывести CAN1 из спячки
- запросить инициализацию установкой бита INRQ
- дождаться установления бита INAK
- разрешить прерывания в модуле CAN
- настроить тайминги приёма/передачи
- выйти из режима инициализации
- дождаться сброса бита INAK
Далее, что бы принимать сообщения с помощью прерываний, необходимо:
- настроить NVIC
- вставить адрес обработчика прерывания в таблицу векторов
- настроить фильтры входящих сообщений
Итак, поехали. Настраиваем ножки порта. Синтаксис GCC AS.
@ Используется PA11-PA12
@ Затактируем порт A
MOV32 R0, ((RCC_BASE+RCC_AHB1ENR) & 0x00FFFFFF) * 0x20 + 0x42000000 + 0 * 4
MOV R1, 1
STR R1, [R0]
@ Установим частоту ножек
MOV32 R0, GPIOA_BASE
LDR R1, [R0, GPIO_OSPEEDR] @ Все ножки на частоту 25 мгц
ORR R1, R1, GPIO_OSPEEDER_OSPEEDR11_1
ORR R1, R1, GPIO_OSPEEDER_OSPEEDR12_1
STR R1, [R0, GPIO_OSPEEDR]
@ Ножки PA11-PA12 будут выполнять альтернативную функцию
LDR R1, [R0, GPIO_MODER]
ORR R1, R1, GPIO_MODER_MODER11_1
ORR R1, R1, GPIO_MODER_MODER12_1
STR R1, [R0, GPIO_MODER]
@ Задать номер альтернативной функции
MOV32 R1, 0x00099000
STR R1, [R0, GPIO_AFRH]
Затактируем CAN1 в регистре RCC.
MOV32 R0, RCC_BASE
LDR R1, [R0, RCC_APB1ENR]
ORR R1, R1, RCC_APB1ENR_CAN1EN
STR R1, [R0, RCC_APB1ENR]
Теперь загрузим регистр R0, он пригодится нам в дальнейшем коде
MOV32 R0, CAN1_BASE
Выводим CAN1 из спячки
LDR R1, [R0, CAN_MCR]
BIC R1, R1, CAN_MCR_SLEEP
STR R1, [R0, CAN_MCR]
Разрешить инициализацию модуля CAN1 установкой бита INRQ
ORR R1, R1, CAN_MCR_INRQ
STR R1, [R0, CAN_MCR]
INRQ_ravno_0:
LDR R1, [R0, CAN_MSR]
TST R1, CAN_MCR_INRQ
BEQ INRQ_ravno_0
Дождаться установления бита INAK
INAK_ravno_0:
LDR R1, [R0, CAN_MSR]
TST R1, CAN_MSR_INAK
BEQ INAK_ravno_0
Разрешить прерывания от модуля
LDR R1, [R0, CAN_IER]
ORR R1, R1, CAN_IER_FMPIE0 @ Событие приёма сообщения
STR R1, [R0, CAN_IER]
Настроить скорость приёма/передачи сообщений. Обратите внимание, на время отладки я установил флаги CAN_BTR_LBKM и CAN_BTR_SILM.
Вы можете использовать этот режим и учиться работать с модулем CAN на голом МК, даже если у вас не подпаян трансивер
и ножки CAN болтаются в воздухе.
@ Циферка 3 в самом начале = 1 мегабит, 12 = 250 килобит и т.д. сами посчитайте
MOV32 R1, 3-1 + CAN_BTR_TS1_7tq + CAN_BTR_TS2_6tq + CAN_BTR_SJW_1tq + CAN_BTR_LBKM + CAN_BTR_SILM
STR R1, [R0, CAN_BTR]
Выйти из режима инициализации
LDR R1, [R0, CAN_MCR]
BIC R1, R1, CAN_MCR_INRQ
STR R1, [R0, CAN_MCR]
@ Дождаться сброса бита INAK
@ В этом месте контроллер ждёт 11 нулевых бит на физической линии CAN (ножка RX=1)
@ Это означает, что CAN и его трансивер ожили и предположительно готовы к работе.
@@@ В режиме отладки эти строки нужно закомментировать!!
@ Потому что никаких линий и никаких единичек у вас не будет!
@ Трансивера нет. И цикл ожидания станет безконечным..
INAK_ravno_1:
LDR R1, [R0, CAN_MSR]
TST R1, CAN_MSR_INAK
BNE INAK_ravno_1
Что бы полноценно работать с CANом, потребуются прерывания. Настроим NVIC.
Разрешим 20 прерывание от CAN и установим ему приоритет, пусть это будет 5.
@ Конфигурация NVIC
.equ Номер_прерывания, 20
.equ Приоритет_прерывания, 5
@ Настроим приоритет
MOV32 R0, NVIC_IPR0 + Номер_прерывания + 7 @ +7 потому что приоритеты отсчитываются от MPU Fault
MOV R1, Приоритет_прерывания << 4 @ Задвинуть приоритет в старшую тетраду
STRB R1, [R0]
@ Разрешить прерывание непосредственно в NVIC
MOV32 R0, NVIC_ISER0 + (Номер_прерывания/32*4) @ целое от деления на 32 с шагом 4
LDR R1, [R0]
ORR R1, 1 << (Номер_прерывания%32) @ остаток от деления на 32
STR R1, [R0]
Не забудьте вставить адрес обработчика прерывания в таблицу векторов прерываний!!
Сделайте это самостоятельно.
Настраиваем фильтры входящих сообщений.
Что бы CAN заработал, должен быть настроен хотя бы один фильтр.
Иначе приёма не будет.
MOV32 R0, CAN1_BASE
@ Сначала разрешить настройку фильтров
LDR R1, [R0, CAN_FMR]
ORR R1, R1, CAN_FMR_FINIT
STR R1, [R0, CAN_FMR]
ADD R1, 28<<8 @ Сдвинуть в сторону банки относящиеся к CAN2
STR R1, [R0, CAN_FMR]
@ Режим маска
LDR R1, [R0, CAN_FM1R]
BIC R1, R1, CAN_FM1R_FBM16 @ Режим 1=лист 0=маска
STR R1, [R0, CAN_FM1R]
@ Ширина фильтра 32 бита
LDR R1, [R0, CAN_FS1R]
ORR R1, R1, CAN_FS1R_FSC16
STR R1, [R0, CAN_FS1R]
@ Значения фильтров
MOV32 R1, 0b00000000000000000000001111111100 @ ID сообщения = старшие 29 бит
STR R1, [R0, CAN_F16R1]
MOV32 R1, 0b00000000000000000000001111111100 @ Маска, 1 = важный бит
STR R1, [R0, CAN_F16R2]
@ Принадлежность фильтра
LDR R1, [R0, CAN_FS1R]
BIC R1, CAN_FFA1R_FFA16 @ 0=CAN1 1=CAN2
STR R1, [R0, CAN_FFA1R]
@ Разрешить фильтр
LDR R1, [R0, CAN_FA1R]
ORR R1, R1, CAN_FA1R_FACT16
STR R1, [R0, CAN_FA1R]
@ Закрыть настройку фильтров
LDR R1, [R0, CAN_FMR]
BIC R1, R1, CAN_FMR_FINIT
STR R1, [R0, CAN_FMR]
Поняли что-нибудь? Ну ничего.. берите референс и изучайте.
Это несложно, только названия регистров немного заумные, а так всё просто.
Регистров много, поэтому в именах используются цифры и какие-то непонятные буквы..
И вот настал этот момент! Мы отправляем реальное сообщение в сеть CAN! О.. да.
MOV32 R0, CAN1_BASE
@ Перед началом проверяем флаг TXRQ
@ На предмет занятости ящика
Ящик_0_занят:
LDR R1, [R0, CAN_TI0R]
TST R1, CAN_TI0R_TXRQ
BNE Ящик_0_занят
@ Длинна пакета в байтах
MOV R1, 8
STR R1, [R0, CAN_TDT0R]
@ Передаваемые данные, младшее и старшее слово по 4 байта каждый
MOV32 R1, 0x00112233
STR R1, [R0, CAN_TDL0R]
MOV32 R1, 0x44556677
STR R1, [R0, CAN_TDH0R]
@ Стартовать передачу
@ ID получателя сообщения = старшие 29 бит
MOV32 R1, 0b00000000000000000000001111111101
STR R1, [R0, CAN_TI0R]
@ Всё, сообщение пошло в сеть
По окончании передачи, флаг TXRQ сбросится аппаратно. Усекли? ;-)
Последнее что нужно сделать, это написать обработчик прерывания для вхоящих сообщений.
Помните, мы разрешили его во всех возможных местах
и даже назначили приоритет 5, или уже забыли?
CAN1_RX0_INT:
MOV32 R0, CAN1_BASE
@ Вытаскиваем и сохраняем в переменной размер принятого сообщения
LDR R1, [R0, CAN_RDT0R]
MOV32 R2, Размер_принятого_сообщения_CAN
STR R1, [R2]
@ Вытаскиваем и сохраняем ID принятого сообщения
LDR R1, [R0, CAN_RI0R]
MOV32 R2, ID_принятого_сообщения_CAN
STR R1, [R2]
@ Вытаскиваем святое - принятые данные!
LDR R1, [R0, CAN_RDH0R]
MOV32 R2, Старшее_слово_буфера_CAN
STR R1, [R2]
LDR R1, [R0, CAN_RDL0R]
MOV32 R2, Младшее_слово_буфера_CAN
STR R1, [R2]
@ Очистить буфер приёма от этого (одного) сообщения
LDR R1, [R0, CAN_RF0R]
ORR R1, CAN_RF0R_RFOM0
STR R1, [R0, CAN_RF0R]
@ Ждём завершения очистки
c1riwait:
LDR R1, [R0, CAN_RF0R]
TST R1, CAN_RF0R_RFOM0
BNE c1riwait
@ Если в буфере остались другие входящие сообщения, прерывание тут же повторится
RET
Вот собственно и всё. Вам ещё понадобится
файл заголовков.
В программе встречаются команды
MOV32 и
RET. В ассемблере таких команд нет, это макросы.
Надеюсь вы сами сможете их создать.