Arduino开发板实验二:模拟输入和输出(用电位器和开关控制直流电机)

在上一个Arduino小实验里,我们尝试了用开发板来读写数字信号(0和1),貌似非常简单,难度系数跟吃苹果差不多。昨天淘宝的直流电机控制板终于送到了,接下来这个实验准备测试读写模拟信号。这个实验难度系数稍大一点,达到了吃香蕉的程度,搞不定的同学请去动物园请教猴子兄弟(开个玩笑,但是Arduino的确非常好开发)

我计划的实验目标是:
1,使用电位器控制直流电机的转速
2,使用开关控制直流电机的旋转方向

模拟输入

查了下资料,许多单片机的管脚电压都是+5V或0V,分别对应1和0。而机器人面对的自然界却没有这么泾渭分明,例如大气温度,到墙壁的距离,声音的强度等等,这些值就是模拟值。Arduino开发板上,标记了“Analog In”的16个管脚,就是用来测量模拟值输入的。这些输入电压的范围是0~+5V,开发板会把它映射到0~1023的整数。从这个数值范围,我们可以估算出Arduino能识别的电压精度大概是5mV,小于这个范围的变换无法识别。

电位器

这个东西是从邻居小朋友那里骗来的。在我读高中的时候,好像把这个东西叫滑动变阻器,个头很大。现在都是小小的旋转电位器了,原理图应该是一样的:

电位器

电位器

接线方式是两端分别接GND和+5V,中间随意接在一个Analog In的管脚上。我选择了一个大吉大利的6号。

电位器接线

电位器接线

读取模拟电压值的函数为:

int readValue = analogRead(readPin);

用端口监视器做个分解实验,使用Serial.println命令把readValue显示在电脑上,和想象中的结果完全一样。端口读写部分准备做为下一个实验主题,这里就不细说了:

端口监视

端口监视

PWM输出

作为一个外行,我之前曾经想过怎么让单片机输出一个模拟值。觉得必须用10个管脚,对应的二进制从0~1023,然后找一个数模转换的东西变成模拟电压值。结果一看资料发现自己太圭了,原来有一种叫做占空比的东西,只用一个管脚就可以输出0~255的数值。从这里也了解到嵌入式常用的一个叫“时序”的东西,以后应该会经常遇到,这种方式可以用很少的管脚实现非常复杂的功能(估计业内人士又要鄙夷的飘过了)。看下面的图解,从Arduino官网顺来的:

PWM模拟输出

PWM模拟输出

板子上标注了“PWM”的区域就是管脚均可以用于这种输出。使用的函数是:

analogWrite(pin, value);

注意value值的范围是0~255。

直流电机控制板

这个控制板的功能是把PWM的输出,转换成真正的模拟电压值,从而控制直流电机旋转。电机的种类很多,还有舵机,步进电机等等,控制方式都不一样,那些以后再试。先看一下这个板子的介绍,也是一个顺手牵来的图:

直流电机控制板

直流电机控制板

这个图里信息太多了,咱们捞干的讲几个:
1,左右两边是对称的,可以控制两个直流电机,咱们下面只看左边
2,绿色的“直流电机A接口”,用于接电机的两根电源线
3,直流电机A信号输入接口,一共有3个脚,分别是I1,I2和EA。其中I1和I2是数字接口,用于控制开关和方向,EA是模拟接口,用于控制转速
例如:I1=1,I2=0顺时针转,I1=0,I2=1就逆时针转,I1=I2的时候,停止转动。EA是0~255的PWM值,对应从小到大的转速
4,最下面的VMS接电源正极,GND接地,边上还有一个+5V,不用管它,本来是由它给逻辑电路供电的,但是默认情况下,是通过DUAL那个跳线帽由VMS搭车送电。

看看最终的接线图,专业人士一般用面包板之类的东西实验,我就直接把铜丝拧上了,请大家不要效仿:

接线图

接线图

接下来是写代码,一共也没几行,大家看注释吧:
注意:我发现Analog输入区编号是0~15,PWM输出区也有0~13,为了验证这两类管脚编号会不会冲突,我特地把readPin和speedPin都设置成了大吉大利的6号。事实证明两者不冲突。

int readPin = 6;    //用来连接电位器
int buttonPin = 38; //用来连接开关,这次用了个吉利的端口号
int i1Pin = 31;     //连接电机驱动板的I1接口
int i2Pin = 30;     //连接电机驱动板的I2接口
int speedPin = 6;   //连接电机驱动板的EA接口

