在工作中,经常遇到大量使用GPIO作为数字量输入输出来控制设备或采集状态,每次定义操作不同的GPIO针脚既麻烦又容易出错,于是就想要简化操作过程。对于数字量输入来说就是采集对应针脚的状态;而输出则是根据逻辑关系置位或复位对应得针脚。

为了使用方便,我们按可复用和经常变化的部分叫软件的实现划分为2个部分。相对固定的部分我们封装成操作函数供调用,对于经常变化的部分(如硬件配置等)我们另外实现,并调用前面封装的函数实现功能。

现在我们只要实现了通用性较好的函数封装,剩下就是调用来实现具体控制的问题。那么怎么封装这些函数呢?

我们首先定义两个枚举类型分别定义如下:

//定义数字量输出通道枚举类型,规定通道的范围
typedef enum {DOChannel1,DOChannel2,DOChannel3,DOChannel4,DOChannel5,DOChannelNum
} DigitalOutput;//定义数字量输入通道枚举类型,规定通道的范围
typedef enum {DIChannel1,DIChannel2,DIChannel3,DIChannel4,DIChannel5,DIChannelNum
} DigitalInput;

数字量输入输出的枚举主要是为了方便操作和识别,通道数量出现变化时只需要增加枚举两种的通道定义即可。此处数字量输入输出均定义了5个通道。枚举量的最后一个成员代表了通道的数量,在枚举全部通道时能够很好的避免超出范围的错误。

同时还要定义如下的结构体,用于定义需要操作GPIO目标。

//定义用于针脚操作的目标针脚类型
typedef struct{GPIO_TypeDef* GPIOx;uint16_tGPIO_Pin;
}TargetPin;

有了上述的定义则可以实现前面设想的操作了,接下来我们还需要定义两个数字量输入输出通道的TargetPin类型的数组,用于存放想要操作的目标通道,和前面枚举两种定义的通道一致,此处也是5个通道。

//定义DI通道的全部目标针脚数组
TargetPindiPin[]={{GPIOE,GPIO_Pin_2},{GPIOE,GPIO_Pin_3},{GPIOE,GPIO_Pin_4},{GPIOE,GPIO_Pin_5},{GPIOE,GPIO_Pin_6}};//定义DO通道的全部目标针脚数组
TargetPindoPin[]={{GPIOD,GPIO_Pin_3},{GPIOD,GPIO_Pin_4},{GPIOD,GPIO_Pin_5},{GPIOD,GPIO_Pin_6},{GPIOD,GPIO_Pin_7}};

有了以上2个数组就可以在避免在操作过程中大量使用条件分支语句(Switch或if语句),简化编码和避免在增加通道时号要修改函数的情况。现在如果通道数量出现变化则只需要修改枚举量和数组的值就可。或者操作的管脚出现变化则只需要修改数组的值就可以了。而不需要去修改函数体,而且函数体的编码也非常简单。

对数字量输出的操作如下,在操作全部通道时,以枚举变量作为循环变量,以枚举的最后定义的数量来控制,并以枚举量的取值作为数组下标,有效避免出现超出范围的错误,同时在通道数量和通道对应的具体针脚发生变化时,无需修改函数。

//操作全部继电器DO通道
//输入参数TargetPin *doPin为要操作的DO通道列表
//输入参数BOOL *commands欲写给DO通道的值列表
void OperationAllRelayChannel(TargetPin*doPin,BOOL *commands)
{DigitalOutputDOChannel;for(DOChannel=DOChannel1;DOChannel<DOChannelNum;DOChannel++){OperationSingleRelayChannel(doPin[DOChannel],commands[DOChannel]);}
}//操作单个继电器DO通道
//输入参数TargetPin doPin为要操作的DO通道
//输入参数BOOL command欲写给DO通道的值
void OperationSingleRelayChannel(TargetPindoPin,BOOL command)
{if(command==True){GPIO_SetBits(doPin.GPIOx,doPin.GPIO_Pin);}else{GPIO_ResetBits(doPin.GPIOx,doPin.GPIO_Pin);}
}

对数字量输入的操作函数的编写采用与数字量输出相同的思路。对于枚举之所以可以用作数组下标,是因为枚举没被指定值时,总是从0开始向上累加,正好与数组下标是一致的。这要做还有一个好处是,通道与具体的GPIO引脚是由TargetPin数组的赋值顺序决定的,修改非常方便。

//获取全部DI量状态输入值
//输入参数TargetPin *diPin为需要读取的DI通道列表
//输入参数BOOL *result为读取的通道值返回列表
void GetAllDIStatusInput(TargetPin *diPin,BOOL*result)
{DigitalInputDIChannel;for(DIChannel=DIChannel1;DIChannel<DIChannelNum;DIChannel++){result[DIChannel]=GetSingleDIStatusInput(diPin[DIChannel]);}
}//获取单个DI量状态输入值
//输入参数TargetPin diPin是需要读取的DI通道
//返回值为读取的通道值
BOOL GetSingleDIStatusInput(TargetPin diPin)
{uint8_treadValue;readValue= GPIO_ReadInputDataBit(diPin.GPIOx,diPin.GPIO_Pin);return(readValue>0)?True:False;
}

通过以上的编码操作DI、DO已经很方便了,但在操作单个DO通道的函数中还有一个if…else语句给人的感觉比较不太好。因为操作简单就是置位和复位,所以我们定义一个指向函数的指针数组,如下:

/*定义操作GPIO管脚的函数指针*/
void (*OperationGPIOBits[])(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)={GPIO_ResetBits,GPIO_SetBits};

有了这个指向函数的指针数组我们可以将上面的操作单个DO通道的函数简化为如下:

//操作单个继电器DO通道
//输入参数TargetPin doPin为要操作的DO通道
//输入参数BOOL command欲写给DO通道的值
void OperationSingleRelayChannel(TargetPindoPin,BOOL command)
{OperationGPIOBits[command](doPin.GPIOx,doPin.GPIO_Pin);
}

其中command是一个布尔变量取值为0和1,正好与指向函数的指针数组对应,实现在command取不同值时,调用复位或置位函数。

以上代码在IAR EWARM和STM32F103VET平台测试正确。

通过数组和枚举简化GPIO操作编码相关推荐

  1. orangepi zero 的gpio操作

    orangepi zero 的gpio操作 据我搜集的资料得知,操控gpio有下面几种方法 第一个是使用系统的内核来操作 可以参考下面的链接 Orange pi GPIO输出控制,从点灯开始! 这个是 ...

  2. C# 使用 Index 和 Range 简化集合操作

    C# 使用 Index 和 Range 简化集合操作 Intro 有的语言数组的索引值是支持负数的,表示从后向前索引,比如:arr[-1] 从 C# 8 开始,C# 支持了数组的反向 Index,和 ...

  3. 在ASP.NET Core中使用AOP来简化缓存操作

    前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...

  4. W806 基于Arduino开发的GPIO操作示例,勇于尝新

    W806 基于Arduino的点灯实验的 一.写在前面 1. W806芯片参数: 2. MCU 特性: 3. 供电: 二.环境准备,添加支持包 三.打开永恒的LED测试 四.总结 一.写在前面 w80 ...

  5. 火山视窗文本数组类增删查改操作

    本源码转载自利快云https://www.lkuaiy.com/ 火山视窗文本数组类增删查改操作     文本数组类可对文本型数据进行数组操作,本类可以非常方便的实现对文本数组的增删查改.   一.添 ...

  6. 火山视窗整数数组类增删查改操作

    本源码转载自利快云https://www.lkuaiy.com/ 火山视窗整数数组类增删查改操作     整数数组类可对整数数据进行数组操作,本类可以非常方便的实现对整数数组的增删查改.   一.添加 ...

  7. firefly-rk3399开发板100天之第2天 记录firefly-rk3399 wiki中gpio操作

    firefly-rk3399开发板100天之第2天 记录firefly-rk3399 wiki中gpio操作 Docs » GPIO 使用 GPIO 使用 简介 GPIO, 全称 General-Pu ...

  8. LCD/HDMI OUT调试经验(3)------GPIO操作与MIPI参数的获取方式

    在了解上一章节引脚权限相关操作后,我将抛开官方描述,从入手调试角度详细介绍模块中的GPIO是什么.怎么操作等等.另外,最后将以LCD的mipi数据获取方式介绍AP与BP的交流. 一.GPIO 之前讲述 ...

  9. Linux下给arm的引脚电平,ARM-Linux GPIO操作事宜

    原标题:ARM-Linux GPIO操作事宜 1.在 Linux 表示为"S3C2410_GPx(n)",其中x的取值为"A,B,C,D,E,F,G,H.....&quo ...

最新文章

  1. 最长公共子序列的C++实现---附二维指针的使用方法
  2. 强化学习之原理详解、算法流程及Python代码
  3. 0003-Longest Substring Without Repeating Characters(无重复字符的最长子串)
  4. bzoj1058: [ZJOI2007]报表统计
  5. 独占设备的分配与回收_灵魂拷问:Java对象的内存分配过程是如何保证线程安全的?...
  6. MFC程序执行过程剖析
  7. [css] :placeholder-shown和:focus-within这两个伪类你有使用过吗?说说看
  8. 取MapString,sitemap中的全部key
  9. google search console的使用
  10. 【C语言】关键字volatile之有关__IO、__O、__I的故事
  11. SQL优化-第二章-从解释计划层面让SQL飞
  12. Static allocator 3
  13. 一:MySQL数据库的性能的影响分析及其优化
  14. 1000套微信小程序源码模板分享下载,各种类型任意选择
  15. VB6各类源码开源 - 开源研究系列文章
  16. cmd命令把GHO转换成虚拟机可直接加载的硬盘格式实例
  17. 达内python第一次月考题目_第一次月考作文六篇
  18. 数据库查询语句遇到:Unknown column 'XXXX' in 'where clause'解决方法
  19. 程序猿的移民加拿大后的所感
  20. CorelDRAW2022中文正式版特色功能介绍

热门文章

  1. 带偏移量的AES加密工具
  2. HTTP的请求报文与响应报文
  3. SSL/TLS 配置
  4. pat1111-1120
  5. 11月25号站立会议
  6. Android - 布局详解之LinearLayout与RelativeLayout
  7. 牛客16654 谁拿了最多奖学金
  8. hadoop 重新格式化 NameNode
  9. python自己创建模块路径_python之本地模块/包使用和创建
  10. 惠普打印机怎么无线连接电脑_惠普打印机连不上无线?怎么解!