Код инициализации модуля 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. В ассемблере таких команд нет, это макросы. Надеюсь вы сами сможете их создать.