一、硬件抽象层设计

设计思想
1.采用通用性设计思想,屏蔽底层硬件差异,简化程序移植过程。
2.采用层次化设计思想,硬件抽象层调用驱动层,应用层则调用硬件抽象层。
3.采用模块化设计思想,以外围电路模块为单元进行设计。
板级支持包BSP
应用层---调用BSP提供的接口函数API
操作系统--调用BSP提供的接口函数API
板级支持包--BSP_LED_Init/BSP_LED_On/BSP_LED_Off
驱动程序寄存器或者固件库--硬件平台8051/MSP430/STM32
设计实现:
以外围电路模块为单元设计,分文头文件和源文件
头文件:宏定义、枚举类型、结构体类型等数据类型的定义,以及提供给外部调用的接口函数的声明
源文件:变量的定义、硬件引脚的定义以及接口函数的具体实现
指示灯模块的硬件抽象层设计
指示灯模块分成BSP_LED.h和BSP_LED.c两个文件
设计时假设有4个指示灯LEDO~LED3,均采用高电平驱动方式,对应的控制引脚依次为PA0、PBO、PCO和PDO
指示灯模块的BSP_LED.h 文件:
文件基本描述:
模块名称: 指示灯驱动模块 文件名:BSP_LED.h 说明:头文件,提供自定义数据类型及外部调用的接口函数的声明
版本号: V2.0 修改记录:版本号,日期,作者
#ifndef __BSP_LED_H #define __BSP_LED_H 避免重复包含
用户自定义数据类型:
typedef enum{LED0 = 0,LED1 = 1,LED2 = 2,LED3 = 3}LED_INDEX:定义指示灯序号的数据类型,用枚举类型实现,四个枚举常量表示4个指示灯
本模块提供给外部函数调用的函数(提供给外部使用函数的声明)
extern void BSP_LED_Init (LED_INDEX led);//指示灯初始化
extern void BSP_LED_On (LED_INDEX led);//指示灯开启
extern void BSP_LED_Off (LED_INDEX led);//指示灯关闭
extern void BSP_LED_Toggle (LED_INDEX led);//指示灯状态翻转
指示灯模块的 BSP LED.c 文件
文件基本描述:
模块名称: 指示灯驱动模块 文件名 : BSP_Led.c 说明: 驱动LED指示灯 版本号: V2.0
*修改记录:1、将LED驱动采用结构体指针数组的形式改写,移植时只需要修改LED的数量,引脚定义以及存放端口和引脚的两个数组
版本号 日期 作者
模块移植性说明(模块的移植方法)
1.如果用户的LED指示灯个数小于等于4个,可以将多余指示灯的引脚定义全部定义为和第1个指示灯一样,其余代码不做任何修改
2.如果用户的LED指示灯个数大于k个,需要改如下以容:
(1)增加.h文件中LED_INDEX类型的枚举常量
(2)修改指示灯数量定义LEDn
(3)修改指示灯控制引脚的定义
(4)修改端口和引脚两个数组: GPIO_PORT[LEDn]和GPIO_PIN[LEDn]
(5)修改引脚的时钟控制宏定义LEDX GPIO CLK ENABLE( __NDEX__)的内容
添加HAL库头文件,以便使用HAL库提供的数据类型和接口函数
添加本模块自身的头文件,以便使用指示灯序号LED INDEX这个数据类型
#include "stm32f4xx hal.h"
#include "BSP_LED.h"
指示灯硬件接口定义
// 指示灯数量
#define LEDn 4
// 指示灯0指示灯控制引脚的宏定义,引脚号,端口号,端口时钟使能
#define LEDO PIN GPIO PIN_0 // PAO
#define LED GPIO PORT GPIOA
#define LEDO GPIO CLK ENABLE() HAL_RCC_GPIOA CLK ENABLE()
// 指示灯1
#define LED1_PIN GPIO_PIN_0 // PBO
#define LED1 GPIO PORT GPIOB
#define LED1 GPIO CLK ENABLE()HAL RCC GPIOB CLK ENABLE()
//端口数组,数组中存放的都是指示灯控制引脚所属端口,数据类型为只想端口寄存器的结构体指针GPIO_TypeDef
GPIO_TypeDef *GPIO_PORT[LEDn] = {LEDO_GPIO_PORT,LED1_GPIO PORT,LED2_GPIO_PORT,LED3_GPIO_PORT};
// 引脚数组,数组中存放的都是指示灯控制引脚
const uint16 t GPIO PIN[LEDn] = {LEDO_PIN,LED1_PIN,LED2_PIN,LED3_PIN,};
// 端口时钟使能宏定义,使用do while(e)的形式是为了避免宏展开时发生错误
#define LEDX GPIO CLK_ENABLE(__INDEX__) do{\
If(( INDEX) == 0) LEDO GPIO CLK ENABLE();\
else if(( INDEX ) == 1)LED1 GPIO CLK ENABLE();\
else if((_INDEX ) == 2)LED2 GPIO CLK ENABLE();\
else if(( INDEX ) == 3)LED3 GPIO CLK ENABLE();\
}while(0)

