Send/Receiver(S/R)接口:
sender提供数据,receiver获取数据。


Client/Server(C/S)接口:server提供功能函数,client调用功能函数。

Provide Ports(P-Ports):sender port、server port
Require Ports(R-Ports):receiver port、client port

1.1 Port和interface区别

生成RTE接口时,使用的是 PORT Element ,比如:
Rte_Write_PassSeatSts_PassSeatSts_ELE();
PassSeatSts为port
PassSeatSts_ELE为Element


Interface可以被多个Port使用。
Rte_Write_Pamb_PassSeatSts_ELE();
Pamb为port
PassSeatSts_ELE为Element

2. DaVinci文件夹说明

3. Application Data Type和Implementation Data Type

每个接口里的的成员会用到数据类型定义,每个数据类型定义都分为Implementation Data Type(IDT)和Application Data Type(ADT),在接口里的成员的data type使用的是ADT,通过type mapping映射到对应IDT。
生成代码时会用到IDT里面的基类型base type,通常uint8,uint16等。ADT里面的解析compu在davinci生成代码时不会体现,在A2L文件中会体现。
在simulink中如果接口里勾选了使用物理值的选项,ADT的compu在simulink生成代码时会使用。

CPU的内核,有单核有多核。一个核做为一个对象。多核就是多个对象。

2. OS Application

每个核对象内部又划分成多个成员,可以是OS Application1,OS Application2,OS Application3,... OS ApplicationN 可以N个。

3. 基本元素

每个OS Application有包含了6个基本元素: Counter,Alarm,Schedule Table,Task,ISRs(Interrupt Service Routines),Resource

3.1 Task

Autosar OS调度的对象。
有两类: 基本任务(basic task) 扩展任务(extended task)

基本任务可以配置成激活几次,davinci里Task的配置项task activation设置成多少,基本任务就可以被激活几次。


扩展任务比基本任务多了一个等待状态(Waiting)。当处于运行状态的任务需要等待某个事件时(比如是AD采集完成事件),这时扩展类型任务会切换到等待状态,并释放CPU资源,等到AD采集完成事件触发后,再切换到准备状态,最后经过调度再进入运行状态。如果是基本类型任务处于运行状态需要等待某个事件,它不会像扩展类型任务切换到等待状态(因为它没有),而是一直占用CPU资源,直到事件触发。

任务状态描述:
运行状态(Running):
当处于该状态的任务被高优先级的任务或中断 抢占(preempt) 后,切换到准备状态(Ready)。
当处于该状态的任务运行 结束(terminate) 后,切换到挂起状态(Suspend)。
当处于该状态的任务需要 等待(wait) 某一事件时,切换到等待状态(Waiting)。
注:在同一CORE中,只会存在一个任务处于运行状态(Running)。

准备状态(Ready):
当处于该状态的任务由OS调度器(依据任务优先级调度) 启动(start) 后,切换到运行状态(Running)。

等待状态(Waiting):
当处于该状态的任务所等待的事件 触发(trigger) 时,切换到准备状态(Ready)。
注:该状态只有扩展任务有。

挂起状态(Suspend):
当处于该状态的任务被Alarms等 激活(Activate) 时,切换到准备状态(Ready)。

基本任务示例代码:

扩展任务示例代码:


3.1.1 Task调度属性
OsTaskPriority :任务的优先级, 值越小优先级越小 。在davinci里面的配置参数为:


注:idle task的该值为最大值即0xFFFFFFFF(4294967295)但是代表的却是最低优先级。

OsTaskSchedule :任务的是否可以抢占。 FULL为可以抢占 NON为不可以抢占
在davinci里面的配置参数为:

如果task为可以抢占,则表示task运行过程中可以被高优先级的task打断,释放内核资源,而进入准备状态。高优先级的task则进入运行状态。
如果task为不可以抢占,则表示task运行过程中不会被高优先级task打断,只有运行完成后才会进行任务的切换。

3.2 Counter

给Alarm或Schedule Table提供时基。多个Alarm可以公用一个Counter。计数加1就是tick加1。
davinci里会默认配置为:

3.3 Alarm

Task从挂起状态激活到准备状态可以由配置好的Alarm周期触发完成。
在davinci中配置好对应的task后,点击检验按钮,davinci会自己生成Alarm:


3.4 Schedule Table

