您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 
 订阅
CP Autosar - 万字长文调试NvM
 
作者:业余正规军
  1765  次浏览      19 次
 2023-7-5
 
编辑推荐:
本文主要介绍CP Autosarde NvM模块,如何调试NVM模块,希望对您的学习有所帮助。
本文来自于汽车与基础软件 ,由火龙果软件Alice编辑,推荐。

00 先给读者们跳一段舞

图片

01 前言

NvM模块我们知道,位于AutoSar的Memory Stack的服务层, Stack的 结构图如下:

图片

那么,AutoSar(ETAS)的代码是如何实现EEPROM的读写呢?以如下代码为引,调试下NvM读取时关键变量、状态机的跳转过程。

NvM_ReadAll();
do
{
NvM_MainFunction();
MemIf_Rb_MainFunction();
NvM_Rb_GetStatus(&NvM_Sts);
MemIF_Sts = MemIf_Rb_GetStatus();
}
while ((NVM_RB_STATUS_BUSY == NvM_Sts) || (MEMIF_BUSY == MemIF_Sts));

02 NvM_ReadAll()

根据isolar中配置的NvM block属性 NvM_Prv_BlockDescriptors _ acst ,

设置NvM block的请求状态 NvM_Prv_stRequests_rAMwAM_au16 为

NvM_Prv_ServiceBit_ReadAll_e ,

设置 请求结果状态 NvM_Prv_stRequestResult_rAwAM_au8 为

NVM_REQ_PENDING 。

( 方便后面调 试复制,蓝色:变量,绿色:枚举,咖色:函数 )

如图 所 示:

图片

如开头代码的while循环,接下来是挨个block去读取内容的过程。

03 NvM_MainFunction()

还算是个比较清爽的、以 NvM_Prv_Main_st.Activity_rAMwM_en 变化做状态机跳转的函数。

图片

接下来一个个状态机分解调试一下看看。

首先可以看下Init之后, NvM_Prv_Main_st.Activity _ rAMwM_en :

图片

NVM_PRV_ACTIVITY_ARBITRATE

NvM活动仲裁:

在执行完函数 NvM_Prv_MainFunctionArbitrate() 后,可以看到

NvM_Prv_Main_St 结构体的变化如下:

图片

选取了第一个有效的地址进行Read操作。

NVM_PRV_ACTIVITY_JOB_START

  1. 如果我们配置了SingleBlockStartCallback,则会首先调用回调函数;

图片

2.之后会根据当前NvM的状态,去调用 NvM_Prv_JobStart_Read 函数,而从这边, 会去区分是使用FEE(flash模拟EEPROM)接口还是EA(eeprom抽象)接口。

目前项目使用的是FEE,自然就执行到了 FEE_Read 函数了。

这里面也不算复杂,是对 Fee_OrderFifo_st 结构体的赋值。

图片

3.最后,改变状态机进入下一个步骤。

图片

NVM_PRV_ACTIVITY_POLL_RESULT

调用 Fee_GetJobResult 获取下全局变量 Fee_GlobModuleState_st 的值,即Fee的当前状态。当状态不为 MEMIF_JOB_PENDING , 可以去下一个步骤

NVM_PRV_ACTIVITY_JOB_COMPLETE 。

那么其实NvM就是通过 job_start 和 poll_result 这2个状态机,对Fee模块做请求和等待结果的处理。

NVM_PRV_ACTIVITY_JOB_COMPLETE

调用的 NvM_Prv_JobComplete_Read ,其实是一个保护函数,如果NvM状态为未完成状态,

重新回到 NvM_Prv_MainFunctionJobStart 。

之后 进入 NVM_PRV_ACTIVITY_RESULT_EVAL 。

NVM_PRV_ACTIVITY_RESULT_EVAL

如果读的流程没有问题,修改 NvM_Prv_stRequestResult_ rAwAM_au8 的状态为 NVM_REQ_OK ,则会调用

NvM_Prv_MainFunctionResultEval _ ResetMainStates ,这边又是个内联函数,需要打特殊的断点才能看到。

清除 NvM_Prv_stRequests_rAMwAM_au16 请求状态,初始化

NvM_Prv_Main_st.JobData_st 。

再回到 NVM_PRV_ACTIVITY_ARBITRATE , 准备执行下一个block的读任务。

04 MemIf_Rb_MainFunction()

其实就是封装了Fee的mainfuction。而 Fee_MainFunction() ,是以

Fee_Rb_WorkingState_en 变化做状态机跳转。暂时调试一下读。

FEE_RB_IDLE_E

承接 NvM_Mainction() 中的 NVM_PRV_ACTIVITY_JOB_START , 可以看到是在那边修改了 Fee_OrderFifo_st 中 FEE_NVM_JOB 的 Mode_en 为 Fee_Read_Order 。

