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