打印
[活动]

【极海G32A1465汽车通用MCU评估板测评】“以为是把工兵铲,实际是柄倚天剑”

[复制链接]
693|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lihuasoft|  楼主 | 2024-12-15 14:18 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 lihuasoft 于 2024-12-15 14:53 编辑

  11月下旬,在21ic论坛活动专区,无意中看到了Geehy在做“G32A1465汽车通用MCU评估板测评”活动。一念之间,一扇门就打开了,一片崭新的天地就在面前。因G32A1XXX的部分资料需要在官网申请下载,使我有机会大致浏览了Geehy官网,惊叹极海公司网站的清晰、简洁,每一张图片、每一个文件、每一个链接,都为了实用,根本不存在为了美工效果而拼凑的虚头巴脑的内容。产品门类不多,但已足够丰富,APM32微控制器已经形成几大系列,而电机控制专用MCU、汽车通用MCU产品对我最具吸引力。
  拿到G32A1465的数据手册、用户手册、SDK,离公布测评名单还有一些日子,我只是习惯性地跳到文档的CANFD外设等章节,简略了解。一是因为还不清楚自己能否入选,不想浪费了时间;二是固执地认为:“汽车通用MCU”,那不就是“车规级MCU”吗?重点看看CAN通信、LIN通信,别的也没什么新意了吧?就这样直到12月初,确定我已进入名单,于是用了一个晚上从头研读G32A1465的用户手册,心里惊呼:

  ——纳尼?!原来“汽车通用MCU”是超越“车规级MCU”一个维度的存在!特别是与ISO 26262安全规范有关的内容,一边读,一边让我明明白白地认识到了自己的无知浅薄。
  ——原以为这款MCU只是一把普普通通的“工兵铲”,没成想它竟是一柄内藏真经的“倚天剑”!

  那时我已经后悔在申请板子时说自己“曾经做过轮式工程机械的研发”了,觉得非常不好意思——我用的MCU,在车辆应用方面,根本无法与G32A1465这种专业的汽车MCU相提并论。这还在其次(毕竟我搞的是非道路车辆),主要问题在于:我设计软硬件时,缺少安全意识,就连做个伪随机数,ECU之间互相验证一下,都没有想过。如此说来,我设计的ECU那基本上就是光着屁股上街......至于非法嵌入汽车总线系统内部的鬼,就更不敢想了。唉呀呀呀,不能说了,请原谅我们只是小小的个体户,而且前东家是个好老板。

  所以,“测评”二字,真的不敢当,我觉得,就算一次学习经历吧,感谢Geehy+21ic论坛给的这次机会,使我这样一个小学生,有幸进入这片对我来说崭新的天地。期待以此为契机,持续了解极海的MCU,未来有能力把G32A1XXX、APM32M3514写入自己的简历,用Geehy的MCU去赚钱。

  废话已经写了不少,接下来打算写一写这几个题目:
    ●  研读G32A1465手册、SDK的一些收获
    ●  点灯
    ●  CAN通信测试
    ●  CAN FD通信测试
    ●  结合某两种功能做个接近真实场景的实验
    ●  ......
  下面,先呈上第一部分,后续的内容会利用下班时间,在占楼部分陆续完成。

  (一)研究G32A1465手册、SDK的一些收获

  1、G32A1465的用户手册写得真好,实用、易读,干货满满。篇幅最大的几个章节,应该是“安全相关、CFGTMR定时器、CAN”这几章,这也是这个MCU最重要的武器库。以CAN这一章为例,有近一百页,里面详细介绍了CAN外设在芯片内部的工作逻辑,比STM32的手册详细多了。SDK每个例程都有一个readme,文档写得相当规范,让人信服,虽然是英文表达,但丝毫不影响阅读理解。

  2、我所见过的一些名为“某某32”的Cortex-M内核的MCU,内核自不必说,外设其实与STM32大同小异,因为寄存器逻辑都是差不多的。然而我发现G32A1465还是很不同的,就拿GPIO来说吧,它有一个“端口逻辑电平翻转寄存器”PLLOT (port logic level out toggle),端口电平的翻转可以批量完成:

void PINS_HW_GPIO_TogglePins(GPIO_T * const gpioBase, PINS_CHANNEL_TYPE_T pins)

{

    gpioBase->PLLOT.reg = pins;//可以同时翻转多个IO,只需一个赋值,交给硬件就完事了

}
  可以同时翻转多个IO,只需一次寄存器赋值,交给硬件就完事了!用途......车用LED动态氛围灯?令人有无穷想像!

  而STM32是这样:

void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

