MultiWii

Gene
mahowik:

Эксперименты всегда хорошо, но реально для достойной стабилизации самплинг в 200-300гц с головой и для защиты от вибраций на гиру нч фильтр внутренний 42гц ставится…

НЧ фильтр работает, это проверено… Скорость сэмплирования желательно сделать больше именно из-за количества степов, это сильно упростит код и упростит настройку драйвера степпера – иначе для скорости придется постоянно переключать степпинг, а это чревато вибрациями. Я его отладил именно для 1/16, и это потребовало даже некоторой перепайки. Ну по крайней мере было бы ОЧЕНЬ желательно увеличить сэмплинг. С обычными сервами такой проблемы конечно даже не возникло бы, но со степперами другое дело…

драйвер для 6050 и иму можно взять из сор сов вия соот-но…

Так вроде бы там эта же библиотека и используется, нет? Александр, Вы специалист, Вы можете сказать, с какой примерно частотой производится сэмплирование в стандартном софте мультивия? Если с такой же, как у меня так может и копать не нужно…

mahowik
Gene:

с какой примерно частотой производится сэмплирование в стандартном софте мультивия?

примерно через 2-5мс, в зависимости от навески… т.е. 200-500гц…

Gene
mahowik:

примерно через 2-5мс, в зависимости от навески… т.е. 200-500гц…

Получается столько же, значит, смысла копать нет. Попробую в таком случае обойти библиотеку и читать нужные данные непосредственно из регистров…

mahowik

в Sensors.ino есть все что надо и I2C драйвер и драйвер mpu6050… все в одном и без либ 😉

Gene
mahowik:

в Sensors.ino есть все что надо и I2C драйвер и драйвер mpu6050… все в одном и без либ 😉

Возможно я тупой, но не смог разобраться в коде 😦 Один кусок кода тянет за собой другой, и так до бесконечности… до конца цепочки я добраться не смог. все что мне нужно – считать значение регистра…

mataor
Gene:

Скорость сэмплирования желательно сделать больше именно из-за количества степов, это сильно упростит код и упростит настройку драйвера степпера

возьмите хмегу - там аппаратная поддержка 2 шаговых/трехфазных моторов, да и цикл более шустрый получается

Gene:

но не смог разобраться в коде Один кусок кода тянет за собой другой, и так до бесконечности…

там весь код в шапке сенсор.ино, все остальное чего оно хочет - в основном переменные определенные в мультивий.ино и дефайны из деф.н и сонфиг.н

Gene
mataor:

возьмите хмегу - там аппаратная поддержка 2 шаговых/трехфазных моторов, да и цикл более шустрый получается

Возможно в след. версии 😃

там весь код в шапке сенсор.ино, все остальное чего оно хочет - в основном переменные определенные в мультивий.ино и дефайны из деф.н и сонфиг.н

Я как Остап Бендер, только он играл в шахматы, а я программирую второй раз в жизни 😃 Не для моего нетренированного мозга разбираться в этом. Код я воспринимаю и имею шанс понять, только когда вижу его целиком перед глазами. Когда он начинает разбегаться по всяческим инклюдам и дефайнам, пиши пропало, нить потеряна – могу только собезьянничать, не понимая что происходит на самом деле.

mataor

хм… ну вообще если нужно чисто и2с и получить 6 данных с датчиков то тебе нужно:

  1. определится с переменными, со знаками и направлениями
uint8_t rawADC[6];
static int16_t  gyroADC[3],accADC[3];
#define MPU6050_ADDRESS     0x68
#define MPU6050
#define ACC_ORIENTATION(Y, X, Z)  {accADC[ROLL]  = -Y; accADC[PITCH]  = -X; accADC[YAW]  =  Z;}
#define GYRO_ORIENTATION(Y, X, Z) {gyroADC[ROLL] =  X; gyroADC[PITCH] = -Y; gyroADC[YAW] = -Z;}
  1. взять основные ф-ции и2с
// ************************************************************************************************************
// I2C general functions
// ************************************************************************************************************

