假设您使用无线串口控制小车,那么你需要设计一个串口指令协议,一般来说,通信协议的数据帧包括3部分,一是指令头,又叫做帧同步字;二是指令内容,也叫帧内容;三是校验码。
同步字是为了便于程序分析连续传输的数据中,帧与帧之间的间隔,帧内容包含了所有经过16进制编码的各种控制指令和数据,校验码通常采用累加和与异或算法,为了计算本数据帧是否正确。(工业环境中,所有数据传输系统都存在一定的误码干扰,校验字能有效判别数据传输是否正确,进而通过错误处理机制,有效避免错误动作)。
刚好前段时间写了个小车的控制程序,使用的是双路H桥来驱动电机的,不过不是控制履带式的此拆,稍微改动了下。
(注意,本程序仅适用于履带式小车,即左右电机正反转实现前后及转向,程序支持前后左右混控)
//********************************
// 遥控帧协议和结构
// FB|80|51|AA|FF|BB|FF|BY|BY|BY|BY|BY|BY|CR
// 00|01|02|03|04|05|06|07|08|09|10|11|12|13
// 14 byte
//FB 80 51 遥控帧同步字 00-02
//AA 前后控制量 128为中立位 森散枣 03
//FF 恒定为0xFF 04
//BB 左右控制量 128为中立位 05
//FF 恒定为0xFF 掘吵 06
//BY 备用控制指令 本程序为空 07-12
//CR 校验码,从00至12字节逐字节位异或 13
//********************************
// 遥测帧协议结构
// FB|80|61|AA|FF|BB|FF|BY|BY|BY|BY|BY|BY|CR
// 00|01|02|03|04|05|06|07|08|09|10|11|12|13
// 14 byte
// 重复遥控指令,除帧头外,其定义与遥控指令相同
#define BR 9600 //定义串口波特率
//遥控指令接收处理相关定义
#define TCDL 14 //定义遥控数据帧长度字节数
#define BUFF_LONG 30 //定义串口接收缓存的长度
byte BUFF[BUFF_LONG]//定义串口数据接收存储缓存
int BUFF_IDX = 0 //定义串口数据接收存储缓存的指针
//遥测指令发送处理相关定义
#define TMFQ 80 //定义遥测数据帧发送间隔为80ms
#define TMDL 14 //定义遥测数据帧长度字节数
byte TM_Data[TMDL] //定义遥测数据缓存
int TM_Index = 0 //定义遥测数据指针
unsigned long Time = 0//定义遥测发送计时器为0
//定义左、右电机与Arduino的驱动信号接口
//注意,这里用了4个PWM输出,部分Arduino板可能不支持,请查看手册
int Moto_Right_A = 3
int Moto_Right_B = 5
int Moto_Left_A = 6
int Moto_Left_B = 9
//数据接收指示灯相关定义
boolean IS_Blink = false //定义是否闪烁LED灯的变量
int LED = 13 //定义LED灯接口为13
//指令变量定义
byte RunPWM = 128 //定义前进、后退控制PWM指令变量
byte TurnPWM = 128 //定义左转、右转控制PWM指令变量
void setup()
{
pinMode(Moto_Right_A, OUTPUT)//定义输出
pinMode(Moto_Right_B, OUTPUT)//定义输出
pinMode(Moto_Left_A, OUTPUT)//定义输出
pinMode(Moto_Left_B, OUTPUT)//定义输出
pinMode(LED, OUTPUT)//定义LED管脚为数字输出
Port_Init() //初始化串口
Blink_LED(false)//关闭指示灯
}
void loop()
{
byte c
if (Serial.available() >0) //如果接收到串口数据
{
c = Serial.read() //读一个字节
Buff_AddChar(c) //塞进缓存进行存储、校验和解码
}
TM_Freq() //尝试按要求逐字节发送遥测数据
Driver_CAR(RunPWM, TurnPWM)//驱动左右电机正反转,实现运动控制
}
//驱动左右电机调速运行,实现左右正反转组合,实现控制
//前后、左右混控
void Driver_CAR(byte Run, byte Turn)
{
byte Run_L, Run_R //定义左右前进混合值
byte PWM_R_A, PWM_R_B, PWM_L_A, PWM_L_B// 定义4个信号的独立PWM
Run_L = Run - (128 - Turn) //将转向PWM混合到左路
Run_R = Run - (Turn - 128) //将转向PWM混合到右路
PWM_L_A = (255 / 128) * Run_L - 255 //左路混合值结算为左路A信号PWM
PWM_L_B = (-1 * 255 / 128) * Run_L + 255 //左路混合值结算为左路B信号PWM
PWM_R_A = (255 / 128) * Run_R - 255 //右路混合值结算为右路A信号PWM
PWM_R_B = (-1 * 255 / 128) * Run_R + 255 //右路混合值结算为右路B信号PWM
analogWrite(Moto_Right_A, PWM_R_A) //输出右路A信号
analogWrite(Moto_Right_B, PWM_R_B) //输出右路B信号
analogWrite(Moto_Left_A, PWM_L_A) //输出左路A信号
analogWrite(Moto_Left_B, PWM_L_B) //输出左路B信号
}
//********************************
//子程序 Port_Init()
//功能:初始化串口通信
void Port_Init()
{
BUFF_IDX = 0 //初始化串口缓存指针为0
TM_Data[0] = 0xFB //初始化遥测数据帧头
TM_Data[1] = 0x80 //初始化遥测数据帧头
TM_Data[2] = 0x61 //初始化遥测数据帧头
Serial.begin(BR) //定义波特率并启动串口
}
//********************************
// 子程序 Buff_AddChar(byte b)
// 功能:串口接收字节加入缓存
// 1、加入字节到缓存;
// 2、检测到帧头则尝试检测遥控帧;
// 3、管理缓存指针
void Buff_AddChar(byte b)
{
BUFF[BUFF_IDX] = b // 缓存[缓存指针] = 接收字节
if (BUFF_IDX >= 2)
{
if (BUFF[BUFF_IDX] == 0x51 &&BUFF[BUFF_IDX - 1] == 0x80 &&BUFF[BUFF_IDX - 2] == 0xFB) // 检测遥控帧头
{
if (BUFF_IDX == (TCDL - 1))
{
Buff_DCHK() // 检测校验码
}
BUFF_IDX = 0 // 缓存指针归零
return
}
}
BUFF_IDX++
if (BUFF_IDX >(BUFF_LONG - 1)) // 缓存指针越界
{
BUFF_IDX = 0 // 缓存指针归零
}
}
//********************************
//子程序 Buff_DCHK()
//功能:校验遥控帧校验字是否正确
void Buff_DCHK()
{
byte DCHK = 0x00
// 0xFB XOR 0x80 XOR 0x51
DCHK = BUFF[0]
DCHK = DCHK ^ BUFF[1]
DCHK = DCHK ^ BUFF[2]
int i
for (i = 0i <(TCDL - 4)i++)
{
DCHK = DCHK ^ BUFF[i]
}
if (DCHK == BUFF[TCDL - 4])
{
//校验字正确
Blink_LED(true)//闪烁LED
Get_CMD() //解码指令
}
}
//********************************
// 子程序 Get_CMD()
// 功能:解码遥控指令
void Get_CMD()
{
RunPWM = BUFF[0]
TurnPWM = BUFF[2]
//后面可以自己添加关于BY指令的解码处理
}
//********************************
//子程序 TM_Freq()
//功能:遥测数据发送帧率控制
void TM_Freq()
{
unsigned long t
if (TM_Index <TMDL) //如果一帧数据还没有发完
{
Serial.write(TM_Data[TM_Index])//继续发数据
TM_Index++ //指针累加
}
else //如果发完
{
if (Time == 0) //检测计时器是否为0
{
Time = millis()//是则赋予计时器初始值
}
else //不是
{
t = millis()
if (t - Time >= TMFQ) //检测计时器是否到达帧率控制的时间
{
Time = t
TM_Make_Data() //到达发送时间,则组一帧遥测数据,并开始发送
}
}
}
}
//********************************
// 子程序 TM_Make_Data()
// 功能:发送遥测数据
void TM_Make_Data()
{
// 遥测帧协议结构
//FB|80|61|AA|FF|BB|FF|BY|BY|BY|BY|BY|BY|CR
//00|01|02|03|04|05|06|07|08|09|10|11|12|13
byte CHK = 0xFB //赋值帧头
CHK = CHK ^ 0x80//赋值帧头
CHK = CHK ^ 0x61//赋值帧头
TM_Data[3] = RunPWM //回写前后指令
TM_Data[4] = 0xFF
CHK = CHK ^ TM_Data[3]//计算校验码
CHK = CHK ^ TM_Data[4]//计算校验码
TM_Data[5] = TurnPWM //回写转向指令
TM_Data[6] = 0xFF
CHK = CHK ^ TM_Data[5]//计算校验码
CHK = CHK ^ TM_Data[6]//计算校验码
CHK = CHK ^ TM_Data[7]//计算校验码
CHK = CHK ^ TM_Data[8]//计算校验码
CHK = CHK ^ TM_Data[9]//计算校验码
CHK = CHK ^ TM_Data[10]//计算校验码
CHK = CHK ^ TM_Data[11]//计算校验码
CHK = CHK ^ TM_Data[12]//计算校验码
TM_Data[13] = CHK//计算校验码
TM_Index = 0//清除遥测发送指针位置
}
//********************************
//子程序 Blink_LED()
//功能:闪烁内置LED灯
void Blink_LED(boolean Blink)
{
if (Blink == false)
{
IS_Blink = false
}
else
{
IS_Blink = !IS_Blink
}
digitalWrite(LED, IS_Blink)
}
SD卡体积小,价格便宜,因此在许多工业数据记录和家用电子产品中有越来越多的应用。Arduino可以通过SPI接口与之通信,进行诸如建立文件、删除文件、向文件中添加内容、修改文件等 *** 作,这样采用Arduino配合SD卡可以开发数据记录设备。Arduino与SD卡的简单连接,只需要6只电阻和1张SD卡,通过软件模拟的方式实现SPI接口,Arduino与SD卡连接电路如图2所示。
2.gif
图2 Arduino与SD卡连接电路
由于SD卡的 *** 作电压为3.3 V,而Arduino的逻辑电压为5 V,因此需要用起分压作用的电阻(本文采用了Josh Adams书中的验证性电路),在实际的应用中最好采用分压模块以保证卡和Arduino板的安全。之后在网址http://code.google.com/p/sdfatlib/下载Bill Greiman开发的SdFat.h和SdFatUtil.h头文件和库文件,并安装到Arduino安装目录中的库文件夹中就可以使用了。简化程序如下:
#include
#include
Sd2Card card
SdVolume volume
SdFile root,file
void writeString(SdFile&f,char * str){
Uint8_t n
for(n=0,str[n]n++)
F.write((uint8_t*) str,n)
}
void setup(){
card.init(SPI_HALF_SPEED)
root.openRoot(&volume)
File.open(&root,“testfile.txt”,0_CREAT|0_EXCL|0_WRITE)斗仔庆
File.timestamp(2,2011,11,11,25.12.34.56)
writeString(file,“something you want ”)
File.cose()
}
void loop(){
}
将该程序下载到Arduino主控板内即可向SD卡内建立一个新的文件,并写入“something you want ”字符,当然也可以根据需要写入想要记录的信息。
程序的开头包含了两戚桥个头文件:SdFat.h和SdFatUtil.h。这两个头文件定义了一些 *** 作SD卡必需的类。之后建立4个有关 *** 作SD卡的对象。然后自定义了一个向SD卡内写一个字符串的函数,这个函数需要一个文件的引用和一个字符串作为参数。setup函数是Arduino软件项目中必须有的,做一些运行的初始化工作。这个实例程序由于只是完成简单的写文件 *** 作,因此对文件的写 *** 作在这个函数中完成。函数依次完成了设定SD卡的通信模式为SPI模式、打开卡的根目录、建立文件名为testfile.txt的文本文件、给文件添加时间信息、向文件内写入文件内容、关闭文件。从以上的过程中看,采用Arduino *** 作SD卡上的文件与采用C语言 *** 作PC上的文件十分相似,这也是空握Arduino易用性的体现,再一次验证了采用Arduino开发电子互动产品的方便性。
1、首先,请按照下图连接双路H桥驱动器和电机,4个按钮(右前进,右后退,左前进,左后退),以及核心板。
需要说明的是,双路H桥驱动器Vin和GND管脚,是接入驱动电机的电源的管脚,建议单独用一组负责动力的电池或电源,不要与单片机的供电混接,以防止大功率消耗瞬时拉低电压而死机。
完成好接线后,请在Arduino IDE中输入如下代码:(已提供源码下载)
int R_Q = 8//定义右前按钮管脚
int R_H = 4//定义右后按钮管脚
int L_Q = 7 //定义左前按钮管脚
int L_H = 2 基伍//定义左后按钮管脚
int MOTO_A_a = 3 //定义右电机控制端a
int MOTO_A_b = 5 //定义右电机控制端b
int MOTO_B_a = 6 //定义左电机控制端a
int MOTO_B_b = 9 //定义左电机控制端b
//需要注意的是,双路H桥驱动器支持PWM方式输入,故In1-In4接3,5,6,9端口,便于以后改造为PWM信号输入
void setup()
{
pinMode(R_Q,INPUT_PULLUP) //定义右前按钮为输入且上拉
pinMode(R_H,INPUT_PULLUP) //定义右后按钮为输入且上拉
pinMode(L_Q,INPUT_PULLUP) //定义左前按钮为输入且上拉
pinMode(L_H,INPUT_PULLUP) //定义左后按钮为输入且上拉
pinMode(MOTO_A_a,OUTPUT) //定义输出
pinMode(MOTO_A_b,OUTPUT) //定义输出神锋灶
pinMode(MOTO_B_a,OUTPUT) //定义输出
pinMode(MOTO_B_b,OUTPUT) //定义输出
}
void loop()
{
//如果不按下右前和右后,则停转
if(digitalRead(R_Q)==1 &&游扮 digitalRead(R_H)==1){STOP_Moto_A()}
//如果同时按下右前和右后,逻辑错误,则停转
if(digitalRead(R_Q)==0 &&digitalRead(R_H)==0){STOP_Moto_A()}
//如果按下右后,则向后转
if(digitalRead(R_Q)==1 &&digitalRead(R_H)==0){Driver_Moto_A(255,false)}
//如果按下右前,则向前转
if(digitalRead(R_Q)==0 &&digitalRead(R_H)==1){Driver_Moto_A(255,true)}
//如果不按下左前和左后,则停转
if(digitalRead(L_Q)==1 &&digitalRead(L_H)==1){STOP_Moto_B()}
//如果同时按下左前和左后,逻辑错误,则停转
if(digitalRead(L_Q)==0 &&digitalRead(L_H)==0){STOP_Moto_B()}
//如果按下左后,则向后转
if(digitalRead(L_Q)==1 &&digitalRead(L_H)==0){Driver_Moto_B(255,false)}
//如果按下左前,则向前转
if(digitalRead(L_Q)==0 &&digitalRead(L_H)==1){Driver_Moto_B(255,true)}
}
//驱动右边电机,pwm为能量值,对应转速,Is_Forward为转向
void Driver_Moto_A(int pwm, bool Is_Forward)
{
if(Is_Forward)
{
digitalWrite(MOTO_A_a,LOW)
analogWrite(MOTO_A_b,pwm)
}
else
{
digitalWrite(MOTO_A_b,LOW)
analogWrite(MOTO_A_a,pwm)
}
}
//停止右边电机
void STOP_Moto_A()
{
digitalWrite(MOTO_A_a,LOW)
digitalWrite(MOTO_A_b,LOW)
}
//驱动左边电机,pwm为能量值,对应转速,Is_Forward为转向
void Driver_Moto_B(int pwm, bool Is_Forward)
{
if(Is_Forward)
{
digitalWrite(MOTO_B_a,LOW)
analogWrite(MOTO_B_b,pwm)
}
else
{
digitalWrite(MOTO_B_b,LOW)
analogWrite(MOTO_B_a,pwm)
}
}
//停止左边电机
void STOP_Moto_B()
{
digitalWrite(MOTO_B_a,LOW)
digitalWrite(MOTO_B_b,LOW)
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)