{

  if ((GPIOx->ODR & GPIO_Pin) != 0x00u) //需要先读出、做判断

  {

    GPIOx->BRR = (uint32_t)GPIO_Pin; //虽然看起来可以一次翻转多个IO,但实际受到极性一致的限制

  }

  else                                 //何况还必须软件做判断,这样效率就低了

  {

    GPIOx->BSRR = (uint32_t)GPIO_Pin;

  }

}
  需要先读出、做判断。虽然看起来可以一次翻转多个IO,但实际受到极性一致的限制。何况还必须软件做判断,这样效率就低了。

  G32A1465在寄存器级基因上做的这种改进,一定有令人兴奋的用途,这就需要嵌入式工程师的智慧了。应该不止这一个寄存器,手册还没看完,我只负责抛砖引玉。

  3、像我这样的已经熟悉了STM32CubeMX自动生成初始化代码的,其实是被驯养,有了工具依赖。这样就会逐渐丢失了直接撸代码的血性。而且STM32的HAL库函数封装嵌套层数太多,代码跟踪学习让人望而生畏。
  G32A1465的SDK库代码,却让人觉得层次清晰(某些特征挺像TI的SimpleLink库,但要比TI库清晰得多),让我这样的水平差一些的初级程序员感觉亲切,有了信心。


使用特权

评论回复
沙发
lihuasoft|  楼主 | 2024-12-15 14:20 | 只看该作者
本帖最后由 lihuasoft 于 2024-12-30 10:14 编辑

注:这层楼的内容,时间上是在“CAN通信测试”“CANFD通信测试”这两层楼之后发出来的。
  (关于0822版SDK的小插曲)

  进入评估群之前,我先从官网申请SDK,有一位地区销售总监联系我(我当时挺感动他们的工作效率的),给我一个压缩包名为“G32A1xxx_SDK_V1.1_0822.zip”:



  收到开发板、进群后,编译下载这个0822版SDK的Examples\G32A1465\PINS\PINS_Isr例程,并没有预期的DEMO效果。
  用评估群里提供的SDK,完全正常:



  对比两个版本SDK的例程代码,完全相同,可见问题是出在库里的。
  继续对比,问题出在这两个文件夹:



  然后我就放弃进一步问题定位了,因为两个SDK版本的寄存器名称都有一些改动,比如:





  于是我对那位地区销售总监说:“你发给我的SDK是有问题的,用评估群里的SDK就没问题。你找同事更新一下版本吧,别给自己工作造成被动。”我的想法是:如果这个0822发给了好多客户,那他岂不是惹了好多麻烦。
  然而他说给我的0822版,就是最新版,没有问题。后来我看了两个压缩包里的文件修改日期,的确0822版反而新一些。
  我建议他下载Examples\G32A1465\PINS\PINS_Isr这个例程测试,他说会找技术人员试一下,然而半个月过去了,没有什么结果反馈。如果有人正在使用0822版的SDK,在配置中断服务函数时出现奇怪的情况,可以尝试换一个SDK试试。

  (二)点灯

  把点灯的测试补一下吧:
  写了一个简易的上位机,可以设置某一个颜色,把RGB三色的值,通过CAN总线发给G32A1465;
  G32A1465收到后,分别换算为RGB三个LED的PWM占空比数值,还原为上位机设置的颜色。
  没啥新意,上个效果图充数:












使用特权

评论回复
板凳
lihuasoft|  楼主 | 2024-12-15 14:20 | 只看该作者
本帖最后由 lihuasoft 于 2024-12-22 22:31 编辑

  (三)CAN通信测试

  这层楼主要是做传统CAN的测试,CAN FD的测试会放在下一层楼。
  但是在此有必要先说一说我的CAN通信工具。因我以前的项目只用到经典CAN,从来没有用过CAN FD,于是,得知自己入选名单后,斥“巨资”买了USB-CANFP工具,就是下图中的小小黑盒子(本人不建议你买这个);而那个红色的东西,是我一直在用的USB-CAN(同样不推荐买这个,我了解它的缺点)。
  于是,现在我有2个CAN分析仪了,那就把这总共3个CAN设备连起来:



  G32A1465板子上已经焊接了120欧的匹配电阻,再把小黑盒自带的120欧电阻接上,那个红色的CAN分析仪接在中间,要把电阻拨到关闭位置。于是总线两端都有了匹配电阻,一条情绪平和的CAN总线就有了。实际上这么短的线,不接120欧电阻应该也是没问题的。
  先做一个CAN通信测试吧,评估板发出协议帧,两个CAN分析仪都收到了:

  为了人为地让帧数据显得复杂,我把数据搞成1-10个连续的bit作为一个数据元素,这可能就是“画蛇添足”的本意吧。下面是上述效果的发送代码:

void Key1Handler(void)
{
                /* Send standard CAN frame */
                eightBytes_t eightBytes = {
                                .bit0                 = 0b1,
                                .bit1_2         = 0b10,
                                .bit3_5  = 0b111,
                                .bit6_9  = 0b1011,
                                .bit10_14 = 0b11000,
                                .bit15_20 = 0b100111,
                                .bit21_27 = 0b1001100,
                                .bit28_35 = 0x5A,
                                .bit36_44 = 0b100000110,
                                .bit45_54 = 0b1111111111,
                                .bit55_63 = 0b000000000
                };

                CAN_DATA_INFO_T txDataInfo = {
                                .msgIdType  = CAN_ID_STANDARD,  /* Standard CAN ID */
                                .dataLen    = sizeof(eightBytes_t),      /* Payload size */
                                .fdEnable   = false,
                                .isRemote   = false
                };
                CAN_SendNonBlocking(CAN0_INSTANCE, TX_MB, &txDataInfo, TX_CAN_MESSAGE_ID_1, (uint8_t *)&eightBytes);
}



  还简单地做了报文负载测试,两个分析仪马力全开,同时以最短的时间间隔持续向总线上发送报文,G32A1465评估板全部接收成功。我觉得G32A1465用在汽车总线通信和工业即时通信场景,一定能够胜任。

------------------20241222追加测试如下:CAN中断里把接收到的报文发回总线(CAN-ECHO)----------------
  看到评估群里有工程师提问:“怎样在CAN中断里发报文?”
  对此,我在SDK例程CAN_ClassicFrames的基础上修改代码,做了测试。是用经典CAN做的,CANFD同理。

1、把main函数里的两个变量放到main.c全局变量区,变为全局变量:
    CAN_MSG_BUF_T rxFrame0;
    CAN_MSG_BUF_T rxFrame1;
2、main()函数的 CAN_InstallEventCallback(CAN0_INSTANCE, &CanEventCallback, NULL);这句之后,加:
   //开始接收CAN报文
    CAN_ReceiveNonBlocking(CAN0_INSTANCE, RX_MB_0, &rxFrame0);
    CAN_ReceiveNonBlocking(CAN0_INSTANCE, RX_MB_1, &rxFrame1);

3、main()函数的while(!exit)循环体内部代码删除。
4、main()的其他配置代码,不需要改变。
5、修改CAN中断服务函数。修改后的 main() 和 CanEventCallback() 代码如下。
简单说来,就是:
配置CAN,加载中断服务函数,开启接收,在中断里处理接收到的报文后,再次开启接收。

void CanEventCallback(
    uint8_t instance,
    CAN_EVENT_TYPE_T eventType,
    uint32_t mbIndex,
    CAN_STATE_T *driverState)
{
                uint8_t payload[64];
                CAN_DATA_INFO_T txDataInfo;
    /* A frame was received in the configured Rx MB */
    if (eventType == CAN_EVENT_RX_COMPLETE)
    {
        /* Set the receive complete flags */
        if (mbIndex == RX_MB_0)
        {
                                        memcpy(payload, rxFrame0.data, rxFrame0.dataLen);
                                        txDataInfo.msgIdType = CAN_ID_STANDARD;
                                        txDataInfo.dataLen = rxFrame0.dataLen;
                                        txDataInfo.fdEnable   = false;
                                        txDataInfo.isRemote   = false;
                                        CAN_SendNonBlocking(CAN0_INSTANCE, TX_MB, &txDataInfo, rxFrame0.messageId, payload);
                                        //再次开始接收
                                        CAN_ReceiveNonBlocking(CAN0_INSTANCE, RX_MB_0, &rxFrame0);
                                        LED_Toggle(LED_BLUE);
       }
        if (mbIndex == RX_MB_1)
        {
                                        memcpy(payload, rxFrame0.data, rxFrame1.dataLen);
                                        txDataInfo.msgIdType = CAN_ID_EXTENDED;
                                        txDataInfo.dataLen = rxFrame1.dataLen;
                                        txDataInfo.fdEnable   = false;
                                        txDataInfo.isRemote   = false;
                                        CAN_SendNonBlocking(CAN0_INSTANCE, TX_MB, &txDataInfo, rxFrame1.messageId, payload);
                                        //再次开始接收
                                        CAN_ReceiveNonBlocking(CAN0_INSTANCE, RX_MB_1, &rxFrame1);
                                        LED_Toggle(LED_GREEN);
        }
    }
}

