我一直在宽松地关注 ROS 上的 Articulated Robotics 系列,并决定使用他的 ros_arduino_bridge 代码通过串行方式从 ROS2 驱动我的电机到我的 Arduino Mega 2560。无论如何,我的机器人使用 4 轮麦克纳姆驱动,所以代码确实需要修改。
除了编码器代码之外,一切看起来都很简单,可以更改,因为它利用了我不熟悉的 Arduino 概念。从那以后我学到了一些,但不是100%。以下是我认为与编码器代码相关的所有主要部分。
在设置中:
void setup() {
...
//set as inputs
DDRD &= ~(1<<LEFT_ENC_PIN_A);
DDRD &= ~(1<<LEFT_ENC_PIN_B);
DDRC &= ~(1<<RIGHT_ENC_PIN_A);
DDRC &= ~(1<<RIGHT_ENC_PIN_B);
//enable pull up resistors
PORTD |= (1<<LEFT_ENC_PIN_A);
PORTD |= (1<<LEFT_ENC_PIN_B);
PORTC |= (1<<RIGHT_ENC_PIN_A);
PORTC |= (1<<RIGHT_ENC_PIN_B);
// tell pin change mask to listen to left encoder pins
PCMSK2 |= (1 << LEFT_ENC_PIN_A)|(1 << LEFT_ENC_PIN_B);
// tell pin change mask to listen to right encoder pins
PCMSK1 |= (1 << RIGHT_ENC_PIN_A)|(1 << RIGHT_ENC_PIN_B);
// enable PCINT1 and PCINT2 interrupt in the general interrupt mask
PCICR |= (1 << PCIE1) | (1 << PCIE2);
...
}
在econder_driver.h中:
...
//below can be changed, but should be PORTD pins;
//otherwise additional changes in the code are required
#define LEFT_ENC_PIN_A PD2 //pin 2
#define LEFT_ENC_PIN_B PD3 //pin 3
//below can be changed, but should be PORTC pins
#define RIGHT_ENC_PIN_A PC4 //pin A4
#define RIGHT_ENC_PIN_B PC5 //pin A5
...
在encoder_driver.ino中:
volatile long left_enc_pos = 0L;
volatile long right_enc_pos = 0L;
static const int8_t ENC_STATES [] = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0}; //encoder lookup table
/* Interrupt routine for LEFT encoder, taking care of actual counting */
ISR (PCINT2_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PIND & (3 << 2)) >> 2; //read the current state into lowest 2 bits
left_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
/* Interrupt routine for RIGHT encoder, taking care of actual counting */
ISR (PCINT1_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PINC & (3 << 4)) >> 4; //read the current state into lowest 2 bits
right_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
/* Wrap the encoder reading function */
long readEncoder(int i) {
if (i == LEFT) return left_enc_pos;
else return right_enc_pos;
}
...
我采用了猴脑策略,仅添加另外两个 DDRB 和 DDRA,以及另外两个电机的 PORTB 和 PORTA 分配,并添加另外两个 ICR 功能。我不太确定如何进行位掩码和移位以及功能的填充。尽管据我所知,Mega 可能甚至没有足够的中断引脚来容纳 4 个电机。遗憾的是,我无法用实际的电机进行实际测试,这是一个有趣的问题,所以我希望有人可以帮助我。
ros_arduino_bridge 主要是为 Arduino Uno Atmega328p 编码的,您可以从所选的引脚及其对应的 PCINT 中看到这一点
ATmega328P 微控制器具有三个引脚变化中断向量: PCINT0_vect:适用于引脚 PCINT0-7(物理引脚 23-30) PCINT1_vect:适用于引脚 PCINT8-14(物理引脚 1-7) PCINT2_vect:适用于引脚 PCINT16-23(物理引脚 8-14) ATmega328P上共有24个引脚可以触发引脚变化中断,但它们被分为这三个向量。您应该喜欢 Arduino uno 引脚排列以了解更多信息。
您始终可以配置多对引脚来触发编码器更改。它不一定是 PD2 和 PD3,例如仅由 PCINT2_vect 处理。您可以在那里处理其他引脚。
如果您使用 Arduino Uno,您可以执行以下操作。您可以使用分组在 PCINT0_vect 中的 PCINT0-3 并将其交叉到引脚 PB0-4(uno 头上的引脚 8-11)并连接两个新闻编码器。 (ofc 假设它们没有被使用 - 如果它们被使用,你就会明白这个概念并可以找到其他的)。
设置它们
// Set PB0-PB3 as input pins
DDRB &= ~((1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3));
// Enable pin change interrupts for PB0-PB3
PCMSK0 |= (1 << PCINT0) | (1 << PCINT1) | (1 << PCINT2) | (1 << PCINT3);
// Enable pin change interrupt for PCINT0_vect
PCICR |= (1 << PCIE0);
// Enable global interrupts
sei();
ISR 可以是这样的
volatile long left_enc_pos = 0L;
volatile long right_enc_pos = 0L;
// encoder lookup table
static const int8_t ENC_STATES[] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0};
ISR(PCINT0_vect) {
static uint8_t prev_state = 0;
uint8_t current_state = PINB & 0x0F; // Read PB0-PB3 pins
// Update enc_last based on the changes in the state of PB0-PB3
uint8_t change = current_state ^ prev_state;
// Check for changes on PB0 and PB1 (Left encoder)
if (change & ((1 << PB0) | (1 << PB1))) {
uint8_t state = (current_state >> PB0) & 0x03; // Extract the state of PB0 and PB1
left_enc_pos += ENC_STATES[state];
}
// Check for changes on PB2 and PB3 (Right encoder)
if (change & ((1 << PB2) | (1 << PB3))) {
uint8_t state = (current_state >> PB2) & 0x03; // Extract the state of PB2 and PB3
right_enc_pos += ENC_STATES[state];
}
// Store the current state for the next iteration
prev_state = current_state;
}
如果您想使用 Arduino Mega,您必须配置用于所有 4 个编码器的引脚和 PINCT#_Vect。现有的和新的也是如此。您可以查看大型引脚图并轻松找出数字。 PCINT 在 Arduino Mega 中分组,类似于 UNO,只是映射到不同的端口和 PIN 码