在工作中,经常遇到大量使用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_t GPIO_Pin;
}TargetPin;

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

//定义DI通道的全部目标针脚数组
TargetPin diPin[]={{GPIOE,GPIO_Pin_2},{GPIOE,GPIO_Pin_3},{GPIOE,GPIO_Pin_4}
,{GPIOE,GPIO_Pin_5},{GPIOE,GPIO_Pin_6}}; //定义DO通道的全部目标针脚数组
TargetPin doPin[]={{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)
{
DigitalOutput DOChannel;
for(DOChannel=DOChannel1;DOChannel<DOChannelNum;DOChannel++)
{
OperationSingleRelayChannel(doPin[DOChannel],commands[DOChannel]);
}
} //操作单个继电器DO通道
//输入参数TargetPin doPin为要操作的DO通道
//输入参数BOOL command欲写给DO通道的值
void OperationSingleRelayChannel(TargetPin doPin,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)
{
DigitalInput DIChannel;
for(DIChannel=DIChannel1;DIChannel<DIChannelNum;DIChannel++)
{
result[DIChannel]=GetSingleDIStatusInput(diPin[DIChannel]);
}
} //获取单个DI量状态输入值
//输入参数TargetPin diPin是需要读取的DI通道
//返回值为读取的通道值
BOOL GetSingleDIStatusInput(TargetPin diPin)
{
uint8_t readValue;
readValue = GPIO_ReadInputDataBit(diPin.GPIOx,diPin.GPIO_Pin);
return (readValue>)?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(TargetPin doPin,BOOL command)
{
OperationGPIOBits[command](doPin.GPIOx,doPin.GPIO_Pin);
}

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

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

通过数组和枚举简化GPIO操作编码的更多相关文章

  1. 通过数组和枚举简化GPIO操作编码(转)

    源: 通过数组和枚举简化GPIO操作编码

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

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

  3. 如何利用反射简化Servlet操作

    如何利用反射简化Servlet操作   一.反射的实现 新建类BaseServlet,继承HttpServlet(不需要在web.xml文件中配置) 1.在doPost()方法中处理请求乱码,并调用d ...

  4. 深入分析Java反射(二)-数组和枚举

    前提 Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行 ...

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

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

  6. linux 标准 GPIO 操作

    Linux 提供了GPIO 操作的 API,具体初始化及注册函数在 driver/gpio/lib_gpio.c 中实现.   #include    int gpio_request(unsigne ...

  7. STM32标准库GPIO操作

    STM32标准库GPIO操作 STM32任何外围设备的使用都分为两部分:初始化和使用.体现在代码上就是:(1)有一个初始化函数(2)main函数中的使用 1.初始化GPIO 初始化GPIO函数代码: ...

  8. day08文件操作的三步骤,基础的读,基础的写,with...open语法,文件的操作模式,文件的操作编码问题,文件的复制,游标操作

    复习 ''' 类型转换 1.数字类型:int() | bool() | float() 2.str与int:int('10') | int('-10') | int('0') | float('-.5 ...

  9. RK3399/NanoPC-T4开发板使用/sys/class/gpio操作外接GPIO设备-【申嵌视频-RK3399篇】

    实验2:RK3399/NanoPC-T4开发板使用/sys/class/gpio操作外接GPIO设备,比如外接一个LED模块,通过GPIO1_A0管脚 1 介绍   LED模块   Matrix-LE ...

随机推荐

  1. JS使用getComputedStyle()方法获取CSS属性值

    在对网页进行调试的过程中,经常会用到js来获取元素的CSS样式,方法有很多很多,现在仅把我经常用的方法总结如下: 1. obj.style:这个方法只能JS只能获取写在html标签中的写在style属 ...

  2. [转]别再抱怨了,国内这么多优秀的Android资源你都知道吗?

    因为一些大家都知道的原因,android很多官方出品的优秀开发资源在国内无法访问. 国内的同行们对此也做出了很多努力,有很多朋友通过各种手段把很多优秀的资源搬运到了国内,为国内android开发者提供 ...

  3. 调戏OpenShift:一个免费能干的云平台

    一.前因后果 以前为了搞微信的公众号,在新浪sae那里申请了一个服务器,一开始还挺好的 ,有免费的云豆送,但是一直运行应用也要消费云豆,搞得云豆也所剩无几了.作为一名屌丝,日常吃土,就单纯想玩一玩微信 ...

  4. mybits批量插入

    <!--批量插入--> <insert id="batchSave" parameterType="java.util.List"> i ...

  5. Python Day21

    Cookie 1.获取Cookie: request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt=' ...

  6. php验证身份证号码的正确性

    /********************php验证身份证号码是否正确函数*********************/function is_idcard( $id ) {   $id = strto ...

  7. C# GetType与typeof

    在反射和泛型中经常会使用到Type类,获取Type的最常用的方法是 obj.GetType(),和typeof(T).在获取泛型的type时有些小坑. public static void Main( ...

  8. MVC IIS环境部署注意事项

    环境:MVC3.0,IIS7 Mvc3.0的部署除像正常部署aspx程序一样外,另外还需要注意的几点: 1. 安装MVC3.0 确保服务器上安装了MVC3.0,默认版本是“3.0.20105.0” 2 ...

  9. Bubble Cup 8 finals G. Run for beer (575G)

    题意: 给定一个带权无向图,每条边的代价为边权/当前速度,每次到达一个新节点,速度都会除以10. 求0号点到n-1号点的最小代价,如果多解输出点数最少的解,输出代价.路径点数.路径经过的点. 1< ...

  10. leetcode--Majority Element

    题目链接:https://leetcode.com/problems/majority-element/ 算法类型:分治法 题目分析:获取长度为n的数组中的众数(出现次数大于等于⌊ n/2 ⌋) 代码 ...