Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: HMC5843 Honeywell digital compass
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > MSP430
Kaplinsky
Салют разработчикам !

Имею затык при взаимодействии с компасом HMC5843 посредством протокола I2C.
В качестве МК применяю MSP430F2471

Проблема в следующем:
для прочтения регистра нужно записать в HMC5843 по I2C протоколу адрес регистра а затем произвести операцию чтения.
Делаю так:

Код
#define NACK_1 0x0100
#define NACK_2 0x0200

unsigned int I2C_Read(unsigned char addr){
unsigned int data = 0;  


  UCB0CTL1 |= UCTXSTT | UCTR;                   // UCTR - Transmit, UCTXSTT - generate START
  while (UCB0CTL1 & UCTXSTT);                     // waiting untill START sent
  
  
  UCB0TXBUF = addr;                                    // Loading register address
  while (!(IFG2 & UCB0TXIFG)) if (UCB0STAT & UCNACKIFG){         // waiting until data sent or NACK

    UCB0CTL1 |= UCTXSTP;                            // Generate STOP
    while (UCB0CTL1 & UCTXSTP);                   // Ensure stop condition got sent
    
    return NACK_1;                                     // wait untill rx flag is set
  }

  
  UCB0CTL1 &= ~UCTR;                              // Clear UCTR for reading operation
  UCB0CTL1 |= UCTXSTT;                            // Send START, SLAVE ADDR with WRITE
  while (UCB0CTL1 & UCTXSTT);                   // waiting untill START sent
  
    
  while (!(IFG2 & UCB0RXIFG)) if (UCB0STAT & UCNACKIFG){

    UCB0CTL1 |= UCTXSTP;                            // Generate STOP
    while (UCB0CTL1 & UCTXSTP);                   // Ensure stop condition got sent
    
    return NACK_2;                                         // wait untill rx flag is set
  }


  data = UCB0RXBUF;
  
  UCB0CTL1 |= UCTXSTP;                              // Generate STOP
  while (UCB0CTL1 & UCTXSTP);                     // Ensure stop condition got sent
  
  return data;

}//I2C_Read


Получаю такой результат:



Почему вылазит этот NACK ???
Адрес правильный а он со мной говорить не хочет !


Скорость I2C - 10Кгц
Подтягивающие резисторы 15К.


При чтении почему-то компас шлет два байта (регистр из которого читать не указываем по причине невозможности см. выше)
и что интересно при запросе чтения компас дает нам ACK а при запросе записи NACK - почему ?


или


Из нижней картинки хорошо видно что есть START мы передаем адрес 0x1E и бит чтения 1, далее компасс дает ACK и выдает ДВА байта - 0x10 (что может быть значением нулевого регистра компаса) и еще какой-то байт 0x7F и потом говорит нам NACK а мы делаем STOP.

вот процедура чтения:
Код
#define NACK_1 0x0100
#define NACK_2 0x0200

unsigned int I2C_Read(unsigned char addr){
unsigned int data = 0;  

  
  UCB0CTL1 &= ~UCTR;                              // Clear UCTR for reading operation
  UCB0CTL1 |= UCTXSTT;                            // Send START, SLAVE ADDR with WRITE
  while (UCB0CTL1 & UCTXSTT);                   // waiting untill START sent
  
    
  while (!(IFG2 & UCB0RXIFG)) if (UCB0STAT & UCNACKIFG){

    UCB0CTL1 |= UCTXSTP;                            // Generate STOP
    while (UCB0CTL1 & UCTXSTP);                   // Ensure stop condition got sent
    
    return NACK_2;                                         // wait untill rx flag is set
  }


  data = UCB0RXBUF;
  
  UCB0CTL1 |= UCTXSTP;                              // Generate STOP
  while (UCB0CTL1 & UCTXSTP);                     // Ensure stop condition got sent
  
  return data;

}//I2C_Read


