
用Arduino控制好盈无刷电调本来的想法用arduino的pwm脚,通过analogWrite 输出pwm信号就可以驱动电调的。写了个程序一试,不行,电机就是B-B的叫。后来找了一些资料,看到网上一些朋友用Servo库以控制舵机的方式来驱动电调。说arduino的analogWrite输出跟Servo的不是一个东西(http://www.elecfans.com/article/89/169/2012/20120621277343.html) ,后来我用Servo库试了一下,电机是可以动起来了,但是只动了一下,又停了 :
http://wenku.baidu.com/view/6c2537353968011ca3009119.html 为什么用Servo就可以,而用analogWrite 输出的PWM信号就不行了呢。书上说analogWrite 产生的是一个大约490Hz的占空比可变的脉冲序列,而根据文库中写的,控制无刷电调要使用20ms的PWM信号。我想应该就是这个原因吧。 后来继续搜,在arduino.cn上看到一篇贴子,不用Servo来驱动舵机 http://www.arduino.cn/thread-45-1-1.html 。我尝试用它来控制我的无刷电调,但是一开始是失败的。后来我看到了好盈电调的说明书,http://www.docin.com/p-305686520.html 原来这电调可高级了,还可以进入编程模式啥的。而且电调首次使用,没调制好油门行程,也是不能驱动电机的。但是我手头上并没有遥控器,于是改了一下代码,模拟设置油门行程。设置好了之后,电机终于转起来了! //see http://www.docin.com/p-305686520.html int sp1=9;//定义舵机接口数字接口7 int pulsewidth;//定义脉宽变量 int val; int val1; int myangle1; //下面是servopulse函数部分(此函数意思:也就是說每次都是0.5ms高電平 1.98ms低電平 然後再0.52ms低電平 17ms延時也是低電平) void servopulse(int sp1,int val1)//定义一个脉冲函数 { myangle1=map(val1,0,180,500,2480); digitalWrite(sp1,HIGH);//将舵机接口电平至高 delayMicroseconds(myangle1);//延时脉宽值的微秒数 digitalWrite(sp1,LOW);//将舵机接口电平至低 delay(20-val1/1000); } //servopulse函数部分到此结束 void setup() { pinMode(sp1,OUTPUT);//设定舵机接口为输出接口 //设置两组串口波特率 Serial.begin(9600); Serial.println("servu=o_seral_simple ready" ) ; //看电调说明书,设置油门行程时,一开始需要把遥控器打到最高点。i<=110大概是2杪多 for(int i=0;i<=110;i++) { servopulse(sp1,150);//引用脉冲函数 } //等电机发出B-B两声后(就是两秒后,大概就是这里了)把油门打到最低点 for(int i=0;i<=55;i++) { servopulse(sp1,20);//引用脉冲函数 } //后面提示后,就可以控制电机转动了 for(int i=0;i<=150;i++) { servopulse(sp1,150);//引用脉冲函数 } Serial.println("hello world" ) ; } void loop()//将0到9的数转化为0到180角度,并让LED闪烁相应数的次数 { val='8'; if(val>'0'&& val<='9') { val1=val-'0';//将特征量转化为数值变量 val1=map(val1,0,9,0,180);//将角度转化为500-2480的脉宽值 Serial.print("moving servo to "); Serial.print(val1,DEC); Serial.println(); for(int i=0;i<=50;i++)//给予舵机足够的时间让它转到指定角度 { servopulse(sp1,val1);//引用脉冲函数 } } }
这东西很危险 高速转动,手就没有了
今天是个开心的日子啊!~~电调,电机到了,然后迫不及待的各种焊啊。。杯具的功率太低,铜片焊不上,最后想了个办法解决了。。终于是没白费这么多天的研究,成功的让电机转起来了!~

今天考了3门考试太累了,然后又一顿玩这个,写不了太多,而且这个方案肯定不能用在真正的项目上:但是有始有终,我会把这个继续写下去!直到他能真正的用在项目上。 我今年大四了,找工作没压力,所以3天后的寒假和下学期会比较闲,可以有很多时间整这些。但是如果有大神或者有兴趣的能一起加入讨论更好了。 下面我今天先把核心的放上去,明天再放一些采购物品的注意事项(我都非常小心了还是被坑了TAT)后面还有timer的解决方案。(求大神们提点那><...) 还有就是,本来不想说,但是还是稍微提一下吧,如果真正想研究一个东西(无论是什么),还是应该静下心来,慢慢看不要被功利迷惑了我们的内心。一天不行,两天,两天不行三天,再不行一个月、一年。贵在坚持!(这不是我的话,是我的一位恩师提醒我的,我觉得很有道理,我也看到太多太多心志不坚定的人和他们的结局。)
首先是电调控制方法(关于PMW什么的不在解释了,因为我到现在还是不非常清楚哎): 我们需要做的是 单片机输出1ms~2ms的方波脉冲,根据航模标准,PWM信号线的频率应该是50Hz,对应的每个周期总时长是20ms,输出到电调的油门线(控制线,也就是细细的,除了红的是接5V电源,黑的GND,另外那个就是数据线)。 如果是单向电调,1ms表示0%的油门,2ms表示100%的油门。如果是双向电调(有正、反转和刹车),标准1.5ms是0点,1ms是反向油门最大(100%油门),用于刹车或反转;2ms正向油门最大(100%油门),用于正转。 这是无线遥控模型比例控制的一个标准。对于其它电调也一样。注意,电调转速只与1ms~2ms的脉宽有关,与脉冲重复率无关。1~2ms的方波脉宽渐变过程对应油门的从小到大,从负到正的渐变。 脉宽的幅度2.5V~6V;所以3~5V工作电压的单片机都适用。 重要,我买的是好盈电调,它的说明书要求必须校准范围,其实原理很简单,就是设置脉冲最大最小值。 而如何控制PMW波形是一个难点,这里用最原始的servo方法,代码复制过来有些乱,复制到IDE整理下再看: #include <Servo.h> Servo powerServo[4]; int motorPins[4]; //arduino 上只能是3 5 6 9 10 11这几个 // the setup routine runs once when you press reset: void setup() { motorPins[0] = 3; //x1 motorPins[1] = 5; //x2 motorPins[2] = 6; //y1 motorPins[3] = 9; //y2 for(int i = 0; i < 4; i++) powerServo[i].attach(motorPins[i]); /* min (optional): the pulse width, in microseconds, corresponding to the minimum (0-degree) angle on the servo (defaults to 544) max (optional): the pulse width, in microseconds, corresponding to the maximum (180-degree) angle on the servo (defaults to 2400) */ Serial.begin(9600); } int ctrlPower = -1; // the loop routine runs over and over again forever: void loop() { a: if(Serial.available()) { ctrlPower = Serial.read(); //there are sth wrong... arduino can only receive only 1 byte.. Serial.print("read a data: "); Serial.println(ctrlPower); } if(ctrlPower == -1 ) goto a; //其实也可以某认为0,这样是为了调试方便 /* 这里需要注意一点,如果把ctrlPower弄成-1他就不会再发送了,这样电调就认为它和遥控器断开了!这样会导致电调无法启动。 */ double T = 2000; // 一个周期的微秒值 long t0 = micros(); //记录begin的时间 //ctrlPower从串口读取,0~200分别对应高电平脉宽0.9ms~2.1ms double len = 1000 * (0.9 + ctrlPower * (2.1 - 0.9) / 200.0); //len = 0.9*1000 ~ 2.1*1000 for(int i = 0; i < 4; i++) powerServo[i].writeMicroseconds(len); //这样会同时控制4个电调同时输出一样的电位 //may be we can do something here?? int leftMs = (int) (t0 + T - micros()); //Serial.print("I'm idle when : "); //Serial.println(leftMs, 4); //everything done... /* 我原先的想法是在这可以做其他事情,但是考虑到如果事情没做完,但是时间到了,或者发生啥阻塞了,那不废废了?所以还是这个还是不行的。只能是先试验试验,转转电机。必须得用定时器。 */ delayMicroseconds(leftMs); } 下一楼我会附带一个我写的C#控制程序,我编译好了,如果不会C#可以直接运行exe,如果会的可以改改源代码来符合你的需求
很久没发了,不是偷懒,而是一直在看一本书,arduino技术内幕,推荐一下,图灵出版的感觉写的很不错,学了那么久感觉看了它才看到门了,咳咳还没入门就是- - 下面是两个正式的版本了,第一个是servo的,第二个是直接通过寄存器。 第一个很简单就不解释什么了,这个库很牛,可以在任何端口模拟pwm。当然是有代价了,编译出来很大。如果是个大项目那么小小的328妥妥不够用了- - #include <Servo.h> int pin1 = 6; int pin2 = 9; Servo servo1; Servo servo2; void setup(){ Serial.begin(9600); servo1.attach(pin1); servo2.attach(pin2); } void loop(){ int a=0; st: if(Serial.available()){ a = Serial.read(); } if(a==0) goto st; Serial.println(a); servo1.write(a); servo2.write(a); }
第二个寄存器版本,是看了arduino技术内幕这书和官网的PWM的秘密,再掰了德国人的飞行器的源码才最终弄出的 - - 我想说。。其实写起来也就是那么简单,感觉我绕了老大一圈。。。(sign~~) void setup(){ Serial.begin(9600); //pinMode(3,OUTPUT); bitSet(DDRD, 3); //pinMode(9,OUTPUT); bitSet(DDRB, 1); //pinMode(10,OUTPUT); bitSet(DDRB, 2); //pinMode(11,OUTPUT); bitSet(DDRB, 3); TCCR1A |= _BV(COM1A1); // connect pin 9 to timer 1 channel A PB1 TCCR1A |= _BV(COM1B1); // connect pin 10 to timer 1 channel B PB2 TCCR2A |= _BV(COM2A1); // connect pin 11 to timer 2 channel A PB3 TCCR2A |= _BV(COM2B1); // connect pin 3 to timer 2 channel B PD3 } void loop(){ int a=0; st: if(Serial.available()){ a = Serial.read(); OCR2A=a; //D11 OCR2B=a; //D3 OCR1A=a; //D9 OCR1B=a; //10 } //[1000;2000] => [125;250] if(a==0) goto st; Serial.println(a); }
PWM的原理我就不说了,主要原理是控制占空比。PWM有2种方式,fast 和 phrase correct,结合它的图还有这篇翻译帖子,就能很清楚这两个的原理,和操作方法了: http://www.diy-robots.com/?p=852 这里说明一下,我原先开始玩的时候一直不知道OCR1A之类的是什么东西,后来看了我以前推荐的那本书:arduino技术内幕才明白,那是AVR的寄存器,而UNO MEGA之类的arduino芯片采用的就是AVR芯片。所以在代码里,直接操纵芯片就等于操纵了单片机。 这里顺便说一下,也许有人很奇怪ino的两个函数:void setup() void loop() 是什么原理,C语言的入口函数不应该是int main()吗?其实一切尽在源码里。 源码文件在:arduino-1.0.5\hardware\arduino\cores\arduino 在main.cpp文件里就能看到了,代码就不贴出来了,这里需要注意,其中还包含了<arduino.h>这个头文件非常重要,为什么在ino文件里可以用的函数到了.cpp .c文件就不能用了?原因就是你没有加这个头文件,arduino其实就是通过这里包含的代码操纵了AVR单片机。 好了扯远了,如果你看了arduino源码的话,就应该知道了像anagloWrite之类的函数为什么可以运行,其实它就是操纵了寄存器。然而为了效率(因为这些函数需要有普遍性所以需要判断之类的额外操作影响了效率,直接操纵寄存器将会事半功倍。) 下一贴具体说说如何操纵寄存器。
要控制PWM就不得不需要timer,在arduino里有3种timer频率:8bit 11bit(较少,只有 ATmega32u4 -> promicro 有) 还有16bit ,不同的bit,说白了其实就是bit越高的,控制的频率越灵活。 Promini :timer 0 和 2 是8bit,timer 1是16bit Promicro:timer 0 是 8bit , timer 1 和3 16 bit ,timer4 是11bit Mega:timer0 timer2 是 8bit, timer 3、4、5(ABC)是16bit, timer1(A&B) 下面三种不同的bit timer和其解释: 1、8bit timer: 这个timer只有8bit(0~255)可以控制 所以8bit的控制非常简单,只要打开channel就可以了EX: TCCR2A |= _BV(COM2A1); // this connects Comperator 2A to its pin 具体哪哪个是哪个可以参考data sheet 2、16bit timer: 16bit就比较复杂了,因为分辨率(resolution)越高,timer始终count得越慢(0~65535才一个周期),这里提一下bit位运算: 10>>1 10向右一两位其实就是10/2,10>>2就是10/2/2 相反,10<<1 向左移1位就是10*2 在MWC里,因为PPM的频率在1000~2000所以这里把这个寄存器设置成14位(0 - 16383)也就是把一个周期分成0~16383,这样把1000~2000乘以8就是8000~16000了。这里刚开始看有点疑惑,其实它就是把16000当作满频了,(这里需要再重新设置一下电调的最大最小量程了。)在看下16bit的代码,这样一些世界都美好了! 16bit timer的周期其实就是 int dutytime = (1500<<3); 而8bit timer的周期其实就是 125~250! 这里说明一下我之前写的程序是在UNO上用的是8bit timer,所以移植到mega它的量程就完全不一样了。 如果想用计算机直接发信号控制的话,那么周期可以在8000~16383之间选择,这样控制的能更加精细(当然其实也没必要。。。1000~2000足够了) 3、11bit timre 11bit 其实非常简单,大家注意和8bit比较一下,其实就是差了3位,也就是0~2047,其实很简单。 设8bit的频率为X ,那么11bit其实就是 8<<3 也就是1000~2000(125~250)完美设置了,可以直接把遥控器的PPM直接发给PWM。
结束语: 单片机的世界真的很神奇,作为一个将要毕业学软件的学生,偶然兴起想做一个帅气的四轴,到现在也有3个多月的时间了,虽然四轴还没飞起来,但是我还是很高兴我能一直坚持到现在。 PWM是我的第一个难点,但却不是最后一个,后面还有PID的控制,还有前后左右,当然我还是会一直坚持下去。虽然我在这个帖子只讲我明白的地方,但是一定能有许多谬误,还希望大家能加以指正,共同进步。 顺便提一下,我发现许多人都不知道好好利用开源的力量,而放弃自己擅长的东西,去纠结与一些自己不擅长的东西(对于我来说,比如算法。。)其实PID控制、卡尔曼滤波在github上都有实现好的不错的算法可供使用。明白自己想要的,而略过其它的,这是我一直坚持的方法,希望对大家借鉴意义~

2015年02月11日 17:03:49
阅读数:4150
error: stray ‘\302’ in program
*.c:3: error: stray ‘\302’ in program *.c:3: error: stray ‘\240’ in program *.c: In function ‘main’: *.c:5: error: stray ‘\302’ in program *.c:5: error: stray ‘\240’ in program *.c:5: error: stray ‘\302’ in program
在vi编辑器中出现如上问题,你却找不到错在哪。。。当然应该要想到是编辑器的缘故,所以可以试试以下指令
od -c *.c
好嘛,都出来了,\302\240也无处藏身了,到相应行去删除修改就好了。。。
system/vold/DirectVolume.cpp:476: error: stray ‘\302’ in program system/vold/DirectVolume.cpp:476: error: stray ‘\240’ in program system/vold/DirectVolume.cpp:477: error: stray ‘\302’ in program system/vold/DirectVolume.cpp:477: error: stray ‘\240’ in program system/vold/DirectVolume.cpp:477: error: stray ‘\302’ in program system/vold/DirectVolume.cpp:477: error: stray ‘\240’ in program system/vold/DirectVolume.cpp:484: error: stray ‘\302’ in program system/vold/DirectVolume.cpp:484: error: stray ‘\240’ in program
查看到字符出错的地方都是因为空格,应该是拷贝过程中格式不对导致的。
0032460 * * * * * * * * * / \n / / z h o 0032500 n g h u i q u a n 2 0 1 2 1 1 0032520 0 8 a d d h a l d T h r e a 0032540 d \n i n t D i r e c t V o l u 0032560 m e : : s t a r t H a l d ( ) 0032600 { \n \t p t h r e a d _ a t t r _ 0032620 t a t t r ; \n \n \t S L O G D ( 0032640 ” H a l d : S t a r t h a l 0032660 d T h r e a d ! ” ) ; \n \t p t h 0032700 r e a d _ a t t r _ i n i t 302 240 0032720 ( & a t t r ) ; \n \t p t h r e a 0032740 d _ a t t r _ s e t d e t a c h 0032760 s t a t e 302 240 ( & a t t r , 302 240 0033000 P T H R E A D _ C R E A T E _ D 0033020 E T A C H E D ) ; \n \n \t i f ( 0033040 p t h r e a d _ c r e a t e ( & 0033060 m T h r e a d , & a t t r , 0033100 D i r e c t V o l u m e : : h a 0033120 l d T h r e a d , t h i s ) ) 0033140 { \n S L O G E 0033160 ( ” H a l d : p t h r e a d _ 0033200 c r e a t e ( % s ) ” , s t 0033220 r e r r o r ( e r r n o ) ) ; \n 0033240 r e t u r n - 0033260 1 ; \n } \n \n \t p t h r e 0033300 a d _ a t t r _ d e s t r o y 302 0033320 240 ( & a t t r ) ; \n \n \t r e t u 0033340 r n 0 ; \n } \n \n \n i n t D i 0033360 r e c t V o l u m e : : s t o p 0033400 H a l d ( ) { \n v o i 0033420 d * r e t ; \n \t i n t k i l