ESP32 Arduino学习篇:引脚与串口的基本应用

什么是arduino?

简单来讲arduino就是通过源代码控制开发板的信号输入输出从而实现对一些电子元件的控制,例如LED灯,蜂鸣器,按键

arduino程序

首先来讲arduino程序是基于C语言,而实现类似对于LED灯和蜂鸣器的简单控制只需简单C语言知识即可实现。arduino程序是基于函数的,而在arduino的库有很多函数,我们只需要调用即可。

arduino的程序主体是**setup()函数和loop()**函数以及其它在使用过程中需要构建的函数:

setup()函数

**setup()**函数主要功能是初始化变量,调用库函数,管理引脚等。

loop()函数

**loop()**函数的主要功能是循环的执行函数内的语句,简单来讲就是一个循环。

一、常用引脚控制函数:

1.pinMode(pin,mode):

数字I/O口输入输出定义函数,pin代表数字数字I/O输入输出端的1~13,mode表示输入(INPUT)输出(OUTPUT),例如你想实现从端口6输出信号,则代码是:

pinMode(6,OUTPUT);

2.digitalWrite(pin,vale):

数字I/O口电平定义函数,pin同样代表1~13,vale代表HIGH(高电平)或LOW(低电平),对于LED灯来说,HIGH可使灯发光,LOW可使灯熄灭。例如想实现位于端口6的灯亮的代码是:

digitalWrite(6,HIGH);

3.delay(ms):

延时函数,单位ms,可实现对命令的延迟,例如,实现亮灯500ms,就是让输出的高电平持续500ms,代码是:

delay(500);

4.analogWrite(pin,value) 脉冲宽度调制技术:

(1)作用:输出除了0V和5V之外的电压,产生一个十分接近于模拟信号的输出

(2)使用:通过带有"~"符号的端口输出,使用**analogWrite(pin,value)**函数输出模拟信号,pin代表引脚,value代表输出值(0-255)。

(3)原理:调制一串方波(只有开和关的信号)的占空比(一串方波中高低电平时间所占百分比),通过调控占空比就可以达到调控输出电压的值,例如

analogWrite(pin,127); 表示的时在引脚输出占空比为50%的方波,也就是输出2.5V的模拟电压。

analogWrite(pin,value);

5.digitalRead(pin)==vale(LOW or HIGH ):

数字I/O口电平读取函数,pin同样代表1~13,vale代表HIGH(高电平)或LOW(低电平),

digitalRead(BUTTON)==LOW

二、串口函数

1.串口初始化:serial.begin(value)

在使用串口前,我们需要对串口的波特率也就是传输速度进行设置,需要用到函数 serial.begin(value),value即是设置波特率。

2.串口发送函数 serial.println():

在函数中输入参数就可以使用串口输出了。

3.串口接收:serial.read()

使用 serial.read()函数,它可以读取我们通过串口发送的信号

三、示例

1.串口输出"Hello,world"信号

void setup(){
  Serial.begin(9600);
}

void loop(){
  if (Serial.available() > 0){
    char ch=Serial.read();
    Serial.println("Hello, world")
}
}

我们注意到,在loop()函数使用了Serial.available()函数,在使用串口时ARDUINO会将所有数据暂时存放在缓冲区,而当串口缓冲区没有可读数据时,Serial.read()函数返回的值会造成乱码,时泳Serial.available()函数可以检测串口缓冲区是否有可读数据,搭配 if 语句时,有可读数据则再进行命令,还特别值得注意的是,在串口监视器的右下角有一个波特率设置,这里的波特率数值需要和前面定义的数值一致

2.在串口发送字符控制灯的亮灭

程序如下:

int LED = 9;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(LED,OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
  if(Serial.available()>0){
    char ch=Serial.read();
    Serial.println(ch);
    if (ch == 'A'){
      digitalWrite(LED,HIGH);
      Serial.println("turn on");
    }
   else if (ch == 'B'){
      digitalWrite(LED,LOW);
      Serial.println("turn off");
    }
  }
}

通过这个程序,当你向ARDUINO串口发送数据 ‘A’ 时,LED灯会亮起来,同时串口监视器会接收到一串"turn on"字符,再次发送 ‘B’ 时,LED灯会灭,同时发送"turn off"

运行结果如下

3.匹配一个字符串并且输出相应的内容