Почему два байта и почему NACK при попытке записи - ломаю голову третий день. Подкиньте идею.
rezident
Цитата(Kaplinsky @ Dec 4 2009, 17:23) *
Почему два байта и почему NACK при попытке записи - ломаю голову третий день. Подкиньте идею.
Не очень внимательно прочитал ваше сообщение, но зацепившись взглядом за адрес 0x1E ... может поэтому (см. цитату из даташита)
Цитата
The default (factory) HMC5843 7-bit slave address is 0x3C for write operations, or 0x3D for read operations.

Заметил, что и у писателей документации и у читателей-разработчиков очень часто возникает недопонимание в части трактовки адреса устройства I2C. Бывает, что ошибки и тех, и других. laughing.gif
Alexashka
Тоже не внимательно прочитал)
Если адрес не его то устройство вообще по идее вообще должно молчать. А оно отвечает и отвечает отказом. Значит команда ему не понятна...мэйби...ор нот. либо оно не готово ответить
Kaplinsky
3С и 3D это уже 8-ми битные адреса с учетом флага RW.
7-ми битный адлрес 1E - сдвиньте на 1 бит вправо 3С или 3D.
В даташите на HMC так написано:
7-ми битный адрес 1E,
8-ми битный для записи 3C
8-ми битный для чтения 3D

Экспериментировал, с другим адрес не откликается.
jam
Цитата(Kaplinsky @ Dec 6 2009, 21:18) *
3С и 3D это уже 8-ми битные адреса с учетом флага RW.
7-ми битный адлрес 1E - сдвиньте на 1 бит вправо 3С или 3D.
В даташите на HMC так написано:
7-ми битный адрес 1E,
8-ми битный для записи 3C
8-ми битный для чтения 3D

Экспериментировал, с другим адрес не откликается.

Они же просят скорость обмена 100к или 400к , а у Вас вроде 10к - может в этом дело?
Kaplinsky
Цитата(jam @ Dec 6 2009, 21:27) *
Они же просят скорость обмена 100к или 400к , а у Вас вроде 10к - может в этом дело?


Думал об этом. Давал 100к, 400к - результат тот же.
На этих скоростях мне фронты не нравятся вот и работаю на 10к, сигналы нормальные (прямоугольные, без затянутых фронтов) так вроде надежнее.
jam
Цитата(Kaplinsky @ Dec 6 2009, 22:47) *
Думал об этом. Давал 100к, 400к - результат тот же.
На этих скоростях мне фронты не нравятся вот и работаю на 10к, сигналы нормальные (прямоугольные, без затянутых фронтов) так вроде надежнее.


А может быть дело в том, что I2C предусматривает slow rate control, а фронты у Вас заваленные...
Кроме того, напряжение питания должно быть в предлелах 1.6 - 2.0 , а у Вас там целых 3 - может ему от этого плохеет?
rezident
Мне кажется подозрительной большая пауза на SCL перед выдачей NAK при записи адреса м/с. На диаграмме чтения такой паузы нет. Откуда она берется? Сам датчик SCL "растягивает"? По спецификации I2C это допустимо и нормально и вполне объяснимо тем, что у м/с есть свой собственный внутренний клок. Но какова же причина этой паузы? При беглом чтении даташита я ответа не увидел. laughing.gif
jam
Цитата(rezident @ Dec 7 2009, 03:08) *
Мне кажется подозрительной большая пауза на SCL перед выдачей NAK при записи адреса м/с. На диаграмме чтения такой паузы нет. Откуда она берется? Сам датчик SCL "растягивает"? По спецификации I2C это допустимо и нормально и вполне объяснимо тем, что у м/с есть свой собственный внутренний клок. Но какова же причина этой паузы? При беглом чтении даташита я ответа не увидел. laughing.gif

