//蓝牙遥控小车
//Arduino 源程序
//定稿日期:2016-3-16
//程序功能简介:
//程序采用软件PWM方式,控制两支直流电机的运行行为,实现直行、后退、左转和右转动作。
//操作者使用Android手机的蓝牙功能发出指令,操控小车动作。
//操作者还通过蓝牙对小车的动作参数进行调试。
//使用自定义串口收发数据
//使用软件PWM,输出引脚可任意制定
//使用Atmega48芯片
//Arduio 版本1.0.5
#include
#include "usart.h"
unsigned int counter; //PWM计数器
unsigned char wCnt = 0; //接收字计数
unsigned int pwm_LH; //左电机高电平计数
unsigned int pwm_RH; //右电机高电平计数
unsigned char lDirect; //左电机运转方向
unsigned char rDirect; //右电机运转方向
unsigned int LP = 0;
unsigned int RP = 0;
unsigned int LD = 0;
unsigned int RD = 0;
unsigned int PWM[6]; //存放当前PWM参数的整数型数组,全局变量
unsigned char inputString[8]; // 存输入数据字符串变量
boolean stringComplete = false; // 数据串结束标志
//定时器2初始化函数
{
cli();
TCCR2B = 0x00; //
TCNT2 = 0xF6; //
TCCR2A = 0x00;
TCCR2B = 0x02; //
TIMSK2 = 0x01; //定时器2中断允许
sei();
}
//定时器2中断服务函数
//PWM波形产生器
ISR(TIMER2_OVF_vect)
{
TCNT2 = 0xF6; //
counter++;
if(counter == 0x3ff)
{
if (rDirect == 1)
bitSet(PORTD,5);
else
bitSet(PORTD,4);
if (lDirect == 1)
bitSet(PORTD,7);
else
bitSet(PORTD,6);
counter = 0;
}
if(counter == pwm_RH)
{
bitClear(PORTD,4);
bitClear(PORTD,5);
}
if(counter == pwm_LH)
{
bitClear(PORTD,6);
bitClear(PORTD,7);
}
}
//电机运行函数
void Move(unsigned int LS,unsigned charLD,unsigned int RS,unsigned char RD)
{
asm("BCLR 7"); //关中断
pwm_LH = LS;
pwm_RH = RS;
lDirect = LD;
rDirect = RD;
asm("BSET 7"); //开中断
}
//获取EEPROM数据函数
//功能:从EEPROM里顺序读出六个PWM参数,存入PWM数组
void GetData()
{
unsigned char bytes[2]; //暂时存放PWM参数的字节数组,全局变量
unsigned char i;
unsigned char j;
unsigned char k;
for(i = 0;i < 6;i++) //for循环,读六个参数
{
for (j = 0;j < 2;j++) //内循环,每次读两个字节
{
k = i * 2 + 1 - j; //地址计算
bytes[j] = EEPROM.read(k); //EEPROM读操作
}
PWM = word(bytes[0], bytes[1]); //将读出的两个字节合成一个PWM整数数据
}
}
//数据发送函数
//功能:将一个整数拆分成四个ASCII代码,通过蓝牙串口发出的函数。
//例如:整数784,将拆分成;' ','7','8','4'四个字符
void Number(int val)
{
inttmp; //中间变量
unsigned char i; //循环计数变量
unsigned char buf[4]; //存字符数组
tmp= val / 1000;
buf[0] = tmp + 0x30; //获得千位
val= val % 1000;
tmp= val / 100;
buf[1] = tmp + 0x30; //获得百位
val= val % 100;
tmp= val / 10;
buf[2] = tmp + 0x30; //获得十位
val= val % 10;
buf[3] = val + 0x30; //获得个位
for(i = 0;i < 4;i++)
{
if (buf == 0x30) //从高位整理,如果是0,则转换成空格。
buf = 0x20;
else
break;
}
Usart_Transmit(buf[0]); //通过蓝牙串口连续发出四个字符。
Usart_Transmit(buf[1]);
Usart_Transmit(buf[2]);
Usart_Transmit(buf[3]);
}
void setup() {
timer2_init();
Usart_Init(9600);
sei();
PORTD = 0x00;
DDRD = 0xF0;
GetData(); //初始化PWM参数
}
void loop() {
unsigned char buf[6]; //存连续字符的数组
unsigned char index = 0; //存索引值变量
unsigned char i;
unsigned char k;
unsigned int para; //存PWM数据变量
delay(500);
if(stringComplete == true) //分解手机传过来的参数
{ //格式是:#n%dddd
k= 0; //其中:n 为索引(地址);dddd 为数据
index = 0;
for (i = 0;i <= wCnt ;i++)
{
if (inputString == '%')
{
index = inputString[i-1] - 0x30; //获得索引
k = 0;
}
else
{
buf[k] = inputString - 0x30; //获得数据
k++;
}
}
para = 0;
for (i = 0;i < k-1;i++)
para = para * 10 + buf;
PWM[index] = para; //将得到的整数参数立即存入对应的PWM数组单元,修改当前运行参数
buf[1] = lowByte(para); //将整数转换成两个字节。
buf[0] = highByte(para);
index = index * 2; //计算EEPROM地址
EEPROM.write(index, buf[1]); //写入EEPROM低位在前
index++;
EEPROM.write(index, buf[0]); //高位在后
stringComplete = false;
wCnt = 0;
}
}
ISR(USART_RX_vect)
{
unsigned char command=0;
inti;
command = UDR0; //接收数据
inputString[wCnt] = command;
wCnt++;
if(command == 13) {
stringComplete = true;
wCnt--;
}
else if (command == 10)
{
stringComplete = false;
wCnt = 0;
}
switch (command) {
case '@':
GetData(); //从EEPROM中读出数据
for (i = 0;i < 6;i++)
{
Number(PWM); //通过蓝牙回传,Android手机上会有显示
}
wCnt = 0;
break;
case '#': //得到手机发回的修改参数数据
break;
case 'w' : // 接收到 'w',前进
LP = PWM[0];
RP = PWM[1];
LD = 1;
RD = 1;
wCnt = 0;
break;
case 'x' : // 接收到 'x',后退
LP = PWM[0];
RP = PWM[1];
LD = 0;
RD = 0;
wCnt = 0;
break;
case 'a' : // 接收到 'a',左转
LP = PWM[2];
RP = PWM[3];
LD = 0;
RD = 1;
wCnt = 0;
break;
case 'd' : // 接收到 'd',右转
LP = PWM[4];
RP = PWM[5];
LD = 1;
RD = 0;
wCnt = 0;
break;
case 's' : // 接收到 's',停止电机
LP = 0;
RP = 0;
LD = 1;
RD = 1;
wCnt = 0;
break;
default:
LP = 0;
RP = 0;
LD = 1;
RD = 1;
}
Move(LP,LD,RP,RD);
}
感兴趣的朋友可以跟帖,我将又问必答。
|