我们知道,串口通讯函数serial.read() 的功能是从串口缓存区读取一个字符并将之删除,此时我的想法是构建一个 Str类,用+=语法将每次读取的一个字符存入一个变量为str的字符串中,那么代码如下

void loop(){
    String str="";
    while(Serial.available()>0){
        char ch=Serial.read();
        str +=ch;
    }
    Serial.print("input the str:");
    Serial.println(str);
}

当我们向串口发送"hello"时,可以看到结果如下

这个时候我又想到了用strcmp(str1,str2)函数,它的功能是对两个字符串进行比较,如果相等则返回"0”。好,我们用函数来试一下:

char trystring[6];
char serialdata[6];
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  strcpy(trystring,"hello");
}

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available()>0){
    
    for (int i=0;i<=4;i++){
      char str = Serial.read();
      serialdata[i]=str;
    }
  }
    
  if (strcmp(trystring,serialdata)==0){
      Serial.print("data is: ");
      Serial.println(serialdata);
      Serial.println("hello,world!");
      
    }
    delay(500);
  
}

当我们再次向串口发送"hello"时,来看看运行结果:

编辑

4.呼吸灯

呼吸灯?那是啥?呃…就是从亮到暗从再从暗到亮,我们知道数字输出要么输出5V要么输出0V,那我们如果让输出的电压从0~5V一点一点的增加再一点一点的减少不就可以了吗?

要实现这个目的,我们就要用到PWM波模拟值函数analogWrite(pin,value)函数了,他和digiralWrite(pin,value)用法一样,只是value值变成了0~255,映射到了0–5V,代码如下:

int LED=9;
void setup(){
    pinMode(LED,OUTPUT);
}

void loop(){
    for (int i=0;i<=255;i++){
        digital.Write(LED,i);
        delay(5);
    }
    for (int i=255;i>=0;i--){
        digital.Write(LED,i);
        delay(5);
    }
}

5.通过光敏电阻与程序实现对LED灯的控制

代码:

void setup() {
  // put your setup code here, to run once:
  pinMode(2,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(A0,INPUT);
  pinMode(A1,INPUT);
  Serial.begin(9600);
  digitalWrite(2,HIGH);
  delay(500);
  digitalWrite(4,HIGH);
  delay(500);
}

void loop() {
  // put your main code here, to run repeatedly:
  int a =analogRead(A0);
  int b= analogRead(A1);
  if (a>b){
    digitalWrite(2,LOW);
    digitalWrite(4,HIGH);
    delay(300);
  }
  else if(a<b){
    digitalWrite(4,LOW);
    digitalWrite(2,HIGH);
    delay(300);
  }
  delay(500);
  Serial.print("a: ");
  Serial.println(a);
  Serial.print("b: ");
  Serial.println(b);

}

我们在这里用到了analogRead(pin)函数,它的功能是模拟输入,用于模拟输入引脚读取数值,可以将0–5V的电压映射到数值0–1023,也就是0V等于0,5V等于1023,

只需要测量两个光敏电阻上的电压大小,再进行比较就可以实现了,当左边电阻减小时,让右边亮起来,当右边电阻减小时,让左边亮起来,当我们挡住任意一边时,另一边永远都不会亮。

6.光敏呼吸灯

 void setup() {
  // put your setup code here, to run once:
  pinMode(3,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(A0,INPUT);
  pinMode(A1,INPUT);
  Serial.begin(9600);
  analogWrite(3,127);
  analogWrite(5,127);
  for (int i=127;i<=255;i++){
    analogWrite(3,i);
    analogWrite(5,255-i);
    delay(10);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  int a =analogRead(A0);
  int b= analogRead(A1);
  if (a>b){
    for (int i=0;i<=255;i++){
      analogWrite(3,255-i);
      analogWrite(5,i);
      delay(5);
    }
  }
  else if(a<b){
    for (int i=0;i<=255;i++){
      analogWrite(5,255-i);
      analogWrite(3,i);
      delay(5);
    }
  }
  delay(100);
  Serial.print("a: ");
  Serial.println(a);
  Serial.print("b: ");
  Serial.println(b);

}

7.舵机

在使用舵机时需要用到ARDUINO的扩展库<Servo.h>,

该库的一些基本函数:

1. attach()

描述:

将Servo变量附加到引脚,注意:在Arduino 0016及之前的版本上,Servo库仅支持将舵机连接至第9和第10脚上。

语法:

  • servo.attach(pin)

  • servo.attach(pin, min, max)

  • 参数说明:

    1. servo,一个类型为servo的变量

    1. pin,连接至舵机的引脚编号

    1. min(可选),舵机为最小角度(0度)时的脉冲宽度,单位为微秒,默认为544

    1. max(可选),舵机为最大角度(180度时)的脉冲宽度,单位为微秒,默认为2400

    2. write()

    描述

    向舵机写入一个数值,来直接控制舵机的轴。在一个标准的舵机中,这将设定齿轮的角度,将齿轮转到对应的位置。在一个连续旋转的舵机中,这将设置一个舵机的角度(0作为全速向一边,180为全速向另一边,在90附近的值为停止)。

    语法

    servo.write(angle)

    参数说明

    servo:一个类型为servo的变量

    angle:写向舵机的角度,从0到180之间

    3. writeMicroseconds()

    描述

    向舵机写入一个微秒的值来控制舵机的轴。在一个标准舵机中,这将设置舵机齿轮的角度。在标准舵机中,参数设置为1000为完全逆时针方向,2000完全顺时针方向,1500为在中间。

    注意:

    一些生产厂商没有按照这个标准,以至于,舵机通常响应在700到2300之间的值。自由地增加终点值直到舵机不再增加它的范围。注意,让舵机旋转超过它的终点(通常会发出异常声响)是一个高电流状态,应该被避免。连续旋转舵机对该函数的响应类似于write()函数

    语法

    servo.writeMicroseconds(uS)

    参数说明

    1. servo,一个类型为servo的变量

    1. uS,一个代表微秒值的整数参数

    4. writeMicroseconds()

    描述:

    向舵机写入一个微秒的值来控制舵机的轴。在一个标准舵机中,这将设置舵机齿轮的角度。在标准舵机中,参数设置为1000为完全逆时针方向,2000完全顺时针方向,1500为在中间。

    注意:

    一些生产厂商没有按照这个标准,以至于,舵机通常响应在700到2300之间的值。自由地增加终点值直到舵机不再增加它的范围。注意,让舵机旋转超过它的终点(通常会发出异常声响)是一个高电流状态,应该被避免。连续旋转舵机对该函数的响应类似于write()函数

    语法:

    servo.writeMicroseconds(uS)

    参数说明:

    1. servo,一个类型为servo的变量

    1. uS,一个代表微秒值的整数参数

    5. read()

    描述:读取舵机当前的角度(最后一次用write()函数写入的值)

    语法

    servo.read()

    参数说明

    servo,一个类型为servo的变量

    返回值

    舵机的角度,从0至180度

    6. attached

    描述:检查一个servo变量是否被附加到一个引脚

    语法:

    servo.attached()

    参数说明:

    servo,一个类型为servo的变量

    返回值:

    返回true,如果被附加到一个引脚,反之返回false

    7. detach

    描述:将servo变量与引脚脱离,如果所有servo变量均被脱离,第9和第10教将可以用analogWrite()函数进行PWM输出。

    语法:

    servo.detach()

    参数说明:

    servo,一个类型为servo的变量

    示例分析:

    完整代码:

    该库中有控制舵机的函数,舵机有三个接口,分别是电源、接地、控制口,先来看看我们的代码:

    # include <Servo.h>      //伺服电机扩展库
    Servo myservo;
    void setup() {
      // put your setup code here, to run once:
    
      Serial.begin(9600);
      myservo.attach(2);
    }
    int INPUTdata=0;
    void loop() {
      // put your main code here, to run repeatedly:
      if(Serial.available()>0){
        INPUTdata+=1;
        int toPos = Serial.parseInt();
        int fromPos = myservo.read();
        if(toPos>=fromPos){
          for (int i=fromPos;i<=toPos;i++){
            myservo.write(i);
            delay(15);
          }
        }
        else{
          for (int i=fromPos;i>=toPos;i--){
            myservo.write(i);
            delay(15);
          }
        }
        Serial.print("INPUTdata");
        Serial.print(INPUTdata);
        Serial.print("  fromPos: ");
        Serial.print(fromPos);
        Serial.print("    toPos: ");
        Serial.println(toPos);
        while(Serial.available()>0){
          Serial.read();
        }
      }
        
    }

    语句介绍:

    定义一个伺服电机对象:
    Servo myservo;

    将伺服电机对象接到2号口,可对伺服电机控制:
    myservo.attach(2);

    parseInt()函数,从串口接收数据,只读取数字;
    Serial.parseInt();

    读取当前电机状态,返回电机位置:
    myservo.read();

    伺控制电机转动到值为value的位置

    ( 0=<value<=180)

    myservo.write(value);

    程序解释:

    从串口接收一个数据然后转到对应位置,但是我们将程序烧到UNO板上,发现电机转动的很快,这里我们设置一个for()循环,控制电机转动速度。

    8.数码管

    (1)数码管简介

    数码管是一种半导体发光器件,其基本单元是发光二极管。数码管按段数分为七段数码管

    和八段数码管,八段数码管比七段数码管多一个发光二极管单元(多一个小数的显示);

    按发光二极管单元连接方式分为共阳极数码管和共阴极数码管。共阳极数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)数码管。共阳数码管在应用时应将公共极

    COM 接到+5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮。当某一字段的阴极为高电平时,相应字段就不亮。

    (2)实验代码:

    数码管随机显示数字

    int BUTTON=2;
    void style_0(void){
      digitalWrite(3,LOW);
      digitalWrite(4,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
    }
    void style_1(void){
      digitalWrite(4,LOW);
      digitalWrite(7,LOW);
    }
    void style_3(void){
      digitalWrite(4,LOW);
      digitalWrite(3,LOW);
      digitalWrite(5,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
    }
    void style_2(void){
      digitalWrite(3,LOW);
      digitalWrite(4,LOW);
      digitalWrite(5,LOW);
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
    }
    void style_4(void){
      digitalWrite(4,LOW);
      digitalWrite(5,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
    }
    void style_5(void){
      digitalWrite(3,LOW);
      digitalWrite(5,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
    }
    void style_6(void){
      digitalWrite(3,LOW);
      digitalWrite(5,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
    }
    
    void setup() {
      // put your setup code here, to run once:
      for(int i=3;i<=9;i++){
        pinMode(i,OUTPUT);
        pinMode(BUTTON,INPUT);
      }
    
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      int Number = GetRandomNumber(0,7);
      DisplayNumber(Number);
      delay(250);
      DisplayClear();
      delay(250);
      
      
    }
    int GetRandomNumber(int MinNum,int MaxNum){
      int RandNumber = random(MinNum,MaxNum);
      return RandNumber;
    }
    void DisplayClear(){
      for (int i=3;i<=9;i++){
        digitalWrite(i,HIGH);
      }
      }
    
    void DisplayNumber(int GetNumber){
          switch(GetNumber){
          case 1:
            style_1();
            break;
          case 2:
            style_2();
            break;
          case 0:
            style_0();
            break;
          case 3:
            style_3();
            break;
          case 4:
            style_4();
            break;
          case 5:
            style_5();
            break;
          case 6:
            style_6();
            break;
        }
      
    }

    9.超声波测距模块:

    (1)模块介绍

    US=015超声波测距模块

    电气参数

    US-015

    工作电压

    DC 5V

    工作电流

    2.2mA

    工作温度

    0~+70度

    输出方式

    GPIO

    探测距离

    2cm-400cm

    探测精度

    0.1cm+1%

    分辨率

    高于1mm

    模块一共四个接口

    1号pin:接VCC电源(直流5V)。

    2号pin:接外部电路的Trig端,向此管脚输入一个10us以上的高电平,可触发模块测距

    3号pin:接外部电路的Echod端,当测距结束时,此管脚会输出一个高电平,电平宽度会输出一个高电平,电平宽度为超声波往返时间之和

    4号pin:接外部电路的地

    测距原理:在Trig引脚输入一个10us以上的高电平,系统便可发出8个40KHZ的超声波脉冲,然后检测回波信号。当检测到回波信号后,通过Echo管脚输出,最后通过 L=1/2time340m/s 则可得出距离

    (2)实验示例:模块搭配数码管和伺服电机。

    现在,我们已经知道了如何使用数码管、伺服电机和超声模块,那么接下来我们要做的就是将这三部分结合:测定障碍物的距离,当障碍物与装置的距离小于10cm时,通过数码管显示当前距离,并且让舵机转动,想象一些,如果将这些东西只能装到避障小车会是什么效果?就是寻找没有障碍的方向。

    首先看看代码:

    #include <Servo.h>
    Servo my_servo;
    int TrigPin=11;
    int EchoPin=12;
    long Echo_time=0;             //接收到高电平所用时间
    double distance;
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
      my_servo.attach(2);        //将伺服电机接到2号引脚
      pinMode(TrigPin,OUTPUT);
      pinMode(EchoPin,INPUT);
      for (int i=3;i<=9;i++){   //将所有数码管设置为输出模式
        pinMode(i,OUTPUT);
      }
      my_servo.write(90);
    }
      void style_0(void){    //显示的数字设置
      digitalWrite(3,LOW);
      digitalWrite(4,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
    }
      void style_1(void){
      digitalWrite(4,LOW);
      digitalWrite(7,LOW);
    }
      void style_3(void){
      digitalWrite(4,LOW);
      digitalWrite(3,LOW);
      digitalWrite(5,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
    }
      void style_2(void){
      digitalWrite(3,LOW);
      digitalWrite(4,LOW);
      digitalWrite(5,LOW);
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
    }
      void style_4(void){
      digitalWrite(4,LOW);
      digitalWrite(5,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
    }
      void style_5(void){
      digitalWrite(3,LOW);
      digitalWrite(5,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
    }
      void style_6(void){
      digitalWrite(3,LOW);
      digitalWrite(5,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
    }
      void style_7(void){
      digitalWrite(3,LOW);
      digitalWrite(4,LOW);
      digitalWrite(7,LOW);
    }
      void style_8(void){
      digitalWrite(3,LOW);
      digitalWrite(4,LOW);
      digitalWrite(5,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
      }
      void style_9(void){
      digitalWrite(3,LOW);
      digitalWrite(4,LOW);
      digitalWrite(5,LOW);
      digitalWrite(6,LOW);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
    }
    
    
    
    void loop() {
      // put your main code here, to run repeatedly:
      for (int i=3;i<=9;i++){
        digitalWrite(i,HIGH);
      }
      digitalWrite(TrigPin,LOW);
      delayMicroseconds(2);
      digitalWrite(TrigPin,HIGH);
      delayMicroseconds(10);       //让测距模块开始工作发出频率电波
      digitalWrite(TrigPin,LOW);
      Echo_time=pulseIn(EchoPin,HIGH);       //读取接收到声波来回的时间
      if (Echo_time>1&&Echo_time<85000){
        distance=Echo_time*0.017;     //通过接收到高电平的时间算出当前距离
      }
      Serial.print("the distance is: ");
      Serial.print(distance);
      Serial.println(" cm");
      int real_dis=(int)distance;  //将浮点数类型的distance转换为整数
      if (real_dis<10){
        waring_num(real_dis);     //显示危险距离
        delay(200);
        for(int i=3;i<=9;i++){
          digitalWrite(i,HIGH); //熄灭所有灯管
    
        }
        int fromPos=my_servo.read();
        servo_turn(fromPos);
        delay(100);
        
          
      }
       delay(500);
    }
    void waring_num(int waring_dis){   //显示小于10的危险距离
          switch(waring_dis){
          case 1:
            style_1();
            break;
          case 2:
            style_2();
            break;
          case 0:
            style_0();
            break;
          case 3:
            style_3();
            break;
          case 4:
            style_4();
            break;
          case 5:
            style_5();
            break;
          case 6:
            style_6();
            break;
          case 7:
            style_7();
            break;
          case 8:
            style_8();
            break;
          case 9:
            style_9();
            break;
        }
    
    }
    void servo_turn(int fromPos){         //舵机转动函数
      int toPos=30*random(1,6);
      if (fromPos<toPos){
        for (int i=fromPos;i<=toPos;i++){
          my_servo.write(i);
          delay(15);
        }
      }
      else{
        for (int i=fromPos;i>=toPos;i--){
          my_servo.write(i);
          delay(15);
          }
        }
        Serial.print("the formPos: ");
        Serial.print(fromPos);
        Serial.print(" the toPos: ");
        Serial.println(toPos);
        delay(100);
    }

    (3)实验讲解:

    将舵机初始位置设为90°,然后每次测距之后都判断距离是否在安全范围类,如果不在安全范围内,显示当前距离,然后就让舵机随机像一个方向转动任意的30°的倍数,然后再次检测。代码本身是没有什么难度,而是在于将三个部分组合起来使用的思维,使用合适的代码将三个部分的代码组合并且使之连贯。

    物联沃分享整理
    物联沃-IOTWORD物联网 » ESP32 Arduino学习篇:引脚与串口的基本应用

    发表评论