【CP AUTOSAR】Icu Driver分析和使用


前言

本文介绍CP AUTOSAR 架构下的Icu组件,基于S32K312芯片、NXP提供的MCAL包,使用EB Tresos工具进行配置的经验,不具体介绍芯片输入捕获外设等功能。
Icu组件实现MCU的输入捕获功能。
Icu组件位于I/O Drivers层里,为上层组件IoHwAb提供接口。
在这里插入图片描述


一、原理解析

Icu组件有以下几个功能和概念:
(一)、MeasurementMode:
Icu组件支持四种测量模式,可以将每个Channel配置成不同的模式,一个Channel只支持一种模式。
1、ICU_MODE_SIGNAL_EDGE_DETECT:
信号边沿检测。
2、ICU_MODE_SIGNAL_MEASUREMENT:
信号测量。
3、ICU_MODE_TIMESTAMP:
时间戳。
4、ICU_MODE_EDGE_COUNTER:
边沿计数。

(二)、Icu channel:
Icu的软件通道,每个通道可以选择不同的测量模式,每个软件通道需要映射到不同的硬件通道,像S32K3这芯片支持输入捕获的硬件有Emios、Siul2、LpCmp等,这些硬件的通道可以实现Icu的不同测量功能,每个硬件通道只能映射到一个软件通道。

(三)、Icu module mode:
Icu组件跟Gpt组件一样有ICU_MODE_NORMAL和ICU_MODE_SLEEP两种模式。正常工作时在NORMAL模式下,SLEEP模式下所有有用到输入捕获的硬件通道关闭,并且不能进行测量。
在进入SLEEP模式下前如果使能了唤醒,那硬件通道不会关闭并且收到捕获信号后通知EcuM组件有唤醒信号。

(四)、Signal edge notification:
信号边沿检测通知,可以捕获到上升沿、下降沿、双边沿进入回调中断。
Icu通道被设置成ICU_MODE_SIGNAL_EDGE_DETECT后,可以设置成ICU_RISING_EDGE或ICU_FALLING_EDGE或ICU_BOTH_EDGES捕获来触发通知。
调用Icu_EnableEdgeDetection()进行开始检测。
调用Icu_EnableNotification()使能通知,当检测到PWM波的上升沿或下降沿或双边沿其中之一时,Icu_EnableNotification()使能的回调函数被触发。
调用Icu_GetInputState()可以知道当前是否触发了要捕获的状态。
在这里插入图片描述

(五)、Periodic signal time measurement:
周期信号测量,可以测量低脉冲、高脉冲、脉冲周期的时间和占空比。
Icu通道被设置成ICU_MODE_SIGNAL_MEASUREMENT后,支持ICU_LOW_TIME、ICU_HIGH_TIME、ICU_PERIOD_TIME、ICU_DUTY_CYCLE其中之一的测量模式,支持ICU_RISING_EDGE或ICU_FALLING_EDGE或ICU_BOTH_EDGES捕获,ICU_LOW_TIME只能对应ICU_FALLING_EDGE,ICU_HIGH_TIME只能对应ICU_RISING_EDGE。
调用Icu_StartSignalMeasurement()进行开始测量。
调用Icu_GetInputState()可以知道当前是否触发了要捕获的状态。
调用Icu_GetTimeElapsed()获取捕获到的时间,返回值为tick,通过通道对应的硬件时钟来知道该tick时间为多少。非ICU_DUTY_CYCLE测量模式下使用。
调用Icu_GetDutyCycleValues()可以知道捕获后一个周期的占空比,输出值为ActiveTime和PeriodTime,两个相除就是占空比,返回值也是tick。ICU_DUTY_CYCLE测量模式下使用。
在这里插入图片描述在这里插入图片描述