Наверно это компас делает stetching, чтобы дать ACK/NACK
rezident
Цитата(jam @ Dec 7 2009, 05:25) *
Наверно это компас делает stetching, чтобы дать ACK/NACK
Что вы имеете в виду? Я не нашел упоминания этого термина в даташите. И вообще эту команду обычно аппаратура автомата I2C отрабатывает. Поскольку у датчика нет других аппаратных адресов и он не может быть мастером (а следовательно ему не нужно арбитраж на шине поддерживать), то дешифрация адреса должна происходить без каких-либо задержек, на частоте тактирования его мастером.
jam
Цитата(rezident @ Dec 7 2009, 04:07) *
Что вы имеете в виду? Я не нашел упоминания этого термина в даташите. И вообще эту команду обычно аппаратура автомата I2C отрабатывает. Поскольку у датчика нет других аппаратных адресов и он не может быть мастером (а следовательно ему не нужно арбитраж на шине поддерживать), то дешифрация адреса должна происходить без каких-либо задержек, на частоте тактирования его мастером.

Любой участник протокола имеет право на bus wait, сажая SCL в ноль, при этом мастер должен застопорить clock и ждать, пока линия освободится.
Kaplinsky
Вот такая схема подключения используется

только еще есть два подтягивающих резистора по 15К (других не было, я не думаю что проблема может быть в этом) к питанию линий SDA и SCL
Питание подаю 3.3 в.


вот такая платка у меня
вот отсюда http://www.sparkfun.com/commerce/product_i...roducts_id=9371

Проверял гипотезу что разные адреса могут быть и что 0x1Е для чтения, а для записи какой не известно.
Написал перебор адресов, перебрал все для записи ( W=0 ) не откликнулся никакой - везде этот NACK ...

Цитата(rezident @ Dec 7 2009, 02:08) *
Мне кажется подозрительной большая пауза на SCL перед выдачей NAK при записи адреса м/с. На диаграмме чтения такой паузы нет. Откуда она берется? Сам датчик SCL "растягивает"? По спецификации I2C это допустимо и нормально и вполне объяснимо тем, что у м/с есть свой собственный внутренний клок. Но какова же причина этой паузы? При беглом чтении даташита я ответа не увидел. laughing.gif


Вот на что я обратил внимание:

Цитата("Datasheet на HMC5843 @ страница 18, I2C COMMUNICATION PROTOCOL")
After each 8-bit transfer, the master device generates a 9 th clock pulse, and releases the SDA line.
The receiving device (addressed slave) will pull the SDA line low to acknowledge (ACK) the successful transfer or leave
the SDA high to negative acknowledge (NACK).


так вот причина паузы скорее всего мастер, MSP430.
кстати пауза эта плавающая. Я наблюдал увеличение/уменьшение этой паузы при многократных попытках записи.

надо зреть в себя...
jam
Цитата(Kaplinsky @ Dec 7 2009, 11:29) *
Вот такая схема подключения используется
только еще есть два подтягивающих резистора по 15К (других не было, я не думаю что проблема может быть в этом) к питанию линий SDA и SCL
Питание подаю 3.3 в.

Ну осталось проверить только резисторы - по стандарту 2.4ком...
И ещё - попробуйте перед тем как просить данные дать ему адрес конфигурационного регистра , потом записать туда 0х00, а потом только читать...
пошлите ему 0х3с 0х00 0х00, а потом только читайте.
rezident
Цитата(jam @ Dec 7 2009, 07:28) *
Любой участник протокола имеет право на bus wait, сажая SCL в ноль, при этом мастер должен застопорить clock и ждать, пока линия освободится.
Я об этом знаю и упоминал, что этот прием полностью соответствует спецификации I2C. Только при реализации "чистого" слейва это чаще всего не требуется. Например, в EEPROM и ADC пин SCL функционально является входом, без возможности управления (удержания) SCL.
Цитата(Kaplinsky @ Dec 7 2009, 13:29) *
только еще есть два подтягивающих резистора по 15К (других не было, я не думаю что проблема может быть в этом) к питанию линий SDA и SCL
Все же попробуйте уменьшить номиналы резисторов до 2...4,7кОм.
Цитата(Kaplinsky @ Dec 7 2009, 13:29) *
кстати пауза эта плавающая. Я наблюдал увеличение/уменьшение этой паузы при многократных попытках записи.
Пауза только при записи? А при чтении она есть?
Kaplinsky
Немного поэкспериментировал с резисторами, вместо SLAVE устройства (компаса) поцепил два резистора подтянутых к питанию.

