锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

【单片机】Arduino进阶应用

时间:2022-09-27 05:30:01 2c公形引脚插头线对线连接器

文章目录

  • 前期教程
  • 前言
  • 一、库文件丰富
    • 1. 舵机库——Servo.h
    • 2. 软串口库——Softwareserial.h
    • 3. IIC总线——Wire.h
    • 4. 老旧的库——Wprogram.h
  • 二、Arduino下载编程器&USBasp下载
    • (一)用Arduino下载编程器
    • (二)使用USBasp下载程序
  • 三、Arduino作为USB转TTL模块
  • 四、EEPROM操作
    • 使用参考
  • 五、总结其他问题
    • 1. 电机和舵机不能同时运行?
    • 2. Arduino程序烧不上去?
    • 3. 建立多文件工程文件 /\*2022.3.14\*/更新
    • 4. 利用MATLAB开发Arduino
    • 5. 软件复位

前期教程

  • 【单片机】Arduino(以UNO R3为例)—— 基本函数汇总
  • 【单片机】Arduino供电总结
  • 嵌入式模块舵机使用详细说明

前言

??作为最容易入门的单片机之一,Arduino总总是可以使用非常简单和容易理解的代码来实现你想要的功能,许多人可能会使用它Arduino做一些相对简单的工作或其他原因,从而形成一种思维定势:Arduino只能做简单的事。但是随着我的使用Arduino其他单片机的程度逐渐加深,我对Arduino的一些 “高级功能” 有了更深的了解,发现Arduino真的不简单!

一、库文件丰富

??Arduino一个强大的功能是它配备了丰富的包装库文件,有些是安装软件时带来的,有些可以额外下载。
??首先,我们如何找到这些库文件? 有两个地方可以找到:一个是安装目录libraries文件夹,安装软件时自带;另一个是文档Arduino文件夹下的libraries文件夹,应该是额外下载的库文件的安装位置。
??那如何学习库文件? 在我看来,首先是单片机的工作原理和Arduino电影外设有足够的了解,其次是学习其内部函数。此外,官方还提供了一个故事libraries总是阅读链接。

1. 舵机库——Servo.h

??通过查看Servo.h可以发现舵机被定义为一个类,即Servo,舵机的相关操作是其中的成员函数,因此在使用该库时,应首先定义一个类别,如Servo myservo,然后再使用myservo.xxxxx调用各类成员函数。

servo.attach(pin)  servo.attach(pin, min, max) 

??参数解释
???pin:设定的信号输出引脚;
???min:当舵机转向0度时(默认值为544us,周期是20ms)
???max:当舵机转向180度时(默认值为2500us,周期为20ms)

  • attached()
    ??判断是否判断舵机信号引脚"绑定"如果是,返回true,如果没有,返回false
bool a; a = servo.attached(); 
  • detach()
    ??可以使用信号输出引脚解绑analogWrite()来输出PWM波。
servo.detach() 
  • write()
    直接输出舵机的角度
servo.write(angle);  //angle0-180之间的输出角度 

??常用例程

#include   Servo myservo; void setup()  { 
            myservo.attach(9);
} 
void loop() 
{ 
        
	myservo.write(90);
	delay(1000);
	myservo.write(180);
	delay(1000);
} 
  • writeMicroseconds()
      输出脉冲时间,相比于直接输出角度稍有点复杂,对于标准舵机,PWM频率固定是50Hz,当脉冲宽度为1500us时,舵机处于中间状态,上下限和舵机舵机生产厂商有关,一般来说下限是500us,上限是2500us。【注意看舵机数据手册】
    语法
servo.writeMicroseconds(us);  //us即为脉冲宽度,单位为微秒
  • read()
    读取当前舵机的角度,返回值为0-180。
int angle = servo.read();

2. 软串口库——Softwareserial.h

  在Arduino中,只有一个串口模块,即0,1引脚,而且这个串口模块还是和插电脑的USB的连通的,因此在烧录程序时只能拔掉串口连接的设备,否则无法烧录程序,因此软串口十分有必要。
  所谓软串口,就是使用一般的数字引脚模拟串口传输时的高低电平变化,而这个库Softwareserial.h的作用就是将一些底层代码封装成一个个函数,方便直接调用。下面是这个库中封装的好的函数
在这里插入图片描述
  由于这里很多函数名字和使用都和硬串口中的函数相同,故这里只总结第一个函数 SoftwareSerial的使用,并给出一个使用例程,方便参考。

  • SoftwareSerial()
    选择引脚设置为软串口,其语法如下:
SoftwareSerial(rxPin, txPin, inverse_logic);

  其中rxPin和txPin分别为输入引脚和输出引脚号,而inverse_logic参数是设置为负逻辑,不加时默认为false。

