Подключение датчика по SPI

joint931
Offline
Зарегистрирован: 26.09.2011

Пытаюсь подключить датчик магнитного поля ХЕN-1210. Даташит сдесь. Использую arduino nano.

Набросал вот такой код.. явно с ошибками, т.к. раньше с spi никогда не работал. На выходе пока одни нули.

#include <SPI.h>
const int DV = 2;     // DV
const int CS = 10;   // CS
const int SDO = 12; // SDO
const int CLK = 3;   // CLK
byte WriteSPIBuf[2];
byte ReadSPIBuf[2];

void setup() {
Serial.begin(9600); // Установка скорости передеачи данных
pinMode(CS, OUTPUT);
SPI.begin(); // Старт SPI библиотеки:
}

void SPI_Write(unsigned char *data, unsigned char lenght)
{
unsigned char i; // счетчик
digitalWrite(CS, LOW);
delay(0.022);
for (i = 0; i < lenght; i++) SPI.transfer(*(data+i)); // передаем данные
delay(0.022);
digitalWrite(CS, HIGH);
}

void SPI_Read(unsigned char *data, unsigned char lenght)
{
unsigned char i;
digitalWrite(CS, LOW);
delay(0.022);
for (i = 0; i < lenght; i++)
SPI.transfer(*(data+i)); // считываем даные
delay(0.022);
digitalWrite(CS, HIGH);
}

void Init_XEN (void)
{
//Этот кусок когда мне подсказали на этом форуме - генерирует импульсы частотой ~1Мгц на ноге D3.
TCCR2B = _BV(CS20); // prescaler = F_CPU/1
OCR2A = 7; // clear timer at 0 count, f = F_CPU/2
TCCR2A = _BV(COM2B0) | _BV(WGM21); // toggle PD3 on compare match
DDRD |= _BV(PORTD3); // start clock output on XIN/PD3

// Подаем команду POWER-OFF
WriteSPIBuf [0] = 0x40;
WriteSPIBuf [1] = 0x00;
WriteSPIBuf [2] = 0x00;
SPI_Write(WriteSPIBuf, 3);
delay(0.022);

// Подаем команду RESET
WriteSPIBuf [0] = 0x10;
WriteSPIBuf [1] = 0x00;
WriteSPIBuf [2] = 0x00;
SPI_Write(WriteSPIBuf, 3);
delay(0.022);

// Задаем время измерений
WriteSPIBuf [0] = 0x01;
WriteSPIBuf [1] = 0x41;
WriteSPIBuf [2] = 0x13;
SPI_Write(WriteSPIBuf, 3);
delay(0.022);

// Подаем команду TEST
WriteSPIBuf [0] = 0x02;
WriteSPIBuf [1] = 0x3A;
WriteSPIBuf [2] = 0x00;
SPI_Write(WriteSPIBuf, 3);
delay(0.022);
}

void loop() {
unsigned int i; // счетчик
unsigned char magdata[12]; // временный буфер для сохранения данных c датчика
signed long *plong; // указатель на переменную типа long

Init_XEN();

// Задаем частоту ~1Мгц на ноге D3
TCCR2B = _BV(CS20);
OCR2A = 7;
TCCR2A = _BV(COM2B0) | _BV(WGM21);
DDRD |= _BV(PORTD3);

delay(0.022);

// Подаем команду Single Shot
WriteSPIBuf [0] = 0x60;
WriteSPIBuf [1] = 0x00;
WriteSPIBuf [2] = 0x00;
SPI_Write(WriteSPIBuf, 3);

delay(10); //ждем окончания измерени

SPI_Read(ReadSPIBuf, 3); // Считываем 3 байта
delay(0.022);

// переводим полученные данные в формат удобный для вывода информации
magdata[0] = ReadSPIBuf[0];
magdata[1] = ReadSPIBuf[1];
magdata[2] = ReadSPIBuf[2];

Serial.println(magdata[0], DEC);
Serial.println(magdata[1], DEC);
Serial.println(magdata[2], DEC);
delay(1000);
}

Можете подсказать, что я делаю не так? Заранее спасибо.

leshak
Offline
Зарегистрирован: 29.09.2011

 Ну первое что бросается в глаза: почему "Init_XEN();" и "" Задаем частоту ~1Мгц на ноге D3" попали в функцию loop, а не setup? Получается что вы постоянно производите инициализацию датчика и считываете только первое, после инициализации, значение.

Кстати "Задаем частоту на ноге D3", вы тоже делате два раза. Вначале внутри Init_Xen(), а потом отдельно в loop. Что-то одно явно лишнее.

Может быть "так и нужно" или "это не играет роли", но выглядит подозрительно.
 

joint931
Offline
Зарегистрирован: 26.09.2011

