[资源分享] 【杰发科技AC7840x测评】CAN收发测试

[复制链接]
 楼主| lulugl 发表于 2023-11-30 19:04 | 显示全部楼层 |阅读模式
<
#申请原创# #有奖活动#[url=home.php?mod=space&uid=760190]@21小跑堂 [/url]
【前言】
AC7840x做为一款车规级芯片,CAN通信是必不可少的,开发板的主控芯片是AC78406YGLA,拥有4个CAN控制器,可以支持CAN2.0以及CAN FD模式。在用户手册中得知CAN2.0可以支持一次传输8个字节,最高1M,CAN FD可以支持最高64字节,最高8M的通迅速率。本次实验,主要是体检CAN2.0的数据收发。接收到的信息后显示在LCD屏上。
【实验器材】
AC7840x开发板、CAN分析仪。
【开发板CAN总线介绍】
1、在AC7840x开发板入门指南中,详细的描述了CAN总线的原理图。板上已经安装好了TTL转CAN收发器如下图所示:
1764b9eb3122d3dec0e61e6e448599c9
2、开发板的CAN采CAN0与主控芯片相连,连接下PE5、PE4、PE10与收发器连接,原理图如下:
ad6f7856457b7fd34222e6c6559b1161
3、在官方提供的例程中,有CAN2.0与CANFD的samplecode。
ad7748f248c81cfa05379f98dd85d11b
由于手头上没有CANFD的分析仪,只能体难CAN2.0的数据收发。
【代码移植与体验】
1、我们在前面TFT的帖子的基础之上,我们复制sample中CAN的收发例程到工程中,然后引入.c与.h。
2、代码分析
1)首先定义了时钟的速度,由于can是主频2分频后供给can总线使用,所以定义为60M
#define CAN_SOURCE_CLOCK             (60UL)         /* 时钟源选择60M */
2)使用条件编译来定义了几频率下的总线速率的计算:
  1. <p>/* ==========================================  Variables  =========================================== */</p><p>/*  </p><p> *  波特率和采样点计算公式:</p><p> *  tSeg1 = (S_SEG_1 + 2); tSeg2 = (S_SEG_2 + 1).</p><p> *  BandRate = (SRC_CLK / (S_PRESC + 1) / ((S_SEG_1 + 2) + (S_SEG_2 + 1))), SRC_CLK 为CAN模块时钟源</p><p> *  SamplePoint = (tSeg1 / (tSeg1 + tSeg2)).</p><p> */</p><p>#if (CAN_SOURCE_CLOCK == 80UL)  /*时钟源为80M*/</p><p>static const can_time_segment_t s_canBitrate[CAN_BITRATE_NUM] =</p><p>{</p><p>    {0x1C, 0x09, 0x09, 0x01},  /*   1M, 75% */</p><p>    {0x26, 0x09, 0x09, 0x01},  /* 800K, 80% */</p><p>    {0x1C, 0x09, 0x09, 0x03},  /* 500K, 75% */</p><p>    {0x1C, 0x09, 0x09, 0x07},  /* 250K, 75% */</p><p>    {0x1C, 0x09, 0x09, 0x0F},  /* 125K, 75% */</p><p>    {0x1C, 0x09, 0x09, 0x13},  /* 100K, 75% */</p><p>    {0x1C, 0x09, 0x09, 0x27},  /*  50K, 75% */</p><p>};</p><p>#elif (CAN_SOURCE_CLOCK == 60UL) /*时钟源为60M*/</p><p>static const can_time_segment_t s_canBitrate[CAN_BITRATE_NUM] =</p><p>{</p><p>    {0x0D, 0x04, 0x04, 0x02},  /*   1M, 75% */</p><p>    {0x12, 0x04, 0x04, 0x02},  /* 800K, 80% */</p><p>    {0x1C, 0x09, 0x09, 0x02},  /* 500K, 75% */</p><p>    {0x1C, 0x09, 0x09, 0x05},  /* 250K, 75% */</p><p>    {0x1C, 0x09, 0x09, 0x0B},  /* 125K, 75% */</p><p>    {0x1C, 0x09, 0x09, 0x0E},  /* 100K, 75% */</p><p>    {0x1C, 0x09, 0x09, 0x1D},  /*  50K, 75% */</p><p>};</p><p>#elif (CAN_SOURCE_CLOCK == 48UL) /* 时钟源为48M*/</p><p>static const can_time_segment_t s_canBitrate[CAN_BITRATE_NUM] =</p><p>{</p><p>    {0x22, 0x0B, 0x0B, 0x00},  /*   1M, 75% */</p><p>    {0x16, 0x05, 0x05, 0x01},  /* 800K, 80% */</p><p>    {0x22, 0x0B, 0x0B, 0x01},  /* 500K, 75%*/</p><p>    {0x22, 0x0B, 0x0B, 0x03},  /* 250K, 75% */</p><p>    {0x22, 0x0B, 0x0B, 0x07},  /* 125K, 75% */</p><p>    {0x22, 0x0B, 0x0B, 0x09},  /* 100K, 75% */</p><p>    {0x22, 0x0B, 0x0B, 0x13},  /*  50K, 75% */</p><p>};</p><p>#elif (CAN_SOURCE_CLOCK == 24UL) /* 时钟源为24M*/</p><p>static const can_time_segment_t s_canBitrate[CAN_BITRATE_NUM] =</p><p>{</p><p>    {0x10, 0x05, 0x05, 0x00},  /*   1M, 75% */</p><p>    {0x16, 0x05, 0x05, 0x00},  /* 800K, 80% */</p><p>    {0x10, 0x05, 0x05, 0x01},  /* 500K, 75% */</p><p>    {0x10, 0x05, 0x05, 0x03},  /* 250K, 75% */</p><p>    {0x10, 0x05, 0x05, 0x07},  /* 125K, 75% */</p><p>    {0x10, 0x05, 0x05, 0x09},  /* 100K, 75% */</p><p>    {0x10, 0x05, 0x05, 0x13},  /*  50K, 75% */</p><p>};</p><p>#elif (CAN_SOURCE_CLOCK == 8UL) /*时钟源为8M*/</p><p>static const can_time_segment_t s_canBitrate[CAN_BITRATE_NUM] =</p><p>{</p><p>    {0x04, 0x01, 0x01, 0x00},  /*   1M,75% */</p><p>    {0x06, 0x01, 0x01, 0x00},  /* 800K, 80% */</p><p>    {0x0A, 0x03, 0x03, 0x00},  /* 500K,75% */</p><p>    {0x0A, 0x03, 0x03, 0x01},  /* 250K,75% */</p><p>    {0x0A, 0x03, 0x03, 0x03},  /* 125K,75% */</p><p>    {0x0D, 0x04, 0x03, 0x03},  /* 100K,75% */</p><p>    {0x0D, 0x04, 0x03, 0x07},  /*  50K,75% */</p><p>};</p><p>#endif</p>

