关于解决arduino pluseIn与官方舵机库冲突的办法

一、前言

Arduino在国内的知名度逐年提高,多数单片机爱好者选择使用其进行项目开发。期间难免遇见一些从未见过的bug。在实际工程中,能遇到教程书里所见不到的神奇bug,在国内的网络里一般找不到解决办法。就如这次的官方库冲突,也是我在工程训练大赛中才遇见的。比赛中,我们使用arduino作为车体的控制,车体位置定位是用的超声波HR-04模块,车体姿态陀螺仪输出的量也是PWM信号,都需要pulseIn函数进行读取。而车体运动我们采用的是有刷电机带电调,需要向电调输入pwm信号。arduino输出PWM信号我们用的是舵机库中的Servo.writeMicroseconds()函数。发现两者出现干涉。国内很少关于这个问题的解决方法。本例只针对Arduino的AVR系列。maxkz

二、问题所在

arduino常用的UNO板子主要有三个定时器:
timer0————delay(),millis().micros()
timer1————Servo()
timer2————Tone()
冲突根源:
C:\Program Files (x86)\Arduino/libraries/Servo/src/avr/Servo.cpp中的这段程序:

void Servo::write(int value)
{if(value < MIN_PULSE_WIDTH){  // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)if(value < 0) value = 0;if(value > 180) value = 180;value = map(value, 0, 180, SERVO_MIN(),  SERVO_MAX());}this->writeMicroseconds(value);
}void Servo::writeMicroseconds(int value)
{// calculate and store the values for the given channelbyte channel = this->servoIndex;if( (channel < MAX_SERVOS) )   // ensure channel is valid{if( value < SERVO_MIN() )          // ensure pulse width is validvalue = SERVO_MIN();else if( value > SERVO_MAX() )value = SERVO_MAX();value = value - TRIM_DURATION;value = usToTicks(value);  // convert to ticks after compensating for interrupt overhead - 12 Aug 2009uint8_t oldSREG = SREG;cli();servos[channel].ticks = value;SREG = oldSREG;}
}

从以上代码可以看出,Servo.write(degree)和Servo.writeMicroseconds(value)是差不多一样的,只不过Servo.write(degree)把输入进去的角度转换成脉冲宽度,传入调用的Servo.writeMicroseconds(value)而已,主要问题出在了void Servo::writeMicroseconds(int value)中调用的一个函数cli()上,不少有经验的开发者经常用到,关闭中断 意思是设置了之后,外部中断和内部中断都被屏蔽了,不会去处理了,然后把脉宽值转为Ticks值,从而设置定时器。
我们已知舵机库可以定义任何一个引脚为输出口。以下的代码反应出是通过digitalWrite来实现的


static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
{if( Channel[timer] < 0 )*TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timerelse{if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated}Channel[timer]++;    // increment to the next channelif( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {*OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;if(SERVO(timer,Channel[timer]).Pin.isActive == true)     // check if activateddigitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high}else {// finished all channels so wait for the refresh period to expire before starting overif( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) )  // allow a few ticks to ensure the next OCR1A not missed*OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);else*OCRnA = *TCNTn + 4;  // at least REFRESH_INTERVAL has elapsedChannel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel}
}

再看下面的代码:C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\wiring_pulse.c

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
{// cache the port and bit of the pin in order to speed up the// pulse width measuring loop and achieve finer resolution.  calling// digitalRead() instead yields much coarser resolution.uint8_t bit = digitalPinToBitMask(pin);uint8_t port = digitalPinToPort(pin);uint8_t stateMask = (state ? bit : 0);// convert the timeout from microseconds to a number of times through// the initial loop; it takes approximately 16 clock cycles per iterationunsigned long maxloops = microsecondsToClockCycles(timeout)/16;unsigned long width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops);// prevent clockCyclesToMicroseconds to return bogus values if countPulseASM timed outif (width)return clockCyclesToMicroseconds(width * 16 + 16);elsereturn 0;
}

这个函数里并没有用到定时器中断,或者什么其他的中断,其核心在unsigned long width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops);这句话上
我们可以打开同目录的wiring_pulse.S,这里面是一个汇编的代码,不过它有对应的C注释,可以看一下

 * unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops)* {*     unsigned long width = 0;*     // wait for any previous pulse to end*     while ((*port & bit) == stateMask)*         if (--maxloops == 0)*             return 0;**     // wait for the pulse to start*     while ((*port & bit) != stateMask)*         if (--maxloops == 0)*             return 0;**     // wait for the pulse to stop*     while ((*port & bit) == stateMask) {*         if (++width == maxloops)*             return 0;*     }*     return width;* }

当执行到pulsIn()的时候,先把这个引脚上的高电平先过掉,再开始读值,这里用的不是中断的方式,是进程阻塞式的,而Servo的定时器溢出时会中断,r然后去改变舵机引脚的电平,期间就过去了几十个时钟周期。虽然有时候也不多就30个us这样,但是对于某些条件下是相当的要命。

三、解决办法

解决办法还是有的

1.关中断

/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH* or LOW, the type of pulse to measure.  Works on pulses from 2-3 microseconds* to 3 minutes in length, but must be called at least a few dozen microseconds* before the start of the pulse.** This function performs better with short pulses in noInterrupt() context*/

从最后那句话中可以看到,作者建议没中断的状态使用这个函数。我们可以noInterrupt()把中断都关了,再pulseIn()。

2.手写PWM

我再网上找的解决方案https://www.cnblogs.com/sjsxk/p/5832406.html
这位老哥用手写的办法解决舵机控制

//这个函数的精确度一般,但是能用int servopin = 7;    //定义舵机接口数字接口7 ,接舵机信号线,这个IO口随便定义void servopulse(int angle)//定义一个脉冲函数,这个函数的频率是50hz的,20000us=20ms=1/50hz{int pulsewidth=(angle*11)+500;  //将角度转化为500-2480的脉宽值digitalWrite(servopin,HIGH);    //将舵机接口电平至高,反过来也是可以的delayMicroseconds(pulsewidth);  //延时脉宽值的微秒数digitalWrite(servopin,LOW);     //将舵机接口电平至低delayMicroseconds(20000-pulsewidth);
}void setup(){pinMode(servopin,OUTPUT);//设定舵机接口为输出接口
}
void loop()
{//把值的范围映射到0到165左for( int angle = 0;angle<180;angle+=10){for(int i=0;i<50;i++)  //发送50个脉冲{servopulse(angle);   //引用脉冲函数}  delay(1000);  }
}

