Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: atmega32 + HMC5883 (по i2c)
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
Kruftin
Фатал. Как оказалось компас базируется не на HMC5883, а на HMC5883L и они немного отличаются, правда отличия не критичные. Т.е. код для 5883 должен работать и на 5883L. Даташит новый я приложил. Там кстати есть примеры, по которым я сегодня код отредактировал и получил незначительное изменение: в X (MSB - 0x83 LSB 0xFF ), Y (0xFF,0xFF), Z(0xFF,0xFF). В даташите написано, что переполнение регистров возможно, если шум выставлен неверно, так вот я выставлял его на все варианты, не помогает. Правда есть еще строчка в даташите, которую я не понимаю как реализовать: Send 0x3D 0x06 что это делает мне непонятно(смотри картинку из даташита). Пробовал все режимы - безрезультатно.
Вот код основной:
CODE
void HMC5843(void)
{
//unsigned char xh, xl, yh, yl, zh, zl;
long xo, yo, zo;

i2cSendStart();
i2cWaitForComplete();
i2cWrite_Address(0x3C); //write to HMC
i2cWaitForComplete();
i2cWrite_Data(0x00); //confA reg
i2cWaitForComplete();
i2cWrite_Data(0x70); //value for confA
i2cWaitForComplete();
i2cSendStop();
delay_ms(5);

i2cSendStart();
i2cWaitForComplete();
i2cWrite_Address(0x3C); //write to HMC
i2cWaitForComplete();
i2cWrite_Data(0x01); //confB reg
i2cWaitForComplete();
i2cWrite_Data(0xA0); //value for confB
i2cWaitForComplete();
i2cSendStop();
delay_ms(5);

i2cSendStart();
i2cWaitForComplete();
i2cWrite_Address(0x3C); //write to HMC
i2cWaitForComplete();
i2cWrite_Data(0x02); //mode register
i2cWaitForComplete();
i2cWrite_Data(0x01); //continuous measurement mode
i2cWaitForComplete();
//status_er = i2cGetStatus();
//i2cCheckForMT_SLA();
i2cSendStop();
delay_ms(10);

i2cSendStart();
i2cWaitForComplete();
i2cWrite_Address(0x3C); //write to HMC
i2cWaitForComplete();
i2cWrite_Data(0x09); //status register
i2cWaitForComplete();
i2cSendStop();
delay_ms(10);

i2cSendStart();
i2cWaitForComplete();
i2cRead_Address(0x3D); //write to HMC
i2cWaitForComplete();
reg_stcmp = i2cRead_Data(); //status register
i2cWaitForComplete();
i2cSendStop();
delay_ms(10);

i2cSendStart();
i2cWaitForComplete();
i2cWrite_Address(0x3C); //write to HMC
i2cWaitForComplete();
i2cWrite_Data(0x02); //mode register
i2cWaitForComplete();
i2cSendStop();
delay_ms(10);

//must read all six registers plus one to move the pointer back to 0x03
i2cSendStart();
i2cWaitForComplete();
i2cRead_Address(0x3D); //read from HMC
i2cWaitForComplete();
i2cReceiveByte(TRUE);
//i2cWaitForComplete();
xh = i2cRead_Data(); //x high byte
i2cWaitForComplete();

i2cReceiveByte(TRUE);
i2cWaitForComplete();
xl = i2cRead_Data(); //x low byte
i2cWaitForComplete();
xo = xl|(xh << 8);

i2cReceiveByte(TRUE);
i2cWaitForComplete();

yh = i2cRead_Data(); //y high byte
i2cWaitForComplete();

i2cReceiveByte(TRUE);
i2cWaitForComplete();
yl = i2cRead_Data(); //y low byte
i2cWaitForComplete();
yo = yl|(yh << 8);

i2cReceiveByte(TRUE);
i2cWaitForComplete();
zh = i2cRead_Data();
i2cWaitForComplete(); //z high byte

i2cReceiveByte(TRUE);
i2cWaitForComplete();
zl = i2cRead_Data(); //z low byte
i2cWaitForComplete();
zo = zl|(zh << 8);

//i2cRead_Data(); //must reach 0x09 to go back to 0x03
//i2cWaitForComplete();
// i2cReceiveByte(TRUE);
//i2cWaitForComplete();
status_er = i2cGetStatus();

i2cSendStop();
}