void i2c_init(void) {
  #if defined(INTERNAL_I2C_PULLUPS)
    I2C_PULLUPS_ENABLE
  #else
    I2C_PULLUPS_DISABLE
  #endif
  TWSR = 0;                                    // no prescaler => prescaler = 1
  TWBR = ((F_CPU / I2C_SPEED) - 16) / 2;   // change the I2C clock rate
  TWCR = 1<<TWEN;                              // enable twi module, no interrupt
}

void i2c_rep_start(uint8_t address) {
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) ; // send REPEAT START condition
  waitTransmissionI2C();                       // wait until transmission completed
  TWDR = address;                              // send device address
  TWCR = (1<<TWINT) | (1<<TWEN);
  waitTransmissionI2C();                       // wail until transmission completed
}

void i2c_stop(void) {
  TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
  //  while(TWCR & (1<<TWSTO));                // <- can produce a blocking state with some WMP clones
}

void i2c_write(uint8_t data ) {
  TWDR = data;                                 // send data to the previously addressed device
  TWCR = (1<<TWINT) | (1<<TWEN);
  waitTransmissionI2C();
}

uint8_t i2c_read(uint8_t ack) {
  TWCR = (1<<TWINT) | (1<<TWEN) | (ack? (1<<TWEA) : 0);
  waitTransmissionI2C();
  uint8_t r = TWDR;
  if (!ack) i2c_stop();
  return r;
}

uint8_t i2c_readAck() {
  return i2c_read(1);
}

uint8_t i2c_readNak(void) {
  return i2c_read(0);
}

void waitTransmissionI2C() {
  uint16_t count = 255;
  while (!(TWCR & (1<<TWINT))) {
    count--;
    if (count==0) {              //we are in a blocking state => we don't insist
      TWCR = 0;                  //and we force a reset on TWINT register
      neutralizeTime = micros(); //we take a timestamp here to neutralize the value during a short delay
      i2c_errors_count++;
      break;
    }
  }
}

size_t i2c_read_to_buf(uint8_t add, void *buf, size_t size) {
  i2c_rep_start((add<<1) | 1);	// I2C read direction
  size_t bytes_read = 0;
  uint8_t *b = (uint8_t*)buf;
  while (size--) {
    /* acknowledge all but the final byte */
    *b++ = i2c_read(size > 0);
    /* TODO catch I2C errors here and abort */
    bytes_read++;
  }
  return bytes_read;
}

size_t i2c_read_reg_to_buf(uint8_t add, uint8_t reg, void *buf, size_t size) {
  i2c_rep_start(add<<1); // I2C write direction
  i2c_write(reg);        // register selection
  return i2c_read_to_buf(add, buf, size);
}

/* transform a series of bytes from big endian to little
   endian and vice versa. */
void swap_endianness(void *buf, size_t size) {
  /* we swap in-place, so we only have to
  * place _one_ element on a temporary tray
  */
  uint8_t tray;
  uint8_t *from;
  uint8_t *to;
  /* keep swapping until the pointers have assed each other */
  for (from = (uint8_t*)buf, to = &from; from < to; from++, to--) {
    tray = *from;
    *from = *to;
    *to = tray;
  }
}

void i2c_getSixRawADC(uint8_t add, uint8_t reg) {
  i2c_read_reg_to_buf(add, reg, &rawADC, 6);
}

void i2c_writeReg(uint8_t add, uint8_t reg, uint8_t val) {
  i2c_rep_start(add<<1); // I2C write direction
  i2c_write(reg);        // register selection
  i2c_write(val);        // value to write in register
  i2c_stop();
}

uint8_t i2c_readReg(uint8_t add, uint8_t reg) {
  uint8_t val;
  i2c_read_reg_to_buf(add, reg, &val, 1);
  return val;
}
  1. получить данные по акселю и гире
// ************************************************************************************************************
// I2C Gyroscope and Accelerometer MPU6050
// ************************************************************************************************************
#if defined(MPU6050)