Оставил код настройки частоты только в setup. Ничего не изменилось. Я не совсем понимаю как в SPI организуется считывание данных. Вот в шине I2C есть адрес чтения устройства. С него и берем данные. А сдесь как? В даташите на девайс написано вот что: single-shot операции с таймингами измерения 1мс (при частоте 1MHz) показаны в таблице 12. Команда включает устройство для 1 измерения и сразу же выключает девайс после измерения. Затем устройство хранит данные в памяти, пока их считает микроконтроллер.
В таблице 12: Program Byte= 0x61, Setting Bytes=0x1113.
Что с этим делать? Послать последовательно в девайс 0x61, 0x11, 0x13? А потом что? Пригонять в переменную через SPI.transfer(0) данные? Проясните, пожалуйста.
Заранее спасибо.
 

leshak
Offline
Зарегистрирован: 29.09.2011

 Сразу оговорюсь что с SPI я тоже только начал разбиратся (и вообще в микроконтроллерах я нуб). Просто посмотрел на ваш код с точки зрения "программерского здравого смысла". Так что все мои слова только "как бы я пытался решить проблему".

>Вот в шине I2C есть адрес чтения устройства. С него и берем данные. А сдесь как?

Если верить http://arduino.cc/en/Reference/SPI то тут выбор происходит с помощью пина SS (10). Когда он LOW - то девайс слушает/отвечает в шину. Если High - игнорирует мастера.  У вас вроде "по рекомендации" правильно  сделано (установить его в OUTPUT).  Если я правильно понимаю, то в случае нескольких устройств, всем, кроме одного, нужно SS перевести в HIGH и тогда мастер будет общаться только с "этим одним".

Следующие что я бы сделал, это заменил ваш код ""Задаем частоту ~1Мгц на ноге D3" на вызов библиотечной функции SPI.setClockDivider() (скорее всего с параметром SPI_CLOCK_DIV16). Как я понимаю она аналогична вашему коду. Просто "доверия" к ней больше, чем к коду копипасченному из какого-то контекста, да и код "компактней". Так же очень меловероятно, но возможно, что-бы "заработал CLOCK" нужно разрешить прерывания (хотя скорее всего в библиотеках они уже включаются сами).

>Пригонять в переменную через SPI.transfer(0) данные?

В туториале http://arduino.cc/en/Tutorial/BarometricPressureSensor похоже так и делают

// send a value of 0 to read the first byte returned:
  result = SPI.transfer(0x00);

Причем вызывать ее нужно столько раз, сколько байт вы ожидаете получить. Обратите внимание на DV пин в вашем даташите. Если не ошибаюсь это аналог dataReadyPin из туториала. Похоже он как раз сообщает что "данные готовы". Поэтому попробуйте ориентироваться на его состояние, вместо "delay(10); //ждем окончания измерени" подобно тому как это делают в туториале. Можно вообще попробовать повестися на состояние этого пина через attachInterrupt и вычитывать данные только когда они "действительно готовы".

 

Так же проверил бы подключение. Я например, наступал на грабли, когда MOSI девайса подключил на MISO ардуины. По аналогии с USART где RX,TX нужно инвертировать. Тут такого не нужно. MOSI должно идти к MOSI, MISO к MISO.

Так же стоит внимательно просмотреть на arduino.cc/en/Reference/SPI пункты начинающиеся словами "To write code for a new SPI device you need to note a few things". То есть покурить даташит вашего устройства на предмет какой у него должен быть "BitOrder", как он интерпретирует уровни на Clock-пине. Отсутвие данных это LOW или HIGH на этом пине. В какой момент данные передаются когда он переходит из LOW в HIGH или из HIGH в LOW. Вообщем разобратся с функциями SPI.setBitOrder(), SPI.setDataMode(),SPI.setClockDivider()

Так же, надеюсь вы не забыли что чип нужно запитать именно от 3.3V. Правда тут тоже есть "нюанс".  По крайней мере на моей меге, эти 3.3V не совсем "честные". Отдельного регулятора питания на них нет. Они берутся от микросхемы usb. Брать можно не более только небольшой ток (не помню сейчас точную цифру, толи 50, то ли 100 ma). Лично у меня они запросто "просаживаются" до 2.9V, и мой модуль (я подключал Ethernet по SPI) начинал глючить. Пока не собрал отдельный регулятор питания для него.Померяйте какое у вас питание на модуле в реальности. 

Кроме того должно быть согласование по вольтажу на управляющих сигналах между ардуиной и датчиком. То есть сама ардиина 3.3V нормально поймет как "единицу", а вот если она даст 5v на дата вход модуля - он может этого "не понять и обидится". Магических слов "5v tolerant" я в его даташите не увидел. В моем случае мне повезло и такие слова были, но "если их нет", то мне советовали гуглить "74HC08" для согласования уровней.