stm32单片机端口_STM32开发

(3) 2024-09-18 20:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说
stm32单片机端口_STM32开发,希望能够帮助你!!!。

#科技# #科技V计划#

前言:在网上有很多面向接口编程的文章与例子,特别是在面向对象编程方面的,比如JAVA里面的接口,C++里面的接口,还有C语言的函数指针类的接口。但是我这里的讲的面向"接口"编程与其不一样,所以我这里加了双引号,避免大家说我偷换概念。我这里的面向"接口"编程这个接口是在嵌入式中实际存在的一个物理接口。

关键点:面向接口编程,相当于透过现象看本质,抓住事务的共性;在程序实现中应该依赖于抽象,而不依赖于具体。

为了更加容易理解面向接口编程的思想,现在假设有一个简单的灯光控制系统(娱乐室内灯控系统):由一个主控设备与三个子设备组成。主控设备有三个接口,每个接口由24VDC电源输出与一个串口组成,一共4根线。每个子设备由一个接口(串口及电源共4根线)及其控制的一种灯组成,图示如下:

stm32单片机端口_STM32开发_https://bianchenghao6.com/blog__第1张

重点来了

方案1、不同种类子设备子固定插入到主控固定接口(LED类固定插入接口1,普通照明类固定插入接口2,激光灯类固定插入接口3)。

方案2、不同种类子设备子插入主控的接口不固定,随便插。

任务是:实现主控的程序功能。

针对方案1,得出的设计极有可能是面向具体对象的设计实现,即灯光类型这个对象。针对方案2得到的必定是高度抽象的面向接口方式的设计实现。从方案的优劣来看,应该极大部分人都会选择方案2。但是对一般设计实现者(码农)极有可能选择方案1,因为"看起来"实现的难度小,都是固定的不是很容易吗。下面我选择方案2来实现这个设计。由上面假设的内容可见,它们的物理接口是一样的四根线,分别为24V+GND+TXD+RXD,这样子设备插入任意接口的物理基础有了。(假如采用方案1还需要实现物理防呆的设计)

1、抽象出模块的接口

为了适应需求的灵魂性需要对主控模块的接口进行抽象出去基本功能需求,得到下图所示的样子。

stm32单片机端口_STM32开发_https://bianchenghao6.com/blog__第2张

由上图及需求很容易想象到:对接口进行编程才是唯一出路,不可能针对某种类型的具体设备进行编程,否则需要对每个接口进行适配三种类型的灯控功能(因为方案2的需求)。此即为依赖于抽象,而不依赖于具体(灯光类型)。此处的抽象为主控的接口,而此处的具体的某个类型的子设备。所以要实现这个主控的程序,必须对子设备进行一定程度的抽象,其共性一定要认识得非常透切。

2、实现接口描述

现在看看每个接口的共性有那些属性:给出代码如下(此处给出简单的共同属性)

typedef struct

{

bool Inserted; //插入标志

char name[10]; //名称

u8 LampType; //类型

u8 Sn[10]; //序列号

u8 WorkFunc; //工作

u8 Status; //状态

u8 NeedPower; //需求功率

u8 FuncList[10]; //功能列表

}LampInfo_t;

再给出灯类型定义(代码如下):

typedef enum

{

eLampTypeNone=0, //类型不确定(初始类型)

eLampTypeLed=1, //LED类

eLampTypeLaser, //激光类

eLampTypeNormal, //普通类(普通照明)

}eLampType_t;

给出接口类型定义

typedef enum

{

eLampIFNone=0, //接口类型

eLampIF1=1, //接口1

eLampIF2, //接口2

eLampIF3, //接口3

}eLampIF_t;

现在再给出三个接口的描述

typedef struct

{

u8 OnlineDevAmount; //在线子设备数量

LampInfo_t IF1; //接口1

LampInfo_t IF2; //接口2

LampInfo_t IF3; //接口3

}LampIfInfo_t;

现在抽象出这几种类型的灯光控制效果,给出代码如下

typedef enum

{

eLampFuncNone=0, //功能

eLampFuncOpen=1, //打开

eLampFuncClose, //关闭

eLampFuncFlicker05HZ,//0.5Hz闪烁

eLampFuncFlicker1HZ,//1Hz闪烁

eLampFuncFlicker2HZ,//2Hz闪烁

eLampFuncFlicker3HZ,//3Hz闪烁

eLampFuncFlicker5HZ,//5Hz闪烁

eLampFuncFlicker10HZ,//10Hz闪烁

//其他灯光效果(子设备是这个功能列表的子集)

}eLampFuncList_t;

3、 实现接口描述功能初始化

//灯描述初始化

Static void LampInfoInit(LampInfo_t *LampInfo)

{

LampInfo->Inserted=FALSE; //插入标志

memcpy(LampInfo->name,"default",10); //名称

LampInfo->LampType=eLampTypeNone; //类型

memset(LampInfo->Sn,0,10); //序列号

LampInfo->WorkFunc=eLampFuncNone; //工作

LampInfo->Status=0; //状态

LampInfo->NeedPower=0; //需求功率

memset(LampInfo->FuncList,0,10); //功能列表

}

//灯接口描述初始

void LampIfInfoInit(LampIfInfo_t *Lamp)