3)定义了过滤器的几种配置:
  1. <p>static const can_filter_config_t s_canFilterList[16] =  /* 过滤器配置*/</p><p>{</p><p>    {0x00000001, 0x00000000, CAN_MSG_ID_BOTH},         /* 只接收ID 0x0000_0001和0x001的数据 */</p><p>    {0x00000002, 0x00000000, CAN_MSG_ID_STD},          /* 只接收ID 0x002的标准帧数据 */</p><p>    {0x00000013, 0x00000000, CAN_MSG_ID_EXT},          /* 只接收ID 0x0000_0013的扩展帧数据 */</p><p>    {0x00000124, 0x00000000, CAN_MSG_ID_BOTH},         /* 只接收ID 0x0000_0124和0x124的数据 */</p><p>    {0x00000050, 0x1FFFFF0F, CAN_MSG_ID_BOTH},         /* 可接收ID 0xXXXX_XX5X 或 0xX5X的数据,X为任意值 */</p><p>    {0x00000060, 0x1FFFFF0F, CAN_MSG_ID_BOTH},         /* 可接收ID 0xXXXX_XX6X 或 0xX6X的数据,X为任意值 */</p><p>    {0x00000007, 0x1FFFFFF0, CAN_MSG_ID_BOTH},         /* 可接收ID 0xXXXX_XXX7 或 0xXX7的数据,X为任意值 */</p><p>    {0x00000008, 0x1FFFFFF0, CAN_MSG_ID_BOTH},         /* 可接收ID 0xXXXX_XXX8 或 0xXX8的数据,X为任意值 */</p><p>    {0x00000009, 0x1FFFFFF0, CAN_MSG_ID_STD},          /* 可接收ID 0xXX9的标准帧数据,X为任意值 */</p><p>    {0x0000000a, 0x1FFFFFF0, CAN_MSG_ID_EXT},          /* 可接收ID 0xXXXX_XXXa的扩展帧数据,X为任意值 */</p><p>    {0x00000700, 0x1FFFF0FF, CAN_MSG_ID_BOTH},         /* 可接收ID 0xXXXX_X7XX 或 0x7XX的数据,X为任意值 */</p><p>    {0x0000c000, 0x1FFF0FFF, CAN_MSG_ID_EXT},          /* 可接收ID 0xXXXX_CXXX 的扩展帧数据,X为任意值 */</p><p>    {0x000d0000, 0x1FF0FFFF, CAN_MSG_ID_EXT},          /* 可接收ID 0xXXXX_DXXX 的扩展帧数据,X为任意值 */</p><p>    {0x00e00000, 0x1F0FFFFF, CAN_MSG_ID_EXT},          /* 可接收ID 0xXXEX_XXXX 的扩展帧数据,X为任意值 */</p><p>    {0x0f000000, 0x10FFFFFF, CAN_MSG_ID_EXT},          /* 可接收ID 0xXXXX_XXXX 的扩展帧数据,X为任意值 */</p><p>    {0x10000000, 0x0FFFFFFF, CAN_MSG_ID_EXT},          /* 可接收ID 0xXXXX_XXXX 的扩展帧数据,X为任意值 */</p><p>};</p>

