]> defiant.homedns.org Git - ros_wild_thumper.git/blobdiff - avr/motor_ctrl/main.c
avr: test i2c fixes for status = TW_NO_INFO
[ros_wild_thumper.git] / avr / motor_ctrl / main.c
index 3dca8cc80bb9fa380d3e9f0d3689a31d28929b09..f7c9f0ae73822057176994f8082b4660970988bc 100644 (file)
@@ -5,6 +5,10 @@
 #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 <avr/pgmspace.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,
@@ -149,6 +167,10 @@ static volatile int16_t speed1_wish=0; // step/s
 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;
@@ -163,6 +185,8 @@ static volatile uint8_t count_test=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=0;
+static volatile uint8_t last_i2c_status = 0;
 
 ISR(TWI_vect)
 {
@@ -171,13 +195,14 @@ ISR(TWI_vect)
        static ufloat_t tmp_speed;
        static ufloat_t tmp_angle;
 
-       switch (TWSR & 0xF8)
+       last_i2c_status = TW_STATUS;
+       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;
@@ -329,6 +354,7 @@ ISR(TWI_vect)
                                        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
@@ -362,10 +388,10 @@ ISR(TWI_vect)
                                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;
@@ -599,6 +625,11 @@ ISR(TWI_vect)
                        }
                        ireg++;
                        break;
+               case TW_SR_STOP:
+                       TWI_ACK;
+                       break;
+               case TW_NO_INFO:
+                       break;
                default:
                        TWI_RESET;
        }
@@ -694,18 +725,24 @@ static void update_motor(void) {
                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;
@@ -716,18 +753,24 @@ static void update_motor(void) {
                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;
@@ -738,18 +781,24 @@ static void update_motor(void) {
                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;
@@ -760,18 +809,24 @@ static void update_motor(void) {
                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;
@@ -859,26 +914,25 @@ static void update_pid(void) {
        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);
@@ -886,10 +940,15 @@ static void update_pid(void) {
        }       
 
        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;
@@ -897,17 +956,22 @@ static void update_pid(void) {
                        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;
@@ -915,17 +979,22 @@ static void update_pid(void) {
                        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;
@@ -933,17 +1002,22 @@ static void update_pid(void) {
                        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;
@@ -951,8 +1025,8 @@ static void update_pid(void) {
                        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;
                }
@@ -985,12 +1059,15 @@ int main(void) {
 
        // 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;
@@ -998,16 +1075,20 @@ int main(void) {
        // 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");
+       printf_P(PSTR("\r\nStart\r\n"));
 
        set_sleep_mode(SLEEP_MODE_IDLE);
        // Enable Timer 1 Overflow Interrupt
@@ -1019,10 +1100,11 @@ int main(void) {
                        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;
                }
@@ -1063,13 +1145,25 @@ int main(void) {
                        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;
+                               if (last_man_update_count == 100) {
+                                       printf_P(PSTR("I2C State: 0x%x\r\n"), last_i2c_status);
+                               }
+                               last_man_update_count = I2C_TIMEOUT_DISABLE;
+                       }
                }
 
                sleep_mode();