void main(void)
ILYAUL
3. Write Mode (02) – send 0x3C 0x02 0x00 (Continuous-measurement mode)
Код
i2cWrite_Data([b]0x01[/b]);    //continuous measurement mode
!!!!!!!!!

Вот эту часть не понял, ну прочёл ты статусный регистр , а зачем? Если проверить RDY то где она?
Код
i2cSendStart();
    i2cWaitForComplete();
    i2cWrite_Address(0x3C);    //write to HMC
    i2cWaitForComplete();
    i2cWrite_Data(0x09);    //status register
    i2cWaitForComplete();
    i2cSendStop();
    delay_ms(10);
    
    i2cSendStart();
    i2cWaitForComplete();
    i2cRead_Address(0x3D);    //write to HMC
    i2cWaitForComplete();
    reg_stcmp = i2cRead_Data();    //status register
    i2cWaitForComplete();
    i2cSendStop();
    delay_ms(10);



Здесь поставил адрес на mode register , а начал читать измеренные данные
Код
i2cSendStart();
    i2cWaitForComplete();
    i2cWrite_Address(0x3C);    //write to HMC
    i2cWaitForComplete();
    i2cWrite_Data(0x02);    //mode register
    i2cWaitForComplete();
    i2cSendStop();
    delay_ms(10);
    
    //must read all six registers plus one to move the pointer back to 0x03
    i2cSendStart();
    i2cWaitForComplete();
    i2cRead_Address(0x3D);          //read from HMC
    i2cWaitForComplete();
    i2cReceiveByte(TRUE);
    //i2cWaitForComplete();
    xh = i2cRead_Data();    //x high byte
    i2cWaitForComplete();


Так они показали, что в continuous measurement mode ,
Цитата
5. Loop
Send 0x3D 0x06 - это (Read all 6 bytes. If gain is changed then this data set is using previous gain)
Convert three 16-bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively.
Send 0x3C 0x03 (point to first data register 03)
Wait about 67 ms (if 15 Hz rate) or monitor status register or DRDY hardware interrupt pin
End_loop

не нужно посылать вот это, снова и снова - достаточно одной таблетки
Цитата
1. Write CRA (00) – send 0x3C 0x00 0x70 (8-average, 15 Hz default, normal measurement)
2. Write CRB (01) – send 0x3C 0x01 0xA0 (Gain=5, or any other desired gain)
3. Write Mode (02) – send 0x3C 0x02 0x00 (Continuous-measurement mode)
4. Wait 6 ms or monitor status register or DRDY hardware interrupt pin


И разберись с delays , что-то их до хрена , как мне кажется. И где-то надо бы подождать 67ms , а не 10. Если сделаешь проверку RDY то delays вообще не нужны, но код придётся подправить
Kruftin
Да задержку поставлю. Режим single mode я пробую здесь, просто комментарий забыл изменить. Ну я пробовал указатель ставить и на первый регистр Х - одно и тоже в результате. Т.е. 0х3D 0x06 - из этого послать надо только 0х3D, а 0х06 означает, что надо считать все регистры осей. А RDY я пока сам смотрел в уарте, он ставится в единичку.
ILYAUL
Цитата(Kruftin @ May 22 2012, 23:30) *
Т.е. 0х3D 0x06 - из этого послать надо только 0х3D, а 0х06 означает, что надо считать все регистры осей.

Да под одним стартом прочитать подряд все 6 регистров и затем вернуться к началу 0х3С 0х03
Цитата
To clock out the new data, send:
0x3D, and clock out DXRA, DXRB, DZRA, DZRB, DYRA, and DYRB located in registers 3 through 8.......

