arduino 编程分享

2018-11-12 11:20:46 ufryyfdf 阅读数 32581

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

淘宝店铺:http://robotstudio.taobao.com 群号: 259189548 

Arduino语言注解Arduino语言是建立在C/C++基础上的,其实也就是基础的C语言,Arduino语言只不过把AVR单片机(微控制器)相关的一些参数设置都函数化,不用我们去了解他的底层,让我们不了解AVR单片机(微控制器)的朋友也能轻松上手。
       在与Arduino DIYER接触的这段时间里,发现有些朋友对Arduino语言还是比较难入手,那么这里我就简单的注释一下Arduino语言(本人也是半罐子水,有错的地方还请各位指正)。

 

关键字:
if
if...else
for
switch case
while
do... while
break
continue
return
goto
语法符号:
;
{}
//

运算符:
=
+
-
*
/
%
==
!=
<
>
<=
>=
&&
||
!
++
--
+=
-=
*=
/=
数据类型:
boolean  布尔类型
char
byte  字节类型
int
unsigned int
long
unsigned long
float
double
string
array
void
数据类型转换:
char()
byte()
int()
long()
float()
常量:
HIGH | LOW     表示数字IO口的电平,HIGH 表示高电平(1),LOW 表示低电平(0)。
INPUT | OUTPUT   表示数字IO口的方向,INPUT 表示输入(高阻态),OUTPUT  表示输出(AVR能提供5V电压 40mA电流)。
true | false    true 表示真(1),false表示假(0)。

       以上为基础c语言的关键字和符号,有c语言基础的都应该了解其含义,这里也不作过多的解释。

 

结构
void setup()   初始化变量,管脚模式,调用库函数等
void loop()  连续执行函数内的语句
功能
数字 I/O

pinMode(pin, mode)    数字IO口输入输出模式定义函数,pin表示为0~13, mode表示为INPUT或OUTPUT。
digitalWrite(pin, value)    数字IO口输出电平定义函数,pin表示为0~13,value表示为HIGH或LOW。比如定义HIGH可以驱动LED。
int digitalRead(pin)    数字IO口读输入电平函数,pin表示为0~13,value表示为HIGH或LOW。比如可以读数字传感器。
模拟 I/O

int analogRead(pin)    模拟IO口读函数,pin表示为0~5(Arduino Diecimila为0~5,Arduino nano为0~7)。比如可以读模拟传感器(10位AD,0~5V表示为0~1023)。
analogWrite(pin, value) - PWM     数字IO口PWM输出函数,Arduino数字IO口标注了PWM的IO口可使用该函数,pin表示3, 5, 6, 9, 10, 11,value表示为0~255。比如可用于电机PWM调速或音乐播放。
扩展 I/O

shiftOut(dataPin, clockPin, bitOrder, value)    SPI外部IO扩展函数,通常使用带SPI接口的74HC595做8个IO扩展,dataPin为数据口,clockPin为时钟口,bitOrder为数据传输方向(MSBFIRST高位在前,LSBFIRST低位在前),value表示所要传送的数据(0~255),另外还需要一个IO口做74HC595的使能控制。
unsigned long pulseIn(pin, value)    脉冲长度记录函数,返回时间参数(us),pin表示为0~13,value为HIGH或LOW。比如value为HIGH,那么当pin输入为高电平时,开始计时,当pin输入为低电平时,停止计时,然后返回该时间。
时间函数

unsigned long millis()   返回时间函数(单位ms),该函数是指,当程序运行就开始计时并返回记录的参数,该参数溢出大概需要50天时间。
delay(ms)    延时函数(单位ms)。
delayMicroseconds(us)    延时函数(单位us)。
数学函数

min(x, y)  求最小值
max(x, y)  求最大值
abs(x)   计算绝对值
constrain(x, a, b)  约束函数,下限a,上限b,x必须在ab之间才能返回。
map(value, fromLow, fromHigh, toLow, toHigh)    约束函数,value必须在fromLow与toLow之间和fromHigh与toHigh之间。
pow(base, exponent)  开方函数,base的exponent次方。
sq(x)     平方
sqrt(x)   开根号
三角函数

sin(rad)
cos(rad)
tan(rad)
随机数函数

randomSeed(seed)   随机数端口定义函数,seed表示读模拟口analogRead(pin)函数 。
long random(max)   随机数函数,返回数据大于等于0,小于max。
long random(min, max)   随机数函数,返回数据大于等于min,小于max。
外部中断函数

attachInterrupt(interrupt, , mode)     外部中断只能用到数字IO口2和3,interrupt表示中断口初始0或1,表示一个功能函数,mode:LOW低电平中断,CHANGE有变化就中断,RISING上升沿中断,FALLING 下降沿中断。
detachInterrupt(interrupt)    中断开关,interrupt=1 开,interrupt=0 关。
中断使能函数

interrupts()  使能中断
noInterrupts()  禁止中断
串口收发函数

Serial.begin(speed)  串口定义波特率函数,speed表示波特率,如9600,19200等。
int Serial.available()  判断缓冲器状态。
int Serial.read()   读串口并返回收到参数。
Serial.flush()    清空缓冲器。
Serial.print(data)  串口输出数据。
Serial.println(data)   串口输出数据并带回车符。

 

官方库文件
EEPROM - EEPROM读写程序库
Ethernet - 以太网控制器程序库
LiquidCrystal - LCD控制程序库
Servo - 舵机控制程序库
SoftwareSerial - 任何数字IO口模拟串口程序库
Stepper - 步进电机控制程序库
Wire - TWI/I2C总线程序库
Matrix - LED矩阵控制程序库
Sprite - LED矩阵图象处理控制程序库
非官方库文件
DateTime - a library for keeping track of the current date and time in software.
Debounce - for reading noisy digital inputs (e.g. from buttons)
Firmata - for communicating with applications on the computer using a standard serial protocol.
GLCD - graphics routines for LCD based on the KS0108 or equivalent chipset.
LCD - control LCDs (using 8 data lines)
LCD 4 Bit - control LCDs (using 4 data lines)
LedControl - for controlling LED matrices or seven-segment displays with a MAX7221 or MAX7219.
LedControl - an alternative to the Matrix library for driving multiple LEDs with Maxim chips.
Messenger - for processing text-based messages from the computer
Metro - help you time actions at regular intervals
MsTimer2 - uses the timer 2 interrupt to trigger an action every N milliseconds.
OneWire - control devices (from Dallas Semiconductor) that use the One Wire protocol.
PS2Keyboard - read characters from a PS2 keyboard.
Servo - provides software support for Servo motors on any pins.
Servotimer1 - provides hardware support for Servo motors on pins 9 and 10
Simple Message System - send messages between Arduino and the computer
SSerial2Mobile - send text messages or emails using a cell phone (via AT commands over software serial)
TextString - handle strings
TLC5940 - 16 channel 12 bit PWM controller.
X10 - Sending X10 signals over AC power lines

以上库文件都需要下载到编译环境(如下目录:arduino-0011\hardware\libraries)中才能使用。

 

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述
2019-06-19 13:47:50 dpjcn1990 阅读数 19308

授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。

共同学习成长QQ群 622368884,不喜勿加,里面有一大群志同道合的探路人

快速导航
单片机菜鸟的博客快速索引(快速找到你要的)

如果觉得有用,麻烦点赞收藏,您的支持是博主创作的动力。

    笔者本书的主题是基于Arduino平台来开发ESP8266。那么从另外一个角度来看待这句话,可以理解为:把ESP8266当作一款类似于Arduino UNO型号(为什么不是Mega2560呢?可以从硬件资源方向考虑)的Arduino开发板,用Arduino平台的开发方式来开发项目,只不过ESP8266是在Arduino UNO的基础上加了网络功能
    本章将介绍ESP8266作为Arduino UNO开发板的一些重要开发知识点。
    主要分为8个部分:
    1.ESP8266 Arduino程序结构
    2.计时和延时(Timing and delays)
    3.NodeMcu 端口映射
    4.数字IO(Digital IO)
    5.中断功能
    6.模拟输入(ADC)
    7.模拟输出(PWM)
    8.串口通信(Serial)