调度表可以同时激活多个task或event,从挂起状态切换到准备状态。Alarm只能激活一个task或event
调度表参数:
Schedule Table Counter Ref:
设置调度表时基,可以和Alarm公用一个counter

Schedule Table Duration:
设置调度表持续时间,单位为tick

Schedule Tabel Repeating:
设置调度表单次执行或否循环执行。如果是循环执行,调度表会在持续时间结束后,又重新执行调度表。

Expiry Point:
每个调度表都有最少一个expiry point,每个expiry point到起点之间都有一个offset时间偏移,在每个expiry point都可以设置多个task或event,当偏移时间到了,就会激活expirty point定义的task或event,将task切换到准备状态。

注:每个调度表最少有一个expiry point,每个expiry最少配置一个task或event,每个expry point优先激活task在设置event。

davinci中的配置:

调度表时序图:

调度表持续时间为4+8+8+12+8+10=50tick

3.5 ISRs(Interrupt Service Rountines)

中断分为两类中断:
一类中断(CATEGORY_1): 这类中断内不能使用os服务(中断内不能激活task或event)
二类中断(CATEGORY_2): 这类中断内能使用os服务(中断内可以激活task或event),如果在这类中断中激活了更高优先级的task2,中断执行完成后会去执行task2然后再回到task1。

注:在autosar里,中断优先级高于所有task优先级(即使是不可抢占的task),而一类中断的优先级高于二类中断的优先级。

3.6 Resouces

这个是为了防止多个task或中断并行访问同一资源(内存等资源)冲突而设计的一个资源管理。
将访问共享资源的task的优先级提升到并行访问该资源的所有task优先级之上(占用共享资源的时间就是提升优先级持续的时间),但是此时task的优先级又低于不访问该资源的task中的最低优先级的task。
这个是自动生成的逻辑,单核中只需davinci中给os appication添加OsResource即可:

Resoucre Property配置项:

有三个属性:
INTERNAL:resource是一个内部资源
LINKED:resource是一个link的资源(现有资源,resource名字不一样)
STANDARD:resource是一个标准资源,一般使用这个熟悉

Resource Linked Resource Ref配置项:

该配置项必须property为LINKED才有效,链接到一个resource。

Resource Accessing Application配置项:
可以访问该resource的Os Application,单核直接把所有Os Application填入即可。

OsResource应用举例:比如睡眠时需要调用GetResource(OsRescource)将执行的task优先级提到最高,避免被其它task打断。


4. IOC(Inter OS Application Communicator)


绑定到相同或不同内核的不同的OS Application之间的通讯。

5. Splin Locks

防止不同内核之间访问共享资源冲突的功能。
如果共享资源已经被其它核占用时,该核要等其它核释放后才能使用共享资源。

6. Memory Protection

用来创建所有内存保护相关的配置参数。
注:内存保护需要特定的硬件支持(MPU(memory protection unit))
只有当OS scalability class设置为SC3或SC4时,才有内存保护。


7. OS scalability class

AUTOSAR提供了四类不同功能安全:
SC1: osek os + schedule table
SC2: osek os + schedule table + timing protection
SC3: osek os + schedule table + memory protection
SC4: osek os + schedule table + timing protection + memory protection

8. Timing Protection

AUTOSAR不提供deadline monitoring(task结束时间监控)来进行时间保护。
deadline monitoring是os监控是否task结束时间超时。但是有时task是由于其它更高优先级的task抢占而导致了结束时间超时的,所以并不能完全识别出导致时间错误的task,导致os结束了正确的task而让导致错误的task继续运行。

引用AUTOSAR规范里的一个例子:
假如有如下配置的task:

在如下整个时序图中三个任务都满足了各自的deadline时间要求

现在假设发生如下图情况,任务A和任务B执行时间超出了规定的时间,任务B提前执行。这是任务B和任务A是满足deadline,然而任务C由于A和B的出错导致不满足deadline时间。所以这个是deadline monitoring的不足。

在像AUTOSAR OS这样的系统中,任务是否能满足dealine是由以下因素决定的:
(1) 系统中任务或中断的执行时间
(2) 被低优先级的任务或中断抢占共享资源或禁止中断导致的执行时间堵塞
(3) 任务或中断之间的时间间隔率

为了安全和准确的时间,AUTOSAR需要控制这些因素,以确保任务或中断满足deadline:
Execution Budget: AUTOSAR设置任务或二类中断执行时间上限来避免上述(1)的时间错误。