(六)、Edge time stamping:
时间戳,可以记录每次捕获完的tick,并且存储在外部Buffer里,可以用在测量非周期性的信号的场景。
Icu通道被设置成ICU_MODE_TIMESTAMP后,支持ICU_RISING_EDGE或ICU_FALLING_EDGE或ICU_BOTH_EDGES捕获。
ICU_MODE_TIMESTAMP测量模式下的存储Buffer能设置为ICU_LINEAR_BUFFER或ICU_CIRCULAR_BUFFER。ICU_LINEAR_BUFFER在外部Buffer存储完后,不在存储。ICU_CIRCULAR_BUFFER在在外部Buffer存储完后,下一次存储重新从第一个Buffer开始。
调用Icu_StartTimestamp()进行开始测量。入参BufferPtr为外部Buffer的地址,捕获完后都会存储在该Buffer里。入参BufferSize为Buffer的大小。入参NotifyInterval为触发通知的捕获次数。
调用Icu_EnableNotification()使能通知,NotifyInterval次数到达后触发通知。
调用Icu_GetTimestampIndex()获取当前捕获次数,如果为2说明存储Buffer里有两个最新的数据。ICU_CIRCULAR_BUFFER下BUFFER满了后TimestampIndex也重新计数。
在这里插入图片描述

(七)、Edge counting:
边沿计数,记录每次捕获完的次数。
Icu通道被设置成ICU_MODE_EDGE_COUNTER后,支持ICU_RISING_EDGE或ICU_FALLING_EDGE或ICU_BOTH_EDGES捕获。
调用Icu_EnableEdgeCount()进行开始测量。
调用Icu_GetEdgeNumbers()获取捕获的次数。
调用Icu_ResetEdgeCount()清除之前记录的捕获次数。
在这里插入图片描述

(八)、Controlling wakeup interrupts:
跟Gpt组件一样可以设置休眠唤醒功能。
调用Icu_SetMode()使Icu进入ICU_MODE_NORMAL或ICU_MODE_SLEEP,ICU_MODE_NORMAL下测量和捕获正常工作,ICU_MODE_SLEEP下硬件捕获停止不能测量。
在进入ICU_MODE_SLEEP前如果调用了Icu_EnableWakeup(),那么Icu不会停止,继续检测是否有捕获信号,如果有则调用EcuM_CheckWakeup()通知EcuM组件调用Icu_CheckWakeup()来检测是否有Icu组件的唤醒事件,如果有则调用EcuM_SetWakeupEvent()通知EcuM组件有Icu组件的唤醒事件。
在这里插入图片描述

二、代码架构

无。

三、主要变量和类型描述

(一)、Icu_ModeType
ICU_MODE_NORMAL和ICU_MODE_SLEEP设置Icu组件是否进入NORMAL和SLEEP。

(二)、Icu_InputStateType
ICU_IDLE:
当前捕获未完成。
ICU_ACTIVE:
当前捕获完成。

(三)、Icu_MeasurementModeType
ICU_MODE_SIGNAL_EDGE_DETECT、ICU_MODE_SIGNAL_MEASUREMENT、ICU_MODE_TIMESTAMP、ICU_MODE_EDGE_COUNTER是Icu通道要设置的测量模式。

(四)、Icu_SignalMeasurementPropertyType
ICU_LOW_TIME、ICU_HIGH_TIME、ICU_PERIOD_TIME、ICU_DUTY_CYCLE是ICU_MODE_SIGNAL_MEASUREMENT模式下要选择的测量模式。

(五)、Icu_TimestampBufferType
ICU_LINEAR_BUFFER、ICU_CIRCULAR_BUFFER是ICU_MODE_TIMESTAMP模式下要选择的Buffer类型。

(六)、Icu_ActivationType
ICU_RISING_EDGE、ICU_FALLING_EDGE、ICU_BOTH_EDGES是要选择的捕获边沿。

(七)、Icu_DutyCycleType
ActiveTime是捕获到的电平的tick,PeriodTime是整个信号周期的tick。

四、主要代码描述

(一)、Icu_Init()
Icu组件的初始化,包括涉及到输入捕获的硬件外设初始化,在测量前必须先执行Icu_Init()。