1. Arduino程序结构

    在第2章中,笔者提供了一个测试用例,让我们来回顾一下,代码如下:

/**
 * Demo:
 *    测试ESP8266 demo
 *    打印ESP8266模块信息
 *    1.打印Arduino Core For ESP8266 版本,笔者是2.4.2版本
 *    2.打印Flash的唯一性芯片id(读者可以思考一下是否可以用来做点什么唯一性参考)
 *    3.打印Flash实际大小
 *    4.打印IDE配置的使用Flash大小
 *    5.打印IDE配置的Flash连接通信的频率
 *    6.打印Flash连接模式:QIO QOUT DIO DOUT,可以理解为Flash传输速率
 * @author 单片机菜鸟
 * @date 2018/10/22
 */
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  //使能软件看门狗的触发间隔
  ESP.wdtEnable(5000);
}

void loop() {
  //喂狗
  ESP.wdtFeed();
  FlashMode_t ideMode = ESP.getFlashChipMode();
  String coreVersion = ESP.getCoreVersion();
  Serial.print(F("Arduino Core For ESP8266 Version: "));
  Serial.println(coreVersion);
  Serial.printf("Flash real id(唯一标识符):   %08X\n", ESP.getFlashChipId());
  Serial.printf("Flash 实际大小: %u KBytes\n", ESP.getFlashChipRealSize()/1024);
  Serial.printf("IDE配置Flash大小: %u KBytes,往往小于实际大小\n", ESP.getFlashChipSize()/1024);
  Serial.printf("IDE配置Flash频率 : %u MHz\n", ESP.getFlashChipSpeed()/1000000);
  Serial.printf("Flash ide mode:  %s\n\n", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT" : ideMode == FM_DIO ? "DIO" : ideMode == FM_DOUT ? "DOUT" : "UNKNOWN"));
  
  delay(1000);
}

    去掉代码细节,会得到类似于Arduino编程的代码结构:

/**
 * ESP8266 Arduino程序结构
 * @author 单片机菜鸟
 * @date 2018/10/24
 */
void setup() {
  // 这里开始写初始化代码,只会执行一次
  
}

void loop() {
  //这里写运行代码,重复执行
}

    对于习惯c语言编程的读者,以上代码又可以抽象成以下伪代码结构:

/**
 * ESP8266 Arduino程序伪代码结构
 * @author 单片机菜鸟
 * @date 2018/10/24
 */
void main(){
  watchdogEnable();//启动看门狗
  setup();//初始化函数
  while(1){
    loop();//业务代码函数
  }
}

代码解析

1.在ESP8266 Arduino编程中,默认会开启看门狗功能,也就是对应伪代码的watchdogEnable(),意味着我们需要适当喂狗,不然会触发看门狗复位;
2.setup()方法:初始化函数,只会运行一次,所以一般情况下,我们都会在这里配置好初始化参数,比如IO口模式、串口波特率设置等等;
3.loop()方法:不断重复执行,这里编写我们的业务代码,同时要注意执行喂狗操作。

2. 计时和延时(Timing and delays)

    时间控制,基本上可以说存在于每一个项目代码中。目前在Arduino中跟时间控制有关的方法包括以下几个:
    delay(ms)
    暂停一个给定的毫秒数的时间间隔。
    delayMicroseconds(us)
    暂停一个给定的微秒数的时间间隔。
    millis()
    返回重启(reset)后所经过的毫秒数。
    micros()
    返回重启(reset)后所经过的微秒数

温馨提示

    通常,我们控制LED灯闪烁都会加上一个delay延时来达到切换亮灭时间长度。但是delay有个缺点就是:在给定的时间间隔内是不能做其他操作,这样对于一些需要响应按键操作的场景就不适用了。那么有没有什么办法既能延时又能不影响其他操作呢?当然,这就是millis()的妙用,通过获取两个时间点的毫秒数,然后计算它们的差值,差值时间间隔内是可以执行其他操作的。代码片段如下:

long debouncdDelay = 60;//延时间隔
long lastDebounceTime = 0; //最近记录的一次时间

// 判断时间间隔是否大于设定的时间间隔。
if(millis()-lastDebounceTime>debouncdDelay){
    lastDebounceTime = millis();
}

3. NodeMcu 端口映射

    在前面,笔者有说到,本书的实验案例是基于NodeMcu这块ESP8266开发板来进行的,其中NodeMcu的核心芯片是ESP8266-12F。要想知道ESP8266-12F给我们提供了什么功能模块,首先了解一下它有什么引脚端口以及NodeMcu与它之间的引脚端口映射关系。

3.1 ESP8266-12F

    首先,认识一下ESP8266-12F的引脚定义,通常会隐藏pin6-pin11,如下图:
image
    当然,笔者也会提供完整的引脚图以便对比,如下图:
image
    分析引脚图,可以得出几个结论:
    1.ESP8266-12F总共有22个引脚,对应了第1章选型表的SMD-22封装工艺,同时有GPIO0-GPIO16共17个通用IO口,但是得注意有些IO口还可以完成其他功能(也叫做引脚复用),诸如Serial、I2C、SPI,由相应的函数库完成;
    2.ESP8266具有一个可用的单通道ADC;
    3.GPIO6-GPIO11(复用引脚CS、MISO、MOSI、SCK)用于连接外部flash,对用户不可用,试图使用这些引脚作为IO将会导致程序奔溃;
    4.支持SPI总线通信,对应引脚为GPIO12-GPIO15;
    5.支持I2C总线,对应引脚为GPIO4-GPIO5;
    6.支持串口通信Serial、Serial1,默认对应引脚GPIO1-GPIO3;

3.2 NodeMcu

    接下来,先了解一下NoodeMcu的实物图,如下图:

image

    同时,读者也需要知道ESP8266-12F与NodeMcu的端口映射关系,如下图:
image

    可以看出:
    1.中间的DEVKIT部分,就是NodeMcu提供给外界的端口,对应实物图上标注的端口名称;
    2.除开中间部分,其他部分基本上对应ESP8266引脚,以不同颜色块来区分不同功能;

温馨提示

    NodeMcu上的CLK、SD0、CMD、SD1、SD2引脚,是用于连接外接flash芯片,不应该用于连接其他模块,悬空即可,以防程序奔溃。

    或许笔者会觉得看图有点复杂,所以笔者总结了下面的GPIO引脚映射表,以供参考:

NodeMCU的引脚名称 ESP8266内部GPIO引脚号 可复用功能 备注
D0 GPIO16 可用,只能用作GPIO读/写,不支持特殊功能
D1 GPIO5 I2C总线的SCL 可用
D2 GPIO4 I2C总线的SDA 可用
D3 GPIO0 不可用,烧录固件或者运行模式控制端口
D4 GPIO2 Serial1的TX Serial1没有RX
D5 GPIO14 SPI总线的SCLK 可用
D6 GPIO12 SPI总线的MISO 可用
D7 GPIO13 SPI总线的MOSI、Serial的RX 可用
D8 GPIO15 SPI总线的CS、Serial的TX 可用
D9 GPIO3 Serial的RX 可用
D10 GPIO1 Serial的TX 可用
SD2 GPIO9 尽量不用
SD3 GPIO10 尽量不用

    从上面表格可以看出,我们大约11个GPIO引脚可用。而11个中的2个引脚通常被保留用于RX和TX,以便进行串口通信。因此最后,只剩下8个通用I / O引脚,即D0到D8(除开D3特殊用途)。

温馨提示

    请注意,D0 / GPIO16引脚只能用作GPIO读/写,不支持特殊功能。

4. 数字IO(Digital IO)

    上面说到,ESP8266-12F(也可以大胆说ESP8266-12系列)最终只剩下8个通用的I/O引脚以供我们使用,即是NodeMcu上的D0-D8(除D3之外)。
    Arduino中的引脚号直接与ESP8266 GPIO的引脚号对应通信。pinMode/digitalRead/digitalWrite函数不变,所以要读取GPIO2,可调用digitalRead(2)。除了D0可以设置为INPUT(输入)、OUTPUT(输出)或者INPUT_PULLDOWN(输入,默认下拉,也就是低电平),剩余的数字IO引脚可以设置为INPUT(输入)、OUTPUT(输出)或者INPUT_PULLUP(输入,默认上拉,也就是高电平)。
    下面,将在NodeMcu的D1引脚上写一个LED Blink的Arduino草图:

/**
 * LED灯闪烁实验
 */
void setup() {
    pinMode(D1, OUTPUT);   // 初始化D1引脚为输出引脚
}
 
void loop() {
    digitalWrite(D1, LOW); // 亮灯
    delay(1000); // 延时1s
    digitalWrite(D1, HIGH);// 灭灯
    delay(1000); // 延时1s
}

注意

    某些开发板和模块,仍将使用第9和第11引脚(如果闪存芯片工作于DIO模式,与默认的QIO模式相反),它们可用于IO。

5. 中断功能

    中断可以理解为在正常的运行流程中突然插入的操作,这就像你在忙于工作的时候,领导突然叫你去买个下午茶,然后你就去把下午茶买回来,再继续工作。基于ESP8266的NodeMcu的数字IO的中断功能是通过attachInterruptdetachInterrupt函数所支持的。除了D0/GPIO16,中断可以绑定到任意GPIO的引脚上。所支持的标准中断类型有:CHANGE(改变沿,电平从低到高或者从高到低)、RISING(上升沿,电平从低到高)、FALLING(下降沿,电平从高到低)。
    首先,我们来看看Arduino IDE中用于中断的函数。
    1.attachInterrupt()
    该功能用于在将指定引脚设置为响应中断。
    函数: attachInterrupt(pin, function, mode);
    参数:
        pin:要设置中断编号,注意,这里不是引脚编号。
        function:中断发生时运行的函数, 这个函数不带任何参数,不返回任何内容。
        Interrupt type/mode:它定义中断被触发的条件方式。
            CHANGE:改变沿,引脚电平从低变为高或者从高变为低时触发中断。
            RISING:上升沿,引脚电平从低变为高时触发中断。
            FALLING:下降沿,引脚电平从高变为低时触发中断。
    返回值: 无;

    2.detachInterrupt()
    该功能用于禁用指定GPIO引脚上的中断。
    函数: detachInterrupt(pin)
    参数:
        pin:要禁用的中断的GPIO引脚。
    返回值: 无;

    3.digitalPinToInterrupt()
    该功能用于获取指定GPIO引脚的中断号。
    函数: digitalPinToInterrupt(pin)
    参数:
        pin:要获取中断号的GPIO引脚。

例子
    将NodeMcu的D2引脚设置为上升沿中断。在D2上外接一个按键,按键通过电阻下拉到地。当发生中断的时候,我们在串口监视器上打印“Hello ESP8266”。

image

例子代码

/**
 * 功能描述:ESP8266中断演示
 */ 
void setup() {
 Serial.begin(115200);//设置串口波特率
 attachInterrupt(digitalPinToInterrupt(D2), InterruptFunc, RISING);//设置中断号、响应函数、触发方式
}
 
void loop() {
}

/**
 * 中断响应函数
 */ 
ICACHE_RAM_ATTR void InterruptFunc(){
 Serial.println("Hello ESP8266");
}

6. 模拟输入(ADC)

    学过模拟电路或者数字电路的人都会听过ADC,它又叫做模数转换器,用于将模拟信号转换成可视化的数字形式。ESP8266具有内置的10位ADC,只有一个ADC通道,即只有一个ADC输入引脚可读取来自外部器件的模拟电压。
    ESP8266上的ADC通道和芯片供电电压复用,也就是说我们可以将其设置为测量系统电压或者外部电压。

6.1 测量外部电压

    相关方法
        analogRead(A0),用于读取施加在模块的ADC引脚上的外部电压;
    输入电压范围
        0 - 1.0V之间;
    测量精度
        由于ADC具有10位分辨率,因此会给出0-1023的值范围;
    注意点
        为了支持外部电压范围(0-3.3v),NodeMcu做了一个电阻分压器,如图所示:

image

    例程
    编写一个读取NodeMcu的ADC引脚上的模拟电压。我们这里使用电位器在ADC引脚上提供0-3.3V的可变电压。如下图连接线:
image
    代码如下:

/**
 * 功能描述:ESP8266 ADC 读取外部电压
 * 在串口调试器查看效果
 */
void setup() {
  Serial.begin(115200);//配置波特率
}
 
void loop() {
  Serial.print("ADC Value: ");
  Serial.println(analogRead(A0));//输出0-1023 对应 外部输入电压 0-1.0v
  //延时1s
  delay(1000);
}

6.2 测量系统电压

    相关方法
        ESP.getVcc(),读取NodeMCU模块的VCC电压,单位是mV;
    注意点
        ADC引脚必须保持悬空;在读取VCC电源电压之前,应更改ADC模式以读取系统电压。
要ADC_MODE(mode)在#include行后面改变ADC模式。
模式是ADC_TOUT(对于外部电压),ADC_VCC(对于系统电压)。默认情况下,它读取外部电压。
    例程
    编写ESP8266读取系统电压,代码如下:

/**
 * 功能描述:ESP8266 ADC 读取系统电压
 * 在串口调试器查看效果
 */
ADC_MODE(ADC_VCC);//设置ADC模式为读取系统电压
 
void setup() {
  Serial.begin(115200);
}
 
void loop() {
  Serial.print("ESP8266当前系统电压(mV): ");
  Serial.println(ESP.getVcc());
  delay(1000);
}

7. 模拟输出(PWM)

    PWM(Pulse Width Modulation,脉宽调制),是在保持波的频率不变的同时改变脉宽的技术。当我们需要连续控制电压变化,实现呼吸灯或者电机转速的时候,就要用到PWM,如下图。
image
    首先,我们来理解一下占空比。一个脉冲周期由一个ON周期(VCCC)和一个OFF周期(GND)组成。一段时间内ON周期占据脉冲周期的比例就叫做占空比。

DutyCycle(percentage)=Ton/TotalPeriodX100

    例如,一个10ms的脉冲保持ON 2ms,那么根据公式,占空比是20%。

注意点

    脉冲频率一般都是固定的,跟占空比没有关系。

NodeMcu PWM引脚

    如下图,标注PWM引脚。

image

    基本上数字IO都可以作为PWM复用引脚,除了D0。不过需要注意的是,D3尽量不用,它内部连接ESP8266 GPIO0。

NodeMcu PWM有关Arduino函数

    1.analogWrite()
    该功能用于在指定的引脚上启用软件PWM。
    函数: analogWrite(pin,val)
    参数:
        pin:要启用软件PWM的GPIO引脚。
        val:数值,一般在0到PWMRANGE范围,默认PWMRANGE是1023。
    返回值: 无;
    注意点:
        analogWrite(pin, 0)用于禁用指定引脚上的PWM。
    2.analogWriteRange()
    该功能用于改变PWMRANGE数值。
    函数: analogWriteRange(new_range)
    参数:
        new_range:新的PWMRANGE数值。
    返回值: 无;
    注意点:
        可以理解为PWM精度范围。同样的PWM频率下,默认占空数值0-123。如果你改变PWMRANGE为2047,那么占空数值就变成0-2047。精度高了一倍。
    3.analogWriteFreq()
    该功能用于改变PWM频率。
    函数: analogWriteFreq(new_frequency)
    参数:
        new_frequency:新PWM频率,默认是1kHZ。
    返回值: 无;
    注意点:
        百度上很多资料都说PWM频率范围为1-1KHz。但是通过查看源码,如下:

static uint16_t analogFreq = 1000;

extern void __analogWriteFreq(uint32_t freq) {
  if (freq < 100) {
    analogFreq = 100;
  } else if (freq > 40000) {
    analogFreq = 40000;
  } else {
    analogFreq = freq;
  }
}

    可以看出,Arduino For ESP8266的PWM频率范围应该是100Hz-40KHz

PWM例程

    呼吸灯,LED灯明暗连续变化。代码如下:

/**
 * 功能描述:ESP8266 PWM演示例程
 * @author 单片机菜鸟
 * @date 2018/10/25
 */
 
#define PIN_LED D6
 
void setup() {
  // 这里开始写初始化代码,只会执行一次
  pinMode(PIN_LED,OUTPUT);
  analogWrite(PIN_LED,0);
}

void loop() {
  //这里写运行代码,重复执行
  for(int val=0;val<1024;val++){
     //占空比不断增大  亮度渐亮
	 analogWrite(PIN_LED,val);
	 delay(2);
  }
  
  for(int val=1023;val>=0;val--){
     //占空比不断变小  亮度渐暗
	 analogWrite(PIN_LED,1023);
	 delay(2);
  }
}

8. 串口通信(Serial)

    ESP8266的串口通信与传统的Arduino设备完全一样。除了硬件FIFO(128字节用于TX和RX)之外,硬件串口还有额外的256字节的TX和RX缓存。发送和接收全都由中断驱动。当FIFO/缓存满时,write函数会阻塞工程代码的执行,等待空闲空间。当FIFO/缓存空时,read函数也会阻塞工程代码的执行,等待串口数据进来。
    NodeMcu上有两组串口,Serial和Serial1。
    Serial使用UART0,默认对应引脚是GPIO1(TX)和GPIO3(RX)。在Serial.begin执行之后,调用Serial.swap()可以将Serial重新映射到GPIO15(TX)和GPIO13(RX)。再次调用Serial.swap()将Serial重新映射回GPIO1和GPIO3。不过,一般情况下,默认就好。

串口映射例程

/**
 * 功能描述:ESP8266 Serial映射例程
 * @author 单片机菜鸟
 * @date 2018/10/25
 */
 
void setup() {
  // 这里开始写初始化代码,只会执行一次
  Serial.begin(115200);
  Serial.println("GPIO1(TX),GPIO3(RX)");
  //调用映射方法
  Serial.swap();
  Serial.println("GPIO15(TX),GPIO13(RX)");
  //重新映射回来
  Serial.swap();
  Serial.println("GPIO1(TX),GPIO3(RX)");
}

void loop() {
  //这里写运行代码,重复执行
}

    Serial1使用UART1,默认对应引脚是GPIO2(TX)。Serial1不能用于接收数据,因为它的RX引脚被用于flash芯片连接。要使用Serial1,请调用Serial.begin(baudrate)。代码如下:

/**
 * 功能描述:ESP8266 串口例程
 * @author 单片机菜鸟
 * @date 2018/10/25
 */
 
void setup() {
  // 这里开始写初始化代码,只会执行一次
  Serial.begin(115200);
  Serial.println("Hello Serial");
  Serial1.begin(115200);
  Serial1.println("Hello Serial1");
}

void loop() {
  //这里写运行代码,重复执行
}

    如果不使用Serial1并且不映射串口,可以将UART0的TX映射到GPIO2,具体操作是:在Serial.begin()之后调用Serial.set_tx(2)或者直接调用Serial.begin(baud,config,mode,2)。
    默认情况下,当调用Serial.begin后,将禁用WiFi库的诊断输出。要想再次启动调试输出,请调用Serial.setDebugOutput(true)。要将调试输出映射到Serial1时,需要调用Serial1.setDebugOutput(true)。
    调用Serial.setRxBufferSize(size_t size)允许定义接收缓冲区的大小,默认值是256(缓冲区也是使用内存,意味着不能一味地去增大这个值)。
    Serial和Serial1对象都支持5,6,7,8个数据位,奇数(O)、偶数(E)和无(N)奇偶校验,以及1或者2个停止位。要设置所需的模式,请调用Serial.begin(baudrate, SERIAL_8N1), Serial.begin(baudrate, SERIAL_6E2)等。
    Serial和Serial1都实现了一种新方法用来获取当前的波特率设置。要获取当前的波特率,请调用Serial.baudRate(),Serial1.baudRate()。代码如下:

/**
 * 功能描述:ESP8266 串口波特率例程
 * @author 单片机菜鸟
 * @date 2018/10/25
 */
 
void setup() {
  // 这里开始写初始化代码,只会执行一次
  // 设置当前波特率为57600
  Serial.begin(57600);
  // 获取当前波特率
  int br = Serial.baudRate();
  // 将打印 "Serial is 57600 bps"
  Serial.printf("Serial is %d bps", br);
}

void loop() {
  //这里写运行代码,重复执行
}

    Serial和Serial1都属于硬件串口(HardwareSerial)的实例,如果读者需要使用ESP8266 软件串口的功能,请参考以下库:https://github.com/plerup/espsoftwareserial。
    为了检测进入Serial的未知波特率的数据,可以调用Serial.detectBaudrate(time_t timeoutMillis)。这个方法尝试在timeoutMillis ms的时间内检测波特率,检测成功返回波特率,检测失败返回0。detectBaudrate()方法在Serial.begin()被调用之前调用(因为它不需要用到接收缓冲区或者串口配置),并且它不能检测数据位位数或者停止位。这个检测过程不会去改变数据的波特率,所以可以在检测成功之后,调用Serial.begin(detectedBaudrate)。

串口用处

    一般来说,串口通信用在两个方面:
    1.与外围串口设备传输数据,比如蓝牙模块、Arduino等等;
    2.开发过程中用来调试代码,通过串口输出Debug信息了解程序运行信息。例程如下:

/**
 * Demo1:
 *    statin模式下,创建一个连接到可接入点(wifi热点),并且打印IP地址
 * @author 单片机菜鸟
 * @date 2019/09/02
 */
#include <ESP8266WiFi.h>
 
#define AP_SSID "xxxxx" //这里改成你的wifi名字
#define AP_PSW  "xxxxx"//这里改成你的wifi密码
//以下三个定义为调试定义
#define DebugBegin(baud_rate)    Serial.begin(baud_rate)
#define DebugPrintln(message)    Serial.println(message)
#define DebugPrint(message)    Serial.print(message)
 
void setup(){
  //设置串口波特率,以便打印信息
  DebugBegin(115200);
  //延时2s 为了演示效果
  delay(2000);
  DebugPrintln("Setup start");
  //启动STA模式,并连接到wifi网络
  WiFi.begin(AP_SSID, AP_PSW);
 
  DebugPrint(String("Connecting to ")+AP_SSID);
  //判断网络状态是否连接上,没连接上就延时500ms,并且打出一个点,模拟连接过程
  //笔者扩展:加入网络一直都连不上 是否可以做个判断,由你们自己实现
  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    DebugPrint(".");
  }
  DebugPrintln("");
 
  DebugPrint("Connected, IP address: ");
  //输出station IP地址,这里的IP地址由DHCP分配
  DebugPrintln(WiFi.localIP());
  DebugPrintln("Setup End");
}
 
