• Корпоративный Новый год
  • Организация выпускных
  • Студия вокала
  • Организация свадеб
  • Программы для диджеев
  • Организация корпоративов
  • Написания музыки
  • Вокала
  • Дни рождения
  • Новости
  •  

    STM32 → CAN

    Опубликовано: 11.01.2019

    видео STM32 → CAN

    "Біля тополі" - Неможливо слухати без сліз... Українські пісні

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


    Fast review firmware True-D v3.7 RC1

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

    Подробное описание шины CAN я приводил здесь.


    임베디드위저드 테스트프레임워크(Embedded Wizard テストフレームワーク)

    Для связи потребуется CAN-драйвер, он описан мной в прошлой статье. Вкратце повторюсь — используем микросхему PCA82C250, опторазвязку и несколько компонентов для защиты от помех. Используется один драйвер на каждое устройство, то есть таких драйверов нужно минимум два (при наличии двух устройств на шине).

    Hello world, или передадим что-нибудь в пределах процессора

    Инженеры ST предусмотрели для отладочных целей удобный режим «тихий loopback». В этом режиме выход интерфейса не отправляет информацию наружу, а замыкается на вход — типа того, как накидывают перемычку на RX и TX в USART. В остальном работа в этом режиме ничем не отличается от стандарта.

    Режим silent loopback

    На самом деле, этот режим является комбинацией двух других режимов:

    Режим silent

    Режим loopback

    Сначала конфигурируем интерфейс CAN:

    CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; CanTxMsg TxMessage; void CAN1_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /* CAN GPIOs configuration */ /* Enable GPIO clock */ RCC_AHB1PeriphClockCmd(CAN1_GPIO_CLK, ENABLE); /* Connect CAN pins to AF9 */ GPIO_PinAFConfig(CAN1_GPIO_PORT, CAN1_RX_SOURCE, CAN1_AF_PORT); GPIO_PinAFConfig(CAN1_GPIO_PORT, CAN1_TX_SOURCE, CAN1_AF_PORT); /* Configure CAN RX and TX pins */ GPIO_InitStructure.GPIO_Pin = CAN1_RX_PIN | CAN1_TX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(CAN1_GPIO_PORT, &GPIO_InitStructure); /* CAN configuration */ /* Enable CAN clock */ RCC_APB1PeriphClockCmd(CAN1_CLK, ENABLE); /* CAN register init */ CAN_DeInit(CAN1); /* CAN cell init */ CAN_InitStructure.CAN_TTCM = DISABLE; CAN_InitStructure.CAN_ABOM = DISABLE; CAN_InitStructure.CAN_AWUM = DISABLE; CAN_InitStructure.CAN_NART = ENABLE; CAN_InitStructure.CAN_RFLM = DISABLE; CAN_InitStructure.CAN_TXFP = DISABLE; CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; /* CAN Baudrate = 125 kBps (CAN clocked at 42 MHz) */ CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq; CAN_InitStructure.CAN_Prescaler = 12; CAN_Init(CAN1, &CAN_InitStructure); /* CAN filter init */ CAN_FilterInitStructure.CAN_FilterNumber = 0; 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 = 0; CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; CAN_FilterInit(&CAN_FilterInitStructure); /* Transmit Structure preparation */ TxMessage.StdId = 0x321; TxMessage.ExtId = 0x01; TxMessage.RTR = CAN_RTR_DATA; TxMessage.IDE = CAN_ID_STD; TxMessage.DLC = 1; /* Enable FIFO 0 message pending Interrupt */ CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); CAN_ITConfig(CAN1, CAN_IT_FMP1, ENABLE); CAN_ITConfig(CAN1, CAN_IT_TME, ENABLE); }

    Всё как обычно — запускаем тактирование GPIO и CAN (используем CAN1), настраиваем ножки порта GPIO, и переходим к настройке CAN — общие настройки, скорость (точнее, тайминги), настройки приёмника сообщений и параметры адресации. В конце включаем прерывания по приёму.

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

    После отсева сообщения попадают в один из трёх «почтовых ящиков», в зависимости от заполненности каждого из них. Я не наблюдал случаи приёма в два и тем более в три ящика — возможно, это будет проявляться только на больших скоростях приёма и медленной обработке сообщений процессором. После попадания сообщения в ящик вызывается соответствующее прерывание.

    Окей, интерфейс настроен. Передача очень проста:

    CanTxMsg TxMessage; TxMessage.StdId=134222929; TxMessage.ExtId=458227712; TxMessage.Data[0] = 0; TxMessage.Data[1] = 1; TxMessage.Data[2] = 2; TxMessage.Data[3] = 3; TxMessage.Data[4] = 4; TxMessage.Data[5] = 5; TxMessage.Data[6] = 6; TxMessage.Data[7] = 7; CAN_Transmit(CAN1, &TxMessage);

    Задаём адрес сообщения и данные. Передаём функцией из StdPeriph.

    Нужно ещё указать обработчик прерывания приёма сообщения.

    void CAN1_RX0_IRQHandler (void) { if (CAN_GetITStatus(CAN1,CAN_IT_FMP0)) { CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); CanRxMsg msg_buf; CAN_Receive(CAN1, CAN_FIFO0, &msg_buf); uint32_t a, b, data; data=((((((msg_buf.Data[0]*255+msg_buf.Data[1])*255+msg_buf.Data[2])*255+msg_buf.Data[3])*255+msg_buf.Data[4])*255+msg_buf.Data[5])*255+msg_buf.Data[6])*255+msg_buf.Data[7]; a=data/100000; b=data%100000; printf("%d%drn", a, b); } }

    При получении сообщения — сбрасываем бит прерывания, принимаем данные, превращаем принятый массив в число и выводим. Функция printf — только для примера, вы можете выводить данные в USART, USB на индикатор или куда-то ещё.

    Тайминги

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

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

    Необходимо чётко понимать этот процесс, потому что при правильной настройке он даёт отличные результаты захвата частоты передатчика. Рекомендуется принимать следующие значения таймингов:

    SJW = 1 BS1 = 3-5 BS2 = 3-5

    Конечно, конкретные значения зависят от частоты шины и частоты процессора, однако от них можно отталкиваться. Стоит начать с равенства периодов BS1 и BS2, а в случае ошибок приёма — пытаться их изменять.  К сожалению, для чёткой настройки в сложных случаях всё-таки понадобится осциллограф.

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

    Для работы с автомобильной CAN-шиной на скорости 125 кбит/с и частотой процессора 42МГц я использовал прескалер величиной 12, SJW=1, BS1=3, BS2=3.

     






    © 2009 Broadway 21 Records
    Все права сохранены.