接口函数的实现:
1.指示灯引脚初始化函数BSP_LED_Init
函数的基本描述
函数名称 函数功能 入口参数 返回值 使用注意
void BSP LED Init(LED INDEX Led)
{
GPIO InitTypeDef GPIO InitStruct = {0};//定义引脚初始化变量,并赋初值为0
LEDX GPIO_CLK_ENABLE(Led); //使能引脚所属端口的时钟
/* Configure the GPIO LED pin */
GPI0_Initstruct.Pin = GPIO_PIN[Led];
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO SPEED_FREQ LOW;
HAL_GPIO_Init(GPIO_PORT[Led], &GPIO_InitStruct);//以上配置引脚参数并初始化引脚
HAL GPIO Writepin(GPIO PORT[Led],GPIO_PIN[Led], GPIO_PIN_RESET);//设置引脚初始电平
}
接口函数之指示灯开启: BSP_LED_On
调用接口函数HAL_GPIO_WritePin
void BSP_LED On(LED INDEX Led)
{
HAL_GPIO_WritePin(GPIO_PORT[Led], GPIO_PIN[Led], GPIO_PIN_SET);
}
接口函数之指示灯关闭: BSP_LED_Off
调用接口函数HAL_GPIO_WritePin
void BSP_LED_Off(LED INDEX Led)
{
HAL_GPIO_WritePin(GPIO_PORT[Led], GPIO_PIN[Led], GPIO_PIN_RESET);
}
接口函数之指示灯状态翻转: BSP_LED_Toggle
调用接口函数HAL_GPIO_TogglePin
void BSP_LED_Toggle(LED INDEX Led)
{
HAL_GPIO_WritePin(GPIO_PORT[Led], GPIO_PIN[Led]);
}


硬件抽象层设计总结

设计总结
硬件抽象层设计总结
头文件:
只包括数据类型的定义以及提供给外部调用的接口函数的说明
一般不进行变量的定义以及硬件引脚的说明,也不包含任何其他的头文件,以确保头文件的通用性
应用层只调用头文件提供的接口函数,因此不需要修改应用层的代码
源文件:
完成接口函数的实现
包含相关的头文件
进行模块内的变量定义以及与硬件相关的全部定义
调用HAL库提供的接口函数,进行二次封装,提供出一个可读性更强移植性更好的模块接口函数
硬件抽象层的移植步骤:
1.根据移植的硬件电路实际情况修改BSP->2.将BSP相关文件添加到工程文件中->3.调用BSP提供的接口函数编写应用程序。
修改BSP的两种情况
指示灯数量小于等于4:
修改指示灯控制引脚的定义
将多余指示灯的控制引脚的定义全部修改为与第一个指示灯一样
指示灯数量大于4:
增加LEDINDEX类型的枚举常量
修改指示灯数量
修改指示灯控制引脚的定义
修改与端口和引脚相关的两个数组
修改与引脚相关的时钟控制宏定义
LED接口的硬件定义(移植到Nucleo开发板,只有PA5连接LED)
//指示灯0修改指示灯控制引脚为PA5
#define LEDO PIN GPIO PIN 5 //PA5
#define LED9 GPIO PORT GPIOA
#define LEDO_GPIO CLK ENABLE() HAL_RCC_GPIOA_CLK_ENABLE()
// 指示灯1
#define LED1 PIN GPIO_PIN_5// PA5
#define LED1 GPIO PORT GPIOA
#define LED1 GPIO CLK ENABLE()HAL RCC_GPIOA CLK ENABLE()
// 指示灯2 将多余的指示灯控制引脚定义修改为PA5
#define LED2 PIN GPIO_PIN_5// PA5
#define LED2 GPIO PORT GPIOA
#define LED2_GPIO_CLK_ENABLE() HAL_RCC_GPIOA_CLK_ENABLE()
// 指示灯3
#define LED3_PIN GPIO_PIN_5// PA5
#define LED3_GPIO_PORT GPIOA
#define 1ED3_GPIO_CLK_ENABLE() HAL_RCC_GPIOA_CLK_ENABLE()
第一步:复制BSP文件
利用CubeMX新建工程或复制已有工程,在工程文件夹中新建BSP文件夹及LED文件夹,并将BSP LED.c和BSP LED.h复制到该文件夹
BSP文件夹:硬件抽象层BSP
Drivers文件夹:HAL库驱动及CMSIS驱动
Inc文件夹:用户程序的头文件
Src文件夹:用户程序的源文件
.ioc文件: STM32CubemX工程文件
第二步:添加BSP文件到MDK工程
点击新建组图标,(Groups右侧)新建一个名为“BSP”的组,选中BSP组,利用“Add Files”按钮找到BSP\LED下面的BSP LED.C和 BSP LED.h文件,添加到BSP组
第三步:添加头文件包含路径
工程设置图标Options for Target,C/C++标签卡includePaths,添加头文件路径,新建头文件路径图标,选择BSP_LED.h所在路径
在main.h中包含LED模块的头文件
/* Private includes ---
/*USER CODE BEGIN Includes */
#include"BSP LED.h" //包含LED模块的头文件
/* USER CODE END Includes */
在main.c中添加初始化代码
main.c
/* Initialize all configured peripherals */
//MX_GPIO_Init();// 注释掉CubeMX软件生成的指示灯控制引脚初始化程序
/*USER CODE BEGIN 2 */
BSP LED Init(LEDO);//调用指示灯模块的初始化接口函数
/*USER CODE END 2 */

* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/*USER CODE BEGIN 3 */
//调用LED模块的接口函数控制指示灯
BSP_LED_On(LED0); // 开启LD2
HAL Delay(1000); // 延时1000ms
BSP_LED_Off(LED0); // 关闭LD2
HAL Delay(1000); //延时1000ms
}
/*USER CODE END 3 */