int main(void)
{
    bool exit = false;
    CAN_STATE_T canState;
    CAN_TIME_SEGMENT_T canBitrate;

    /* Initialize clock */
    CLOCK_SYS_Init(&g_clockConfig);

    /* Initialize pins */
    PINS_Init(NUM_OF_CONFIGURED_PINS0, g_pinsConfig);

    /* Initialize LEDs */
    LED_Init();
    LED_On(LED_GREEN);

    /* Initialize buttons */
    BTN_Init();
    BTN_InstallKey1Handler(Key1Handler);
    BTN_InstallKey2Handler(Key2Handler);

    /* Initialize the CAN controller, bitrate is 250K */
    CAN_Init(CAN0_INSTANCE, &canState, &g_canConfig);

    /* Get the current CAN bitrate */
    CAN_GetBitrate(CAN0_INSTANCE, &canBitrate);

    /* Configure Tx MB */
    CAN_DATA_INFO_T txMbInfo = {
        .msgIdType  = CAN_ID_STANDARD,   /* Standard CAN ID */
        .dataLen    = 8,        /* Used only to initialize the MB, will be updated when send data */
        .fdEnable   = false,    /* Disable CAN FD */
        .isRemote   = false
    };
    CAN_ConfigureTxMb(CAN0_INSTANCE, TX_MB, &txMbInfo, 0);

    /* Use individual Rx masking for the MBs */
    CAN_SetRxMaskType(CAN0_INSTANCE, CAN_RX_MASK_INDIVIDUAL);

    /* Configure Rx MB0, it will receive standard frames with ID 1 */
    CAN_DATA_INFO_T rxMb0Info = {
        .msgIdType  = CAN_ID_STANDARD,   /* Standard CAN ID */
        .dataLen    = 8,        /* Max data length for classic CAN is 8 bytes */
        .fdEnable   = false,    /* Disable CAN FD */
        .isRemote   = false
    };
    CAN_ConfigureRxMb(CAN0_INSTANCE, RX_MB_0, &rxMb0Info, RX_CAN_MESSAGE_ID);
    CAN_SetRxIndividualMask(CAN0_INSTANCE, CAN_ID_STANDARD, RX_MB_0, 0xFFFFFFFF);

    /* Configure Rx MB1, it will receive extended frames with ID 1 */
    CAN_DATA_INFO_T rxMb1Info = {
        .msgIdType  = CAN_ID_EXTENDED,   /* Extended CAN ID */
        .dataLen    = 8,        /* Max data length for classic CAN is 8 bytes */
        .fdEnable   = false,    /* Disable CAN FD */
        .isRemote   = false
    };
    CAN_ConfigureRxMb(CAN0_INSTANCE, RX_MB_1, &rxMb1Info, RX_CAN_MESSAGE_ID);
    CAN_SetRxIndividualMask(CAN0_INSTANCE, CAN_ID_EXTENDED, RX_MB_1, 0xFFFFFFFF);

    /* Set the CAN event callback handler */
    CAN_InstallEventCallback(CAN0_INSTANCE, &CanEventCallback, NULL);
                //开始接收CAN报文
    CAN_ReceiveNonBlocking(CAN0_INSTANCE, RX_MB_0, &rxFrame0);
    CAN_ReceiveNonBlocking(CAN0_INSTANCE, RX_MB_1, &rxFrame1);

    /* Receive CAN frames from MB0 and MB1 */
    while (!exit)
    {
                        ;
    }

    CAN_DeInit(CAN0_INSTANCE);
    LED_On(LED_RED);
    return 0;
}



使用特权

评论回复
地板
lihuasoft|  楼主 | 2024-12-15 14:20 | 只看该作者
本帖最后由 lihuasoft 于 2024-12-15 16:56 编辑


  (四)CAN FD通信测试

  CAN FD是在数据负荷区提高速率,加大了数据负荷能力。以前了解过,但没用过,在此只做一个实验。在使用时需要注意比特率有两个,都要设置正确,否则会报错的。
  为了掩饰自己小白的真实身份,特意用了一个充电机的协议帧,显得自己好像很懂的样子(名为BCS,扩展帧ID为0x1811FEFE):



  实验代码直接用 SDK\Examples\G32A1465\CAN\CAN_FdFrames 这个例程稍做修改如下:

void Key1Handler(void)
{
                BCS_t BCS = {
                        .BCS_C_Vol = 470,                //充电电压测量值
                        .BCS_C_Cur = 1190,        //充电电流测量值
                        .BCS_B_VMax = 425,        //最高单体动力蓄电池电压
                        .BCS_B_VMGroup = 3,        //单体蓄电池组号
                        .BCS_SOC = 90,                        //当前荷电百分比
                        .BCS_E_Tim = 18                        //估计剩余充电时长
                };
                CAN_DATA_INFO_T txDataInfo = {
                        .msgIdType  = CAN_ID_EXTENDED,  /* 扩展帧ID */
                        .dataLen    = sizeof(BCS),      /* 数据长度 */
                        .fdEnable   = true,             /* Enable CAN FD */
                        .fdPadding  = 0x00,                 /* Use 0x00 for FD padding */
                        .brsEnable  = true,                 /* Enable bitrate switch */
                        .isRemote   = false
                };
                CAN_SendNonBlocking(CAN0_INSTANCE, TX_MB, &txDataInfo, 0x1811FEFE, (uint8_t *)&BCS);
}

  编译下载固件到G32A1465评估板,按下Key1,CANFD分析仪收到报文BCS这个报文(ID = 0x1811FEFE):







使用特权