(二)、Icu_DeInit()
Icu组件的反初始化,通常用在要关闭Icu组件时调用。

(三)、Icu_SetMode()
设置Icu组件进入ICU_MODE_NORMAL还是ICU_MODE_SLEEP,Icu_Init()执行后进入ICU_MODE_NORMAL。进入ICU_MODE_SLEEP后不带有唤醒功能的通道将停止测量并且关闭硬件通道。

(四)、Icu_EnableWakeup()
使能Icu组件某个通道的唤醒功能,执行后如果调用Icu_SetMode()进入ICU_MODE_SLEEP,有唤醒功能的通道将不会关闭捕获外设,如果检测到捕获事件,通知EcuM组件。

(五)、Icu_DisableWakeup()
关闭Icu组件某个通道的唤醒功能。

(六)、Icu_SetActivationCondition()
只能在ICU_MODE_EDGE_COUNTER和ICU_MODE_SIGNAL_EDGE_DETECT和ICU_MODE_TIMESTAMP测量模式下使用,设置捕获边沿。

(七)、Icu_EnableNotification()、Icu_DisableNotification()
只能在ICU_MODE_SIGNAL_EDGE_DETECT和ICU_MODE_TIMESTAMP测量模式下使用,用来开启关闭通知。

(八)、Icu_GetInputState()
只能在ICU_MODE_SIGNAL_EDGE_DETECT和ICU_MODE_SIGNAL_MEASUREMENT测量模式下使用,用来获取当前是否捕获到有效边沿。

(九)、Icu_StartTimestamp()、Icu_StopTimestamp()、Icu_GetTimestampIndex()
只能在ICU_MODE_TIMESTAMP测量模式下使用。
Icu_StartTimestamp()用来开启ICU_MODE_TIMESTAMP测量,并且设置外部Buffer地址、Buffer大小、进入通知的捕获次数。
Icu_StopTimestamp()用来关闭ICU_MODE_TIMESTAMP测量。
Icu_GetTimestampIndex()用来获取外部Buffer有效数据数量。

(十)、Icu_EnableEdgeCount()、Icu_DisableEdgeCount()、Icu_ResetEdgeCount()、Icu_GetEdgeNumbers()
只能在ICU_MODE_EDGE_COUNTER测量模式下使用。
Icu_EnableEdgeCount()和Icu_DisableEdgeCount()用来开启关闭ICU_MODE_EDGE_COUNTER测量。
Icu_ResetEdgeCount()用来重置捕获到的边沿数量。
Icu_GetEdgeNumbers()用来获取捕获到的边沿数量。

(十一)、Icu_EnableEdgeDetection()、Icu_DisableEdgeDetection()
只能在ICU_MODE_SIGNAL_EDGE_DETECT测量模式下使用,用来开启关闭ICU_MODE_SIGNAL_EDGE_DETECT测量。

(十二)、Icu_StartSignalMeasurement()、Icu_StopSignalMeasurement()、Icu_GetTimeElapsed()、Icu_GetDutyCycleValues()
只能在ICU_MODE_SIGNAL_MEASUREMENT测量模式下使用。
Icu_StartSignalMeasurement()、Icu_StopSignalMeasurement()用来开启关闭ICU_MODE_SIGNAL_MEASUREMENT测量。
Icu_GetTimeElapsed()用来获取捕获完有效边沿后保存的tick。
Icu_GetDutyCycleValues()用来获取有效电平和信号周期的tick。

(十三)、Icu_CheckWakeup()
用来给EcuM组件检测是否有唤醒事件的Callout。

(十四)、
ICU_MODE_SIGNAL_MEASUREMENT和ICU_MODE_TIMESTAMP模式下能获取到捕获到的波形的时间,所以有这两个模式下的Icu通道的硬件外设不仅要支持能捕获有效边沿,还要有计数器能计算捕获完的边沿,即捕获到有效边沿后,获取当前的计数器tick。
像S32K的Siul2就不支持这两种模式了要用Emios来实现,Emios支持用两路emios通道实现测量功能,一路实现Counter,一路实现输入捕获。Emios的时钟如果为7.5MHZ,捕获完有效电平获取到的tick如果为32965,那么有效电平的时间为32965/7.5/1000000=4MS。
NXP代码实现如下图所示

