一、简介
IFX低电平驱动程序库(iLLD)的目标是为Infineon微控制器的集成外设提供访问和配置功能。从一个简单的定时器寄存器访问到三相PWM驱动器的逆变器应用。与SFR头文件,他们是基础设施的一个基本部分的测试和应用程序,由几个团队在IFX ATV开发。
所有驱动程序遵循相同的编码风格,以确保一个共同的"Look&Feel”。应用程序接口(api)由硬件/软件专家定义和实现,已经用虚拟原型或RTL模拟测试了预硅,然后由参与软件编程的所有团队共享。这种交叉使用模型不仅可以帮助减少开发工作,还可以提高驱动程序的质量/可靠性。严格的iLLD编码准则允许为多维系统场景分层驱动程序。外设驱动程序之间没有依赖关系。也可以将它们从库中分离出来,并作为一个单独的包提供(例如,用于发送给客户的申请说明)。目前计划将illd用于Aurix和Aurix 2G衍生物。每个派生都有自己的一组iLLD源,它们位于GIT中。版本化的包也通过MyICP发布。
二、iLLD一般结构
我们基本上可以区分两种类型的iLLDs
2.1单功能的外围驱动程序
单一功能外设驱动程序是目标外设能够执行一种功能的简单模块。
例如:VADC外设用于模数转换。Multican用于CAN协议通信,DMA用于DMA功能,等等。
2.2 多功能外围设备驱动程序
这些外设驱动程序处理起来很复杂,在这里目标外设可以执行多种功能(“用例”)。
例如:asclin外围设备可以配置为一个普通的UART,也可以配置为LIN,甚至是SPI。
GTM TOM/ATOM可以作为PWM发生器、复杂波形发生器和任何通用定时器等。
2.3外设驱动程序内容
iLLD由以下几部分组成:
1.外设寄存器内存映射(SFR头文件)
2.标准层(详细)访问SFRs的功能,在应用程序运行时更改
3.具有配置和处理功能的接口层,如果外设用于不同的目的,可以使用多个接口层
4.Pinmaps配置I/O多路复用器
2.4配置数据结构
配置结构是接口层的一部分,它保存配置驱动程序的参数。根据iLLD及其相关外围设备的不同,可以有多个级别的配置数据结构。在VADC的情况下,可以有数据结构来配置VADC模块,一个结构用于配置组,另一个结构用于配置通道。 考虑到结构在内存映射中是非包装的,数据结构应该以内存优化的方式定义。所有的结构元素都应该按照其物理内存大小的降序排列。
用法:通常情况下,配置数据结构在特定的初始化例程中本地存储(在堆栈上)。它们也可以放在闪 存/ROM中
typedef struct
{
const IfxVadc_Adc *module; /**< \brief Specifies pointer to the IfxVadc_Adc module handle */
IfxVadc_GroupId groupId; /**< \brief Specifies the group/kernel id */
IfxVadc_GroupId master; /**< \brief Specifies the master group if own group is different master slave is configured */
IfxVadc_Adc_ArbiterConfig arbiter; /**< \brief Specifies arbiter configuration */
IfxVadc_Adc_ClassConfig inputClass[IFXVADC_NUMBER_OF_INPUTCLASS]; /**< \brief Specifies conversion settings one and two */
IfxVadc_Adc_ScanConfig scanRequest; /**< \brief Specifies scan mode configuration */
IfxVadc_Adc_QueueConfig queueRequest; /**< \brief Specifies queued mode configuration */
IfxVadc_Adc_ScanConfig backgroundScanReq; /**< \brief Specifies back ground scan configuration */
boolean disablePostCalibration; /**< \brief Specifies if calibration after conversion (post calibration) should be disabled */
} IfxVadc_Adc_GroupConfig;
2.5运行时变量(处理)
运行时变量通常存储在所谓的“句柄”中。这个结构与上面提到的配置结构分离(因为这样的结构只在初始化阶段需要)。句柄用来通过接口函数引用外围设备的资源,而无需进行不必要的计算或参数传递开销。句柄结构体至少包含一个指向应该访问的外围设备基址的指针,需要时指向外围设备子组件的指针,和/或索引(如通道或定时器号)。句柄将被使用外设的每个实例实例化。更高的SW层存储预期应用程序所需的实例数量,并为这些数据结构预留内存。该数据结构必须在Cpu的DMI RAM中可用,lld API调用从这里更频繁地进行。
句柄的示例:
typedef struct
{
IfxVadc_Adc module; /**< \brief The VADC handle structure */
Ifx_VADC_G *group; /**< \brief Pointer to the group registers */
IfxVadc_GroupId groupId; /**< \brief Specifies the group index */
} IfxVadc_Adc_Group;
三、iLLD APIs
本节提供iLLD所需api的一般概述
3.1 iLLD Init APIs
iLLDs需要用户配置数据的id,应声明用于配置的数据类型。这些数据结构由高级软件实例化。以下是对驾驶员的要求。
3.2 初始化配置数据结构
任何使用配置数据结构的驱动程序都应该提供一个接口来将这些数据结构元素初始化为默认值。这种实现的主要用例是,当驱动程序使用额外的配置参数升级时,这样的初始化将为变量提供正确的值,以便在与使用该驱动程序以前版本的遗留应用程序软件一起使用时,驱动程序能够正确工作。API应该在flash中作为一个实例,但应该能够在任何数量的相同外设上运行。
数据结构应在驱动程序针对的每个硬件实例中进行实例化
通用原型应如
<驱动前缀>_initConfig(<驱动前缀>_Config *cfg);其中“Driver Prefix”示例为“IfxVadc”或“IfxGtmTom_Pwm”。
3.3 驱动程序初始化
iLLD应该提供一个API来根据用户配置初始化驱动外设。配置数据应预先用init config接口初始化为默认值。API应该在flash中作为一个实例,但应该能够在任何数量的相同外设上运行。驱动程序初始化API应该初始化外设寄存器,使外设能像驱动程序一样工作。这个API还应该将驱动程序内部数据结构初始化为所需的初始化值。
通用原型应如
<驱动前缀>_init(<驱动前缀>_Handle *handle, <驱动前缀>_Config *cfg);其中“Driver Prefix”示例为“IfxVadc”或“IfxGtmTom_Pwm”。
3.4 Driver De-Initialization
iLLD可以提供一个API来重置驱动程序到默认的重置状态,并使其消耗最少的电力。API应该在flash中作为一个实例,但应该能够在任何数量的相同外设上运行。通用原型应如
<驱动前缀>_deInit(<驱动前缀>_Handle *handle);其中“Driver Prefix”示例为“IfxVadc”或“IfxGtmTom_Pwm”。
3.5 iLLD functional APIs
iLLD应为用户可能使用的每个所需功能提供API。API应该在flash中作为一个实例,但应该能够在任何数量的相同外设上运行。对于每个外设实例,传递的参数将被延迟。
通用原型应如
<驱动前缀>_<功能>(<驱动前缀>_Handle *handle);其中“Driver Prefix”示例为“IfxVadc”或“IfxGtmTom_Pwm”。
3.6 iLLD internal methods
为了更好的模块化可读性,驱动实现应该使用内部的静态函数。在这种情况下,函数定义应以类似于iLLD函数api的方式定义
四、命名规格
本节提供开发过程中要遵循的命名约定。
一般来说,所有“exported”的类型/定义/类/对象(即Function, enum, typedef, constant, variable,…)都应该以文件名+下划线“_”开头。
例如:在文件IfxScuWdt.c/h中,函数应为:
IfxScuWdt_enableEndinit ()
IfxScuWdt_disableEndinit ()
4.1 iLLD 文件
Ifx<HW模块名称>[<HW子模块名称>][_SW Driver if special][Sub SW Module name].c/h
例子:
1.只有HW模块,没有子模块:IfxPort.c/.h
2.具有HW模块和子模块:IfxScuWdt.c/.h
3.与硬件模块子模块和软件驱动程序:IfxGtmTom_Pwm.c/.h
4.与HW模块子模块和SW驱动程序+ SW子模块:ifxgtmtom_pwmll .c/.h(只有在有意义的拆分 10:文件,使其模块化)
在审查期间或使用静态代码检查工具时需要检查的重要事项:
1.前缀:Ifx(以标题为准)
2.<模块名>[<HW子模块名>](以驼峰形式)
3. [ _SW Driver if special ](一个下划线分开和标题大小写SW驱动程序名称)
4. 小写的.c和.h
4.2数据类型(Data Types)
typedef <标准类型> Ifx<HW模块名>[<HW子模块名>][SW Driver if special]<用户类型名>:
<标准类型>结构/联合/enum定义及其元素,以及其他C数据类型
例子:
typedef struct
{
uint32 pllFreqInHz;
uint32 stepFreqInHz;
uint8 stepPerIn_uSec;
uint8 pDivider;
uint8 nDivider;
uint8 k2Divider;
} IfxScu_Pll_Config;
需要检查的重要事项:
1.文件名的第1到3点
2._<用户类型名>:下划线与其余的和驼峰大小写字母分开(首字母也是大写)。
4.3 结构体(Structures)
要定义结构类型,必须遵循上述类型定义中的约定。
对于结构元素,使用驼峰大小写的简单名称,首字母为小应使用。
请参考类型定义的命名约定示例
4.4 枚举(enum)
要定义enum类型,必须遵循上述类型定义中的约定。
对于要遵循命名约定的枚举元素:
Ifx<HW模块名称>[<HW子模块名称>][SW Driver if special]<Enum名称>_<Enum元素>
<Enum元素>驼色字母<Enum名称>驼色字母
例子:
typedef enum {
IfxDma_ChannelPriority_low = 0,
IfxDma_ChannelPriority_medium,
IfxDma_ChannelPriority_high
} IfxDma_ChannelPriority;
需要检查的重要事项:
1.文件名的第1到3点
2._<Enum元素>:下划线与其余部分隔开,驼峰大小写(首字母小写)。
4.5 #define常量或宏
#define IFX<模块>[<HW子模块名称>][SW DRIVER]<功能名称> . #define IFX<模块>[<HW子模块名称>
<功能性名称>:表示功能的有意义的名称。用“_”分隔单词
例子:
#define IFXSCUCCU_PLL_FREE_RUNNING_FREQ (100000000U)
需要检查的重要事项: 所有大写字母
4.6 函数(Functions)
所有导出的函数:Ifx<HW模块名>[<HW子模块名>][SW Driver if special]<功能性>
例子:
IfxGtmTom_Pwm_setDutyCycle(..)
需要检查的重要事项:
1.文件名的第1到3点
2._<功能>:一个下划线分开和驼峰大小写字母的第一个字母小。(面向对象编程的约定)。功能应该从界面要实现的主要动作(动词)开始。
举例:
1.置引脚级别高的接口,函数名为setPinHigh。
2.读取ADC转换结果的接口为readResult
3.通过通信外设sendByte发送数据字节的接口
4.7 函数的形参和结构成员(Function's parameter & struct members)
函数参数没有做一个动作,但做了设置值的配置,那么在名称中没有动词。
例子:
typedef struct
{
boolean hardwareRequestEnabled; // \brief Enabling the hardware channel request
} IfxDma_Dma_ChannelConfig;
4.8 内联函数(Inline Functions)
所有内联函数如下:
函数定义IFX_INLINE < >
应遵循函数中的命名约定
4.9 Infineon标准数据类型的定义
位于Cpu / Std / Platform_Types.h
五、编码规则
5.1代码格式(Code formating)
5.1.1 自动化格式(Auto-formating)
请只使用以下脚本定义的代码格式:$VERIF_LLD/bin/indent_rec . txt
5.1.2 忽略自动格式化(Ignore auto-formating)
如果源代码不能被格式化工具格式化,可以使用以下命令:
// *INDENT-OFF* Note: this file was indented manually by the author.
... place here code that shall not be formated by the tool
// *INDENT-ON*
5.1.3 文件结构(File structure)
文件中的源代码应该使用doxygen组进行分组。文件中的组应该按照组名的字母顺序排序。在一个组内,代码应按API名称的字母顺序排序
5.2 一般规则(General rules)
总是使用花括号if, for, while, do, ...
正确的:
static void IfxDma_Dma_configureTransactionSet(Ifx_DMA_CH* channel, IfxDma_Dma_ChannelConfig* config)
{
...
if( config->shadowControl != 0)
{
channel->SHADR.U = config->shadowAddress;
}
}
错误的:
static void IfxDma_Dma_configureTransactionSet(Ifx_DMA_CH* channel, IfxDma_Dma_ChannelConfig* config)
{
...
if( config->shadowControl != 0)
channel->SHADR.U = config->shadowAddress;
}
5.3 代码检查(Code checking)
如果编译器不能检查参数的有效值,则应该使用断言函数来检查。
例子:
IFX_INLINE void IfxDma_setChannelTransaction(Ifx_DMA* dma,IfxDma_ChannelId channelId, uint32 transferCount)
{
IFX_ASSERT(VERBOSE_LEVEL_ERROR,transferCount < 32);
}
5.4许多模块驱动程序通用的api(APIs common to many module drivers)
5.4.1<modue>_get<Interrupt>Src()
对于每个中断服务请求控制寄存器,应该提供一个返回寄存器地址的API。该API的语法如下:volatile Ifx_SRC_SRCR* <driverName>_get<Interrupt>Src(<ModuleType>*module);
例如:QSPI驱动的传输中断有以下API: volatile Ifx_SRC_SRCR* IfxQspi_getTransmitSrc(Ifx_QSPI* module);
5.4.2 <modue>_getIndex()
API Ifx<Module>_getIndex(Ifx_< Module> * Module)应在文件Ifx<Module>.c/h中实现,并使用在Ifx<Module>_cfg.c/h中定义的数组;
例子:
In file Ifxcpu.h
IFX_EXTERN IfxCpu_ResourceCpu IfxCpu_getIndex(Ifx_CPU* cpu);
In file Ifxcpu.c
// brief Return cpu index
//
// \param cpu Specifies cpu module
//
// \return Return cpu index.
//
IfxCpu_ResourceCpu IfxCpu_getIndex(Ifx_CPU* cpu)
{
IfxCpu_ResourceCpu result = IfxCpu_ResourceCpu_none, index;
for (index = 0; index < IFXCPU_COUNT; index++)
{
if (IfxCpu_cfg_indexMap[index].module == cpu)
{
result = IfxCpu_cfg_indexMap[index].index;
break;
}
}
return result;
}
In file Ifxcpu_cfg.h
// List of the available CPU resources
typedef enum
{
IfxCpu_ResourceCpu_0 = 0, // CPU 0
IfxCpu_ResourceCpu_1, // CPU 1
IfxCpu_ResourceCpu_2, // CPU 2
IfxCpu_ResourceCpu_none // None of the CPU
} IfxCpu_ResourceCpu;
IFX_EXTERN IfxCpu_ResourceCpu IfxCpu_getIndex(Ifx_CPU* cpu);
In file Ifxcpu_cfg.c
const IfxModule_IndexMap IfxCpu_cfg_indexMap[IFXCPU_COUNT] =
{
{&MODULE_CPU0, IfxCpu_ResourceCpu_0},
{&MODULE_CPU1, IfxCpu_ResourceCpu_1},
{&MODULE_CPU2, IfxCpu_ResourceCpu_2},
};
六、Doxygen规则
6.1 Microcontroller root group
微控制器文档根组是IfxLld。组在文件信息中定义。Dox位于微控制器的导数根目录。
例子:
\defgroup IfxLld TC27x B-Step Microcontroller
6.2 Modules group
模块的组名有以下语法:'\defgroup IfxLld_<模块>'。这个组是微控制器根组的直接子组。
v <模块>是第一个字母大写,然后小写的模块短名称。组名定义在Ifx<Module>_regdef.h文件中。例子:
\defgroup IfxLld_Dma DMA \ingroup IfxLld
以下组也在Ifx<Module>_regdef.h文件中为每个模块定义:
1.fxLld_<模块>_Bitfields:位域宏定义
2.IfxLld_<模块>_Union:模块联合类型
3.IfxLld_<模块>_Struct:模块结构类型
6.3 Module standard driver group
模块的标准驱动组有以下语法:'\defgroup IfxLld_<模块>_Std'。这个组是Modules组的直接子组。
例子:
\defgroup IfxLld_Asclin_Std Basic functionality \ingroup IfxLld_Asclin
6.4 Module interface driver group
模块的接口驱动组有以下语法:'\defgroup IfxLld_<模块>_<接口>'。这个组是Modules组的直接子组。
<接口>为接口驱动程序名称。
例子:
\defgroup IfxLld_Asclin_AscIf ASC interface driver
Implements the standard ASC interface
\ingroup IfxLld_Asclin
6.5 General rules
1.由于组名是全局名称,所以组名必须以父组名开头。
2.组名是第一个大写字母,然后驼峰大小写,组级别由下划线分隔
例子:
1.|–IfxLld
2.| |–IfxLld_Dma
3.| | |–IfxLld_Dma_Std
4.| | |–IfxLld_Dma_Std_ChannelTransaction
5.| | |–IfxLld_Dma_Dma
6.6 Documentation of return values
当为单一情况提供文档时,应使用\return记录返回值。
当为多种情况提供文档时,应使用\retval记录返回值。
示例(单):
\return Return the raw converted analog value.
Output:
Returns
返回原始转换后的模拟值。
示例(多个):
\retval TRUE in case of success
\retval FALSE in case of error
Output:
Return values
TRUE | 成功 |
FALSE | 失败 |
6.7 parameterDocumentation
文件应在未完全定义参数时提供参数范围。
例子:
// \param transferCount Number of transaction, Range=[0,32]
IFX_INLINE void IfxDma_setChannelTransaction(Ifx_DMA* dma,IfxDma_ChannelId channelId, uint32 transferCount)
{ ... }