评论回复
5
lihuasoft|  楼主 | 2024-12-15 14:20 | 只看该作者
本帖最后由 lihuasoft 于 2024-12-29 22:11 编辑


  (五)做一个接近真实场景的实验



  这个实验涉及以下内容:
  1、LIN通信
  2、LIN转CAN网关
  3、G32A1的CAN“假装联网”功能
  4、G32A1安全相关的应用

  设想如下的汽车应用场景:打开雨刷开关后,ECU通过LIN总线,定时向雨量传感器请求数据,收到数据后除做必要的动作处理,还通过CAN总线向整车控制器转发数据,而整车控制器则需对收到的报文做安全验证,进而生成指令发给其他机构。
  拓扑如下(传感器图片来自网络):


  在这个实验中,G32A1评估板要担任ECU和VCU两个角色,而我只有一块评估板因此这个实验要分两步做
  第一步,实现拓扑图中的左半部分,评估板为ECU,与传感器(用一块其他厂家的板子做传感器)做LIN通信,而PC上位机做VCU角色,只负责接收CAN报文供验证;
  第二步,实现拓扑图中的下半部分,评估板为VCU,用PC上位机做ECU角色,验证假装联网+唤醒及安全验证等功能。

  这层楼实验第一步。
  下图是实物连接。


  绿色板子(传感器)为LIN从机,而评估板为LIN主机,每次主机发出ID为2的数据接收命令,从机都会发2个字节的数据到LIN总线上,LIN主机收到这2字节,立即转发到CAN总线上。这部分实验已成功,关键代码附后。
  另外,建议:G32A1 SDK中的LIN代码虽简洁明了,但不太方便对接友商LIN设备,如果能写成固定格式(移植时可修改)的“调度表”,就方便很多。

  LIN总线很简单,关键是主从双方的调度表要约定一致。
  每次通信,LIN主机发出一个Header,然后根据双方约定的调度表,特别是每一帧的数据方向,来判断数据是从机发给主机,还是主机发给从机。如有不太熟悉LIN总线的朋友可以查看我过去发的贴子,有一篇是专门写LIN总线的。

  本次实验的LIN主机代码在SDK例程LIN_Master的基础上修改。

#define BUFFER_SIZE             (2U)
uint8_t g_rxBuffer[BUFFER_SIZE] =  {0x00, 0x00};//这是接收到的传感器数据的存放处
uint8_t g_txBuffer2[BUFFER_SIZE] = {0xF0, 0x55};//这是原例程中用于比对的,因实验使用了固定数据,我并未删除比对的代码
//...

//这句是LIN主机向LIN从机发出请求数据的命令,可以根据需要定时执行。FRAME_MASTER_RX_DATA的值即PID,需要与从机设置一致
LIN_MasterTxHeader(LIN1_INSTANCE, FRAME_MASTER_RX_DATA);

//在main函数里初始化CAN总线(可参考SDK例程中的CAN程序)
int main(void)
{
    bool exit = false;
    CAN_STATE_T canState;
    CAN_TIME_SEGMENT_T canBitrate;

    /* Initialize clocks */
    CLOCK_SYS_ClockManagerInit(g_clockConfigsArr,
                               CLOCK_CONFIG_CNT,
                               g_clockCallbacksArr,
                               CLOCK_CALLBACK_CNT);
    CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_AGREEMENT);

    /* Init pins */
    PINS_Init(NUM_OF_CONFIGURED_PINS, g_pinsConfig);

    /* Initialize LEDs */
    LED_Init();

    /* Initialize buttons */
    BTN_Init();
    BTN_InstallKey1Handler(Key1Handler);
    BTN_InstallKey2Handler(Key2Handler);

    LIN_EnableTransceiver();
          CAN_Init(0, &canState, &g_canConfig);
    /* Configure Tx MB */
    CAN_DATA_INFO_T txMbInfo = {
        .msgIdType  = CAN_ID_STANDARD,   /* Standard CAN ID */
        .dataLen    = 8,        /* Used only to initialize the MB, will be updated when send data */
        .fdEnable   = false,    /* Disable CAN FD */
        .isRemote   = false
    };
    CAN_ConfigureTxMb(0, 2, &txMbInfo, 0);

    /* Initialize LPTMR */
    LPTMR_Init(INST_LPTMR1, &g_lptmrConfig, false);
    INT_SYS_InstallHandler(LPTMR_IRQn, LPTMR_ISR, (ISR_T *)NULL);
    INT_SYS_EnableIRQ(LPTMR_IRQn);
    LPTMR_StartCounter(INST_LPTMR1);

    /* Start LIN master task */
    LinMasterTask();//进入LIN主机模式

    while (!exit);
}