А Вы смотрите данные пересылая их на комп? Может в пересылке что-то не так ?
Kruftin
По идее, так и по даташиту, код который составлен на сегодня должен работать для режима single mode. И хоть какие-то регистры должны быть считаны верно. Пересылка на комп стандартно по уарту, смотрю через терминал. Сомневаюсь, что данные именно регистров могут исказиться, посылка остальных символов еще ни разу не давала сбоев.
Я полазил по зарубежным сайтам, так там нашел только мистическое изменение адреса компаса с 0x1E на 0x1C. Но раз он тут нам все подтверждает, значит адрес верен.
Кстати раз написано Send 0x3D 0x06, так значит надо послать 0x06, а только потом считывать данные.
Нашел на компас errata sheet http://www.kamami.pl/dl/hmc5883l_datasheet_errata.pdf
ILYAUL
Цитата(Kruftin @ May 23 2012, 07:08) *
Кстати раз написано Send 0x3D 0x06, так значит надо послать 0x06, а только потом считывать данные.

Куда послать? 3D - это чтение!
Цитата(Kruftin @ May 23 2012, 07:08) *
Нашел на компас errata sheet ...

Так они обещали всё исправить к 1 кварталу 2011 года Посмотри твой когда выпущен.

И попробуй сделать это SELF TEST OPERATION
Kruftin
Ща попробую self-test. На нем тока написано L883 2048 и даты изготовления нету. В X регистре получаю в self test изменяющиеся при мневре компасом значения 0xFF - LSB 0XC1 - MSB, ну вместо C1 бывает С4 - С6(ну хоть куда вращай где эти числа меняются в районе C1-C6). Регистры Y,Z по прежнему 0xFF.

Ого регистр X начал реагировать в обычном continuous measurement mode режиме MSB начал выдавать правдивый результат, а вот с остальными регистрами пока тихо. Странные чувства заработала часть регистра X. А остальные нет.
Может я считываю не так остальные регистры?
CODE
//must read all six registers plus one to move the pointer back to 0x03
i2cSendStart();
i2cWaitForComplete();
i2cRead_Address(0x3D); //read from HMC
i2cWaitForComplete();
delay_ms(200);
i2cReceiveByte(TRUE);
i2cWaitForComplete();
xh = i2cRead_Data(); //x high byte
i2cWaitForComplete();

i2cReceiveByte(TRUE);
i2cWaitForComplete();
xl = i2cRead_Data(); //x low byte
i2cWaitForComplete();
xo = xl|(xh << 8);

i2cReceiveByte(TRUE);
i2cWaitForComplete();

yh = i2cRead_Data(); //y high byte
i2cWaitForComplete();

i2cReceiveByte(TRUE);
i2cWaitForComplete();
yl = i2cRead_Data(); //y low byte
i2cWaitForComplete();
yo = yl|(yh << 8);

i2cReceiveByte(TRUE);
i2cWaitForComplete();


Да похоже, что косяк при считывании данных из регистров. Считывается только xh, т.е. значение первого регистра, а остальные просто не считались потому и ff.
Kruftin
Ну в общем заметен прогресс. Менял я сейчас значение с которого начинать читать и вот в результате значения правильные есть во всех трех MSB байтах, а в LSB 0xFF. Осталось только придумать как считывать все регистры сразу, а не по одному меняя код.
Код
i2cSendStart();
    i2cWaitForComplete();
    i2cWrite_Address(0x3C);    //write to HMC
    i2cWaitForComplete();
    i2cWrite_Data(0x08);    //mode register
    i2cWaitForComplete();
    i2cSendStop();
    delay_ms(200);
    
    //must read all six registers plus one to move the pointer back to 0x03
    i2cSendStart();
    i2cWaitForComplete();
    i2cRead_Address(0x3D);          //read from HMC
    i2cWaitForComplete();
    i2cReceiveByte(TRUE);
    i2cWaitForComplete();
    xh = i2cRead_Data();    //x high byte
    i2cWaitForComplete();


Все кароче пошла рыбка yeah.gif Только вот пока LSB байты остаются FF. Написал я считывание к каждому регистры по отдельности и заработало в single mode. ILYAUL a14.gif
Правда из-за обращения к каждому регистру индивидуально код получился довольно громоздким.
ILYAUL
Посмотри, что вот так будет
CODE
i2cSendStart();
i2cWaitForComplete();
i2cWrite_Address(0x3C); //write to HMC
i2cWaitForComplete();
i2cWrite_Data(0x03); //XH register
i2cWaitForComplete();
//i2cSendStop();
delay_ms(67);