void loop() {
}

9. 总结

    总体上讲,本章基础内容比较多,笔者介绍ESP8266在Arduino平台上的一些基础知识点,包括程序结构、NodeMcu端口映射、ESP8266 数字IO、PWM、ADC、串口通信等等。
本章目的很简单,就是为了告诉读者,ESP8266到底给我们提供了什么可利用硬件资源,以方便我们项目开发。

2018-11-23 17:46:08 bobo184 阅读数 13559

前言

时代在进步,极客们手上的单片机也从古老的51、PIC变成了AVR、STM32,也出现了各种便捷的开发工具,例如盛极一时的Arduino;不过Atmega328所属的AVR单片机,终究还是老点了,算起来差不多是20年前的主流了,现在流行的是ARM,今天我们就来玩下Arduino与STM32的结合。

经过半天的努力,终于可以让Arduino IDE支持我的32小板子了,STM32的芯片性价比普遍高于arduino里avr的单片机,所以好处就不用多说了。在这分享下这个有趣的玩法,将会使得STM32的编程更加简单,而不会像KEIL里那么的麻烦(不需了解底层的运作),使得新手更加容易上手,话不多说,走起。。。

板子+USB转TTL(CH340)+USB线

 但其实最经典也是最便宜的是下面这款,10块左右(淘宝)

 引脚定义为:

 好不容易找到一张图,感觉和uno一样功能一目了然,但不是很清晰(将就吧,下面会罗列出来)