Это 47К, 100Кгц


Это 4.3К, 100Кгц.

Сигнал стал правильнее.

Обратите внимание паузы нет между 8-м и 9-м клоками

А теперь компас с резисторами 4.3К и 100Кгц клоками


пауза 400 мксек. И это одна из самых маленьких что мне удалось поймать - в основном 9-й клок уходит далеко за экран осциллографа.


2 Resident: Паузы при чтении нет, как видно на осциллограмме. В чем же дело ?


...9-й клок, NACK его !
не переключайтесь, будьте с нами, продолжение следует...
rezident
Цитата(Kaplinsky @ Dec 7 2009, 22:39) *
2 Resident: Паузы при чтении нет, как видно на осциллограмме.
Чтении? cranky.gif Вроде как запись по осциллограмме получается. Адрес 0x3C=0b00111100. Или я ошибаюсь?
Цитата(Kaplinsky @ Dec 7 2009, 22:39) *
В чем же дело ?
Выходит сама м/с растягивает SCL. laughing.gif Но почему она выдает NACK на запись я не знаю. sad.gif
Kaplinsky
Цитата(rezident @ Dec 7 2009, 20:34) *
Чтении? cranky.gif Вроде как запись по осциллограмме получается. Адрес 0x3C=0b00111100. Или я ошибаюсь?
Выходит сама м/с растягивает SCL. laughing.gif Но почему она выдает NACK на запись я не знаю. sad.gif

Ты не на ту осциллограмму смотрел. Чтение у меня вторая осциллограмма в этой теме.


ПРОРЫВ !
Проблема решена, причина найдена (неправильное программирование I2C модуля MSP430) !
(подробнее позже)

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


================================================================================

Изначально идея состояла в том что бы не использовать прерывания для чтения данных компаса.
Не хотелось городить глобальные переменные,флаги, счетчики, делать "роботу" в прерывании... и прочую ерунду - зачем если можно подождать (пусть и в активном ожидании) соответствующих флагов.

Моей главной ошибкой было то что я ждал бита UCTXSTT - ждал пока передастся START и адрес Славы (ну логично же! )

Код
  UCB0CTL1 |= UCTXSTT | UCTR;                   // Send START, SLAVE ADDR with WRITE
//  while (UCB0CTL1 & UCTXSTT);


а потом уже записывал адрес регистра в буфер передатчика.
А не надо было его ждать (почему - не знаю, ведь написано же, флаг очищается после того как передастся START и адрес Славы)
Но еще, после передачи устанавливается флаг прерывания передатчика (UCB0TXIFG) сигнализирующий о том что можно записывать данные в буфер передатчика, мол заряжай давай.

А понял я когда у меня уже вскипел моск, и я перебрав все варианты (и даже перепробовав разные резисторы) все же решил создать ISR и включить прерывания. Где то глубоко внутри терзало меня сомнение что примеры все написаны с использованием прерываний но я отмахивал это сомнение в конец очереди, так как не очень мне нравиться как сделаны примеры)

...
rolleyes.gif

вот рабочая функция, может кому пригодиться:

Код
#define NACK_TX -1
#define NACK_RX -2

