【嵌入式】Arduino编程基础到应用全解析

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();
  }
}

关于控制Arduino小车这个项目,我会给出具体的实现方案以及全部源程序。以上。

原文地址:https://www.cnblogs.com/duye/p/8641072.html