//must read all six registers plus one to move the pointer back to 0x03
i2cSendStart();
i2cWaitForComplete();
i2cRead_Address(0x3D); //read from HMC
i2cWaitForComplete();
i2cReceiveByte(TRUE);
//i2cWaitForComplete();
xh = i2cRead_Data(); //x high byte
i2cWaitForComplete();

i2cReceiveByte(TRUE);
i2cWaitForComplete();
xl = i2cRead_Data(); //x low byte
i2cWaitForComplete();
xo = xl|(xh << 8);

i2cReceiveByte(TRUE);
i2cWaitForComplete();

yh = i2cRead_Data(); //y high byte
i2cWaitForComplete();

i2cReceiveByte(TRUE);
i2cWaitForComplete();
yl = i2cRead_Data(); //y low byte
i2cWaitForComplete();
yo = yl|(yh << 8);

i2cReceiveByte(TRUE);
i2cWaitForComplete();
zh = i2cRead_Data();
i2cWaitForComplete(); //z high byte

i2cReceiveByte(TRUE);
i2cWaitForComplete();
zl = i2cRead_Data(); //z low byte
i2cWaitForComplete();
zo = zl|(zh << 8);
i2cSendStop();


Цитата
Только вот пока LSB байты остаются FF
Всё таки доделай self test до конца , как они рекомендуют выставить коэффициент усиления. Может и LSB тогда оживут
Kruftin
0xFF во всех регистрах. Только я не совсем понял как доделать самотестирование. Там вылазят при самотестировании фиксированные значения. Ну поставил усиление по дефолту и они вроде тоже оживают при движении.
ILYAUL
Цитата(Kruftin @ May 23 2012, 15:23) *
0xFF во всех регистрах

Блин , ерунда какая-то и как назло в Москве ни у кого нет. Поставки 2-3 недели. А атмеловская приблуда с ним - дорого. Попробую завтра на рынках поискать. Так будет проще разбираться. Так , что уся надежда на тебя.
Kruftin
Дак и LSB меняются, усиление меняешь дак они сильнее меняются. Надо вообщем прочитать про калибровку. Ну получается был глюк с продвижением указателя по регистрам, хотя может так и надо к каждому регистру по отдельности обращаться. Вот рабочий код.
А еще как мне тип дабл по уарту передать(по уарту я могу передать 8 бит максимум)? Я же получу градусы по следующей формуле:
angle= atan(y/x)* (180 / 3.14159265) +180; // angle in degrees
где x = xl|xh << 8
y = yl|yh << 8

Это получится мне нужно градусы передать как два байта за раз, поскольку я сливаю старший и младший байты?
ILYAUL
Цитата(Kruftin @ May 23 2012, 15:45) *
Это получится мне нужно градусы передать как два байта за раз, поскольку я сливаю старший и младший байты?

Пересчитай в ASCII и передавай сразу в привычном виде- чуть длинее но понятнее

ДА ! И поздравляю!!!!
Kruftin
А как пересчитать? Получается я использую double а потом его конвертирую в char. Пойду обычный компас покупать, чтобы точность проверить.
Ну т.е. сейчас у меня есть например число 185.2(double) и мне его надо сделать как char для того чтобы передать.
ILYAUL
Поищи в инете, где-то я видел как пересчитывают , но помоему используют float
Kruftin
Вот нашел пример преобразования. Только надо чтобы число был в один байт. Вопрос в том получится(уместится) ли значение angle в один байт.
У меня же angle меняется от 0 до 360 даже если я к целому преобразую, а в один байт влазит до 255 1111493779.gif
_Артём_
Цитата(Kruftin @ May 23 2012, 19:53) *
Вот нашел пример преобразования.

Мутный пример какой-то.

Цитата(Kruftin @ May 23 2012, 19:53) *
Вот нашел пример преобразования. Только надо чтобы число был в один байт.

Ну 360 не вместится, разве что на 2 поделить.

Пример
Код
void SendToUart(unsigned char new_byte)
{
// посылка в порт
}
#include <stdio.h>

