/*************************************************
* _ _ _ _ _ _
* | | | | |_ ___ ___| |_|___
* | | | | | -_| -_| | | -_|
* |_____|_|_|___|___|_|_|___|
*
**************************************************/
#ifndef F_CPU
#define F_CPU 16000000
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include "fifo.h"
#include "adc.h"
#include "ars.h"
#include "uart.h"
#include "i2cmaster.h"
//ADC Inputs
#define Ad_kanal_batt 0
#define Ad_kanal_zgyro 1
#define Ad_kanal_adxl 3
#define Ad_kanal_gyro 5
#define Ad_kanal_fuss 7
#define Ad_kanal_rocker 6
#define Ad_kanal_roll_adxl 2
#define Ad_kanal_roll_gyro 4
//maximum Power, limit to 20 for first Tests, then you don't destroy your furniture
#define max_PWM 180 // 180
//limit the I-Part of the PID-Control
#define Drivesumlimit 39000
//Calculate Wheel_const = circumference of the wheel in cm * 93,75 (f.e. 126 cm * 93,75 = 11812,5) for correct speed
#define Wheel_const 11812
//Status of the Wheelie
#define m_standby 1
#define m_run 2
#define m_down 3 //power down the wheels
//footswitch is pressed under this level
#define Sw_down 50
int drive_r = 0;
int drive_l = 0;
signed long Anglecorrection=0;
float Angle_Rate=0;
float Roll_Angle_Rate=0;
float Balance_moment=0;
float Drive_sum=0;
int Drivespeed=0;
int Rockersq=0;
int Rockerold=0;
int Rocker_zero=0;
int Rocker_diff=0;
int Speed_sum=0;
int Rocker_sensivity=0;
int Rocker_position=0;
int Speed_error=0;
int Speed_diff=0;
int Speed_correction=0;
int Steeringsignal=0;
//the I²C-Values are stored in this Array (Speed, Current, and Direction)
uint8_t Speed_array[9]={ 0,0,0,0,0,0,0,0,0} ;
uint16_t* Speed_array16 = (uint16_t*)Speed_array;
#define Speed_left Speed_array16[0]
#define Speed_right Speed_array16[1]
#define Curr_left Speed_array16[2]
#define Curr_right Speed_array16[3]
uint8_t Out_array[44]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ;
int16_t* Out_array16 = (int16_t*)Out_array;
int Speed_left_out;
int Speed_right_out;
int Direction_left;
int Direction_right;
uint8_t cw_r;
uint8_t cw_l;
//This 6 values can controlled from the displayunit to improove PID and steering
int Parameter1=15; //Steering depends on speed higher Value, lower Steeringsignal
uint16_t P1 = 0x10;
int Parameter2=5; //Speederror from the Geartooths 27 higher Value, lower Correction
uint16_t P2 = 0x20;
int Parameter3=210;
uint16_t P3 = 0x30;
int Parameter4=10; //Gain P-Part
uint16_t P4 = 0x40;
int Parameter5=15; //Gain I-Part
uint16_t P5 = 0x50;
int Parameter6=1; //Gain D-Part
uint16_t P6 = 0x60;
uint8_t Mmode = m_standby;
uint8_t Errno = 0;
uint8_t Zaehler = 0;
uint16_t spl = 0;
uint16_t Ad_gyro = 0;
uint16_t Ad_adxl = 0;
uint16_t Ad_batt = 0;
uint16_t Ad_adxl_roll = 0;
uint16_t Ad_gyro_roll = 0;
uint16_t Ad_rocker = 0;
uint16_t Ad_z = 0;
uint16_t Z_zero = 0;
int Z_error = 0;
long Adxl_zero = 0;
long Roll_adxl_zero = 0;
long Gyro_zero = 0;
long Roll_gyro_zero = 0;
uint16_t Ad_swi = 0;
char Text[40];
char Inputstring[40];
uint8_t StringCounter = 0;
float roll_adxl_sensor = 0;
float adxl_sensor = 0;
float tilt_angle = 0.0;
float roll_angle = 0.0;
char s[20];
char t[20];
float dt;
struct Gyro1DKalman filter;
struct Gyro1DKalman filterX;
int x_error;
void Init(void);
void startup(void);
void set_pwm(void);
void uart_puts (const char *s);
int main(void);
void algo(void);
void Err_value(void);
void Steering(void);
void Process(void);
void Gear(void);
void Parser(void);
void saveData(void);
/******************************************************************
* Init:
*
* set the Registers
******************************************************************/
void Init(void)
{
DDRA = 0B00000000; // PA0..PA7: ADC // PA7 unbelegt
PORTA = 0; // no Pull-ups
DDRB = 0B00011100;
PORTB = 0B11100011;
DDRC = 0B00001100;
PORTC = 0B11111111;
DDRD = 0B11111010;
PORTD = 0B00000101;
TCCR0 |= (1<<WGM01);
OCR0 = 156; // 10ms Period
TCCR1A = 0xb1; //
TCCR1B = 0x01; //
OCR1AL = 255;
OCR1BL = 0;
Adc_init();
int i = 0;
for(i = 0;i<10;i++)
{
Ad_swi += ReadChannel(Ad_kanal_fuss);
_delay_ms(1);
}
Ad_swi /= 10;
if (Ad_swi < 100)
{
Errno = 4;
uart_puts ("2:11=14");
//Err_value();
}
i2c_init();
eeprom_read_block(&Parameter1,(uint8_t *)P1,sizeof(int));
if (Parameter1 == -1) //no Data in EEPROM
{
Parameter1 = 14; // write the Standardvalues to EEPROM
saveData();
}
else
{
eeprom_read_block(&Parameter2,(uint8_t *)P2,sizeof(int));
eeprom_read_block(&Parameter3,(uint8_t *)P3,sizeof(int));
//eeprom_read_block(&Parameter4,(uint8_t *)P4,sizeof(int));
//eeprom_read_block(&Parameter5,(uint8_t *)P5,sizeof(int));
//eeprom_read_block(&Parameter6,(uint8_t *)P6,sizeof(int));
}
}
/*********************************************************************
* startup:
*
* calculate the zeropoints from the ADCs
*********************************************************************/
void startup(void)
{
int i = 0;
/*
Ad_rocker = 0;
Rocker_zero = 0;
for (i = 0; i<10; i++)
{
Rocker_zero += ReadChannel(Ad_kanal_rocker);
_delay_ms(3);
}
Rocker_zero /= 10;
*/
Rocker_zero = 434; //the rocker is exact vertical to ground
Ad_gyro = 0;
Gyro_zero = 0;
for (i = 0;i < 100;i++)
{
Gyro_zero += ReadChannel(Ad_kanal_gyro);
_delay_ms(3);
}
Gyro_zero /= i;
Ad_adxl = 0;
Adxl_zero = 0;
for (i = 0;i < 100;i++)
{
Adxl_zero += ReadChannel(Ad_kanal_adxl);
_delay_ms(3);
}
Adxl_zero /= i;
Ad_z = 0;
Z_zero = 0;
for (i = 0;i < 100;i++)
{
Z_zero += ReadChannel(Ad_kanal_zgyro);
_delay_ms(3);
}
Z_zero /= i;
/*Ad_gyro_roll = 0;
Roll_gyro_zero = 0;
for (i = 0;i < 100;i++)
{
Roll_gyro_zero += (1024 - ReadChannel(Ad_kanal_roll_gyro));
_delay_ms(3);
}
Roll_gyro_zero /= i;
*/
Roll_gyro_zero = 550; //the platform is exact horizontal to ground
/*
Ad_adxl_roll = 0;
Roll_adxl_zero = 0;
for (i = 0;i < 100;i++)
{
Roll_adxl_zero += ReadChannel(Ad_kanal_roll_adxl);
_delay_ms(3);
}
Roll_adxl_zero /= i;
*/
Roll_adxl_zero = 507; //the platform is exact horizontal to ground
}
/************************************************************************
* main:
*
* Mainloop
************************************************************************/
int main(void)
{
_delay_ms(1000);
Init();
startup();
PORTD &= ~(1 << PD3); //turn on 15V
uart_init();
init_Gyro1DKalman(&filter, 0.0001, 0.0003, 0.3);
init_Gyro1DKalman(&filterX, 0.0001, 0.0003, 0.3);
dt = 0.0157; // time passed in s since last call
sei();
while (1)
{
if ( Mmode != m_standby){
sprintf( Text, "2:1=%i\r\n", (int)tilt_angle);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:2=%i\r\n", (int)roll_angle);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:3=%i\r\n", (int)Z_error);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:4=%i\r\n", (int)Rockersq);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:5=%i\r\n", (int)Speed_error);
uart_puts (Text);
/* _delay_ms(6);
sprintf( Text, "2:6=%i\r\n", (int)Gyro_zero);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:7=%i\r\n", (int)drive_r);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:8=%i\r\n", (int)drive_l);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:9=%i\r\n", (int)Speed_left_out);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:10=%i\r\n", (int)Speed_right_out);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:15=%i\r\n", (int)Curr_left);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:16=%i\r\n", (int)Curr_right);
uart_puts (Text);
_delay_ms(6);*/
}
else
{
sprintf( Text, "2:17=%i\r\n", (int)Parameter1);
//uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:18=%i\r\n", (int)Parameter2);
//uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:19=%i\r\n", (int)Parameter3);
//uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:20=%i\r\n", (int)Parameter4);
//uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:21=%i\r\n", (int)Parameter5);
//uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:22=%i\r\n", (int)Parameter6);
//uart_puts (Text);
Parser();
_delay_ms(6);
}
Process();
sprintf( Text, "2:12=%i\r\n", (int)Ad_batt);
//uart_puts (Text);
_delay_ms(6);
}
}
/***************************************************************
* ISR:
*
* Interrupt all 10ms
***************************************************************/
ISR(TIMER0_COMP_vect)
{
//get all values from the ADCs
Ad_gyro = ReadChannel(Ad_kanal_gyro);
Ad_adxl = ReadChannel(Ad_kanal_adxl);
Ad_z = ReadChannel(Ad_kanal_zgyro);
Ad_adxl_roll = ReadChannel(Ad_kanal_roll_adxl);
Ad_gyro_roll = 1024 - ReadChannel(Ad_kanal_roll_gyro);
Ad_rocker = ReadChannel(Ad_kanal_rocker);
Ad_swi = 1024 - ReadChannel(Ad_kanal_fuss);
Ad_batt = ReadChannel(Ad_kanal_batt);
//kill the offset and calculate the Gains, so that all values are ° or °/s
// 3.0V/1025/0.002V for IDG300
Angle_Rate = ((float)(Ad_gyro-Gyro_zero))* 1.4634135;
Roll_Angle_Rate = ((float)(Ad_gyro_roll-Roll_gyro_zero))* 1.4634135;
// 3.0V/1025/0.0019333V 0,348V for 180° = 1,9333mV per ° for ADXL320
// 3.0V/1025/0.0046666V 0,840V for 180° = 4,6666mV per ° for ADXL322
adxl_sensor = ((float)(Ad_adxl-Adxl_zero))* 0.6272; //* 1.5139;
roll_adxl_sensor = ((float)(Ad_adxl_roll-Roll_adxl_zero))* 0.6272; //* 1.5139;
//the two Kalman steps: predict and update
ars_predict(&filter, Angle_Rate, dt); // Kalman predict
ars_predict(&filterX, Roll_Angle_Rate, dt); // Kalman predict
tilt_angle = ars_update(&filter, adxl_sensor);
roll_angle = ars_update(&filterX, roll_adxl_sensor);// * 2.6; //Anpassung ans Lenkerpoti
//Z-Axis
Z_error = Ad_z - Z_zero ;
algo();
Gear();
Steering();
set_pwm();
}
/**************************************************************************
* algo:
*
* PID-Controller
***************************************************************************/
void algo(void)
{
//The I-Part of the Controller
Drive_sum += tilt_angle;
if(Drive_sum > Drivesumlimit)Drive_sum = Drivesumlimit; //you have to limit the I-Controller
if(Drive_sum < -Drivesumlimit)Drive_sum = -Drivesumlimit; //Drive_sum max = 255*150
Drivespeed = (int)(tilt_angle * Parameter4 + Drive_sum * Parameter5 / 10);
}
/**************************************************************************
* Gear:
*
* get Speed from the Geartoothsensors
**************************************************************************/
void Gear(void)
{
uint8_t i = 0;
i2c_start_wait(0x41);
for (i=0;i<8;i++) // get the 8 Bytes from the I2C-Slave
{
Speed_array[i] = i2c_readAck();
}
Speed_array[i] = i2c_readNak(); // get the 9th Byte
i2c_stop();
//calculate Speed*10
Speed_left_out = Wheel_const / Speed_left;
Speed_right_out = Wheel_const / Speed_right;
//evaluate the Direction-byte
if ((Speed_array[8] & 0x08) != 0) Speed_right_out = -Speed_right_out;
if ((Speed_array[8] & 0x10) != 0) Speed_right_out = 0; //no Direction detected
if ((Speed_array[8] & 0x40) != 0) Speed_left_out = -Speed_left_out;
if ((Speed_array[8] & 0x80) != 0) Speed_left_out = 0; //no Direction detected
Speed_sum -= Speed_sum / 5;
Speed_sum += Speed_right_out + Speed_left_out;
Speed_diff -= (Speed_diff / 10);
Speed_diff += (Speed_left_out - Speed_right_out);
Speed_correction = Speed_diff / 22; //22 ist ausprobiert, bis aufgebockt bei jedem Lenkereinschlag Null rauskommt
}
/****************************************************************************
* Steering:
*
* calculate the steering
****************************************************************************/
void Steering(void)
{
int Buf1;
//Ad_rocker = Rocker_zero;
Rockersq = Rocker_zero - Ad_rocker;// + (int)roll_angle;
x_error = Rockersq;
Speed_error = Rockersq - Speed_correction;
Buf1 = abs(Speed_sum / 70);
if (Buf1 > 20) Buf1 = 20;
Rocker_sensivity = 22 - Buf1;
Rocker_sensivity = Rocker_sensivity / 2;
if (Rockersq > 45) Rockersq = 45;
if (Rockersq < -45) Rockersq = -45;
Rocker_position = Rockersq;
Rockersq = Rockersq * Rocker_sensivity;
Rockersq = Rockersq / 15;
Z_error /= 2;
if (Speed_error > 40) Speed_error = 40;
if (Speed_error < -40) Speed_error = -40;
Steeringsignal = Rockersq + Speed_error + Z_error;
drive_r = Drivespeed - Steeringsignal;
drive_l = Drivespeed + Steeringsignal;
}
/********************************************************************************
* Process:
*
* get and set the Status
********************************************************************************/
void Process(void)
{
int i;
switch(Mmode)
{
case m_standby:
drive_l = 0;
drive_r = 0;
Drivespeed = 0;
Drive_sum = 0;
set_pwm();
sprintf( Text, "2:11=Standby\r\n");
//uart_puts (Text);
Ad_swi = 1024 - ReadChannel(Ad_kanal_fuss);
Ad_batt = ReadChannel(Ad_kanal_batt);
if (Ad_swi > Sw_down){
startup();
TCNT0 = 0;
TCCR0 |= (1<<CS00)|(1<<CS02)|(1<<WGM01); //Timer0 starten
TIMSK |= (1<<OCIE0); // enable Interrupt
Mmode = m_run;
sprintf( Text, "2:11=Drive!\r\n");
uart_puts (Text);
}
break;
case m_run:
if (Ad_swi < Sw_down) {Mmode = m_down;}
break;
case m_down:
TCCR0 &= ~(1<<CS00)|(1<<CS02)|(1<<WGM01); //Timer0 ausschalten
TIMSK &= ~(1<<OCIE0); // enable Interrupt
for(i = 0; i < 255; i++)
{
if (drive_l == 0 && drive_r == 0) {goto Shutdown;}
if (drive_l > 0) { drive_l--;}
if (drive_l < 0) { drive_l++;}
if (drive_r > 0) { drive_r--;}
if (drive_r < 0) { drive_r++;}
_delay_ms(25);
set_pwm();
}
Shutdown:
Mmode = m_standby;
break;
default:
Mmode = m_down;
}
}
/**********************************************************************************
* set_pwm:
*
* set the PWMs and the Directionbits
***********************************************************************************/
void set_pwm(void)
{
int l = 0;
int r = 0;
if(drive_l > max_PWM)drive_l = max_PWM;
if(drive_l < -max_PWM)drive_l = -max_PWM;
if(drive_r > max_PWM)drive_r = max_PWM;
if(drive_r < -max_PWM)drive_r = -max_PWM;
r = abs(drive_r);
l = abs(drive_l);
if(drive_r < 0) // Check direction
{
PORTD |= (1<<PD6);
cw_r = 1;
}
else
{
PORTD &= ~(1<<PD6);
cw_r = 0;
}
if(drive_l < 0) // Check direction
{
PORTD |= (1<<PD7);
cw_l = 1;
}
else
{
PORTD &= ~(1<<PD7);
cw_l = 0;
}
OCR1AL = 255 - r;
OCR1BL = l;
}
/********************************************************************************
* Err_value:
*
* throw the program in an black whole
********************************************************************************/
void Err_value(void)
{
while(1) { _delay_ms(2000);}
}
/********************************************************************************
* Parser:
*
* get Commands from USART
********************************************************************************/
void Parser(void)
{
char *ptr;
char delimiter[] = ":=";
uint8_t Channel;
int Wert;
int NextChar;
NextChar = uart_getc_nowait();
while(NextChar != 10 && NextChar !=-1 && StringCounter < 40)
{
if (NextChar != '\0' && NextChar != 13) {Inputstring[StringCounter++] = (unsigned char)NextChar;}
NextChar = uart_getc_nowait();
}
if (NextChar == 10)
{
if (Inputstring[1] != 58 || StringCounter > 39) {goto Ausgang;}
Inputstring[StringCounter++] = '\0';
ptr = strtok(Inputstring, delimiter);
if(ptr != NULL)
{
if (atoi(ptr)==0)
{
ptr = strtok(NULL, delimiter);
Channel = atoi(ptr);
ptr = strtok(NULL, delimiter);
Wert = atoi(ptr);
if (Channel == 17) Parameter1 = Wert;
if (Channel == 18) Parameter2 = Wert;
if (Channel == 19) Parameter3 = Wert;
if (Channel == 20) Parameter4 = Wert;
if (Channel == 21) Parameter5 = Wert;
if (Channel == 22) Parameter6 = Wert;
if (Channel == 16)
{
saveData();
sprintf( Text, "2:11=STORED!\r\n");
uart_puts (Text);
_delay_ms(500);
Zaehler =0;
}
sprintf( Text, "2:20=%i\r\n", (int)Parameter4);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:21=%i\r\n", (int)Parameter5);
uart_puts (Text);
_delay_ms(6);
sprintf( Text, "2:22=%i\r\n", (int)Parameter6);
uart_puts (Text);
}
}
Ausgang:
Inputstring[0] = '\0';
StringCounter = 0;
}
}
/***************************************************************************************
* saveData:
*
* store all Parameters in EEPROM
***************************************************************************************/
void saveData(void)
{
eeprom_write_block(&Parameter1,(uint8_t *)P1,sizeof(int));
eeprom_write_block(&Parameter2,(uint8_t *)P2,sizeof(int));
eeprom_write_block(&Parameter3,(uint8_t *)P3,sizeof(int));
eeprom_write_block(&Parameter4,(uint8_t *)P4,sizeof(int));
eeprom_write_block(&Parameter5,(uint8_t *)P5,sizeof(int));
eeprom_write_block(&Parameter6,(uint8_t *)P6,sizeof(int));
}