GPIO输入详解 ============ 作者:陈之敏 时间:2020年08月15日 关键字:csdk、RDA8910、二次开发、GPIO输入 ## 目录 `点击这里查看所有博文 `__   最近听说有的小伙伴看了我的教程后,有一些问题都跑到官方的gitee上面去问去了。导致官方的人没搞懂问的是啥,小伙伴们也没能知道自己想要的答案。给大家造成了困扰,这里我说声抱歉。   既然出现了这个问题,我这里就声明一下,本系列教程所涉及的内容(demo)不是官方的作品。我个人觉得官方的demo内容太多太全,往往都是把一个模块内所有的东西全部放在一起。这样的话对新手不是很友好,阅读起来也比较费劲。我就把官方的部分demo进行相关的简化,并推出教程这样的话可能会对新手朋友们有一定的帮助。   有时候周末闲暇时间我也会加上一些我觉得好玩的模块在里面,这些可能在官方的demo都没有,比如cJSON、PAHO-MQTT、http-client。   这就是官方的代码仓库。 .. code:: c git clone --recursive https://gitee.com/openLuat/Luat_CSDK_Air724U.git   当然各位小伙伴在看本教程时,我建议还是使用我下面提供的仓库比较好,看完之后在迁移到官方的仓库⇧。 .. code:: c git clone --recursive https://gitee.com/chenxiahuaxu/RDA8910_CSDK.git   再看本教程过程中如果遇到了问题,可以在本人的代码仓库下面评论。也可以在本人的\ `博客 `__\ 下面评论。我要是看到的话,并且这个问题在我的能力范围的话我会尽力解答的(非官方,不要对我要求太多哦,要求太多我可能就不管啦)。 一、前言 --------   本系列开发教程最近才开始准备,sdk中也有了很多内置的demo,但是这个配套的博客教程却更不上节奏。大家也不要急,我会尽量加快速度,最近事情也是挺多的。别指望我一天更新一篇,写这玩意太费时间了。大家也可以先攒一攒,先攒它一个月在讲,养肥了再看。在这里不得不佩服那些网络小说的作者能做到一天一章,一章好几千字在下实在佩服。 |在这里插入图片描述|   人们为了能够从外面的世界获取有用的信息,不得不需要借助于像眼睛鼻子等感觉器官,而在探索自然的现象和研究自然的规律以及总结人们在生产时的活动时,仅仅依靠人们自身的感觉器官这些,它们的功能就根本不够使用了,为了能够适应这种特殊的情况,那么就必须要使用外部的传感器。 |image1|\ |image2|   有很大一部分传感器输出的信号仅仅是一个开关量,对于开关量的信号的采集,我们这里需要用到一个功能,那就是GPIO的输入功能。紧接着上篇博客我门用到了GPIO0和GPIO1模拟了一下LED的以一定的周期闪烁。那么这篇博客我们就把GPIO0和GPIO1初始化为输入功能,模拟一下开关量的采集。 |image3| ## 二、编写测试程序   利用GPIO的输入功能获取开关信号有两种方法,一种是查询法,另外一种是中断法,我们依次讲解 2.1、中断法 -----------   中断我对它的理解是,他的优先级高于普通函数,可以打断正在执行的低优先级中断,和普通任务。   有打断的意思,只要正在执行的任务优先级比它低,当中断来临时就可以被打断,转而去执行中断函数。   中断的原则是快进快出,不能在里面执行复杂的事情,否则会影响其他任务执行。相对普通任务实时性较高,适用于对事件敏感的情况   具体使用方法如下, ### 2.1.1、初始化GPIO0为中断输入模式 .. code:: c T_AMOPENAT_GPIO_CFG input_cfg = {0}; BOOL err = 0; input_cfg.mode = OPENAT_GPIO_INPUT_INT; //配置输入中断 input_cfg.param.defaultState = 0; //默认为低电平 input_cfg.param.intCfg.debounce = 50; //防抖50ms input_cfg.param.intCfg.intType = OPENAT_GPIO_INT_BOTH_EDGE; //中断触发方式双边沿 input_cfg.param.intCfg.intCb = gpio_handle; //中断处理函数 err = iot_gpio_config(0, &input_cfg); //初始化gpio为中断输入模式 if (!err) return; iot_debug_print("[gpio] set gpio0 INT input"); 2.1.2、编写中断服务程序 ~~~~~~~~~~~~~~~~~~~~~~~ .. code:: c void gpio_handle(E_OPENAT_DRV_EVT evt, E_AMOPENAT_GPIO_PORT gpioPort, unsigned char state) { uint8 status = 0; //iot_debug_print("[gpio] gpio_handle %d,%d,%d", evt, gpioPort, state); // 判断是gpio中断 if (evt == OPENAT_DRV_EVT_GPIO_INT_IND) { // 判断触发中断的管脚 if (gpioPort == 0) { // 触发电平的状态 iot_debug_print("[gpio] input handle gpio %d, state %d", gpioPort, state); // 读当前gpio状态, 1:高电平 0:低电平 iot_gpio_read(gpioPort, &status); iot_debug_print("[gpio] input handle gpio %d, status %d", gpioPort, state); } } }   中断的使用方法相比查询到要显得麻烦一下,需要多写一个函数,代码量比较多,但是好处就是实时性高。 ## 2.2、查询法   顾名思义,查询法就是不停的查询GPIO的状态,不管开关有没有按下,我都去查询,查询到什么就是什么,我没有查询的时候,你按了也没有用,这种方法的实时性比较低。 ### 2.2.1、初始化GPIO1为普通输入模式   虽然gpio模式已经设置为了普通输入模式,这里需要注意的是,中断的类型必须要设置为,\ ``不使用中断``\ ,不然初始化会报错(ps:这里我觉得应该可以库内部实现默认,既然设置了普通输入模式那不久和中断没关系了吗,没有必要再去设置一下不使用中断)。 .. code:: c T_AMOPENAT_GPIO_CFG input_cfg = {0}; BOOL err = 0; input_cfg.mode = OPENAT_GPIO_INPUT; //配置输入模式 input_cfg.param.defaultState = 0; //默认为低电平 input_cfg.param.intCfg.debounce = 50; //防抖50ms input_cfg.param.intCfg.intType = OPENAT_GPIO_NO_INT; //不使用中断 err = iot_gpio_config(1, &input_cfg); //初始化gpio为普通输入模式 if (!err) return; iot_debug_print("[gpio] set gpio1 input"); 2.2.2、死循环查询程序 ~~~~~~~~~~~~~~~~~~~~~   由于是1000ms查询一次,防止每次查询都会打印查询信息,导致查询日志刷屏。这里做了一个小处理,保存一下上次的查询结果,每次查询都会与上次查询的结果进行比较,若不等于就说明状态发生了变化,这时候就可以打印输出了。 .. code:: c uint8 value = 0; uint8 oldstate = 0; while (1) { //iot_debug_print("[gpio] Tesk Run"); if (iot_gpio_read(1, &value) == TRUE) { /* iot_debug_print("[gpio] input gpio1 ok"); iot_debug_print("[gpio] value:%d,oldstate:%d", value, oldstate); */ if (value != oldstate) { iot_debug_print("[gpio] input gpio1, state %d", value); oldstate = value; } } else { iot_debug_print("[gpio] input gpio1 error"); } //线程休眠500ms osiThreadSleep(1000); }   查询法用起来比较简单需要不停的去查询   假如延时过短,就会出现任务切换频繁,执行效率低,cpu一直在忙着查询。   延时时间过长就会出现实时性低,比如我上面的代码GPIO1接通后运气不好的时候需要等一秒钟才会有反应,不建议在对事件敏感的时候使用   使用GPIO的驱动需要包含\ ``#include "iot_gpio.h"``\ 头文件,我们这里只用到了两个函数,分别是: | /**初始化gpio *@param port: GPIO编号*\ @param cfg: 配置信息 \*@return TRUE: 成功 |     FALSE: 失败 \**/ - BOOL ``iot_gpio_config``\ (E_AMOPENAT_GPIO_PORT port,T_AMOPENAT_GPIO_CFG \*cfg); .. /**设置gpio *@param port: GPIO编号*\ @param value: 0 or 1 \*@return TRUE: 成功      FALSE: 失败 \**/ - BOOL ``iot_gpio_read``\ (E_AMOPENAT_GPIO_PORT port,UINT8\* value) 三、编译并下载程序 ------------------   完整代码在这,自取。 .. code:: c /* * @Author: your name * @Date: 2020-05-19 14:05:32 * @LastEditTime: 2020-05-20 10:45:57 * @LastEditors: Please set LastEditors * @Description: In User Settings Edit * @FilePath: \RDA8910_CSDK\USER\user_main.c */ #include "string.h" #include "cs_types.h" #include "osi_log.h" #include "osi_api.h" #include "iot_debug.h" #include "iot_uart.h" #include "iot_os.h" #include "iot_gpio.h" #include "iot_pmd.h" //GPIO_Input void gpio_handle(E_OPENAT_DRV_EVT evt, E_AMOPENAT_GPIO_PORT gpioPort, unsigned char state) { uint8 status = 0; //iot_debug_print("[gpio] gpio_handle %d,%d,%d", evt, gpioPort, state); // 判断是gpio中断 if (evt == OPENAT_DRV_EVT_GPIO_INT_IND) { // 判断触发中断的管脚 if (gpioPort == 0) { // 触发电平的状态 iot_debug_print("[gpio] input handle gpio %d, state %d", gpioPort, state); // 读当前gpio状态, 1:高电平 0:低电平 iot_gpio_read(gpioPort, &status); iot_debug_print("[gpio] input handle gpio %d, status %d", gpioPort, state); } } } static void TestTask(void *param) { T_AMOPENAT_GPIO_CFG input_cfg = {0}; BOOL err = 0; input_cfg.mode = OPENAT_GPIO_INPUT; //配置输入模式 input_cfg.param.defaultState = 0; //默认为低电平 input_cfg.param.intCfg.debounce = 50; //防抖50ms input_cfg.param.intCfg.intType = OPENAT_GPIO_NO_INT; //不使用中断 err = iot_gpio_config(1, &input_cfg); //初始化gpio为普通输入模式 if (!err) return; iot_debug_print("[gpio] set gpio1 input"); uint8 value = 0; uint8 oldstate = 0; while (1) { //iot_debug_print("[gpio] Tesk Run"); if (iot_gpio_read(1, &value) == TRUE) { /* iot_debug_print("[gpio] input gpio1 ok"); iot_debug_print("[gpio] value:%d,oldstate:%d", value, oldstate); */ if (value != oldstate) { iot_debug_print("[gpio] input gpio1, state %d", value); oldstate = value; } } else { iot_debug_print("[gpio] input gpio1 error"); } //线程休眠500ms osiThreadSleep(1000); } osiThreadExit(); } //main函数 int appimg_enter(void *param) { T_AMOPENAT_GPIO_CFG input_cfg = {0}; BOOL err = 0; input_cfg.mode = OPENAT_GPIO_INPUT_INT; //配置输入中断 input_cfg.param.defaultState = 0; //默认为低电平 input_cfg.param.intCfg.debounce = 50; //防抖50ms input_cfg.param.intCfg.intType = OPENAT_GPIO_INT_BOTH_EDGE; //中断触发方式双边沿 input_cfg.param.intCfg.intCb = gpio_handle; //中断处理函数 err = iot_gpio_config(0, &input_cfg); //初始化gpio为中断输入模式 if (!err) return; iot_debug_print("[gpio] set gpio0 INT input"); //创建一个任务,用作查询法 osiThreadCreate("TestTask", TestTask, NULL, OSI_PRIORITY_NORMAL, 2048, 0); return 0; } //退出提示 void appimg_exit(void) { OSI_LOGI(0, "application image exit"); }   查看输出,将GPIO0和GPIO1分别接\ ``高电平``\ ,高电平只能接入\ ``1.8v``\ ,一定不要接入比1.8v高的电压!!!GPIO0双边沿都成功触发中断打印,查询法也只是打印出来了状态交替的现象,验证了程序运行是正确的。 |image4| |image5| 不会下载的\ `点击这里 `__\ ,进去查看我的\ ``RDA8910 CSDK二次开发入门教程``\ 专题第一篇博文\ ``1、RDA8910CSDK二次开发:环境搭建``\ 里面讲了怎么下载 这里只是我的学习笔记,拿出来给大家分享,欢迎大家批评指正,本篇教程到此结束 .. |在这里插入图片描述| image:: https://img-blog.csdnimg.cn/20200520101347617.jpg .. |image1| image:: https://img-blog.csdnimg.cn/20200216132030279.png .. |image2| image:: https://img-blog.csdnimg.cn/20200216132307740.png .. |image3| image:: https://img-blog.csdnimg.cn/20200520102436809.png .. |image4| image:: https://img-blog.csdnimg.cn/20200520110501839.png .. |image5| image:: https://img-blog.csdnimg.cn/20200520110412248.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU3MDA4Mw==,size_16,color_FFFFFF,t_70