// include the SoftwareSerial library so you can use its functions:
#include 

#define rxPin 10
#define txPin 11

// set up a new serial port
SoftwareSerial mySerial =  SoftwareSerial(rxPin, txPin);

void setup()  { 
        
  // define pin modes for tx, rx:
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);
}

void loop() { 
        
  if (mySerial.available()>0){ 
        
  mySerial.read();
 }
}

  需要注意:如果定义多个软串口,则同一时刻只能接收一个串口的字符。而且软串口传输的波特率不宜过高(<115200)。在Mega及其他板子中,并不是所有引脚都能设置为软串口引脚,具体参考这个链接,其他使用注意事项可以看看这篇博客。

3. IIC总线——Wire.h

参考链接
Arduino Wire.h 库函数基本操作 IIC - CSDN
官方Wire库讲解
认识
  Arduino的Wire.h库主要用于进行IIC通信(还有TWI,但是不常用),将IIC通信传输的操作封装成函数,方便直接调用。建议在使用之前找到这个库的源代码,位置在C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries,找到Wire文件夹,看源码和关键词。

Wire.begin()
Wire.begin(address)

  其中address为地址,如果不加,默认为主设备。

  • requestFrom()
    主设备向从设备请求发送数据,这个数据可以被read()available()函数接收
Wire.requestFrom(address, quantity)
Wire.requestFrom(address, quantity, stop)

  其中,quantity为接收数据的字节数; stop为一个布尔值,1 :再请求完数据之后,发送一个停止信号,释放总线;0 :不发送停止信号,不释放总线,主设备可以再次请求。默认值为1。
  返回值为从设备传输的字节数。

  • beginTransmission()
    开启向从设备输出数据
Wire.beginTransmission(address);
  • endTransmission()
    用来结束对从设备的数据输入。其中,里面可以加上参数stop,如果stop为1,则会在传输结束之后再发送一个结束信号,从而释放总线;如果stop为0,则表明在传输数据之后不发送结束信号,不释放总线。不加这个参数时,默认值为1。
Wire.endTransmission()
Wire.endTransmission(stop)

  其返回值有5种情况:
    0 :传输成功
    1 :数据太长,无法装入缓冲区
    2 :在传输地址时接收到NACK(非应答信号)
    3 :在传输数据时接收到NACK
    4 :其他错误

  • write()
    使主设备传输到从设备的数据形成队列
Wire.write(value)
Wire.write(string)
Wire.write(data, length)

  value:单个字节
  string:字符串,一串数据
  data:一个数组
  length:传输数据的字节长度

需要注意的是:传输数据时一般是先begin,再write,再end。参考下面这个例程。

#include 

byte val = 0;

void setup()
{ 
        
  Wire.begin(); // join i2c bus
}

void loop()
{ 
        
  Wire.beginTransmission(44); // transmit to device #44 (0x2c)
                              // device address is specified in datasheet
  Wire.write(val);             // sends value byte 
  Wire.endTransmission();     // stop transmitting

  val++;        // increment value
  if(val == 64) // if reached 64th position (max)
  { 
        
    val = 0;    // start over from lowest value
  }
  delay(500);
}
  • available()
    返回能够读入的数据字节数,一般来说这个函数的调用要紧接着函数requestFrom()之后,如果是从设备,要紧接着onReceive() 函数。
Wire.available()

  函数返回值为能够读入的数据字节数

  • read()
    主设备读入从设备的数据字节数
    返回值为读到的一个字节

主设备读入数据时,一般要按照下面这个例程来进行。

#include 