{

Lamp->OnlineDevAmount=0; //在线子设备数量

LampInfoInit(&Lamp->IF1); //接口1

LampInfoInit(&Lamp->IF2); //接口2

LampInfoInit(&Lamp->IF3); //接口3

}

4、实现接口常规功能

4.1、设置与获取子设备插入接口标志

void SetLampInterFaceInsertedFlag(LampIfInfo_t *Lamp,eLampIF_t IfNumber,bool FLAG)

{

switch(IfNumber)

{

case eLampIF1:Lamp->IF1.Inserted=FLAG; //设置接口1插入标志

break;

case eLampIF2:Lamp->IF2.Inserted=FLAG; //设置接口2插入标志

break;

case eLampIF3:Lamp->IF3.Inserted=FLAG; //设置接口3插入标志

break;

default:

break;

}

}

bool GetLampInterFaceInsertedFlag(LampIfInfo_t *Lamp,eLampIF_t IfNumber,bool FLAG)

{

bool result=FALSE;

switch(IfNumber)

{

case eLampIF1:result=Lamp->IF1.Inserted;

break;

case eLampIF2:result=Lamp->IF2.Inserted;

break;

case eLampIF3:result=Lamp->IF3.Inserted;

break;

default:

break;

}

return result;

}

4.2、获取子设备在线数量

u8 GetLampOnlineAmount(LampIfInfo_t *Lamp)

{

Lamp->OnlineDevAmount=0; //在线设备数量清零

if(Lamp->IF1.Inserted==TRUE)Lamp->OnlineDevAmount+=1; //接口1在线总数+1

if(Lamp->IF2.Inserted==TRUE)Lamp->OnlineDevAmount+=1; //接口2在线总数+1

if(Lamp->IF3.Inserted==TRUE)Lamp->OnlineDevAmount+=1; //接口3在线总数+1

return Lamp->OnlineDevAmount; //返回在线子设备数量

}

4.3、设置与获取接口上子设备的名称

void SetLampDeviceName(LampIfInfo_t *Lamp,eLampIF_t IfNumber,char *DevName)

{

switch(IfNumber)

{

case eLampIF1:memcpy(Lamp->IF1.name,DevName,strlen(DevName)); //拷贝接口1名称

break;

case eLampIF2:memcpy(Lamp->IF2.name,DevName,strlen(DevName)); //拷贝接口2名称

break;

case eLampIF3:memcpy(Lamp->IF3.name,DevName,strlen(DevName)); //拷贝接口3名称

break;

default:

break;

}

}

bool GetLampDeviceName(LampIfInfo_t *Lamp,eLampIF_t IfNumber,char *DevName)

{

bool result=TRUE;

switch(IfNumber)

{

case eLampIF1:memcpy(DevName,Lamp->IF1.name,10); //拷贝接口1名称

break;

case eLampIF2:memcpy(DevName,Lamp->IF2.name,10); //拷贝接口2名称

break;

case eLampIF3:memcpy(DevName,Lamp->IF3.name,10); //拷贝接口3名称

break;

default:result=FALSE;

break;

}

return result;

}

4.4、设置与获取子设备类型

void SetLampDeviceType(LampIfInfo_t *Lamp,eLampIF_t IfNumber,eLampType_t Type)

{

switch(IfNumber)

{

case eLampIF1:Lamp->IF1.LampType=Type;

break;

case eLampIF2:Lamp->IF2.LampType=Type;

break;

case eLampIF3:Lamp->IF3.LampType=Type;

break;

default:

break;

}

}

eLampType_t GetLampDeviceType(LampIfInfo_t *Lamp,eLampIF_t IfNumber)

{

eLampType_t Type;

switch(IfNumber)

{

case eLampIF1:Type=Lamp->IF1.LampType;

break;

case eLampIF2:Type=Lamp->IF2.LampType;

break;

case eLampIF3:Type=Lamp->IF3.LampType;

break;

default:Type=eLampTypeNone;

break;

}

return Type;

}

4.5、其他功能

忽略部分代码:包括子设备序列号,需求功率,功能列表,当前工作模式(功能列表中的一项),设备状态。

5、工作流程

以上模块代码是主控模块对于子设备的功能抽象描述,此为面向“接口”编程的基础。其他工作流程均依据这个基础。现在看看面向“接口”编程的主控工作流程。如下图所示。

stm32单片机端口_STM32开发_https://bianchenghao6.com/blog__第3张

面向接口描述字的流程

由上图可见接口描述字是面向“接口”编程的关键,只有高度抽象出其描述字才能设计出好的产品。实现了接口描述字相关功能,方案2的设计基本就实现了。

总结:

面向“接口”编程是一个编程思想,它依赖的是对象抽象出来的共同属性部分,它不依赖于具体的某一个对象。就如上面的例子一样,只有这样高度抽象出子设备的共同属性,才能实现子设备插入任意物理接口的需求。当然实际产品中的物理接口可能还有其他形式,比如增加一个插拔检测引脚,或者其他IO信号。无论物理接口形式如何变化,只要心里有面向“接口”编程的思想,就能抽象出实物的共同属性。另外面向“接口”编程可以演化为面向“接口”设计,包括硬件设计,结构设计。只有硬件与结构按照面向“接口”设计之后,才能给编程实现面向“接口”编程提供实现基础。

大家有什么不同看法可以留言交流,再次谢谢。

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复