TT无人机扩展模块库分析(default.ino)补篇1
昨天文章的最后着急的发表,没有好好分析最后这一个
我决定今天继续写一下
因为在具体的语句里面使用预处理命令是我第一次见的
平时都是在文件首有,今天在内部。值得说说
这个地方我在截图一下
在一个{
}内部写了几句
https://www.cnblogs.com/zi-xing/p/4550246.html
https://www.cnblogs.com/klb561/p/9256408.html
常见的预处理指令如下:
#空指令,无任何效果
#include包含一个源代码文件
#define定义宏
#undef取消已定义的宏
#if如果给定条件为真,则编译下面代码
#ifdef如果宏已经定义,则编译下面代码
#ifndef如果宏没有定义,则编译下面代码
#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个#if……#else条件编译块
#error停止编译并显示错误信息
什么是预处理指令?
预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。
以前没有在意的学者注意了,预处理指令是在编译器进行编译之前进行的操作.预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。可见预处理过程先于编译器对源代码进行处理。在很多编程语言中,并没有任何内在的机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码(防止重复包含某些文件)。要完成这些工作,就需要使用预处理程序。尽管在目前绝大多数编译器都包含了预处理程序,但通常认为它们是独立于编译器的。预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行响应的转换。预处理过程还会删除程序中的注释和多余的空白字符。
#include包含一个源代码文件
这个预处理指令,我想是见得最多的一个,简单说一下,第一种方法是用尖括号把头文件括起来。这种格式告诉预处理程序在编译器自带的或外部库的头文件中搜索被包含的头文件。第二种方法是用双引号把头文件括起来。这种格式告诉预处理程序在当前被编译的应用程序的源代码文件中搜索被包含的头文件,如果找不到,再搜索编译器自带的头文件。采用两种不同包含格式的理由在于,编译器是安装在公共子目录下的,而被编译的应用程序是在它们自己的私有子目录下的。一个应用程序既包含编译器提供的公共头文件,也包含自定义的私有头文件。采用两种不同的包含格式使得编译器能够在很多头文件中区别出一组公共的头文件。
#ifdef,#ifndef,#endif...的使用
以上这些预编译指令,都是条件编译指令,也就是说,将决定那些代码被编译,而哪些不被编译。
这些就应该解决问题了~
1. 常常使用宏来调试代码:
#if 0
///< 旧的代码(或函数) (旧的代码, 将会被预处理的时候,屏蔽掉, 不进行编译)
#else
///< 新的代码(或函数)
#endif
#ifndef JOE_DEBUG
///< 新的代码(或函数)
#else
///< 旧的代码(或函数) (旧的代码, 将会被预处理的时候,屏蔽掉, 不进行编译)
#endif
#ifdef Q_DEBUG
///< 新的代码(或函数)
#else
///< 旧的代码(或函数) (旧的代码, 将会被预处理的时候,屏蔽掉, 不进行编译)
#endif<br><br>
2. 使用宏来根据不同的平台包含不同的文件. 很多时候, 我们的代码是需要跨系统平台编译和运行的. 比如: 一个小功能代码, 需要既可以在Win下面运行, 还要可以在Max, linux上面运行. 可是, 因为系统的不一样, 有些时候, 头文件的包含的名字是不一样的. 所以,这时候, 就是用到了宏. 因为我们使用编程工具分不同的系统平台, 编程工具自身的环境就会包含不同平台的系统宏, 假设OS_Win, OS_Mac, OS_Linux 分别代码三种系统不同的宏. 而且,Win版本的编程工具中已经定义了OS_Win, 类似的Mac下, 编程工具定义的是OS_Mac, Linux...
#ifdef OS_Win
#include <windows.h>
#endif
#ifdef OS_Mac
#include <mac.h>
#endif
#ifdef OS_Linux
#include <linux.h>
#endif
/** 不仅使用在头文件的包含. 而且,对于不同的系统平台. 你也可以使用不同的代码结构. */
如果觉得上面不好,可以再看下面这段:
#define 定义一个预处理宏
#undef 取消宏的定义
#if 编译预处理中的条件命令,相当于C语法中的if语句
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
defined 与#if, #elif配合使用,判断某个宏是否被定义
以#开头的都是预编译指令,就是在正式编译之前,编译器做一些预处理的工作
#if 条件语句
程序段1 //如果条件语句成立,那么就编译程序段1
#endif
程序段2//如果条件不语句成立,那么就编译程序段2
#ifndef x//先测试x是否被宏定义过
#define 程序段1 //如果x没有被宏定义过,那么就编译程序段1
#endif
程序段2 //如果x已经定义过了则编译程序段2的语句,“忽视”程序段1。
#ifdef x //先测试x是否被宏定义过
程序段1 //如果x被宏定义过,那么就编译程序段1
#endif
程序段2 //如果x没有被定义过则编译程序段2的语句,“忽视”程序段1。
if就是判断语句,不是预编译指令
我们有了基础知识继续看
如果__DEFAULT_LOG__通过
第一次出现,被注释了。所以应该是调试语句
此时我要打开它
第一次出现
第二次
第三次
第四次
第五次
可以看到就是对单一的模块的调试,课本上课不讲这个
再继续吧这个放到这里
这个CommonSerial用宏定义到serial
其实就是个串口打印~
只要上面的开关打开,下面运行的时候会打印状态信息。也可以关闭。
安静的运行。这个方法我学会了!
除去这个调试的语句。
如果!=按键切换为真
打印操控功能开启
否则输出控制功能关闭
看typedef
typedef
现在再看这个程序是不是就很清楚了
一个中断程序,作用是按两次按键激活一个功能
接着三个定义,不解释意思了就
接着用IO口读取一个引脚的值
并且分次测量运行时间
作差
然后是一个逻辑的判断,做一些打印,功能激活
初始化全局电流,最大值。应该是屏幕会全亮一次
接着是设置函数(横排,竖排,LED的状态(开闭))
设置阵列中所有LED的LED亮度。三行代码~
参数是一个指针,所以需要强制转换参数,来读一个数组。在下面
就是这样
一共128颗灯
64个红,64 个蓝
函数及其参数表
看最后一个显示函数
文中第一次出现,是函数申明。方便编译器使用
在设置这个LED的函数里面第二次出现
这段是点亮LED的具体实现
这个文件系统可以让我们存储一些变更频率不频繁的文件例如网页、配置或者是某些固化的数据等,它就是SPIFFs - SPI Flash Filing System!
https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/storage/spiffs.html
先启用这个文件系统。
https://www.jianshu.com/p/014bcae94c8b
begin该方法用于挂载SPIFFS文件系统,必须在使用SPIFFS之前就调用,一般都会在setup()
过程调用。该方法如果调用成功将会返回true
,否则返回false
接着判断(用spiffs模块的查看文件是否存在的功能看这个有对LED描述的数组有没有存在,有就继续运行。没有就关闭文件系统)
下面写继续运行的代码
把if缩进了看一眼
接着一个int 变量。盲猜循环变量
然后一个图像缓存空间。就是128的一个数组。名字有点厉害
接着open函数,打开且可读。
open打开指定位置上的一个文件并返回File
对象。
path
- 文件的路径(如:/test.text
)mode
- 文件的读写模式,可以为 "r", "w", "a", "r+", "w+", "a+"中的任意一个,这个与C言语中访问文件系统的方式是一样的。
该方法返用成功后会返回一个File
对象,否则就会返回空。
文件系统结构:
struct FSInfo {
size_t totalBytes; // 可用量
size_t usedBytes; // 已用
size_t blockSize; // 块大小
size_t pageSize; // 页大小
size_t maxOpenFiles; // 最大打开文件数
size_t maxPathLength; // 最大文件路径长度
};
读到这个文件,开始用while读取
结束条件是文件一直打开可用和i的值小于128.可以保证读取完整。
graph_buff[i++] = (char)file.read();
然后这句是用(char)转了一下读到的内容。并且存到数组里面,就是图像显存里面。一会儿用。
开始setup(配置环节了)
这是第一次出现,函数申明
这边简单
34的引脚下拉输入模式
如果34jio读取到值为0
就执行这个WiFi的函数
函数的具体定义。我来看看
先把while缩起来
一个计数变量 cnt 初值为0
两个串口,一个波特率比一个波特率高
https://www.arduino.cc/reference/en/#serial
设置以每秒比特数(波特)为单位的串行数据传输的数据速率。要与串行监视器通信,请确保使用其屏幕右下角菜单中列出的波特率之一。但是,您可以指定其他速率-例如,通过引脚0和1与需要特定波特率的组件进行通信。
可选的第二个自变量配置数据,奇偶校验和停止位。默认值为8个数据位,无奇偶校验,一个停止位。
将设置代码放在此处,即可运行一次
初始化灯
同时设置三个灯的亮度
这个太简单了,不说了
附上一个呼吸灯的程序
引脚34设置为输入上拉
接下来是一个中断函数
attachInterrupt()函数是用于为Arduino开发板设置和执行ISR(中断服务程序)用的
ISR(中断服务程序)顾名思义就是中断Arduino当前正在处理的事情而优先去执行中断服务程序。当中断服务程序完成以后,再回来继续执行刚才执行的事情。中断服务程序对监测Arduino输入有很大的用处。
注意
在ISR(中断服务程序)函数中,delay()函数是不工作的,而且millis()函数返回值也不再增长。在ISR(中断服务程序)运行期间Arduino开发板接收到的串口数据也可能丢失。另外ISR函数里所使用的变量应声明为volatile类型。详情请见以下”关于ISR(中断服务程序)”部分。
使用中断
中断很适合执行那些需要不断检查的工作,比如检查一个引脚上连接的按键开关是否被按下。中断更适用于很快就会消失的信号检查,比如某一个引脚用于检测脉冲信号,这个脉冲信号的持续时间可能十分短暂。如果不使用中断,那么假如Arduino开发板正在执行其它任务时,突然这个脉冲信号来了,还不等Arduino开发板完成正在执行的工作,这个脉冲信号可能就已经消失了。而使用中断,就可以确保这个转瞬即逝的脉冲信号可以很好的被Arduino开发板检测到并执行相应任务。
关于ISR(中断服务程序)
对于Arduino开发板来说,ISR(中断服务程序)是一种特殊的函数。它的特殊意味着它具有其它类型函数所不具备的限制和特点。
ISR函数不能有任何参数。ISR也没有任何返回值。
通常ISR需要越短小精悍越好!另外如果您的代码中有多个ISR函数,那么每次Arduino只能运行一个ISR函数,其它ISR函数只有在当前的ISR函数执行结束以后,才能按照其优先级别顺序执行。
millis()函数的运行依赖Arduino开发板的中断功能,因此ISR函数中的millis()函数是无法正常运行的。micros() 也是类似的情况,它只能在初始的1-2毫秒中可以运行,但是过了这1-2毫秒后就开始出现问题了。delayMicroseconds() 不需要任何计数器就可以运行,所以delayMicroseconds() 运行是不会受到影响的。
一般情况下,ISR函数与主程序之间传递数据是依靠全局变量来实现的。为了确保全局变量在ISR函数中可以正常的工作,应该将可能被ISR函数中使用的全局变量声明为volatile类型。
语法
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);
参数
pin: 中断引脚号
ISR: 中断服务程序名
mode:中断模式
中断模式(mode)有以下几种形式:
LOW:当引脚为低电平时触发中断服务程序
CHANGE:当引脚电平发生变化时触发中断服务程序
RISING:当引脚电平由低电平变为高电平时触发中断服务程序
FALLING:当引脚电平由高电平变为低电平时触发中断服务程序
http://www.taichi-maker.com/homepage/reference-index/arduino-code-reference/attachinterrupt/
有了这么多的知识铺垫,继续
34脚,双击这个函数,当引脚电平由高电平变为低电平时触发中断服务程序
打印字符串和SDk的版本
先这篇到这里~下文继续