软件下载

版本Arduino IDE1.8.7 https://www.arduino.cc/en/Main/Software

安装过程一路next就可以,但需要知道安装目录,默认C盘,本人安装在D盘。

安装完之后,界面非常简单。

 Arduino_STM32下载

然后去github上下载代码

Arduino_STM32:https://github.com/rogerclarkmelbourne/Arduino_STM32

 下载压缩文件并解压,并重命名复制到你的Arduino IDE的安装目录里的hardware下,

 现在你就可以在工具-开发板里找到STM32系列的板子了

 下载安装Arduino SAM开发板

接下来打开Arduino IDE然后选项栏工具-开发板-开发板管理器,下载安装Arduino SAM开发板,不安装这个开发板后编译会出现arm-none-eabi-g++错误(这个应该是交叉编译器);

 下载和烧写Bootloder

首先我们要下载Bootloader,进入刚刚那个人的另一个开源项目,地址如下:
https://github.com/rogerclarkmelbourne/STM32duino-bootloader/tree/master/binaries

选择你的STM32最小系统板的除电源指示灯外的另一个LED所对应的接口名字的文件下载(一定要选择对应的),我的板子为PC13,所以下载这个:

然后使用USB转串口线连接开发板和电脑USB,其中串口线的TX RX分布连接开发板PA10 PA9,然后将BOOT0短接3.3V BOOT1短接GND进入烧写状态,串口线的VCC GND分布连接开发板的5V 即可无需另外供电。

