导图社区 0基础自动化学习机相关项目内容
分析RC遥控及单片机控制逻辑,让上手RC及单片机的新手有一个清晰概念。单片机是整个遥控模型控制的核心,它根据用户操作或预设的程序来驱动电机和其他设备。在控制电机的过程中,单片机采用了PWM(脉宽调制)信号来控制电机的转速和方向。
编辑于2024-06-11 10:21:01自动化机械
外壳
3D打印
3D打印机
Neptune3pro
bambu X1C
切片软件
prusa slicer2.6
bambu studio
建模
工业建模
fusion 360
blender
sketchup(建筑)
人物建模
Zbrush
nomad(华为平板自带)
核心
PCB(印刷电路板)
单片机
硬件
51
arduino
stm32
esp8266
esp32
多个单片机连接
多个arduino连接
ISP串口通信
IIC串口通信
软件
Arduino IDE
控制arduino
控制stm32
?
语言
Arduino IDE //类似C/C++
1.编码结构(整体结构介绍)
1.前置定义
1.变量定义(variable)
int
int *=number or not // 书写结构,如果没有输入默认为0
?
2.函数定义(function)
void
void name(void) {}//name为定义函数的名字
如变量在前则函数定义可以在void setup()后
3.预处理命令
#define
预处理命令以“#”号开头,如#include(包含命令),#define(宏定义命令)等。预处理命令一般放在源文件的开头部分,也称为预处理部分。
在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。
在C或C++语言中,“宏”分为有参数和无参数两种。
#define语句的后面没有分号。如果你加了一个分号”;”编译器将会报错。
另外在使用define时使用等号也会产生错误
无参数
//#define ledPin 13
有参数
//#define M(y) y*y+3*y
#include
#include用于在程序中包含外部的库文件。通过#include语句,你可以在程序中直接使用丰富的标准C程序资源。
2.viod setup(){} //单次执行内容
执行函数(function)和运算
pinMode(pin,INPUT/OUTPUT)//输出输入针角定义,pin为引脚,可以为变量
Serial.begin(9600)
//通信波特率,模拟信号需要输入时执行
3.viod loop(){} //连续执行内容
digitalWrite(pin,HIGH/LOW) //输出引脚高低电平-高亮低不亮,;注意pin可以为变量
delay(time) //延迟毫秒数
4.案例
#include <Servo.h>//无实际意义,只做演示 int ADPIN = A0; int PWM_LEDPIN = 11; int value = 0; float voltage =0.0; void setup() { // put your setup code here, to run once: pinMode(ADPIN, INPUT); pinMode(PWM_LEDPIN, OUTPUT); Serial.begin(9600); } void loop() { value = analogRead(ADPIN); Serial.print(" value = "); Serial.print(value); voltage = (float)value / 1023; Serial.print(" voltage = "); Serial.print(voltage); value = (int)(voltage * 255); Serial.print(" PWM = "); Serial.println(value); analogWrite(PWM_LEDPIN, value); delay(0);
2.结构语言
结构
setup()
建立通信函数,引脚定义,定义特殊函数
loop()
继续运行代码文件
控制结构
if
if(表达式) { 语句块 }
上述结构表示:如果 “表达式” 的条件得到满足则执行语句块。否则Arduino将不执行该语句块
if....else
if( 表达式1 ) { 语句块1 } else { 语句块2 }
上述结构表示:如果 “表达式1” 的条件得到满足则执行”语句块1″。否则Arduino将执行”语句块2″。
for
说明
for语句用于重复执行一段语句块。通常会使用一个增量计数器递增和终止循环。for语句对于任何需要重复的操作是非常有用的。
语法
for(表达式1; 表达式2; 表达式3){ 语句块 }
示例
void setup(){ int i, sum=0; for(i=1; i<=100; i++){ sum = sum + i; } } void loop(){ }
注意
1) for循环中的“表达式1(循环变量赋初值)”、“表达式2(循环条件)”和“表达式3(循环变量增量)”都是选择项,即可以缺省,但分号(;)不能缺省。 2) 省略了“表达式1(循环变量赋初值)”,表示不对循环控制变量赋初值。 3) 省略了“表达式2(循环条件)”,如果不做其它处理就会成为死循环。 4) 省略了“表达式3(循环变量增量)”,则不对循环控制变量进行操作,这时可在语句体中加入修改循环控制变量的语句。如以下示例: for( i=1; i<=100; ){ sum=sum+i; i++; }
switch case
结构
switch (var) { case 1: //当var等于1时执行这里的程序 break; case 2: //当var等于2时执行这里的程序 break; default: // 如果var的值与以上case中的值都不匹配 // 则执行这里的程序 break; }
switch语句通过对一个变量的值与case语句中指定的值进行比较。当一个case语句中的指定值与switch语句中的变量相匹配。就会运行这个case语句下的代码。 通过break关键字,Arduino可以中止并跳出switch语句段,break关键字常常用于每个case语句的最后面。如果没有break语句,switch语句将继续执行下面的表达式(“持续下降”)直到遇到break,或者是到达switch语句的末尾。
注意
1) 在以上结构示例代码中,当变量var和某个case后面的数值匹配成功后,如果没有break, Arduino会执行该分支以及后面所有分支的语句。 2) case 后面必须是一个整数,或者是结果为整数的表达式,但不能包含任何变量。 3) case 后面不能使用字符串,但可以使用字符,使用字符时需要用单引号把字符括起来,如: case: 'b'。 4) default 不是必须的。当没有 default 时,如果所有 case 都匹配失败,那么就什么都不执行。
示例
/* switch...case语句示例程序 v1.0 Created 2016 by 太极创客 www.taichi-maker.com 说明: 本示例程序后,用户可使用Arduino IDE的串口监视器 向Arduino 发送字符(a, b, c, d,或者e)Arduino 根据用户输入的字符,点亮相应的LED。此程序旨在演示 如何使用switch...case语句对Arduino控制器进行编程。 This example code is in the public domain. */ void setup() { Serial.begin(9600); // 初始化串口通讯 for (int thisPin = 2; thisPin < 7; thisPin++) {// 初始化Arduino连接LED的引脚 pinMode(thisPin, OUTPUT); } } void loop() { if (Serial.available() > 0) { int inByte = Serial.read(); // Arduino用switch语句,根据接收到的不同信息进行相应的反应。 switch (inByte) { case 'a': digitalWrite(2, HIGH); break; case 'b': digitalWrite(3, HIGH); break; case 'c': digitalWrite(4, HIGH); break; case 'd': digitalWrite(5, HIGH); break; case 'e': digitalWrite(6, HIGH); break; default: // 熄灭所有LED: for (int thisPin = 2; thisPin < 7; thisPin++) { digitalWrite(thisPin, LOW); } break; } } }
Arduino运行以上程序后,用户可以通过串口监视器向Arduino输入字符。该输入信息将赋值给变量inByte。 例如,用户输入字符a,Arduino将执行case ‘a’后面的语句,点亮连接在引脚2上面的LED。再比如,用户输入字符b,Arduino将执行case ‘b’后面的语句,点亮连接在引脚3上面的LED。以此类推… 如果用户的输入字符是a,b,c,d e以外的字符,Arduino将执行default:后面的语句,将所有LED熄灭。
break
使用说明
break用于绕过正常循环条件并中止do,for,或while循环。它也可用于中止switch语句。
示例
/* break语句示例程序 v1.0 Created 2016 by 太极创客 www.taichi-maker.com 说明: 此程序旨在演示如何使用break语句跳出循环语句。 This example code is in the public domain. */ void setup() { // 初始化串口通讯 Serial.begin(9600); } void loop() { int i; int sum = 0; while(1){ //循环条件为死循环 sum+=i; i++; if(i>100){ break; } } Serial.print ("sum = "); //通过串口监视器输出 Serial.println (sum); //do-while循环结束后的sum值 delay (5000); // 延迟5秒钟 }
以上程序第22行语句中,while循环条件为 1,是一个死循环。当执行到第100次循环后,第24行语句 i++;将使变量 i 的值变为 101。此时 if 语句的条件 i> 100 成立,所以Arduino将执行break;语句结束循环。
return
终止一个函数,并向调用此函数的函数返回一个值
语法
return; return value; // 有无返回值value都可以
value: 任何类型的变量或常量
continue
continue语句的作用是跳过循环体中剩余的语句而强制进入下一次循环。continue语句用于 while、for 循环中,常与 if 条件语句一起使用,判断条件是否成立。
示例
/* continue语句示例程序 v1.0 Created 2016 by 太极创客 www.taichi-maker.com 说明: 此程序旨在演示如何continue语句对Arduino控制器进行编程。 This example code is in the public domain. */ void setup() { pinMode (3, OUTPUT); } void loop() { for (int x = 0; x < 255; x ++) { if (x > 40 && x < 120){ // 当x大于40或小于120 continue; // 跳过此次循环 } analogWrite(3, x); } }
当第19行for语句首次运行时,x的值为0。此时x既不小于40也不大于120,第21行if语句中的表达式内容得不到满足。Arduino将执行第24行analogWrite(3,x);语句。 当for循环进行到第42次,x的值递增为41。此时x值大于40,满足if语句中的表达式内容。Arduino将执行continue;语句,跳过此次循环,不再执行analogWrite(3,x);语句。 当for循环进行到第122次时,x的值递增为121。此时x大于120,不满足if语句中的表达式内容。Arduino将不再执行continue;语句,继续执行analogWrite(3,x);语句。
while
说明
while循环将会连续地无限地循环,直到圆括号()中的表达式变为假。被测试的表达式变量必须被改变,否则while循环将永远不会中止。可以在代码改变测试变量,比如让该变量递增,或者通过外部条件改变测试变量,比如将一个传感器的读数赋值给测试变量。
结构
while(表达式/循环条件){ 语句块/循环体 }
先计算表达式的值,当值为真(非0)时, 执行循环体语句;执行完循环体语句,再次计算表达式的值,如果为真,继续执行循环体……这个过程会一直重复,直到表达式的值为假(0)才退出循环。
区别
在Arduino编程中,if和while语句都有各自的应用场景。 if语句常用于进行一次性条件判断,根据条件是否满足来决定是否执行某段代码。例如,在输入传感器数据时,可以根据传感器读取的数值是否超过某个阈值来决定是否点亮某个LED灯。 while语句则用于循环执行某段代码,直到条件不再满足为止。例如,在持续输出传感器数据时,可以使用while循环来不断读取传感器的数值,并将其输出到串口。 因此,在Arduino编程中,需要根据具体的需求和场景选择使用if还是while语句。如果只需要进行一次性条件判断,可以选择if语句;如果需要循环执行某段代码,则可以选择while语句。
do....while
说明
do-while循环与while循环使用相同方式工作,不同的是表达式条件是在循环的末尾测试的,所以do-while循环总是至少会运行一次。
结构
do{ 语句块 } while(表达式);
do-while循环与while循环的不同在于:它会先执行循环体,然后再判断表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while 循环至少要执行一次循环体。
进阶语法
;
{}
//
单行注释
/**/
多行注释
#define
预处理命令以“#”号开头,如#include(包含命令),#define(宏定义命令)等。预处理命令一般放在源文件的开头部分,也称为预处理部分。
#include
#include用于在程序中包含外部的库文件。通过#include语句,你可以在程序中直接使用丰富的标准C程序资源。
注意
#include和#define相似,没有分号终止符,如果你在末尾加了分号,编译器会产生错误信息。
define 和 include后面没有分号(;)
算术运算符
=
+
_
*
/
%
取模运算
计算一个数除以另一个数的余数。该运算可用于将一个变量保持在一个特定的数值范围内。
x = 7 % 5; // 运算结果为 2 x = 9 % 5; // 运算结果为 4 x = 5 % 5; // 运算结果为 0 x = 4 % 5; // 运算结果为 4
关系运算符
说明
x == y (x 等于 y) x != y (x 不等于 y) x < y (x 小于 y) x > y (x 大于 y) x <= y (x 小于等于 y) x >= y (x 大于等于 y)
注意
比较运算符用于和 if 联合使用,测试某一条件是否达到。例如测试Arduino的某一个引脚输入值是否达到设定数值。
不要把赋值符号(单个等号)和比较运算符(双等号)相互混淆。例如: [c gutter=”0″] if(x = 10) //这个语句错误的将赋值符号用作比较运算符 [/c] 单个等号是赋值运算符,以上程序设置x为10(将值10存入变量x)在这里这个赋值运算符是不对的。因为赋值语句返回值总为真。这就导致if条件判断错误的认为圆括号中的条件总为真。以上圆括号中的语句内容应改为如下形式: [c gutter=”0″] if (x == 10) //比较运算符是由两个等号构成的 [/c] == 是比较运算符,用于测试x是否等于10。在以上程序语句中,只在 x 等于10时返回真
布尔运算符
&&
逻辑与
只有在两个操作数都为真时才返回真。
||
逻辑或
任意一个为真时返回真
!
逻辑非
当操作数为假时返回真
位运算符
& (位与)
按位与运算符是单个与符号(&)。如果两个位都是1,结果为1,否则结果0。如下所示: [c gutter=”0″] 0 0 1 1 运算数1 0 1 0 1 运算数2 ———- 0 0 0 1 (运算数1 & 运算数2) 运算结果
| (位或)
按位或运算符是垂直的条杆符号(|)。其运算规则是:只要任一表达式的一位为 1,则结果中的该位为 1。否则,结果中的该位为 0。
^ (位异或)
位异或运算符是^符号。其运算规则是:如果两个运算位相同,则结果为0,否则为1。
~ (位非)
按位取反运算符为波浪符“~”。按位取反操作会翻转其每一位。0变为1,1变为0。
<< (位左移)
Arduino编程有两个移位运算符:左移运算符(<<)和右移运算符(>>)。这些运算符将使运算数向左或向右移动指定的位数。
示例
[c gutter=”0″] int a = 5; // 二进制: 0000000000000101 int b = a << 3; // 二进制: 0000000000101000, b是a左移三位后的结果 int c = b >> 3; // 二进制: 0000000000000101, c是b右移三位后的结果 [/c]
注意
当把x左移y位(x << y),x中最左边的y位将会丢失。如以下程序所示 [c gutter=”0″] int a = 5; // binary: 0000000000000101 int b = a << 14; // binary: 0100000000000000 – 101 中的第一个 1 被丢弃了 [/c]
从右往左,右为初始位
>> (位右移)
复合运算符
++ (自加)
[c gutter=”0″]i++; //相当于 i = i + 1; [/c]
--(自减)
[c gutter=”0″]i–; //相当于 i = i – 1; [/c]
+= (复合加)
[c gutter=”0″]i+=5; //相当于 i = i + 5;[/c]
-= (复合减)
[c gutter=”0″]i-=5; //相当于 i = i – 5;[/c]
*= (复合乘)
[c gutter=”0″]i*=5; //相当于 i = i * 5;[/c]
/= (复合除)
[c gutter=”0″]i/=5; //相当于 i = i / 5;[/c]
&= (复合与)
[c gutter=”0″]i&=5; //相当于 i = i & 5;[/c]
|= (复合或)
[c gutter=”0″]i|=5; //相当于 i = i | 5;[/c]
指针
概念
在Arduino中,数据是存放在内存中的,一般把内存中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不一样,int占用2个字节,char占用1个字节。为了正确地访问这些内存单元,必须为每个内存单元编上号。每个内存单元的编号是唯一的,根据编号可以准确地找到该内存单元。 内存单元的编号叫做地址(Address),也称为指针(Pointer)。
在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。
严格地说,一个指针只是一个地址值常量。然而一个指针变量却可以被赋予不同的指针地址值,是变量。人们经常把指针变量简称为指针。为了避免混淆,在本网站中,“指针”是指地址值常量。“指针变量”是指取值为地址值的变量。我们将地址值赋给指针变量的目的是为了通过指针访问内存单元。
指针变量
概念
指针变量就是地址变量。换句话说,指针变量所存放的地址是可以改变的。在程序中,每一个变量都有一个地址。这些地址都可以存放在指针变量中。
为了表示指针变量和它所指向的变量之间的关系,在程序中用 *符号表示“指向”。 i_pointer 是指针变量 &i_pointer所存储的内容是变量的地址 *i_pointer是i_pointer所指向的变量存储的内容,也就是变量i的内容。
因此,下面两个语句作用相同: i=3; *i_pointer=3; 第2个语句的含义是将3赋给指针变量i_pointer所指向的变量。
定义指针变量
定义指针变量的一般形式为: 类型说明符 *变量名; 其中,*表示这是一个指针变量,类型说明符表示该指针变量所指向的变量的数据类型。例如: int* p1; 以上程序中,p1是一个指针变量,它的值是某个整型变量的地址。或者说p1指向一个整型变量。至于p1究竟指向哪一个整型变量,应由p1所赋予的地址来决定。再如: int* p2; //p2是指向整型变量的指针变量 float* p3; //p3是指向浮点变量的指针变量 char* p4; //p4是指向字符变量的指针变量 注意:一个指针变量只能指向同类型的变量,如 p3 只能指向浮点变量,不能时而指向一个浮点变量,时而又指向一个字符变量
指针变量的引用
指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值(初始化)。未初始化的指针变量值没有意义,不能使用。 指针变量的赋值只能赋予地址,决不能赋予任何其它数据,否则将引起错误。在C语言中,变量的地址是由编译器分配的,对用户完全透明,用户不能控制变量的具体地址。
指针运算符
& 取地址运算符: 它返回其操作数(即一个变量)在内存中的实际地址; * 指针运算符: 它返回其操作数(即一个指针)所指向的对象的值。
一般形势
&变量名; 假设有整数型变量a,那么&a表示变量a的地址。再假设我们还有一个指向整型变量的指针变量p,如要把变量a的地址赋予指针变量p可以用以下方式。 int a; //建立整数型变量a int* p; //建立整数指针变量p p=&a; //将变量a的地址赋予指针变量p
示例
/* 指针示例程序 太极创客 www.taichi-maker.com 此程序旨在演示如何通过指针操作来改变一个整数变量的数值。 */ void setup() { Serial.begin(9600); int val = 10; // 建立整数型变量val,该变量初始值为10 int* pVal = &val; // 建立整数型指针变量并且将val变量的地址 // 赋值给该指针变量(pVal指向val的地址) Serial.println("val变量原始数值:"); Serial.print("val = ");Serial.println(val); pointerWork(pVal); // 通过指针操作改变变量val数值 Serial.println("val变量经过指针操作后的数值:"); Serial.print("val = ");Serial.println(val); // val变量经过pointerWork函数的操作后 // 数值变为了 123 } void loop() { } void pointerWork(int* pointer){ *pointer = 123; }
实际运行意义
roc机器人案例
#include <Servo.h> Servo myservo; // 创建Servo对象,用于控制ROC机器人 int pos = 0; // 定义变量,用于存储ROC机器人的当前位置 void setup() { myservo.attach(9); // 将 Servo 连接到数字引脚 9 } void loop() { int targetPos = 180; // 定义目标位置 int speed = 1000; // 定义速度 // 将目标位置和速度存储到变量中 int *targetPosPtr = &targetPos; int *speedPtr = &speed; // 通过指针获取目标位置和速度的值 int targetPosValue = *targetPosPtr; int speedValue = *speedPtr; // 控制ROC机器人向目标位置移动,并设置速度 myservo.writeMicroseconds(pos + speedValue); delay(100); // 等待一段时间,以便观察结果 }
在上述代码中,我们首先包含了Servo.h库,以使用Servo对象来控制ROC机器人。然后,我们创建了一个Servo对象myservo,并将其连接到数字引脚9。 在loop()函数中,我们定义了目标位置targetPos和速度speed,并将它们存储到变量中。然后,我们创建了两个指针变量targetPosPtr和speedPtr,并将它们的地址分别设置为targetPos和speed的地址。这样,我们就可以通过指针直接访问和修改这些变量的值。 接下来,我们使用指针targetPosPtr和speedPtr获取目标位置和速度的值,并将它们存储到相应的变量中。然后,我们使用myservo.writeMicroseconds()函数将ROC机器人向目标位置移动,并设置速度。最后,我们使用delay()函数等待一段时间,以便观察结果。 这个代码示例展示了如何使用Arduino指针来控制ROC机器人的运动。通过指针,我们可以更直接地访问和修改变量的值,从而实现更灵活和高效的控制方式。
九轴姿态传感器?
2.其他类型数据及函数等
1.function(I/O) //I/O函数
数字I/O
pinMode()
通过pinMode()函数,你可以将Arduino的引脚配置为以下三种模式:
输入上拉(INPUT_PULLUP)模式 (仅支持Arduino 1.0.1以后版本)
输入(INPUT)模式
输出(OUTPUT)模式
在输入上拉(INPUT_PULLUP)模式中,Arduino将开启引脚的内部上拉电阻,实现上拉输入功能。一旦将引脚设置为输入(INPUT)模式,Arduino内部上拉电阻将被禁用。
语法:pinMode(pin, INPUT/OUTPUT/INPUT_PULLUP)
digitalWrite()
将数字引脚写HIGH或者LOW;如果该引脚通过pinMode()设置为输出模式(OUTPUT),您可以通过digitalWrite()语句将该引脚设置为HIGH(5伏特)或LOW(0伏特/GND)。
语法:digitalWrite(pin, value)
其中value为HIGH或者LOW
digitalRead()
读取数字引脚的 HIGH(高电平)或 LOW(低电平)
digitalRead(pin)
pin为需要识别的引脚;可以作为函数去确定变量的值
模拟I/O
analogRead()
本指令用于从Arduino的模拟输入引脚读取数值。Arduino控制器有多个10位数模转换通道。这意味着Arduino可以将0-5伏特的电压输入信号映射到数值0-1023
在模拟输入引脚没有任何连接的情况下,用analogRead()指令读取该引脚,这时获得的返回值为不固定的数值。这个数值可能受到多种因素影响,如将手靠近引脚也可能使得该返回值产生变化。
analogRead(pin)//返回0到1023之间的值
analogWrite()
将一个模拟数值写进Arduino引脚。这个操作可以用来控制LED的亮度, 或者控制电机的转速. Arduino每一次对引脚执行analogWrite()指令,都会给该引脚一个固定频率的PWM信号。PWM信号的频率大约为490Hz.
在Arduino UNO控制器中,5号引脚和6号引脚的PWM频率为980Hz
在调用analogWrite()函数前,您无需使用pinMode()函数来设置该引脚
analogWrite(pin, value)
value:0到255之间的PWM频率值, 0对应off, 255对应on
高级I/o
tone()
说明
tone()函数可以产生固定频率的PWM信号来驱动扬声器发声。发声时间长度和声调都可以通过参数控制。定义发声时间长度有两种方法,第一种是通过tone()函数的参数来定义发声时长,另一种是使用noTone()函数来停止发声。如果您在使用tone()函数时没有定义发声时间长度,那么除非您通过noTone()函数来停止声音,否则Arduino将会一直通过tone()函数产生声音信号。
注意
1. 对于Arduino Mega以外的控制器,使用tone()函数时会影响引脚3和引脚11的PWM信号输出。 2. 如果你想要使用不同的引脚产生不同的声音音调,每一次更换发声引脚以前都要使用noTone函数停止上一个引脚发声。Arduino是不支持两个引脚同时发声的。
语法
tone(pin, frequency) tone(pin, frequency, duration)
参数 pin: 发声引脚(该引脚需要连接扬声器) frequency: 发声频率(单位:赫兹) – 无符号整数型 duration: 发声时长(单位:微秒,此参数为可选参数) – 无符号长整型
返回值
无
noTone()
说明
noTone()函数用来停止tone()函数发声。 注:如您需要使用多个Arduino引脚发声,要在每个引脚输出声音信号前调用noTone()函数来停止当前的声音信号。
语法
noTone(pin)
参数 pin: 停止发声引脚
返回值
无
案例
void setup() { } void loop() { noTone(8); //停止8号引脚发声 tone(6, 440, 200); //6号引脚发声200毫秒 delay(200); noTone(6); //停止6号引脚发声 tone(7, 494, 500); //7号引脚发声500毫秒 delay(500); noTone(7); //停止7号引脚发声 tone(8, 523, 300); //8号引脚发声300毫秒 delay(300); }
shiftOut()
说明
将一个字节的数据通过移位输出的方式逐位输出。数据可以从最高位(最左位)或从最低位(最右位)输出。在输出数据时,当一位数据写入数据输出引脚时,时钟引脚将输出脉冲信号,指示该位数据已被写入数据输出引脚等待读取。 如果读取数据的设备是在Arduino的时钟引脚脉冲信号上升沿读取Arduino的输出数据,请确保在调用shiftOut()输出数据前,应先通过digitalWrite(clockPin, LOW)语句,将时钟引脚设置为LOW。这样做是为了确保数据读取准确无误。 以上介绍的方法使用软件实现数据输出操作。如果想要通过硬件方法输出数据,请参阅Arduino的SPI库函数。通过硬件方法输出数据更加快捷,但Arduino只有几个特定引脚可用于使用硬件方法输出数据。
语法
shiftOut(dataPin, clockPin, bitOrder, value)
参数 dataPin – 数据引脚 clockPin – 时钟引脚 bitOrder – 移位顺序 ( 高位先出 或 低位先出) val – 数据
返回值
无
注意
使用shiftOut()函数前,数据引脚(dataPin)和时钟引脚(clockPin)必须先通过pinMode()指令设置为输出(OUTPUT)模式。 shiftOut一次只能输出1字节(8位)数据。如果需要输出大于255的数值,需要通过多次使用shiftOut()输出数据。如以下程序所示: [c gutter=”0″] // 高字节先出模式 int data = 500; //待输出数据 shiftOut(dataPin, clock, MSBFIRST, (data >> 8)); // 输出高位字节 shiftOut(dataPin, clock, MSBFIRST, data); // 输出低位字节 // 低字节先出模式 data = 500; //待输出数据 shiftOut(dataPin, clock, LSBFIRST, data); // 输出低位字节 shiftOut(dataPin, clock, LSBFIRST, (data >> 8)); // 输出高位字节 [/c]
shiftIn()
说明
将一个字节的数据通过移位的方式逐位输入。数据可以从最高位(最左位)或从最低位(最右位)输入。在输入数据时,Arduino首先在时钟引脚输出高电平,然后通过数据输入引脚读取一位数据,读取结束后时钟引脚将被Arduino设置为低电平。 如果与Arduino进行数据通讯的设备是在时钟引脚脉冲信号上升沿发送数据,请确保在调用shiftIn()前,应先通过digitalWrite(clockPin, LOW)语句,将时钟引脚设置为LOW。这样做是为了确保数据读取准确无误。 以上介绍的方法使用软件实现数据输出操作。如果想要通过硬件方法输出数据,请参阅Arduino的SPI库函数。通过硬件方法输入数据更加快捷,但Arduino只有几个特定引脚可用于使用硬件方法输入数据。
语法
byte incoming = shiftIn(dataPin, clockPin, bitOrder)
参数 dataPin – 数据引脚 clockPin – 时钟引脚 bitOrder – 移位顺序 ( 高位先入 或 低位先入)
返回值
读取到的数据
pulseIn()
说明
读引脚的脉冲信号, 被读取的脉冲信号可以是 HIGH 或 LOW. 例如我们要检测HIGH脉冲信号, Arduino将在引脚变为高电平时开始计时, 当引脚变为低电平时停止记时,并返回脉冲持续时长(时间单位:微秒)。如果在超时时间内没有读到脉冲信号的话, 将返回0. 根据经验发现,pulseIn()函数在检测脉冲间隔过短的信号时会产生错误。Arduino可检测的脉冲间隔时间范围是10微秒到3分钟。请留意假如调用pulseIn()函数时读取信号的引脚上已经为高电平,此时Arduino将等待该引脚变为低电平以后再开始检测脉冲信号。另外只有Arduino的中断是开启时,才能使用pulseIn()。
语法
pulseIn(pin, value) pulseIn(pin, value, timeout)
参数 pin 引脚编号 state 脉冲状态 timeout 超时时间(单位:微秒) 如果Arduino在超时时间(timeout)内没有读到脉冲信号的话, 该函数将返回0.超时时间参数是可选参数,其默认值为1秒。
返回值
脉冲持续时长。 如果在超时时间内没有读到脉冲信号, 将返回0.
示例
可用于超声波测距
int pin = 7; unsigned long duration; void setup() { pinMode(pin, INPUT); } void loop() { duration = pulseIn(pin, HIGH); }
获取PWM信号
#include <LobotServoController.h> LobotServoController myse; unsigned long duration1; unsigned long duration2; unsigned long duration3; unsigned long duration4; void setup() { pinMode(9, INPUT); pinMode(4, INPUT); pinMode(5, INPUT); pinMode(6, INPUT); Serial.begin(9600); while(!Serial); myse.runActionGroup(0,1); delay(1500); } void loop() { duration1 = pulseIn(9, HIGH);//右油门左右 duration2 = pulseIn(4, HIGH);//左油门上下 //duration3 = pulseIn(5, HIGH);//右油门上下 //duration4 = pulseIn(6, HIGH);//左油门左右 if(duration2>1350&&duration2<1550&&duration1>1350&&duration1<1550){ myse.runActionGroup(0,2); delay(2000); myse.stopActionGroup(); } if(duration2>1750){ myse.runActionGroup(1,0); } if(duration2<1200&&duration2>800){ myse.runActionGroup(2,0); } ////////////////////////// /*if(duration4>1750){ myse.runActionGroup(4,0); } if(duration4<1200&&duration4>800){ myse.runActionGroup(3,0); }*/ ////////////////////////// if(duration1>1750){ myse.runActionGroup(12,0); } if(duration1<1200&&duration1>800){ myse.runActionGroup(11,0); } } ////////////////////////
2.function(通信函数)
Stream
当我们使用ESP8266开发板或者Arduino开发板来开发项目时,可以使用基于Stream类的库来处理Stream数据。以下列表中的库都是基于Stream类所建立的。
库----类:
Serial----Serial
SoftwareSerial----SoftwareSerial
Ehternet----EthernetClient
ESP8266FS----File
SD----File
Wire----Wire
GSM----GSMClient
WifiClient----WifiClient
WiFiServer----WiFiServer
WiFiUDP-----WiFiUDP
WiFiClientSecure-----WiFiClientSecure
Serial//串行通信 ; 格式为 Serial.*****
Serial.begin(speed)
Serial.end() //终止串行通讯,让RX 和 TX引脚用于Arduino的输入(INPUT)或输出(OUTPUT)功能。
println()
同print()
都可加入\n代表换行,需要在“”中间加入
print()
//以人类可读的ASCII码形式向串口发送数据,该函数有多种格式。整数的每一数位将以ASCII码形式发送。浮点数同样以ASCII码形式发送,默认保留小数点后两位。字节型数据将以单个字符形式发送。字符和字符串会以其相应的形式发送。
例:Serial.print(78) 发送 “78” Serial.print(1.23456) 发送 “1.23” Serial.print(‘N’) 发送 “N” Serial.print(“Hello world.”) 发送 “Hello world.”
参数
val: 要发送的数据(任何数据类型)
format: 指定数字的数据形式或小数的位数(用于浮点数)。
语法:
Serial.println(val)
Serial.println(val, format)
此指令也可以通过附加参数来指定数据的格式。这个允许的值为:BIN (binary二进制), OCT (octal八进制), DEC (decimal十进制), HEX (hexadecimal十六进制)。对于浮点数,该参数可以指定小数点的位数。
例:Serial.print(78, BIN) 发送 “1001110” Serial.print(78, OCT) 发送 “116” Serial.print(78, DEC) 发送 “78” Serial.print(78, HEX) 发送 “4E” Serial.println(1.23456, 0) 发送 “1” Serial.println(1.23456, 2) 发送 “1.23” Serial.println(1.23456, 4) 发送 “1.2346”
你可以用F()把待发送的字符串包装到flash存储器。
例如: Serial.print(F(“Hello World”))
要发送单个字节数据,请使用Serial.write()。
ASCII字符代码表
SPI通信
SPI引脚通信
示例
#include <SPI.h> const int ssPin = 10; //设置SS引脚为数字引脚10 void setup() { // 初始化SPI库 SPI.begin(); // 设置SS引脚为输出模式 pinMode(ssPin, OUTPUT); } void loop() { // 设置SS引脚为低电平,使能SPI通信 digitalWrite(ssPin, LOW); // 发送数据 SPI.transfer(0x55); // 接收数据 uint8_t data = SPI.transfer(0x00); // 设置SS引脚为高电平,禁用SPI通信 digitalWrite(ssPin, HIGH); // 等待一段时间 delay(1000); }
Serial通信
TX,RX
Serial.read()
3.function(中断函数)
attachInterrupt()
设置中断
子主题
Arduino控制板 Uno, Nano, Mini
支持中断的引脚 2, 3
注意
在ISR(中断服务程序)函数中,delay()函数是不工作的,而且millis()函数返回值也不再增长。在ISR(中断服务程序)运行期间Arduino开发板接收到的串口数据也可能丢失。另外ISR函数里所使用的变量应声明为volatile类型。详情请见以下”关于ISR(中断服务程序)”部分。
语法
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);
参数 pin: 中断引脚号 ISR: 中断服务程序名 mode:中断模式 中断模式(mode)有以下几种形式: LOW: 当引脚为低电平时触发中断服务程序 CHANGE: 当引脚电平发生变化时触发中断服务程序 RISING: 当引脚电平由低电平变为高电平时触发中断服务程序 FALLING: 当引脚电平由高电平变为低电平时触发中断服务程序
返回值
无
示例
Arduino const byte ledPin = 13; //用2号引脚作为中断触发引脚 const byte interruptPin = 2; volatile byte state = LOW; void setup() { pinMode(ledPin, OUTPUT); //将中断触发引脚(2号引脚)设置为INPUT_PULLUP(输入上拉)模式 pinMode(interruptPin, INPUT_PULLUP); //设置中断触发程序 attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE); } void loop() { digitalWrite(ledPin, state); } //中断服务程序 void blink() { state = !state;
detachInterrupt()
取消中断
4.function(其他函数)
时间
millis()
说明
millis函数可以用来获取Arduino开机后运行的时间长度,该时间长度单位是毫秒,最长可记录接近50天左右的时间。如果超出记录时间上限,记录将从0重新开始。 注: 1秒 = 1000毫秒
子主题
语法参数
无
返回值
Arduino开机后运行的时间长度,此时间数值以毫秒为单位(返回值类型:无符号长整型)
注意
millis函数的返回值为无符号长整型数据, 如果将该数值与整型数据或其它数据类型进行运算,运行结果将产生错误。
示例
unsigned long time; void setup(){ Serial.begin(9600); } void loop(){ Serial.print("Arduino has been running this sketch for "); time = millis(); //输出程序运行时间 Serial.print(time); Serial.println(" milliseconds."); delay(1000); }
micros()
说明
micros函数可以用来获取Arduino开机后运行的时间长度,单位为微秒,最长可记录接近70分钟的时间。如果超出记录时间上限,记录将从0重新开始。 注: 1秒 = 1,000,000微秒 1毫秒 = 1000 微秒
参数
无
返还值
Arduino开机后运行的时间长度,此时间数值以微秒为单位(返回值类型:无符号长整型)
注意
delay()
毫秒暂停
ms: 暂停时间,该时间单位是毫秒( unsigned long型数据)
delay(ms)
delayMicroseconds()
微秒暂停
delayMicroseconds(us)
不会影响全局,不会造成全局代码延时
数学
minx()
mix(x,y)
x: 第一个数字(可以是任何数据类型) y: 第二个数字(可以是任何数据类型)
返回值:两个数字中较小的数值。
max()
max(x,y)
x: 第一个数字(可以是任何数据类型) y: 第二个数字(可以是任何数据类型)
返回值 两个数字中较大的数值。
abs()
取绝对值 abs(x)
注意
max()的括号中参数不能使用函数。 [c gutter=”0″] abs(a++); //a++不能在这里使用。否则会产生错误结果 abs(a); //要实现以上语句功能应该使用这种方法。 a++; //所有数学计算都应在abs()函数外进行。
constrain()
数值限制
将一个数值限制到某一区间
语法
constrain(x, a, b)
x: 被限制到某一区间的数值(可以是任何数据类型) a: 限制区间下限(可以是任何数据类型) b: 限制区间上限(可以是任何数据类型)
返回值
x: 如果x介于a与b之间,则返回x a: 如果x小于限制区间下限a,则返回a b: 如果x大于限制区间上限b,则返回b
map()
等比映射
map()可以用来将某一数值从一个区间等比映射到一个新的区间。
语法
[c gutter=”0″] map (x, in_min, in_max, out_min, out_max) [/c]
x: 要映射的值 in_min: 映射前区间最小值 in_max: 映射前区间最大值 out_min: 映射后区间最小值 out_max 映射后区间最大值
注意
map()不会将数值限制在映射区间内
示例
void setup() {} void loop() { int val = analogRead(0); val = map(val, 0, 1023, 0, 255); //将变量val数值从 0 - 1023 区间映射到 0 - 255区间 analogWrite(9, val); }
可用于pwm调整灯明与暗
pow()
指数函数
语法
float pow (float base, float exponent)
base: 底数(float型)
exponent:指数 (float型)
sqrt()
开方函数
语法
double sqrt (double x)
x: 被开方数
三角函数
sin()
正弦函数 - sin() 计算角度的正弦值
float sin (float rad)
rad: 角度 (浮点型数据)
cos()
计算角度的正弦值
float cos (float rad)
rad: 角度 (浮点型数据)
tan()
计算角度的正切值
float tan (float rad)
rad: 角度 (浮点型数据)
随机数
random()
说明
random函数可用来产生随机数。
语法
random(max) random(min, max)
min: 产生随机数的下限(包含此数值) max: 产生随机数的上限(不包含此数值)
返回值
在最小值(min)和最大值减一(max-1)之间的随机数值
注意
单独使用random()函数。每次程序运行所产生的随机数字都是同一系列数字。并非真实的随机数,而是所谓的伪随机数。如果希望每次程序运行时产生不同的随机数值。应配合使用randomseed()函数。具体操作请参见本站randomseed()函数的具体说明。
randomSeed()
语法
randomSeed(seedVal)
seedVal: 随机种子数值
说明
randomSeed()函数可用来产生随机种子。单独使用random()函数所产生的随机数,在每一次程序重新启动后,总是重复同一组随机数字。如果希望程序重新启动后产生的随机数值与上一次程序运行时的随机数不同,则需要使用randomSeed()函数。 在实际应用时,可以通过调用analogRead()函数读取一个空引脚,作为随机种子数值。具体操作,本页面后续示例程序将进行说明
示例
long randNumber; void setup(){ Serial.begin(9600); randomSeed(analogRead(A0)); //将引脚A0放空,每次程序启动时所读取的数值都是不同的。 //这么做可以产生真正的随机种子值,从而产生随机数值。 } void loop(){ randNumber = random(300); // 产生随机数 Serial.println(randNumber); delay(50); }
位与字节
lowByte()
说明
对变量取低字节
语法
lowByte(x)
x: 被取低字节的变量(可以是任何变量类型)
返回值;字节
highByte()
说明
对变量取高字节。如果该变量有两个以上的字节,则取第二低字节。
语法
highByte(x)
x:被取高字节的变量(可以是任何变量类型)
返回值;字节
bitRead()
从数值中读取bit(位)
语法
bitRead(x, n)
x: 被读取位的数值 n: 被读取的位置(右起第一位为0位,第二位为1,以此类推。)
返回值 bit(位)
bitWrite()
向数值中写入bit(位)
语法
bitWrite(x, n, b)
x: 被写入位的数值 n: 被写入的位置(右起第一位为0位,第二位为1,以此类推。) b: 所写入的位信息 (0 或 1)
返回值 无
bitSet()
将数值的某一位设置为1
语法
bitSet(x, n)
x: 被操作的数字变量 n: 被设置为1的位置(右起第一位为0位,第二位为1,以此类推。)
返回值 无
bitClear()
将数值的某一位清空为0
语法
bitClear(x, n)
x: 被操作的数字变量 n: 被清空为0的位置(右起第一位为0位,第二位为1,以此类推。)
返回值 无
bit()
转换为二级制字符
计算某位的数值(第零位是1,第一位是2,第二位是4…)---
语法
bit(n)
n: 计算的位置
返回值 该位的数值
注意
需要注意的是,bit()函数返回的是一个字符串对象,因此在使用返回值之前需要将其存储在一个字符串变量中。此外,如果你需要将二进制字符串转换为整数,可以使用parseInt()函数。
5.数值
数据类型
int
整型
long
长整型
unsigned long
无符号长整型
boolean
布尔型
char
字符型
word
字型
unsigned char
无符号字符型
byte
字节型
float
浮点型
从int到float计算(int value = 0 ; float voltage = 0.0 ;voltage = ( ( float )value )/1023 ;)
double
双精度浮点型
string
字符串型
array
数组
下面的方法都可以用来创建(声明)数组。
我们可以通过下面的形式将数组的所有元素初始化为 0: int a[10] = {0}; char c[10] = {0}; float f[10] = {0};
可以只给部分元素赋初值。当{ }中值的个数少于元素个数时,只给前面部分元素赋值。例如: int a[10]={12, 19, 22 , 993, 344};
当赋值的元素少于数组总体元素的时候,剩余的元素自动初始化为 0:对于short、int、long类型数据,就是整数0;对于char类型数据,就是字符 ‘\0’;对于float、double类型数据,就是小数0.0。
int Ints[6];
int P[] = {2, 4, 8, 3, 6};
int SensVals[6] = {2, 4, -8, 3, 2};
char message[6] = "hello";
实际案例
#define LED_PIN 2 // 假设我们使用数字引脚2 // 定义一个包含LED灯的数组 int ledPins[] = {LED_PIN, LED_PIN + 1, LED_PIN + 2}; int numOfLeds = sizeof(ledPins)/sizeof(int); // 计算数组中元素的数量 void setup() { for (int i = 0; i < numOfLeds; i++) { pinMode(ledPins[i], OUTPUT); // 设置每个LED灯为输出模式 } } void loop() { for (int i = 0; i < numOfLeds; i++) { digitalWrite(ledPins[i], HIGH); // 打开当前LED灯 delay(100); // 等待100毫秒 digitalWrite(ledPins[i], LOW); // 关闭当前LED灯 } }
这个例子展示了如何使用数组来存储多个LED灯的引脚号,并在setup()函数中设置这些引脚为输出模式。在loop()函数中,我们使用一个循环来依次打开和关闭每个LED灯。通过使用数组,我们可以更方便地管理和控制多个LED灯。 这个例子只是Arduino数组应用的一个方面。实际上,你可以使用数组来存储和操作任何类型的变量,例如传感器读数、按钮状态等。数组的使用可以使代码更加简洁、易于维护和扩展。
常量
HIGH/LOW
INPUT | OUTPUT| INPUT_PULLUP
true | false
整数常量
如10进制,2进制,8进制,16进制所有(整数常量是直接在程序中使用的数字,如123。整数常量默认为十进制,但可以加上特殊前缀表示为其他进制。)
浮点数常量
和整型常量类似,浮点常量可以使得代码更具可读性。浮点数可以用科学记数法表示。’E’和’e’都可以作为有效的指数标志。 例: n = .005;
转换
char()
将一个变量的类型变为char?字符型
byte()
将一个值转换为byte字节型数值。
int()
将一个值转换为int整数型。
word()
把一个值转换为word字型的值,或由两个字节行数据创建一个字符。
word(x) X:任何类型的值
word(h, l)// h:高阶(最左边)字节;l:低序(最右边)字节
long()
将一个值转换为long长整型数据类型。
float()
将一个值转换为float浮点型数值。
变量范围与限定符
变量作用域
全局变量
在Arduino的环境中,任何在函数外声明的变量,都是全局变量。例如在setup(),loop()等函数外声明的函数都是全局变量。
局部变量
static – 静态变量
static静态变量只对声明该变量的函数有效。静态变量和局部变量不同的是,局部变量在每次调用时都会被创建,在调用结束后被销毁。而静态变量在函数调用后仍然保持着原来的数据。
静态变量只会在函数第一次调用的时候被创建和初始化。
volatile-易变变量
当一个变量的控制和改变可能来自程序代码以外的环境,比如在使用Arduino的中断功能时。ISR(中断服务程序)中所涉及的变量需要被声明为volatile易变变量。
const-常量
const关键字定义的常量与其他变量遵守相同的规则。由于使用定义常量具有缺陷 ,所以用 const 关键字定义常量更加好一些。
您可以使用 const 或 #define 创建数字或字符串常量。但要创建arrays数组常量,只能使用const定义。
const关键字代表常量 (Constant)。它用于修改变量性质,使其变为只读状态。常量可以像任何相同类型的其他变量一样使用,但不能改变其数值。也就是说,常量的数值在建立时一旦确定以后,如果在后续的程序中尝试改变常量数值,那么程序编译时将会报错。
工具
sizeof()
定义:sizeof操作符返回一个变量的字节数,或者返回数组占用的字节数。
一般int为4字节,共计32位,4*8。其他可能有不同
语法:sizeof (variable)
variable: 任何变量类型或数组(如int,float,byte)
3.案例
Arduino驱动OLED屏幕
接口定义
OLED0.96 IIC模块使用IIC通信接口,只需要接4根线就可以完成OLED屏数据通信 VCC:电源正极(接5V电源) GND:电源负极(接地) SCL:IIC时钟信号线 SDA:IIC数据信号线
子主题
VCC接到开发板的5V电源引脚上,GND接到开发板GND引脚上,SCL和SDA需要根据不同的开发板引脚定义来接线
nano
连接电源:首先将OLED屏幕连接到电源,然后通过USB连接线将电源连接到Nano。 连接I2C总线:Nano上有两个可用于连接I2C总线的引脚,分别是A4和A5。将OLED屏幕的SDA连接到Nano的A4引脚,SCL连接到Nano的A5引脚。 连接电源和地线:将OLED屏幕的VCC连接到Nano的5V引脚,将GND连接到Nano的GND引脚。 连接数据线:如果使用外部电源为OLED屏幕供电,则需要将外部电源连接到OLED屏幕的VCC和GND引脚。如果使用Nano的USB接口为OLED屏幕供电,则不需要连接额外的电源线。
需要的库文件
Adafruit_SSD1306库
Adafruit-GFX-Library
示例
/********************************************************************** 程序名称/Program name : words_display 团队/Team : 太极创客团队 / Taichi-Maker (www.taichi-maker.com) 作者/Author : Dapenson 日期/Date(YYYYMMDD) : 2020/07/01 程序目的/Purpose : 使用OLED0.96 IIC 12864显示文字 ----------------------------------------------------------------------- 修订历史/Revision History 日期/Date 作者/Author 参考号/Ref 修订说明/Revision Description ----------------------------------------------------------------------- 其它说明: ***********************************************************************/ // 引入IIC通讯所需的Wire库文件 // 教程参考http://www.taichi-maker.com/homepage/reference-index/arduino-library-index/wire-library/ #include <Wire.h> // 引入驱动OLED0.96所需的库 #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 // 设置OLED宽度,单位:像素 #define SCREEN_HEIGHT 64 // 设置OLED高度,单位:像素 // 自定义重置引脚,虽然教程未使用,但却是Adafruit_SSD1306库文件所必需的 #define OLED_RESET 4 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { // 初始化Wire库 // Wire.begin(); // 初始化OLED并设置其IIC地址为 0x3C display.begin(SSD1306_SWITCHCAPVCC, 0x3C); } void loop() { words_display(); display.display(); } void words_display() { // 清除屏幕 display.clearDisplay(); // 设置字体颜色,白色可见 display.setTextColor(WHITE); //设置字体大小 display.setTextSize(1.5); //设置光标位置 display.setCursor(0, 0); display.print("TaichiMaker"); display.setCursor(0, 20); display.print("time: "); //打印自开发板重置以来的秒数: display.print(millis() / 1000); display.print(" s"); display.setCursor(0, 40); display.print("Author: "); display.print("Dapenson"); }
示例(显示活动电量)
要在Arduino OLED上动态显示电池电量图形,您可以按照以下步骤进行操作: 首先,您需要将OLED显示屏与Arduino连接起来。根据您使用的OLED显示屏的型号和连接方式,将相应的引脚连接到Arduino的数字引脚上。 在Arduino IDE中,您需要加载适当的OLED库文件以与显示屏进行通信。确保您已经正确安装了所需的库文件,并且可以在代码中使用相关的函数和方法。 在代码中,您需要使用OLED显示屏的函数和方法来绘制电量图形。这可以通过以下步骤完成: a. 使用OLED的函数来初始化显示屏,设置坐标和颜色等参数。 b. 使用OLED的函数和方法来绘制电池电量图形。您可以根据需要绘制一个表示电量的条形图或数字显示。 c. 在代码中使用读取电池电压的函数,获取实时的电池电压值。 d. 根据电池电压值计算电池电量百分比,并根据需要更新绘制的电量图形。 e. 使用OLED的刷新函数或方法将更改的内容刷新到显示屏上。 下面是一个示例代码,可以帮助您更好地理解这个过程
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // 初始化OLED显示屏 display.display(); // 刷新显示屏 } void loop() { float V_BAT = analogRead(A0); // 读取电池电压值 int val = map(V_BAT, 0, 5.104, 0, 100); // 计算电池电量百分比 int pixelnum = V_BAT / 0.33; // 计算电池能级划分 display.clearDisplay(); // 清空显示屏 display.setCursor(0, 0); // 设置光标位置 display.print("V_Bat:"); // 显示端口电压 display.setCursor(45, 0); // 设置光标位置 display.print(V_BAT, 3); // 显示电池电压值 display.drawRFrame(110, 1, 17, 11, 3); // 绘制空心圆角矩形 display.setCursor(100, 11); // 设置光标位置 display.print("%"); // 显示电量百分比 display.setCursor(85, 11); // 设置光标位置 display.print(val); // 显示电量百分比值 if (pixelnum < 1) { // 判断能级划分是否小于1 pixelnum = 0; // 能级为0 } else if (pixelnum > 14) { // 判断能级划分是否大于14 pixelnum = 15; // 能级为15 } else { // 其他情况,绘制空心圆角矩形表示能级划分 for (int i = 0; i < pixelnum; i++) { display.drawVLine(126 - i, 1, 11); // 在显示屏上绘制垂直线,表示能级划分 } } display.display(); // 刷新显示屏 delay(1000); // 延时1秒 }
库文件及对应案例
库文件定义
内置
可不引入头文件
外置
主库文件
#include<>
其他库文件
#include""
内容
中断
TaskScheduler.h
显示
U8g2lib.h
Adafruit_SSD1306.h
驱动OLED0.96
Adafruit_GFX.h
驱动OLED0.96
Wrie.h
IIC通信库
时钟
DS1302.h
时钟模块DS1302
通信
SBUS通信
sbus.h
遥控器SUBS通信信号转换
PWM通信
pwm.h
遥控器pwm信号转换
TX,RX通信
Serial.h
内置通信模式
SPI通信
SPI.h
内置通信模式
制作库文件
C++
1.编写头文件*.h
预命令处理
#include
#define
条件编译
查找yyy是否在其他位置被define定义过,为了防止重复包含某文件
#ifndef yyy
#define yyy
。。。。
#endif
版本兼容
#if
#else
#endif
类声明
public
private
2.编写*.cpp文件
注意
必须要用到的头文件(含自身头文件)
使用类声明以外的定义成员函数时,需要使用域操作符“::”来说明
使用类声
3.keywords.txt
高亮
类型
KEYWORD1
数据型高亮
KEYWORD2
函数型高亮
LITERAL1
常量型高亮
注意
两个keyword之间空格用Tab输入
4.本人项目
蜘蛛机器人
存在问题
显示时间不对
换DS3231模块,且烧录两次,第二次注释时间,完成显示时间不刷新
显示器外壳修改及打印
测试中
机器人脚上的灯怎么连
加PWM转电压或电压比较器模块,再接LED
方案不行
测试总线舵机有没有电压
有电压,为电池电压
能否供电待后续测试
制作LED板
测试是否可以使用电压供电
完成测试有电压且可供电点亮LED
加LED及200-400Ω电阻
机器人脚部加缓冲模块
修改结构
购买缓冲器
语音模块附加
指针和中断怎么用
用天地飞ET16S控制
已知信息
pwm980-1980,sbus控制电,输入及输出,波特率115200
取反硬件电路制作
总线舵机控制板无法输出pwm信号
使用mega测试是否能收到SBUS信号并反馈出来
具体内容?
输出pwm信号
使用双单片机控制
如何使用双nano进行运算
一个从nano连接OLED电压显示,灯,波箱,接收机,激光发射器
获取SBUS信号
控制信号波箱主动控制
控制信号控制激光
其他控制信号发至主机执行
获取OLED信息并显示
灯信号自主控制
一个主nano连接时钟,灯,总线舵机控制板,
获取时钟,及电压信号发送至从机执行
接收从机有关控制信号来控制总线舵机控制板
灯信号自主控制
2个nano用TX RX相连或者SPI相连
使用mega和nano进行控制
控制内容
mega控制总线控制板,OLED,灯,波箱,接收机,激光发射器,时钟
nano控制下层灯
通信及连接
mega和总线舵机控制板相连
mega和nano相连,根据动作控制灯
波箱和下层供电相连
测试使用软串口与总线舵机控制板进行通信
使用电压读取代码测试
建立2个发射机构的上体结构
发射机构和主体连接旋转
使用N20减速电机齿轮带动大内齿轮旋转
使用电滑环防止线圈扭转
建模
增加船型开关功能
总线舵机控制板
制作线路(xt60头)
波枪
平头哥图传
交流电测试报警器
履带机器人(带机械臂)
机械结构
机械臂
阿奇3D打印结构
如需修改fusion360
备注
A舵机90°-底座舵机90°
B舵机0度-控制主臂
C舵机180°-控制中臂
D舵机0°-控制夹爪
履带控制机构
蒙古野手设计修改
fusion360
控制程序(arduino)
机械臂
测试单个舵机
测试多个舵机
履带控制机构
遥控程序
遥控程序控制机械臂
遥控及单片机控制逻辑
子页面2
stm32
vscode IDE
环境变量搭建
arduino?
C++/C
C语言
二、算法
1.定义:解决问题而采取的方法
类别
数值运算算法
非数值运算算法
流程图
2.基本结构
结构类型
顺序结构
选择结构
循环结构
while
先判断再执行
until
先执行再判断
特点
只有1个入口及1个出口
结构每个部分都有机会会被执行到
结构不存在死循环
三、顺序程序设计
C++
图形化编程
mixly(米思奇)
Mblock
单片机连接算法
ISP协议
IIC协议
制备PCB
PCB设计
明确电路
选核心
画电路
PCB打样
嘉立创打样
?
自制
算法
机械行走步态
2足机器人
4足机器人
6足机器人
三角步态(每次3足)
波动步态(每次单足)
涟漪步态(每次2足)
游动机器人
履带机器人
?
控制
2.4G无线遥控
蓝牙
手机app
DevEco studio(鸿蒙)
安卓
苹果
手势控制
?
机械步态算法
?
机械
电机
舵机
减速电机
电动机
传动
传动模块(增加扭矩)
发射机构
?
?
continue和return都可以用于终止循环,但continue用于结束当前循环的执行并跳到下一个迭代,而return用于结束当前函数的执行并返回一个值。
不要混淆布尔与运算符,&&(两个&符号)和按位与运算符&(单个&符号)。它们是完全不同的概念。 同样,不要混淆布尔或运算符||(双竖杠)和按位或运算符|(单竖杠)。 按位取反 ~(波浪号)看起来与布尔非 ! 有很大不同,必须确保在什么地方用哪一个。
在Arduino中,define和include都是预处理指令,但它们有不同的作用和用途。 #define指令: #define是宏定义命令,用于将一个标识符(宏名)定义为特定的字符串或数字。 在编译时,预处理器会将所有的标识符替换为指定的值。 示例: c`#define PI 3.14159 #define SIZE 10` 在上述示例中,PI被定义为3.14159,SIZE被定义为10。 #include指令: #include是文件包含指令,用于将指定的文件包含进当前源文件。 当预处理器遇到#include指令时,它会将指定的文件内容插入到#include指令的位置。 示例: c`#include <stdio.h> #include "myfile.h"` 在上述示例中,stdio.h文件和myfile.h文件的内容将被包含到当前源文件中。 总结: #define是宏定义命令,用于在编译时进行简单的文本替换。 #include是文件包含指令,用于将其他文件的内容插入到当前源文件中。
浮动主题
在viod setup()前为全局变量,在setup()或loop()里面为局部变量
注意C++中大小写不能通用
RC遥控模型
电源(根据电池确定电压)
舵机
电机
电调(根据电压确定型号)
接收机
普通PWM信号
优点:直接通过pwm信号引脚输出(6,9等,各种单片机不一样)
缺点:线路较多
SBUS信号
缺点:需要增加外界硬件反相器(CD4069-6相反器芯片),且需要信号解析
优点:只需要一组线路,GND,VCC,S三根线就行
单片机(芯片)
9-12v Arduino Mega
5v Arduino Uno
单片机控制模型
电源(根据电池确定电压)
舵机
接收机(类芯片)
电机
电调(根据电压确定型号)