void Gyro_init() {
  TWBR = ((F_CPU / 400000L) - 16) / 2; // change the I2C clock rate to 400kHz
  i2c_writeReg(MPU6050_ADDRESS, 0x6B, 0x80);             //PWR_MGMT_1    -- DEVICE_RESET 1
  delay(5);
  i2c_writeReg(MPU6050_ADDRESS, 0x6B, 0x03);             //PWR_MGMT_1    -- SLEEP 0; CYCLE 0; TEMP_DIS 0; CLKSEL 3 (PLL with Z Gyro reference)
  i2c_writeReg(MPU6050_ADDRESS, 0x1A, MPU6050_DLPF_CFG); //CONFIG        -- EXT_SYNC_SET 0 (disable input pin for data sync) ; default DLPF_CFG = 0 => ACC bandwidth = 260Hz  GYRO bandwidth = 256Hz)
  i2c_writeReg(MPU6050_ADDRESS, 0x1B, 0x18);             //GYRO_CONFIG   -- FS_SEL = 3: Full scale set to 2000 deg/sec
  // enable I2C bypass for AUX I2C
  #if defined(MAG)
    i2c_writeReg(MPU6050_ADDRESS, 0x37, 0x02);           //INT_PIN_CFG   -- INT_LEVEL=0 ; INT_OPEN=0 ; LATCH_INT_EN=0 ; INT_RD_CLEAR=0 ; FSYNC_INT_LEVEL=0 ; FSYNC_INT_EN=0 ; I2C_BYPASS_EN=1 ; CLKOUT_EN=0
  #endif
}

void Gyro_getADC () {
  i2c_getSixRawADC(MPU6050_ADDRESS, 0x43);
  GYRO_ORIENTATION( ((rawADC[0]<<8) | rawADC[1])/4 , // range: +/- 8192; +/- 2000 deg/sec
	            ((rawADC[2]<<8) | rawADC[3])/4 ,
	            ((rawADC[4]<<8) | rawADC[5])/4 );
  GYRO_Common();
}

void ACC_init () {
  i2c_writeReg(MPU6050_ADDRESS, 0x1C, 0x10);             //ACCEL_CONFIG  -- AFS_SEL=2 (Full Scale = +/-8G)  ; ACCELL_HPF=0   //note something is wrong in the spec.
  //note: something seems to be wrong in the spec here. With AFS=2 1G = 4096 but according to my measurement: 1G=2048 (and 2048/8 = 256)
  //confirmed here: 
  #if defined(FREEIMUv04)
    acc_1G = 255;
  #else
    acc_1G = 512;
  #endif

  #if defined(MPU6050_I2C_AUX_MASTER)
    //at this stage, the MAG is configured via the original MAG init function in I2C bypass mode
    //now we configure MPU as a I2C Master device to handle the MAG via the I2C AUX port (done here for HMC5883)
    i2c_writeReg(MPU6050_ADDRESS, 0x6A, 0b00100000);       //USER_CTRL     -- DMP_EN=0 ; FIFO_EN=0 ; I2C_MST_EN=1 (I2C master mode) ; I2C_IF_DIS=0 ; FIFO_RESET=0 ; I2C_MST_RESET=0 ; SIG_COND_RESET=0
    i2c_writeReg(MPU6050_ADDRESS, 0x37, 0x00);             //INT_PIN_CFG   -- INT_LEVEL=0 ; INT_OPEN=0 ; LATCH_INT_EN=0 ; INT_RD_CLEAR=0 ; FSYNC_INT_LEVEL=0 ; FSYNC_INT_EN=0 ; I2C_BYPASS_EN=0 ; CLKOUT_EN=0
    i2c_writeReg(MPU6050_ADDRESS, 0x24, 0x0D);             //I2C_MST_CTRL  -- MULT_MST_EN=0 ; WAIT_FOR_ES=0 ; SLV_3_FIFO_EN=0 ; I2C_MST_P_NSR=0 ; I2C_MST_CLK=13 (I2C slave speed bus = 400kHz)
    i2c_writeReg(MPU6050_ADDRESS, 0x25, 0x80|MAG_ADDRESS);//I2C_SLV0_ADDR -- I2C_SLV4_RW=1 (read operation) ; I2C_SLV4_ADDR=MAG_ADDRESS
    i2c_writeReg(MPU6050_ADDRESS, 0x26, MAG_DATA_REGISTER);//I2C_SLV0_REG  -- 6 data bytes of MAG are stored in 6 registers. First register address is MAG_DATA_REGISTER
    i2c_writeReg(MPU6050_ADDRESS, 0x27, 0x86);             //I2C_SLV0_CTRL -- I2C_SLV0_EN=1 ; I2C_SLV0_BYTE_SW=0 ; I2C_SLV0_REG_DIS=0 ; I2C_SLV0_GRP=0 ; I2C_SLV0_LEN=3 (3x2 bytes)
  #endif
}