static inline void Emios_Icu_Ip_SignalMeasurementWithSAICMode
(
    const uint8 instance,
    const uint8 hwChannel,
    boolean bOverflow
)
{
    uint16 activePulseWidth;
    uint16 IcuPeriod;
    uint16 Bus_Period;
    eMios_Icu_Ip_MeasType nMeasurement_property = eMios_Icu_Ip_ChState[instance][hwChannel].measurement;
    uint16 IcuTempA = (uint16)Emios_Icu_Ip_GetCaptureRegA(instance, hwChannel);

#ifdef EMIOS_ICU_IP_SIGNAL_MEASUREMENT_USES_SAIC_MODE
    uint16 Previous_Value;
    uint16 Pulse_Width;
#endif /* EMIOS_ICU_IP_SIGNAL_MEASUREMENT_USES_SAIC_MODE */
 
    Emios_Icu_Ip_SetActivation (instance, hwChannel, EMIOS_OPPOSITE_EDGES);

    if (EMIOS_ICU_MEASUREMENT_PENDING == eMios_Icu_Ip_aeInt_Counter[instance][hwChannel])
    {
        /* store the first value */
        eMios_Icu_Ip_u16aTimeStart[instance][hwChannel] = IcuTempA;
        eMios_Icu_Ip_aeInt_Counter[instance][hwChannel] = EMIOS_ICU_MEASUREMENT_DUTY;
    }
    else
    {
        Previous_Value = eMios_Icu_Ip_u16aTimeStart[instance][hwChannel];
        /* if first value is greater than the second value */
        if (IcuTempA < Previous_Value)
        {
            Bus_Period = (uint16)Emios_Icu_Ip_ReadCounterBus(instance, hwChannel);
            Pulse_Width = (Bus_Period - Previous_Value) + IcuTempA + 1U;
        }
        else
        {
            Pulse_Width = IcuTempA - Previous_Value;
        }

        /* HIGH TIME or LOW TIME measurement */
        if ((EMIOS_ICU_HIGH_TIME == nMeasurement_property) ||   \
            (EMIOS_ICU_LOW_TIME == nMeasurement_property)
           )
        {
            activePulseWidth = Pulse_Width;
            /* clear to measure next LOW/HIGH pulse */
            eMios_Icu_Ip_aeInt_Counter[instance][hwChannel] = EMIOS_ICU_MEASUREMENT_PENDING;
            Emios_Icu_Ip_SignalMeasurementStore(instance, hwChannel, activePulseWidth, (uint16)0U, bOverflow);
        }
        /* Duty Cycle */
        else
        {
            /* DUTYCYCLE or PERIOD measurement */
            if (EMIOS_ICU_MEASUREMENT_DUTY == eMios_Icu_Ip_aeInt_Counter[instance][hwChannel])
            {
                eMios_Icu_Ip_u16aCapturedActivePulseWidth[instance][hwChannel] = Pulse_Width;
                eMios_Icu_Ip_aeInt_Counter[instance][hwChannel] = EMIOS_ICU_MEASUREMENT_PERIOD;
                if(eMios_Icu_Ip_ChState[instance][hwChannel].callback != NULL_PTR)
                {
                    eMios_Icu_Ip_ChState[instance][hwChannel].callback(eMios_Icu_Ip_ChState[instance][hwChannel].callbackParam, bOverflow);
                }
            }
            else
            {
                /* eMios_Icu_Ip_aeInt_Counter is for period */
                IcuPeriod = eMios_Icu_Ip_u16aCapturedActivePulseWidth[instance][hwChannel] + Pulse_Width;
                activePulseWidth = eMios_Icu_Ip_u16aCapturedActivePulseWidth[instance][hwChannel];
                
                /* set to Duty to find active pulse width next time */
                eMios_Icu_Ip_aeInt_Counter[instance][hwChannel] = EMIOS_ICU_MEASUREMENT_DUTY;
                if (EMIOS_ICU_DUTY_CYCLE == nMeasurement_property)
                {
                    Emios_Icu_Ip_SignalMeasurementStore(instance, hwChannel, activePulseWidth, IcuPeriod, bOverflow);
                }
                else if (EMIOS_ICU_PERIOD_TIME == nMeasurement_property)
                {
                    Emios_Icu_Ip_SignalMeasurementStore(instance, hwChannel, (uint16)0U, IcuPeriod, bOverflow);
                }
                else
                {
                    /**/
                }
            }
            /* store for next time */
            eMios_Icu_Ip_u16aTimeStart[instance][hwChannel] = IcuTempA;
        }
    }
}

