软件构架(硬件无关、接口、分层)方法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Snail Studio
• C语言存储器分配
– 高级语言通常具有两种分配方式
>> 嵌入式C语言常用语法和扩展
• 静态分配:地址和占用的存储器大小在编译时刻就确定了
– 全局变量 » 在任意.c文件中都可以通过extern方式对变量声明后直接访问 – 静态局部变量 » 只能在某一个.c文件中使用
• 动态分配:地址和占用的存储器大小在运行时刻才能确定
Snail Studio
• 内存对齐方式
– 对齐到字节
>> 嵌入式C语言常用语法和扩展
– 对齐到双字节
– 对齐到4字节
Snail Studio
• 百度文库端(Endian)
– 大端系统(Big-endian)
>> 嵌入式C语言常用语法和扩展
• 逻辑最高位存储在物理最低位;逻辑较高位存储在物理较低位的内存 模式称为大端对齐;使用该存储模式的计算机系统称为大端系统;
Snail Studio
• 单元位掩码 _BV()
– 一个不安全的位定义方式
• #define _BV(__N)
>> 嵌入式C语言常用语法和扩展
(1 << (__N))
– 这种定义方式1的类型没有指定,在不同编译环境下可能造成数据溢出, 比如: uint32_t wValue = _BV(17); 在预编译阶段,_BV(17)会被替换为(1 << (17));由于1的类型在宏中没 有强制给定,因此随着编译器对1的默认类型理解不同,以上表达式可能 会出现错误,比如很多8位机默认整数常数都是int型变量,显然,对一 个int型变量进行17位的左移运算会获得一个“零值”这不是我们需要的结 果。
Snail Studio
• C语言存储器分配
>> 嵌入式C语言常用语法和扩展
– 使用指针进行“危险类型转换的精髓”:
• 因为指针本质就是一个整型变量,因此可以通过赋值的方法修改指针 的“属性A” 例如: uint16_t hwValue[2] = {0x1234,0x5678}; uint16_t *phw = hwValue; //!< 设置属性A … phw = &hwValue[1]; //!< 修改属性A • 通过强制类型转换的方式,可以临时修改一个指针所指向变量的“属 性B”和“属性C” 例如: float fValue = 3.1415926; uint8_t *pchArray = (uint8_t *)&fValue; //!< 修改属性B和C
• 硬件抽象层
– 相关定义
• Driver: • BSP: 与芯片外设直接相关的驱动程序 与芯片所在电路板上外围电路直接相关的驱动程序 通常包括调用Driver根据电路板的功能封装和固定 出一些硬件功能。比如调用GPIO驱动封装出LED 控制的驱动程序。 某些芯片外围硬件模块的驱动,比如LCD模块,比 如wifi模块驱动等等。Component通常要调用芯片 的外设驱动。Component和BSP的区别是,BSP的 很多驱动仅仅是针对目标电路板的;而Component 则针对某些固定模块编写的,具有很强的“软构件” 属性。另外,某些模拟的外设,比如模拟的IIC接口 也以Component的形式存在。 上层系统访问硬件抽象层的唯一接口
• C语言存储器分配
>> 嵌入式C语言常用语法和扩展
– C语言中的变量从存储器的角度来说具有3个属性
• 起始地址(属性A) • 占用的连续存储空间大小(属性B) • 如何操作这段存储器(属性C)
– 指针是一个整型变量,保存“属性A”——这是运行时刻可以修改的 – 指针的类型用来表示“属性B”和“属性C”——这些信息是编译时刻 确定的,不可修改,并且只能通过影响操作指针相关的代码来存 储和表示。
Snail Studio
>> 基础培训
• • • • • 嵌入式C语言常用语法和扩展 典型嵌入式系统构架 面向接口开发及头文件包含规则 黑盒子开发原则 SVN使用
Snail Studio
• 标准数据类型
>> 嵌入式C语言常用语法和扩展
– 在C语言开发时尽可能使用标准数据类型
• 标准数据类型的定义(以8位机为例) typedef unsigned char uint8_t; typedef signed char int8_t; typedef unsigned short uint16_t; typedef signed short int16_t; typedef unsigned int uint32_t; typedef signed int int32_t; … • 头文件的包含(stdint.h)
– GCC中直接使用 #include <stdint.h> – IAR中 » 使用DLIB的情况下直接使用 #include <stdint.h> » 使用CLIB的情况下应该在compiler.h中手工定义
Snail Studio
• Boolean型的定义和使用
>> 嵌入式C语言常用语法和扩展
– 使用位掩码将指定二进制位置位
• <变量> |= <位掩码> 例如:wValueA |= _BME(3,6);
>> 嵌入式C语言常用语法和扩展
//!< 将BIT3~BIT6置位
– 使用位掩码将指定二进制位清零
• <变量> &= ~ <位掩码> 例如:wValueA &=~ _BME(2,10); //!< 将BIT2~BIT10清零
– 一些关于内存分配方式优化的简单建议
• 当系统已经开发完成,在优化阶段,如果可能,为了追求系统的稳定 性,尽可能将所有的动态分配修改为静态分配 • 系统开发时,对于函数的编写,尽可能使用局部变量(动态分配)。 这既可以保证函数的可重入性;也可以给编译器留下足够的优化空间。 • 使用指针传递变量的时候要注意
– 获取从指定位置开始的n个连续二进制位掩码
#define _BME(__N1,__N2) (_BM((__N2)+1) - _BM((__N1)))
例如 uint16_t hwValue = wValue & _BME(4,7); // 取BIT4~BIT7
Snail Studio
• 基本的逻辑位运算
– – – – 指针可以安全的传递静态分配的数据; 指针只能将栈分配的变量作为被调函数的传入参数; 不能将栈分配的变量地址作为函数的返回参数; 堆分配的变量,分配后要检查是否分配成功(检查是否为NULL),释放 前要先将指针置空(设置为NULL);
– 要保证堆分配操作的原子性。
Snail Studio
– 在GCC和IAR DLIB模式下直接使用#include <stdbool.h> – 在IAR CLIB和其他编译环境下可以自行定义
• 使用与处理器位数等宽的变量类型作为布尔型变量的基础类型
– 8位机 typedef uint8_t bool; – 16位机 typedef uint16_t bool; – 32位机 typedef uint32_t bool; 这样做的目的是保证处理器能在大部分条件下保证bool变量访问的原子性。
– 栈分配 » 理论上所有的局部变量都是栈分配,实际上很多小数据类型的局部 变量是分配在寄存器页上的(使用寄存器来存储) – 堆分配 » 使用malloc以及相关函数分配的存储器资源 » 使用自己编写的动态内存管理程序分配的静态内存
Snail Studio
• C语言存储器分配
>> 嵌入式C语言常用语法和扩展
• 一个数字本身语义明确的例子 PORTA |= _BV(1); //!< 将PA1置位 • 一个需要对位编号宏定义的例子 #define XXEN _BV(1) //!< XX模块的使能标志位 #define XXIEN _BV(3) //!< XX模块的中断使能标志位 … XXCR |= XXEN | XXIEN; //!< 开启XX模块并使能中断 或者 #define XXEN 1 #define XXIEN 3 … XXCR |= _BV(XXEN) | _BV(XXIEN);
• Component:
• Hal.h
Snail Studio
>> 典型嵌入式系统构架
• 硬件抽象层典型框图
hal.h Component BSP Driver Embedded Hardware
– 小端系统(Little-endian)
• 逻辑最低位存储在物理最低位;逻辑较低位存储在物理较低位的内存 模式称为小端对齐;使用该存储模式的计算机系统成为小端系统;
– 通讯系统中通讯双方要约定好使用的尾端模式 – 大小端系统的转换
Snail Studio
• 尾端(Endian)
– 尾端结构示意图
Snail Studio
• 位运算原则
>> 嵌入式C语言常用语法和扩展
– 如果一个寄存器同时拥有“状态标志位”和“控制标志位”
• 如果状态标志位始终是只读的,则可以使用普通的逻辑位运算对该寄 存器进行操作 • 如果状态标志位是可写的则需要注意检查这些标志位写0和写1的意 义,因为可能存在以下陷阱;
Snail Studio
• 联合体、结构体和位段
– – – – 联合体 结构体 位段 IAR对匿名结构体和联合体的支持
>> 嵌入式C语言常用语法和扩展
Snail Studio
• 内存对齐方式
– 对齐到字节 – 对齐到双字节 – 对齐到四字节
>> 嵌入式C语言常用语法和扩展
– 通讯系统中,通讯双方议定要约定好内存对齐方式,通常习惯采 用的对齐方式是对齐到字节。
>> 嵌入式C语言常用语法和扩展
Snail Studio
• 尾端(Endian)
– 大/小端转换示意图
>> 嵌入式C语言常用语法和扩展
Snail Studio
>> 典型嵌入式系统构架
• 典型的裸机构架
Application Layer
• 典型的操作系统构架
Application Layer
Services Layer
– 如果写“1”可以实现清零,而写“0”没有效果,则不可以使用逻辑位运算 操作该寄存器(除非特别用掩码绕开对应二进制位) 造成这种限制的原因是,假使寄存器中某状态标志为1,我们通过逻辑位 运算对该寄存器进行操作的时候,实际上分为3步: 1)将寄存器值读出 2)改写寄存器的目标二进制位,同时不影响其它位 3)将修改后的值写回。 问题就出在第三步,因为某状态标志是1,而对该状态标志写1会自动清 零该标志,这就导致了无意间的一个误操作。 – 如果写“0”可以实现清零,而写“1”没有效果,则可以使用逻辑位运算
– 一个行为确定的位定义方式
• #define _BV(__N) ((uint16_t)1 << (__N))
Snail Studio
• 位掩码的获取
>> 嵌入式C语言常用语法和扩展
– 获取从BIT0开始的n个连续二进制位掩码
#define _BM(__N) (_BV(__N) – 1) //借助_BV()来定义 #define _BM(__N) ((uint32_t)1 << (__N)) //直接定义法 例如 uint16_t hwValue = wValue & _BM(4); // 取BIT0~BIT3
– 使用位掩码将指定二进制位取反
• <变量> ^= <位掩码> 例如:wValueA ^= _BM(4); //!< 将BIT0~BIT3取反
Snail Studio
• 位运算原则
>> 嵌入式C语言常用语法和扩展
– 同时操作多个二进制位时,尽可能使用位掩码;同时除非数字本身意义 明确,否则尽可能使用宏对位编号进行定义
• 布尔常量的定义
– False有明确的定义 #define false 0 – True要根据False来定义 #define true (!false) 虽然时常能看到有的头文件将true等效为1,这是存在隐患的,因为!false并不一定 是1。因此永远不要拿bool变量和true进行比较 if (true == bAvailable) //错误的方式 if (bAvailable) //正确的方式
OS / OS Service Layer
Hardware Abstract Layer Embedded Hardware Software Framework
Services Layer
Hardware Abstract Layer Embedded Hardware
Snail Studio
>> 典型嵌入式系统构架