//LIN总线很简单,关键是主从双方的调度表要求是一致的。
//每次通信,LIN主机发出一个Header,然后根据双方约定的调度表,以及报文中的数据方向
//判断数据是从机发给主机,还是主机发给从机
//如有不太熟悉LIN总线的朋友可以查看我过去发过的评估贴子,有一篇是专门写LIN总线的
void LinMasterTask(void)
{
    STATUS_T status = STATUS_ERROR;
    bool rxStatus = false;
    uint8_t remainBytes = 1U;
    uint8_t i;
       
                CAN_DATA_INFO_T txDataInfo = {
                                .msgIdType  = CAN_ID_EXTENDED,  /* Extended CAN ID */
                                .dataLen    = sizeof(g_rxBuffer),      /* Payload size */
                                .fdEnable   = false,
                                .isRemote   = false
                };

    /* Initialize LIN interface */
    LIN_Init(LIN1_INSTANCE, &g_lin1UserConfig, &g_lin1State);

    /* Install callback function */
    LIN_InstallCallback(LIN1_INSTANCE, (LIN_CALLBACK_T)LinCallbackHandler);

    /* Infinite loop */
    for (;;)
    {
        status = LIN_GetRxStatus(LIN1_INSTANCE, &remainBytes);
        if (  (status == STATUS_SUCCESS)
           && (remainBytes == 0U)
           && (g_lin1State.currentId == FRAME_MASTER_RX_DATA))
        {
            /* Check received data */
            rxStatus = true;
            for (i = 0; i < BUFFER_SIZE; i++)
            {
                if (g_rxBuffer != g_txBuffer2)
                {
                    rxStatus = false;
                    break;
                }
            }

            if (rxStatus)
            {
                /* Toggle green LED if received data is correct */
                LED_Off(LED_RED);
                LED_Off(LED_BLUE);
                LED_Toggle(LED_GREEN);
        //翻转绿灯,并转发数据到CAN总线上                                               
                CAN_SendNonBlocking(0, 2, &txDataInfo, 0x0001, g_rxBuffer);

            }
            else
            {
                /* Turn on red LED if received data is not correct */
                LED_Off(LED_GREEN);
                LED_Off(LED_BLUE);
                LED_On(LED_RED);
            }
        }

        /* Turn on blue LED if woke-up */
        if (g_wakeupFlag)
        {
            /* Clear wakeup signal */
            g_wakeupFlag = false;

            LED_Off(LED_GREEN);
            LED_Off(LED_RED);
            LED_On(LED_BLUE);
        }

        /* Turn off all LEDs in sleep mode */
        if (LIN_NODE_SLP_MODE == LIN_GetNodeState(LIN1_INSTANCE))
        {
            LED_Off(LED_RED);
            LED_Off(LED_BLUE);
            LED_Off(LED_GREEN);
        }
    }
}

  顺便说一下,如果评估板用作LIN从机,最好把原理图中红圈里的支路断开(做主机又必须要接上):



实验的第二步,楼下见。




使用特权

评论回复
6
lihuasoft|  楼主 | 2024-12-15 14:20 | 只看该作者
本帖最后由 lihuasoft 于 2025-1-8 21:27 编辑

今晚把剩余的一半实验做完。
用文字重新描述一下这个实验:





汽车已熄火,VCU已睡眠。但----车主忘记关闭车窗了。
----偏偏这时下雨了。
此时,雨量传感器处于低功耗工作中。
从第一滴雨落下,它退出了低功耗。
第三滴雨,它确信已经下雨,于是把消息通过LIN总线转发给了雨刷机构的ECU----这些,在实验的前半部分已介绍。
ECU给VCU发了一个ID为0xEE的报文(下图第1条报文):下雨了!
VCU立即被唤醒(此时蓝灯灭、绿灯亮),并且知道是雨刷机构唤醒了它。
VCU掏出密钥,发给ECU(下图第2条报文):验明正身!
ECU收到密钥,把最新的雨量信息加密处理,再次发给VCU(图中第3条报文):真的下雨了!
VCU收到加密报文(此时红、绿灯都亮),通知车窗动作机构(图中第4条报文):关窗!
5秒后,VCU再次睡大觉。






图中:
第1条报文:用上位机模拟ECU,发给VCU的唤醒报文。ID为0xEE。
第2条报文:VCU被唤醒后,发回的密钥报文(第0字节0xAA),ID不变。
第3条报文:用上位机模拟ECU,对传感器数据加密后重新发回VCU。实际未加密,想像一下即可。因上位机不便实现。
第4条报文:VCU收到安全通信报文后,对执行机构做出指示。ID+1,第1字节0xBB。


这个实验到此结束了。不打算包含安全通信了,因为上位机代替ECU,无法做到根据VCU发来的真随机数密钥进行加密,
然后把加密报文发给VCU。只有一个评估板,的确不方便实现。

主要的代码及注释如下:


CAN_MSG_BUF_T wakeupFrame;
CAN_DATA_INFO_T txDataInfo;
CAN_MSG_BUF_T rxFrame0;
CAN_MSG_BUF_T rxFrame1;

