【单片机】Arduino进阶应用

前期教程

前言

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

一、丰富的库文件

  Arduino的一个比较强大的功能在于它搭载了非常丰富的封装好的库文件,有一些是安装软件时自带的,有的可以额外下载。
  首先,我们如何找到这些库文件? 有两个地方可以找到:一个是安装目录下的libraries文件夹,这里面是安装软件时自带的;还有一个是文档中的Arduino文件夹下的libraries文件夹,这里面应该是额外下载的库文件的安装位置。
  那如何学习库文件呢? 我认为,首先是要对单片机的工作原理和Arduino片上外设有足够的了解,其次就是学习它内部的函数。此外,官方还提供了一个讲述libraries链接,时时翻阅。

1. 舵机库——Servo.h

  通过查看Servo.h的源代码,可以发现舵机被定义为一个类,即Servo,舵机的相关的操作都是它里面的成员函数,因此使用这个库时,要先定义一个类,如Servo myservo,然后再使用myservo.xxxxx来调用类内的各成员函数。

  • attach()
      设定舵机控制信号输出引脚,官方文档写的是在Arduino0016以及更早的版本之前,这个设定引脚只支持9和10,至于这个Arduino0016是只IDE还是只芯片目前还不太清楚,但是可以确定的是UNO属于这个行列,但是Mega2560不属于。不属于的应该是可以定义任意支持PWM输出的引脚。
    语法
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);  //angle为输出的角度,0-180之间

  常用例程

#include <Servo.h> 
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 <SoftwareSerial.h>

#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文件夹,看源码和关键词。

  • begin()
    用来初始化IIC总线,使该设备作为主设备或者从设备加入到IIC总线中,一般只调用一次
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 <Wire.h>

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 <Wire.h>

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模块完全一样!

四、其他问题汇总

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

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

2. Arduino程序烧入不上去?

  有一个可以尝试的办法就是重新烧入Bootloader,即引导程序,方法就是插上USB线,然后点击工具->烧录引导程序
在这里插入图片描述
或者使用USBasp烧入,效果一样。

版权声明:本文为CSDN博主「记录无知岁月」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ZHOU_YONG915/article/details/120942298

生成海报
点赞 0

记录无知岁月

我还没有学会写个人说明!

暂无评论

相关推荐

Arduino UNO步进电机控制

1.开发环境:Arduino IDE 2.步进电机驱动器:雷赛DM860 3.单片机程序 //定义了板上的控制端DIR,8作为方向控制端 const int DIRPIN 2;//定义了板上的步数端

嵌入式体系结构复习笔记

寄存器表示C语言的对应关系 R0: 存储C语言函数返回值R14 : 存储C的函数返回地址R15 : 当前执行程序的代码地址 ARM的常用指令 将数据加载到寄存器:MOV/LDR子程序调用指令: BL软中断调用指

基于arduino的温度计

使用器件:arduino uno,LM35模块,LCD602模块,10k电位器,面包板,杜邦线 线路连接 LCD1602模块连接 VSS(电源地)GNDVD