Lock Budget: AUTOSAR设置任务或二类中断共享资源抢占上限时间,和设置OS中断被任务或二类中断挂起的时间上限,和设置了所有中断被任务或者二类中断挂起或者屏蔽的时间上限来避免上述(2)的时间错误。

Time Frame: AUTOSAR通过使用到达时间保护设置TASK的时间间隔下限和二类中断被OS识别时就要到达来避免上述(3)的时间错误。其中这个TASK是有要求的,是允许切换到准备状态的TASK,并且是挂起到准备和等待到准备的TASK。

下图显示了AUTOSAR控制这些因素的流程:

注:
时间保护仅仅用于任务或者二类中断,对于一类中断不起作用;
在OS启动之前,时间保护不起作用;

三、Mode Management

1. ECUM

1.1. STARTUP PHASE

Figure 1 STARTUP 概述

上电从0地址跳转转到20401启动代码处

启动代码里初始化一些堆栈等之后,跳转到 main

main里面执行OS初始化,ECUM初始化 EcuM_Init

Figure 2 StartPreOS Sequence

在EcuM_Init中会调用 EcuM_AL_DriverInitZero ,主要初始化AUTOSAR各个模块的变量,该函的内容在达芬奇配置:

在代码中生成为,可以手动添加:

接着调用 EcuM_AL_DriverInitOne ,该函数主要初始化Mcal,该函的内容在达芬奇配置:

在代码中生成为,可以手动添加:


ECUM StartPreOS初始化的最后会启动OS,到这里StartPreOS过程结束,以上过程都是OS启动前。


Figure 3 StartPostOS sequence

之后启动带有 AUTOSTART属性的task ,在该task里面调用 EcuM_StartupTwo ,这函数里面会调用SchM_Init和BswM_Init进行OS启动后的初始化,之后StartPostOs过程结束


这两个初始化函数之后会调用接口 EcuM_AL_DriverInitTwo 主要是AUTOSAR模块的初始化(带逻辑的),此时OS启动,RTE还没启动,这里的初始化不依赖Nvram ReadAll提供数据, EcuM_AL_DriverInitThree 这是RTE启动后相关的初始化,这里的初始化可能依赖Nvram ReadAll提供数据:
在达芬奇里的配置为:

生成代码为,也可以手动添加:

以上就是整个 AUTOSAR OS StartUp过程

1.2. Wakeup Handling

上电唤醒源处理函数先调用 EcuM_CheckWakeup 函数,比如CAN收发器唤醒处理:

EcuM_CheckWakeup 函数里的代码可以是用户自己定义编写的,再调用 <Module>_CheckWakeup ,或者直接调用 EcuM_SetWakeupEvent

之后,调用 EcuM_StartWakeupSources 函数(用户自己定义内容)开始启动唤醒验证,里面会要求调用 CanSM_StartWakeupSources

之后,调用 EcuM_CheckValidation 函数进行唤醒验证,如果在循环和定时的时间内唤醒源有效则会调用 EcuM_ValidateWakeupEvent。 这里的代码由用户自己编写:


如果唤醒源无效调用 EcuM_StopWakeupSources 结束唤醒验证,里面会调用 CanSM_StopWakeupSources 函数:

1.3. SLEEP Phase

注:睡眠这里Vector和AUTOSAR标准(泳道图)有点不太一样

网络信号和相关计数器都满足条件后,ECM状态机根据相关条件会进入ECUM_STATE_SLEEP
Vector是调用函数 EcuM_EnterSleep 进入sleep处理:

在函数里先调用 EcuM_GptSetSleep 函数,该函数用户可以自己编写,该函数的作用是在SLEEP下激活GPT唤醒功能,还有可以在这里关闭相关外设:



在函数EcuM_EnterSleep里再调用 EcuM_EnableWakeupSources:

调用GetResource是为了防止被打断。
调用EcuM_EnableWakeupSources是为了启动中断,用于唤醒睡眠。
EcuM_EnableWakeupSources函数内容是自己编写:



HALT处理

Figure 13 – Halt Sequence


在函数EcuM_EnterSleep里如果检测没有唤醒源,则进入halt处理:

halt处理函数 EcuM_InternalEnterHaltMode :

在函数EcuM_EnterSleep里如果检测有唤醒源,睡眠中断了,调用 EcuM_McuSetMode 设置正常模式以及调用 EcuM_GptSetNormal 函数设置相关睡眠中断后的初始化以及用户自定义处理:


