В данной статье мы рассмотрим несколько датчиков: акселерометр, гироскопы и магнитометр. Эта статья стала послесловием недавней задачи, которую мы решаем. Считывание данных с этих датчиков было сделано, чтобы создать простейшую инерциальную навигационную систему, которая должна определять углы Эйлера твердого тела. Мы же посмотрим на возможности этих устройств, расскажем про подбор фильтров, питания, особенностей установки. Покажем наглядно на графиках качественные отличия и как обычно предоставим код для микроконтроллеров AVR для всех перечисленных в статье датчиков. Сразу отмечу, что рассказать в статье сразу про 3 датчика было осознанным действием. Ты увидишь далее, что считывание и запись данных в них - совершенно шаблонное действие.Нашими подопечными будут следующие датчики:
1. Гироскоп MMA7455L
2. Акселерометр L3G4200D
3. Гироскоп + магнитометр LSM303DLM.
Для начала ликбез:
В нашей статье гироскопом называется устройство, которое измеряет угловую скорость. Акселерометром - измеряющее 3 составляющих ускорения (и ускорение свободного падения тоже, оно абсолютно равноправно.). Магнитометром - измеряющее 3 составляющих магнитного поля.
Не стоит питать возвышенных чувств к полупроводниковым датчикам. Несмотря на передовые технологии, которые реализовали в этих устройствах фирмы Analog Devices и ST, они никак чисто из-за физики своего устройства и как следствие - погрешностей, уровню шумов и чувствительности, неинвариантности к внешним условиям применения не способны заменить механические датчики, применяемые военными в тех устройствах, которые имеют специальное назначение. Эти датчики были созданы для портативных устройств . Их задача - определять углы поворота твердого тела, углы наклона по отношению к вектору силы тяжести при допущении, что ускорение твердого тела мало, а также в задачах навигации на открытой местности магнитометр хорошо справится с указанием на север. Все датчики из статьи - новые (ну может за исключением MMA7455L) . Их разработали пару лет назад и поставляют до сих пор. Например гироскоп L3G4200D был установлен фирмой Apple в iPhone 4. Учитывая динамику этих датчиков можно смело сказать, что они будут доступны еще как минимум 3 года.
Все датчики имеют цифровой канал связи. С ними можно работать через I2C и SPI. Я выбрал I2C, почему:
1. На плате, на которой я их протестировал, не было возможностей для использования SPI. SPI требует число ножек контроллера, равное (n+3), где n - число устройств на шине. Получается 6 ножек. Это перебор. Столько не было.
2. Эти датчики - очень медленные. Их скорость преобразования - не более 200-400 Гц . Я использую самые медленные настройки и имею что-то около 60 Гц. С такими скоростями отлично справится I2C. На самом деле, проведем расчет: I2C запущена и отлично держит скорость работы 100 кГц. Максимальная заявленная скорость - 400 кГц. В публикациях, где веселые китайцы делают устройство инерциальной навигации на них, они разогнали их до 722кГц благодаря работе на ПЛИС, они очень гибко снимают с них данные - по сути параллельно по мере возникновения прерывания о готовности нового результата. Работа I2C не зависит от скорости работы. В этом очень большое преимущество - при отладке можно хоть переключателем данные слать как в старых аппаратах телеграфа и смотреть на мигание светодиода - определять какое число пришло. Итак типовая операция "прочитать регистр" содержит в себе 4 действия(каждое действие - посылка байта):
1. выдать условие "старта" и передать адрес устройства.
2. передать адрес регистра для считывания
3. выдать условие "старта" и передать адрес устройства, выставив бит чтения.
4. получить байт без подтверждения.
5. дождаться условия "стоп"
Без учета задержек на принимаемом устройстве мы получаем номинально около 30 бит, что означает 30 тактов работы устройства I2C. При скорости 100 кГц и частоте преобразования 100 Гц на каждое преобразование мы имеем 1000 тактов работы устройства I2C. Нужно учесть, что одно измерение - это 3 оси и 6 регистров данных. Это говорит о том, что шина I2C сможет эффективно работать более, чем с 5 устройствами подобного рода на одной шине. Ну или с 2 при выставленной максимальной скорости преобразования на них. Если использовать режим считывания нескольких регистров, то это число можно поднять не более, чем в 2 раза. Однако тут сидят подводные камни, о чем позже.
Важные параметры питания датчиков:
1. Эти штуки питаются от 2.4 до 3.6 вольт напряжения. (min до 1.8 вольт - напряжение питания ввода/вывода). Их линии ввода-вывода не толерантные к 5 вольт.
2. Важно не устанавливать их вблизи силовых дорожек. Также нельзя их ставить вблизи импульсных источников питания. Все это приведет к большому увеличению шума и мы это покажем наглядно.
3. Лично я питаю их от линейного источника питания, на вход которого подается стабилизированное питание от импульсного преобразователя.
4. Превышение напряжения питания 3.6 вольт может не вывести датчик из строя, однако он будет работать хуже или даже неправильно: уменьшится чувствительность, будут потери данных в регистрах инициализации и ошибки при работе i2c. Это сложно детектировать, поэтому очень важен выбор питания. Никаких выбросов и провалов по земле не должно быть. При наличии силовой части в устройстве - нужно полностью разделить земли и использовать хороший стабилизатор входного питания.
Особенности подключения:
1. Подтянуть линии SDL, SDA к верхнему уровню.
Здесь используются резисторы 10 КОм. Это много для шины I2C, рекомендуется не более 4.7 КОм и даже 1 КОм. Это актуально для тех, кто будет использовать датчики в режиме высокой скорости I2C. При таких подтяжках на 400 КГц фронты будут безнадежно завалены.
2. Сага о преобразователях уровней. Их приходится использовать. Одна особенность, на которую я напоролся. Очень долго промучался с этим. Это касается преобразователей уровней от фирмы MAX. На рисунке изображен правильный вариант подключения:
Вот смотрите. Шина I2C использует 3 состояния. Ей нужно Z состояние тоже. Поэтому я в преобразователе подтянул этот вход к верхнему уровню. ОДНАКО, особенно внимательные заметили наверно, что эта микросхема как бы разделена на двое: слева - всё, что касается 1 напряжения питания, допустим, это 3.3 вольта, правая часть - как бы другая сторона 5 вольт. Так вот, даже во всех там рекламных чудо публикациях производитель для наглядности использует этот факт, в структурной схеме вы также увидите этот прием. Не тут то было, вход 3 state НЕ ТОЛЕРАНТЕН к 5 вольт. Если вы подтянете его к 5 вольт - откроется защитный диод и в некоторых случая микросхема защелкнется, в других будет греться и тоже не выполнять свою функцию. Это просто жесть и введение в заблуждение разработчика, однако, производителю на это наплевать. На данный вопрос, фирма MAX не прислала никакого ответа. Подтягивать 3state нужно к тому напряжению, которое слева, хотя он и расположен по ту сторону баррикад. Отмечу, что 3state - это всего лишь включение внутренних подтяжек к верхнему уровню. Это нужно для того, чтобы когда устройство на шине I2C переходит выводами в Z состояние, сохранялась определенность потенциала линии. Скорее всего, трансивер I2C реализован в виде открытых коллекторов, в которых 2 состояния: 0 и Z.
Слева на картинке вы видите чудо линейный преобразователь LM1117. Это дерьмо характерно тем, что может вместо 3.3 вольт выдать что-то в диапазоне 3.3 - 4.1 вольт и убить нафиг девайс. И зависит его поведение от длины дорожек, от нагрузки (потребляемого тока). Но больше - от проводов, т.е. длины питающих линий. В чем дело - я не знаю, купил в Терре. Вот тут, кстати, обсуждение этого глюка. Я думаю - контрафакт. Я решил эту проблему - выведением преобразователя в зону повышенного потребления - нагрузил резисторами. Это греется, но работает. Это происшествие прибило мои датчики. Они все равно работали, но неверно. Да, кондеры на входе и на выходе есть. Всё сделано по даташиту, они просто не уместились на этой картинке. Из-за этого происшествия время поднятия этих датчиков выросло недели на 2. Уже позднее я выпаял этот преобразователь и поставил другой. Он выдавал уже нормально 3.3 вольта. Прием вывода преобразователя в зону повышенного потребления - это старый инструмент. Впервые я столкнулся с необходимостью этого на дешевых AC/DC. Дело в том, что при резко изменяющейся нагрузке, например, включению реле, такие устройства при низком потреблении не способны быстро отдать ток. Их динамика зависит от текущего тока потребления и если их вывести в зону номинальных токов, то они будут лучше стабилизировать выходное напряжение. Эту проблему может также решить блокирующий конденсатор.
Итак, теперь перейдем к делу.
Рутина I2C.
Сразу хочу отметить, я уже точно не помню, откуда у меня появились некоторые исходники для I2C, помню что для чтения и записи я немного их поменял, а вот файлы twimaster.h, twimaster.c от другого автора.
Работу с I2C я построил следующим образом:
- twimaster - для работы с i2c непосредственно.
- I2C_IO - драйвер для операций ввода-вывода, т.е. чтения и записи в регистры устройства.
Эта основа универсальна для всех датчиков и всех контроллеров AVR, я ее приложу в конце статьи.
Датчики.
MMA7455L. 3х осевойАкселерометр от Analog Devices.
Digital Output (I2C/SPI)
• 3mm x 5mm x 1mm LGA-14 Package
• Self-Test for Z-Axis
• Low Voltage Operation: 2.4 V – 3.6 V
• User Assigned Registers for Offset Calibration
• Programmable Threshold Interrupt Output
• Level Detection for Motion Recognition (Shock, Vibration, Freefall)
• Pulse Detection for Single or Double Pulse Recognition
• Sensitivity (64 LSB/g @ 2g and @ 8g in 10-Bit Mode)
• Selectable Sensitivity (±2g, ±4g, ±8g) for 8-bit Mode
• Robust Design, High Shocks Survivability (5,000g)
• RoHS Compliant
• Environmentally Preferred Product
• Low Cost
Typical Applications
• Cell Phone/PMP/PDA: Image Stability, Text Scroll, Motion Dialing,
Tap to Mute
• HDD: Freefall Detection
• Laptop PC: Freefall Detection, Anti-Theft
• Pedometer
• Motion Sensing, Event Recorder
Схема подключения:
Заметим, младший значащий бит адреса устройства будет 1, так как Iaddr0 подтянуто к верхнему уровню.
Список регистров:
Address
Name
Definition
Bit 7
Bit 6
Bit 5
Bit 4
Bit 3
Bit 2
Bit 1
Bit 0
$00
XOUTL
10 bits output value X LSB
XOUT[7]
XOUT[6]
XOUT[5]
XOUT[4]
XOUT[3]
XOUT[2]
XOUT[1]
XOUT[0]
$01
XOUTH
10 bits output value X MSB
-
-
-
-
-
-
XOUT[9]
XOUT[8]
$02
YOUTL
10 bits output value Y LSB
YOUT[7]
YOUT[6]
YOUT[5]
YOUT[4]
YOUT[3]
YOUT[2]
YOUT[1]
YOUT[0]
$03
YOUTH
10 bits output value Y MSB
-
-
-
-
-
-
YOUT[9]
YOUT[8]
$04
ZOUTL
10 bits output value Z LSB
ZOUT[7]
ZOUT[6]
ZOUT[5]
ZOUT[4]
ZOUT[3]
ZOUT[2]
ZOUT[1]
ZOUT[0]
$05
ZOUTH
10 bits output value Z MSB
-
-
-
-
-
-
ZOUT[9]
ZOUT[8]
$06
XOUT8
8 bits output value X
XOUT[7]
XOUT[6]
XOUT[5]
XOUT[4]
XOUT[3]
XOUT[2]
XOUT[1]
XOUT[0]
$07
YOUT8
8 bits output value Y
YOUT[7]
YOUT[6]
YOUT[5]
YOUT[4]
YOUT[3]
YOUT[2]
YOUT[1]
YOUT[0]
$08
ZOUT8
8 bits output value Z
ZOUT[7]
ZOUT[6]
ZOUT[5]
ZOUT[4]
ZOUT[3]
ZOUT[2]
ZOUT[1]
ZOUT[0]
$09
STATUS
Status registers
-
-
-
-
-
PERR
DOVR
DRDY
$0A
DETSRC
Detection source registers
LDX
LDY
LDZ
PDX
PDY
PDZ
INT2
INT1
$0B
TOUT
“Temperature output value” (Optional)
TMP[7]
TMP[6]
TMP[5]
TMP[4]
TMP[3]
TMP[2]
TMP[1]
TMP[0]
$0C
(Reserved)
-
-
-
-
-
-
-
-
$0D
I2CAD
I2C device address
I2CDIS
DAD[6]
DAD[5]
DAD[4]
DAD[3]
DAD[2]
DAD[1]
DAD[0]
$0E
USRINF
User information (Optional)
Ul[7]
Ul[6]
Ul[5]
Ul[4]
Ul[3]
Ul[2]
Ul[1]
Ul[0]
$0F
WHOAMI
“Who am I” value (Optional)
ID[7]
ID[6]
ID[5]
ID[4]
ID[3]
ID[2]
ID[1]
ID[0]
$10
XOFFL
Offset drift X value (LSB)
XOFF[7]
XOFF[6]
XOFF[5]
XOFF[4]
XOFF[3]
XOFF[2]
XOFF[1]
XOFF[0]
$11
XOFFH
Offset drift X value (MSB)
-
-
-
-
-
XOFF[10]
XOFF[9]
XOFF[8]
$12
YOFFL
Offset drift Y value (LSB)
YOFF[7]
YOFF[6]
YOFF[5]
YOFF[4]
YOFF[3]
YOFF[2]
YOFF[1]
YOFF[0]
$13
YOFFH
Offset drift Y value (MSB)
-
-
-
-
-
YOFF[10]
YOFF[9]
YOFF[8]
$14
ZOFFL
Offset drift Z value (LSB)
ZOFF[7]
ZOFF[6]
ZOFF[5]
ZOFF[4]
ZOFF[3]
ZOFF[2]
ZOFF[1]
ZOFF[0]
$15
ZOFFH
Offset drift Z value (MSB)
-
-
-
-
-
ZOFF[10]
ZOFF[9]
ZOFF[8]
$16
MCTL
Mode control
-
DRPD
SPI3W
STON
GLVL[1]
GLVL[0]
MOD[1]
MOD[0]
$17
INTRST
Interrupt latch reset
-
-
-
-
-
-
CLRINT2
CLRINT1
$18
CTL1
Control 1
DFBW
THOPT
ZDA
YDA
XDA
INTRG[1]
INTRG[0]
INTPIN
$19
CTL2
Control 2
-
-
-
-
-
DRVO
PDPL
LDPL
$1A
LDTH
Level detection threshold limit value
LDTH [7]
LDTH [6]
LDTH [5]
LDTH[4]
LDTH [3]
LDTH[2]
LDTH[1]
LDTH[0]
$1B
PDTH
Pulse detection threshold limit value
PDTH[7]
PDTH[6]
PDTH[5]
PDTH[4]
PDTH[3]
PDTH[2]
PDTH[1]
PDTH[0]
$1C
PW
Pulse duration value
PD[7]
PD[6]
PD[5]
PD[4]
PD[3]
PD[2]
PD[1]
PD[0]
$1D
LT
Latency time value
LT[7]
LT[6]
LT[5]
LT[4]
LT[3]
LT[2]
LT[1]
LT[0]
$1E
TW
Time window for 2nd pulse value
TW[7]
TW[6]
TW[5]
TW[4]
TW[3]
TW[2]
TW[1]
TW[0]
$1F
(Reserved)
-
-
-
-
-
-
-
-
Датчик - 2008 года. И тогда, видимо, еще не было возможности для дешевых датчиков у производителя сделать разрешающую способность - 16 бит. Здесь только 10 значащих бит. Кроме того, ВСЕ ДАННЫЕ ПО ИЗМЕРЕНИЯМ ИДУТ В ФОРМАТЕ 2'S. Это формат дополнительного кода = представление отрицательных чисел. И это нужно учитывать при выводе результатов и не жаловаться на то, что выводится какой-то бред и все положительные. Все API функции в выложенных здесь файлах - выводят в формате int16_t, поэтому такой проблемы возникнуть тут не должно. Все остальные разъяснения приведем на примере кода.
Инициализация:
Настраиваем регистр 0x16. (замечу, что адреса регистров в шестнадцатеричной форме).
D7
D6
D5
D4
D3
D2
D1
DO
Bit
-
DRPD
SPI3W
STON
GLVL[1]
GLVL[0]
M0DE[1]
MODE[0]
Function
0
0
0
0
0
0
0
0
Default
GLVL [1:0]
00: 8g is selected for measurement range.
10: 4g is selected for measurement range.
01: 2g is selected for measurement range.
STON
0: Self-test is not enabled
1: Self-test is enabled
MODE [1:0]
00: Standby Mode
01: Measurement Mode
10: Level Detection Mode
11: Pulse Detection Mode
SPI3W
0: SPI is 4 wire mode
1: SPI is 3 wire mode
DRPD
0: Data ready status is output to INT1/DRDY PIN
1: Data ready status is not output to INT1/DRDY PIN
Table 11. Configuring the Mode using Register $16 with MODE[1:0] bits
MODE [1:0]
Function
00
Standby Mode
01
Measurement Mode
10
Level Detection Mode
11
Pulse Detection Mode
void initAxel()
{
writeI2Cbyte(MMA7455_I2CADDR,0x16,0b00000101);
}
Здесь мы включили режим непрерывных измерений и в качестве диапазона измерений выбрали +/-2g.
Считывание регистров:
uint16_t axel[1];
uint16_t getAxelX()
{
readI2CbyteArray(MMA7455_I2CADDR,MMA7455_XOUT10,(uint8_t *)axel,2);
return axel[0];
}
uint16_t getAxelY()
{
readI2CbyteArray(MMA7455_I2CADDR,MMA7455_YOUT10,(uint8_t *)axel,2);
return axel[0];
}
uint16_t getAxelZ()
{
readI2CbyteArray(MMA7455_I2CADDR,MMA7455_ZOUT10,(uint8_t *)axel,2);
return axel[0];
}
Здесь мы для убыстрения считываем сразу несколько регистров. (вообще можно было считать сразу все, но для удобства понимания, я сделал так). А теперь внимание, сейчас это число представлено в дополнительном коде. Чтобы его вывести - нужно провести операцию копирования в память, где расположена переменная int16_t, присваивание использовать довольно опасно, так как результат такого зависит от компилятора. Удобнее всего использовать memcpy, как будет показано далее.
int16_t resX;
int16_t resY;
int16_t resZ;
uint16_t X=(getAxelX()<<6);
memcpy(&resX,& X,sizeof( uint16_t ));
uint16_t Y=(getAxelY()<<6);
memcpy(&resY,& Y,sizeof( uint16_t ));
uint16_t Z=(getAxelZ()<<6);
memcpy(&resZ,& Z,sizeof( uint16_t ));
На этом всё, результат работы показан на графиках. Плата совершает вращательные реверсивные движения. На оси проецируется центростремительное и тангенциальное ускорения тех точек, куда припаяны датчики.
Как вы видите - результат не очень. Приводить статистические данные не буду, поправку на ноль не делали. Датчик был установлен неудачно. Те советы, которые были озвучены - не были учтены.Кроме того, за 3 года датчики стали более совершенны и это можно будет увидеть далее.
Гироскоп L3G4200D
Этот датчик от фирмы ST. Датчики от ST по моим наблюдениям обладают лучшим соотношением цена/качество. Они меньше шумят.
1. Шкала измерений 200/500/2000 градусов в секунду
2. I2C+SPI
3. 16 битные измерения
4. Датчик температуры 8 бит.
5. Встроенные фильтры высоких и низких частот.
Схема подключения:
Вверху схемы - фильтр ФАПЧ. Он нужен обязательно. Я в инете встречал пример, где его не было. Это бред, это подтвердил производитель. Напряжение питания я выбрал 3.3 вольта.
Разбор полёта:
Список регистров
Name
Type
Register address
Default
Comment
Hex
Binary
Reserved
-
00-OE
-
-
WHO_AM_l
r
OF
000 1111
11010011
Reserved
-
10-1F
-
-
CTRL_REG1
rw
20
010 0000
00000111
CTRL_REG2
rw
21
010 0001
00000000
CTRL_REG3
rw
22
010 0010
00000000
CTRL_REG4
rw
23
010 0011
00000000
CTRL_REG5
rw
24
010 0100
00000000
REFERENCE
rw
25
010 0101
00000000
OUT_TEMP
r
26
010 0110
output
STATUS_REG
r
27
010 0111
output
OUT_X_L
r
28
010 1000
output
OUT_X_H
r
29
010 1001
output
OUT_Y_L
r
2A
010 1010
output
OUT_Y_H
r
2B
010 1011
output
OUT_Z_L
r
2C
010 1100
output
OUT_Z_H
r
2D
010 1101
output
FIFO_CTRL_REG
rw
2E
010 1110
00000000
FIFO_SRC_REG
r
2F
010 1111
output
INT1_CFG
rw
30
011 0000
00000000
INT1_SRC
r
31
011 0001
output
INT1_TSH_XH
rw
32
011 0010
00000000
INT1_TSH_XL
rw
33
011 0011
00000000
INT1_TSH_YH
rw
34
011 0100
00000000
INT1_TSH_YL
rw
35
011 0101
00000000
INT1_TSH_ZH
rw
36
011 0110
00000000
INT1_TSH_ZL
rw
37
011 0111
00000000
INT1_DURATI0N
rw
38
011 1000
00000000
Дальше также на примере кода:
uint8_t gyroInit(uint8_t l3g4200address)
{
// Включили оси x, y, z, выключили режим энергосбережения:
writeI2Cbyte(l3g4200address, CTRL_REG1, 0b00111111);//скорость преобразования 100 Гц,полоса - 25Гц
// настройки фильтра высоких частот
writeI2Cbyte(l3g4200address, CTRL_REG2, 0b00100011); //нижняя частота - 1 Гц.
// настройка прерываний
writeI2Cbyte(l3g4200address, CTRL_REG3, 0b10001000); // INT1 включен, INT2 готовность данных тоже
// общие настройки: диапазон +/- 250 градусов в секунду, выключено самотестирование
writeI2Cbyte(l3g4200address, CTRL_REG4, 0b10000000);//включена защита обновления данных при считывании
// включен сброс настроек при перезагрузке
writeI2Cbyte(l3g4200address, CTRL_REG5, 0b11000000); // включен буфер FIFO, все фильтры отключены
writeI2Cbyte(l3g4200address, 0x2E, 0b00000000); // включен режим, когда считывается самый свежий результат, сохраненный в FIFO.
return (readI2Cbyte(l3g4200address,0x0F)==L3G4200D_WHOAMI); // проверка. Эй там, ты вообще жив?
}
Результаты работы на графиках. Тип движений - те же самые. Количество колебаний вокруг осей - разное.
На мой взгляд, данные сняты хорошо. Уровень шума в покое составляет не более 0.1-0.2% шкалы.
LSM303DLH. Акселерометр+гироскоп. Устройство содержит в себе 2 в одном.
Analog supply voltage: 2.5 V to 3.3 V
Digital supply voltage IOs: 1.8 V
Power-down mode
3 magnetic field channels and 3 acceleration
channels
±1.3 to ±8,1 gauss magnetic field full-scale
±2 g/±4 g/±8 g dynamically selectable fullscale
16-bit data out
I2C serial interface
Не буду заострять внимание на этом датчике. Считывание данных происходит по такой же схеме.
Акселерометр.
Покажу графики для примера:
uint16_t AX;
uint16_t AY;
uint16_t AZ;
int16_t A_X;
int16_t A_Y;
int16_t A_Z;
uint8_t LSM303DLH_initAxel(uint8_t axelAddress)
{
writeI2Cbyte(axelAddress, CTRL_REG1_A, 0b00100111); //включили оси, режим постоянной работы
return (readI2Cbyte(axelAddress,0x0F)==axel_WHO_AM_I);
}
int16_t LSM303DLH_getAxel_X()
{
AX=((readI2Cbyte(LSM303DLM_axel_ADDRESS0,0x29)<<8)+(readI2Cbyte(LSM303DLM_axel_ADDRESS0,0x28)));
memcpy(&A_X,&AX, sizeof ( uint16_t ));
return A_X;
}
int16_t LSM303DLH_getAxel_Y()
{
AY=((readI2Cbyte(LSM303DLM_axel_ADDRESS0,0x2B)<<8)+(readI2Cbyte(LSM303DLM_axel_ADDRESS0,0x2A)));
memcpy(&A_Y,&AY, sizeof ( uint16_t ));
return A_Y;
}
int16_t LSM303DLH_getAxel_Z()
{
AZ=((readI2Cbyte(LSM303DLM_axel_ADDRESS0,0x2D)<<8)+(readI2Cbyte(LSM303DLM_axel_ADDRESS0,0x2C)));
memcpy(&A_Z,&AZ, sizeof ( uint16_t ));
return A_Z;
}
Не ругайтесь за глобальные переменные. Они играют роль буфера - временного хранения данных. Из этих переменных берутся данные для их последующего использования. Функции же вызываются в прерывании - когда готов результат. Графики в студию:
Данные намного лучше, чем у MMA7455L. Менее шумные и более чувствительные.
Магнитометр. Принципиально - все также при считывании. Диапазон измеряется в Гауссах. От 1.3 до 8.1. Для измерения магнитного поля Земли нужно выбрать в районе 1 Гаусса.
Лучше поясним на примере кода:
uint16_t MX=0;
uint16_t MY=0;
uint16_t MZ=0;
int16_t M_X;
int16_t M_Y;
int16_t M_Z;
uint8_t LSM303DLH_initMagnet(uint8_t magnetAddress)
{
writeI2Cbyte(magnetAddress, CRA_REG_M, 0b00010100); // скорость работы - 30 Гц
writeI2Cbyte(magnetAddress, CRB_REG_M, 0b11100000); // диапазон +/- 8.1 Гаусса
writeI2Cbyte(magnetAddress, MR_REG_M, 0b00000000); // режим непрерывных измерений
return (readI2Cbyte(magnetAddress,0x0F)==magnet_WHO_AM_I);
}
int16_t LSM303DLH_getMagnet_X()
{
MX=((readI2Cbyte(LSM303DLM_magnet_ADDRESS0,0x03)<<8)+(readI2Cbyte(LSM303DLM_magnet_ADDRESS0,0x04)));
memcpy(&M_X,&MX, sizeof ( uint16_t ));
return M_X;
}
int16_t LSM303DLH_getMagnet_Y()
{
MY=((readI2Cbyte(LSM303DLM_magnet_ADDRESS0,0x05)<<8)+(readI2Cbyte(LSM303DLM_magnet_ADDRESS0,0x06)));
memcpy(&M_Y,&MY, sizeof ( uint16_t ));
return M_Y;
}
int16_t LSM303DLH_getMagnet_Z()
{
MZ=((readI2Cbyte(LSM303DLM_magnet_ADDRESS0,0x07)<<8)+(readI2Cbyte(LSM303DLM_magnet_ADDRESS0,0x08)));
memcpy(&M_Z,&MZ, sizeof ( uint16_t ));
return M_Z;
}
Графики:
Для себя я сделал вывод, что используя магнитометр можно определять абсолютное направление на север. Даже в помещении магнитометр не выдает каких-то хаотических показаний. Его показания в целом закономерны и привязаны к абсолютному угловому положению.
Скачать проект: исходники для всех датчиков, драйвер ввода-вывода, рутина I2C.