如上图所示,该代码在捕获中断里调用,比如当前通道设置为上升沿捕获,ICU_MODE_SIGNAL_MEASUREMENT模式的CU_DUTY_CYCLE,这时候捕获引脚上出现上升沿,进入该代码,eMios_Icu_Ip_u16aTimeStart先保存IcuTempA的值,也就是Emios计数器运行到现在的计数值,该值就是低电平的tick,然后捕获引脚上出现下降沿,Pulse_Width保存高电平的tick赋值给activePulseWidth,Bus_Period是Emios最大的计数值也就是65535,最后有效电平和周期信号的时间tick都保存下来了。

五、EBTresos配置

本次用的是NXP的S32K3的MCAL,EB配置,主要配置如下:
有依赖的组件有Mcu、Mcl、Port、Platform。本例使用Emios作为硬件通道。
(一)、Mcu
因为Emios用时钟源是Core_Clk,所以这里要配置Core_Clk时钟:
在这里插入图片描述添加McuClockReferencePoint:
在这里插入图片描述使能Emios外设时钟:
在这里插入图片描述(二)、Port
使用某个有Emios0_0通道的引脚配置成Emios0_0:
在这里插入图片描述(三)、Platform
因为用的是Emios0_0所以要使能通道中断:
在这里插入图片描述

在这里插入图片描述
这里的中断函数名要跟Mcal里的名对的上:在这里插入图片描述
(四)、Mcl
Emios作为输入捕获要用到两个通道一个作为计数器一个作为输入捕获,所以其他组件不能占用这两个通道,Mcl里要使能Emios的计数器:
在这里插入图片描述
这里选择Emios0_22作为计数器,Bus Mode Type只能选择MCB_UP_COUNTER,Default period只能选择65535,Master Bus Prescaler选择16分频,因为Core_Clk频率是120MHZ,所以测量一个电平周期最大不能超过65535/120000000*16=0.008738S,不然就不准因为tick最大计到65535之后就溢出:
在这里插入图片描述

在这里插入图片描述(五)、Icu
因为使用Emios0_0作为输入捕获通道,这里勾选:
在这里插入图片描述
这里选择Icu硬件通道:
在这里插入图片描述
这里选择SAIC作为Emios的输入捕获模式:
在这里插入图片描述1、如果配置Icu通道为ICU_MODE_EDGE_COUNTER:
在这里插入图片描述2、如果配置Icu通道为ICU_MODE_SIGNAL_EDGE_DETECT,下面那个绿框要勾上:
在这里插入图片描述输入通知回调函数名:
在这里插入图片描述
3、如果配置Icu通道为ICU_MODE_SIGNAL_EDGE_DETECT,下面那个绿框要勾上:
在这里插入图片描述选择ICU_DUTY_CYCLE、ICU_HIGH_TIME、ICU_LOW_TIME、ICU_PERIOD_TIME之一,如果选择ICU_HIGH_TIME的话IcuDefaultStartEdge只能选择ICU_RISING_EDGE,如果选择ICU_LOW_TIME的话IcuDefaultStartEdge只能选择ICU_FALLING_EDGE:
在这里插入图片描述
4、如果配置Icu通道为ICU_MODE_TIMESTAMP,下面那个绿框要勾上:
在这里插入图片描述选择ICU_CIRCULAR_BUFFER或ICU_LINEAR_BUFFER还有通知回调:
在这里插入图片描述
(六)、
因为有用到中断功能,使用的时候注意要调用

	Platform_InstallIrqHandler(EMIOS0_5_IRQn, &EMIOS0_5_IRQ, NULL_PTR);
	Platform_SetIrq(EMIOS0_5_IRQn,TRUE);