所需工具:flash_loader_demo_v2.8.0

ST官方下载地址:http://www.st.com/en/development-tools/flasher-stm32.html

下载有些费劲,不知道是网的问题还是电脑的问题,好不容易才下完,为避免麻烦,这里提供网盘下载:

https://pan.baidu.com/s/1SfAn2l6k6tWdIS_jWSzVsg

 选择USB串口线的串口号,注意需要boot0 为1,boot1 为0(默认全是0)

 

 

 

 在下列红色框内添加下载的bin文件,next,即可完成下载(bootloder的烧写)

 

 驱动安装

进入下面目录D:\Arduino\hardware\Arduino_STM32\drivers\win,以管理员身份运行install_drivers.bat和install_STM_COM_drivers.bat

 然后将开发板的串口线断开,将开发板BOOT0接GND后按住重置键不松,使用手机数据线连接开发板MicroUSB和电脑USB开始安装驱动,会发现一个新的USB串行设备,下面为COM11

 下载例程测试

将Arduino IDE的上传方式设置为STM32duino bootloader

端口号改成新的串口号

打开一个例程,LED闪烁的,例程里引脚为PB1,修改PC13(和板子对应,也可以不修改接一个LED即可)

 例程如下,然后编译上传:

 出现Done就代表上传成功了,

 然后就可以看到LED闪烁了,大功告成,喝杯水休息下吧。

 最后来看下一些常用功能。

1、PWM引脚

序号 物理引脚
0         PA0
 PA1
2 PA2
3 PA3
6 PA6
7 PA7
8 PA8
9 PA9
10 PA10
16 PB0
22 PB6
23 PB7

PWM引脚简易测试程序,总共有12个PWM引脚,16bit即65536级PWM,很精密了:

void setup()
{
        pinMode(PB0, PWM);
        pinMode(PA7, PWM);
        pinMode(PA6, PWM);
        pinMode(PA3, PWM);
        pinMode(PA2, PWM);
        pinMode(PA1, PWM);
        pinMode(PA0, PWM);
        pinMode(PB7, PWM);
        pinMode(PB6, PWM);
        pinMode(PA10, PWM);
        pinMode(PA9, PWM);
        pinMode(PA8, PWM);
}
void loop()
{
        for (int i = 0; i < 65536; i++)
        {
                delayMicroseconds(40);
                pwmWrite(PB0, i);
                pwmWrite(PA7, i);
                pwmWrite(PA6, i);
                pwmWrite(PA3, i);
                pwmWrite(PA2, i);
                pwmWrite(PA1, i);
                pwmWrite(PA0, i);
                pwmWrite(PB7, i);
                pwmWrite(PB6, i);
                pwmWrite(PA10, i);
                pwmWrite(PA9, i);
                pwmWrite(PA8, i);
        }
}

也可以用序号表示:

int pins[12] = {0, 1, 2, 3, 6, 7, 8, 9, 10, 16, 22, 23};
void setup()
{
  for (int i = 0; i < 12; i++)
  {
    pinMode(pins[i], PWM);
  }
}
void loop()
{
  for (int i = 0; i < 100; i++)
  {
    for (int j = 0; j < 12; j++)
    {
      pwmWrite(pins[j], i * i);
    }
    delay(20);
  }
}

2、ADC引脚

物理引脚 ADC通道
PA0       CH0
PA1 CH1
PA2  CH2
PA3 CH3
PA4 CH4
PA5 CH5
PA6 CH6
PA7 CH7
PB0 不能用序号表达
PB1 不能用序号表达

ADC测试程序:

void setup()
{
  Serial.begin(115200);
  pinMode(PB0, INPUT_ANALOG);
  pinMode(PA7, INPUT_ANALOG);
  pinMode(PA6, INPUT_ANALOG);
  pinMode(PA5, INPUT_ANALOG);
  pinMode(PA4, INPUT_ANALOG);
  pinMode(PA3, INPUT_ANALOG);
  pinMode(PA2, INPUT_ANALOG);
  pinMode(PA1, INPUT_ANALOG);
  pinMode(PA0, INPUT_ANALOG);
  pinMode(PB1, INPUT_ANALOG);
}
void loop()
{
  delay(50);
  Serial.print("\tPB0="); Serial.print(analogRead(PB0));
  Serial.print("\tPA7="); Serial.print(analogRead(PA7));
  Serial.print("\tPA6="); Serial.print(analogRead(PA6));
  Serial.print("\tPA5="); Serial.print(analogRead(PA5));
  Serial.print("\tPA4="); Serial.print(analogRead(PA4));
  Serial.print("\tPA3="); Serial.print(analogRead(PA3));
  Serial.print("\tPA2="); Serial.print(analogRead(PA2));
  Serial.print("\tPA1="); Serial.print(analogRead(PA1));
  Serial.print("\tPA0="); Serial.print(analogRead(PA0));
  Serial.print("\tPB1="); Serial.println(analogRead(PB1));
}

3、允许的串口