4)然后根据can模块初始化的gpio复用的配置:
  1. <p>/*!</p><p> * [url=home.php?mod=space&uid=247401]@brief[/url] GPIO 初始化</p><p> *</p><p> * @param[in] instance: CAN 模块通道</p><p> * [url=home.php?mod=space&uid=266161]@return[/url] none</p><p> */</p><p>void CAN_InitGPIO(uint8_t instance)</p><p>{</p><p>    if (0U == instance)</p><p>    {</p><p>        GPIO_DRV_SetMuxModeSel(PORTE, 4U, PORT_MUX_ALT5);           /* CAN0_RX */</p><p>        GPIO_DRV_SetMuxModeSel(PORTE, 5U, PORT_MUX_ALT5);           /* CAN0_TX */</p><p>        GPIO_DRV_SetMuxModeSel(PORTE, 10U, PORT_MUX_ALT5);          /* 配置收发器standby功能*/</p><p>    }</p><p>    else if (1U == instance)</p><p>    {</p><p>        GPIO_DRV_SetMuxModeSel(PORTC, 6U, PORT_MUX_ALT3);           /* CAN1_RX */</p><p>        GPIO_DRV_SetMuxModeSel(PORTC, 7U, PORT_MUX_ALT3);           /* CAN1_TX */</p><p>        GPIO_DRV_SetMuxModeSel(PORTE, 6U, PORT_MUX_ALT5);           /* 配置收发器standby功能*/</p><p>    }</p><p>    else if (2U == instance)</p><p>    {</p><p>        GPIO_DRV_SetMuxModeSel(PORTC, 16U, PORT_MUX_ALT3);          /* CAN2_RX */</p><p>        GPIO_DRV_SetMuxModeSel(PORTC, 17U, PORT_MUX_ALT3);          /* CAN2_TX */</p><p>        GPIO_DRV_SetMuxModeSel(PORTC, 15U, PORT_MUX_ALT5);          /* 配置收发器standby功能 */</p><p>    }</p><p>    else if (3U == instance)</p><p>    {</p><p>        GPIO_DRV_SetMuxModeSel(PORTC, 12U, PORT_MUX_ALT5);          /* CAN3_RX */</p><p>        GPIO_DRV_SetMuxModeSel(PORTC, 13U, PORT_MUX_ALT5);          /* CAN3_TX */</p><p>        GPIO_DRV_SetMuxModeSel(PORTC, 11U, PORT_MUX_ALT5);          /* 配置收发器standby功能 */</p><p>    }</p><p>}</p>

