#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.06
-#define KI 0.10
+#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:47, real wheel diameter: 0.12454m
-#define STEP_PER_M_AVG 5766.1
-#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 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) > 140000 && speed1 == 0) {
+ if (labs(esum1) > STALL_LIMIT && speed1 == 0) {
motor1 = 0;
motor1_mode = MOTOR_MANUAL;
error_state |= (1<<4);
esum1 = 0;
}
- if (labs(esum2) > 140000 && speed2 == 0) {
+ if (labs(esum2) > STALL_LIMIT && speed2 == 0) {
motor2 = 0;
motor2_mode = MOTOR_MANUAL;
error_state |= (1<<5);
esum2 = 0;
}
- if (labs(esum3) > 140000 && 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) > 140000 && speed4 == 0) {
+ if (labs(esum4) > STALL_LIMIT && speed4 == 0) {
motor4 = 0;
motor4_mode = MOTOR_MANUAL;
error_state |= (1<<7);
speed1_wish_old = speed1_wish;
}
- if (speed1_wish == 0) {
+ 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;
error_state &= ~(1<<4);
speed2_wish_old = speed2_wish;
}
- if (speed2_wish == 0) {
+ 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;
error_state &= ~(1<<5);
speed3_wish_old = speed3_wish;
}
- if (speed3_wish == 0) {
+ 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;
error_state &= ~(1<<6);
speed4_wish_old = speed4_wish;
}
- if (speed4_wish == 0) {
+ 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;
error_state &= ~(1<<7);
// 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();