【极海G32A1465汽车通用MCU评估板测评】“以为是把工兵铲,实际是柄倚天剑”
本帖最后由 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-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-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;
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 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):
本帖最后由 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 ={0x00, 0x00};//这是接收到的传感器数据的存放处
uint8_t g_txBuffer2 = {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从机,最好把原理图中红圈里的支路断开(做主机又必须要接上):
实验的第二步,楼下见。
本帖最后由 lihuasoft 于 2025-1-8 21:27 编辑
今晚把剩余的一半实验做完。
用文字重新描述一下这个实验:
static/image/hrline/4.gif
汽车已熄火,VCU已睡眠。但----车主忘记关闭车窗了。
----偏偏这时下雨了。
此时,雨量传感器处于低功耗工作中。
从第一滴雨落下,它退出了低功耗。
第三滴雨,它确信已经下雨,于是把消息通过LIN总线转发给了雨刷机构的ECU----这些,在实验的前半部分已介绍。
ECU给VCU发了一个ID为0xEE的报文(下图第1条报文):下雨了!
VCU立即被唤醒(此时蓝灯灭、绿灯亮),并且知道是雨刷机构唤醒了它。
VCU掏出密钥,发给ECU(下图第2条报文):验明正身!
ECU收到密钥,把最新的雨量信息加密处理,再次发给VCU(图中第3条报文):真的下雨了!
VCU收到加密报文(此时红、绿灯都亮),通知车窗动作机构(图中第4条报文):关窗!
5秒后,VCU再次睡大觉。
static/image/hrline/4.gif
图中:
第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;
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 = 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 = 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 = 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;
}
本帖最后由 lihuasoft 于 2024-12-16 13:31 编辑
(开箱图)
把开箱图片放在这里吧。从细节中,可以看出Geehy做产品很用心。
(因本人见识有限,以上内容观点修辞有不当之处,敬请谅解。谢谢看贴回贴)
尼姑用倚天剑 你这个标题真不错啊,不过汽车芯片确实和车规不是一个概念,这个芯片还是很好用 自己顶一下,呵呵
沙发有更新:(关于0822版SDK的小插曲)
up 最新内容在6楼。5楼+6楼是一个完整实验。。。
页:
[1]