本篇文章首先从理论讲起,基于《MC-ISAR_TC3xx_UM_Adc.pdf》介绍了ADC模块的理论知识,然后详细介绍了在TC397平台使用EB-tresos对ADC驱动模块进行配置与调试的实战过程,帮助第一次接触这个模块的读者能够更快的上手来实现符合自己项目要求的开发工作,完成模拟量到数字量的转换。文章分为软件/硬件触发两种方式介绍了配置以及调试的过程,较为全面的介绍了ADC这个模块的使用原理。
目录
概述
ADC驱动程序负责提供AUTOSAR规范涉及的标准模数转换服务。我们可以使用EB提供的用户界面选项来配置AUTOSARADC规范中描述的驱动程序参数。它还提供额外的参数来配置EVADC(Enhanced Versatile Analog-to-Digital Converter,底层的模式转换IP)的各种功能块。驱动程序提供的API具有多核能力,即可以从多个核同时调用它们。下图为ADC模块与软硬件的接口关系图。
ADC主要硬件设备
EVADC
ADC驱动程序使用EVADC将模拟信号转换为12位数字值(分辨率是固定的)。驱动程序使用的关键硬件功能特性是:
- 基于软件的触发
- 定时器与事件触发
- 范围检查
- 仲裁器的优先级机制
- 节能模式
- 启动校准功能
- 跨硬件组的同步传输功能
ADC驱动程序使用EVADC提供的以下三种硬件事件。
- 结果事件(Result event):当配置了结果处理的DMA模式时,结果事件触发通过DMA传递转换结果。
- 请求源事件(Request source event):当配置了结果处理的中断模式时,请求源事件触发转换结果的传递和组状态的更新。
- 通道事件(Channel event):用于限制检查已启用的AdcChannel组。
CONVERTER
ADC驱动程序依赖于为EVADC提供时钟同步信号的Converter。时钟同步信号使所有EVADC硬件组的模拟时钟同步。同步信号频率由ADC和DSADC驱动器使用,而生成信号的配置由MCU模块完成。
EICR/IGCR
ADC驱动程序依赖于ERU来实现基于硬件事件的触发器和选通特性。驱动程序使用从ERU的事件触发器逻辑块派生的触发器事件。ERU由ADC、DSADC和ICU驱动程序使用。通过MCU模块的配置接口,预留各驱动程序使用的EICR和IGCR通道。通道使用的SFRs由驱动程序编程。由于多个通道共享SFRs,为了避免损坏其他通道的数据,驱动程序使用通道特定的掩码对这些SFRs进行原子编程。数字端口的毛刺滤波器配置由MCU模块完成。
ADC依赖的硬件外围设备
CCU6(Capture Compare Unit 6)
ADC驱动器可以通过CCU6事件来触发AdcChannel组的转换。在这种情况下,用户软件必须直接配置CCU6通道以生成事件,EVADC触发线必须配置为通过CCU6事件触发。CCU6的T12或T13通道只由CCU6用户使用。虽然T12或13通道的用户很多,但一个通道只分配给一个驱动器并由一个驱动器使用。ADC驱动程序实际使用的是计时器通道产生的事件,该事件被用作转换模拟通道的触发器。
DMA
ADC驱动程序可以使用DMA(结果处理为DMA模式)传输转换结果。DMA通道由DMA用户专用,用于ADC的DMA通道必须由应用程序通过DMA提供的接口保留和配置。
GTM
ADC驱动程序可以通过GTM来实现基于时间的触发和选通特性。驱动程序使用比较匹配事件和通道输出信号来启动和停止ADC通道组的转换。GTM用于ADC、PWM、OCU、WDG、GPT和ICU驱动器。各驱动程序所使用的GTM资源通过MCU模块的配置接口进行保留,避免资源冲突。ADC驱动器使用TOM或ATOM通道来产生触发和门控信号。ADC驱动程序调用MCU驱动程序的APl来配置与TOM或ATOM有关的所有SFRs,以产生触发和关断信号。如果使用外部PWM信号触发或关闭AdcChannel组,则用户必须配置GTM通道。ADC驱动程序使用来自GTM的以下硬件事件:
- 比较匹配事件:用于启动转换。
- 通道状态输出:根据定时器输出启动/停止转换。
PORT
模拟信号通过模拟引脚焊盘连接到EVADC转换器,转换器的外部触发事件通过数字引脚焊盘连接,这些可以通过PORT驱动程序进行配置和启用。
SCU
ADC驱动程序依赖于SCU的IP进行时钟、ENDINIT和复位功能。驱动器需要fSPB和fADC时钟信号才能运行。SCU为所有外设提供时钟,MCU驱动程序负责时钟树的配置。为了避免由于同时写入而产生的冲突,使用MCALLIB API对所有受ENDINIT保护的寄存器执行更新。
SRC
ADC驱动程序依赖于中断路由器,它需要根据请求源事件或通道事件向CPU发出中断,来表示一系列ADC通道转换的结束。ADC驱动程序在这些事件的中断服务例程中将转换结果从结果寄存器复制到应用程序缓冲区。中断路由器由IRQ驱动程序或应用软件配置。ADC驱动程序负责清除SRC寄存器中挂起的中断请求。中断路由器引起的中断事件由CPU和DMA进行处理,ADC驱动程序提供中断处理程序作为软件接口,必须由ISR调用。
集成要点
与AUTOSAR基础软件集成
ECU Manager模块是AUTOSAR基础软件的一部分,用于管理ECU的常见方面。具体地说,在MCAL中,EcuM用于初始化和去初始化ECU相关外设。
每个打算使用 ADC驱动程序服务的CPU核心都必须调用ADC驱动程序的初始化。在调用从属核心的初始化之前,必须完成主核心的初始化。所有从属核心可以并行执行初始化。如果将所有ADC资源分配给主核心,则只需单独调用主核心的初始化即可。EVADC还要求在初始化后进行启动校准,下图显示了带有启动校准的初始化序列示例。
应从每个使用ADC驱动程序服务的CPU核心中调用ADC驱动程序的初始化解除。在调用主核心的初始化解除之前,必须完成从所有从属核心的初始化解除。从属核心可以并行执行初始化解除。如果将所有ADC资源分配给主核心,则只需从主核心调用初始化解除即可。下图显示了一个示例初始化解除序列。
存储映射是AUTOSAR的一个概念,它允许将文本、变量、常量和配置数据重新定位到用户特定的内存区域。为了实现这一点,驱动程序中所有可重定位的元素都封装在不同的内存段宏中。这些宏在Adc_MemMap.h文件中定义。
Adc MemMap.h文件作为一个模板代码提供在MCAL软件包中。集成商必须在内存部分宏中放置适当的编译器命令,确保元素被重新定位到正确的内存区域。下面展示的是一个Memory mapping示例。
/**** GLOBAL RAM DATA -- NON-CACHED LMU ****/
if defined ADC_START_SEC_VAR_CLEARED_ASIL_B_GLOBAL_UNSPECIFIED
/*****User pragmas here for Non-cached LMU*****/
#undef ADC_START_SEC_VAR_CLEARED_ASIL_B_GLOBAL_UNSPECIFIED
#undef MEMMAP_ERROR
#elif defined ADC_STOP_SEC_VAR_CLEARED_ASIL_B_GLOBAL_UNSPECIFIED
#ifdef _TASKING_C_TRICORE_
/*****User pragmas here for Non-cached LMU*****/
#undef ADC_STOP_SEC_VAR_CLEARED_ASIL_B_GLOBAL_UNSPECIFIED
#undef MEMMAP_ERROR
/**** CORE[x] CONFIG DATA -- PF[x] ****/ /*[x] = 0..5*/
#elif defined ADC_START_SEC_CONFIG_DATA_ASIL_B_CORE[x]_UNSPECIFIED
/*****User pragmas here for PF[x]*****/
#undef ADC_START_SEC_CONFIG_DATA_ASIL_B_CORE0_UNSPECIFIED
#undef MEMMAP_ERROR
#elif defined ADC_STOP_SEC_CONFIG_DATA_ASIL_B_CORE0_UNSPECIFIED
/*****User pragmas here for PF[x]*****/
#undef ADC_STOP_SEC_CONFIG_DATA_ASIL_B_CORE0_UNSPECIFIED
#undef MEMMAP_ERROR
/**** CODE -- PF[x] ****/
#elif defined ADC_START_SEC_CODE_ASIL_B_GLOBAL
/*****User pragmas here for PF[x]*****/
#undef ADC_START_SEC_CODE_ASIL_B_GLOBAL
#undef MEMMAP_ERROR
#elif defined ADC_STOP_SEC_CODE_ASIL_B_GLOBAL
/*****User pragmas here for PF[x]*****/
#undef ADC_STOP_SEC_CODE_ASIL_B_GLOBAL
#undef MEMMAP_ERROR
#endif
#if defined MEMMAP_ERROR
#error "Adc_MemMap.h, wrong pragma command"
#endif
DET模块是AUTOSAR基础软件的一部分,它处理由BSW模块报告的所有开发和运行时错误。ADC驱动程序通过 Det_ReportError()将所有开发错误报告给DET模块,并通过Det_ReportRuntimeError()将所有的运行时错误报告给DET。ADC驱动器的用户必须处理通过Det_ReportError()或Det _ReportRuntimeError()报告给DET模块的错误。Det.h和Det.c文件在MCAL包中作为一个临时代码提供,需要在集成阶段用完整的DET模块替换。
DEM模块是AUTOSAR基础软件的一部分,它处理由BSW模块报告的所有故障。ADC驱动程序通过AUTOSAR 4.2.2的Dem_ReportErrorStatus()和AUTOSAR4.4.0的Dem_SetEventStatus将所有故障报告给DEM模块。ADC驱动器的用户必须处理通过Dem_ReportErrorStatus()或Dem_SetEventStatus()向DEM模块报告的所有故障(失败/通过)。在MCAL包中,Dem.h和Dem.c文件作为一个空代码提供,需要在集成阶段用完整的DEM模块替换。
SchM模块是RTE的一部分,用于管理BSW Scheduler。ADC驱动程序使用SchM_Adc.h文件中定义的专用区域来保护SFR和变量不受不同线程并发访问的影响。ADC驱动程序需要使用SchM的类型如下所示:
- KernelData
- SrcRegAccess
SchM_Adc.h和SchM_Adc.c文件作为示例代码在MCAL包中提供,需要由集成商更新。用户必须实现由ADC驱动程序定义的SchM函数,作为调用API的CPU的中断挂起/恢复SchM函数的示例实现如下:
/**** Sample implementation of SchM_Adc.c ****/
#include "Os.h"
void SchM_Enter_Adc_KernelData(void)
{
/* Start of Critical Section */
SuspendAllInterrupts(); /* Suspend CPU core interrupt */
}
void SchM_Exit_Adc_KernelData(void)
{
/* End of Critical Section */
ResumeAllInterrupts(); /* Resume CPU core interrupt */
}
void SchM_Enter_Adc_SrcRegAccess(void)
{
/* Start of Critical Section */
SuspendAllInterrupts(); /* Suspend CPU core interrupt */
}
void SchM_Exit_Adc_SrcRegAccess(void)
{
/* End of Critical Section */
ResumeAllInterrupts(); /* Resume CPU core interrupt */
}
ADC驱动程序将通过Mcal_ReportSafetyError()报告所有检测到的安全错误。驱动仅执行安全错误的检测和报告。报告错误的处理应由用户进行。Mcal_ReportSafetyError()在Mcal_SafetyError.c和Mcal_SafetyError.h 文件中作为空函数代码提供,集成商必须更新以处理报告的错误。
驱动程序不执行任何通知。但是,它确实通过通知函数报告了组转换的完成。这些通知函数可以由用户在EB tresos中为每个AdcChannelGroup单独配置。
操作系统或应用程序必须确保在SR寄存器中配置正确的服务类型和中断优先级。中断的启用和禁用也必须由操作系统或应用程序来管理。MCAL包提供的操作系统文件只是一个示例代码,必须由集成商进行更新,包含用于所需功能的实际操作系统文件。
多核操作
ADC驱动程序支持从所有CPU核心并行执行其API。用户应使用Resource Manager模块在预编译时将EVADC的资源分配给CPU核心。在驱动程序中考虑多核的关键点如下:
- ADC硬件组可以在预编译时分配到CPU核心,例如,EVADC_G0和EVADC_G1可以分配到核心-2,EVADC_G9分配到核心-3,EVADC_G3分配到核心-0。
- AdcChannel组属于特定硬件组。因此,属于某个硬件组的所有AdcChannel组都被分配到与该硬件组相同的核心。
- 必须确保在调用API时作为参数传递的 AdcChannel组属于调用API的同一核心。如果调用API时核心组和 AdcChannel组不匹配,则将提出DET。
- 硬件组引起的中断必须由分配给硬件组的CPU核心来处理。
- 用户应将常量、变量和配置数据定位到正确的内存空间。内存区段标记应分为GLOBAL(所有CPU核心的通用)和CORE[x](特定于CPU核心)。用户应考虑以下内容,以确保驱动程序具有更好的性能:
- Code section:ADC驱动程序的可执行代码被放置在单个MemMap区段下,它可以被重新定位到任何PFlash区域。
- Data section:标记为特定于核心的RAM变量存储区应重新定位到同一核心的DSPR/DLMU中。标记为全局的部分应重新定位到非缓存LMU区域。在无LMU的设备中,可以使用非缓存DSPR。
- Configuration data and constants:标记为特定于某个核心的配置数据部分应重新定位到同一核心的PFlash。标记为全局部分的配置数据应重新定位至主核心的PFLASH。
MCU support
ADC驱动程序依赖于MCU驱动程序进行时钟配置和定时器IP等相关服务。只有在MCU初始化完成后才能启动ADC驱动程序的初始化。在EB tresos中配置MCU驱动程序时,必须考虑以下几点:
- 如果应用程序使用多个模拟转换器,则必须使用CONVCTRL块,CONVCTRL的配置和编程由单片机驱动程序直接管理。
- ADC通道组可能需要由GTM生成的定时器事件来触发转换。ADC驱动器使用的GTM通道必须在 MCU配置中保留给 ADC专用。
- ADC通道组可能需要由为EICR-IGCR生成的硬件事件来触发转换。ADC驱动程序使用的EICR-IGCR通道必须在MCU配置中保留,仅供ADC专用。
Port support
PORT驱动程序配置了整个微控制器的端口引脚。用户必须通过PORT配置来配置ADC驱动程序使用的端口引脚,并在调用ADC初始化之前初始化端口引脚。
DMA support
ADC驱动程序可以这样配置,即转换结果通过DMA移动引擎直接从结果寄存器传输到应用程序缓冲区。可以使用DMA驱动程序的API和配置参数来实现这一点。启用DMA模式是整个模块的功能。启用时,优先级和排队不支持。来自EVADC的结果寄存器事件触发服务请求,该请求由DMA处理。DMA将转换结果从结果寄存器传输到应用程序缓冲区。当AdcChannel组的最后一个通道的转换完成时,将触发结果事件。请求源事件也会被触发,该请求由CPU处理并更新AdcChannel组的当前状态。用户在使用此模式时,必须保证以下几点:
- 启用基于DMA的结果传输的配置必须通过EB tresos参数:AdcResultHandlinglmplementation来完成。
- 必须在EB tresos中通过DMA驱动程序来保留和配置用于ADC驱动程序的DMA通道。
- 连续结果寄存器必须分配给EB策略中AdcChannel组的信道。
- ADC驱动程序不配置DMA通道。在启动/停止Adcchannel组之前,ADC的用户应该调用适当的DMA的API来启动/停止DMA通道。
- 地址空间0xD和0xC不应用于DMA相关用途。在scratch pad RAM中分配内存的MemMap部分应始终生成全局地址,而不应生成本地地址。
- 由于DMA的Data CRC和Address CRC功能未用于ADC驱动程序,因此在使用DMA模式时,用户应确保通过冗余或其他方式对转换结果进行可信度检查。
中断连接
当AdcResultHandlinglmplementation等于ADC_INTERRUPT_MODE_RESULT_HANDLING时,选择中断来传输转换结果。在这种模式下,转换结果从结果寄存器传输到请求源事件的ISR中的应用程序缓冲区。ADC硬件组的每个请求源都分配了一个专用服务请求线。作为限制检查结果生成的通道事件也分配了一个专用服务请求线。下图显示了ADC驱动程序所需的中断连接。
调用驱动程序提供的中断处理程序必须由用户完成(作为中断表的一部分,或者交由OS处理),对EVADC_G0的示例如下:
#if (IRQ_ADC0_EXIST == STD_ON)
/******************************************************************************
** Syntax : void ADC0SR0_ISR(void) **
** **
** Service ID: NA **
** **
** Sync/Async: Synchronous **
** **
** Reentrancy: non reentrant **
** **
** Parameters (in): none **
** **
** Parameters (out): none **
** **
** Return value: none **
** **
** Description : Service on ADC Request source conversion complete **
** service request **
** **
******************************************************************************/
#if IRQ_ADC0_SR0_TOS != IRQ_TOS_DMA
#if((IRQ_ADC0_SR0_PRIO > 0) || (IRQ_ADC0_SR0_CAT == IRQ_CAT2))
#if((IRQ_ADC0_SR0_PRIO > 0) && (IRQ_ADC0_SR0_CAT == IRQ_CAT1))
IFX_INTERRUPT(ADC0SR0_ISR, 0, IRQ_ADC0_SR0_PRIO)
#elif IRQ_ADC0_SR0_CAT == IRQ_CAT2
ISR(ADC0SR0_ISR)
#endif
{
/* Enable Global Interrupts */
ENABLE();
/* Call Adc Interrupt function*/
Adc_RS0EventInterruptHandler(0U);
}
#endif
#endif
/******************************************************************************
** Syntax : void ADC0SR1_ISR(void) **
** **
** Service ID: NA **
** **
** Sync/Async: Synchronous **
** **
** Reentrancy: non reentrant **
** **
** Parameters (in): none **
** **
** Parameters (out): none **
** **
** Return value: none **
** **
** Description : Service on ADC Request source conversion complete **
** service request **
** **
******************************************************************************/
#if IRQ_ADC0_SR1_TOS != IRQ_TOS_DMA
#if((IRQ_ADC0_SR1_PRIO > 0) || (IRQ_ADC0_SR1_CAT == IRQ_CAT2))
#if((IRQ_ADC0_SR1_PRIO > 0) && (IRQ_ADC0_SR1_CAT == IRQ_CAT1))
IFX_INTERRUPT(ADC0SR1_ISR, 0, IRQ_ADC0_SR1_PRIO)
#elif IRQ_ADC0_SR1_CAT == IRQ_CAT2
ISR(ADC0SR1_ISR)
#endif
{
/* Enable Global Interrupts */
ENABLE();
#if (ADC_PRIORITY_IMPLEMENTATION != ADC_PRIORITY_NONE)
/* Call Adc Interrupt function*/
Adc_RS1EventInterruptHandler(0U);
#endif
}
#endif
#endif
/******************************************************************************
** Syntax : void ADC0SR2_ISR(void) **
** **
** Service ID: NA **
** **
** Sync/Async: Synchronous **
** **
** Reentrancy: non reentrant **
** **
** Parameters (in): none **
** **
** Parameters (out): none **
** **
** Return value: none **
** **
** Description : Service on ADC Request source conversion complete **
** service request **
** **
******************************************************************************/
#if IRQ_ADC0_SR2_TOS != IRQ_TOS_DMA
#if((IRQ_ADC0_SR2_PRIO > 0) || (IRQ_ADC0_SR2_CAT == IRQ_CAT2))
#if((IRQ_ADC0_SR2_PRIO > 0) && (IRQ_ADC0_SR2_CAT == IRQ_CAT1))
IFX_INTERRUPT(ADC0SR2_ISR, 0, IRQ_ADC0_SR2_PRIO)
#elif IRQ_ADC0_SR2_CAT == IRQ_CAT2
ISR(ADC0SR2_ISR)
#endif
{
/* Enable Global Interrupts */
ENABLE();
#if (ADC_PRIORITY_IMPLEMENTATION != ADC_PRIORITY_NONE)
/* Call Adc Interrupt function*/
Adc_RS2EventInterruptHandler(0U);
#endif
}
#endif
#endif
/******************************************************************************
** Syntax : void ADC0SR3_ISR(void) **
** **
** Service ID: NA **
** **
** Sync/Async: Synchronous **
** **
** Reentrancy: non reentrant **
** **
** Parameters (in): none **
** **
** Parameters (out): none **
** **
** Return value: none **
** **
** Description : Service on ADC Request source conversion complete **
** service request **
** **
******************************************************************************/
#if IRQ_ADC0_SR3_TOS != IRQ_TOS_DMA
#if((IRQ_ADC0_SR3_PRIO > 0) || (IRQ_ADC0_SR3_CAT == IRQ_CAT2))
#if((IRQ_ADC0_SR3_PRIO > 0) && (IRQ_ADC0_SR3_CAT == IRQ_CAT1))
IFX_INTERRUPT(ADC0SR3_ISR, 0, IRQ_ADC0_SR3_PRIO)
#elif IRQ_ADC0_SR3_CAT == IRQ_CAT2
ISR(ADC0SR3_ISR)
#endif
{
/* Enable Global Interrupts */
ENABLE();
#if (ADC_ENABLE_LIMIT_CHECK == STD_ON)
/* Call Adc Interrupt function*/
Adc_ChEventInterruptHandler(0U);
#endif
}
#endif
#endif
#endif
当AdcResultHandlinglmplementation等于ADC_DMA_MODE_RESULT_HANDLING时,选择通过 DMA传输转换结果。在这种模式下,转换结果通过DMA从结果寄存器传输到应用程序缓冲区。结果寄存器事件触发服务请求,由DMA处理。下图表示中断连接。
调用驱动程序提供的中断处理程序必须由用户完成,对EVADC_G0和EVADC_G5的示例调用如下所示:
#include “Adc.h”
/** AdcResultHandlingImplementation = ADC_DMA_MODE_RESULT_HANDLING **/
/************EVADC_G0*************/
/*********SRC_VADC_G0_SR0*********/
ISR(ADC0SR0_ISR)
{
ENABLE(); /* Enable interrupts */
/*The interrupt handler should be called from SRN0 of EVADC_G0 */
Adc_RS0EventInterruptHandler(0u); /*0 indicates the HW group EVADC_G0*/
}
/**** Service Request 3 is service by DMA ****/
/**** Other Service Requests are unused ****/
/************EVADC_G5*************/
/*********SRC_VADC_G5_SR0*********/
ISR(ADC5SR0_ISR)
{
ENABLE(); /* Enable interrupts */
/*The interrupt handler should be called from SRN0 of EVADC_G5*/
Adc_RS0EventInterruptHandler(5u); /*5 indicates the HW group EVADC_G5*/
}
/**** Service Request 3 is service by DMA ****/
/**** Other Service Requests are unused ****/
使用示例
初始化ADC驱动程序的代码如下:
/*
Configuration values mandatory for below code snippet:-
1. AdcResultHandlingImplementation = ADC_DMA_MODE_RESULT_HANDLING: Then Dma_Init() is required
prior to use of runtime ADC services.
2. AdcStartupCalibApi = TRUE: Then Adc_GetStartupCalStatus() and Adc_TriggerStartupCal() can be
invoked
*/
#include “Adc.h”
#include “Mcu.h”
#include “Port.h”
#include “Dma.h”
#include “Irq.h”
/* MCU and GTM Initialization */
Mcu_Init(&Mcu_Config);
Mcu_InitClock(0U);
while(Mcu_GetPllStatus() != MCU_PLL_LOCKED);
Mcu_DistributePllClock();
/* Port Initialization */
Port_Init(&Port_Config);
#if(ADC_RESULT_HANDLING_IMPLEMENTATION == ADC_DMA_MODE_RESULT_HANDLING)
/* Dma Initialization, Only if DMA mode of Result
handling is used */
Dma_Init(&Dma_Config);
#endif
/* ADC Initialization */
Adc_Init(&Adc_Config);
/* ADC Startup Calibration */
Adc_TriggerStartupCal();
/* Wait till the Start Calibration is over*/
while(Adc_GetStartupCalStatus() != ADC_STARTUP_CALIB_OVER);
/* Initialize the SRPN and TOS for used interrupts */
IrqAdc_Init();
/* Enable Interrupts for used ADC HW units(x) */
SRC_VADC_Gx_SR0.B.SRE = 1U;
SRC_VADC_Gx_SR1.B.SRE = 1U;
SRC_VADC_Gx_SR2.B.SRE = 1U;
#if((ADC_RESULT_HANDLING_IMPLEMENTATION == ADC_DMA_MODE_RESULT_HANDLING) ||
(ADC_ENABLE_LIMIT_CHECK == STD_ON))
SRC_VADC_Gx_SR3.B.SRE = 1U;
#endif
/* Further APIs of ADC driver can be called now */
启动软件触发组转换的代码如下:
/*
Configuration values mandatory for below code snippet:-
1. AdcEnableStartStopGroupApi = True
2. AdcGrpNotifCapability = True
3. AdcResultHandlingImplementation = ADC_INTERRUPT_MODE_RESULT_HANDLING
*/
#include “Adc.h”
Adc_GroupType Group ;
Adc_ValueGroupType DataBufferPtr[10]; //Assuming buffer size of 10 is sufficient for the
AdcChannel group being depicted
Std_ReturnType lRetVal;
/*AdcConf_AdcGroup_AdcXGroup_Y is a valid SW group ID macro
generated in Adc_Cfg.h */
Group = AdcConf_AdcGroup_AdcXGroup_Y;
lRetVal = Adc_SetupResultBuffer(Group, &DataBufferPtr[0]);
if(lRetVal != E_NOT_OK)
{
Adc_EnableGroupNotification(Group);
Adc_StartGroupConversion(Group);
}
else
{
/*Could not setup result buffer*/
}
停止软件触发的组转换的代码如下:
/*
Configuration values mandatory for below code snippet:-
1. AdcEnableStartStopGroupApi = True
*/
#include “Adc.h”
Adc_GroupType Group ;
/*AdcConf_AdcGroup_AdcXGroup_Y is a valid SW group ID macro
generated in Adc_Cfg.h */
Group = AdcConf_AdcGroup_AdcXGroup_Y;
/*Make sure Group has already been started by calling
Adc_StopGroupConversion API*/
Adc_StopGroupConversion(Group);
启动硬件触发组转换的代码如下:
/*
Configuration values mandatory for below code snippet:-
1.AdcHwTriggerApi = True
2.AdcEnableStartStopGroupApi = True
3.AdcResultHandlingImplementation = ADC_INTERRUPT_MODE_RESULT_HANDLING
*/
#include “Adc.h”
Adc_GroupType Group ;
Adc_ValueGroupType DataBufferPtr[10]; //Assuming buffer size of 10 is sufficient for the
AdcChannel group being depicted
Std_ReturnType lRetVal;
/*AdcConf_AdcGroup_AdcXGroup_Y is a valid HW group ID macro generated in Adc_Cfg.h */
Group = AdcConf_AdcGroup_AdcXGroup_Y;
lRetVal = Adc_SetupResultBuffer(Group, &DataBufferPtr[0]);
if(lRetVal != E_NOT_OK)
{
Adc_EnableGroupNotification(Group);
Adc_EnableHardwareTrigger(Group);
}
else
{
/*Could not setup result buffer*/
}
停止硬件触发的组转换的代码序列如下:
/*
Configuration values mandatory for below code snippet:-
1. AdcHwTriggerApi = True
*/
#include “Adc.h”
Adc_GroupType Group ;
/*AdcConf_AdcGroup_AdcXGroup_Y is a valid HW group ID */
Group = AdcConf_AdcGroup_AdcXGroup_Y;
/*Make sure Group has already been started by calling Adc_
EnableHardwareTrigger API*/
/* Disable the HW trigger */
Adc_DisableHardwareTrigger(Group);
在结果处理为DMA模式下启用硬件触发组转换的代码如下:
/*
Configuration values mandatory for below code snippet:-
1. AdcHwTriggerApi = True
2. AdcEnableStartStopGroupApi = True
3. AdcResultHandlingImplementation = ADC_DMA_MODE_RESULT_HANDLING
*/
#include “Adc.h”
#include “Dma.h”
Adc_GroupType Group ;
/*AdcConf_AdcGroup_AdcXGroup_Y is a valid HW group ID macro
generated in Adc_Cfg.h */
Group = AdcConf_AdcGroup_AdcXGroup_Y;
/* DMA channel initialization is already completed as part of Dma_Init*/
/* Update the source address to SFR address of result register and destination address to RAM
buffer */
Dma_ChUpdate(0U, PointerToChannelUpdateStruct, NULL_PTR)
/* Enable HW trigger for the DMA Channel used */
Dma_ChEnableHardwareTrigger(0U);
/* Enable notification and HW trigger for the Group*/
Adc_EnableGroupNotification(Group);
Adc_EnableHardwareTrigger(Group);
读取完成转换结果的代码如下:
/*
Configuration values mandatory for below code snippet:-
1. AdcReadGroupApi = True
2. AdcResultHandlingImplementation = ADC_INTERRUPT_MODE_RESULT_HANDLING
*/
#include “Adc.h”
Adc_GroupType Group ;
Std_ReturnType Read_Err;
Adc_StatusType lRetVal;
Adc_ValueGroupType DataBufferPtr[10]; //Assuming buffer size of 10 is sufficient for the
AdcChannel group being depicted
/*AdcConf_AdcGroup_AdcXGroup_Y is a valid group ID macro generated in Adc_Cfg.h */
Group = AdcConf_AdcGroup_AdcXGroup_Y;
lRetVal = Adc_GetGroupStatus(Group);
if((lRetVal == ADC_STREAM_COMPLETED) || (lRetVal == ADC_COMPLETED))
{
Read_Err = Adc_ReadGroup (Group, &DataBufferPtr[0]);
if(Read_Err != E_NOT_OK)
{
/*Adc read group is successful*/
}
}
else
{
/*Results
去初始化驱动的代码如下:
/*
Configuration values mandatory for below code snippet:-
1. AdcDeInitApi = True
*/
#include “Adc.h”
/*Stop all the currently converting groups before calling Adc De-init so that De-Initialization
can go on successfully */
/* ADC De-Initialization */
Adc_DeInit();
ADC通道组转换可以由GTM通道的定时器事件触发。此类组中的ADC通道的转换在每次检测到GTM定时器事件时都会发生。为了解决多个应用程序用例,ADC驱动程序提供了多种配置GTM通道的方法,如下所示:
- Time-based trigger using GTM:在这种模式下,每次对以GTM为触发源的组调用Adc_EnableHardwareTrigger()时,ADC驱动程序都会(通过单片机的API)配置GTM通道。
我们首先需要通过McuGtmAllocationConf容器下的配置参数在MCU驱动程序中专门保留要使用的GTM用到,然后通过GtmTriggerForAdc容器下列出的配置参数在MCU驱动程序中配置用于GTM到 EVADC触发连接的多路复用。然后GTM的时钟和相关配置还需要通过MCU驱动程序配置完成。我们还需要将AdcHwTriggerApi配置参数设置为TRUE,在AdcGroup/AdcHwExtTrigSelect参数中选择正确的GTM触发线,并在AdcGroup/AdcHwTrigSignal参数中选择外部请求信号的边缘,最后在容器AdcGroup/GtmTriggerTimerConfig中配置GTM定时器和触发器间隔。 - PWM signal-based trigger using GTM:在此模式下,ADC驱动程序依赖于应用程序或其他驱动程序来配置生成PWM信号的GTM信道。预定使用的GTM定时器必须由ADC驱动程序之外的用户配置。然后通过GtmTriggerForAdc容器下列出的配置参数在MCU驱动程序中配置用于GTM到EVADC触发连接的多路复用。GTM的时钟和相关配置必须通过MCU驱动程序配置完成。我们还需要在AdcHwTriggerApi配置参数设置为true,在 AdcGroup/AdcHwExtTrigSelect 参数中选择正确的GTM触发线,最后在AdcGroup/AdcHwTrigSignal参数中选择外部请求信号的边缘。
ADC驱动程序支持在ADC硬件组中同步转换通道。此功能可以通过AdcSyncConvEnable、AdcSyncConvMode和AdcSyncConvChannelEnable配置参数启用。AdcSncConyMode参数确定主和从硬件组。当主硬件组上触发ADC通道组时,则通过AdcSyncConvChannelEnable标记的所有同步转换通道也会在从硬件组上触发。驱动程序将从属模拟通道(CHCTR)配置为具有与主模拟通道相同的属性。因此,用于从属模拟通道的输入类必须具有与用于主模拟通道的输入类相同的属性。建议使用全局输入类作为主通道,这样从通道也可以配置相同的输入类,从而消除不同的转换属性。在主硬件组通道的转换结果之后,将存储从硬件组的通道的转换结果在应用程序缓冲区中。
在图中解释了以下场景的缓冲区布局。
- ADC信道组中配置有三个通道:CH9、CH1和 CH2。
- 每个通道由两个采样样本。
- 内核K0主核、内核K1和K2从。
- 支持同步转换的通道CH9和CH1。
环境与目标
本文使用的为英飞凌提供的开发板KIT_A2G_TC397XA_TFT,使用开发板排针上引出的AN16~19。
涉及的软件如下:
- EB-tresos:用于生成动态代码,具体工程搭建参考《【AUTOSAR MCAL】MCAL基础与EB tresos工程新建》。
- HighTech:用于编译生成elf文件,具体的工程搭建参考《【MCAL】HighTec集成TC3xx对应MCAL的Demo》。
- UDE 5.2:用于下载和调试程序。
涉及的参考文档如下表。
序号 | 参考资料 | 内容 |
1 | 《Infineon-AURIX_TC39x-UserManual-v02_00-EN》 | 英飞凌TC39x用户手册 |
2 | 《ApplicationKitManual-TC3X7-ADAS-V21.pdf》 | 开发板KIT_A2G_TC397XA_TFT说明 |
3 | 《MC-ISAR_TC3xx_UM_Adc.pdf》 | 英飞凌提供的TC3xx芯片Adc用户手册 |
配置目标如下:
- 软件触发采样获取正确的AD值。
- GTM硬件触发采样获取正确的AD值。
EB tresos配置
软件触发采样配置
ADC
GENERAL配置截图如下:
需要主要关心的配置有:
- AdcDeInitApi:是否打开去初始化功能API。
- AdcInitCheckApi:是否打开初始化检查API。
- AdcEnableLimitCheck:启用或禁用ADC驱动程序中的限制检查功能。当选择结果处理为DMA模式时,不能将其设置为TRUE。
- AdcEnableStartStopGroupApi:参数确定软件触发机制(Adc_StartGroupConversion()和Adc_StopGroupConversion())在运行时是否可用。当配置为TRUE时,这些API在运行时可用。
- 参数确定组通知机制(Adc_EnableGroupNotification()和Adc_DisableGroupNotification())在运行时是否可用。当配置为TRUE时,这些API在运行时可用。
- AdcHwTriggerApi:参数确定硬件触发机制(Adc_EnableHardwareTrigger()和Adc_DisableHardwareTrigger())在运行时是否可用。当配置为TRUE时,这些api在运行时可用。
- AdcReadGroupApi:参数从代码中添加或删除Adc_ReadGroup() API。当设置为TRUE时,API在运行时可用。当选择结果处理为DMA模式时,AdcReadGroupApi应该为FALSE。
- AdcSyncConvEnable:参数启用或禁用跨ADC硬件组的同步转换。依赖于配置项
前置条件AdcResultHandlingImplementation = 中断。 - AdcPriorityImplementation:参数确定转换请求的优先级机制是否可用。可以选择两种类型的优先排序机制。硬件优先级机制(ADC_PRIORITY_HW)使用ADC硬件特性进行优先级排序。硬件和软件混合优先级机制(ADC_PRIORITY_HW_SW)利用ADC的硬件特性和软件逻辑对AdcChannel组进行优先级排序。软件触发组的组优先级通常配置为比硬件触发组的组优先级低的优先级级别。
- AdcResultHandlingImplementation:参数决定ADC驱动程序的结果处理:ADC_DMA_MODE_RESULT_HANDLING:转换结果使用DMA传输
ADC_INTERRUPT_MODE_RESULT_HANDLING:转换结果使用中断传输 - AdcResultAlignment:参数决定ADC结果缓冲区中的原始ADC结果是左对齐还是右对齐。
- AdcMaxChConvTimeCount:参数确定所有配置的通道之间的最大通道转换时间。
ADC驱动程序在试图停止正在进行的转换时使用此参数值。这将确保当前转换通道在停止请求后完成转换,并且EVADC硬件单元在驱动程序启动新的转换之前进入空闲状态。该参数的默认值大于停止转换的默认时间。默认停止时间基于硬件的输入类配置。 - AdcSleepMode:参数决定ADC驱动是否接受或拒绝来自SCU的睡眠模式请求。该参数的默认值为对应SFR的重置值。
- AdcSupplyVoltage:参数调整模拟电路到电源电压。该参数的默认值为对应SFR的重置值。ADC_VOLTAGE_3P3V:固定3.3V电源已连接。ADC_VOLTAGE_5V: 固定5V电源已连接。ADC_VOLTAGE_CONTROLLED_BY_SUPPLY: 自动控制:电压范围由供电电压决定。
AdcGlobalInputClass配置截图如下:
我们这个容器主要关心的配置有:
- AdcChSampleTime:ADC通道采样周期。
- AdcChPreChargeClkCycles:参数定义标准转换的模拟输入预充电容时钟周期数。该参数的默认值为对应SFR的重置值。
- AdcChConvMode:参数定义标准转换的降噪级别。
- AdcChSESPSEnable:参数定义标准转换的扩展早期采样点是启用还是禁用。
AdcHwUnit配置截图如下:
我们这个容器主要关心的配置有:
- AdcHwUnitId:它对应芯片种包含的ADC硬件单元,因为我们使用的是AN16~19,所以我们查找手册如下选择ADC2。
- AdcPrechargeReference:参数决定是否使用VDDM/VSSM进行预充。
- AdcReferencePrechargePhases:参数配置参考输入的预充电时钟相位数。
- AdcCalibrationSampleTime:参数配置校准采样时间。
AdcClockSource配置截图如下:
AdcPrescale配置截图如下(预分频数):
AdcHwUnitInputClass配置截图如下:
AdcChannel配置截图如下:
我们这个容器主要关心的配置有:
- AdcChannelId:参数定义通道的数字ID,这是通道的逻辑ID。每个ADC硬件单元必须从0开始连续。
- AdcAnChannelNum:通道号,与手册中可以查到对应的Port口。
AdcGroup配置截图如下:
我们这个容器主要关心的配置有:
- AdcGroupAccessMode:ADC_ACCESS_MODE_STREAMING不允许单次软件触发组,也就是说在AdcGroupTriggSrc = ADC_TRIGG_SRC_SW的同时不允许配置AdcGroupConvMode = ADC_CONV_MODE_ONESHOT。只有当adcresulthandlingimplementation为
ADC_INTERRUPT_MODE_RESULT_HANDLING时,AdcGroupAccessMode才可编辑。 - AdcGroupConversionMode:ADC_CONV_MODE_CONTINUOUS为ADC通道组的转换在软件API调用(start)后连续执行。转换本身自动运行(不需要额外的软件或硬件触发器)。
ADC_CONV_MODE_ONESHOT为触发后进行一次通道组转换。 - AdcGroupId:参数定义ADC通道组的数字ID。每个ADC硬件单元的最大组数限制为32。组的数字ID由配置工具根据硬件单元自动生成。此参数的符号名称将在调用api时使用。每个AdcChannel组的符号名称是一个用数字ID定义的宏。
- AdcGroupTriggSrc:参数定义启动组转换的源事件的类型。组转换由配置为ADC_TRIGG_SRC_SW的组的软件API调用和配置为ADC_TRIGG_SRC_HW的组的硬件事件启动。
- AdcStreamingNumSamples:参数配置在流访问模式下每个通道要获取的ADC值的数量。
AdcNotification配置截图如下(回调函数名):
AdcGroupDefinition配置截图如下(组里包含通道):
AdcResRegDefinition配置截图如下:
其中唯一的参数为AdcResRegDefinition,配置存储ADC转换结果顺序。该参数的值应按升序排列。因此,第一个配置的通道应该具有最低的结果寄存器索引。为具有五个通道的组配置的可能的结果寄存器顺序如下:4,5,6,7,8或0,1,2,3,4或11,12,13,14,15.
Port
不需要对模拟PORT进行单独配置,ADC配置好之后PORT默认配置好了,事实上英飞凌的模拟口很多只能作为模拟口来使用,没有普通IO的功能,部分如下图所示D口也仅仅复用到了与支持SENT协议的传感器通信。我们这里的示例PORT对应的是AN16~19,对应ADC为Group2 Channel0~Channel3。
MCU
需要在McuClocksettingConfig中设置ADC时钟,示例配置的为160Mhz。
IRQ
因为我们使用的是中断模式,所以需要配置ADC中断,因为我们使用的是ADC2的通道,且采用软件触发,则配置ADC2SR0即可。
ResourceM
GTM硬件触发采样配置
ADC
这里我们着重介绍跟软件触发采样配置不同的地方,首先是General配置。这里我们需要打开AdcHwTriggerApi。
下面是AdcGroup的配置。
下面我们重点介绍一下跟软件触发采样配置不一致的地方。
- AdcGroupAccessMode:这里采用的流访问组,当然硬件触发也可以采用单次。
- AdcGroupConversionMode:转换模式,硬件触发EB里面要求是ONESHOT,也可以配置成循环,不报错,报警告,也可以正常采取AD值。
- AdcStreamingBufferMode:流访问组采用的是线性的buffer,满了之后新的AD值不会覆盖之前的结果。
- AdcStreamingNumSamples:在流访问组的时候,必须大于一,表示组内通道一次流采集对应的次数。
- AdcHwExtTrigSelect:外部触发信号,我们这里选择的是ADC_TRIG_8_GxREQTRI_GTM_ADCx_TRIG0,这里我们具体选择的是GTM ADC trigger 0,我们要根据这个触发源进行相应的GTM配置。
AdcHwTrigSignal配置截图如下:
GtmTriggerTimerConfig配置截图如下:
我们需要关心的配置如下:
- GtmTimerUsed:选择使用哪一个TOM/ATOM通道作为外部触发信号,这里我们选择TOM0_6,这跟我们后边GTM的配置有关。
- GtmTimerClockSelect:参数决定GTM定时器时钟源。
- GtmTimerTimePeriod:参数定义GTM定时器的时间周期,单位为微秒。 如果计算出的GTM滴答数大于OxFFFF(对于TOM通道)或大于OxFFFFFF(对于ATOM通道),则引发错误消息。
只有当GtmTimerCMOTicks等于0(用户没有直接输入周期匹配ticks)时,该参数才可编辑。
GTM
GtmTriggerForAdc配置截图如下:
因为我们在之前选择的是GTM ADC trigger 0,所以我们在GtmAdcTrigger0Select下选择触发通道,TRIG_1对应的通道如下图,上面ADC中GtmTimerUsed的配置就根据此而来。
McuGtmTomAllocationConf配置截图如下:
ADC驱动调试
软件触发采样配置测试代码及结果
测试代码如下,示例代码包含两个部分,第一个部分为回调函数,在AD转换完毕之后调用,示例程序对不同AD值做了一个判断。第二部分是ADC的初始化部分,包含启动转换等操作:
uint32 PCBLayout = 0xff;
volatile uint32 EVADCInterrupt = 0;
void IoHwAb_AdcNotification64(void)
{
if (ADC_SW_GRP_RES[2] > 0x0300)
{
PCBLayout = 0;
}
else if (ADC_SW_GRP_RES[3] > 0300)
{
PCBLayout = 1;
}
EVADCInterrupt++;
}
static volatile uint8 EVADCInitReturn = 100;
static Adc_ValueGroupType ADC_SW_GRP_RES[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static Std_ReturnType AdcBufferSetupStatus = 100;
void ADC_Init()
{
int loopindex=3;
IrqAdc_Init();
SRC_VADCG2SR0.B.SRE = 1;
for(loopindex = 0 ;loopindex < 3;loopindex++)
{
Adc_Init(&Adc_Config);
EVADCInitReturn = Adc_InitCheck(&Adc_Config);
if(EVADCInitReturn==E_OK)
{
Adc_EnableGroupNotification(AdcConf_AdcGroup_AdcGroup_2);
AdcBufferSetupStatus = Adc_SetupResultBuffer(AdcConf_AdcGroup_AdcGroup_2,ADC_SW_GRP_RES);
Adc_StartGroupConversion(AdcConf_AdcGroup_AdcGroup_2);
return;
}
}
}
通过UDE将程序烧录进去之后,在回调函数处增加断点,程序运行之后能够走到断点,并且在ADC_SW_GRP_RES处有读取到的AD值。
GTM硬件触发采样配置测试代码及结果
测试代码如下,第一部分完成了ADC的初始化,第二部分则在设置了AD转换结果buffer之后,使能了硬件触发,并在流式转后填满buffer之后,失能了硬件触发(屏蔽的打印部分打印了各个通道的其中一个结果)。
void DemoApp_Adc_Init(void)
{
SRC_VADCG0SR0.U |= SRE_ENABLE;
const Adc_ConfigType * ConfigPtr = NULL_PTR;
ConfigPtr = &Adc_Config;
Adc_Init(ConfigPtr);
/* Wait till the StartUp calibration is over */
while( (Adc_GetStartupCalStatus()) != E_OK)
{}
}
Adc_ValueGroupType ADC_HW_GRP_RES[16];
void Adc_HWGroupDemo(void)
{
Adc_StreamNumSampleType AdcHWGroupNoSamples;
Std_ReturnType AdcBufferSetupStatus;
/* Initialize ADC interrupt */
IrqAdc_Init();
/* Initialize ADC */
DemoApp_Adc_Init();
/* Buffer Marker value */
ADC_HW_GRP_RES[0] = 0x00C8;
/* Initialize the Result buffer for AdcHWGroup */
AdcBufferSetupStatus = Adc_SetupResultBuffer(AdcConf_AdcGroup_AdcGroup_2, ADC_HW_GRP_RES);
if(AdcBufferSetupStatus == E_OK)
{
Demo_AdcNoSamp = 0;
/* Initialize the no. of group samples (Streaming length)
for AdcHWGroup as configured
*/
AdcHWGroupNoSamples = (Adc_StreamNumSampleType)2;
/* Start the AdcHWGroup group */
Adc_EnableHardwareTrigger(AdcConf_AdcGroup_AdcGroup_2);
while(Demo_AdcNoSamp != AdcHWGroupNoSamples)
{
/* Get the pointer to last conversion result of Adc Group */
Demo_AdcNoSamp = Adc_GetStreamLastPointer(AdcConf_AdcGroup_AdcGroup_2, &Demo_ResultPtr);
}
/* Print the latest converted result of the channels as pointed by
Demo_ResultPtr
*/
// print_f("\n ADC Conversion Results (Raw Value): \n");
// print_f("AN0) : %4x\n", *(Demo_ResultPtr));
// print_f("AN1) : %4x\n", *(Demo_ResultPtr + AdcHWGroupNoSamples));
// print_f("AN2) : %4x\n", *(Demo_ResultPtr + 2*AdcHWGroupNoSamples));
// print_f("AN3) : %4x\n", *(Demo_ResultPtr + 3*AdcHWGroupNoSamples));
// print_f("AN4) : %4x\n", *(Demo_ResultPtr + 4*AdcHWGroupNoSamples));
// print_f("AN5) : %4x\n", *(Demo_ResultPtr + 5*AdcHWGroupNoSamples));
// print_f("AN6) : %4x\n", *(Demo_ResultPtr + 6*AdcHWGroupNoSamples));
// print_f("AN7) : %4x\n", *(Demo_ResultPtr + 7*AdcHWGroupNoSamples));
/* Check if the Group has implictly stopped and status is IDLE
before Stop request
*/
if( (Adc_GetGroupStatus(AdcHWGroup)) != ADC_IDLE)
{
/* Stop the conversion which will reset the status and result buffer
and notification is disabled
*/
Adc_DisableHardwareTrigger(AdcHWGroup);
}
}
else
{
//print_f("\nError in ADC_HW_GRP_RES buffer setup for AdcHWGroup");
}
Adc_DeInit();
return;
} /* Adc_HWGroupDemo */
测试结果可以在ADC_HW_GRP_RES数组中看到读出来的各通道AD值,记得通道数由低到高排布,且每个通道有两个值,当然,您也可以通过Adc_ReadGroup函数将每个通道最近的AD值拷贝出来。
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。