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