Controller Area Network на STM8
Вместе с компилятором Raisonance и средой разработки Ride7 поставляется библиотека периферийных устройств для STM8S. У меня она находится по адресу:
"C:\Program Files\Raisonance\Ride\Examples\STM8\ST_Libraries\STM8A-S_StdPeriph_Lib-v2.0.0\Libraries\STM8S_StdPeriph_Driver"
Так вот там есть файл stm8s_can.c, а в нём всё необходимое для работы с CAN. Напомню, что такое CAN. Это такая последовательная пакетная широковещательная сеть, по-английски назыавется Controller Area Network. В сети передаются маленькие пакеты, максимум 8 байт. У них есть идентификаторы, длиной 11 бит (стандартные) или 29 бит (расширенные). Я рассматриваю только вариант, где используются стандартные идентификаторы.
Кроме того, пакеты бывают двух видов — обычный пакет, и запрос на передачу. Запрос это такой пакет, где выставлен бит RTR (remote transmission request).
Настройка
Чтобы работать с CAN, микроконтроллер должен брать частоту из внешнего кварца. Иначе модуль CAN не работает. Поэтому ставим кварц, настраиваем STM8 на работу от внешнего кварца, как описано здесь. В конец функции Init_CPU() добавляем настройку предделителя CAN:
CLK_CANConfig( CLK_CANDIVIDER_1 );
Выводы процессора CAN_TX и CAN_RX нужно настроить на вывод и ввод.
// CAN_TX GPIO_Init( GPIOG, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST ); // CAN_RX GPIO_Init( GPIOG, GPIO_PIN_1, GPIO_MODE_IN_PU_NO_IT );
Внизу будет ссылка на проект с описываемыми в этой статье исходниками для отладочной платы Reva v3.3. Я использую вот такую дочернюю плату STM8S208RB. В ней, чтобы заработал CAN, нужно организовать внешнюю частоту. Для этого на дочернюю плату можно проводком завести частоту 12 МГц, которая берётся с платы Reva (найдёте на плате пин, который соответствующим образом маркирован). Куда его заводить? На дочерней плате есть шесть «дырок» в левой части, как раз под установку кварца. Проводок можно воткнуть в правую нижнюю дырку, и всё заработает. У меня работает такая схема.
Ещё, на этой плате есть CAN Transiever. Чтобы работал CAN, нужно ещё и его включить. Делается это одной так:
// Configure the CAN transceiver in active mode GPIO_Init( GPIOE, GPIO_PIN_6, GPIO_MODE_OUT_PP_LOW_FAST );
Инициализация
Дальше идёт инициализация модуля CAN. Полный исходный код смотрите в проекте, ссылка на которой расположена в конце статьи. Здесь покажу только основные строчки, на которые надо обратить внимание.
Параметры, влияющие на частоту передачи в шине CAN:
Параметры ниже подобраны мной для частоты передачи 25 КГц, в то время как частота микроконтроллера 12 МГц (а предделитель CAN равен 1, т.е. те же 12 МГц). Если эти параметры изменятся, то и следующие строчки тоже придётся менять, подбирать параметры заново.
Вычисляется это так:
QUANTA = (1 + BitSeg1 + BitSeg2) CAN_FRQ = CPU_FRQ / (Presc * QUANTA)
Допустим, хотим частоту CAN 25 КГц, и частота процессора 12 МГц.
25000 = 12000000 / (Presc * QUANTA)
Надо подобрать такую (Presc * QUANTA), чтобы при делении 12 МГц на это число, получалась частота CAN 25 КГц. При вычислении QUANTA нужно иметь в виду, что BitSeg1 может быть в пределах от 1 до 16, а BitSeg2 в пределах от 1 до 8. QUANTA должна получиться в диапазоне от 8 до 25. В нашем случае (Presc * QUANTA) равно 480.
Допустим, выбрали BitSeg1 равным 7, а BitSeg2 равным 2. Тогда QUANTA = (1 + 7 + 2) = 10. Теперь узнаем значение предделителя. (Presc * QUANTA) должно равняться 480. Значит Presc равен 48.
Получаем такие строчки инициализации:
// 25 КГц CAN_SynJumpWidth = CAN_SynJumpWidth_1TimeQuantum; CAN_BitSeg1 = CAN_BitSeg1_7TimeQuantum; CAN_BitSeg2 = CAN_BitSeg2_2TimeQuantum; CAN_Prescaler = 48;
Фильтры
Теперь о фильтрах. Фильтры нужны для того, чтобы микроконтроллер не тратил процессорное время на обработку всех приходящих по шине сообщений. Ведь их может быть довольно много, а микроконтроллер должен обрабатывать только какие-то конкретные сообщения с известными ему идентификаторами. Остальные надо фильтровать.
Приведу один возможный пример фильтрации: 8-битный режим фильтрации по списку идентификаторов. Фильтрация в этом режиме возможна по битам 3-10 стандартного идентификатора. В следующем коде у идентификатора 0x5FC сдвигом отбрасываются три младших бита. Не смотрите на то, что другие переменные содержат в названии «mask». В этом режиме все они содержат биты идентификаторов, а не маски. Названы они так для того, чтобы можно было просто и незатейливо использовать их же для другого режима, где эти маски есть.
CAN_FilterNumber = 0; CAN_FilterActivation = ENABLE; CAN_FilterMode = CAN_FilterMode_IdList; CAN_FilterScale = CAN_FilterScale_8Bit; CAN_FilterID1 = (0x5FC >> 3); CAN_FilterIDMask1 = 0; CAN_FilterID2 = 0; CAN_FilterIDMask2 = 0; CAN_FilterID3 = 0; CAN_FilterIDMask3 = 0; CAN_FilterID4 = 0; CAN_FilterIDMask4 = 0;
Таким образом, пропускаются только сообщения с идентификатором 0x5FC, и те, которые отличаются от него тремя младшими битами. Все остальные сообщения не пропускаются. Добавлять другие разрешённые идентификаторы следует в остальные CAN_FilterID*.
Отправка сообщений
Отправляются сообщения так: формируется пакет и отправляется, что тут ещё скажешь? Я использую такую функцию:
CAN_TxStatus_TypeDef CAN_Send(u16 uId, bool uRtr, u8 uLen, const void* const pData) { CAN_TxStatus_TypeDef TxStatus; CAN_TransmitMailBox_TypeDef TransmitMailbox; u16 uSendLimit; if (!uRtr) { if (0 == uLen || uLen > 8 || NULL == pData) return CAN_TxStatus_Failed; } TransmitMailbox = CAN_Transmit(uId, CAN_Id_Standard, uRtr ? CAN_RTR_Remote : CAN_RTR_Data, uLen, (u8*)pData); uSendLimit = 0xFFF; // Ограничение, чтобы не зависнуть на передаче do { TxStatus = CAN_TransmitStatus(TransmitMailbox); } while (TxStatus != CAN_TxStatus_Ok && uSendLimit--); if (TxStatus != CAN_TxStatus_Ok) { printf("CAN: передача сообщения не удалась и отменена.\r\n"); CAN_CancelTransmit(TransmitMailbox); } else { printf("CAN: Успешная передача сообщения.\r\n"); } return TxStatus; }
Получение сообщений
Принимаются сообщения в прерывании CAN. Прерывание будет наступать только по приходу сообщений, которые успешно преодолели фильтр. Прерывание выголядит так:
void CAN_RX_IRQHandler(void) interrupt 8 { u8 i; CAN_Receive(); CanMsgIn.id = CAN_GetReceivedId(); CanMsgIn.rtr = CAN_GetReceivedRTR(); CanMsgIn.len = CAN_GetReceivedDLC(); for (i = 0; i < CanMsgIn.len; i++) { CanMsgIn.dat[i] = CAN_GetReceivedData(i); } CAN_PrintMsg(); }
Дальше у меня тут вызывается CAN_PrintMsg(), которая просто выводит пришедшее сообщение на UART.
void CAN_PrintMsg(void) { u8 i; if (!CanMsgIn.rtr) { printf("ИД: %04X\tДлина: %d\tRTR: %d\tДанные:", CanMsgIn.id, (int)CanMsgIn.len, (int)CanMsgIn.rtr ? 1 : 0); for (i = 0; i < CanMsgIn.len; i++) { printf(" %02X", (int)CanMsgIn.dat[i]); } } else { printf("ИД: %04X\tДлина: %d\tRTR: %d\t", CanMsgIn.id, (int)CanMsgIn.len, (int)CanMsgIn.rtr ? 1 : 0); printf("(Запрос сообщения)"); } printf("\r\n"); }
Проект
Скачать пример работы с CAN для STM8S: stm8-can.zip (189 Кб).
Автор: амдф
Дата: 26.10.2012
Избранное
Остальное
Лента atom