void setup()
{
  pinMode(buttonPin, INPUT);  //开关用于输入
  pinMode(i1Pin, OUTPUT);     //I1和I2都是数字信号
  pinMode(i2Pin, OUTPUT);     //通过设置I1和I2来控制电机旋转方向
  pinMode(speedPin, OUTPUT);  //按占空比方式输出的模拟信号
  digitalWrite(buttonPin, HIGH);  //设置上拉电阻
}

void loop()
{
  //读取按钮状态
  boolean buttonPressed = digitalRead(buttonPin);
  //设置转动方向,I1和I2值相反时,分别对应两种不同的转向;I1和I2值相同时停止转动
  digitalWrite(i1Pin, buttonPressed);
  digitalWrite(i2Pin, !buttonPressed);
  //读取电位器(传感器)的读数,值范围从0到1023
  int readValue = analogRead(readPin);
  //PMW的值范围是0~255
  readValue/=4;
  //设置转速
  analogWrite(speedPin, readValue);
  delay(500);
}

上传程序以后,旋转电位计,可以看到电机的转速不断变化着;掰一下开关,电机就会反转,实验到此顺利结束。



对 “Arduino开发板实验二:模拟输入和输出(用电位器和开关控制直流电机)” 的 41 条 评论

  1. galaxy 说:

    EA和电位器应该不能共用pin6,互相之间有干扰
    你可以直接把EA直接与电位器相连,不通过单片机控制来试试

    • 何明珍 说:

      1到255分辨率的数的对应温度如何算?????每刻精度电压 目前用的是5V电压=5000豪伏
      由于电压是用5~~~12V电压
      热电阻单位也没确定;

      分辨公试:255/总电压

      温度公试希望大家帮算找查表;
      计划上限温度;80`C
      下限温度;5`C
      求:1.1KΩ热电阻温度表?
      2.10KΩ热电阻温度表?

  2. Li Fanxi 说:

    @galaxy
    确定么?
    手册上有句话说:
    analogWrite has nothing whatsoever to do with the analog pins or analogRead.
    看这块Mega板子的的电路图,好像这些PWM的脚跟模拟输入用的引脚都是独立的,对应ATmega1280上引脚也是不一样的,应该不会干扰吧。

  3. 楼上的兄弟们,我刚才做了两个测试

    1,电位器直接接EA,也可以调速。不过我觉得这是因为直流电机控制板也可以接受模拟电压信号
    2,我把程序的倒数第二行,改成了一个固定值
    analogWrite(speedPin, 128);
    接线不变,这时候调节电位器就没有反应了,证明这两个口没有干扰

  4. galaxy 说:

    好吧,我弄错了。
    我以为PWM6和ANALOG6是单片机上同一个管脚。

  5. to: galaxy
    我开始也是担心它们是同一个管脚,所以特地选用同一个编号来试验
    你的思路比较严谨,因为我原来那个试验不能证明两个管脚没有干扰,
    假设两个管脚是通的,结果同样可以实现调速
    需要用那个补充试验才能证明

  6. zxzxy1988 说:

    博主太犀利了…嘿嘿,前一阵子家里有点事情,就没怎么来,再次来就已经进度这么快了~呵呵

    关于L298N,原来貌似用过一次这个片子,感觉一般般,用来驱动步进电机好一些,驱动直流电机总感觉它的驱动能力不够(可以在安装好电机之后在架子上面放点东西,然后用这个驱动板驱动一下试试看,呵呵,我感觉你做的东西块头有点大,不一定能行~)推荐使用分立元件搭的H桥,驱动起来顶呱呱的,呵呵,原来有点资料给你发邮箱了~

    另外还一个疑问~不知道楼主的电源打算怎么弄?充电电池么~还是咋样呢~

    • 多谢你的资料,呵呵
      过年前工作比较多,小车一直还没有开始组装呢,我也有点担心直流电机带不动
      电源我从一个玩具车里拆了个铅蓄电池,6V/4.5Ah的,
      原来计划买个电动自行车专用的,先用着看看效果如何

      • 老顽童 说:

        铅电池很容易硫化变质.

        你充满电后要以最大要求的电流值(比如启动电流值)放电.
        同时监控电压跌落情况.

        测电流时不得用串入电流表的方式.
        应该串入取样小电阻(焊接).
        再测量取样电阻的压降,换算出电流值.

  7. SSS_SXS 说:

    受益非浅啊,跟进学习中

    老男人2号顶一下楼主:)

  8. 李宙恒 说:

    设置上拉电阻有什么用啊 ???
    如果要使arduino 只能拔电吗 ?假如现在正在驱动一个电机转动 那这个时候突然拔电会不会不好?

  9. 龙杨 说:

    大哥,看了您的设计,跟我想要设计的一个电路基本相同,想跟您交流,有机会可以合作,在下QQ:296272043,想进一步了解这个东西,谢谢

  10. 龙杨 说:

    大哥,我想设计一个电路,两个电位器联动, 通过一个马达驱动去跟踪另一个物体,到达电位平横后马达停止运动,同时锁定刹车。 根据电位器的位置来控制马达的正反转, 马达最好可以用24V,马达转速有几个档就可以了。研发的费用我们商量,,谢谢,请您给我点建议

    QQ 296272043

    • 你的描述不是很清晰,照你说的,只需要一个电位器就可以了。
      另外,这个电位器是靠什么动态调节的呢?(我猜想你是有个机械结构用来控制它)
      看上去不是非常复杂的功能,我觉得你仔细看看几个开发板实验就可以做出来的 :)

  11. 龙杨 说:

    看了您的设计,越来越佩服您了,但是我就砖科毕业,实在是有点难度,是我们的加工设备,两个立柱上有运动机构,平时需要人力分别上下拉动,保持同一个高度,,我想设计一个电路来控制马达,一个立柱仍然用人来控制高度,另一个立柱用马达自动跟踪,这样我往下拉,另一个立柱就跟着往下运动,往上拉,另一个立柱就往上运动,始终保持一个高度,请您帮忙,一定会酬谢您的

    • 我重复下你的需求,看理解的对否:你有两根立柱,其中一根手动控制,另一根用电机控制。它们分别关联到两个电位器上,你期望的是手动调节立柱1的时候,电机会自动带动立柱2,然后让两个电位器的读数相等,从而实现运动到相同的高度?
      这个方案其实有点问题的,主要在于你需要的高度误差精度是多少,两个立柱关联到电位器,可能会有比较大的误差。另外如果不加其他辅助设备的话,直流电机刹车不是那么容易,总是会有一点惯性。

      如果不考虑误差的话,这个问题的解法应该是这样:
      int r1 = analogRead(Pin1); //电位器1
      int r2 = analogRead(Pin2); //电位器2
      if (5 > abs(r1 – r2) ) {
      // 电机停止,这里的5是个示意值,表示给惯性留的一点运动空间
      } else if(r1 > r2) {
      // 电机正转
      } else {
      // 电机反转
      }
      原理大概就是这样,其中电机正转或反转都可以参考博客里的代码完成。你的电机50W的话,肯定不能用Arduino直接输出控制了,会烧板子。但是L298N也许勉强可以撑住,你需要查查文档。
      酬谢就不必了,举手之劳,后面的具体工作还得你自己下功夫完成。

  12. 龙杨 说:

    电位器就是跟机械装置联动的,立柱的高度不同,电位就不同

  13. 龙杨 说:

    我不懂程序,基础不好,很多东西都是一知半解的, 别人一天能搞定,我可能要一年,只能求助别人了,我看了您的设计,关键的差别,是要根据电位器位置的不同,控制马达的正反转,和需要刹车,我的电机平均功率估计50W左右吧,如果那些电路板不能驱动,我可以用继电器来控制,关键是逻辑和程序要设计好

  14. 龙杨 说:

    老大是这样的,运动的速度不需要很快,质量也不大,所以基本没有很大的惯性的,还有刹车会动作的,刹车机器上面本来就有,效果很好的,能分辨5毫伏的电位差别就可以了,至于酬劳,我也是做技术的,做维修的,知道大家都很辛苦,这是必须的,我想让您帮我把程序写进去,(主要是我只知道修,设计不在行,也不知道那两块小板是哪里买的,程序就更不懂了)后面驱动马达的电路我自己装继电器,您买配件也需要钱,我会先给您付款。

    我是广州的,来广州一定要联系我。13825048226。

    • 汗,这个忙我帮不了你,不是钱的问题。我只能大概描述了它的运动原理,真实环境下的代码肯定需要调试,不是说我写一些代码发给你,装上就能用了。所以建议你最好还是找找广州的朋友,或者下点功夫学一下Arduino,不然的话靠这样讨论是做不成的。

  15. 龙杨 说:

    您好,刹车的信号不需要了,直接通过并联马达电压来控制就可以了,设计需要得到一个+ — 12V的输出电压就可以控制马达的正反转,物体运动速度慢,惯性也不需要考虑了,

    谢谢

    • darkorigin 说:

      ARDUINO控制电路用光耦下联一个继电器就搞定了
      很安全,
      逻辑方面读取电阻值,2个电机直接同步工作就好了,不需要搞那么多东西。

      或者直接整个步进电机,用ARDUINO貌似可以读取它的实时角度值的。 更精确

  16. Lasme 说:

    动力老男孩你好,我在网上查资料的时候看到了你这个帖子,发现你也研究这个,我有个问题想问你一下。

    我目前用的是Arduino Motor Shield Rev3 这个板子,这个板子的芯片是L298P,可能和你用的这个扩展板的芯片差不多。这个板是也专门控制电机的。

    我现在有个电位计通过一个木板和直流电机相连,也就是说电机带动木板进而带动电位计,然后我想通过这个电位计返回的DA转换后的值来控制电机,我觉得是否可以用程序这样控制。电机旋转的函数执行很简单,我就不写出来了。

    motorLeft()//电机向左转

    if(potiValue=analogRead(A5),potiValue>400){
    motorStop();
    }//判断电位计的值,如果电位计的值比400大,电机停止转动

    motorRight();//然后电机向另一个方向转动

    if(potiValue=analogRead(A5),potiValue<150){
    motorStop();
    }//如果电位计的值小于150,程序从开始执行,电机转向另一个方向

    请问这么写程序有什么问题,因为我发现电机在执行向另一个方向转动的时候好像有问题,因为电机不会在电位计是大概140的时候往回转,而是会转到电位计的边界值,比如说0然后再往回转。请问程序这样写是否有些问题。如果程序没有问题的话是否应该考虑电机旋转的惯性的问题呢。非常感谢!

    • darkorigin 说:

      我给点拙见:
      int A5Val;

      VOID loop()
      {
      val=analogRead(A5);
      motorStop();
      if(A5Val>400){ motorStop(); motorRight();} 大于400就先停再右转
      IF(A5Val<150)( motorStop(); motorLeft();} 小于150就先停后左
      }

      这个代码似乎更适合你代码的思路
      另外提醒你几个小问题,就是扩考等标点必须是半角,另外,不管是循环语句还是条件或者判断语句,如果后面只有1行代码,则无须花括号,1条以上才需要

      你上面的代码分析是这样:
      上来,先左转(这个是无条件的);
      然后取值并且判断,但是IF语句很少看到人家如此用,放在外面每个LOOP循环只读1次,减少处理时间和代码长度(优化代码角度) 大于400就停下,然后又无条件的右转…..然后再判断小于150 又停下.代码很怪
      如果你的值在400-150之间会出现无条件的左转然后直接右转(判断语句都不符合….)

      很怪的代码,既然你每个中间都用了STOP过程,这样转来转去有意义么?

      • Lasme 说:

        非常感谢你的耐心回复!!

        多谢你的建议,你的代码我试了一下,但是还是不行,你的逻辑100%是正确的,我觉得我没有把我的问题描述清楚,代码也不规范给人造成误解。我先把我的代码贴出来然后再阐述一下我的思路。

        完整代码:

        void setup() {
        pinMode (9,OUTPUT);
        pinMode (12, OUTPUT);
        pinMode (12,INPUT);
        //pinMode (pwm, OUTPUT);
        Serial.begin(9600);

        }

        void motorStop(){
        digitalWrite(9,HIGH);//motor brake
        delay(2000);//2 Sec
        }

        void motorUp(){
        digitalWrite(12,HIGH);//one direction
        digitalWrite(9,LOW);//motor doesnt brake
        analogWrite(3,50);//motor speed
        delay(1000);
        }

        void motorDown(){
        digitalWrite(12,LOW);//the other direction
        digitalWrite(9,LOW);//motor doesnt brake
        analogWrite(3,50);//motor speed
        delay(1000);
        }

        int potiValue;//potentiometer value

        void loop(){

        motorDown();//motor running in one direction
        if(potiValue=analogRead(A5),potiValue400){
        motorStop();
        Serial.println(potiValue);//if the poti value bigger than 400
        }

        • Lasme 说:

          循环体字数限制不够地方了。。。
          int potiValue;//potentiometer value

          void loop(){

          motorDown();//motor running in one direction
          if(potiValue=analogRead(A5),potiValue400){
          motorStop();
          Serial.println(potiValue);//if the poti value bigger than 400
          }
          }

          • Lasme 说:

            void loop(){

            motorDown();//motor running in one direction
            if(potiValue=analogRead(A5),potiValue400){
            motorStop();
            Serial.println(potiValue);//if the poti value bigger than 400
            }
            }

      • 多谢darkorigin同学的耐心和热心解答!

    • darkorigin 说:

      还有个小建议,就是写代码的时候只要弄清楚硬件的软件接口函数就好不需要拘泥很多,因为只要函数接口相同,可以透明处理(就好像你电脑用网线或者无线上网甚至通过GPRS上网 QQ的代码不需要更改一样);

      另外,思维要缜密.很多人说学程序的人容易死脑筋,确实(很多IT人很多问题总是简单的用”是否”"好坏”来考虑问题),但是对于程序而言这个是必须的,否则就会出千奇百怪的小问题;

      还有就是Arduino语言是基于AVR C的,如果希望入门,建议还是对C语言基本语法结构做个深入的了解(不管是C C++ C# JAVA 只要是C派生出来的,基本语法都是差不多的,条件分支判断语句写法都是一样的,主要是函数区别)

      • Lasme 说:

        不知道什么问题贴了两次下面的代码贴不上了,不过和上面一样就是判断一下电位计的值是不是小于200,如果是的话电机就向回转,然后这个电机在200和400之内上下移动。

        我使用的这个电机是汽车中控锁用的小电机,他是上下移动的,实际上他的构造内部是个直流电机,然后这个直流电机连接着齿轮和杠杆,这个电机的上部通过内部的直流电机(左传或者右转)控制而实现上下移动(活塞运动)。这个电机链接着一个木杆,木杆的另一端连着电位计,当这个电机上下运动的时候连着的木杆也上下移动,然后带动电位计转动,然后通过电位计返回的max和min的值来控制电机进而控制这个木杆。

        程序上你给我提出的问题:
        1,循环体内的程序我想上来就让电机先动起来方向无所谓,然后再进行判断语句,因为这个电机控制板的程控制电机的函数的参数digitalWrite(12,HIGH),HIGH和LOW就代表了两个不同的方向。而且电机如果不动的话那么电位计的值也就不会改变。
        你的代码中电机先停一会,然后判断电位计的值,可是如果电机不转动的话,电位计的值也不会改变,那么后面代码就不会被执行,我是这么理解的,不知道对不对。
        2,电机停止这个函数我是这么想的,我想让电机达到电位计的值的时候比如说400的时候停上2秒,而不是马上就反转,同理到另一端的时候也是如此。这样可以进一步进行控制。实际上我是想做一个简易的球杆系统,控制球在木杆上滚。

        最后再次非常感谢你的回复还有你给我提出的建议!我会去再好好看看C语言的。

        • 这个博客的留言系统有bug,你贴代码的时候,如果前面出现小于号,后面出现大于号,之间的部分会被全部当作注释删掉。
          所以建议你贴代码的时候,先把大于号贴出来,然后贴小于号,不然看上去就会很奇怪。

          你的程序问题很多,首先你要按darkorigin给你的提示,把motorUp和motorDown放到if大括号里。
          另外,有个很大的问题,motorUp和motorDown都是delay了一秒钟,对于电机来说,1秒钟可能已经运行到头了。
          简单的尝试是把这里的1000改成1ms试试。

    • Croatia 说:

      弱弱的问一句可不可以给我讲一下板子谢谢谢谢

  17. XYZ 说:

    我用四个三极管做了个H桥不小心出了下轨直接炸管了

  18. yangjiang2015 说:

    请问楼主,如果我想根据一个轴的转速来控制 另外一个无刷的转速,改怎么编程,

  19. [...] PWM是“怕玩命”的缩写,英文写法是“Pulse-width modulation”,也有些外行人士把它翻译成“脉冲宽度调制”。对于没有听说过PWM的同学,请先参考一下我的另一篇博客Arduino的模拟输入和输出。 [...]

  20. pwSong 说:

    感谢博主。哈哈 来自2022年arduino入门小白的感谢^-^

  21. [...] PWM是啥玩意儿? PWM是“怕玩命”的缩写,英文写法是“Pulse-width modulation”,也有些外行人士把它翻译成“脉冲宽度调制”。Arduino有很多种版本,这篇文章里是以ATmega168为例,有用过其他型号的兄弟请补充。 对于没有听说过PWM的同学,请先参考一下我的另一篇博客Arduino的模拟输入和输出。 [...]

  22. [...] 对于没有听说过PWM的同学,请先参考一下我的另一篇博客 Arduino的模拟输入和输出 [...]

发表评论

可以使用下列 XHTML 标签:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>