Artery AT32F403A. Работа с CAN

от автора

Добрый день, я продолжаю небольшой курс по ознакомлению с микроконтроллером (МК) Artery AT32F403A. В прошлый раз мы изучили:

  1. Знакомство с таймерами и LED

  2. Работа с USB

Сегодня я познакомлю Вас с работой МК с 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); }


ссылка на оригинал статьи https://habr.com/ru/articles/727760/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *