参考链接

C语言 | 嵌入式C语言编程规范_函数名开头加uc-CSDN博客

[!tip]

以下文件排版将会按照自顶向下的方式进行编排,==文件夹 > 文件名 > 排版 > 函数 > 变量==

文件架构

缩写

该项目文件树参考AUTOSAR结构,为了便于理解,此处展示部分缩写的全称:

缩写 全称 描述
AUTOSAR AUTomotive Open System ARchitecture 汽车开放系统架构,是汽车软件开发的标准框架。
ASW Application Software 应用软件,主要负责实现特定功能的业务逻辑。
BSW Basic Software 基础软件,负责底层硬件控制和通用服务的实现。
BSP Board Support Package 板级支持包,包含硬件初始化、驱动和相关工具。
HAL Hardware Abstraction Layer 硬件抽象层,提供对硬件的抽象访问接口。
MCAL Microcontroller Abstraction Layer 微控制器抽象层,提供对芯片外设的直接访问接口。
RTE Run-Time Environment 运行时环境,负责 ASW 与 BSW 的通信管理。
UDS Unified Diagnostic Services 统一诊断服务协议(ISO 14229),用于 ECU 的诊断通信。
Dcm Diagnostic Communication Manager 诊断通信管理模块,实现 UDS 服务的解析和处理。
Dem Diagnostic Event Manager 诊断事件管理模块,负责故障码存储和管理。
PduR Protocol Data Unit Router 协议数据单元路由模块,负责数据路由。
CanTp CAN Transport Protocol CAN 传输协议模块,用于实现 UDS 消息的传输。
NvM Non-Volatile Memory Manager 非易失性内存管理模块,负责数据的持久化存储。
SoAd Socket Adapter 套接字适配器,用于实现基于 TCP/IP 的通信协议(如 DoIP)。
ECU Electronic Control Unit 电子控制单元,是汽车的嵌入式控制器。
GPIO General Purpose Input/Output 通用输入输出,硬件外设的一种。
UART Universal Asynchronous Receiver-Transmitter 通用异步收发器,用于串行通信。
SPI Serial Peripheral Interface 串行外设接口,用于高性能串行通信。
I2C Inter-Integrated Circuit 一种常用的两线串行通信协议。
CAN Controller Area Network 控制器局域网,是汽车通信总线的一种。
FlexRay Flexible Ray 一种高速汽车通信协议,用于安全关键应用。
LIN Local Interconnect Network 局域互连网络,低成本汽车通信协议。
DoIP Diagnostics over Internet Protocol 基于 IP 协议的诊断通信。
OS Operating System 操作系统,提供任务调度和资源管理功能。
SWC Software Component 软件组件,用于实现应用逻辑,是 ASW 层的核心构成模块。

概览

1
2
3
4
5
6
7
8
├─.vscode
├─ASW 此处存放与硬件平台无关代码
├─BSW 存放与硬件相关代码,所以子文件夹需要区分不同芯片型号
├─BOOT 存放BOOT相关代码
├─Documents 存放项目相关的文档,为了方便使用,最好将使用的芯片的手册放于此处
├─OS 存放和系统相关的,如文件系统、操作系统、调度系统等等
├─Output 存放输出文件,请在设置Keil和IAR等软件时,选用输出到此文件夹,方便管理,并且增强Project中的整洁性
└─Project 存放和工程相关的文件树,如KEIL、IAR、SOURCEINSIGHT、TSMATSER、CANoe、JFLASH,与项目相关的工程请放置此处

[!important]

不知道要将代码放在哪个文件夹中需要参考以下准则:

  1. 与硬件无关的请存放于ASW,也就是逻辑相关
  2. 与硬件有关的请存放于BSW,也就是对接硬件平台
  3. OS中存放系统相关,如文件系统、操作系统、调度系统
  4. BOOT中存放和BOOT相关的文件,采用的协议文件,如UDS、OSI

[!note]

为了保持风格的一致性,高两级的文件夹请使用开头大写(ASW、OS、User、Components等),再低级的文件夹就使用小写(inc、src、cfg等)

ASW

1
2
3
4
5
6
7
8
9
10
11
├─.vscode
├─ASW 此处存放与硬件平台无关代码
│ ├─SWC 此处存放APP相关组件代码,如门窗控制器组件等等
│ │ └─btdm 此级文件夹下的文件树可以按照自己的喜好存放,如可以将c和h文件放在一起,也可按照不同的文件后缀对文件树进行构建
│ │ ├─inc 存放头文件
│ │ ├─lib 存放lib库文件
│ │ └─src 存放c文件
│ │ └─cfg 存放cfg文件
│ ├─User 一般用于存放main函数和相关app操作函数,一般来说main函数尽量不要有太多的内容,使用函数调用,方便在此基础上修改
│ └─Utils 存放工具文件,如delay、RTT、coreMark等工具。
│ └─Include 存放APP端的相关宏配置文件。

[!note]

注意,ASW中存放和硬件无关的代码,所以在写其中的代码,我们应该要尽量减少和硬件有关的代码,使用抽象代码,如使用定死的函数名访问,我们并不需要知道它下面是怎么实现的,我们只需要调用。(使用定死的函数名方便进行抽象)

[!important]

‼‼‼在ASW中写代码,一定要有抽象的概念‼‼‼

例如SWC是组件的意思,也就意味着,当你写好一个组件,那么我下次使用别的平台的时候可以直接调用你的组件,而不需要再修改很多东西,如定义一个宏开关来控制。

对于一个组件来说,我只需要知道你的组件的输入和输出即可,所以其中的代码尽量写的抽象,减少重复造轮子。

BSW

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├─BSW				存放与硬件相关代码,所以需要区分不同芯片型号
│ ├─Bsp 存放供应商和用户的接口函数
│ │ └─FR3032D 注意,这里需要定义到芯片的具体型号
│ │ ├─inc
│ │ └─src
│ ├─Cmsis 这里存放和芯片内核文件,如core_cm3.h、cmsis_compile.h等等类似文件
│ │ └─FR3032D
│ ├─Diagnostics 这里存放诊断相关文件,如UDS
│ ├─Scripts 存放脚本相关文件,如sct、icf、bat、py,和工程相关的脚本文件请都放至此处。在Keil中如果使用可视化宏文件请放置此处
│ │ └─UDS
│ └─Include 存放BSW端的相关宏配置文件及基础类型,如Std_Types.h。
│ ├─Drivers 这里存放驱动文件
│ │ └─FR3032D
│ └─StartUp 这里存放启动文件,如startUp.s等文件
│ └─FR3032D

[!tip]

需要考虑的是,是否要增加芯片厂商的文件夹,如ZX/FR3032D,如果要制定芯片厂商,请和plm等保持一致的缩写,如ZX代表智芯

BOOT

1
2
3
4
├─BOOT
│ └───BootLoader 存放Boot相关的代码,如控制启动流程等
│ ├─inc
│ └─src

Documents

1
2
├─Documents		存放和项目相关的手册、点检表、变更履历表,如果要存放过多的文件,请使用文件夹分类,便于查阅
│ └─FR3032D

OS

1
2
3
4
5
6
7
8
9
├─OS		
│ ├─FlashDB 文件系统
│ │ ├─inc
│ │ └─src
│ ├─CropFreeRTOS 裁剪的操作系统
│ └─FreeRTOS 操作系统
│ ├─inc
│ └─src

Output

1
2
3
├─Output
│ ├─Listings 存放链接文件和映射文件,注意该文件夹是软件生成的,在Keil或IAR中设置即可
│ ├─Objects 存放中间文件,如o文件、obj文件,该文件夹也是软件自动生成的,需要配置

[!note]

请务必在工程中设置output和listing以保证项目树的整洁性

Project

1
2
3
4
5
6
7
8
9
10
└─Project
├─KEIL
│ └─RTE
│ └─_blueTooth
├─SOURCEINSIGHT
│ └─blueTooth.si4project
│ ├─Backup
│ └─cache
│ └─parse
└─VSCODE

[!important]

和项目相关的工程请存放至此,我们常用的工程有:

  1. Keil
  2. IAR
  3. SourceInsight
  4. Vscode
  5. TSMaster
  6. Canoe
  7. ZLG
  8. JFLash
  9. QAC
  10. Tessy

[!note]

QAC和Tessy的测试输出报告请不要放置Document中,需要存放在Project下的相关文件夹中

ReadMe

ReadMe文件中用来存放和项目相关教程,如Flash、Ram分布,如何使用函数,当使用的时候需要修改哪些等等。

[!note]

最好是看完ReadMe就能知道项目怎么配置‼‼‼

文件

文件的命名要准确清晰地表达其内容,同时文件名应该精练,防止文件名过长而造成使用不便。在文件名中可以适当地使用缩写。 以下提供两种命名方式以供参考:

  1. 各程序模块的文件命名开头 2 个消协字母代表本模块的功能:如:主控程序为 mpMain.c,mpDisp.c 等。

  2. 不写模块功能标识:如:主控程序为 Main.c,Disp.c 等。

文件名命名规则

  1. 模块前缀
    • 文件名通常以模块或组件的缩写作为前缀,例如:
      • Com_:通信栈模块。
      • Dcm_:诊断通信管理模块。
      • EcuM_:ECU状态管理模块。
      • Os_:操作系统模块。
      • BswM_:基础软件管理模块。
  2. 文件类型
    • 文件名中通常包含文件类型的标识,例如:
      • _Cfg:配置文件。
      • _Types:类型定义文件。
      • _Api:API接口文件。
      • _Impl:实现文件。
      • _PbCfg:Post-build 配置文件。
  3. 文件内容描述
    • 文件名中应包含文件内容的简要描述,例如:
      • ComStack_Types.h:通信栈的类型定义文件。
      • Dcm_Cfg.c:诊断通信管理的配置文件。
  4. 文件扩展名
    • 头文件:.h
    • 源文件:.c
    • 配置文件:.c.h
    • 描述文件:.arxml(AUTOSAR XML 文件)
  5. 大小写
    • 文件名通常采用驼峰命名法(CamelCase),首字母大写。

命名标准

[!important]

请严格遵守以下命名准则书写文件名‼‼‼‼‼

通信栈模块(Com)

  • 类型定义文件:

    1
    ComStack_Types.h
  • 配置文件:

    1
    Com_Cfg.c
  • API接口文件:

    1
    Com_Api.h

诊断通信管理模块(Dcm)

  • 类型定义文件:

    1
    Dcm_Types.h
  • 配置文件:

    1
    Dcm_Cfg.c
  • 实现文件:

    1
    Dcm_Impl.c

基础板级支持包(BSP)

命名规则为:==Bsp_芯片型号 _模块名称==

1
2
3
4
Bsp_ZX116_ADC.h
Bsp_ZX116_ADC.c
Bsp_FR8016H_GPIO.h
Bsp_FR8016H_GPIO.c

ECU状态管理模块(EcuM)

  • 类型定义文件:

    1
    EcuM_Types.h
  • 配置文件:

    1
    EcuM_Cfg.c
  • API接口文件:

    1
    EcuM_Api.h

操作系统模块(Os)

  • 类型定义文件:

    1
    Os_Types.h
  • 配置文件:

    1
    Os_Cfg.c
  • API接口文件:

    1
    Os_Api.h

基础软件管理模块(BswM)

  • 类型定义文件:

    1
    BswM_Types.h
  • 配置文件:

    1
    BswM_Cfg.c
  • API接口文件:

    1
    BswM_Api.h

非易失性存储管理模块(Nvm)

  • 类型定义文件:

    1
    Nvm_Types.h
  • 配置文件:

    1
    Nvm_Cfg.c
  • API接口文件:

    1
    Nvm_Api.h

AUTOSAR XML 文件

  • 系统描述文件:

    1
    SystemDescription.arxml
  • ECU配置描述文件:

    1
    EcuConfiguration.arxml

AUTOSAR文件名命名总结

  1. 模块前缀:文件名以模块或组件的缩写作为前缀。

  2. 文件类型:文件名中包含文件类型的标识(如 _Cfg_Types)。

  3. 文件内容描述:文件名应简要描述文件内容。

  4. 文件扩展名:根据文件类型使用合适的扩展名(如 .h.c.arxml)。

  5. 大小写:采用驼峰命名法,首字母大写。

  6. 文件命名规范
    AUTOSAR对文件的命名有严格的规范,以确保文件的可读性和一致性。常见的命名规则包括:
    模块名前缀:文件名通常以模块名作为前缀。例如:(如:Dio_Cfg.h:DIO模块的配置文件、Can_PBcfg.c:CAN模块的Post-Build配置文件)

    文件类型后缀:文件名中包含文件类型的标识。例如:(_Cfg:表示配置文件、_PBcfg:表示Post-Build配置文、_LCfg:表示Link-Time配置文件。)
    版本信息:文件名中可以包含版本号或日期信息,以便于版本管理。

头文件

在写头文件时,可以按照以下的段落排版顺序写:

1
2
3
4
5
6
7
8
9
10
// 1、文件头注释
// 2、防止重复引用头文件的设置
// 3、#include 部分
// 4、enum 常量声明
// 5、类型声明和定义,包括 struct、union、typedef 等
// 6、全局变量声明
// 7、文件级变量声明
// 8、全局或文件级函数声明
// 9、函数实现。按函数声明的顺序排列
// 10、文件尾注释

如果使用绝对路径,当需要移动目录时,必须修改所有相关代码,繁琐且不安全;使用相对路径,当需要移动目录时,只需修改编译器的某个选项即可。例如:

1
2
#include “/project/inc/hello.h” /* 不应使用绝对路径 */
#include “../inc/hello.h” /* 可以使用相对路径 */=

在引用头文件时,注意<> “”的区别:

1
2
3
4
#include <stdio.h>      /* 标准头文件 */
#include <projdefs.h> /* 工程指定目录头文件 */
#include “global.h” /* 当前目录头文件 */
#include “inc/config.h” /* 路径相对于当前目录的头文件 */

在写头文件时要加上条件编译指令以防止头文件被重复引用:

1
2
3
4
5
#ifndef __DISP_H /* 文件名前名加两个下划线“__”,后面加 “_H”
#define __DISP_H
...
...
#endif

在头文件中只存放申明而不存放定义

1
2
/*模块1头文件:nodule1.h*/
extern int a 5; /*在模块1的.h文件中声明变量*/

并且文件的长度没有非常严格的要求,但应避免文件过长,一般来说,文件长度应尽量保持在1000行之内。

[!important]

尽量使用模板自动配置,具体配置见4.5,使用软件自动配置管理不仅可以节约时间,也可保持一致性。

排版

缩进与对齐

缩进

  • 使用 4 个空格 进行缩进,禁止使用 Tab(如果要使用Tab键,请确保一个Tab等于4个空格)。
  • 缩进应清晰反映代码的层次结构。

示例

1
2
3
4
5
6
7
void Function(void)
{
if (condition)
{
// Code block
}
}

对齐

  • 括号 {} 应独占一行,并与上一行的代码对齐。
  • 代码块内的语句应对齐。

示例

1
2
3
4
5
6
7
8
9
if (condition)
{
DoSomething();
DoAnotherThing();
}
else
{
DoElseThing();
}

行长度与换行

行长度

  • 每行代码不应超过 120 个字符
  • 如果一行代码过长,应将其拆分为多行。

示例

1
2
uint8 result = FunctionWithManyParameters(parameter1, parameter2,
parameter3, parameter4);

换行规则

  • 在运算符后换行,保持运算符在行尾。
  • 换行后应对齐到上一行的起始位置或适当缩进。

示例

1
2
uint8 result = value1 + value2 + value3 +
value4 + value5;

空格与空行

空格

  • 在运算符两侧添加空格。
  • 在逗号后添加空格。
  • 在关键字后添加空格。

示例

1
2
3
4
5
uint8 result = (value1 + value2) * value3;
for (uint8 i = 0; i < 10; i++)
{
// Code block
}

空行

  • 在函数之间添加空行,以分隔不同的代码块。
  • 在逻辑相关的代码块之间添加空行,以提高可读性。

示例

1
2
3
4
5
6
7
8
9
void Function1(void)
{
// Code block
}

void Function2(void)
{
// Code block
}

括号与分号

括号

  • 括号 {} 应独占一行,并与上一行的代码对齐。
  • 即使代码块只有一行,也应使用括号。

示例

1
2
3
4
if (condition)
{
DoSomething();
}

分号

  • 每个语句应以分号 ; 结尾。
  • 分号前不应有空格。

示例

1
2
uint8 value = 10;
DoSomething();

注释格式

单行注释

  • 使用 // 进行单行注释。
  • 注释应与代码对齐。

示例

1
uint8 value = 10;  // Initialize value

多行注释

  • 使用 /* ... */ 进行多行注释。
  • 注释内容应清晰、简洁。

示例

1
2
3
4
/*
* This is a multi-line comment.
* It provides additional information about the code.
*/

函数排版

函数声明

  • 函数声明应包含返回值类型、函数名和参数列表。
  • 参数列表应换行对齐。

示例

1
2
3
Std_ReturnType FunctionName(uint8 parameter1,
uint8 parameter2,
uint8 parameter3);

函数定义

  • 函数定义应包含详细的注释,描述其功能、参数和返回值。
  • 函数体应缩进 4 个空格。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/******************************************************************************
* Function: FunctionName
* Description: This function does something.
* Parameters:
* - parameter1: Description of parameter1.
* - parameter2: Description of parameter2.
* Return: E_OK if successful, E_NOT_OK otherwise.
******************************************************************************/
Std_ReturnType FunctionName(uint8 parameter1, uint8 parameter2)
{
// Function body
if (condition)
{
DoSomething();
}
return E_OK;
}

结构体与枚举排版

结构体

  • 结构体定义应包含详细的注释,说明其用途和成员的含义。
  • 结构体成员应对齐。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/******************************************************************************
* Structure: CanMessage_t
* Description: Represents a CAN message.
* Members:
* - id: The CAN message ID.
* - data: The CAN message data.
* - length: The length of the CAN message data.
******************************************************************************/
typedef struct
{
uint8 id;
uint8 data[8];
uint8 length;
} CanMessage_t;

枚举

  • 枚举定义应包含详细的注释,说明其用途和枚举值的含义。
  • 枚举值应对齐。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/******************************************************************************
* Enum: CanState_t
* Description: Represents the state of the CAN controller.
* Values:
* - CAN_STATE_UNINIT: CAN controller is uninitialized.
* - CAN_STATE_READY: CAN controller is ready.
* - CAN_STATE_ERROR: CAN controller is in error state.
******************************************************************************/
typedef enum
{
CAN_STATE_UNINIT = 0,
CAN_STATE_READY = 1,
CAN_STATE_ERROR = 2
} CanState_t;

注释

文件注释规范

文件头注释

每个文件(包括 .c.h 文件)应包含文件头注释,通常包括以下内容:

  1. 文件名:文件的名称。
  2. 描述:文件的简要功能描述。
  3. 作者:文件的作者或开发者。
  4. 版本:文件的版本号。
  5. 日期:文件的创建或修改日期。
  6. 版权声明:文件的版权信息。

注释格式

  • 使用 /* ... */ 进行多行注释。
  • 注释内容应清晰、简洁,避免冗余信息。

文件头注释示例

.c 文件注释

1
2
3
4
5
6
7
8
/******************************************************************************
* File: Can_Driver.c
* Description: CAN driver implementation for AUTOSAR.
* Author: John Doe
* Version: 1.0
* Date: 2023-10-01
* Copyright: (c) 2023 by Company Name. All rights reserved.
******************************************************************************/

.h 文件注释

1
2
3
4
5
6
7
8
/******************************************************************************
* File: Can_Driver.h
* Description: CAN driver interface for AUTOSAR.
* Author: John Doe
* Version: 1.0
* Date: 2023-10-01
* Copyright: (c) 2023 by Company Name. All rights reserved.
******************************************************************************/

文件内容注释

模块注释

在文件的开头,添加模块的详细描述,说明模块的功能、依赖关系和设计思路。

示例

1
2
3
4
5
6
7
8
9
10
11
12
/******************************************************************************
* Module: CAN Driver
* Description: This module implements the CAN driver for AUTOSAR. It provides
* functions for initializing the CAN controller, sending and
* receiving CAN messages.
* Dependencies:
* - Std_Types.h: Standard type definitions.
* - Can_GeneralTypes.h: CAN module type definitions.
* Design Notes:
* - The driver supports CAN 2.0A and 2.0B frames.
* - Error handling is implemented for invalid inputs and hardware errors.
******************************************************************************/

函数注释

每个函数应包含详细的注释,描述其功能、参数和返回值。

示例

1
2
3
4
5
6
7
8
9
10
/******************************************************************************
* Function: Can_Init
* Description: Initializes the CAN controller and configures the baud rate.
* Parameters:
* - None
* Return:
* - E_OK: Initialization successful.
* - E_NOT_OK: Initialization failed.
******************************************************************************/
Std_ReturnType Can_Init(void);

结构体和枚举注释

为结构体和枚举类型添加注释,说明其用途和成员的含义。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/******************************************************************************
* Structure: CanMessage_t
* Description: Represents a CAN message with ID, data, and length.
* Members:
* - id: The CAN message ID.
* - data: The CAN message data (up to 8 bytes).
* - length: The length of the CAN message data.
******************************************************************************/
typedef struct
{
uint8 id;
uint8 data[8];
uint8 length;
} CanMessage_t;

其他注释

代码块注释

在复杂的代码块前添加注释,解释其逻辑和实现思路。

示例

1
2
3
4
5
// Check if the CAN message is valid
if ((message.id > 0) && (message.length <= 8))
{
// Process the CAN message
}

修改记录

在文件的末尾或注释中添加修改记录,记录每次修改的内容、作者和日期。

示例

1
2
3
4
5
/******************************************************************************
* Modification History:
* - 2023-10-01: Version 1.0 by John Doe (Initial implementation).
* - 2023-10-05: Version 1.1 by Jane Smith (Added error handling).
******************************************************************************/

软件配置

Vscode

下载C/C++ Snippets插件,上网查询相关配置,该插件用于对代码进行位置分布做限定,参考配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
{
"resource files": {
"prefix": ".chinit",
"body": [
"/*==============================================================================",
"======= Includes =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= Defines & Macros for General Purpose =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= Constants & Types =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= Global variables =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= Local variables =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= Global Function =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= Local Function =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= Function Implement List =======",
"==============================================================================*/",
"",
],
"description": "use to c files"
},
"header files": {
"prefix": ".hhinit",
"body": [
"#ifndef ${1:${TM_FILENAME/(.*)\\.h$/${1:/upcase}_H/i}}",
"#define $1",
"\n${2:#ifdef __cplusplus",
"extern \"C\"{",
"#endif}",
"",
"/*==============================================================================",
"======= INCLUDES =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= DEFINES & MACROS FOR GENERAL PURPOSE =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= CONSTANTS & TYPES =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= EXPORTS =======",
"==============================================================================*/",
"",
"/*==============================================================================",
"======= PROTOTYPES OF PUBLIC FUNCTIONS =======",
"==============================================================================*/",
"",
"#ifdef __cplusplus",
"}",
"#endif",
"",
"#endif\t/* $1 */\n"
],
"description": "Avoid Repetition"
},
"line": {
"prefix": ".line",
"body": [
"/*************************************** ${xxx} ****************************************/",

],
"description": "Avoid Repetition"
}
}

下载KoroFileHeader插件,该配置用于生成头部申明和函数注释参考配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
"fileheader.customMade": {
// Author字段是文件的创建者 可以在specialOptions中更改特殊属性
// 公司项目和个人项目可以配置不同的用户名与邮箱 搜索: gitconfig includeIf 比如: https://ayase.moe/2021/03/09/customized-git-config/
// 自动提取当前git config中的: 用户名、邮箱
"Author": "git config user.name && git config user.email", // 同时获取用户名与邮箱
// "Author": "git config user.name", // 仅获取用户名
// "Author": "git config user.email", // 仅获取邮箱
// "Author": "OBKoro1", // 写死的固定值 不从git config中获取
"Date": "Do not edit", // 文件创建时间(不变)
// LastEditors、LastEditTime、FilePath将会自动更新 如果觉得时间更新的太频繁可以使用throttleTime(默认为1分钟)配置更改更新时间。
"LastEditors": "git config user.name && git config user.email", // 文件最后编辑者 与Author字段一致
// 由于编辑文件就会变更最后编辑时间,多人协作中合并的时候会导致merge
// 可以将时间颗粒度改为周、或者月,这样冲突就减少很多。搜索变更时间格式: dateFormat
"LastEditTime": "Do not edit", // 文件最后编辑时间
// 输出相对路径,类似: /文件夹名称/src/index.js
"FilePath": "Do not edit", // 文件在项目中的相对路径 自动更新
// 插件会自动将光标移动到Description选项中 方便输入 Description字段可以在specialOptions更改
"Description": "", // 介绍文件的作用、文件的入参、出参。
// custom_string_obkoro1~custom_string_obkoro100都可以输出自定义信息
// 可以设置多条自定义信息 设置个性签名、留下QQ、微信联系方式、输入空行等
"custom_string_obkoro1": "",
// 版权声明 保留文件所有权利 自动替换年份 获取git配置的用户名和邮箱
// 版权声明获取git配置, 与Author字段一致: ${git_name} ${git_email} ${git_name_email}
"custom_string_obkoro1_copyright": "Copyright (c) ${now_year} by Yasid, All Rights Reserved. "
// "custom_string_obkoro1_copyright": "Copyright (c) ${now_year} by 写死的公司名/用户名, All Rights Reserved. "
},
"fileheader.cursorMode": {

"description": "", // 函数注释生成之后,光标移动到这里
"param": "", // param 开启函数参数自动提取 需要将光标放在函数行或者函数上方的空白行
"return": "",
},

最终效果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
* @Author: ryf 1815338123@qq.com
* @Date: 2025-01-13 15:08:15
* @LastEditors: ryf 1815338123@qq.com
* @LastEditTime: 2025-01-13 16:54:04
* @FilePath: \Bluetooth\BSW\Bsp\FR8016H\src\Bsp_Api.c
* @Description:
*
* Copyright (c) 2025 by Yasid, All Rights Reserved.
*/
/*==============================================================================
======= Includes =======
==============================================================================*/
#include "driver_gpio.h"
/*==============================================================================
======= Defines & Macros for General Purpose =======
==============================================================================*/

/*==============================================================================
======= Constants & Types =======
==============================================================================*/

/*==============================================================================
======= Global variables =======
==============================================================================*/

/*==============================================================================
======= Local variables =======
==============================================================================*/

/*==============================================================================
======= Global Function =======
==============================================================================*/

/*==============================================================================
======= Local Function =======
==============================================================================*/

/*==============================================================================
======= Function Implement List =======
==============================================================================*/
/**
* @description:
* @return {*}
*/
void BspGpio_Init(void)
{
system_setport_mux(GPIO_PORT_A, GPIO_BIT_5,PORTA5_FUNC_A5);
gpio_set_dir(GPIO_PORT_A, GPIO_BIT_5, GPIO_DIR_OUT);
}

变量

变量类型的定义遵循严格的规范,以确保代码的可移植性、可读性和一致性。本规范定义了一套标准的数据类型,这些数据类型通常通过标准化类型定义文件(如 Std_Types.h)来实现。变量类型的前缀缩写用于标识变量的用途、模块或功能域。这些前缀缩写有助于提高代码的可读性和可维护性,同时确保变量命名的一致性和规范性。

常见变量类型前缀缩写

前缀缩写 含义 示例
u 无符号(unsigned) uint8:无符号8位整数;uint16:无符号16位整数。
s 有符号(signed) sint8:有符号8位整数;sint16:有符号16位整数。
f 浮点数(float) float32:32位浮点数;float64:64位浮点数。
p 指针(pointer) pBuf:指向缓冲区的指针;pData:指向数据的指针。
a 数组(array) aSignal:信号数组;aData:数据数组。
b 布尔(boolean) bFlag:布尔标志;bStatus:布尔状态。
e 枚举(enumeration) eState:状态枚举;eMode:模式枚举。
t 结构体(struct)或类型(type) tConfig:配置结构体;tData:数据结构体。
n 数量(number)或计数器(counter) nCount:计数器;nSize:大小。
c 常量(constant) cMaxValue:最大值常量;cMinValue:最小值常量。
k 配置常量(configuration constant) kConfigParam:配置参数常量。
g 全局变量(global) g_State:全局状态;g_Data:全局数据。
m 模块内部变量(module internal) m_InternalData:模块内部数据;m_State:模块内部状态。
r 引用(reference) rData:数据引用;rConfig:配置引用。
h 句柄(handle) hModule:模块句柄;hResource:资源句柄。
x 扩展类型(extended type)或复杂类型 xComplexData:复杂数据类型;xExtendedConfig:扩展配置类型。

[!important]

组合的顺序为:

1
[作用域前缀][数据类型前缀]_[变量含义]

作用域前缀可选:

前缀 含义 示例
g 全局变量(extern修饰或在c文件定义的全局变量且没有static修饰) gu8Counter
m 模块级静态变量(文件中使用static定义的变量) mu8Counter
p 指针类型 pu8Counter
局部变量(函数中的变量、形参。结构体中的变量) u8Counter

==注意p可以和m或g组合,组合顺序为先g或m,之后再加p。g和m互斥,也就是说前缀可以是mp、gp,但不可以g和m同时出现,如gmp。==

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct _test
>{
uint8 u8Counter,
uint8* pu8Counter
}Test_t;

static uint16 mu16Counter;
uint16* gpu16Counter;
Test_t gtCounter;



void Func(uint8 u8Counter,uint8* pu8Counter)
{
sint8 s8Test = 0;
gtCounter.u8Counter = u8Counter;
gtCounter.pu8Counter = pu8Counter;
}

模块相关变量前缀缩写

除了通用的变量前缀缩写,还有一些与模块相关的变量前缀缩写:

前缀缩写 模块/功能域 示例
Com_ 通信栈(Communication Stack) Com_Signal:通信信号;Com_Pdu:通信PDU。
Pdu_ 协议数据单元(Protocol Data Unit) Pdu_Info:PDU信息;Pdu_Length:PDU长度。
Can_ CAN通信模块(CAN Driver) Can_Pdu:CAN PDU;Can_State:CAN状态。
Lin_ LIN通信模块(LIN Driver) Lin_Pdu:LIN PDU;Lin_Config:LIN配置。
Eth_ 以太网通信模块(Ethernet Driver) Eth_Frame:以太网帧;Eth_Config:以太网配置。
Dcm_ 诊断通信管理(Diagnostic Communication Manager) Dcm_Request:诊断请求;Dcm_Response:诊断响应。
Dem_ 诊断事件管理(Diagnostic Event Manager) Dem_Event:诊断事件;Dem_Status:诊断状态。
Nvm_ 非易失性存储管理(NVRAM Manager) Nvm_Block:NVM块;Nvm_Data:NVM数据。
EcuM_ ECU状态管理(ECU Manager) EcuM_State:ECU状态;EcuM_Mode:ECU模式。
BswM_ 基础软件管理(Basic Software Manager) BswM_Mode:BSW模式;BswM_State:BSW状态。
Os_ 操作系统(Operating System) Os_Task:OS任务;Os_Alarm:OS报警。
SchM_ 调度管理(Scheduler Manager) SchM_Task:调度任务;SchM_Config:调度配置。
Wdg_ 看门狗管理(Watchdog Manager) Wdg_Mode:看门狗模式;Wdg_Config:看门狗配置。
Adc_ 模拟数字转换(ADC Driver) Adc_Channel:ADC通道;Adc_Value:ADC值。
Gpt_ 通用定时器(General Purpose Timer) Gpt_Channel:GPT通道;Gpt_Config:GPT配置。
Mcu_ 微控制器驱动(Microcontroller Driver) Mcu_Mode:MCU模式;Mcu_Config:MCU配置。
Port_ 端口驱动(Port Driver) Port_Pin:端口引脚;Port_Config:端口配置。
Dio_ 数字输入输出(Digital I/O Driver) Dio_Channel:DIO通道;Dio_Config:DIO配置。
Pwm_ 脉宽调制(PWM Driver) Pwm_Channel:PWM通道;Pwm_Config:PWM配置。
Icu_ 输入捕获单元(Input Capture Unit) Icu_Channel:ICU通道;Icu_Config:ICU配置。
Rte_ 运行时环境(Runtime Environment) Rte_Instance:RTE实例;Rte_Config:RTE配置。

基本数据类型

Std_Types.h 中定义的标准基本数据类型:

1
2
3
4
5
6
7
8
typedef unsigned char         uint8;      // 无符号8位整数
typedef signed char sint8; // 有符号8位整数
typedef unsigned short uint16; // 无符号16位整数
typedef signed short sint16; // 有符号16位整数
typedef unsigned long uint32; // 无符号32位整数
typedef signed long sint32; // 有符号32位整数
typedef float float32; // 32位浮点数
typedef double float64; // 64位浮点数

布尔类型

AUTOSAR定义了标准的布尔类型:

1
2
3
typedef unsigned char         boolean;    // 布尔类型
#define TRUE ((boolean)1)
#define FALSE ((boolean)0)

状态类型

AUTOSAR定义了标准的状态类型:

1
2
3
typedef uint8 Std_ReturnType;             // 标准返回类型
#define E_OK ((Std_ReturnType)0) // 操作成功
#define E_NOT_OK ((Std_ReturnType)1) // 操作失败

复合数据类型

复合数据类型(如结构体、枚举)通常用于表示复杂的数据结构。

  • 结构体示例

    1
    2
    3
    4
    typedef struct {
    uint8 channelState; // 通道状态
    uint8 channelId; // 通道ID
    } ComM_Channel_t; // 通信管理模块的通道类型
  • 枚举示例

    1
    2
    3
    4
    typedef enum {
    COM_M_CHANNEL_INACTIVE, // 通道未激活
    COM_M_CHANNEL_ACTIVE // 通道激活
    } ComM_ChannelState_e; // 通信管理模块的通道状态类型

指针类型

AUTOSAR中通常使用标准化的指针类型:

1
2
typedef uint8* Ptr_u8;        // 指向uint8的指针
typedef const uint8* Ptr_const_u8; // 指向const uint8的指针

数组类型

数组类型通常用于表示一组相同类型的数据:

1
typedef uint8 Com_SignalArrayType[10];  // 通信信号数组类型

常量、宏、模版的名字应该全部大写。如果这些名字由多个单词组成,则单词之间用下划线分隔。

1
#define LOG_BUF_SIZE 8000

配置头文件

[!important]

请务必将此文件加入到BSW/Include或ASP/Include文件夹中,该文件为基本类型定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#ifndef STD_TYPES_H
#define STD_TYPES_H

#include<stdio.h>

// 标准数据类型定义
typedef unsigned char uint8;
typedef signed char sint8;
typedef unsigned short uint16;
typedef signed short sint16;
typedef unsigned long uint32;
typedef signed long sint32;
typedef unsigned long long uint64;
typedef signed long long sint64;
typedef float float32;
typedef double float64;

// 布尔类型定义
typedef unsigned char boolean;

#ifndef TRUE
#define TRUE ((boolean) 1)
#endif

#ifndef FALSE
#define FALSE ((boolean) 0)
#endif

// 状态类型定义
#define E_OK ((Std_ReturnType) 0x00)
#define E_NOT_OK ((Std_ReturnType) 0x01)

// NULL 指针定义
#ifndef NULL
#define NULL ((void *)0)
#endif

// 位操作宏
#define SET_BIT(reg, bit) ((reg) |= (1 << (bit)))
#define CLEAR_BIT(reg, bit) ((reg) &= ~(1 << (bit)))
#define TOGGLE_BIT(reg, bit) ((reg) ^= (1 << (bit)))
#define GET_BIT(reg, bit) (((reg) >> (bit)) & 0x01)

#ifdef __cplusplus
extern "C" {
#endif

#ifdef __cplusplus
}
#endif

#endif // STD_TYPES_H

函数

命名规则

  1. 函数的命名规则:

    每一个函数名前缀需包含模块名,模块名为小写,与函数名区别开。

    1
    uartReceive(串口接收)

    对于非常简单的程序,可以不加模块名。

  2. 函数形参

    函数的的形参都以下划线_开头,已示与普通变量进行区分,对于没有形参为空的函数(void)括号紧跟函数后面。

    1
    2
    3
    4
    uint32_t uartConvUartBaud(uint32_t _ulBaud)
    {

    }
  3. 一个函数仅完成一件功能。

  4. 函数名应准确描述函数的功能,使用动宾词组为执行某操作的函数命名。

    避免用含义不清的动词如processhandle等为函数命名,因为这些动词并没有说明要具体做什么。参照如下方式命名函数。

    1
    2
    3
    void PrintRecord(uint32_t _RecInd);
    int32 InputRecord (void);
    uint8 t GetCurrentColor(void);
  5. 避免设计五个以上参数函数,不使用的参数从接口中去掉。目的减少函数间接口的复杂度,复杂的参数可以使用结构传递。

  6. 在调用函数填写参数时,应尽量减少没有必要的默认数据类型转换或强制数据类型转换。因为数据类型转换或多或少存在危险。

  7. 防止把没有关联的语句放到一个函数中。如下函数就是一种随机内聚。

    1
    2
    3
    4
    5
    6
    7
    void InitVar(void)
    {
    Rect.length = 0;
    Rect.width = 0;/*初始化矩形的长与宽*/
    Point.x = 10;
    Point.y = 10;/*初始化“点”的坐标*/
    }

    矩形的长、宽与点的坐标基本没有任何关系,故以上函数是随机内聚。应如下分为两个函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void InitRect(void)
    {
    Rect.length = 0;
    Rect.width = 0;/*初始化矩形的长与宽*/
    }
    void InitPoint(void)
    {
    Point.x = 10;
    Point.y = 10;/*初始化“点”的坐标*/
    }

API接口函数的命名遵循严格的规范,以确保代码的可读性、一致性和可维护性。 API接口函数通常用于模块之间的交互,例如操作系统(OS)、通信栈(COM)、诊断栈(DCM)等。以下是 API接口函数命名的详细规则和示例。

  • 前缀:模块或服务的缩写(如 Os_Com_Dcm_BspBsp<模块名>)。

  • 动作描述:描述函数的主要操作(如 InitStartGetSet)。

  • 对象描述:描述操作的对象(如 StateValueMode)。

  • 大小写:采用驼峰命名法,首字母大写。

    例如:

1
void BspWdg_Init(void);
  1. 前缀

    • 使用模块或服务的缩写作为前缀,例如:
      • Os_:操作系统(OS)模块。
      • Com_:通信栈(COM)模块。
      • Dcm_:诊断通信管理(DCM)模块。
      • EcuM_:ECU状态管理(ECU Manager)模块。
      • SchM_:调度管理(Scheduler Manager)模块。
      • BspGpioBspAdc
  2. 动作描述

    • 描述函数的主要操作,例如:
      • Init:初始化。
      • Start:启动。
      • Stop:停止。
      • Get:获取数据。
      • Set:设置数据。
      • MainFunction:主函数(周期性调用)。
  3. 对象描述

    • 描述函数操作的对象或目标,例如:
      • State:状态。
      • Value:值。
      • Mode:模式。
      • Status:状态。
  4. 大小写

    • 采用驼峰命名法(CamelCase),首字母大写。

BSP层函数名详细设计

  1. 前缀+模块名称

    • 使用 Bsp_ 作为前缀,表示属于BSP层的函数。

    • 如果BSP模块进一步细分(如GPIO、ADC等),可能会使用更具体的前缀,例如 BspGpio_BspAdc_

    • 模块名称:指明具体的硬件模块或功能,例如 GpioAdcPwm 等。

  2. 动作描述

    • 描述函数的具体操作,例如 ReadWriteInitSet 等。
  3. 对象描述

    • 描述函数操作的对象或目标,例如 PinChannelPort 等。
  4. 大小写

    • 采用驼峰命名法(CamelCase),首字母大写。

[!important]

请严格参照以下写法书写BSP函数接口以方便系统移植‼‼‼‼

GPIO模块

  • 初始化GPIO:

    1
    void BspGpio_Init(void);
  • 设置GPIO引脚状态:

    1
    void BspGpio_SetPin(uint32_t Port,uint32_t Pin,uint32_t State);
  • 读取GPIO引脚状态:

    1
    uint8_t* BspGpio_ReadPin(uint32_t Port,uint32_t Pin);

ADC模块

  • 初始化ADC:

    1
    void BspAdc_Init(void);
  • 读取ADC通道的值:

    1
    uint16_t BspAdc_ReadChannel(uint8 Channel);

PWM模块

  • 初始化PWM:

    1
    void BspPwm_Init(void);
  • 设置PWM占空比:

    1
    void BspPwm_SetDutyCycle(uint8 Channel, uint8 DutyCycle);

CAN模块

  • 初始化CAN控制器:

    1
    void BspCan_Init(void);
  • 发送CAN消息:

    1
    void BspCan_WriteMessage(uint8 MessageId, uint8* Data, uint8 Length);

Timer模块

  • 初始化定时器:

    1
    void BspTimer_Init(void);
  • 启动定时器:

    1
    void BspTimer_Start(uint8 TimerId);

Watchdog模块

  • 初始化看门狗:

    1
    void BspWdg_Init(void);
  • 刷新看门狗:

    1
    void BspWdg_Refresh(void);