void CanEventCallback(
    uint8_t instance,
    CAN_EVENT_TYPE_T eventType,
    uint32_t mbIndex,
    CAN_STATE_T *driverState)
{
                uint8_t payload[64];
                CAN_DATA_INFO_T txDataInfo;
    //唤醒事件处理
    if (eventType == CAN_EVENT_WAKEUP_MATCH)
    {
        LED_Off(LED_BLUE);
        LED_On(LED_GREEN);
                }
    //接收事件处理
    if (eventType == CAN_EVENT_RX_COMPLETE)
    {
                          //VCU把收到的加密报文稍做处理,发到总线上去。可以理解为它指示动作机构执行动作(比如:关窗)
        if (mbIndex == 0)
        {
                                         memcpy(payload, rxFrame0.data, rxFrame0.dataLen);
                                        payload[1] = 0xBB;
                                        txDataInfo.msgIdType = CAN_ID_STANDARD;
                                        txDataInfo.dataLen = rxFrame0.dataLen;
                                        txDataInfo.fdEnable   = false;
                                        txDataInfo.isRemote   = false;
                                        CAN_SendNonBlocking(CAN0_INSTANCE, 2, &txDataInfo, rxFrame0.messageId+1, payload);
                                        //再次开始接收
                                        CAN_ReceiveNonBlocking(CAN0_INSTANCE, 0, &rxFrame0);
                                }
        if (mbIndex == 1)
        {
                                        memcpy(payload, rxFrame0.data, rxFrame1.dataLen);
                                        payload[1] = 0xCC;
                                        txDataInfo.msgIdType = CAN_ID_EXTENDED;
                                        txDataInfo.dataLen = rxFrame1.dataLen;
                                        txDataInfo.fdEnable   = false;
                                        txDataInfo.isRemote   = false;
                                        CAN_SendNonBlocking(CAN0_INSTANCE, 2, &txDataInfo, rxFrame1.messageId+1, payload);
                                        //再次开始接收
                                        CAN_ReceiveNonBlocking(CAN0_INSTANCE, 1, &rxFrame1);
        }
                                LED_On(LED_RED);
    }

}