总共3个硬件外接串口Serial1、Serial2、Serial3,一个USB虚拟串口Serial:

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);  //TX=PA9,RX=PA10
  Serial2.begin(115200);  //TX=PA2,RX=PA3
  Serial3.begin(115200);  //TX=PB10,RX=PB11
}
void loop()
{
  delay(100);
  Serial.println("Test Serial");
  Serial1.println("Test Serial1");
  Serial2.println("Test Serial2");
  Serial3.println("Test Serial3");
}

还有I2C,SPI等,下载的Arduino_STM32里有相关的例程。

结语

STM32的主频72M,比atmega系列的快多了,而且AD多PWM多串口还多(总之强大太多了),我觉得如果它的库能像Arduino一样丰富,基本它就淘汰了,优胜劣汰,学无止境,奋斗吧!!!骚年!!!

2018-07-11 17:27:51 monkey2018 阅读数 25113

本聚合技术帖介绍了Arduino基础知识,并通过十几个有趣的项目介绍了Arduino软硬件设计,其中包含空气监测装置、智能小车、示波器、电子秤、智能手表、门禁系统及四轴飞行器等制作实例,把基础知识和实践项目结合起来,使你获得综合性的收获。

1、NANO~~东半球最小的自平衡机器人

之前做过一个小小的自平衡机器人蛋黄(有兴趣的同学可以自行百度),深受大家喜爱,各种求资料求教程求种子(误)的络绎不绝然而,并卵!现在看来,这家伙还是不够可爱!这次更狠了,有了3D打印机,造型什么的总算炫酷了起来还加了一推特技(超声波,摄像头,OLED,抬头舵机,蜂鸣器,红扑扑小脸蛋),蛋黄变蛋白的,光会卖萌怎么行,蛋黄2.0是目前最小功能最多的自平衡机器人。

http://www.cirmall.com/circuit/2022/details?1

 

2、手机目标跟踪寻迹小车+蓝牙SPP通信,附APP和Arduino源码

如何通过目标追踪功能制作一个手机摄像头实时跟踪小车,该设计运用OpenCV的图像处理APP,手机APP通过摄像头实时跟踪设定的颜色目标,并且通过手机蓝牙将目标坐标位置和大小等信息输出,配合蓝牙串口模块和我写的配套Arduino数据接收库,就可以做成一个简单的手机跟踪寻迹小车。

http://www.cirmall.com/circuit/6375/details?1

 

3、空气监测传感器+Arduino=空气监测装置,检测周边PM2.5刻不容缓

生活在魔都的朋友们是否都有此感慨:浓重的雾霾天气,一推开窗,让你鼓不起勇气迈步杀进那个仿佛时刻在摧毁你五脏六腑的外面的世界。对于此,利用一个空气监测传感器搭配Arduino控制器和扩展板,就搭建出一个简易空气监测装置,这样就可以时刻了解呼吸的空气质量了!

http://www.cirmall.com/circuit/2974/details?1

 

4、开源项目制作:基于Arduino平台的Mini四轴飞行器

能亲手制作一架比手掌还小、仅重30多克的Mini四轴飞行器,定会更有成就感吧。本文手把手教大家制作基于Arduino平台的开源四轴飞行控制系统(飞行控制系统,简称“飞控”)。

http://www.cirmall.com/circuit/3013/details?1

 

5、玩Arduino看过来!Arduino基础资料汇总

Arduino是当前世界上最为流行的开源硬件,在此借电路城平台分享Arduino基础知识及应用,途穿插简单项目制作,用于巩固知识与扩展提高。

http://www.cirmall.com/circuit/2937/details?1

 

6、Arduino WIFI智能小车 无线视频遥控小车

这是一款基于arduino的WIFI视频小车,小车上配有703N无线路由器和720P高清视屏摄像头,可以将行进过程中的实时画面通过自带的WIFI反馈到你的手机或者是电脑平板上。用户只需要在相应的设备上安装好软件,即可通过上位机软件来控制小车的前、后、左、右运动。

http://www.cirmall.com/circuit/1294/details?1

 

7、开源全球首款高采样率便携示波器电路+源代码

SmartScope 是世界上第一款采样率可达 100MS /s 的开源示波器,它适用于 iPad、 Android 和 PC 甚至 iPhone 等设备,对于任何从事硬件开发的 Arduino 和树莓派 Raspberry Pi 开发者,SmartScope 应该是一款不错的必备品。SmartScope 支持跨平台作业,它能够兼容 iOS、安卓、Windows、Linux 和 OS X 等系统。重要的是,SmartScope 的这种跨平台兼容性是通过架构在一开始 UI 开发的 Xamarin 框架来实现的,因此,SmartScope 几乎适用于所有的 PC、平板、智能手机和笔记本电脑设备。

http://www.cirmall.com/circuit/2517/details?1

 

8、旧手机+Arduino的巅峰之作—视频监控机器人 ,异地远程遥控

视频监控机器人Ta跟常见的wifi-robot不同。常见的wifi-robot大多采用OpenWRT路由器板子和USB摄像头,而视频监控机器人采用闲置的安卓旧手机作为机器人的大脑。这不仅是废物利用的环保理念,而且通过充分利用安卓手机的强大计算能力和硬件资源,让视频监控机器人的功能更加有亮点。

http://www.cirmall.com/circuit/3170/details?1

 

9、基于Arduino、L293D电机驱动板/马达板电路

这是一款常用的直流电机驱动模块,采用L293D芯片小电流直流电机驱动芯片。管脚被做成了Arduino兼容的,也方便了爱好者快速的基于Arduino的开发。该电机驱动板功能多,操作方便,有强大的驱动库支持及功能更新。适用于Arduino初学者,Arduino实验器材平台,Arduino互动电子,Arduino机器人等。可驱动4路直流电机或者2路步进电机的同时还能驱动2路舵机,支持最新Arduino UNO, Arduino Mega 2560。

http://www.cirmall.com/circuit/2743/details?1

 

10、软硬件开源制作Arduino蓝牙智能手表,12864oled显示

闲暇时间试着用开源的arduino做了一个蓝牙智能手表,简述过程把经验分享给大家,这个蓝牙手表可以显示时间,连接手机显示通知数量,电池,wifi等。

http://www.cirmall.com/circuit/3358/details?1

 

11、基于Arduino和树莓派的门禁系统设计(升级版)

功能描述

人由门外侧需要进入室内时,红外感应传感器检测到人体而启动相机模组拍照功能,相片上传到网络,提供给远端控制人员进行确认,并可以通过远程控制开门

人由室内出去时,不再需要进行身份确认,可有红外感应传感器检测人体后自动开门

门的开启是通过Arduino控制数字继电器来实现的(或者通过舵机的动作来模拟),相机的启动与网络功能是由树莓派实现的,远程控制人员的命令通过树莓派接收后,通知Arduino进行相应的动作。

http://www.cirmall.com/circuit/4029/details?1

 

12、会模仿、会学习的酷炫小型机械臂电路图及代码

该四轴机器人带有一个小夹子且具备训练的功能。它会模仿,还懂学习,并且能无数次的循环重复这些动作。当电池的电量不足时,它的记忆功能便会丢失,当再次充满电运行它的时候,所有数据都会清零,这又是一个全新的机器人了。

http://www.cirmall.com/circuit/4331/details?1

 

13、智能家居开源制作—arduino实现的可跳舞台灯,语音控制

语音控制调节台灯实现了全语音控制,它可以根据不同的语音命令作出开关和不同的动作,台灯可以完成上、下、左、右、开、关、摇头、点头、跳舞的动作。语音控制台灯需要的硬件机械臂,一个Arduino控制板,一个扩展板,一个语音控制模块,一个升压模块,一个LED灯和电池。最后就是将这些零零散散的东西组装起来并编写程序,反复调试,直至它可以根据命令准确无误的完成各种动作。