void SendAngle(double angle)
{
unsigned short tmp=(unsigned short)Value;
char Buf[20];
unsigned char l=sprintf(Buf,"Angle=%d\n\r", tmp);
for (char i=0; i<l; i++) {
   SendToUart(Buf[i]);
}

Kruftin
Т.е. в массиве Buf будут лежать десятичные цифры? Если число 180.52, то в Buf[0]=1 Buf[1] = 8 Buf[3] = . Buf[4] = 5?
Если да, то это круто). Или просто число будет разбито по байтам?
Спасибо огромное ILYAUL и Артем!!! 08.gif a14.gif Не знаю что бы я без вас делал.
Поехал сейчас компас покупать.
Код отредактировал маленько:
Код
void SendAngle(float angle)
{
unsigned short tmp=(unsigned short)angle; //преобразуем угол в целое число
char Buf[20]; //создаем массив
unsigned char l=sprintf(Buf,"Angle=%d\n\r", tmp); //записываем в буфер значение угла по цифрам
unsigned char i = 0;
for (i=0; i<l; i++) {
   USART_SendChar(Buf[i]);//выводим все по уарту
}
}
Kruftin
Так вот только формула для определения севера неверная походу, хотя написана тут http://www.seeedstudio.com/wiki/index.php?...s_Compass_v1.0b

По ней получается, что значения меняются от 180 до 268, учитывая что по даташиту значения регистров меняются от 0xF800 to 0x07FF получается по формуле
xv = xh << 8|xl;
yv = zh << 8|zl;
angle = atan2(yv,xv)*(180/3.14)+180;

имеем от 180 до 268 значения угла.
В даташите вот ни слова нету как вычислить угол. Но компас вроде как верно работает.
Хотя если считать как angle = atan2(yv,xv) то можно получать значения от 0 до 90 со странные перескоками с 30 на 60 и т.д. Поскольку нету отрицательных значений. Режим postive и negative bias как я понял для самотестирования. Сделать компас под выдачу нормальных данных так и не удалось и в даташите про расчет углов ничего вроде как нету. Смотрел пример расчета и калибровки для ардуино, так вот чето не все понял. Мда столько времени на него потратил, а он самый последний шаг сделать не получается. Код для ардуино приложил.
Kruftin
Мда, на данный момент я получаю правдивые значения углов в диапазоне от 0 до 270 градусов, в оставшемся компас выдает скачущие и неверные данные. Правдивыми являются данные angle от 0 до -135, это результат atan2(yv,xv)*180/pi, где xv = (xh << 8)|xl; yv = (yh << 8)|yl;
Причем относительно реального севера нулевая отметка смещена градусов на 30, но это не проблема, главное что изменение градусов он показывает верно(правда надо умножить значение на 2) но только в диапазоне от 0 до 270 градусов. Похоже это баг компаса моего, либо надо поиграть усилением, но ведь на 75% значений при вращении оно не влияет. Код на сегодня приложил.

P.S. Хотя судя по примеру для ардуино я должен получать сразу значение от - 180 до 180, не умножая на два.
_Артём_
Цитата(Kruftin @ May 31 2012, 16:06) *
Причем относительно реального севера нулевая отметка смещена градусов на 30

Так вроде и должно быть: северный полюс и магнитный полюс не совпадают. Разница градусов 20.
Kruftin
Не я имею ввиду, что магнитный север я смотрел по компасу обычному и сравниваю значение нуля, оно отличается градусов на 20-30. Причем есть зона градусов в 45, где компас выдает нестабильные и левые значения.
ILYAUL
Z нашёл иот такую формулу для него

Код
// convert the raw data into a heading in degrees
        float headingDegrees = atan2((double)raw_y,(double)raw_x)* 180 / 3.14159265
Kruftin
Ну у меня точно такая же, если скачать код и посмотреть). Только нету преобразования в из инта в дабл для значений х и у. А так все тоже.
Kruftin
А компас то работает четко. Правда оказалось, что откуда не возьмись в комнате наводки брались. Принес в просторную комнату и компас работает как надо. yeah.gif Хотя если поставить компас вблизи розеток и ноутов, то север смещается. Все работает, всем спасибо!
eu1cc
А кто знает, как смещение по Z координате корректировать?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.