那么 FEE_Mainfunction() ,在执行查找 Fee_SearchNextOrder() 之后,就执行 Fee_LoadNextOrder() ,

去修改 Fee_Rb_WorkingState_en 为 Read( FEE_RB_READ_MODE_E )。

FEE_RB_READ_MODE_E

执行函数:

图片

备注有误哦。

这个函数里面每个调用的子函数都有自己单独的状态机,调试的相当头疼,不过结束再回来看,发现其实状态机的跳转过程全部是一样的,大致如下:

Init→Search →Read → R ead_W a it→Finished →Init。

在读到数据之后,则调用 Fee_UpdateStatus() 修改状态机,结束当前block读取,进入下一个block或者全部读取完退出while循环。

这边还是把函数单列一个章节去讲吧,内容其实还是比较多的(调试了有点久 )。

05 Fee_HLReadBlock()

基于 Fee_RdWrOrder_st.Fee_HLRdBlock 的状态机跳转。

FEE_HL_RDWR_BLK_INIT_E

获取 Fee_OrderFifo_st 中当前需要读的block的PersistentId、lenth和Flags

,赋值给 Fee_GlobInfoLastRdHeader_st 。

然后设置 Fee_RdWrOrder_st.Fee_HLRdBlock 状态为

FEE_HL_SEARCH_BLK_HDR_E 去查找block的头。

图片

FEE_HL_SEARCH_BLK_HDR_E

循环执行 函数 Fee_LLSearchSpecifiedBlkHeader() ,它也有自己的状态机跳转

Fee_RdWrOrder_st.Fee_LLSearchBlkHdr_en ,断点打上调就完了!

1.Fee_RdWrOrder_st.Fee_LLSearchBlkHdr_en

执行 Fee_GetMostCurrentSectorIdx() 获取当前的使用物理块,

图片

然后调用 Fee_LLGetAddressFromCache ,获取block的地址给到结构体Fee_RdWrOrder_st。

其中, Fee_Cache_au32 就是 fee block的存储位置,在 Fee_Init() 时就已经设置好了。

如果我们曾经写过某个block,那么cache中存储的即为具体位置,如果没有写过,那么会有一个无效的地址

FEE_CACHE_TMP_INVALID_VALUE (0xCAFEAFFE)。

图片

2.FEE_LL_SEARCHBLK_BLK_HEADER_E

执行 Fee_LLSearchNextBlkHeader() ,它的状态机是

Fee_RdWrOrder_st.Fee_LLRdState_en。

简单总结就是确定读取位置,polling模式读取,读取完成后校验内容。

如果是无效地址,则返回 Fee_ERROR_E ,初始化

Fee_RdWrOrder_st.Fee_LLRdState_en ;

如果是有效地址,则在 Fee_LLSearchNextBlkHeader 函数中会进一步调用

Fls_Read()。

不过这个函数也不是读函数,仅是在dflash not busy时,将地址偏移0xAF00 0000(使用的tc3**)后给到Fls_CinfigPtr。

之后调用 Fls_17_Dmu_MainFunction ,到最底层的 Fls_lMainRead ,才是读函数。

首先确认 寄存器状态 HF_ERRSR、HF_ECCC ,然后将ReadAddress的内容读到ReadBufferPtr的位置。

图片

读完之后置位 FlsJobResult 为 MEMIF_JOB_OK ,

那么 Fee_CheckFlsJobResult 函数就会去设置

Fee_RdWrOrder_st.Fee_LLRdState_en 为 FEE_LL_READ_FINISHED_E。

3.FEE_LL_READ_FINISHED_E

然后校验数据的头(0xA53C96)和 CRC16,之后

函数 Fee_ LLSearchSpecifiedBlkHeader 返回 FEE_ORDER_FINISHED_E ,结束读命令。

设置 Fee_RdWrOrder_st.Fee_HLRdBlock = FEE_HL_CHECK_BLK_CS_E。

block header结构体如下:

图片

FEE_HL_CHECK_BLK_CS_E

执行 函数 Fee_LLCalcBlkCrcInFlash,它也有状态机

Fee_RdWrOrder_st.Fee_LLCalcCrcBlk_en,其中又是去Dflash中读取数据,不过这次读的是Crc32部分的内容并校验。

FEE_HL_RD_DATA_FROM_BLK_E

执行 Fee_LLCopyData2Buffer(),终于到了给咱们设置的Ram buffer赋值的地方了,最终将函数读取完毕。

图片

整个流程Fee的读取流程如下:

图片

06 NvM_Write概述

NvM的写入过程,即是上层通过调用NvM接口改变队列信息,之后

NvM_MainFunction() 再去逐级往下调用 Fee_MainFunction() ,最终将源Ram数据拷贝到目标Dflash地址中去。

流程图如下:

图片

再附一个write过程的最底层接口调试图:

图片

接下来就是具体的写入过程。

07 关键变量

UDS中某个2E服务DID:

图片