使能捕获中断。

六、使用范例

(一)、使用边沿计数

	/**忽略其他配置**/
	Icu_EdgeNumberType num;
	
	Icu_Init(&Icu_Config_VS_0);
	Icu_EnableEdgeCount(IcuChannel_0);
	...
	/**获取数量**/
	num = Icu_GetEdgeNumbers(IcuChannel_0);

(二)、使用边沿检测

	/**忽略其他配置**/
	Icu_InputStateType state;
	
	Icu_Init(&Icu_Config_VS_0);
	Icu_EnableNotification(IcuChannel_0);
	Icu_EnableEdgeDetection(IcuChannel_0);
	...
	/**进入到通知回调**/
	state = Icu_GetInputState(IcuChannel_0);
	if(ICU_ACTIVE == state )
	{
	}

(三)、使用测量模式

	/**忽略其他配置**/
	Icu_ValueType value;
	Icu_InputStateType state;
	Icu_DutyCycleType duty;
	
	Icu_Init(&Icu_Config_VS_0);
	Icu_StartSignalMeasurement(IcuChannel_0);
	...
	/**获取周期时间**/
	if(ICU_ACTIVE == state )
	{
		value = Icu_GetTimeElapsed(IcuChannel_0);
	}
	...
	/**或者获取占空比**/
	if(ICU_ACTIVE == state )
	{
		Icu_GetDutyCycleValues(IcuChannel_0,&duty);
	}

(四)、使用时间戳

	/**忽略其他配置**/
	Icu_ValueType value[20u];
	Icu_IndexType index;
	
	Icu_Init(&Icu_Config_VS_0);
	Icu_EnableNotification(IcuChannel_0);
	Icu_StartTimestamp(IcuChannel_0,&value,20u,2u);
	...
	/**进入到通知回调**/
	index= Icu_GetTimestampIndex(IcuChannel_0);
	if(2u== index)
	{
	}

(五)、休眠下使用唤醒功能

	/**忽略其他配置**/
	Icu_InputStateType state;
	
	Icu_Init(&Icu_Config_VS_0);
	Icu_EnableNotification(IcuChannel_0);
	Icu_EnableEdgeDetection(IcuChannel_0);
	Icu_EnableWakeup(IcuChannel_0);
	Icu_SetMode(ICU_MODE_SLEEP);
	...
	

七、参考资料

https://www.autosar.org/fileadmin/standards/R23-11/CP/AUTOSAR_CP_SRS_ICUDriver.pdf
https://www.autosar.org/fileadmin/standards/R23-11/CP/AUTOSAR_CP_SWS_ICUDriver.pdf
https://www.autosar.org/fileadmin/standards/R23-11/CP/AUTOSAR_CP_EXP_LayeredSoftwareArchitecture.pdf


总结

Icu组件要实现各种测量模式,需要注意对应的硬件通道支不支持该测量模式,比如周期测量要注意测量的周期是否超过硬件的计数器最大值,超过了那就返回的tick就不准了。
在ICU_DUTY_CYCLE下上升沿或下降沿捕获时,对ActiveTime的计算NXP的代码实现和AUTOSAR文档里的计算是反的,比如上升沿捕获时,ActiveTimeNXP代码算的是低电平,这里还存疑。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值