void setup()
{ 
        
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop()
{ 
        
  Wire.requestFrom(2, 6);    // request 6 bytes from slave device #2

  while(Wire.available())    // slave may send less than requested
  { 
        
    char c = Wire.read();    // receive a byte as character
    Serial.print(c);         // print the character
  }

  delay(500);
}
  • setClock()
    设置传输时钟。单位为Hz
Wire.setClock(clockFrequency)

100KHz :标准模式(clockFrequency = 100000,下面类似。)
400KHz :快速模式
部分支持:
10KHz :慢速模式
1MHz : 快速模式Plus
3.4HMz :高速模式

  • onRequest()&onReceive()
    设置主设备请求从设备发送数据时执行的函数;设置从设备接收到数据时执行的函数。
Wire.onReceive(handler)
Wire.onRequest(handler)

  其内部的参数为一个函数,且要求这个函数没有参数,没有返回值。

  使用时需要注意UNO板子上,SDA和SCL是和A4,A5引脚共用的,即使用了IIC数传,则不能再用A4,A5引脚。

4. 老旧的库——Wprogram.h

  这个库现在一般不用了,因为它并入了Arduino.h文件中。记录一下。

二、Arduino作为编程器下载&USBasp下载

  此前因为意外,不小心烧了一块板子,其结果就是插上电脑会弹出下面这个窗口,同时Arduino IDE中端口选项是灰色的。总而言之,这块板不能再通过USB烧入程序了。

  但是我不甘心啊,之前也遇到过这样的问题,但是当时因为没有时间,加上烧的是UNO,不是很贵,所以没研究。而这次烧的可是Arduino Mega,一块可以买两三块UNO,所以这块板子一直留了下来,因为我始终坚信这块板子的芯片没坏。
  我的猜测是这块板子是CH340芯片坏掉了,导致电脑无法识别,但是内部芯片没问题,那Arduino除了用USB插电脑来下载程序,有没有其他的方式下载程序呢?有,而且还有两种。一种是用另外一块板子(比如UNO)对目标板进行程序下载,另一种就是去买一个USBasp下载器,下载器如下图所示。

(一)用Arduino作为编程器下载

  首先需要准备另一块好的板子,型号不固定,我这有现成的UNO,故使用这块板子。

  • 第一步就是把Arduino UNO变成一个编程器。方法就是在Arduino 正常下载特定的程序Arduino ISP,直接在文件->示例中就能找到。
  • 下载完这个程序,此时的Arduino板就成为一个编程器了,然后就是连线。对此,我们需要简单理解一下这种下载方式的原理,这种下载方式使用的协议为SPI,而SPI为四线协议,因为这里没有片选,所以只有三线,即MOSI,MISO,SCK,所以连线的主要任务就是将编程器的这三个引脚与需要烧入程序的板子之间进行连接。
      这一点,建议去看Arduino ISP例程中的注释,如下图所示。

    从图中可以看到,目标板还需要连接RESET引脚,这个引脚连接的编程器的 Pin 10,故连线对应关系如下表所示
编程器(UNO) 目标板
11 MOSI
12 MISO
13 SCK
10 RES
5V,GND 5V,GND

注意和RX,TX连接方式区分开来!MOSI连接MOSI,MISO连接MISO!

所以在连线时,需要先了解目标板子的引脚分布,看哪些引脚对应的是SPI通信的三个引脚。一般来说,Arduino板子上会有6个ICSP的接口,如下图所示。这些接口实际上就是从边上的那几排接口中引出来的,

所以方便起见,可以直接看这六个引脚的分布,如下图所示(大部分Arduino的引脚顺序都是这样)

  • 接好线之后,下面就是烧录程序了,将编程器通过USB插到电脑上,打开需要下载到目标板的程序,然后按照下图设置一下。
    设置完之后,不能直接点击上传按钮上传,而要在项目中选择使用编程器上传,然后等待上传成功即可。

(二)使用USBasp下载程序

  那假如手边没有其他的Arduino板,那又如何下载程序呢?还有一个办法,就是去买一个USBasp,总归要比再买一个Arduino UNO来得便宜。图片参考上面的某张图。
  下面简单记录一下操作方法。

三、Arduino作为USB转TTL模块

  因为要调试蓝牙模块,但是却缺了一个USB转TTL的模块,然后就想到用多串口的单片机实现,比如Arduino Mega,然后写了一个程序,如下所示:

void setup() { 
        
  Serial.begin(9600);  //与电脑连接
  Serial1.begin(9600);//与蓝牙模块连接
}

void loop() { 
        
  send_data();
  receive_data();
}

char send_data()
{ 
        
  while (Serial.available())
  { 
        
    char a = Serial.read();
    Serial1.write(a);
  }
}

char receive_data()
{ 
        
  while (Serial1.available())
  { 
        
    char b = Serial1.read();
    Serial.write(b);
  }
}

  但是很遗憾,这个程序烧入进去之后从电脑上发出AT指令,得到的回应只有\0,不知道为啥,然后就去网上找相关的资料,但没想到,程序没找到,倒是找到一个解决这个问题的另一种方法! 附:参考链接
  实际上,Arduino本身就可以作为USB转TTL模块,而且使用方法也非常简单。
  首先,在Arduino中烧入以下程序:

void setup() { 
        
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);  //将串口的两个引脚设置为上拉输入
}

void loop() { 
        
  
}

  然后将蓝牙模块连接到与USB端口所用的串口上,而且务必注意:是TX连接TX,RX连接RX!!!,这样,就可以通过电脑上的串口调试助手来调试蓝牙模块了,而且使用体验与USB转ttl模块完全一样!

四、EEPROM操作

  最近才知道,原来Arduino和STC的51单片机一样,除了写入程序的flash之外,还内置了一个EEPROM,而且它还内置了一个库函数EEROM.h,方便用户直接调用写入读出等函数,非常友好。

