Добрый день, я продолжаю небольшой курс по ознакомлению с микроконтроллером (МК) Artery AT32F403A. В прошлый раз мы изучили:
Сегодня я познакомлю Вас с работой МК с CAN шиной. В автомобиле, да и не только, без неё никуда. Пример мы сделаем на основе прошлого примера работы с USB. То есть соединим пример работы с USB и с CAN. Дополнительно нам понадобится канхакер, и приложение для работы с ним, например CarBUSAnalyzer.


Открываем в Keil наш прошлый проект с USB: AT32F403A_407_Firmware_Library_V2.1.4\project\at_start_f403a\examples\usb_device\virtual_comport\mdk_v5
Параллельно открываем проект AT32F403A_407_Firmware_Library_V2.1.4\project\at_start_f403a\examples\can\communication_mode\mdk_v5. Из него мы будем копировать нужные нам участки кода. Копируем из этого примера следующие функции целиком в пример virtual_comport:
static void can_gpio_config(void)
static void can_configuration(void)
static void can_transmit_data(void)
void CAN1_SE_IRQHandler(void)
Но это не все. Из тела main копируем две строчки в main примера virtual_comport, в конец инициализации, перед while(1)
can_gpio_config(); can_configuration();
Внимание. Функция void USBFS_L_CAN1_RX0_IRQHandler (void). Она есть в обоих примерах. Поэтому нам нужно тело функции скопировать и соединить с нашим первым примером. Должно получится вот так:
void USBFS_L_CAN1_RX0_IRQHandler(void) { can_rx_message_type rx_message_struct; if(can_flag_get(CAN1,CAN_RF0MN_FLAG) != RESET) { can_message_receive(CAN1, CAN_RX_FIFO0, &rx_message_struct); if(rx_message_struct.standard_id == 0x400) at32_led_toggle(LED2); else at32_led_toggle(LED3); } usbd_irq_handler(&usb_core_dev); usb_ready = 1; }
Всё, с копированием закончили. Частоты работы мы с вами выставили на прошлом примере работы с USB. Теперь нам нужно настроить в коде CAN интерфейс. Нам понадобится приложение Artery_CAN_BitRate_Configuration_V1.0.0. Оно есть в архиве первой статьи или на сайте artery. Запускаем, нажимаем кнопку Calculate и всё. Частота у нас 120 МГц.


Копируем текст с правого окна в приложении
/** * @brief set the baudrate of the can peripheral * @param can_x: select the can peripheral. * this parameter can be one of the following values: * CAN1,CAN2. * @param baudrate_div: baudrate division. * @param rsaw_size: resynchronization adjust width. * @param bts1_size: bit time segment 1. * @param bts2_size: bit time segment 2. * @note baudrate calculate method is: * baudrate = fpclk/(baudrate_div *(1 + bts1_size + bts2_size)) */ can_baudrate_type can_baudrate_struct; can_baudrate_default_para_init(&can_baudrate_struct); can_baudrate_struct.baudrate_div = 30; can_baudrate_struct.rsaw_size = CAN_RSAW_1TQ; can_baudrate_struct.bts1_size = CAN_BTS1_6TQ; can_baudrate_struct.bts2_size = CAN_BTS2_1TQ; can_baudrate_set(CANx, &can_baudrate_struct);
Находим в нашем примере функцию can_configuration(void), в ней похожие строки, и заменяем их все. Комментарии можем убрать, вместо CANx пишем CAN1. В функции can_gpio_config(void) проверяём пины, куда подключен CAN. В моём случае ничего не меняем. У меня PB8 и PB9.
Лайфхак. Нажав F12 на CAN_MODE_COMMUNICATE, вы попадете в описание настроек. Нажав на ttc_enable вы так же попадете на описание настроек. Так можно исследовать очень много кода из примеров, везде есть описание. Думаю с переводом у вас проблем не будет.