操作系统(OS)模块

  • 启动操作系统:

    1
    void Os_Start(void);
  • 获取任务状态:

    1
    StatusType Os_GetTaskState(TaskType TaskId, TaskStateRefType State);

通信栈(COM)模块

  • 初始化通信栈:

    1
    void Com_Init(void);
  • 发送信号:

    1
    Std_ReturnType Com_SendSignal(Com_SignalIdType SignalId, const void* SignalData);
  • 通信栈主函数:

    1
    void Com_MainFunction(void);

诊断通信管理(DCM)模块

  • 初始化诊断模块:

    1
    void Dcm_Init(void);
  • 处理诊断请求:

    1
    Std_ReturnType Dcm_ProcessRequest(Dcm_MessageType RequestMessage);

ECU状态管理(EcuM)模块

  • 初始化ECU状态管理:

    1
    void EcuM_Init(void);
  • 启动ECU:

    1
    void EcuM_Start(void);

调度管理(SchM)模块

  • 初始化调度管理:

    1
    void SchM_Init(void);
  • 主调度函数:

    1
    void SchM_MainFunction(void);

存储栈(NVM)模块

  • 初始化NVM模块:

    1
    void Nvm_Init(void);
  • 读取数据:

    1
    Std_ReturnType Nvm_ReadBlock(Nvm_BlockIdType BlockId, void* Data);

看门狗管理(Wdg)模块

  • 初始化看门狗:

    1
    void Wdg_Init(void);
  • 刷新看门狗:

    1
    void Wdg_Refresh(void);

注意事项

  1. 一致性:确保所有API接口函数的命名风格一致。
  2. 可读性:函数名应清晰表达其功能,便于开发人员理解。
  3. 模块化:根据模块的功能划分API接口,避免功能混杂。
  4. 标准化:遵循AUTOSAR官方文档中的命名规范,确保与AUTOSAR标准兼容。

6.3 函数设计

在 AUTOSAR 中,函数设计规范是确保代码可读性、可维护性和一致性的重要部分。AUTOSAR 的函数设计规范通常基于 MISRA C 标准,并结合汽车电子系统的特殊需求。以下是 AUTOSAR 中函数设计规范的主要内容:


6.3.1. 函数命名规范

命名规则

  • 模块前缀:函数名应以模块名作为前缀(如 Can_ 表示 CAN 模块,Dcm_ 表示诊断通信管理模块)。
  • 动作描述:函数名应清晰描述其功能,使用动词开头。
  • 命名风格:使用驼峰命名法(CamelCase)。

示例

1
2
void Can_Init(void);                      // CAN 模块初始化
Std_ReturnType Can_SendMessage(uint8 messageId, uint8* data); // 发送 CAN 消息

6.3.2. 函数长度

函数长度限制

  • 每个函数的代码行数不应超过 50 行
  • 如果函数逻辑复杂,应将其拆分为多个子函数。

6.3.3. 函数参数

参数数量

  • 函数的参数数量不应超过 5 个
  • 如果参数过多,应考虑使用结构体封装。

示例

1
2
3
4
5
6
7
8
typedef struct
{
uint8 messageId;
uint8* data;
uint8 length;
} CanMessage_t;

Std_ReturnType Can_SendMessage(const CanMessage_t* message);

数类型

  • 使用 AUTOSAR 定义的标准数据类型(如 uint8sint16float32 等)。
  • 使用 const 修饰只读参数。

示例

1
void Can_ProcessMessage(const uint8* data, uint8 length);

参数顺序

  • 输入参数在前,输出参数在后。
  • 如果函数有返回值,输出参数应放在最后。

示例

1
Std_ReturnType Can_ReadMessage(uint8 messageId, uint8* data, uint8* length);

6.3.4. 返回值

返回值类型

  • 使用 Std_ReturnType 作为函数返回值类型,表示函数执行状态。

    1
    2
    3
    typedef uint8 Std_ReturnType;
    #define E_OK 0x00
    #define E_NOT_OK 0x01

示例

1
Std_ReturnType Can_Init(void);

返回值检查

  • 调用函数时,应检查其返回值,确保错误被正确处理。

示例

1
2
3
4
5
Std_ReturnType ret = Can_Init();
if (ret != E_OK)
{
// Handle error
}

6.4 函数内部设计

局部变量

  • 局部变量应在函数开头声明,并初始化。
  • 变量名应清晰表达其用途。

示例

1
2
3
4
5
6
void Can_ProcessMessage(const uint8* data, uint8 length)
{
uint8 messageId = data[0];
uint8 payloadLength = length - 1;
// Process message
}

控制结构

  • 使用括号明确条件表达式的优先级。
  • 避免使用复杂的嵌套条件语句。

示例

1
2
3
4
if ((u8Value > 0) && (u8Value < 100))
{
// Code block
}

错误处理

  • 在函数内部检查输入参数的有效性,并在发现错误时立即返回。
  • 使用 assert 检查程序中的假设条件。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
Std_ReturnType Can_SendMessage(uint8 messageId, const uint8* data, uint8 length)
{
if (data == NULL)
{
return E_NOT_OK; // Invalid input parameter
}
if (length > MAX_MESSAGE_LENGTH)
{
return E_NOT_OK; // Invalid message length
}
// Send message
return E_OK;
}

6.5 函数复用与模块化

复用性

  • 将通用功能封装为独立的函数,以便复用。
  • 避免在多个函数中重复相同的代码。

示例

1
2
3
4
5
6
7
8
9
uint8 CalculateChecksum(const uint8* data, uint8 length)
{
uint8 checksum = 0;
for (uint8 i = 0; i < length; i++)
{
checksum += data[i];
}
return checksum;
}

模块化

  • 将功能相关的函数组织在同一个模块中。
  • 使用头文件声明模块的接口函数。

示例

1
2
3
4
5
6
7
8
9
10
// can_driver.h
#ifndef CAN_DRIVER_H
#define CAN_DRIVER_H

#include "Std_Types.h"

Std_ReturnType Can_Init(void);
Std_ReturnType Can_SendMessage(uint8 messageId, const uint8* data, uint8 length);

#endif // CAN_DRIVER_H