int I2C_Read(unsigned char addr, unsigned char *data, int cnt){
int result,i;  

  result = 0;

  UCB0CTL1 |= UCTXSTT | UCTR;                   // Send START, SLAVE ADDR with WRITE
//  while (UCB0CTL1 & UCTXSTT);
  
  
  while (!(IFG2 & UCB0TXIFG)) if (UCB0STAT & UCNACKIFG){
                                result = NACK_TX;
                                goto STOP;
                              }
  UCB0TXBUF = addr;
  
  while (!(IFG2 & UCB0TXIFG)) if (UCB0STAT & UCNACKIFG){
                                result = NACK_TX;
                                goto STOP;
                              }
  
  UCB0CTL1 &= ~UCTR;
  UCB0CTL1 |= UCTXSTT;                            // Send START, SLAVE ADDR with WRITE

  for (i=0;i<cnt;i++){
  
    while (!(IFG2 & UCB0RXIFG)) if (UCB0STAT & UCNACKIFG){
                                  result = NACK_RX;
                                  goto STOP;
                                }
  
    data[i] = UCB0RXBUF;
    
  }//for
  
STOP:  
  UCB0CTL1 |= UCTXSTP;
  while (UCB0CTL1 & UCTXSTP);                     // Ensure stop condition got sent

  if (IFG2 & UCB0RXIFG) UCB0RXBUF;
  
  return result;
  
}//I2C_Read


Благодарю всех кто откликнулся: Alexashka, jam, rezident cheers.gif - спасибо ребята !
А также Форуму Electronix.ru - a14.gif

давно я так не подсаживался...
Alexashka
Таки объясните откуда Nax брался? Таймаут в слейве? smile.gif
Kaplinsky
Цитата(Alexashka @ Dec 8 2009, 18:57) *
Таки объясните откуда Nax брался? Таймаут в слейве? smile.gif


Думаю так: Из за того что я ждал больше положенного 9-клок задерживался со стороны микроконтроллера, и компас на нас забивал, потом, когда мы все же давали 9-й клок компасу уже было на нас начхать вот он и делал NACK. На самом деле делать NACK - значит ничего не делать на шине, а вот для ACK надо при девятом клоке удерживать SDA в низком состоянии.
rezident
Цитата(Kaplinsky @ Dec 8 2009, 23:53) *
когда мы все же давали 9-й клок компасу уже было на нас начхать вот он и делал NACK. На самом деле делать NACK - значит ничего не делать на шине, а вот для ACK надо при девятом клоке удерживать SDA в низком состоянии.
ACK - ACKnowledge (подтверждение), NACK - No ACKnowledge (отсутствие подтверждения). Согласно спецификации I2C, минимальная частота обмена на шине не ограничена, т.е. шина по сути полностью статическая. В отличие от SMBUS, например. Так что slave "начхать" на дешифрацию своего адреса никак не может (не имеет права), если только у него не аварийная ситуация при которой он не может корректно функционировать. Так что ваши догадки выглядят не очень убедительно. Может вы функцию инициализации модуля USCI еще до кучи приведете?
Kaplinsky
Цитата(rezident @ Dec 8 2009, 23:02) *
ACK - ACKnowledge (подтверждение), NACK - No ACKnowledge (отсутствие подтверждения). Согласно спецификации I2C, минимальная частота обмена на шине не ограничена, т.е. шина по сути полностью статическая. В отличие от SMBUS, например. Так что slave "начхать" на дешифрацию своего адреса никак не может (не имеет права), если только у него не аварийная ситуация при которой он не может корректно функционировать. Так что ваши догадки выглядят не очень убедительно. Может вы функцию инициализации модуля USCI еще до кучи приведете?


ОК, Слава дешифровал свой адрес, готов выдать ACK но 9-го клока все нет и нет. ( ну по крайней мере у меня других идей нет почему оно не работало раньше)

инициализация I2C:

Код
void I2C_Init(unsigned char s_addr){
  P3SEL |= 0x06;

  UCB0CTL1 |= UCSWRST;
  
  UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC;
  UCB0CTL1 = UCSSEL_3 + UCSWRST;

/* BASE FREQ (SMCLK) 8 Mhz
  0x0020 - 400 Kbs
  0x0050 - 100 Kbs
  0x0320 - 10 Kbs
  0x1F40 - 1 Kbs  */  
  
  UCB0BR0 = 0x50;
  UCB0BR1 = 0x00;
  
  UCB0I2COA = 0x01;
  UCB0I2CSA = s_addr;
  
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  IFG2 &= ~ (UCB0TXIFG | UCB0RXIFG);
    
}//I2C_Init
rezident
Цитата(Kaplinsky @ Dec 9 2009, 02:11) *
ОК, Слава дешифровал свой адрес, готов выдать ACK но 9-го клока все нет и нет.
Как я понял, у вас не писался следующий байт в регистр передатчика (не то условие проверяли, UCTXSTT вместо UCBxTXIFG), потому SCL тормозился. А потом у вас генерировалось STOP-условие, невзирая на отсутствие флага UCNACKIFG. Так?
Kaplinsky
Цитата(rezident @ Dec 9 2009, 03:52) *
Как я понял, у вас не писался следующий байт в регистр передатчика (не то условие проверяли, UCTXSTT вместо UCBxTXIFG), потому SCL тормозился.

Это верно.

Цитата(rezident @ Dec 9 2009, 03:52) *
А потом у вас генерировалось STOP-условие, невзирая на отсутствие флага UCNACKIFG. Так?

Нет, флаг UCNACKIFG был, я его обнаруживал и давал STOP что бы освободить шину.
Собственно из за этого флага невозможно было передать байт для записи (адрес регистра компаса)
Pavel V.
Буквально на днях боролся с аппаратным I2C на процессоре F1611, в итоге так и не смог его заставить работать (точнее, смог, но были какие-то паранормальные ошибки). Осциллографа под рукой, к сожалению, не было, поэтому точно сказать что происходило на шине не могу, но по косвенным признакам не формировался STOP после передачи заданного числа байт, приходилось формировать его вручную. Читал Errat-у по этому камню, там куча проблем с I2C, видимо, я на одну из них и напоролся.

Вылечилось все только программной реализацией I2C..
Kaplinsky
Цитата(Pavel V. @ Dec 9 2009, 15:47) *
Читал Errat-у по этому камню, там куча проблем с I2C, видимо, я на одну из них и напоролся.

Вылечилось все только программной реализацией I2C..

Таки да, граблей с I2C там хватает.
Перед тем как вскипятить себе мозг на MSP430F2471 я работал с MSP430F2274 (eZ430-RF2500) хотел подключить туда компас. В Errata проблема не описана но при работе линия SCL всегда была в нуле - что я только не делал.
http://e2e.ti.com/forums/p/9665/51804.aspx#51804

Поделись ка программной реализацией I2C пожалуйста ?
Pavel V.
Цитата(Kaplinsky @ Dec 9 2009, 17:07) *
Поделись ка программной реализацией I2C пожалуйста ?


Прикрепил к сообщению библиотечку. Использую ее для работы с часами PCF8563, весьма стабильно и предсказуемо, в отличие от аппаратной реализации unsure.gif

Использовать примерно так:

Код
uint8_t pcf8563_get_byte(uint8_t addr)
{
    i2cStart();
    i2cWrite(0xA2);
    i2cWrite(addr);
    i2cStart();
    i2cWrite(0xA3);
    uint8_t ret = i2cRead(0);
    i2cStop();

    return ret;
}

void pcf8563_set_byte(uint8_t addr, uint8_t value)
{
    i2cStart();
    i2cWrite(0xA2);
    i2cWrite(addr);
    i2cWrite(value);
    i2cStop();
}


Удачи!
1921
Преречитываю в который раз как увлекательный роман. Надо было пройти этим путём, чтобы оценить. КЛАСС!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.