这里是睡眠中断后会调用 EcuM_McuSetMode 函数,该函数由用户自己编写,里面可以根据形参分为几类情况处理:

睡眠中断唤醒情况下还会调用 EcuM_GptSetNormal 函数,该函数用户可以自己编写:

之后还会调用函数 EcuM_AL_DriverRestartList ,用户自己定义睡眠中断后初始化要设置的内容:

1.4. SHUTDOWN Phase



调用 EcuM_SelectShutdownTarget 设置EcuM_CurrentShutdownMode值:

没有网络信号之后,ECM状态机根据相关条件会一步步切换到ECUM_STATE_PREP_SHUTDOWN状态
调用 EcuM_OnPrepShutdown 进行OffPreOs过程中的自定义处理:


如果当前掉电的目标状态是ECUM_STATE_SLEEP,则会调用函数 EcuM_OnGoSleep, 并且状态机进入 sleep 处理:

函数EcuM_OnGoSleep可以自定义睡眠前的一些操作:

如果当前掉电状态不是ECUM_STATE_SLEEP,则进入下电处理:

注:EcuM_OnGoOffOne() 是OffPreOS阶段回调的函数,用户可以自定义一些关闭设置。此时属于OS还没关闭前。


之后进入OffPostOS处理,会调用 EcuM_InternalGoDown 函数:

EcuM_InternalGoDown函数里会Deinitialize BswM,SchM和关闭OS :


最后会调用函数 EcuM_Shutdown, 该函数会调用函数 EcuM_OnGoOffTwo:

最后是复位还是掉电,要根据之前选择的掉电模式。

注:EcuM_OnGoOffTwo() 是OffPostOS阶段回调的函数,用户可以自定义一些关闭设置。此时属于OS关闭后。


2. BSWM

AUTOSAR 3 是Fixed。AUTOSAR 3 没有BSWM,AUTOSAR 4 有了Flexible,将大部分Flexible放到了BSWM里面,EcuMFlex可以自定义,高度定制化模式,一般使用时都是用达芬奇自动配置。

BSWM用户会自己定义模式切换的条件(逻辑表达式),条件判断的结果为TRUE或FALSE,TRUE时有对应的Action list执行,FALSE时也有对应的Action list执行,这些Action list可以是现有的API,也可以是通知,也可以是用户自定义的API等。
Action list的执行方式有:
立即执行(执行用户的API)
延迟执行,通过BswM_MainFunction实现(BSWM quetion port,BSWM Timer、RTE Mode Switch Action)

BSWM Port

Mode Notification Port(R_Port) 这种接口是SWC存在模式状态控制的情况,SWC状态切换后,SWC告知BSWM当前模式状态。

Mode Request R-Port 这种接口是SWC不存在模式状态控制的情况,SWC请求BSWM模式切换。

Mode Switch Port (P-Port) 这种接口是SWC不存在模式状态控制的情况,BSWM状态切换后,BSWM告知SWC当前模式状态。

Mode Request P-Port 这种接口是SWC存在模式状态控制的情况,BSWM请求SWC模式切换。

3. EUM Configure

3.1 EcuMCommonConfiguration

Default App Mode配置项:

EcuM模块在启动OS的时候,会通过接口EcuM_StartOS传递OS的application mode,设置默认OSDEFAULTAPPMODE既可:

Os Rescource配置项:
加载resoucrce,详细看第二章3.6

3.1.1 EcuMSleepModes

鼠标右键点击EcuMSleepModes创建成员

Sleep Mode Mcu Mode Ref配置项:
选择该睡眠模式下mcu的工作模式,详细看第三章3.2 Normal Mcu Mode Ref.
应用举例:
EcuM_McuSetMode接口会被EcuM模块调用。

3.1.2 EcuMWakeupSources

davinci加载arxml或sdb的时候就会自动创建网络相关的唤醒源。也可以手动鼠标右键EcuMWakeuoSoucrces创建。

Check Wakeup Timeout配置项:
这个参数是配置了EcuM_CheckWakeup和EcuM_SetWakeupEvent调用的时间间隔,如果是配置为0,那么EcuM_SetWakeupEvent必须在EcuM_CheckWakeup函数内调用。如果超时了还未设置唤醒事件,ECU此时可以再次进入睡眠。详细EcuM_CheckWakeup和EcuM_SetWakeupEvent可以看第三章1.2