5)定义CAN回调函数:
  1. /*!
  2. * @brief CAN_EventCallback 中断回调函数, 可通过CAN_IRQHandler了解参数具体含义
  3. *
  4. * @param[in] instance: CAN 模块通道
  5. * @param[in] event: 中断事件
  6. * @param[in] koer: 错误类型
  7. * @return 0: none
  8. */

  9. void CAN_EventCallback(uint8_t instance, uint32_t event, uint32_t koer)
  10. {
  11.    
  12.     uint8_t recvData[8] = {0};
  13.                 can_msg_info_t recvMsg = {0};
  14.     if (event & (uint32_t)CAN_EVENT_BUS_ERROR)         /* 发生错误后,打印错误消息,仅用于调试 */
  15.     {
  16.         printf("CAN[%d]e: EVEN: %x  KOER: %x\r\n", instance, event, koer);
  17.     }

  18.     if (event & (uint32_t)CAN_EVENT_RECEIVE_DONE)      /* 接收数据*/
  19.     {
  20.         while (CAN_DRV_GetRbufStatus(instance))
  21.         {
  22.             recvMsg.DATA = recvData;                   /* 为接收数据索引中的数据域分配空间, 如果未给接收数据索引的数据域分配空间,直操作会触发Haltfault */
  23.             if (!CAN_DRV_Receive(instance, &recvMsg))
  24.             {
  25.                 CAN_HandleMsg(instance, &recvMsg);
  26.             }
  27.         }
  28.     }
  29. }

6)定义CAN回调处理事件处理函数:
  1. /*!
  2. * @brief CAN_HandleMsg 接收数据处理
  3. *
  4. * @param[in] instance: CAN 模块通道
  5. * @param[in] msgInfo: 接收数据
  6. * @return none
  7. */
  8. can_msg_info_send_t can_recv_info[64];
  9. static void CAN_HandleMsg(uint8_t instance, const can_msg_info_t *msgInfo)
  10. {
  11.     uint8_t i = 0;
  12.         uint8_t count = 0;
  13.     ;             /* 节点信息更新 */
  14.         s_canCurNode[instance].recvCount++;
  15.         count = s_canCurNode[instance].recvCount;
  16. #if CAN_MSG_HANDLE_DIRECT                           /* 处理数据*/
  17.                 can_recv_info[0].DLC = msgInfo->DLC;
  18.                 can_recv_info[0].ID = msgInfo->ID;
  19.                 can_recv_info[0].IDE = msgInfo->IDE;
  20.     if (msgInfo->IDE)
  21.     {
  22.         printf("CAN[%d] RecvCount:%06d ID:%08x DLC:%d", instance, s_canCurNode[instance].recvCount, msgInfo->ID, msgInfo->DLC);
  23.     }
  24.     else
  25.     {
  26.         printf("CAN[%d] RecvCount:%06d  ID:%03x DLC:%d", instance, s_canCurNode[instance].recvCount, msgInfo->ID, msgInfo->DLC);
  27.     }
  28.     if ((0 == msgInfo->RTR) && msgInfo->DLC)
  29.     {
  30.         printf("\r\tData: ");
  31.         for (i = 0; (i < msgInfo->DLC) && (i < 8); i++)
  32.         {
  33.             printf(" %02x", msgInfo->DATA[i]);
  34.                                                 can_recv_info[0].DATA[i] = msgInfo->DATA[i];
  35.         }               
  36.                                
  37.     }
  38.                
  39.     printf("\r\n");
  40. #endif
  41. }