使用参考

  • Arduino EEPROM的使用
  • 官网参考文件

五、其他问题汇总

1. 电机与舵机不能同时运行?

  此前在参加一个智能小车的比赛时,发现上舵机之后电机不会动,找了半天也没找到原因,最后经过别人的提醒,是因为舵机的原因,因为Arduino UNO中引脚9,10都能输出PWM,且二者共用一个定时器,如果引脚9作为舵机信号线使用,那么另一个引脚输出PWM的功能就会收到影响,因为舵机输出的PWM固定为50Hz,这样频率的PWM会导致直流电机运行不正常。解决办法就是换引脚,或者是使用detach()函数。
  参考链接

2. Arduino程序烧入不上去?

  有一个可以尝试的办法就是重新烧入Bootloader,即引导程序,方法就是插上USB线,然后点击工具->烧录引导程序

或者使用USBasp烧入,效果一样。

3. 多文件工程文档的建立 /*2022.3.14*/更新

  相信对于习惯使用Keil创建51系列、STM32系列单片机工程文档的人来说,Arduino似乎就是一个异类,因为它一个程序似乎只能放在一个文件中,对于习惯以文件来管理外设的人来说会很不习惯。而我就是其中一个,因为一个项目稍有点复杂,我仔细研究了一下这个Arduino的文件逻辑。下面是一些记录。

  • 首先Arduino是能够添加其他文件的,包括 .c,.cpp,.h,.ino,都是支持的,但是它必须要有一个和它外面套的文件夹名相同的文件(估计这也是为什么它必须要求每个文件外面都要套一层同名文件夹)
  • 虽然Arduino能够支持添加多种文件,但和一般的C51工程文档还是有点区别,不注意的话编译是无法通过的。
  • 对于 .cpp,.h文件,这个可能是最为常用的,因为一般Arduino的库文件就是这样一对的形式出现的。但是,相信很多人看到的都是类声明和类定义,经过我的测试,如果在cpp文件中写其他函数或者引用其他库文件,都是会报错的,即Arduino要求你的库文件都必须以类的形式展现
  • 对于 .c,.h文件,首先确定一点,它们的使用逻辑和C51是一样的,但是,经过我的不完整测试,**它们不能在文件中include除Arduino.h外的所有自带库,而且不能引用工程下面的其他文件。**也就是只能写一些基本的digitalWrite之类的函数。
  • 综上所述,似乎想要在Arduino中添加库文件很麻烦?确实,但还有一种是使用ino格式的文件,这种方法是我发现最为方便的方法了,它相当于是C51工程文档下一对.c,.h文件,而且主ino里面不需要include。

4. 利用MATLAB开发Arduino

  由于个人比较常用MATLAB,上次无意间看到利用MATLAB开发Arduino的方法,记录一下。
  参考链接1
  参考链接2
  参考链接3

  • 1、在MATLAB中下载Arduino硬件支持包。 arduino硬件支持包可联网直接下载(有时无法成功下载,只能翻墙才行)。获取安装包步骤:①在matlab主页→在附加功能项目→点击硬件支持包→点击matlab support package for Arduino Hardware下载(需要注册一个matlab账户,大多数情况下需要墙才可下载成功,添加到路径。
  • 2、利用Arduino IDE在Arduino中烧录Arduino IO文件。文件下载链接见上文参考链接1。到此软硬件配置完毕。
  • 3、连接Arduino(只支持UNO、Mega2560),开始在MATLAB上开发。在MATLAB命令行中输入:a = arduino(“COM4”),(注意:COM口自己选对),等待片刻会显示连接成功。

5. 软件复位

  • 参考链接

  在写一些程序时,有时候想要程序在不受外界操作之下自动进行程序复位,即所谓的软件复位。经过查找资料,找到两种方式,一种是通过给reset引脚单独接一个数字引脚,这样需要复位时,只需要将这个引脚输出值改为低电平就行;还有一种方式是使用Arduino自带的一个软件复位函数resetFunc,其实例程序如下所示。

void(* resetFunc) (void) = 0;  //指将resetFunc这个函数的地址设置为0,即初始地址

void setup() { 
             
  Serial.begin(9600);
  Serial.println("How to Reset Arduino Programmatically");
  delay(200);
}

void loop() 
{ 
        
  Serial.println("A");
  delay(1000);               
  Serial.println("B");
  delay(1000);               
  Serial.println("Now we are Resetting Arduino Programmatically");
  Serial.println();
  delay(1000);
  resetFunc();  //相当于强制使得程序跳转到初始位置开始执行
  Serial.println("Arrduino will never reach there.");
}

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章