http://www.cirmall.com/circuit/3074/details?1

 

14、非特定人语音识别模块LD3320基于Arduino应用篇

LD3320 语音识别模块概述:

采用SPI通信接口,板载咪头和有源晶振,方便在电子产品中实现语音识别、声控和人机对话等功能。注意事项:由于该模块使用3.3V电压,所以将其和Arduino连接时,请务必注意供电电压。

本教程主要实现:通过语音模块控制LED的亮和灭。说“开灯”,LED点亮;说“关灯”,LED熄灭。

http://www.cirmall.com/circuit/5441/details?1

 

15、模仿人脸表情的高逼真机器人,附模仿表情的APP+Arduino源码

和之前的迹APP一样,我给颜艺Boy也提供了Arduino的库函数支持,可以让Arduino通过蓝牙读取人脸的表情、姿态、位置信息,库的使用非常简单,看示例代码就知道了。支持通过蓝牙输出人脸数据,开发板通过蓝牙串口读取,可以结合Arduino实现一些有趣的作品。

http://www.cirmall.com/circuit/6374/details?1

 

2020-05-17 20:27:14 ironpandaaa 阅读数 424

众所周知,掌控板在创客教育中用的非常广泛,它是一块基于 ESP32 的学习开发板。大家对掌控板编程,用的比较多的都是图形化编程的方式,比如 mPython、Mind+ 等。但是,既然掌控板是基于 ESP32 芯片的,所以我们也可以用 Arduino 软件对其编程。所以,有时间的话,我准备给大家分享一系列用 Arduino 代码对掌控板(ESP32)编程的教程:用Arduino玩转ESP32与掌控板系列

本系列历史文章目录:

本期给大家带来的是:掌控板IO接口编程入门,教你用 Arduino IDE 学会掌控板 IO 接口的简单编程操作。IO 就是 INPUT 与 OUTPUT 的缩写,即输入与输出。
微信封面
其实本篇是这个系列的入门篇之一,但是由于我一直偷懒,这篇一直放着没写,先写了一些进阶的内容。本篇比较简单,所以就不放案例演示视频了。

概述

掌控板的 IO 口主要是用来连接外部传感器、执行器等相关外设的。平时我们使用最多最常见的一些外设,一般都是数字量或模拟量的,传感器一般都是输入设备、执行器一般都是输出设备。所以,一般情况下学习一个主控板的 IO 口基本操作,我们都是从数字输入数字输出模拟输入模拟输出这个 4 个方面来进行学习的,本篇也不例外。

这里需要注意的是,输入和输出是相对主控板来说的

  • 当信号是由主控板向外设发送时,相应的外设就是主控板的输出设备;
  • 当信号是由外设向主控板发送时,也就是主控板去读取外设的信号时,相应的外设就是主控板的输入设备。

所以:

  • 大部分传感器都是输入设备,因为他们的功能是测量数据,然后主控板去读取他们的数据;
  • 大部分执行器都是输出设备,因为是主控板向他们发送信号、控制他们完成相应的操作。

数字输入

数字输入(或数字量输入)设备是生活中最常见的设备,比如房间里的开关、按钮、楼道里的人体感应设备,这些设备有一个共同的特点,即只有 2 中情况:有或无、开或关、1 或 0,这些只有 2 种情况的设备,一般称为数字量设备。相应地,如果他们是传感器,则称为数字输入设备,或数字输入传感器。

掌控板上的按键 A 和 B,其实就是数字输入传感器。所以本节就以读取掌控板上的按键 A 和 B 为例,教你对数字输入设备编程。

先来先一下完整的程序:

// 设置掌控板按键 A 的引脚编号
const int buttonAPin = P5;

// 设置按键 A 的状态值变量
int buttonAState = 0;

void setup() {
  // 初始化串口监视器
  Serial.begin(9600);

  // 初始化按键 A 对应的引脚为输入模式
  pinMode(buttonAPin, INPUT);
}

void loop() {
  // 读取按键 A 的值
  buttonAState = digitalRead(buttonAPin);

  // 如果按键 A 被按下了,串口监视器输出信息
  if (buttonAState == LOW) {
    Serial.println("Button A pressed");
  }

  delay(100);
}

这个程序很简单,程序中也有相应的注释了,我们简单来讲解一下。

首先在程序开头定义了两个变量,buttonAPin 用来定义按键 A 对应的引脚,buttonAState 用来存储按键 A 的状态。

// 设置掌控板按键 A 的引脚编号
const int buttonAPin = P5;

// 设置按键 A 的状态值变量
int buttonAState = 0;

这里讲一下命名变量的小技巧,变量一般要命名成方便用户辨别、并且有一定实际字面意义的名称,这样当程序比较长的时候更加容易读懂。尽量不要命名成 a、b 这些没有太多实际字面意义的名称,当然特殊情况除外。

那么我们是如何知道按键 A 对应的引脚编号是 P5 呢

实际上掌控板官方 Wiki 上提供了非常详细的说明和电路图,我们阅读相应的文档,就可以知道掌控板上的板载资源对应的引脚了。我们可以直接点击下方链接,查看掌控板官方 Wiki 资料。

https://mpython.readthedocs.io/zh/master/board/hardware.html

在上面的网页中,我们可以查到一张表格,上面记录了掌控板上所有板载资源对应的引脚。从这里我们可以查到按键 A 对应的引脚编号为 P5。
掌控板引脚说明表
接下来在初始化程序 setup() 中,我们可以看到下面两句代码,分别用来对串口监视器进行初始化、以及设定按键 A 引脚的模式。对串口监视器初始化时,我们要设定串口波特率,常用的波特率是 9600 和 115200,设置波特率的作用是让电脑和掌控板保持相同的通信速率,以便可以互相收发信息。引脚模式,最常用的一般为输入模式 INPUT 和输出模式 OUTPUT,我们知道按键是数字输入传感器,所以要设置为输入模式 INPUT。

  // 初始化串口监视器
  Serial.begin(9600);

  // 初始化按键 A 对应的引脚为输入模式
  pinMode(buttonAPin, INPUT);

这里顺便再提一下,在 Arduino IDE 中编程,一般会有两个自带的函数,分别为初始化函数 setup() 和循环函数 loop()。setup() 函数的功能是在程序开始前做相应的设置,设备上电后(或者按下 RESET 按键之后)只执行一次;loop() 函数是在 setup() 运行完成之后开始运行的,它的功能是不断重复执行其中的代码,因此需要重复运行的代码或任务,一般放在 loop() 函数中,比如不断读取按键的状态。当然也有一些特殊情况,比如中断的设置等,是不用放在 loop() 中的,这部分等讲到相应内容的时候再来展开。

接下来在 loop() 函数中,首先读取按键 A 的状态值,并且赋值给变量 buttonAState。读取数字量输入的程序为 digitalRead(),跟变量命名一样,程序中函数命名也是带有一定的意义、简单易读的。

// 读取按键 A 的值
buttonAState = digitalRead(buttonAPin);

然后根据按键 A 的状态值,通过串口监视器打印出相应的信息。这里需要注意的是,一般情况下,按键弹起时的默认值为 LOW(或 0),当按键被按下时,按键的值变为 HIGH(或 1)。但是这不是绝对的,跟电路设计有关,掌控板的按键值与按键状态,就正好相反。所以当掌控板的按键 A 被按下时,它的状态值为 LOW。为了防止程序运行太快,导致串口监视器一下子输出太多信息,我们这里加了一个延时函数:delay(100),让程序每次都延时 100 毫秒再去运行下一次循环任务。

// 如果按键 A 被按下了,串口监视器输出信息
if (buttonAState == LOW) {
    Serial.println("Button A pressed");
}

delay(100);

