Arduino:关于解决 pluseIn与官方舵机库冲突的办法
关于解决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与官方舵机库冲突的办法相关推荐
- 解决eclipse快捷键Ctrl+Alt+Down冲突问题办法
解决eclipse快捷键Ctrl+Alt+Down冲突问题办法 时间:2016-01-18 21:11:08 阅读:376 评论:0 收藏:0 [点我收藏+] ...
- ios 重复引用 静态库_iOS 解决一个因三方静态库冲突产生的duplicate symbol的问题...
转自:http://www.cnblogs.com/rayshen/p/5160218.html 最近在开发项目时编译三方.a时出现了冲突,原因是存在duplicate symbol. <1&g ...
- 创造性解决arch/manjaro官方aur源下载慢办法
Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/cou ...
- esp8266舵机驱动_使用Arduino和ESP8266通过网页控制舵机
在本文中,我们将尝试通过网页来控制舵机,通过滑动网页上对应的滑块,促使舵机做相应地运动.这样的尝试在做一些远程开关或者远程控制的小设备时非常有用,比如把 宠物定时喂食器 改造成网页控制的也是可以实现的 ...
- 安卓集成云闪付,以及So库冲突多moudle项目解决办法
安卓集成云闪付,以及So库冲突多moudle项目解决办法 支付流程介绍 1.无图无真相 2.前往银联下载修仙工具 3.权限和混淆配置 4.开始编写代码 5.完结撒花 支付流程介绍 交易流程如下图: 安 ...
- 解决PyCharm下载Python第三方库时速度慢的问题
解决PyCharm下载Python第三方库时速度慢的问题 最近在PyCharm环境下下载Python包时频繁遇到time out类型的问题,现将解决方法描述如下: 打开Pycharm,点击File - ...
- 蓝桥杯备赛 | 官方题库基础练习(含VIP试题)试题+答案(共33题)
引言 最近在进行蓝桥杯python组的备赛学习,做了官方题库中的基础练习,包括VIP题库,下面是所有试题和AC代码,所有代码都已经通过测试(VIP试题的测试方法见https://blog.csdn.n ...
- Arduino 机械爪 (适合初学者-舵机-蓝牙-触点开关-小喇叭)
一张总括图 (寒假入坑Arduino一个月 想着做一个小东西练练手 便想出了这个小创意) 首先:介绍下本项目的实现内容: Arduino控制舵机转动角度 -> 舵机的角度转为机械爪的抓取角度 - ...
- jQuery与其它库冲突的解决方法(转)
原文出处:http://www.jb51.net/article/24014.htm 在jQuery库中,几乎所有的插件都被限制在它的命名空间里.全局的对象都很好地存储在jQuery命名空间里,因此当 ...
最新文章
- python下载安装教程3.7.3-【最新】Python-3.7.0安装教程及下载链接
- 从 for of 聊到 Generator
- 基于Hadoop的云盘系统客户端技术选型说明
- 辨析Java与Javascript
- 点击回应、关闭确认以及另一种获取设备环境句柄的方法
- (转)C# 把我所积累的类库全部分享给博友(附件已经上传)
- c++ 三次多项式拟合_线性回归进阶版,多项式线性回归讲解与实现(附完整代码)...
- make pycaffe 报错:“fatal error: numpy/arrayobject.h: No such file or directory” 解决方案
- jq使用教程02_安装的问题
- python+selenium+unittest测试框架3-项目构建和发送邮件
- HDU6266 - Hakase and Nano 狄利克雷卷积
- python结巴怎么安装_Python3.6 结巴分词安装和使用
- 恩格列净治疗心衰获得FDA快速通道资格
- 计算机网络验证性实践
- Codeforces Gym 100015B Ball Painting 找规律
- win10卸载软件程序
- Windows 内网渗透之攻击域控
- 华为鸿蒙魔法闪投大小屏幕互动,「老熊科普」魔法闪投,荣耀智慧屏就是你的“超级大手机”...
- 什么是多态?为什么要使用多态?什么时候用多态?多态是如何实现的?使用多态有什么好处?
- Yii 用户登陆机制
热门文章
- 最新智云全能API接口PHP源码
- C++歌曲播放管理系统
- ThinkPad X1 Carbon 2021和X1 Nano有什么区别 哪个好详细性能配置对比
- 一步一步学习Android TV/盒子开发(一)
- linux mipi摄像头驱动,VS-RK3399 在linux系统下面调试Mipi camera接口介绍
- 物联网wifi模块一键配置
- Windows XP sp3 系统安装 Windbg 符号文件 Symbols 时微软失去支持的解决方案
- 以过来人的身份看高考,祝学弟学妹们高考加油!!!
- 寻找起伏度最佳窗口、使用arcpy求取地形起伏度的最佳统计单元
- (Scrapy框架)爬虫获取百度新冠疫情数据 | 爬虫案例