这个老哥是用这个方法来避开舵机库与PWM函数的冲突,同样也可以运用到这上面

3.第三方库

Arduino的资源是相当多的,我用的解决方法是安装了一个第三方库Servo_Hardware_PWM.h 这个库只用用于Arduino mega2560。该库用了mega2560的定时器3,4,5及其相对应的引脚进行使用,库里直接寄存器操作,省去了大量的时钟周期。示例代码如下:

/* Servo SweepCreated by Daniel Duller, 12. January, 2019.Changed by Daniel Duller, 11. October, 2019.This example code is in the public domain.
*/#include <Servo_Hardware_PWM.h>Servo myServo1; //creating a servo object (any custom name could be used)
Servo myServo2;
Servo myServo3;
Servo myServo4;
Servo myServo5;
Servo myServo6;unsigned int valueMicros = 0; //variable that contains the microseconds
int valueDegrees = 0; //variable that contains the degreesvoid setup() {myServo1.attach(2); //attaches the servo to pin 2myServo2.attach(3);myServo3.attach(7);myServo4.attach(8);myServo5.attach(44);myServo6.attach(45);
}void loop() {//option 1 - using microseconds and the writeMicroseconds-function:for (valueMicros = 500; valueMicros < 2500; valueMicros++){ //goes from 500us to 2500us (0° to 180°)myServo1.writeMicroseconds(valueMicros); //writes the value of valueMicros to the servomyServo2.writeMicroseconds(valueMicros);myServo3.writeMicroseconds(valueMicros);myServo4.writeMicroseconds(valueMicros);myServo5.writeMicroseconds(valueMicros);myServo6.writeMicroseconds(valueMicros);delay(1);}//option 2 - using degrees and the write-function:for (valueDegrees = 180; valueDegrees > 0; valueDegrees--){ //goes from 180° to 0° (2500us to 500us)myServo1.write(valueDegrees); //writes the value of valueDegrees to the servomyServo2.write(valueDegrees);myServo3.write(valueDegrees);myServo4.write(valueDegrees);myServo5.write(valueDegrees);myServo6.write(valueDegrees);delay(10);}
}

感谢阅读———maxkz