В код второго таймера (1 секунда) пишем строчку
can_transmit_data();
Остальное всё проверяем, и нажимаем F7. Переходим в режим Debug и.... устройство не опознано. И ничего не работает. Первый раз я сидел очень долго над этим. Сейчас чуть быстрее. Проблема у нас в одной строчке функции void can_transmit_data(void): while(can_transmit_status_get(CAN1, (can_tx_mailbox_num_type)transmit_mailbox) != CAN_TX_STATUS_SUCCESSFUL);
То есть пока отправка пакета не пройдет удачно, ничего не делать. Уберем проверку (как показывает работа моего устройства, это не приводит к каким-то последствиям для его работы). Строчка теперь будет выглядеть так:
can_transmit_status_get(CAN1, (can_tx_mailbox_num_type)transmit_mailbox);
Запускаем снова, ура! Всё запустилось. В терминале каждую секунду идет серийный номер МК, а в CarBUSAnalyzer:

Обратите внимание, ID и полезные байты прописаны в функции void can_transmit_data(void). Давайте сделаем так, что в каншину будет посылаться серийный номер МК. Для этого вводим новую переменную
uint8_t transmit_mailbox;
изменим нашу функцию отправки данных на следующий код, добавим ей универсальности, и для того, что бы из любого места её вызывать с любыми данными для отправки.
void can_transmit_data(can_tx_message_type tx_message_struct) { tx_message_struct.extended_id = 0; tx_message_struct.id_type = CAN_ID_STANDARD; tx_message_struct.frame_type = CAN_TFT_DATA; tx_message_struct.dlc = 8; transmit_mailbox = can_message_transmit(CAN1, &tx_message_struct); can_transmit_status_get(CAN1, (can_tx_mailbox_num_type)transmit_mailbox); }
Изменим немного тело функции второго таймера (1 секунда):
if(tmr_flag_get(TMR2, TMR_OVF_FLAG) != RESET) { cortex_id = *(uint32_t *)0x1FFFF7E8; cortex_id_2 = *(uint32_t *)0x1FFFF7EC; cortex_id_3 = *(uint32_t *)0x1FFFF7F0; memcpy(&ButtonTx_Buffer_usb[2], (uint32_t*)&cortex_id_3, 4); memcpy(&ButtonTx_Buffer_usb[6], (uint32_t*)&cortex_id_2, 4); usb_vcp_send_data(&usb_core_dev, ButtonTx_Buffer_usb, 0x000A); tx_message_struct_2.standard_id = 0x2F2; // - ID memcpy(tx_message_struct_2.data, &ButtonTx_Buffer_usb[2], 8); // копируем серийный номер can_transmit_data(tx_message_struct_2); // отправляем /* add user code... */ at32_led_toggle(LED3); tmr_flag_clear(TMR2, TMR_OVF_FLAG); }

С частотой немного что-то у меня не так, хочется ровно 1000 мс. С ходу разобраться не получилось, но вопрос интересный, буду разбираться.
Итак, в CAN шину отправлять научились. Теперь давайте сделаем отправку в CAN шину из терминала. Как мы помним, данные из терминала перехватываются в функции work_with_mmc(). Туда и заглянем, а заодно выделим управляющую посылку с нулевым байтом AA, и сигнал в каншину с нулевым байтом FF без проверки на CRC.
Добавим немного кода в функцию work_with_mmc()
void work_with_mmc(void) { uint8_t i2; can_tx_message_type tx_message_struct_2; memcpy(receivedUSBData, usb_buffer, data_len); // input data if (data_len == 13) { if (receivedUSBData[0] == 0xAA) { USB_CRC = 0; for (i2 = 0; i2 < 12; i2++) { USB_CRC = USB_CRC + receivedUSBData[i2]; } if (0xFF-USB_CRC == receivedUSBData[12]) { memcpy(&ButtonTx_Buffer_usb[0], (uint8_t*)&receivedUSBData[3], 10); usb_vcp_send_data(&usb_core_dev, ButtonTx_Buffer_usb, 0x000A); } } if (receivedUSBData[0] == 0xFF) { tx_message_struct_2.standard_id = receivedUSBData[2]<<8 | receivedUSBData[1]; // выделяем ID из пакета memcpy(tx_message_struct_2.data, &receivedUSBData[4], 8); // копируем полезные 8 байт can_transmit_data(tx_message_struct_2); // отправляем } } }

Осталось нам посмотреть, как получить данные с CAN шины и отправить их в терминал через USB. Данные с CAN приходят в функцию USBFS_L_CAN1_RX0_IRQHandler(void). Поменяем немного код функции
void USBFS_L_CAN1_RX0_IRQHandler(void) { can_rx_message_type rx_message_struct; uint8_t CDC_Tx_Buffer[10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if(can_flag_get(CAN1,CAN_RF0MN_FLAG) != RESET) { can_message_receive(CAN1, CAN_RX_FIFO0, &rx_message_struct); memcpy(&CDC_Tx_Buffer[0], &rx_message_struct.standard_id, 2); memcpy(&CDC_Tx_Buffer[2], &rx_message_struct.data, 8); usb_vcp_send_data(&usb_core_dev, CDC_Tx_Buffer, 0x000A); } usbd_irq_handler(&usb_core_dev); usb_ready = 1; }
Всё, смотрим на результат

На этом пример работы с CAN закончен.
Небольшая поправка, питания платы с программатора не хватает для работы с CAN. У меня по крайней мере так. Поэтому необходимо было подключить питание по USB.
В последней статье я расскажу про состояние портов ввода/вывода и может что-то ещё интересное.
Полный код примера
#include "at32f403a_407_board.h" #include "at32f403a_407_clock.h" #include "usbd_core.h" #include "cdc_class.h" #include "cdc_desc.h" #include "usbd_int.h" #include <string.h> /** @addtogroup AT32F403A_periph_examples * @{ */ /** @addtogroup 403A_USB_device_vcp_loopback USB_device_vcp_loopback * @{ */ uint8_t transmit_mailbox; uint8_t USB_CRC = 0; uint8_t receivedUSBData[13] = {0}; uint16_t data_len; uint32_t timeout; uint8_t send_zero_packet = 0; uint32_t cortex_id, cortex_id_2, cortex_id_3; uint8_t ButtonTx_Buffer_usb[10] = {0x0A, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; crm_clocks_freq_type crm_clocks_freq_struct = {0}; usbd_core_type usb_core_dev; uint8_t usb_buffer[256]; uint8_t usb_ready = 0; /* usart global struct define */ extern linecoding_type linecoding; void usb_usart_config(linecoding_type linecoding); void usart_gpio_config(void); #define usart_buffer_size 2048 uint8_t usart_rx_buffer[usart_buffer_size]; uint16_t hw_usart_rx_index = 0; uint16_t hw_usart_read_index = 0; uint16_t usart_rx_data_len = 0; uint16_t ov_cnt = 0; void usart_send_data(uint8_t *send_data, uint16_t len); uint16_t usart_receive_data(void); /** * @brief usb 48M clock select * @param clk_s:USB_CLK_HICK, USB_CLK_HEXT * @retval none */ static void can_gpio_config(void) { gpio_init_type gpio_init_struct; crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE); gpio_pin_remap_config(CAN1_GMUX_0010,TRUE); gpio_default_para_init(&gpio_init_struct); /* can tx pin */ gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_mode = GPIO_MODE_MUX; gpio_init_struct.gpio_pins = GPIO_PINS_9; gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init(GPIOB, &gpio_init_struct); /* can rx pin */ gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; gpio_init_struct.gpio_pins = GPIO_PINS_8; gpio_init_struct.gpio_pull = GPIO_PULL_UP; gpio_init(GPIOB, &gpio_init_struct); } /** * @brief can configiguration. * @param none * @retval none */ static void can_configuration(void) { can_base_type can_base_struct; can_baudrate_type can_baudrate_struct; can_filter_init_type can_filter_init_struct; crm_periph_clock_enable(CRM_CAN1_PERIPH_CLOCK, TRUE); /* can base init */ can_default_para_init(&can_base_struct); can_base_struct.mode_selection = CAN_MODE_COMMUNICATE; can_base_struct.ttc_enable = FALSE; can_base_struct.aebo_enable = TRUE; can_base_struct.aed_enable = TRUE; can_base_struct.prsf_enable = FALSE; can_base_struct.mdrsel_selection = CAN_DISCARDING_FIRST_RECEIVED; can_base_struct.mmssr_selection = CAN_SENDING_BY_ID; can_base_init(CAN1, &can_base_struct); can_baudrate_default_para_init(&can_baudrate_struct); can_baudrate_struct.baudrate_div = 30; can_baudrate_struct.rsaw_size = CAN_RSAW_1TQ; can_baudrate_struct.bts1_size = CAN_BTS1_6TQ; can_baudrate_struct.bts2_size = CAN_BTS2_1TQ; can_baudrate_set(CAN1, &can_baudrate_struct); /* can filter init */ can_filter_init_struct.filter_activate_enable = TRUE; can_filter_init_struct.filter_mode = CAN_FILTER_MODE_ID_MASK; can_filter_init_struct.filter_fifo = CAN_FILTER_FIFO0; can_filter_init_struct.filter_number = 0; can_filter_init_struct.filter_bit = CAN_FILTER_32BIT; can_filter_init_struct.filter_id_high = 0; can_filter_init_struct.filter_id_low = 0; can_filter_init_struct.filter_mask_high = 0; can_filter_init_struct.filter_mask_low = 0; can_filter_init(CAN1, &can_filter_init_struct); /* can interrupt config */ nvic_irq_enable(CAN1_SE_IRQn, 0x00, 0x00); nvic_irq_enable(USBFS_L_CAN1_RX0_IRQn, 0x00, 0x00); can_interrupt_enable(CAN1, CAN_RF0MIEN_INT, TRUE); /* error interrupt enable */ can_interrupt_enable(CAN1, CAN_ETRIEN_INT, TRUE); can_interrupt_enable(CAN1, CAN_EOIEN_INT, TRUE); } void can_transmit_data(can_tx_message_type tx_message_struct) { tx_message_struct.extended_id = 0; tx_message_struct.id_type = CAN_ID_STANDARD; tx_message_struct.frame_type = CAN_TFT_DATA; tx_message_struct.dlc = 8; transmit_mailbox = can_message_transmit(CAN1, &tx_message_struct); can_transmit_status_get(CAN1, (can_tx_mailbox_num_type)transmit_mailbox); } void CAN1_SE_IRQHandler(void) { __IO uint32_t err_index = 0; if(can_flag_get(CAN1,CAN_ETR_FLAG) != RESET) { err_index = CAN1->ests & 0x70; can_flag_clear(CAN1, CAN_ETR_FLAG); /* error type is stuff error */ if(err_index == 0x00000010) { /* when stuff error occur: in order to ensure communication normally, user must restart can or send a frame of highest priority message here */ } } } void TMR1_OVF_TMR10_IRQHandler(void) { can_tx_message_type tx_message_struct_2; if(tmr_flag_get(TMR1, TMR_OVF_FLAG) != RESET) { if (usb_ready == 1) { at32_led_on(LED2); } else { at32_led_toggle(LED2); } usb_ready = 0; /* add user code... */ //at32_led_toggle(LED3); tmr_flag_clear(TMR1, TMR_OVF_FLAG); } if(tmr_flag_get(TMR2, TMR_OVF_FLAG) != RESET) { cortex_id = *(uint32_t *)0x1FFFF7E8; cortex_id_2 = *(uint32_t *)0x1FFFF7EC; cortex_id_3 = *(uint32_t *)0x1FFFF7F0; memcpy(&ButtonTx_Buffer_usb[2], (uint32_t*)&cortex_id_3, 4); memcpy(&ButtonTx_Buffer_usb[6], (uint32_t*)&cortex_id_2, 4); usb_vcp_send_data(&usb_core_dev, ButtonTx_Buffer_usb, 0x000A); tx_message_struct_2.standard_id = 0x2F2; memcpy(tx_message_struct_2.data, &ButtonTx_Buffer_usb[2], 8); can_transmit_data(tx_message_struct_2); /* add user code... */ at32_led_toggle(LED3); tmr_flag_clear(TMR2, TMR_OVF_FLAG); } } void init_led(void) { gpio_init_type GPIO_Init; crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE); // - очень важно не пропустить GPIO_Init.gpio_mode = GPIO_MODE_OUTPUT; GPIO_Init.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; GPIO_Init.gpio_pull = GPIO_PULL_NONE; GPIO_Init.gpio_pins = GPIO_PINS_1; GPIO_Init.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init(GPIOC, &GPIO_Init); GPIO_Init.gpio_mode = GPIO_MODE_OUTPUT; GPIO_Init.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; GPIO_Init.gpio_pull = GPIO_PULL_NONE; GPIO_Init.gpio_pins = GPIO_PINS_2; GPIO_Init.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init(GPIOC, &GPIO_Init); } void work_with_mmc(void) { uint8_t i2; can_tx_message_type tx_message_struct_2; memcpy(receivedUSBData, usb_buffer, data_len); // input data if (data_len == 13) { if (receivedUSBData[0] == 0xAA) { USB_CRC = 0; for (i2 = 0; i2 < 12; i2++) { USB_CRC = USB_CRC + receivedUSBData[i2]; } if (0xFF-USB_CRC == receivedUSBData[12]) { memcpy(&ButtonTx_Buffer_usb[0], (uint8_t*)&receivedUSBData[3], 10); usb_vcp_send_data(&usb_core_dev, ButtonTx_Buffer_usb, 0x000A); } } if (receivedUSBData[0] == 0xFF) { tx_message_struct_2.standard_id = receivedUSBData[2]<<8 | receivedUSBData[1]; memcpy(tx_message_struct_2.data, &receivedUSBData[4], 8); can_transmit_data(tx_message_struct_2); } } } /** * @brief main function. * @param none * @retval none */ int main(void) { /* config nvic priority group */ nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); system_clock_config(); /* enable usb clock */ crm_periph_clock_enable(CRM_USB_PERIPH_CLOCK, TRUE); /* enable usb interrupt */ nvic_irq_enable(USBFS_L_CAN1_RX0_IRQn, 0, 0); /* usb core init */ usbd_core_init(&usb_core_dev, USB, &cdc_class_handler, &cdc_desc_handler, 0); /* enable usb pull-up */ usbd_connect(&usb_core_dev); // таймеры /* enable tmr1 tmr2 clock */ crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_TMR2_PERIPH_CLOCK, TRUE); /* tmr1 tmr2 configuration */ /* time base configuration */ /* systemclock/24000/10000 = 1hz */ tmr_base_init(TMR1, 1999, (crm_clocks_freq_struct.ahb_freq / 10000) - 1); tmr_cnt_dir_set(TMR1, TMR_COUNT_UP); tmr_base_init(TMR2, 3999, (crm_clocks_freq_struct.ahb_freq / 10000) - 1); tmr_cnt_dir_set(TMR2, TMR_COUNT_UP); /* overflow interrupt enable */ tmr_interrupt_enable(TMR1, TMR_OVF_INT, TRUE); tmr_interrupt_enable(TMR2, TMR_OVF_INT, TRUE); /* tmr1 overflow interrupt nvic init */ nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); nvic_irq_enable(TMR1_OVF_TMR10_IRQn, 0, 0); /* enable tmr1 tmr2 */ tmr_counter_enable(TMR1, TRUE); tmr_counter_enable(TMR2, TRUE); // LED init_led(); at32_led_off(LED3); at32_led_on(LED2); // can can_gpio_config(); can_configuration(); while(1) { data_len = usb_vcp_get_rxdata(&usb_core_dev, usb_buffer); if(data_len > 0) { work_with_mmc(); } } } /** * @brief this function handles usb interrupt. * @param none * @retval none */ void USBFS_L_CAN1_RX0_IRQHandler(void) { can_rx_message_type rx_message_struct; uint8_t CDC_Tx_Buffer[10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if(can_flag_get(CAN1,CAN_RF0MN_FLAG) != RESET) { can_message_receive(CAN1, CAN_RX_FIFO0, &rx_message_struct); memcpy(&CDC_Tx_Buffer[0], &rx_message_struct.standard_id, 2); memcpy(&CDC_Tx_Buffer[2], &rx_message_struct.data, 8); usb_vcp_send_data(&usb_core_dev, CDC_Tx_Buffer, 0x000A); } usbd_irq_handler(&usb_core_dev); usb_ready = 1; } /** * @brief usb delay millisecond function. * @param ms: number of millisecond delay * @retval none */ void usb_delay_ms(uint32_t ms) { /* user can define self delay function */ delay_ms(ms); } /** * @brief usb delay microsecond function. * @param us: number of microsecond delay * @retval none */ void usb_delay_us(uint32_t us) { delay_us(us); }
