当前位置:文档之家› 软件构架(硬件无关、接口、分层)方法

软件构架(硬件无关、接口、分层)方法


• Component:
• Hal.h
Snail Studio
>> 典型嵌入式系统构架
• 硬件抽象层典型框图
hal.h Component BSP Driver Embedded Hardware
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)
>> 嵌入式C语言常用语法和扩展
Snail Studio
• 尾端(Endian)
– 大/小端转换示意图
>> 嵌入式C语言常用语法和扩展
Snail Studio
>> 典型嵌入式系统构架
• 典型的裸机构架
Application Layer
• 典型的操作系统构架
Application Layer
Services Layer
Snail Studio
• 内存对齐方式
– 对齐到字节
>> 嵌入式C语言常用语法和扩展
– 对齐到双字节
– 对齐到4字节
Snail Studio
• 尾端(Endian)
– 大端系统(Big-endian)
>> 嵌入式C语言常用语法和扩展
• 逻辑最高位存储在物理最低位;逻辑较高位存储在物理较低位的内存 模式称为大端对齐;使用该存储模式的计算机系统称为大端系统;
OS / OS Service Layer
Hardware Abstract Layer Embedded Hardware Software Framework
Services Layer
Hardware Abstract Layer Embedded Hardware
Snail Studio
>> 典型嵌入式系统构架
– 使用位掩码将指定二进制位置位
• <变量> |= <位掩码> 例如:wValueA |= _BME(3,6);
>> 嵌入式C语言常用语法和扩展
//!< 将BIT3~BIT6置位
– 使用位掩码将指定二进制位清零
• <变量> &= ~ <位掩码> 例如:wValueA &=~ _BME(2,10); //!< 将BIT2~BIT10清零
– 一些关于内存分配方式优化的简单建议
• 当系统已经开发完成,在优化阶段,如果可能,为了追求系统的稳定 性,尽可能将所有的动态分配修改为静态分配 • 系统开发时,对于函数的编写,尽可能使用局部变量(动态分配)。 这既可以保证函数的可重入性;也可以给编译器留下足够的优化空间。 • 使用指针传递变量的时候要注意
– – – – 指针可以安全的传递静态分配的数据; 指针只能将栈分配的变量作为被调函数的传入参数; 不能将栈分配的变量地址作为函数的返回参数; 堆分配的变量,分配后要检查是否分配成功(检查是否为NULL),释放 前要先将指针置空(设置为NULL);
– 要保证堆分配操作的原子性。
Snail Studio
– 栈分配 » 理论上所有的局部变量是栈分配,实际上很多小数据类型的局部 变量是分配在寄存器页上的(使用寄存器来存储) – 堆分配 » 使用malloc以及相关函数分配的存储器资源 » 使用自己编写的动态内存管理程序分配的静态内存
Snail Studio
• C语言存储器分配
>> 嵌入式C语言常用语法和扩展
– 一个行为确定的位定义方式
• #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
• 一个数字本身语义明确的例子 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);
– 如果写“1”可以实现清零,而写“0”没有效果,则不可以使用逻辑位运算 操作该寄存器(除非特别用掩码绕开对应二进制位) 造成这种限制的原因是,假使寄存器中某状态标志为1,我们通过逻辑位 运算对该寄存器进行操作的时候,实际上分为3步: 1)将寄存器值读出 2)改写寄存器的目标二进制位,同时不影响其它位 3)将修改后的值写回。 问题就出在第三步,因为某状态标志是1,而对该状态标志写1会自动清 零该标志,这就导致了无意间的一个误操作。 – 如果写“0”可以实现清零,而写“1”没有效果,则可以使用逻辑位运算
– 在GCC和IAR DLIB模式下直接使用#include <stdbool.h> – 在IAR CLIB和其他编译环境下可以自行定义
• 使用与处理器位数等宽的变量类型作为布尔型变量的基础类型
– 8位机 typedef uint8_t bool; – 16位机 typedef uint16_t bool; – 32位机 typedef uint32_t bool; 这样做的目的是保证处理器能在大部分条件下保证bool变量访问的原子性。
• 硬件抽象层
– 相关定义
• Driver: • BSP: 与芯片外设直接相关的驱动程序 与芯片所在电路板上外围电路直接相关的驱动程序 通常包括调用Driver根据电路板的功能封装和固定 出一些硬件功能。比如调用GPIO驱动封装出LED 控制的驱动程序。 某些芯片外围硬件模块的驱动,比如LCD模块,比 如wifi模块驱动等等。Component通常要调用芯片 的外设驱动。Component和BSP的区别是,BSP的 很多驱动仅仅是针对目标电路板的;而Component 则针对某些固定模块编写的,具有很强的“软构件” 属性。另外,某些模拟的外设,比如模拟的IIC接口 也以Component的形式存在。 上层系统访问硬件抽象层的唯一接口
Snail Studio
• 位运算原则
>> 嵌入式C语言常用语法和扩展
– 如果一个寄存器同时拥有“状态标志位”和“控制标志位”
• 如果状态标志位始终是只读的,则可以使用普通的逻辑位运算对该寄 存器进行操作 • 如果状态标志位是可写的则需要注意检查这些标志位写0和写1的意 义,因为可能存在以下陷阱;
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
– 获取从指定位置开始的n个连续二进制位掩码
#define _BME(__N1,__N2) (_BM((__N2)+1) - _BM((__N1)))
例如 uint16_t hwValue = wValue & _BME(4,7); // 取BIT4~BIT7
Snail Studio
• 基本的逻辑位运算
– GCC中直接使用 #include <stdint.h> – IAR中 » 使用DLIB的情况下直接使用 #include <stdint.h> » 使用CLIB的情况下应该在compiler.h中手工定义
Snail Studio
• Boolean型的定义和使用
>> 嵌入式C语言常用语法和扩展
• C语言存储器分配
>> 嵌入式C语言常用语法和扩展
– C语言中的变量从存储器的角度来说具有3个属性
• 起始地址(属性A) • 占用的连续存储空间大小(属性B) • 如何操作这段存储器(属性C)
– 指针是一个整型变量,保存“属性A”——这是运行时刻可以修改的 – 指针的类型用来表示“属性B”和“属性C”——这些信息是编译时刻 确定的,不可修改,并且只能通过影响操作指针相关的代码来存 储和表示。
相关主题