void ACC_getADC () {
  i2c_getSixRawADC(MPU6050_ADDRESS, 0x3B);
  ACC_ORIENTATION( ((rawADC[0]<<8) | rawADC[1])/8 ,
                   ((rawADC[2]<<8) | rawADC[3])/8 ,
                   ((rawADC[4]<<8) | rawADC[5])/8 );
  ACC_Common();
}

GYRO_Common и ACC_Common ф-ции переделать под себя будет чуть геморойнее - они используют больше переменных из других файлов проэкта

Gene

Наверное я изначально неправильно сформулировал задачу. Мне не столько нужно повысить именно СКОРОСТЬ сэмплирования, сколько иметь возможность вставить куски кода по управлению степпером в самые “долгоиграющие” циклы. Проблема состояла в том чтобы найти эти самые циклы (особенно там, где считываются данные с MPU6050) и наглядно разместить их в своем скетче. кроме того, желательно было не тратить время на обработку лишних данных, касающихся оси z.

Вот что у меня пока получилось:

.....
//получаем x,y с гир и акселей
getMotion4my(&ax, &ay, &gx, &gy);
.....
void getMotion4my(int16_t* ax, int16_t* ay, int16_t* gx, int16_t* gy) {

int8_t count = 0;
uint16_t timeout = 1000;
uint8_t data[12];
Wire.beginTransmission(0x68);
Wire.write(0x3B);
Wire.endTransmission();
Wire.beginTransmission(0x68);
Wire.requestFrom(0x68, 12);
uint32_t t1 = millis();
for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
data[count] = Wire.read();

//здесь попробую воткнуть управление степперами

}
if (timeout > 0 && millis() - t1 >= timeout && count < 12) count = -1; // timeout
Wire.endTransmission();
*ax = (((int16_t)data[0]) << 8) | data[1];
*ay = (((int16_t)data[2]) << 8) | data[3];
*gx = (((int16_t)data[8]) << 8) | data[9];
*gy = (((int16_t)data[10]) << 8) | data[11];
}

Скорость считки в голом виде примерно 580hz. Собственно, если заработает, мне этого будет достаточно…

mataor

слушай… а зачем тебе в код втыкать? почему таймеры не хочеш задействовать с нужной частотой, по таймеру прерывание с циклом управления ШД, а потом меняй частоту таймера/отрубай в тех местах где тебе нужно? для примера управление моторками именно так и осуществлено - на таймере генерим ШИМ а когда нужно меняем значение

П.С. немного не в тему но - kit-e.ru/articles/micro/2008_5_80.php тут есть про AWeX технологию у хмег

Gene

слушай… а зачем тебе в код втыкать?

Просто любопытно. Да и тренировка головы. Тоже своего рода моделирование. А то все вокруг меня все давно программисты, а я как будто только с дерева слез 😃

почему таймеры не хочеш задействовать с нужной частотой, по таймеру прерывание с циклом управления ШД, а потом меняй частоту таймера/отрубай в тех местах где тебе нужно?

Не не хочу, а просто пока не сообразил 😃 Так и попробую сделать. Вообще на прерывания хотел подвесить читку PWM, но наверное можно и то и другое

mataor
Gene:

Вообще на прерывания хотел подвесить читку PWM, но наверное можно и то и другое

вообщет прерывания бывают разных несвязанных типов - от входа, от таймера, от уарта, от TWI (i2c) и проч… в данном случае ты путаеш прерывания со входа с прерыванием по переполнению таймера.
Главное не забывать, что код в прерывании должен быть минимальным и выполнятся макс быстро, чтобы не тормозить прочие прерывания (например в ардуино подсчет времени осуществляется тоже с помощью 0-го таймера и состоит из инкременирования переменной)