7)定义初始化函数
  1. /*!
  2. * @brief  CAN_Test, 用于测试发送接收数据
  3. *
  4. * @param[in] none
  5. * @return none
  6. */
  7. void CAN_Test(void)
  8. {
  9.     uint8_t instance = 0U;
  10.     can_user_config_t canCfg = {0};
  11.     can_bitrate_t bitrateIndex = CAN_BITRATE_500K;                 /* 低速波特率设置 */
  12.     can_transmit_buff_t type = CAN_TRANSMIT_SECONDARY;             /* 发送缓存设置 */

  13.     canCfg.filterNum = 0U;                                         /* 过滤器数量 */
  14.     canCfg.canMode = CAN_NORMAL_MODE;                              /* 正常模式 */
  15.     canCfg.interruptEn = true;                                     /* 使能中断*/
  16.     canCfg.tsMode = CAN_TSMODE_FIFO;                               /* 选择FIFO模式*/
  17.     canCfg.tsAmount = CAN_TRANSMIT_SEC_ALL;                        /* TSALL*/
  18.     canCfg.tpss = false;                                           /* 失能PTB单次发送 */
  19.     canCfg.tsss = false;                                           /* 失能STB单次发送 */
  20.     canCfg.rom = CAN_ROM_OVER_WRITE;                               /* 缓冲区溢出后,新数据会覆盖旧数据*/
  21.     canCfg.errorWarningLimit = 0x0BU;                              /* 错误警告限制,当 TECNT|RECTN 超过这个限制,会触发EIF错误中断 */
  22. #if CAN_TIME_STAMP_ENABLE
  23.     canCfg.timeStampEn = true;                                     /* 时间戳使能 */
  24.     canCfg.timeStampClkSrc = CAN_TIME_STAMP_CLK_SRC_EXT;           /* 时间戳时钟源选择 */
  25.     canCfg.timeStampClkDiv = CAN_TIME_STAMP_CLK_DIV_8;             /* 时间戳时钟源分频 */
  26.     canCfg.timeStampPos = CAN_TIME_STAMP_SOF;                      /* 时间区采样位置选择 */
  27. #endif
  28.     canCfg.dmaRecvEn = false;                                      /* 不使用DMA */
  29.     canCfg.memEccEn = false;                                       /* 失能ECC */
  30.     canCfg.wakeupIntEn = false;                                    /* 失能唤醒功能 */
  31.     canCfg.busOffRecDisable = false;                               /* 失能自动总线关闭恢复, busoff产生后, 会遵循ISO 11898-规范 */
  32.     canCfg.interruptMask = CAN_IRQ_ALL_ENABLE_MSK;                 /* 使能所有中断*/
  33.     canCfg.bitrate = CAN_GetNormalBitrate(bitrateIndex);           /* 低速波特率配置*/
  34.     canCfg.filterList = (can_filter_config_t *)s_canFilterList;    /* 过滤器配置*/
  35.     canCfg.callback = (can_callback_t)CAN_EventCallback;

  36.     CAN_InitGPIO(instance);                                        /* CAN模块IO口初始化 */
  37.     GPIO_LedInit();                                                /* LED IO口初始化 */
  38.     (void)CAN_DRV_Init(instance, &canCfg);                         /* CAN模块初始化 */

  39. //    while (1)
  40. //    {
  41. //        CAN_SendTest(instance, type);                              /* 发送数据测试 */
  42. //        OSIF_TimeDelay(500);
  43. //        LED3_TOGGLE;                                               /* LED3 翻转 */
  44. //    }
  45. }