NvM的请求队列 :

NvM_Prv_Queue_ast[]

Fee的请求队列:

Fee_OrderFifo_st []

Fee存储 读、写的状态机和其他信息:

Fee_RdWrOrder_st

Fee的工作状态机 :

Fee_Rb_WorkingState_en

Fee上次读指令存储的信息 :

Fee_GlobInfoLastRdHeader_st

写入的存储临时buffer :

Fee_llPageBuf_au32

08 写入过程

上层请求逻辑

其实应用层调用写入接口有2种,一种是仅请求状态,之后统一时间段存储(可以是休眠前):

NvM_SetRamBlockStatus(NvM_BlockIdType BlockId , boolean BlockChanged) ,

另一种则是目前项目使用的立即触发存储:

NvM_WriteBlock(NvM_BlockIdType BlockId , const void*NvM_SrcPtr)

从函数内容来看,基本上只是请求的类型不同。

函数会首先判断下请求的NvM block的lenth和buffer地址的有效性,之后调用 NvM_Prv_Queue_EnqueueRequest (idQueue_uo,&BlockData_pcst> QueueEntry_st ) ;

对队列进行赋值操作,如下图:

图片

这边还能看到一个细节信息,队列的最大值size_cu16为50,其实是与isolar中的配置是一致的。

图片

NvM_MainFunction()

和Read一样,以 NvM_Prv_Main_st.Activity_rAMwM_en 为状态机跳转。

首先, NvM_Prv_MainFunctionArbitrate() 函数将 队列

NvM_Prv_Queues_ast 的内容拷贝到 NvM_Prv_Main_st 中,之后设置

JobData_st 。

经过函数 NvM_Prv_JobStart_Write () 之后 ,再看下属性:

图片

这边会调用 F ee_Write() 设置 Fee_OrderFifo_st [ FEE_NVM_JOB ] ,

图片

图片

那么之后的 NvM_Prv_MainFunctionPollResult() , 只需要等待Fee的返回结果就可以了。

最后再执行 NvM_Prv_MainFunctionJobComplete() 、

NvM_Prv_MainFunctionResultEval() 清空状态,完成本次写操作。

rba_FeeFs1_MainFunction()

NvM 在改变 F ee_orderFifo_st 之后 , FEE_mainfunction() 就要出场工作了。

主函数以 为状态机工作。 Fee通过执行 Fee_SearchNextOrder () 和 Fee_LoadNextOrder () 来查找order和Fee block的属性。

之后,调用函数 Fee_HLWriteBlock() , (状态机 Fee_RdWrOrder_st.Fee_HLWrBlock_en )。

写之前先要读,把当前dflash中最新的一块数据读出来,通过

Fee_LLSearchSpecifiedBlkHeader () ,流程跟readall中一致。

图片

然后计算当前待存储数据的Crc,

图片

如果crc和取读的内容一致,那么其实就不需要写入,直接返回上一级即可,如果需要写入那么就开始填充。这里可以看到除了 CRC, 数据段也填充了 2 位,这就是整个头了。

再附一下header的结构体:

图片

再往底层去,调用

Fee_ LLWriteBlock (&Fee_GlobInfoLastRdHeader _st,

Fee_ OrderFifo_st [Fee_idxActQueue_u8] .DataBufferPtr_pu8 );

( 状态机: Fee_RdWrOrder_st.Fee_LLWrBlock_en )

计算存储所用空间,空间不够会触发erase的逻辑,下一篇做压力测试的时候再来调试。

如果空间是够用的,则调用 做真正的写动作,这里是先写了8个字节(1个page)。

图片

整个Fls写过程的调试过程如下,

FEE_LL_WR_WRITEHEADER_E

图片

FEE_LL_WR_WRITED A TA_SEC_A_E

图片

图片

FEE_LL_WR_WAIT_WRITEHDRPG2_E

图片

最后的最后,清空状态,结束“写”动作。

图片

 

 
   
1765 次浏览       19
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试

最新活动计划
MBSE(基于模型的系统工程)4-18[北京]
自然语言处理(NLP) 4-25[北京]
基于 UML 和EA进行分析设计 4-29[北京]
以用户为中心的软件界面设计 5-16[北京]
DoDAF规范、模型与实例 5-23[北京]
信息架构建模(基于UML+EA)5-29[北京]
 
 
最新文章
简述Matplotlib
Python三维绘图--Matplotlib
Python数据清洗实践
PyTorch实战指南
Python爬虫与数据可视化
最新课程
Python应用开发最佳实践
Python+数据分析+tensorflow
Python 编程方法和应用开发
人工智能+Python+大数据
Python及数据分析
更多...   
成功案例
某通信设备企业 Python数据分析与挖掘
某银行 人工智能+Python+大数据
某领先数字地图提供商 Python数据分析与机器学习
北京 Python及数据分析
某金融公司 Python编程方法与实践培训
更多...