Gene

вообщет прерывания бывают разных несвязанных типов - от входа, от таймера, от уарта, от TWI (i2c) и проч… в данном случае ты путаеш прерывания со входа с прерыванием по переполнению таймера.

Я их не путаю, я просто с ними незнаком. Вообще все это железо для меня новость. Что ты хочешь, я только неделю назад первую ардуину купил 😃

Главное не забывать, что код в прерывании должен быть минимальным и выполнятся макс быстро

Это я к счастью помню! (еще с тех древних времен, когда писал на ассемблере под Z80) Если удастся задать прерывание с промежутком в миллисекунду или меньше, то не придется трогать шаг степпера, и весь код будет в три строки с целыми числами.

techalex

Всем добрый вечер.
Подскажите пожалуйста, зеленая платка с ХК www.hobbyking.com/hobbyking/…/uh_viewItem.asp?idPr…
должна хоть как то работать без подключения приемника и регуляторов?
А то что-то она ничего в GUI не посылает…

Gene
techalex:

Всем добрый вечер.
Подскажите пожалуйста, зеленая платка с ХК www.hobbyking.com/hobbyking/…/uh_viewItem.asp?idPr…
должна хоть как то работать без подключения приемника и регуляторов?
А то что-то она ничего в GUI не посылает…

Дык, да, у меня завелась сразу, как только в USB воткнул. Лампочки-то горят?

techalex

при втыкании в usb горит красная и мигают синяя и зеленая на самой wii и зеленая на ftdi - секунд 10 а потом все гаснет. При нажатии start в gui начинает мигать красная на ftdi. и все…
яву обновил, 115200 на порту выставил.

mataor
Gene:

Если удастся задать прерывание с промежутком в миллисекунду или меньше, то не придется трогать шаг степпера, и весь код будет в три строки с целыми числами.

тфу блин писал-писал… талмут написал а ток птом увидел что ты про 1 милисекунду а не микросекунду говориш… вообще не проблема - выставляеш делитель 1/8/64/256/1024 от тактовой (16МГц), разрешаеш прерывание по переполнению таймера, выставляеш значение от которого таймер считать будет (обновлять значение по прерыванию) до макс значения (256 для 0 и 2 таймеров и 65535 для таймера 1) и вуаля…

Gene
techalex:

при втыкании в usb горит красная и мигают синяя и зеленая на самой wii и зеленая на ftdi - секунд 10 а потом все гаснет. При нажатии start в gui начинает мигать красная на ftdi. и все…
яву обновил, 115200 на порту выставил.

Может, драйвер COM-порта взглючил?.. Я вообще не спец, но у меня завелась буквально сразу, ничего кроме хвоста USB не втыкал.

mataor:

и вуаля…

Я уже попробовал, вроде работает. Спасибо за наводку!

techalex
Gene:

Может, драйвер COM-порта взглючил?.. Я вообще не спец, но у меня завелась буквально сразу, ничего кроме хвоста USB не втыкал.

Я уже и на работе и дома попробовал, разные компы и драйвера, на работе винда сама что то поставила, дома с сайта ftdi.
Одинаковый результат, т.е. никакого. Не пойму куда копать вообще.
Нашел вот такое в описании: The blinks at the initialization is very long (more than 5s): it is sometimes the case with a WMP+NK configuration. A way to correct this is to increase the INTERLEAVING DELAY in the sketch. (from 3000 to 3500 or 4000), не знаю относится это к моей платке или это только к арду…

У вас вообще какие светодиоды горят после включения, чем мигает?

devv
techalex:

Одинаковый результат, т.е. никакого. Не пойму куда копать вообще.

Прошивку залили в платку через Ардуино ИДЕ ?
Может платка пустая пришла ?

techalex
devv:

Прошивку залили в платку через Ардуино ИДЕ ?
Может платка пустая пришла ?

Пока ничего не заливал, хотел так попробовать. Походу раз чем то мигает при включении значит не пустая, просто может версия не та, написано 2.1, но что на самом деле…