我在MG90S伺服器上有同样的问题(抖动),我的信号线相对较长(60〜70cm),在信号上放置一个103(10nF)的电容器,地线为我解决了这个问题(我将电容器放置在中间,在原始伺服电缆连接到我的内部电缆的位置)。
另外,我无法使用标准的Servo库,因为它在Arduino Mega上抓到的第一个定时器是Timer-5,我需要它来进行频率测量。由于我仅使用10个伺服器,因此我从伺服库中提取了关键代码,并将其更改为使用Timer-1(每个计时器在Mega上最多支持12个伺服器)。
下面是独立代码供参考,如果您想将其包含在自己的项目中,则只能使用顶部,下部是测试顶部(它侦听串行端口,可以给sX和vX命令,其中sX选择一个伺服,s0将选择第一个伺服,vX设置我们中的伺服位置,因此v1500会将伺服0设置为中间位置(假设您首先给出了s0命令)。
//----------------------------------------------------------------
// This is the actual servo code extracted from the servo library
//----------------------------------------------------------------
#include <avr/pgmspace.h>
//----converts microseconds to tick (assumes prescale of 8)
#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8)
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds
#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009
struct s_servar {
//----counter for the servo being pulsed for each timer (or -1 if refresh interval)
int8_t channel;
};
static volatile struct s_servar gl_vars;
//----maximum number of servos controlled by one timer
#define SERVOS_PER_TIMER 12
//----this can not be higher than SERVOS_PER_TIMER
#define SERVO_AMOUNT 6
struct s_servo {
volatile unsigned int ticks;
unsigned char pin;
};
struct s_servo gl_servos[SERVO_AMOUNT] = {
{ usToTicks(DEFAULT_PULSE_WIDTH), 22 },
{ usToTicks(DEFAULT_PULSE_WIDTH), 23 },
{ usToTicks(DEFAULT_PULSE_WIDTH), 24 },
{ usToTicks(DEFAULT_PULSE_WIDTH), 25 },
{ usToTicks(DEFAULT_PULSE_WIDTH), 26 },
{ usToTicks(DEFAULT_PULSE_WIDTH), 27 },
};
ISR(TIMER1_COMPA_vect) {
unsigned char servooff;
if(gl_vars.channel < 0 ) {
//----channel set to -1 indicated that refresh interval completed so reset the timer
TCNT1 = 0;
}
else{
servooff = gl_vars.channel;
if(servooff < SERVO_AMOUNT) {
//----end the pulse
digitalWrite(gl_servos[servooff].pin, LOW);
}
}
//----increment to the next channel
gl_vars.channel++;
servooff = gl_vars.channel;
if(servooff < SERVO_AMOUNT) {
//----set timer interrupt for pulse length
OCR1A = TCNT1 + gl_servos[servooff].ticks;
//----start the pulse
digitalWrite(gl_servos[servooff].pin, HIGH);
}
else {
// finished all channels so wait for the refresh period to expire before starting over
//----allow a few ticks to ensure the next OCR1A not missed
if(((unsigned)TCNT1) + 4 < usToTicks(REFRESH_INTERVAL)) {
OCR1A = (unsigned int)usToTicks(REFRESH_INTERVAL);
}
else {
//----at least REFRESH_INTERVAL has elapsed
OCR1A = TCNT1 + 4;
}
//----this will get incremented at the end of the refresh period to start again at the first channel
gl_vars.channel = -1;
}
}
void InitServoISR() {
unsigned char ct;
gl_vars.channel = -1;
//----init timer 1
TCCR1A = 0; // normal counting mode
TCCR1B = _BV(CS11); // set prescaler of 8
TCNT1 = 0; // clear the timer count
TIFR1 |= _BV(OCF1A); // clear any pending interrupts;
TIMSK1 |= _BV(OCIE1A); // enable the output compare interrupt
//----set all servo pins to output
for(ct = 0; ct < SERVO_AMOUNT; ct++) {
pinMode(gl_servos[ct].pin, OUTPUT);
}
}
void SetServoMicroSecs(unsigned char servooff, unsigned short value) {
uint8_t oldSREG;
if(servooff < SERVO_AMOUNT) {
//----ensure pulse width is in range
if(value < MIN_PULSE_WIDTH) { value = MIN_PULSE_WIDTH; }
else {
if(value > MAX_PULSE_WIDTH) { value = MAX_PULSE_WIDTH; }
}
value -= TRIM_DURATION;
value = usToTicks(value);
oldSREG = SREG;
cli();
gl_servos[servooff].ticks = value;
SREG = oldSREG;
}
}
//------------------------------------------------
// This is code to test the above servo functions
//------------------------------------------------
#define ERR_OK 0
#define ERR_UNKNOWN 1
#define ERR_OUTOFRANGE 2
#define SERDEBUG_CODE
#define MAX_SER_BUF 12
void setup() {
InitServoISR();
#ifdef SERDEBUG_CODE
Serial.begin(9600);
Serial.println(F("Start"));
#endif
}
void loop() {
#ifdef SERDEBUG_CODE
uint8_t ct, chr;
char buf[MAX_SER_BUF];
ct = 0;
#endif
//----main while loop
while(1) {
#ifdef SERDEBUG_CODE
//--------------------
// Serial Port
//--------------------
while (Serial.available() > 0) {
chr = Serial.read();
if(chr == '\n') {
ProcSerCmd(buf, ct);
ct = 0;
}
else {
//----if for some reason we exceed buffer size we wrap around
if(ct >= MAX_SER_BUF) { ct = 0; }
buf[ct] = chr;
ct++;
}
}
#endif
}
}
//------------------------------
// Serial Port Code
//------------------------------
#ifdef SERDEBUG_CODE
uint16_t RetrieveNumber(char *buf, uint8_t size) {
//--------------------------------------------------------------
// This function tries to convert a string into a 16 bit number
// Mainly for test so no strict checking
//--------------------------------------------------------------
int8_t ct;
uint16_t out, mult, chr;
out = 0;
mult = 1;
for(ct = size - 1; ct >= 0; ct--) {
chr = buf[ct];
if(chr < '0' || chr > '9') { continue; }
chr -= '0';
chr *= mult;
out += chr;
mult *= 10;
}
return(out);
}
void ProcSerCmd(char *buf, uint8_t size) {
//-----------------------------------------------------------
// supported test commands
// sX X = 0 to SERVO_AMOUNT Sets the servo for test
// vX X = MIN to MAX PULSE WIDTH Sets the test servo to value X
//-----------------------------------------------------------
static unsigned char lgl_servooff = 0;
uint8_t chr, errcode;
uint16_t value;
errcode = 0;
while(1) {
chr = buf[0];
//----test commands (used during development)
if(chr == 's') {
value = RetrieveNumber(buf + 1, size - 1);
if(value < 0 || value >= SERVO_AMOUNT) { errcode = ERR_OUTOFRANGE; break; }
lgl_servooff = (unsigned char)value;
break;
}
if(chr == 'v') {
value = RetrieveNumber(buf + 1, size - 1);
if(value < MIN_PULSE_WIDTH || value > MAX_PULSE_WIDTH) { errcode = ERR_OUTOFRANGE; break; }
SetServoMicroSecs(lgl_servooff, value);
break;
}
errcode = ERR_UNKNOWN;
break;
}
if(errcode == 0) {
Serial.println(F("OK"));
}
else {
Serial.write('E');
Serial.println(errcode);
}
}
#endif