#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
+#include <util/twi.h>
+#include <avr/eeprom.h>
+#include <avr/wdt.h>
#include "uart.h"
/*
*/
-#define TWI_ACK TWCR = (1<<TWEA) | (1<<TWINT) | (1<<TWEN) | (1<<TWIE)
-#define TWI_RESET TWCR &= ~((1 << TWSTO) | (1 << TWEN)); TWI_ACK
-#define TWI_NAK TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE)
-
-#define KP 0.039
-#define KI 0.08
+#define KP 0.062
+#define KI 0.12
#define KD 0.0
#define PID_T 0.01
-// wheel diameter=12cm, encoder=48cpr, gear ratio=1:34, real wheel diameter: 0.12454m
-#define STEP_PER_M_AVG 4171.4
-#define STEP_PER_M_LEFT (STEP_PER_M_AVG)
-#define STEP_PER_M_RIGHT (STEP_PER_M_AVG)
-#define WHEEL_DIST 0.36923 // Real: 0.252
+// wheel diameter=12cm, encoder=48cpr, gear ratio=1:47
+// STEP_PER_M = 48*47/(d*pi)
+// Left real diameter: 0.12808, Right real diameter: 0.121
+#define STEP_PER_M 5573.0
+#define STEP_PER_M_LEFT (STEP_PER_M)
+#define STEP_PER_M_RIGHT (STEP_PER_M)
+#define WHEEL_DIST 0.39912 // Measured: 0.252
+#define PWM_BREAK INT16_MIN
+#define STALL_LIMIT 140000
+#define I2C_TIMEOUT_DISABLE 255
+
+#define TWI_ACK TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN) | (1<<TWIE)
+#define TWI_NAK TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE)
+#define TWI_RESET TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWSTO) | (1<<TWEN) | (1<<TWIE);
+#define ENABLE_PWM_MOTOR1 TCCR1A |= (1 << COM1A1)
+#define ENABLE_PWM_MOTOR2 TCCR1A |= (1 << COM1B1)
+#define ENABLE_PWM_MOTOR3 TCCR2 |= (1 << COM21);
+#define ENABLE_PWM_MOTOR4 TCCR0 |= (1 << COM01);
+#define DISABLE_PWM_MOTOR1 TCCR1A &= ~(1 << COM1A1)
+#define DISABLE_PWM_MOTOR2 TCCR1A &= ~(1 << COM1B1)
+#define DISABLE_PWM_MOTOR3 TCCR2 &= ~(1 << COM21);
+#define DISABLE_PWM_MOTOR4 TCCR0 &= ~(1 << COM01);
+
enum mode {
MOTOR_MANUAL,
static volatile int16_t speed2_wish=0;
static volatile int16_t speed3_wish=0;
static volatile int16_t speed4_wish=0;
+static volatile int16_t speed1_wish_old=0;
+static volatile int16_t speed2_wish_old=0;
+static volatile int16_t speed3_wish_old=0;
+static volatile int16_t speed4_wish_old=0;
static volatile uint8_t run_update=0;
static volatile int16_t speed1=0; // step/s
static volatile int16_t speed2=0;
static volatile uint8_t front_handicap=0;
static volatile uint8_t aft_handicap=0;
static volatile uint8_t error_state=0;
+static volatile uint8_t last_man_update_count=I2C_TIMEOUT_DISABLE;
ISR(TWI_vect)
{
static ufloat_t tmp_speed;
static ufloat_t tmp_angle;
- switch (TWSR & 0xF8)
+ switch(TW_STATUS)
{
- case 0x60: // start write
+ case TW_SR_SLA_ACK: // start write
TWI_ACK;
ireg = 0;
break;
- case 0x80: // write
+ case TW_SR_DATA_ACK: // write
switch(ireg) {
case 0x00: // register select
ireg = TWDR;
tmp_angle.i = tmp_angle.i << 8 | TWDR;
cmd_vel.angle = tmp_angle.f;
cmd_vel.bUpdate = 1;
+ last_man_update_count = 0;
TWI_ACK;
break;
case 0x90: // Motor 1 switch
default:
TWI_NAK;
}
- ireg++;
+ if (ireg < 0xff) ireg++;
break;
- case 0xA8: // start read
- case 0xB8: // read
+ case TW_ST_SLA_ACK: // start read
+ case TW_ST_DATA_ACK: // read
switch(ireg) {
case 0x02: // Motor 1 PWM
TWDR = OCR1A;
}
ireg++;
break;
+ case TW_SR_STOP:
+ TWI_ACK;
+ break;
default:
TWI_RESET;
}
if (motor1 == 0) {
// stop
PORTC &= ~(1 << 3) & ~(1 << 2);
+ DISABLE_PWM_MOTOR1;
+ } else if (motor1 == PWM_BREAK) {
+ PORTC |= (1 << 3) | (1 << 2);
+ ENABLE_PWM_MOTOR1;
} else if ((!motor1_switch && motor1 > 0) || (motor1_switch && motor1 < 0)) {
// forward
uint8_t tmp=PORTC;
tmp &= ~(1 << 3);
tmp |= (1 << 2);
PORTC = tmp;
+ ENABLE_PWM_MOTOR1;
} else { // motor1 < 0
// backward
uint8_t tmp=PORTC;
tmp &= ~(1 << 2);
tmp |= (1 << 3);
PORTC = tmp;
+ ENABLE_PWM_MOTOR1;
}
m1_old = motor1;
if (motor2 == 0) {
// stop
PORTC &= ~(1 << 5) & ~(1 << 4);
+ DISABLE_PWM_MOTOR2;
+ } else if (motor2 == PWM_BREAK) {
+ PORTC |= (1 << 5) | (1 << 4);
+ ENABLE_PWM_MOTOR2;
} else if ((!motor2_switch && motor2 > 0) || (motor2_switch && motor2 < 0)) {
// forward
uint8_t tmp=PORTC;
tmp &= ~(1 << 5);
tmp |= (1 << 4);
PORTC = tmp;
+ ENABLE_PWM_MOTOR2;
} else { // motor2 < 0
// backward
uint8_t tmp=PORTC;
tmp &= ~(1 << 4);
tmp |= (1 << 5);
PORTC = tmp;
+ ENABLE_PWM_MOTOR2;
}
m2_old = motor2;
if (motor3 == 0) {
// stop
PORTC &= ~(1 << 7) & ~(1 << 6);
+ DISABLE_PWM_MOTOR3;
+ } else if (motor3 == PWM_BREAK) {
+ PORTC |= (1 << 7) | (1 << 6);
+ ENABLE_PWM_MOTOR3;
} else if ((!motor3_switch && motor3 > 0) || (motor3_switch && motor3 < 0)) {
// forward
uint8_t tmp=PORTC;
tmp &= ~(1 << 7);
tmp |= (1 << 6);
PORTC = tmp;
+ ENABLE_PWM_MOTOR3;
} else { // motor3 < 0
// backward
uint8_t tmp=PORTC;
tmp &= ~(1 << 6);
tmp |= (1 << 7);
PORTC = tmp;
+ ENABLE_PWM_MOTOR3;
}
m3_old = motor3;
if (motor4 == 0) {
// stop
PORTD &= ~(1 << 3) & ~(1 << 2);
+ DISABLE_PWM_MOTOR4;
+ } else if (motor4 == PWM_BREAK) {
+ PORTD |= (1 << 3) | (1 << 2);
+ ENABLE_PWM_MOTOR4;
} else if ((!motor4_switch && motor4 > 0) || (motor4_switch && motor4 < 0)) {
// forward
uint8_t tmp=PORTD;
tmp &= ~(1 << 3);
tmp |= (1 << 2);
PORTD = tmp;
+ ENABLE_PWM_MOTOR4;
} else { // motor4 < 0
// backward
uint8_t tmp=PORTD;
tmp &= ~(1 << 2);
tmp |= (1 << 3);
PORTD = tmp;
+ ENABLE_PWM_MOTOR4;
}
m4_old = motor4;
static int32_t esum4=0;
// protect motors from damage if stalling
- if (labs(esum1) > 120000 && speed1 == 0) {
+ if (labs(esum1) > STALL_LIMIT && speed1 == 0) {
motor1 = 0;
motor1_mode = MOTOR_MANUAL;
error_state |= (1<<4);
esum1 = 0;
}
- if (labs(esum2) > 120000 && speed2 == 0) {
+ if (labs(esum2) > STALL_LIMIT && speed2 == 0) {
motor2 = 0;
motor2_mode = MOTOR_MANUAL;
error_state |= (1<<5);
esum2 = 0;
}
- if (labs(esum3) > 120000 && speed3 == 0) {
+ if (labs(esum3) > STALL_LIMIT && speed3 == 0) {
motor3 = 0;
motor3_mode = MOTOR_MANUAL;
error_state |= (1<<6);
esum3 = 0;
}
- // protect motors from damage if stalling
- if (labs(esum4) > 120000 && speed4 == 0) {
+ if (labs(esum4) > STALL_LIMIT && speed4 == 0) {
motor4 = 0;
motor4_mode = MOTOR_MANUAL;
error_state |= (1<<7);
}
if (motor1_mode == MOTOR_PID) {
- if (speed1_wish == 0) {
+ if (speed1_wish != speed1_wish_old) {
+ if (abs(speed1_wish - speed1_wish_old) > 500) esum1 = 0;
+ speed1_wish_old = speed1_wish;
+ }
+
+ uint8_t dir_change = (speed1_wish > 0 && speed1 < 0) || (speed1_wish < 0 && speed1 > 0); // Prevent dangerous immediate engine reverse
+ if (speed1_wish == 0 || dir_change) {
motor1 = 0;
eold1 = 0;
- esum1 = 0;
error_state &= ~(1<<4);
} else {
int16_t e = speed1_wish - speed1;
motor1 = KP*e + KI*PID_T*esum1 + KD/PID_T*(e - eold1);
eold1 = e;
- if (motor1 > 0 && speed1_wish < 0) motor1=0;
- else if (motor1 < 0 && speed1_wish > 0) motor1=0;
+ if (motor1 > 0 && speed1_wish < 0) motor1=PWM_BREAK;
+ else if (motor1 < 0 && speed1_wish > 0) motor1=PWM_BREAK;
else if (motor1 > 255) motor1 = 255;
else if (motor1 < -255) motor1 = -255;
}
}
if (motor2_mode == MOTOR_PID) {
- if (speed2_wish == 0) {
+ if (speed2_wish != speed2_wish_old) {
+ if (abs(speed2_wish - speed2_wish_old) > 500) esum2 = 0;
+ speed2_wish_old = speed2_wish;
+ }
+
+ uint8_t dir_change = (speed2_wish > 0 && speed2 < 0) || (speed2_wish < 0 && speed2 > 0); // Prevent dangerous immediate engine reverse
+ if (speed2_wish == 0 || dir_change) {
motor2 = 0;
eold2 = 0;
- esum2 = 0;
error_state &= ~(1<<5);
} else {
int16_t e = speed2_wish - speed2;
motor2 = KP*e + KI*PID_T*esum2 + KD/PID_T*(e - eold2);
eold2 = e;
- if (motor2 > 0 && speed2_wish < 0) motor2=0;
- else if (motor2 < 0 && speed2_wish > 0) motor2=0;
+ if (motor2 > 0 && speed2_wish < 0) motor2=PWM_BREAK;
+ else if (motor2 < 0 && speed2_wish > 0) motor2=PWM_BREAK;
else if (motor2 > 255) motor2 = 255;
else if (motor2 < -255) motor2 = -255;
}
}
if (motor3_mode == MOTOR_PID) {
- if (speed3_wish == 0) {
+ if (speed3_wish != speed3_wish_old) {
+ if (abs(speed3_wish - speed3_wish_old) > 500) esum3 = 0;
+ speed3_wish_old = speed3_wish;
+ }
+
+ uint8_t dir_change = (speed3_wish > 0 && speed3 < 0) || (speed3_wish < 0 && speed3 > 0); // Prevent dangerous immediate engine reverse
+ if (speed3_wish == 0 || dir_change) {
motor3 = 0;
eold3 = 0;
- esum3 = 0;
error_state &= ~(1<<6);
} else {
int16_t e = speed3_wish - speed3;
motor3 = KP*e + KI*PID_T*esum3 + KD/PID_T*(e - eold3);
eold3 = e;
- if (motor3 > 0 && speed3_wish < 0) motor3=0;
- else if (motor3 < 0 && speed3_wish > 0) motor3=0;
+ if (motor3 > 0 && speed3_wish < 0) motor3=PWM_BREAK;
+ else if (motor3 < 0 && speed3_wish > 0) motor3=PWM_BREAK;
else if (motor3 > 255) motor3 = 255;
else if (motor3 < -255) motor3 = -255;
}
}
if (motor4_mode == MOTOR_PID) {
- if (speed4_wish == 0) {
+ if (speed4_wish != speed4_wish_old) {
+ if (abs(speed4_wish - speed4_wish_old) > 500) esum4 = 0;
+ speed4_wish_old = speed4_wish;
+ }
+
+ uint8_t dir_change = (speed4_wish > 0 && speed4 < 0) || (speed4_wish < 0 && speed4 > 0); // Prevent dangerous immediate engine reverse
+ if (speed4_wish == 0 || dir_change) {
motor4 = 0;
eold4 = 0;
- esum4 = 0;
error_state &= ~(1<<7);
} else {
int16_t e = speed4_wish - speed4;
motor4 = KP*e + KI*PID_T*esum4 + KD/PID_T*(e - eold4);
eold4 = e;
- if (motor4 > 0 && speed4_wish < 0) motor4=0;
- else if (motor4 < 0 && speed4_wish > 0) motor4=0;
+ if (motor4 > 0 && speed4_wish < 0) motor4=PWM_BREAK;
+ else if (motor4 < 0 && speed4_wish > 0) motor4=PWM_BREAK;
else if (motor4 > 255) motor4 = 255;
else if (motor4 < -255) motor4 = -255;
}
// I2C
TWAR = 0x50;
- TWI_RESET;
+ TWI_ACK;
// Motor 1 & 2
- // Timer 1: Fast PWM non-inverting mode, Top=255 => 15.625kHz
+ // Also used for PWM frequency TIMER1_FREQ (F_CPU/256)
+ // Timer 1: Fast PWM non-inverting mode, Top=255 => 19.531kHz
// Prescaler=1
- TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM10);
+ //TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM10);
+ // Avoid narrow spike on extreme pwm value 0 by not setting COM1*1
+ TCCR1A = (1 << WGM10);
TCCR1B = (1 << WGM12) | (1 << CS10);
OCR1A = 0;
OCR1B = 0;
// Motor 3
// Timer 2: Fast PWM non-inverting mode, Top=255
// Prescaler=1
- TCCR2 = (1 << WGM21) | (1 << WGM20) | (1 << COM21) | (1 << CS20);
+ //TCCR2 = (1 << WGM21) | (1 << WGM20) | (1 << COM21) | (1 << CS20);
+ // Avoid narrow spike on extreme pwm value 0 by not setting COM21
+ TCCR2 = (1 << WGM21) | (1 << WGM20) | (1 << CS20);
OCR2 = 0;
// Motor 4
// Timer 0: Fast PWM non-inverting mode, Top=255
// Prescaler=1
- TCCR0 = (1 << WGM01) | (1 << WGM00) | (1 << COM01) | (1 << CS00);
+ //TCCR0 = (1 << WGM01) | (1 << WGM00) | (1 << COM01) | (1 << CS00);
+ // Avoid narrow spike on extreme pwm value 0 by not setting COM01
+ TCCR0 = (1 << WGM01) | (1 << WGM00) | (1 << CS00);
OCR0 = 0;
printf("\r\nStart\r\n");
case 0xff: // Magic reg that starts the bootloader
if (bootloader == 0xa5) {
cli();
- {
- void (*start)(void) = (void*)0x1800;
- start();
- }
+ // write mark to first area in eeprom
+ eeprom_write_byte((uint8_t*)0, 123);
+ eeprom_busy_wait();
+ // Use watchdog to restart
+ wdt_enable(WDTO_15MS);
}
break;
}
motor4_mode = MOTOR_PID;
}
- if (run_update >= 156) { // ~100Hz
+ if (run_update >= 195) { // TIMER1_FREQ/195 = ~100Hz
run_update=0;
update_pos();
update_pid();
update_motor();
count_test++;
+ if (last_man_update_count != I2C_TIMEOUT_DISABLE) last_man_update_count++;
+
+ if (last_man_update_count > 100) {
+ // ~1s without a new i2c command
+ cmd_vel.speed = 0;
+ cmd_vel.angle = 0;
+ cmd_vel.bUpdate = 1;
+ last_man_update_count = I2C_TIMEOUT_DISABLE;
+ }
}
sleep_mode();