8)定义发送测试函数:
  1. /*!
  2. * @brief CAN_SendTest 发送数据测试
  3. *
  4. * @param[in] instance: CAN 模块通道
  5. * @param[in] type: CAN 发送缓冲区选择
  6. *              - CAN_TRANSMIT_PRIMARY
  7. *              - CAN_TRANSMIT_SECONDARY
  8. * @return 0: 发送成功   1: 发送失败
  9. */
  10. int32_t CAN_SendTest(uint8_t instance, can_transmit_buff_t type)
  11. {
  12.     int32_t ret = 1;
  13.     static can_msg_info_t s_sendMsg = {0};
  14.     static uint8_t sendData[8] = {0};

  15.     if (!CAN_DRV_IsTransmitBusy(instance, type))
  16.     {
  17.         s_sendMsg.ID = 0x220;                        /* CAN ID*/
  18.         s_sendMsg.IDE = 0;                           /* 标准帧*/
  19.         s_sendMsg.RTR = 0;                           /* 非远程帧*/
  20.         s_sendMsg.DLC = 8;                           /* 数据长度*/
  21.         s_sendMsg.DATA = sendData;                   /* 分配空间*/
  22.         s_sendMsg.DATA[0] = 0x11;                    /* 填充数据*/
  23.         s_sendMsg.DATA[1] = 0x22;
  24.         s_sendMsg.DATA[2] = 0x33;
  25.         s_sendMsg.DATA[3] = 0x44;
  26.         s_sendMsg.DATA[4] = 0x55;
  27.         s_sendMsg.DATA[5] = 0x66;
  28.         s_sendMsg.DATA[6] = 0x77;
  29.         s_sendMsg.DATA[7] = 0x88;
  30.         if (STATUS_SUCCESS == CAN_DRV_Send(instance, &s_sendMsg, type))
  31.         {
  32.             s_canCurNode[instance].sendCount++;      /* 节点信息更新 */
  33.             ret = 0;
  34.         }
  35.     }

  36.     return ret;
  37. }

【实验效果】
用逻辑分析仪与can接口连接了后,可以检索到500K的CAN总线,发送数据后,可以在串口看到接收到的数,并接收到了回发的数据。
7641524f0f6493d17e3467f2b3852294
同时在TFT屏上也可以同时显示接收到的数据:
6df63fa8e78ebf958f0b14cf2dc4788e
【总结】
AC7840x是一款车规线的MCU,板载了CAN收发器,可以非常方便的实现CAN通讯。同时支持CAN2.0 FDCAN协议,可以灵活的配置为不同模式的通讯方式,实现高速的CAN通信。
附件:工程源码
SPI__tft_CAN.zip (5.84 MB, 下载次数: 20)

tpgf 发表于 2023-12-13 11:24 | 显示全部楼层
楼主是使用了两个不同的can通道进行的自发自收是吗
磨砂 发表于 2023-12-13 11:49 | 显示全部楼层
对于车规级芯片来说can总线肯定是必不可少的
 楼主| lulugl 发表于 2023-12-13 13:06 | 显示全部楼层
tpgf 发表于 2023-12-13 11:24
楼主是使用了两个不同的can通道进行的自发自收是吗

是用分析仪做的测试。
观海 发表于 2023-12-13 13:11 | 显示全部楼层
绝对的车规级芯片  光是can就有四路
 楼主| lulugl 发表于 2023-12-13 13:14 | 显示全部楼层
观海 发表于 2023-12-13 13:11
绝对的车规级芯片  光是can就有四路

确实非常强大的开发板。
guanjiaer 发表于 2023-12-13 14:39 | 显示全部楼层
是不是车规级芯片的can比较容易调试啊
 楼主| lulugl 发表于 2023-12-13 20:07 | 显示全部楼层
guanjiaer 发表于 2023-12-13 14:39
是不是车规级芯片的can比较容易调试啊

都是一样的,车规级的稳定性与运行环境要求要高一些。
八层楼 发表于 2023-12-13 21:31 | 显示全部楼层
这两路can是不是没有任何区别呢
晓伍 发表于 2023-12-13 22:06 | 显示全部楼层
楼主的接收端使用的是什么呢
 楼主| lulugl 发表于 2023-12-30 09:06 | 显示全部楼层
晓伍 发表于 2023-12-13 22:06
楼主的接收端使用的是什么呢

can的分析仪。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

180

主题

830

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部

180

主题

830

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部