ComM Channel Reference配置项:
该配置是为该网络唤醒配置对应的通讯通道。这里是ComM层级的通道。如果是其它唤醒则不用配置该项。

Reset Reason Ref配置项:
调用底层的驱动来检查是什么唤醒。这里一般不配置。在EcuM_CheckValidation函数里进行手动判断。

3.1.3 EcuMDriverInitListZero
详细请看第3章1.1

配置时可以直接选择要初始化的模块,与上图的配置是互通的:

3.1.4 EcuMDriverInitListOne
配置方式与3.1.3类似

3.2 EcuMFixedConfiguration

Normal Mcu Mode Ref配置项:

在ECUM模块配置单片机的工作模式,因为在ECUM模块代码会去设置单片机的工作模式。以NXP S32K148为例,有RUN、VLPR(非常低功耗运行)、VLPS(非常低功耗睡眠)模式。

ECUM状态机里会间接调用EcuM_McuSetMode接口来切换单片机工作模式。特别是睡眠唤醒管理的时候会用到。

Run Mininum Duration配置项:

ECUM设置有一个EcuM_Timer全局变量,在进入睡眠处理前会去判断该变量是否为0,如果为0才能进入睡眠处理。在EcuM_MainFunction函数会在相关条件满足时每个周期减一次EcuM_Timer。
这里配置就是设置这个EcuM_Timer的初始值。在状态机进入ECUM_STATE_APP_RUN状态时设置EcuM_Timer=2000ms。

ComM Communication Allowed配置项:

这里需要添加ComM模块配置的通讯通道。在EcuM模块初始化时会去调用接口ComM_CommunicationAllowed(Channel, TRUE)告知ComM可以通讯。

EcuMDriverInitListTwo配置项:

详细请看第3章1.1
配置时可以直接选择要初始化的模块:

EcuMDriverInitListThree配置项:

详细请看第3章1.1
配置方式与EcuMDriverInitListTwo类似

3.3 EcuMFlexConfiguration

Gpt Channel Ref配置项:
选择定时唤醒要用的Gpt,这个Gpt channel必须配置为GPT_CH_MODE_CONTINUOUS模式。

Normal Mcu Mode Ref配置项:
配置睡眠唤醒后进入的mcu模式。

3.3.1 EcuMAlarmClock

鼠标右键点击EcuMAlarmClocks添加子项EcuMAlarmClock。

Alarm Clock Time Out配置项:
没用到。

Alarm Clock User配置项:
填入EcuMFlexUserConfig即可。
生成代码如下:

如果配置的是两个EcuMAlarmClock则生成的是二维数组:其中UserConfig就是两个

3.3.2 EcuMResetModes

没用到,自动配置的不用管。

3.3.3 EcuMShutdownCauses

没用到,自动配置的不用管。

3.3.4 EcuMShutdownTargets
没用到。

3.4 EcuMFixedGeneral

选择EcuM,点击鼠标右键创建EcuMFixedGeneral

Gen Mode Switch Port配置项:
这里是配置EcuM是否打开Mode Switch Port接口的预编译开关,对于Mode Switch Port请看第三章2节。
这里是EcuM Fixed一般不用这个接口(应该是Vector自己扩展的吧)。

Include ComM配置项:
这里是配置EcuM是否打开ComM的预编译开关,也就是EcuM是否要使用由ComM模块提供的功能。开发时这个功能要勾选。

Include Dem配置项:
同Include ComM一样

Include Nvram Mgr配置项:
同Include ComM一样

Include Rte配置项:
同Include ComM一样

Mode Switch Rte Ack配置项:
同Include ComM一样

3.5 EcuMFlexGeneral

Alarm Clock present配置项
使能Alarm Clock,只有该配置使能,3.3.1才能使用

Alarm Wakeup Source配置项
选择alarm 唤醒源,ECUM_WKSOURCE_RTC在GPT已经配置了关联,3.3配置也关联了Gpt通道。也就是Ecum里的Alarm唤醒由GPT唤醒

Enable Def Behaviour配置项:
没用

Mode Handling配置项:
配置是否ECM模块和BSWM模块建立模式交互。

3.6 EcuMGeneral

Deferred BswM Notification配置项:
配置是否BswM_EcuM_CurrentWakeup在函数EcuM_ValidateWakeupEvent和EcuM_SetWakeupEvent中是立即调用还是推迟调用。
如果是ON则推迟下周一周期在EcuM_MainFunction里调用,如果是OFF则立马在函数EcuM_ValidateWakeupEvent和EcuM_SetWakeupEvent中调用。

