之前出于好奇就入手了树莓派小车,某宝入手,费了挺大的劲才拼好的
首先,这车比想象中的大,也比想象中的还要难拼一点,非常锻炼动手能力。我当初的想法是想要稍微接触一点嵌入式的东西于是就决定用小车上手。
再来说说配置,除了外壳以外,我买的套餐包括4个直流电动机作为轮子的驱动,4个舵机的机械臂(不知道为什么机械臂动不了),一个720p摄像头,一块扩展板,电压表(就是有数字显示的那个小东西)还有一个蓝牙模块(我感觉没什么用就没装)和4000毫安的大电池(可以充电)。
那个扩展板真的很方便,而且板载了TB6612用来控制电机 和 pca9685(16路电机控制板)用来控制机械臂。TB6612网上的资料和例程都挺多的,pca9685就少的可怜了。对了,还有一个无源蜂鸣器,不过刚刚拼好就被我玩坏了,一开始不会用,运行一段程序之后树莓派卡死之后无源蜂鸣器就不能用了,本来还想用来演奏歌曲的。
总的来说的话,对于想要了解嵌入式的人来说的话还是很值得买的。那么话不多说,我就简单说一下我在上面完成的几个试验吧。
PS:我的树莓派是之前就买了的,自己之前也把树莓派的环境配置过了,比如安装中文输入法,安装vim进行一些设置来适应树莓派上的编程,修改apt源等等,这方面东西网上教程很多,所以遇到不会的直接百度就行。
扩展板自带了两个LED灯,电路图如下
GPIO就是树莓派上的物理接口,P21 和 P22就是使用wiringPi库的时候用到的编号。
#include
#include
#define LEDpin1 21 //LED的编号
#define LEDpin2 22 //LED的编号
int main(){
int stat = wiringPiSetup();
if(stat==-1) printf("init failly...\n");
else printf("init successful..\n");
pinMode(LEDpin1, OUTPUT); //设置管脚模式为输出电压
pinMode(LEDpin2, OUTPUT);
char input='1';
while(input!='q'&&input!='Q'){
system("stty -icanon"); //关闭缓冲区,无需回车就能直接接受
input = getchar();
switch(input){
case 'j':
digitalWrite(LEDpin1, 1);
printf("LED1 ON..\n");
break;
case 'k':
digitalWrite(LEDpin2, 1);
printf("LED2 ON..\n");
break;
case 'u':
digitalWrite(LEDpin1, 0);
printf("LED1 OFF..\n");
break;
case 'i':
digitalWrite(LEDpin2, 0);
printf("LED2 OFF..\n");
break;
default: break;
}
}
printf("demo end\n");
return 0;
}
稍微总结一下使用wiringPi库的步骤
首先初始化wiringPi库 wiringPiSetup()
然后设置pin的模式 pinMode()
最后控制针脚送高低电平 digitalWrite()
这些函数wiringPi的用户手册上都有,可以自己去网上查。
无源蜂鸣器内部没有振动源,需要外部频率刺激才能有动作。这里涉及到pwm的概念,简单的说pwm就是脉冲宽度调制,也就是说可以调整脉冲的占空比,通过调节占空比可以调节信号和能量的编号。我们只需要设置好特定的频率就可以让无源蜂鸣器演奏啦。
#include
#include
#include
#include
/*这个文件用来使用无源蜂鸣器来演奏音乐*/
typedef struct _TONE{
int freq; //频率
int t_ms; //持续时间
}TONE, *POINT;
#define voice 0 //蜂鸣器接口
#define ONESEC 1000/2
#define DO 2093 //1
#define RE 2349 //2
#define MI 2637 //3
#define FA 2794 //4
#define SO 3136 //5
#define LA 3520 //6
#define XI 3951 //7
#define DO1 4186 //8
#define RI1 4698 //9
//小星星
TONE star_notation[]={
{DO, ONESEC},
{DO, ONESEC},
{SO, ONESEC},
{SO, ONESEC},
{LA, ONESEC},
{LA, ONESEC},
{SO, ONESEC*2},
{FA, ONESEC},
{FA, ONESEC},
{MI, ONESEC},
{MI, ONESEC},
{RE, ONESEC},
{RE, ONESEC},
{DO, ONESEC*2},
{SO, ONESEC},
{SO, ONESEC},
{FA, ONESEC},
{FA, ONESEC},
{MI, ONESEC},
{MI, ONESEC},
{RE, ONESEC*2},
{SO, ONESEC},
{SO, ONESEC},
{FA, ONESEC},
{FA, ONESEC},
{MI, ONESEC},
{MI, ONESEC},
{RE, ONESEC*2},
{DO, ONESEC},
{DO, ONESEC},
{SO, ONESEC},
{SO, ONESEC},
{LA, ONESEC},
{LA, ONESEC},
{SO, ONESEC*2},
{FA, ONESEC},
{FA, ONESEC},
{MI, ONESEC},
{MI, ONESEC},
{RE, ONESEC},
{RE, ONESEC},
{DO, ONESEC*2}
};
//@param freq 频率
//@param t_ms 持续时间或者节拍
void beep(int freq, int t_ms){
int range;
if(freq<2000 || freq>5000){ //选择2000-5000的频率
printf("invalid freq\n");
return;
}
range=600000/freq; //range
pwmSetRange(range);
pwmWrite(voice, range/2); //设置占空比为50%
printf("singing...\n");
if(t_ms>0) delay(t_ms); //设置持续时间
}
//初始化函数
void init(){
if(wiringPiSetup()==-1){
printf("初始化失败\n");
exit(1);
}
pinMode(voice, PWM_OUTPUT); //选择引脚模式
pwmSetMode(PWM_MODE_MS); //设置PWM模式为MS模式
pwmSetClock(32); //设置时钟基础频率为19.2M/32=600KHZ
}
int main(){
int index=0;
init();
for(;index
这应该说是小车试验的核心了,毕竟能动起来才能叫小车嘛。
小车的每一个轮子都是由一个直流电动机驱动,板子上一共有两片TB6612
真不知道上面那片6612经历了什么ヽ( ̄▽ ̄)ノ。顺便pca9685也看到了。
这两片应该是并联的关系我们只需要驱动一片就行了。每个TB6612可以控制两个电机,根据我做的实验,我发现按照卖家的接口连法一片是控制前轮的一片是控制后轮的,然后AIN控制左轮,BIN控制右轮
我用下面的四个函数进行测试
//方向控制(just for test)
void run1(){
digitalWrite(AIN1, 1);
digitalWrite(AIN2, 0);
softPwmWrite(PWMA, 150);
printf("mode1: AIN1-H AIN2-L\n");
delay(3000);
softPwmWrite(PWMA, 0);
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 0);
}
void run2(){
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 1);
softPwmWrite(PWMA, 150);
printf("mode2: AIN1-L AIN2-H\n");
delay(3000);
softPwmWrite(PWMA, 0);
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 0);
}
void run3(){
digitalWrite(BIN1, 1);
digitalWrite(BIN2, 0);
softPwmWrite(PWMB, 150);
printf("mode3: BIN1-H BIN2-L\n");
delay(3000);
softPwmWrite(PWMB, 0);
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 0);
}
void run4(){
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 1);
softPwmWrite(PWMB, 150);
printf("mode4: BIN1-L BIN2-H\n");
delay(3000);
softPwmWrite(PWMB, 0);
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 0);
}
然后得出了运动模式对应的真值表
A1 A2 B1 B2
ahead 1 0 1 0
back 0 1 0 1
left 1 0 0 0
right 0 0 1 0
left_b 0 1 0 0
right_b 0 0 0 1
全部代码如下:
#include
#include
#include
#include
//定义接口宏
#define PWMA 1
#define AIN2 2
#define AIN1 3
#define PWMB 4
#define BIN1 6
#define BIN2 5
//初始化
void init(){
//初始化wiringPi库
if(wiringPiSetup()==-1)
printf("init unsuccessfully..\n");
else printf("init successfully..\n");
//设置阵脚模式
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(BIN1, OUTPUT);
pinMode(BIN2, OUTPUT);
//创建爱女软件控制PWM
//softPwmCreate(int pin, int initialValue, int pwmRange))
softPwmCreate(PWMA, 0, 255);
softPwmCreate(PWMB, 0, 255);
printf("soft pwm create successfully..\n");
}
//方向控制(just for test)
void run1(){
digitalWrite(AIN1, 1);
digitalWrite(AIN2, 0);
softPwmWrite(PWMA, 150);
printf("mode1: AIN1-H AIN2-L\n");
delay(3000);
softPwmWrite(PWMA, 0);
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 0);
}
void run2(){
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 1);
softPwmWrite(PWMA, 150);
printf("mode2: AIN1-L AIN2-H\n");
delay(3000);
softPwmWrite(PWMA, 0);
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 0);
}
void run3(){
digitalWrite(BIN1, 1);
digitalWrite(BIN2, 0);
softPwmWrite(PWMB, 150);
printf("mode3: BIN1-H BIN2-L\n");
delay(3000);
softPwmWrite(PWMB, 0);
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 0);
}
void run4(){
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 1);
softPwmWrite(PWMB, 150);
printf("mode4: BIN1-L BIN2-H\n");
delay(3000);
softPwmWrite(PWMB, 0);
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 0);
}
/* A1 A2 B1 B2
ahead 1 0 1 0
back 0 1 0 1
left 1 0 0 0
right 0 0 1 0
left_b0 1 0 0
right_b0 0 0 1
*/
//real control
//@param time time of duration
void go_ahead(int time){
softPwmWrite(PWMA, 150);
softPwmWrite(PWMB, 150);
digitalWrite(BIN1, 1);
digitalWrite(BIN2, 0);
digitalWrite(AIN1, 1);
digitalWrite(AIN2, 0);
printf("mode1:go ahead..H-L-H-L\n");
delay(time);
softPwmWrite(PWMA, 0);
softPwmWrite(PWMB, 0);
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 0);
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 0);
printf("mode1 end..\n");
}
//@param time time of duration
void go_back(int time){
softPwmWrite(PWMA, 150);
softPwmWrite(PWMB, 150);
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 1);
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 1);
printf("mode2:go back..L-H-L-H\n");
delay(time);
softPwmWrite(PWMA, 0);
softPwmWrite(PWMB, 0);
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 0);
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 0);
printf("mode2 end..\n");
}
//@param time time of duration
void turn_left(int time){
softPwmWrite(PWMB, 150);
digitalWrite(BIN1, 1);
digitalWrite(BIN2, 0);
printf("mode3:turn left..L-L-H-L\n");
delay(time);
softPwmWrite(PWMB, 0);
digitalWrite(BIN1, 0);
digitalWrite(BIN2, 0);
printf("mode3 end..\n");
}
//@param time time of duration
void turn_right(int time){
softPwmWrite(PWMA, 150);
digitalWrite(AIN1, 1);
digitalWrite(AIN2, 0);
printf("mode4:turn right..H-L-L-L\n");
delay(time);
softPwmWrite(PWMA, 0);
digitalWrite(BIN1, 1);
digitalWrite(AIN1, 0);
digitalWrite(AIN2, 0);
printf("mode4 end..\n");
}
void control(){
init();
char c='0';
int time=250;
while(c!='q'){
system("stty raw");
c=getchar();
system("stty -raw");
switch(c){
case 'w': go_ahead(time);delay(50); break;
case 's': go_back(time);delay(50); break;
case 'a': turn_left(time);delay(50); break;
case 'd': turn_right(time); delay(50);break;
default: break;
}
}
printf("demo end..\n");
}
//automatically control
void auto_control(char command[]){
init();
int index=0;
int time=1500;
go_ahead(time);
while(command[index]!='\0'){
switch(command[index]){
case 'w': go_ahead(time); break;
case 's': go_back(time); break;
case 'a': turn_left(time); break;
case 'd': turn_right(time);break;
default: break;
}
index++;
delay(50);
}
printf("demo end..\n");
}
int main(){
char command[13]={'w','w','a','w','w','a','w','w','a','w', 'w', 'a', '\0'};
softPwmWrite(PWMA, 150);
softPwmWrite(PWMB, 150);
auto_control(command);
//control();
softPwmWrite(PWMA, 0);
softPwmWrite(PWMB, 0);
return 0;
}
其中我设置了两种运动模式,第一种是用键盘控制移动,第二种是用用实现写好的代码控制移动
softPwmCreate()是创建PWM对象并且设置pwm范围用来调速
softPwmWrite()就是送高电平,后面一个参数用来控制运动的速度
AIN1 AIN2 BIN1 BIN2的用法和LED那个实验的针脚用法一样
直流电动机应该就是我们高中物理所学的洛仑磁力的原理
这个网上资料很少,卖家发的资料里面有pca9685.c 和pca9685.h 文件,这应该相当于板子的驱动吧,我照着卖家的代码写了一下。
#include
#include
#include
#include
#include "pca9685.c"
#define PIN_BASE 300
#define MAX_PWM 4096
#define HERTZ 50
#define BUFSIZE 512
//#########舵机定义###########
int servo1 = 0; //舵机0 手爪
int servo2 = 1; //舵机1 上臂
int servo3 = 2; //舵机2 下臂
int servo4 = 3; //舵机3 底座
float currentAngle[4]; //记录4个舵机当前的角度
float s_MIN[4]; //记录4个舵机最小角度
float s_MAX[4]; //记录4个舵机最大角度
float INITANGLE[4]; //初始定义的4个角度
#define max(x, y) ((x)>(y)?(x):(y))
#define min(x, y) ((x)<(y)?(x):(y))
#define ServoDelayTime 50
int delta = 5; //舵机转动幅度
int delta_bottom = 2;//底座转动幅度
//@param impulseMs 给脉冲的接口
//@param hertz 震动频率
int calcTicks(float impulseMs, int hertz){
float cycleMs = 1000.0f/hertz;
return (int)(MAX_PWM * impulseMs / cycleMs + 0.5f);
}
//@param servonum 舵机接口编号
//@param x 传入角度值,计算后变成电压
void PWM_write(int servonum, float x){
float y;
int tick;
y=x/90.0+0.5; //弧度制?
y=max(y, 0.5);
y=min(y, 2.5); //0.5到2.5
tick=calcTicks(y, HERTZ);
pwmWrite(PIN_BASE+servonum, tick); //上电
}
//############舵机动作函数#############
//手爪打开
void ClampOpen(){
PWM_write(servo1, s_MAX[0]);
printf("手爪张开\n");
delay(300);
}
//手爪关闭
void ClampClose(){
PWM_write(servo1, s_MIN[0]);
printf("手爪闭合\n");
delay(300);
}
//底座左转
void BottomLeft(){
if(currentAngle[3]+delta_bottoms_MIN[3])
currentAngle[3]-=delta_bottom;
printf("底座右转\n");
PWM_write(servo4,currentAngle[3]);
}
//上臂舵机向上
void Arm_a_up(){
if(currentAngle[1]+deltas_MIN[1])
currentAngle[1]-=delta;
printf("上臂向下\n");
PWM_write(servo2,currentAngle[1]);
}
//下臂舵机向上
void Arm_b_up(){
if(currentAngle[2]-delta>s_MIN[2])
currentAngle[2]-=delta;
printf("下臂向上\n");
PWM_write(servo3,currentAngle[2]);
}
//下臂舵机向下
void Arm_b_down(){
if(currentAngle[2]+delta
代码能跑但是机械臂动不了,我用卖家给的python代码跑了一遍还是动不了,我也不知道怎么回事。
好啦,树莓派小车试验到这里就结束啦。
这几天做这个试验从拼装到编程,收获还是蛮大的。
最后我总结一下我用到的软件,需要的话可以去百度搜索。
putty ssh连接
vnc viewer vnc远程桌面
winSCP 远程文件传输