Arduino
Author: Andrew.Du
基础
基础语法:
setup()
loop()
pinMode(引脚,模式)
pinMode(13,OUTPUT);设置13号引脚为输出
//在使用输入或输出功能前,你需要先通过pinMode() 函数配置引脚的模式为输入模式或输出模式。
---
digitalWrite(引脚,HIGH/LOW) 把引脚电平拉高拉低
digitalWrite() 让其输出高电平或者是低电平
digitalRead() 函数读取外部输入的数字信号
int value = digitalRead(pin);
analogWrite函数 //analogWrite(引脚,0-255) 引脚电平,如控制led亮度,马达转速
中断:
对中断引脚进行初始化配置:
setup(){
attachInterrupt(interrupt,function,mode);
// (引脚,函数,模式low/high/change);
}
//当触发中断引脚符合模式时,loop转去执行中断处理函数,执行完毕返回loop
//比如,attachInterrupt(2,fun,change)
//即:2号发生改变,fun执行
串口通信:
Serial.begin(9600);设置波特率
Serial.println("hello world");在串口监视器中打印
Serial.available() 返回当前缓冲区中接受到的字节数
练习使用
数字I/O的使用
流水灯:
比如引脚2-7流水灯:
void setup(){
//初始化I/O口
for(int i = 2;i<8;i++){
pinMode(i,OUTPUT);
}
}
void loop(){
//从引脚2到引脚6,逐个点亮LED,在熄灭
for(int i = 2;i<6;i++){
digitalWrite(i,HIGH);
delay(1000);
digitalWrite(i,LOW);
delay(1000);
}
//引脚7到3,逐个点亮,再熄灭
for(int i = 7;i<=3;i--){
digitalWrite(i,HIGH);
delay(1000);
digitalWrite(i,LOW);
delay(1000);
}
}
按键控制LED:
按键2号,LED 13号;
int buttonPin = 2;
int ledPin = 13;
int buttonState = 0;
void setup(){
pinMode(buttonPin,INPUT);
pinMode(ledPin,OUTPUT);
}
void loop(){
buttonState = digitalRead(buttonPin);
//HIGH说明按键被按下
if(buttonState == HIGH){
digitalWrite(ledPin,HIGH);
}else{
digitalWrite(ledPin,LOW);
}
}
进一步,按下按钮,点亮LED,再按一下,熄灭LED
boolean ledstate = false;
boolean buttonState = true;
void setup(){
pinMode(buttonpin,INPUT_PULLUP);
pinMode(ledpin,OUTPUT);
}
void loop(){
while(digitalRead(buttonPin) == HIGH){
if(ledState == ture){
digitalWrite(ledPin,LOW);
ledState = !ledState;
}else{
digitalWrite(ledPin,HIGH);
ledState = !ledState;
}
delay(50);
}
}
模拟I/O的使用:
Arduino 有模拟0—模拟5 共计6 个模拟接口,
这6 个接口也可以算作为接口功能复用,
除模拟接口功能以外,这6 个接口可作为数字接口使用,编号为数字14—数字19
analogRead()读
analogWrite()写
传感器控制的练习
// 模拟输入analogRead()函数的返回值范围是0 到1023,
//而模拟输出analogWrite()函数的输出值范围是0 到255
//注意除以4
温度传感器
int potpin = 0;//模拟接口0,连接温度传感器
void setup(){
Serial.begin(9600);
}
void loop(){
int val;
int det;
val = analogRead(potpin);//读取模拟值
det = (125 * val) >>8; //将模拟值转换为温度
Serial.print("TEP:");
Serial.print(det);
Serial.println("C");
delay(500);
}
舵机控制
// 一种是通过Arduino 的普通数字传感器接口产生占空比不同的方波,
//模拟拟产生PWM 信号进行舵机定位,
//第二种是直接利用Arduino 自带的Servo 函数进行舵机的控制,
//这种控制方法的优点在于程序编写,缺点是只能控制2 路舵机,
//因为Arduino 自带函数只能利用数字9、10 接口
方法一:
int servopin = 9; //数字接口9连接舵机信号线
int myangle;
int pulsewidth;
int val;
//脉冲函数
void servopulse(int servopin,int myangle){
pulsewidth = (myangle *11) +500; //角度0-180转化为500 - 2480的脉冲
digitalWrite(servopin,HIGH); //舵机电平升高
delayMicroseconds(pulsewidth);//延时脉宽值的微妙数
degitalWrite(servopin,LOW); //拉低电平
delay(20-pulsewidth/1000);
}
void setup(){
pinMode(servopin,OUTPUT);
Serial.begin(9600);
Serial.println("--");
}
void loop(){ //将0-9的数转换为0-180的角度
//读取控制输入,这里做成接口,从客户端传进参数
val = Serial.read();
if(val>'0'&&val<='9'){
val = val-‘0’;
val = val *(180/9); //数字转角度
Serial.print("moving to");
Serial.print(val.DEC);//转换为十进制
Serial.println();
for(int i = 0;i<=50;i++){
//给予舵机时间,让他转动
servopulse(servopin,val);
}
}
}
方法二:
首先:Servo函数学习:
1.attach(接口):设定舵机的接口,只有数字串口9和10可以用
2.write(角度); 用于设定舵机旋转角度的语句,可以设定的角度是0-180
3.read(角度);用于读取舵机角度
4.attached 判断舵机参数是否到达已发送到舵机所在的接口
5.detach() 使得舵机与接口分离,该接口可以继续用作PWM接口
#include <Servo.h> //要注意在#include 与<Servo.h>之间要有空格,否则编译时会报错
Servo myservo;
int getServoAngle(){
//得到输入
int angle = ..
return angle;
}
void setup(){
myservo.attach(9); //连接接口
}
void loop(){
int angle = getServoAngle();
if(angle>0 && angle <=180){
myservo.write(angle);
}
}
小车控制练习
实现小车方向控制、舵机控制、巡线、超声波避障模式等 ---
#include <Servo.h>
#include <EEPROM.h>
int ledpin = A0; //启动指示灯,A0接内置指示灯
//四个马达接口.2是右前,4是左前,1是左后,3是右后
/**
与单片机相连分别控制四个输出端,
输出端口连接电机,控制电机的正反转
HIGH/LOW
*/
int INPUT2 = 7; //左前
int INPUT1 = 8; //左后
int INPUT3 = 12; //右后
int INPUT4 = 13; //右后
int adjust = 1; //电机标志位
int Left_Speed_Hold = 255; //左侧速度
int Right_Speed_Hold = 255; //右侧速度
/**
使能端口连接单片机PWM端口
可以通过调节PWM来调节使能端口的电压,
从而调节电机输出端口的电压,达到调速的目的
*/
int ENB = 6; //L298使能B
int ENA = 5; //L298使能A
int Echo = A5; // 定义超声波信号接收脚位
int Trig = A4; // 定义超声波信号发射脚位
int Input_Detect_LEFT = A3; //定义小车左侧红外
int Input_Detect_RIGHT = A2; //定义小车右侧红外
int Input_Detect = A1; //定义小车前方红外
int Carled = A0; //定义小车车灯接口
int Cruising_Flag = 0; //模式切换标志
int Pre_Cruising_Flag = 0 ; //记录上次模式
Servo servo1; // 创建舵机#1号
Servo servo2; // 创建舵机#2号
Servo servo3; // 创建舵机#3号
Servo servo4; // 创建舵机#4号
//Servo servo5; // 创建舵机#5号
//Servo servo6; // 创建舵机#6号
Servo servo7; // 创建舵机#7号
Servo servo8; // 创建舵机#8号
byte angle1 = 70; //舵机#1初始值
byte angle2 = 60; //舵机#2初始值
byte angle3 = 60; //舵机#3初始值
byte angle4 = 60; //舵机#4初始值
//byte angle5 = 60; //舵机#5初始值
//byte angle6 = 60; //舵机#6初始值
byte angle7 = 60; //舵机#7初始值
byte angle8 = 60; //舵机#8初始值
int buffer[3]; //串口接收数据缓存
int rec_flag; //串口接收标志位
int serial_data; //串口数据零时存储
unsigned long Costtime; //串口超时计数
int IR_R; //巡线右侧红外状态标志
int IR_L; //巡线左侧红外状态标志
int IR; //中间红外状态标志
#define MOTOR_GO_FORWARD {digitalWrite(INPUT1,LOW);digitalWrite(INPUT2,HIGH);digitalWrite(INPUT3,LOW);digitalWrite(INPUT4,HIGH);}
#define MOTOR_GO_BACK {digitalWrite(INPUT1,HIGH);digitalWrite(INPUT2,LOW);digitalWrite(INPUT3,HIGH);digitalWrite(INPUT4,LOW);} //车体后退
#define MOTOR_GO_RIGHT {digitalWrite(INPUT1,HIGH);digitalWrite(INPUT2,LOW);digitalWrite(INPUT3,LOW);digitalWrite(INPUT4,HIGH);} //车体右转
#define MOTOR_GO_LEFT {digitalWrite(INPUT1,LOW);digitalWrite(INPUT2,HIGH);digitalWrite(INPUT3,HIGH);digitalWrite(INPUT4,LOW);} //车体左转
#define MOTOR_GO_STOP {digitalWrite(INPUT1,LOW);digitalWrite(INPUT2,LOW);digitalWrite(INPUT3,LOW);digitalWrite(INPUT4,LOW);} //车体停止
/**
延迟50秒等待WIFI模块启动完毕
*/
void Delayed() {
int i;
for (i = 0; i < 20; i++) {
digitalWrite(ledpin, LOW);
delay(1000);
digitalWrite(ledpin, HIGH);
delay(1000);
}
//加快闪烁
for (i = 0; i < 10; i++) {
digitalWrite(ledpin, LOW);
delay(500);
digitalWrite(ledpin, HIGH);
delay(500);
digitalWrite(ledpin, HIGH);
}
//熄灭
digitalWrite(ledpin, LOW);
MOTOR_GO_STOP;
}
/**
串口初始化函数
*/
void USART_init() {
int BAUD = 9600;
SREG = 0x80; //开启总中断
//bitSet(UCSR0A,U2X0);
bitSet(UCSR0B, RXCIE0); //允许接收完成中断//
bitSet(UCSR0B, RXEN0); //开启接收功能//
bitSet(UCSR0B, TXEN0); //开启发送功能//
bitSet(UCSR0C, UCSZ01);
bitSet(UCSR0C, UCSZ00); //设置异步通信,无奇偶校验,1个终止位,8位数据
UBRR0 = (F_CPU / 16 / BAUD - 1); //波特率9600
}
/***************************************************
功能工具
* ************************************************
*/
void Sendbyte(char c) {
//检查UCSROA的UDREO位是否置位 头文件里定义
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
}
/**
车灯控制
*/
void Open_Light()//开大灯
{
digitalWrite(Carled, HIGH); //拉低电平,正极接电源,负极接Io口
delay(1000);
}
void Close_Light()//关大灯
{
digitalWrite(Carled, LOW); //拉低电平,正极接电源,负极接Io口
delay(1000);
}
/**
串口命令解码
*/
void Communication_Decode() {
if (buffer[0] == 0x00) { //0x00是控制电机命令
switch (buffer[1]) { //电机命令
case 0x01: MOTOR_GO_FORWARD; return;
case 0x02: MOTOR_GO_BACK; return;
case 0x03: MOTOR_GO_LEFT; return;
case 0x04: MOTOR_GO_RIGHT; return;
case 0x00: MOTOR_GO_STOP; return;
default: return;
}
}
else if (buffer[0] == 0x01) { //0x01是舵机命令
if (buffer[2] > 170) return;
switch (buffer[1]) {
case 0x01: angle1 = buffer[2]; servo1.write(angle1); return;
case 0x02: angle2 = buffer[2]; servo2.write(angle2); return;
case 0x03: angle3 = buffer[2]; servo3.write(angle3); return;
case 0x04: angle4 = buffer[2]; servo4.write(angle4); return;
case 0x07: angle7 = buffer[2]; servo7.write(angle7); return;
case 0x08: angle8 = buffer[2]; servo8.write(angle8); return;
default: return;
}
}
else if (buffer[0] == 0x02) { //调速指令
if (buffer[2] > 100) return;
if (buffer[1] == 0x01) { //左侧
Left_Speed_Hold = buffer[2] * 2 + 55; //0-100转换为PWM速度55-255;低于55电机不转
analogWrite(ENB, Left_Speed_Hold);
EEPROM.write(0x09, Left_Speed_Hold); //记录速度、持久化
}
else if (buffer[1] == 0x02 ) {
Right_Speed_Hold = buffer[2] * 2 + 55; //速度档位是0~100 换算成pwm 速度pwm低于55电机不转
analogWrite(ENA, Right_Speed_Hold);
EEPROM.write(0x0A, Right_Speed_Hold); //存储速度
} else return;
}
else if (buffer[0] == 0x33) { //初始化舵机角度值命令
Init_Steer();
return;
}
else if (buffer[0] == 0x32) { //保存命令指令,锁定舵机角度
EEPROM.write(0x01, angle1);
EEPROM.write(0x02, angle2);
EEPROM.write(0x03, angle3);
EEPROM.write(0x04, angle4);
EEPROM.write(0x07, angle7);
EEPROM.write(0x08, angle8);
return;
}
else if (buffer[0] == 0x13) { //模式切换开关
switch (buffer[1]) {
case 0x02: Cruising_Flag = 2; return; //巡线模式
case 0x03: Cruising_Flag = 3; return; //避障模式
case 0x04: Cruising_Flag = 4; return; //雷达避障
case 0x05: Cruising_Flag = 5; return; //超声波距离PC端显示
case 0x00: Cruising_Flag = 0; return; //正常模式
default: Cruising_Flag = 0; return; //正常模式
}
}
else if (buffer[0] == 0x04) //开车灯指令为FF040000FF,关车灯指令为FF040100FF
{
switch (buffer[1])
{
case 0x00: Open_Light(); return; //开车灯
case 0x01: Close_Light(); return; //关车灯
default: return;
}
}
else if (buffer[0] == 0x40) //存储电机标志
{
adjust = buffer[1];
EEPROM.write(0x10, adjust);
}
}
/**
读取串口命令
*/
void Get_uartdata(void)
{
static int i;
serial_data = UDR0;//读取串口
if (rec_flag == 0)
{
if (serial_data == 0xff)//第一次获取到0xff(即包头)
{
rec_flag = 1;
i = 0;
Costtime = 0;
}
}
else
{
if (serial_data == 0xff)//第二次获取到0xff(即包尾)
{
rec_flag = 0;
if (i == 3)//获取到中间数据为3个字节,说明此命令格式正确
{
Communication_Decode();//执行命令解析函数
}
i = 0;
}
else
{
buffer[i] = serial_data;//暂存数据
i++;
}
}
}
ISR(USART_RX_vect)
{
UCSR0B &= ~(1 << RXCIE0); //关闭串口中断
Get_uartdata();
UCSR0B |= (1 << RXCIE0); //打开串口中断
}
/*****************************************************/
/**
舵机初始化,速度初始化
*/
void Init_Steer() {
//angle1 = EEPROM.read(ox01);
angle7 = EEPROM.read(0x07);//读取寄存器0x07里面的值
angle8 = EEPROM.read(0x08);//读取寄存器0x08里面的值
if (angle7 == 255 && angle8 == 255)
{
EEPROM.write(0x01, 90); //把初始角度存入地址0x01里面
EEPROM.write(0x02, 90); //把初始角度存入地址0x02里面
EEPROM.write(0x03, 90); //把初始角度存入地址0x03里面
EEPROM.write(0x04, 90); //把初始角度存入地址0x04里面
//EEPROM.write(0x05,60);//把初始角度存入地址0x05里面
//EEPROM.write(0x06,120);//把初始角度存入地址0x06里面
EEPROM.write(0x07, 90); //把初始角度存入地址0x07里面
EEPROM.write(0x08, 90); //把初始角度存入地址0x08里面
return;
}
servo7.write(angle7);//把保存角度赋值给舵机7
servo8.write(angle8);//把保存角度赋值给舵机8
adjust = EEPROM.read(0x10);//电机标志位放在0x10的位置
if (adjust == 255) { //从未写入时,是255
EEPROM.write(0x10, 1);
}
Left_Speed_Hold = EEPROM.read(0x09); //oxo9是左侧速度
Right_Speed_Hold = EEPROM.read(0x0A); //0x0A是右侧速度
if ((Left_Speed_Hold < 55) | ( Right_Speed_Hold < 55)) {
Left_Speed_Hold = 255;
Right_Speed_Hold = 255;
}
/**
调节使能端口的PWM输入,能够控制电机转速
*/
analogWrite(ENB, Left_Speed_Hold); //给L298使能端B赋值
analogWrite(ENA, Right_Speed_Hold); //给L298使能端A赋值
MOTOR_GO_STOP;
}
/**
串口超时检测
*/
void UartTimeoutCheck(void) {
if (rec_flag == 1) {
Costtime++;
if (Costtime == 100000) {
rec_flag = 0;
}
}
}
/*
通过校准值校准小车方向
*/
void forward(int adjust)
{
//adjust是电机标志位
switch (adjust)
{
case 1: MOTOR_GO_FORWARD; return;
case 2: MOTOR_GO_FORWARD; return;
case 3: MOTOR_GO_BACK; return;
case 4: MOTOR_GO_BACK; return;
case 5: MOTOR_GO_LEFT; return;
case 6: MOTOR_GO_LEFT; return;
case 7: MOTOR_GO_RIGHT; return;
case 8: MOTOR_GO_RIGHT; return;
default: return;
}
}
/*
通过校准值校准小车方向
*/
void back(int adjust)
{
switch (adjust)
{
case 1: MOTOR_GO_BACK; return;
case 2: MOTOR_GO_BACK; return;
case 3: MOTOR_GO_FORWARD; return;
case 4: MOTOR_GO_FORWARD; return;
case 5: MOTOR_GO_RIGHT; return;
case 6: MOTOR_GO_RIGHT; return;
case 7: MOTOR_GO_LEFT; return;
case 8: MOTOR_GO_LEFT; return;
default: return;
}
}
/*
通过校准值校准小车方向
*/
void left(int adjust)
{
switch (adjust)
{
case 1: MOTOR_GO_LEFT; return;
case 2: MOTOR_GO_RIGHT; return;
case 3: MOTOR_GO_LEFT; return;
case 4: MOTOR_GO_RIGHT; return;
case 5: MOTOR_GO_FORWARD; return;
case 6: MOTOR_GO_BACK; return;
case 7: MOTOR_GO_FORWARD; return;
case 8: MOTOR_GO_BACK; return;
default: return;
}
}
/*
通过校准值校准小车方向
*/
void right(int adjust)
{
switch (adjust)
{
case 1: MOTOR_GO_RIGHT; return;
case 2: MOTOR_GO_LEFT; return;
case 3: MOTOR_GO_RIGHT; return;
case 4: MOTOR_GO_LEFT; return;
case 5: MOTOR_GO_BACK; return;
case 6: MOTOR_GO_FORWARD; return;
case 7: MOTOR_GO_BACK; return;
case 8: MOTOR_GO_FORWARD; return;
default: return;
}
}
/**
巡线模式
*/
void TrackLine() {
IR_L = digitalRead(Input_Detect_LEFT); //读取左边传感器数值
IR_R = digitalRead(Input_Detect_RIGHT);//右边红外传感器数值,巡线为LOW
if ((IR_L == LOW) && (IR_R == LOW)) //两边同时探测到路线
{
forward(adjust); //前进
return;
}
if ((IR_L == LOW) && (IR_R == HIGH)) {
left(adjust); //左转
return;
}
if ((IR_L == HIGH) && (IR_R == LOW)) {
right(adjust); //右转
return;
}
if ((IR_L == HIGH) && (IR_R == HIGH)) {
MOTOR_GO_STOP; //停止
return;
}
}
/**
红外线避障
*/
void Avoiding() {
//IR是中间红外状态标志、Input_Detect定义红外接口
IR = digitalRead(Input_Detect);//如果有障碍物,就返回LOW,否则HIGH(1)
if ((IR == LOW)) {
MOTOR_GO_STOP;
}
}
/**
得到距离
Trig是超声波发射位
Echo是超声波接收位
*/
char Get_Distance() {
digitalWrite(Trig, LOW); //超声波发射低电压 2us
delayMicroseconds(2);
digitalWrite(Trig, HIGH); //超声波发射高电压10us 注意至少10us
delayMicroseconds(10);
digitalWrite(Trig, LOW); //维持超声波发射低电压
float Ldistance = pulseIn(Echo, HIGH, 5000); //读取相差时间,超声波发射到接收的时间
Ldistance = Ldistance / 58; //时间转换为cm
return Ldistance;
}
/**
Send_Distance
向上位机发送超声波数据,超声波距离PC端显示
数据格式:0xFF,0x03,角度(默认0x00),距离(dis),0xFF
*/
void Send_Distance() {
int dis = Get_Distance();
Sendbyte(0xff);
Sendbyte(0x03);
Sendbyte(0x00);
Sendbyte(dis);
Sendbyte(0xff);
delay(1000);
}
/**
超声波避障,当超声波检测距离小于distance厘米时,停止。
*/
void AvoidByRadar(int distance) {
int leng = Get_Distance();
if (distance < 10) distance = 10; //限定,最小避障距离10cm
if ((leng > 1 ) && (leng < distance)) {
while ((Get_Distance() > 1) && (Get_Distance() < distance)) {
back(adjust);
}
MOTOR_GO_STOP;
}
}
/**
模式功能切换函数
*/
void Cruising_Mod() {
if (Pre_Cruising_Flag != Cruising_Flag) {
if (Pre_Cruising_Flag != 0) {
MOTOR_GO_STOP;
}
Pre_Cruising_Flag = Cruising_Flag;
}
switch (Cruising_Flag) {
case 2: TrackLine(); return; //2是巡线模式
case 3: Avoiding(); return;//红外线避障模式
case 4: AvoidByRadar(15); return; //超声波避障模式
case 5: Send_Distance(); return; //超声波距离 PC端显示
default: return;
}
}
void setup() {
// put your setup code here, to run once:
pinMode(ledpin, OUTPUT);
pinMode(INPUT1, OUTPUT);
pinMode(INPUT2, OUTPUT);
pinMode(INPUT3, OUTPUT);
pinMode(INPUT4, OUTPUT);
pinMode(Trig, OUTPUT);
pinMode(Echo, INPUT);
Delayed();
servo7.attach(9); //定义舵机7控制口,上下控制
servo8.attach(10); //定义舵机8控制口,左右控制
USART_init(); //串口初始化、开启中断等
Init_Steer(); //舵机角度、电机速度的初始化
}
void loop() {
// put your main code here, to run repeatedly:
while (1) {
UartTimeoutCheck();
Cruising_Mod();
}
}