在工具菜单中,将主控板选为掌控板,并选择正确的串口号,上传程序。然后打开串口监视器,我们看看程序运行的效果。在串口监视器中将波特率设置为 9600,然后按下掌控板上的按键 A。每按下一次按键 A,串口监视器中就会输出相应的信息,说明程序编写成功!
数字输入监视器

试一试:改写程序,让程序同时也可以读取按键 B 的状态,并在串口监视器输出相应的信息。你会了么?

数字输出

学会了数字输入,我们再来学一学数字输出。我们生活中最常见的电灯,一般情况下,都是数字输出设备。数字输出也是只有 2 种控制状态的设备,高或低、1 或 0。这里以控制 LED 灯为例,来学习一下数字输出的程序编写。我们现在掌控板的 P0 端口外接一个 LED 灯,这里你可以选择自己喜欢的任意扩展板。电路比较简单,所以这里不放电路图了。

来看一下完整的程序:

// 设置掌控板按键 A 的引脚编号
const int buttonAPin = P5;

// 设置 LED 灯的引脚编号
const int ledPin = P0;

// 设置按键 A 的状态值变量
int buttonAState = 0;

void setup() {
  // 初始化串口监视器
  Serial.begin(9600);

  // 初始化按键 A 对应的引脚为输入模式
  pinMode(buttonAPin, INPUT);

  // 初始化 LED 对应的引脚为输出模式
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // 读取按键 A 的值
  buttonAState = digitalRead(buttonAPin);

  // 如果按键 A 被按下了,点亮 LED 灯,否则熄灭 LED 灯
  if (buttonAState == LOW) {
    Serial.println("Button A pressed");
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }

  delay(100);
}

仔细观察,我们发现这个程序跟上一节数字输入中的程序非常类似,我们只是在这基础之上加了几行代码。这里只讲差异部分。

首先在程序开头,定义了 LED 灯的引脚编号,我们将 LED 灯通过扩展板接在掌控板的 P0 引脚,这个与定义按键 A 引脚编号的方法一致。

// 设置 LED 灯的引脚编号
const int ledPin = P0;

然后在 setup() 初始化函数中设置了 LED 连接的引脚为输出模式 OUTPUT,这个与设置按键 A 的方法也一样,只不过一个是 LED 设置为输出模式、按键 A 设置为输入模式。

// 初始化 LED 对应的引脚为输出模式
pinMode(ledPin, OUTPUT);

然后在 loop() 函数中,当检测到按键 A 被按下时,点亮 LED,否则熄灭 LED 灯。点亮 LED 灯的代码为:

digitalWrite(ledPin, HIGH);

熄灭 LED 灯的代码为:

digitalWrite(ledPin, LOW);

将程序上传到掌控板,按下按键 A,看看 LED 灯是否点亮了呢?松开按键 A 呢?

试一试:改写程序,实现:按下按键 A,点亮 LED 灯;按下按键 B,熄灭 LED 灯。

模拟输入

讲完数字输入、数字输出,我们再来看一看模拟输入。模拟输入是相对数字输入来说的,数字输入只要 2 种状态,但是模拟输入有很多种连续的状态,比如各种旋钮、温度值等。

在掌控板上也有 2 个模拟输入传感器:声音传感器与光线传感器。下面我们以声音传感器为例,讲一讲模拟输入的程序编写方法。

// 设置掌控板声音传感器引脚编号
const int soundPin = P10;

// 设置声音大小值变量
int soundValue = 0;

void setup() {
  // 初始化串口监视器
  Serial.begin(9600);

  // 初始化声音传感器引脚为输入模式
  pinMode(soundPin, INPUT);
}

void loop() {
  // 读取声音传感器的值
  soundValue = analogRead(soundPin);
  
  // 串口监视器中打印声音传感器的值
  Serial.println(soundValue);

  delay(10);
}

这个程序与数字输入部分的程序也是非常类似的。

在程序开头,定义了两个变量,用来设置声音传感器的引脚编号、声音传感器的值。

// 设置掌控板声音传感器引脚编号
const int soundPin = P10;

// 设置声音大小值变量
int soundValue = 0;

接着在 setup() 初始化函数中,初始化串口监视器和声音传感器的引脚模式:

// 初始化串口监视器
Serial.begin(9600);

// 初始化声音传感器引脚为输入模式
pinMode(soundPin, INPUT);

接着在 loop() 函数中读取声音的值,并且在串口监视器中打印出来。读取模拟量的函数为 analogRead()。

// 读取声音传感器的值
soundValue = analogRead(soundPin);

// 串口监视器中打印声音传感器的值
Serial.println(soundValue);

delay(10);

将程序上传到掌控板中,打开串口监视器,对着声音传感器说话,我们看看效果。
模拟输入监视器
我们还可以以曲线图的形式,将声音传感器数据的变化展现出来。在 Arduino IDE 菜单栏中,依次点击:工具 → 串口绘图器,打开串口绘图器,对着声音传感器说话,看看曲线图变化吧。
模拟输入绘图器

试一试:改写程序,让程序同时也可以读取光线传感器的值,看看串口绘图器会输出什么信息呢?

模拟输出

你有没有注意到,有些人家里的电灯,亮度是可以调节的?这个其实就是模拟输出。相比于数字输出只有两种状态,模拟输出可以有连续的多种状态。

由于在 Arduino IDE 中,用掌控板自带的函数功能实现模拟输出比较复杂。因此我们这里调用别人做好的适用于掌控板 ESP32 的模拟输出函数库。函数库下载地址为:

https://github.com/ERROPiX/ESP32_AnalogWrite

我们要先将这个库导入到 Arduino IDE 中。这个库的功能是可以让掌控板像 Arduino 一样,方便的使用模拟输出功能,有个这个库,掌控板模拟输出的语法,就跟 Arduino 完全一样了。

这里以控制 LED 灯实现呼吸灯效果为例,完整的程序如下:

#include <Arduino.h>
#include <analogWrite.h>

// 设置 LED 灯引脚编号
const int ledPin = P0;

void setup() {
  // 初始化 LED 灯引脚为输出模式
  pinMode(ledPin, OUTPUT);
}

void loop() {
  for (int brightness = 0; brightness <= 255; brightness++) {
    analogWrite(ledPin, brightness);
    delay(5);
  }

  for (int brightness = 255; brightness >= 0; brightness--) {
    analogWrite(ledPin, brightness);
    delay(5);
  }
}

在程序最开始,我们先引入两个头文件,在头文件中定义了实现模拟输出相关的功能函数。然后定义了 LED 灯的引脚编号。

#include <Arduino.h>
#include <analogWrite.h>

// 设置 LED 灯引脚编号
const int ledPin = P0;

接着在 setup() 函数中设置 LED 灯引脚为输出模式。

然后在 loop() 函数中,通过 for 循环结构,控制 LED 灯由暗变亮,再由亮变暗。亮度通过局部变量 brightness 来设置。

for (int brightness = 0; brightness <= 255; brightness++) {
    analogWrite(ledPin, brightness);
    delay(5);
}

for (int brightness = 255; brightness >= 0; brightness--) {
    analogWrite(ledPin, brightness);
    delay(5);
}

将程序上传到掌控板中,LED 灯是不是呈现呼吸灯闪烁变化的样子?说明程序编程成功了。

总结

在本章中,我们学习了:

  • 如何查阅掌控板各引脚相应的资源配置;
  • 数字量与模拟量的区别;
  • 简单的数字输入、数字输出、模拟输入、模拟输出的程序编写;
  • Arduino IDE 的基本使用方法。

学习了这些基础内容之后,就可以学习后面更加进阶的内容啦,其实进阶内容与入门内容类似,都是由一个个小功能组成的,有了这些基础功能的组合,通过不同的排列组合和逻辑设置,就可以实现各种好玩的创意啦!