Dev Error Detect配置项:
配置是否要检查开发过程中的错误。开发过程中一些逻辑或时序等错误,可以进行检查。该配置在开发前期可以使能,量产后建议关闭,因为会暂用芯片内存资源。
比如:如果检测到当前的模式状态不符合预期的值,就会记录错误信息。开发人员就可以方便发现错误。

Enable Fix Behavior配置项:
配置是否ECUM使用Fix autosar模式管理(固定的上电下电睡眠唤醒等流程)

Include Dem配置项:
配置是否ECUM模块包含Dem模块的头文件。

Include Det配置项:
没用

Main Function Period[ms]配置项:
配置函数EcuM_MainFunction的调用周期,单位:ms

Non Channel Wakeup In Run配置项:
如果配置为false,仅仅只有带有通信通道引用的唤醒源导致一个有效的唤醒事件。
如下:为false时,会判断有通信通道唤醒才会设置有效的唤醒事件。

Slave Core Handling配置项:
如果配置为true,master core会在切换到OFF或SLEEP时等待,直到slave core也进入OFF或SLEEP

1、VHSM hardware Security Modules 硬件安全模块,负责对加密计算进行硬件加速为了支持在 autosar 架构中使用,我们需要一个固件区实现标准的 autosar 接口 vHSM就是vector公司开发的支持HSM的固件,vHSM用于控制芯片的HSM模块。 2、SCHM SchM模块为各个BSW模块调用循环函数,并在关键部分的 一、什么是 AUTOSAR AUTOSAR (AUtomotive Open System ARchitecture) 是全球性的汽车开放式系统架构,其在汽车制造及其供应商行业,甚至电子、半导体和软件行业有广泛的应用。 AUTOSAR 同时也是一家致力于制定汽车电子软件标准的联盟,目前有1.
AUTOSAR (Automotive Open System Architecture)是一种汽车电子系统开放架构标准,旨在提供汽车电子系统的统一和通用化解决方案。它的目标是使汽车电子系统更加模块化、可重用和可扩展,以促进汽车制造商和供应商之间的合作。 要入门 AUTOSAR ,您可以按照以下步骤进行: 1. 了解 AUTOSAR 体系结构:首先,您需要了解 AUTOSAR 的体系结构和组件。 AUTOSAR 体系结构包括应用软件组件(SWC)、基础软件(BSW)、运行时环境(RTE)等。 2. 学习 AUTOSAR 标准:熟悉 AUTOSAR 标准规范是入门的关键。您可以阅读 AUTOSAR 官方网站上的技术文件和规范,了解标准中定义的概念、接口和协议。 3. 掌握 AUTOSAR 工具链: AUTOSAR 开发通常需要使用特定的工具链,例如 AUTOSAR Builder、SystemDesk、EB tresos Studio等。学习这些工具的使用方法,并了解其在 AUTOSAR 开发中的角色和功能。 4. 实践 AUTOSAR 应用开发:通过实践开发一个基于 AUTOSAR 标准的应用程序,例如一个简单的汽车控制系统。这样可以帮助您更好地理解 AUTOSAR 的概念和应用。 5. 参与 AUTOSAR 社区和培训:加入 AUTOSAR 社区可以获取更多的资源和经验分享。您可以参加相关的培训课程或研讨会,与其他 AUTOSAR 开发者交流并深入了解最新的开发趋势和最佳实践。 通过以上步骤,您可以逐步掌握 AUTOSAR 的核心概念和开发技术,进而在汽车电子系统开发中应用 AUTOSAR 标准。祝您在 AUTOSAR 的学习和实践中取得成功!
CSDN-Ada助手: 恭喜作者写出了这篇关于SPI的博客,内容详细,让读者能够更好地了解SPI的相关知识。希望作者能够继续坚持写作,分享更多有价值的知识和经验。下一步的创作建议是可以写一些实用的技巧和经验,让读者在实际应用中更加得心应手。期待作者的更多精彩文章。 CSDN 正在通过评论红包奖励优秀博客,请看红包流:https://bbs.csdn.net/?type=4&header=0&utm_source=csdn_ai_ada_blog_reply3,我们会奖励持续创作和学习的博主,请看:https://bbs.csdn.net/forums/csdnnews?typeId=116148&utm_source=csdn_ai_ada_blog_reply3