int main(void)
{
    STATUS_T status = STATUS_ERROR;
    CAN_STATE_T canState;

    /* Initialize clock */
    CLOCK_SYS_Init(&g_clockConfig);

    /* Initialize pins */
    PINS_Init(NUM_OF_CONFIGURED_PINS0, g_pinsConfig);

    /* Initialize LEDs */
    LED_Init();

    /* Initialize the power manager */
    POWER_SYS_Init(&g_powerConfigs,
                   POWER_CONFIG_COUNT,
                   &g_powerManagerCallbacks,
                   POWER_CALLBACK_CNT);

    /* Initialize the CAN controller, bitrate is 250K */
    CAN_Init(CAN0_INSTANCE, &canState, &g_canConfig);

    /* Set the CAN event callback handler */
    CAN_InstallEventCallback(CAN0_INSTANCE, &CanEventCallback, NULL);
               
    /* Configure Rx MB0, it will receive standard frames with ID 0xEE */
    CAN_DATA_INFO_T rxMb0Info = {
        .msgIdType  = CAN_ID_STANDARD,   /* Standard CAN ID */
        .dataLen    = 8,        /* Max data length for classic CAN is 8 bytes */
        .fdEnable   = false,    /* Disable CAN FD */
        .isRemote   = false
    };
    CAN_ConfigureRxMb(CAN0_INSTANCE, 0, &rxMb0Info, 0xEE);
    CAN_SetRxIndividualMask(CAN0_INSTANCE, CAN_ID_STANDARD, 0, 0xFFFFFFFF);

    /* Configure Rx MB1, it will receive extended frames with ID 0xEE */
    CAN_DATA_INFO_T rxMb1Info = {
        .msgIdType  = CAN_ID_EXTENDED,   /* Extended CAN ID */
        .dataLen    = 8,        /* Max data length for classic CAN is 8 bytes */
        .fdEnable   = false,    /* Disable CAN FD */
        .isRemote   = false
    };
    CAN_ConfigureRxMb(CAN0_INSTANCE, 1, &rxMb1Info, 0xEE);
    CAN_SetRxIndividualMask(CAN0_INSTANCE, CAN_ID_EXTENDED, 1, 0xFFFFFFFF);
               
                //开始接收CAN报文
    CAN_ReceiveNonBlocking(CAN0_INSTANCE, 0, &rxFrame0);
    CAN_ReceiveNonBlocking(CAN0_INSTANCE, 1, &rxFrame1);

    /**
     * Enable pretended networking.
     * The device will be woke up when this CAN frame is received:
     * - CAN ID = 0xEE
     */
    CAN_PN_CONFIG_T pnConfig = {
        .wakeupByTimeout    = false,    /* Wakeup by timeout event is disabled */
        .wakeupByMatch      = true,     /* Wakeup by message match event is enabled */
        .matchCount         = 1,        /* Match once */
        .matchTimeout       = 0,        /* No timeout */
        .filterType         = CAN_PN_FILTER_ID,    /* Match both CAN ID */
        .idFilterScheme     = CAN_PN_MATCH_EXACT,   /* Match the exact CAN ID */
        .payloadFilterScheme = CAN_PN_MATCH_EXACT,   /* Match the exact payload */
        .idFilter1 =
        {
            .isExtended = false,    /* Standard CAN ID */
            .isRemote = false,
            .idFilter = 0xEE           /* CAN ID = 0xEE */
        },
        .idFilter2 =
        {
            .isExtended = false,
            .isRemote = false,
            .idFilter = 0xFFFFFFFF  /* Masks for CAN ID */
        },
        .payloadFilter =
        {
            .minPayloadLen = 8, /* Lower limit of the payload size: 8 byte */
            .maxPayloadLen = 8, /* Upper limit of the payload size: 8 byte */
            .payloadFilter1 = {}, /* Match the complete 8 bytes of payload */
            .payloadFilter2 = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Masks for the payload */
        }
    };
    CAN_ConfigurePretendedNetworking(CAN0_INSTANCE, true, &pnConfig);

    /* Go to sleep and wait */
    while (1)
    {
        /* Before going to sleep mode: blue LED = ON, green LED = OFF */
        LED_On(LED_BLUE);
        LED_Off(LED_GREEN);
        LED_Off(LED_RED);

        status = POWER_SYS_ConfigPowerMode(POWER_MODE_STOP2, POWER_POLICY_FORCIBLE);
                                //此时,VCU已睡眠......而CAN的PE仍在工作,维持“假装联网”的状态,等待CAN唤醒报文
        if (status != STATUS_SUCCESS)
        {
            break;//如果进入睡眠失败,则会执行到这里
        }

        //此时,VCU被CAN报文唤醒,查看是谁执行了唤醒,留下了什么报文
        CAN_GetWakeupMessage(CAN0_INSTANCE, 0, &wakeupFrame);
                                
                                //如果唤醒者恰恰是自己关心的,则按原ID发回密钥,进行安全通信
                                if(wakeupFrame.messageId == 0xEE)
                                {
                                        OSIF_TimeDelay(100);
                                        txDataInfo.msgIdType = CAN_ID_STANDARD;
                                        txDataInfo.dataLen = wakeupFrame.dataLen;
                                        txDataInfo.fdEnable   = false;
                                        txDataInfo.isRemote   = false;
                                        //因为上位机不便做密钥计算,此处借修改第0字节数据,象征VCU把真随机数密钥发给唤醒它的ECU
                                        wakeupFrame.data[0] = 0xAA;
                                        CAN_SendNonBlocking(CAN0_INSTANCE, 2, &txDataInfo, wakeupFrame.messageId, wakeupFrame.data);
                                }
                                
        //等待对方发来加密后的正式数据报文。5秒之后,VCU再进被窝继续睡大觉
        OSIF_TimeDelay(5000);
    }

    CAN_DeInit(CAN0_INSTANCE);
    LED_On(LED_RED);
    return 0;
}


使用特权

评论回复
7
lihuasoft|  楼主 | 2024-12-15 14:21 | 只看该作者
本帖最后由 lihuasoft 于 2024-12-16 13:31 编辑


(开箱图)

把开箱图片放在这里吧。从细节中,可以看出Geehy做产品很用心。













(因本人见识有限,以上内容观点修辞有不当之处,敬请谅解。谢谢看贴回贴)

使用特权

评论回复
8
xch| | 2024-12-16 09:31 | 只看该作者
尼姑用倚天剑

使用特权

评论回复
评论
lihuasoft 2024-12-16 11:00 回复TA
好的,杠哥,你赢了 
9
丙丁先生| | 2024-12-16 13:11 | 只看该作者
感谢分享

使用特权

评论回复
10
LEDyyds| | 2024-12-16 14:57 | 只看该作者
你这个标题真不错啊,不过汽车芯片确实和车规不是一个概念,这个芯片还是很好用

使用特权

评论回复
11
lihuasoft|  楼主 | 2024-12-29 09:26 | 只看该作者
自己顶一下,呵呵
沙发有更新:(关于0822版SDK的小插曲)

使用特权

评论回复
12
Pandachen| | 2025-1-3 09:29 | 只看该作者

使用特权

评论回复
13
lihuasoft|  楼主 | 2025-1-8 21:31 | 只看该作者
up

使用特权

评论回复
14
lihuasoft|  楼主 | 2025-1-8 21:32 | 只看该作者
最新内容在6楼。5楼+6楼是一个完整实验。。。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

24

主题

295

帖子

3

粉丝