Arduino:关于解决 pluseIn与官方舵机库冲突的办法相关推荐

  1. 解决eclipse快捷键Ctrl+Alt+Down冲突问题办法

    解决eclipse快捷键Ctrl+Alt+Down冲突问题办法 时间:2016-01-18 21:11:08      阅读:376      评论:0      收藏:0      [点我收藏+] ...

  2. ios 重复引用 静态库_iOS 解决一个因三方静态库冲突产生的duplicate symbol的问题...

    转自:http://www.cnblogs.com/rayshen/p/5160218.html 最近在开发项目时编译三方.a时出现了冲突,原因是存在duplicate symbol. <1&g ...

  3. 创造性解决arch/manjaro官方aur源下载慢办法

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/cou ...

  4. esp8266舵机驱动_使用Arduino和ESP8266通过网页控制舵机

    在本文中,我们将尝试通过网页来控制舵机,通过滑动网页上对应的滑块,促使舵机做相应地运动.这样的尝试在做一些远程开关或者远程控制的小设备时非常有用,比如把 宠物定时喂食器 改造成网页控制的也是可以实现的 ...

  5. 安卓集成云闪付,以及So库冲突多moudle项目解决办法

    安卓集成云闪付,以及So库冲突多moudle项目解决办法 支付流程介绍 1.无图无真相 2.前往银联下载修仙工具 3.权限和混淆配置 4.开始编写代码 5.完结撒花 支付流程介绍 交易流程如下图: 安 ...

  6. 解决PyCharm下载Python第三方库时速度慢的问题

    解决PyCharm下载Python第三方库时速度慢的问题 最近在PyCharm环境下下载Python包时频繁遇到time out类型的问题,现将解决方法描述如下: 打开Pycharm,点击File - ...

  7. 蓝桥杯备赛 | 官方题库基础练习(含VIP试题)试题+答案(共33题)

    引言 最近在进行蓝桥杯python组的备赛学习,做了官方题库中的基础练习,包括VIP题库,下面是所有试题和AC代码,所有代码都已经通过测试(VIP试题的测试方法见https://blog.csdn.n ...

  8. Arduino 机械爪 (适合初学者-舵机-蓝牙-触点开关-小喇叭)

    一张总括图 (寒假入坑Arduino一个月 想着做一个小东西练练手 便想出了这个小创意) 首先:介绍下本项目的实现内容: Arduino控制舵机转动角度 -> 舵机的角度转为机械爪的抓取角度 - ...

  9. jQuery与其它库冲突的解决方法(转)

    原文出处:http://www.jb51.net/article/24014.htm 在jQuery库中,几乎所有的插件都被限制在它的命名空间里.全局的对象都很好地存储在jQuery命名空间里,因此当 ...

最新文章

  1. python下载安装教程3.7.3-【最新】Python-3.7.0安装教程及下载链接
  2. 从 for of 聊到 Generator
  3. 基于Hadoop的云盘系统客户端技术选型说明
  4. 辨析Java与Javascript
  5. 点击回应、关闭确认以及另一种获取设备环境句柄的方法
  6. (转)C# 把我所积累的类库全部分享给博友(附件已经上传)
  7. c++ 三次多项式拟合_线性回归进阶版,多项式线性回归讲解与实现(附完整代码)...
  8. make pycaffe 报错:“fatal error: numpy/arrayobject.h: No such file or directory” 解决方案
  9. jq使用教程02_安装的问题
  10. python+selenium+unittest测试框架3-项目构建和发送邮件
  11. HDU6266 - Hakase and Nano 狄利克雷卷积
  12. python结巴怎么安装_Python3.6 结巴分词安装和使用
  13. 恩格列净治疗心衰获得FDA快速通道资格
  14. 计算机网络验证性实践
  15. Codeforces Gym 100015B Ball Painting 找规律
  16. win10卸载软件程序
  17. Windows 内网渗透之攻击域控
  18. 华为鸿蒙魔法闪投大小屏幕互动,「老熊科普」魔法闪投,荣耀智慧屏就是你的“超级大手机”...
  19. 什么是多态?为什么要使用多态?什么时候用多态?多态是如何实现的?使用多态有什么好处?
  20. Yii 用户登陆机制

热门文章

  1. 最新智云全能API接口PHP源码
  2. C++歌曲播放管理系统
  3. ThinkPad X1 Carbon 2021和X1 Nano有什么区别 哪个好详细性能配置对比
  4. 一步一步学习Android TV/盒子开发(一)
  5. linux mipi摄像头驱动,VS-RK3399 在linux系统下面调试Mipi camera接口介绍
  6. 物联网wifi模块一键配置
  7. Windows XP sp3 系统安装 Windbg 符号文件 Symbols 时微软失去支持的解决方案
  8. 以过来人的身份看高考,祝学弟学妹们高考加油!!!
  9. 寻找起伏度最佳窗口、使用arcpy求取地形起伏度的最佳统计单元
  10. (Scrapy框架)爬虫获取百度新冠疫情数据 | 爬虫案例