Акселерометр LIS302DL
Если вы счастливый обладатель платы STM32F4Discovery, но наверняка вас посещала мысль запустить акселерометр который на ней установлен. Для этого нам потребуется сама платка и переходник USB-UART через который мы будем отправлят данные в компьютер. Сам акселерометр уже подключен к микроконтроллеру как надо, если же у вас нет платы дискавери, то нужно подключить его как на рисунке ниже:
У акселерометра есть два интерфейса для обмена данными - i2c и SPI. Использовать мы будем последний, благо я еще не забыл как это делать, да и разводка платы подразумевает его использование. Сам акселерометр обладает следующими характеристиками:
- Напряжение питания 2.16 - 3.6 в.
- Измерение ускорения по трём осям
- Два диапазона измерения 2G/8G
- Два настраиваемых выхода для прерываний
- Самодиагностика
- Обнаружение кликов (постукиваний)
- Встроенный фильтр
- Корпус LGA14
Пожалуй, последняя характеристика является минусом данного датчика, он очень мелкий и я рад что мне не придётся ничего паять :) Сразу предупреждаю, что меня одолело графоманство и поэтому букв будет много, запаситесь терпением.
У чипа 14 ног, их назначение описано в таблице ниже, а картинка показывает нумерацию выводов и расположение осей:
Номер вывода |
Имя |
Назначение |
1 |
Vdd_IO |
Питание ножек I/O |
2 |
GND |
Земля |
3 |
Reserved |
Зарезервировано. Подключать к питанию |
4 |
GND |
Земля |
5 |
GND |
Земля |
6 |
Vdd |
Питание |
7 |
CS |
Выбор интерфейса. Если на ноге лог. 1, то акселерометр думает, что используется i2c, в противном случае – SPI. |
8 |
INT 1 |
Вывод прерывания 1 |
9 |
INT2 |
Вывод прерывания 2 |
10 |
GND |
Земля |
11 |
Reserved |
Зарезервировано, подключить к земле |
12 |
SDO |
В режиме SPI эта нога служит для передачи данных к мастеру (MISO). Если используется i2c интерфейс, то логический уровень на этой ноге определяет младший бит адреса. Т.е. если на ней лог. 0, то адрес устройства - 0x1C, в противном случае адрес будет 0x1D |
13 |
SDA SDI SDO |
Если используется i2c интерфейс, то этот вывод используется для передачи данных (SDA). В режиме SPI эта нога служит для приёма данных от мастера (MOSI). Если используется 3-х проводной режим SPI, то через этот вывод происходит двусторонний обмен данными. |
14 |
SCL SPC |
Если используется i2c интерфейс, то этот вывод используется для передачи тактовых импульсов (SCL), в режиме SPI он тоже используется для тактирования (SCK) |
Взаимодействие с акселерометром осуществляется через его регистры. Чтоб прочитать или что-то записать в них, нужно отправить через SPI посылку определённого формата. В даташите есть несколько хороших картинок по этому поводу. Нам хватит одной вот этой:
На картинке изображены 4 линии интерфейса SPI, каждый бит подписан:
- DO7..DO0 - байт данных отправленный акселерометром в микроконтроллер
- DI7..DI0 - байт данных переданный микроконтроллером в акселерометр
- AD5..AD0 - адрес записываемого/считываемого регистра
- RW - если бит=0 то байт данных DI7..DI0 будет записан в регистр по адресу AD5..AD0, в противном случае (RW=1) байт данных DO7..DO0 будет прочитан из регистра по адресу AD5..AD0
-
MS - используется если мы хотим прочитать/записать несколько регистров подряд. Если бит сброшен, то после передачи адреса мы будем считывать/записывать один и тот же регистр вне зависимости от того, сколько раз будет предпринята попытка чтения/записи. Если же этот бит установлен, то адрес будет автоматически увеличиваться на единицу после каждой записи или чтения.
Чтение одного регистра
- Вывод CS прижать к земле.
- Передать первый байт (0x8F) который содержит адрес считываемого регистра, сброшеный бит MS (так как мы читаем только один регистр) и установленый бит RW (ведь мы читаем данные)
- Передать любой байт. Ведь интерфейс SPI так устроен, что слейв (наш акселерометр) не может передавать данные по своей инициативе. Поэтому он начнет передавать значение хранящееся в регистре только в момент передачи микроконтроллером второго байта (который по сути будет просто проигнорирован).
- Прочитать значение из регистра данных SPI (то, что пришло от акселерометра)
- Установить высокий логический уровень на выводе CS
Запись одного регистра
- Вывод CS прижать к земле
- Передать первый байт (0x20) который содержит адрес записываемого регистра, сброшеный бит MS (так как мы пишем только один регистр) и сброшенный бит RW (ведь мы пишем данные)
- Передать новое значение регистра (0x47)
- Установить высокий логический уровень на выводе CS
Если вдруг потребуется быстро писать какие-либо значения в один и тот же регистр, то можно постоянно повторять 3-й шаг.
Чтение нескольких регистров
Очень удобная вещь, можно взять и за один раз прочитать всю память акселерометра :) Для того чтоб начать чтение, указываем адрес с которого нужно начать (например 0x0F) . А потом просто читаем байт за байтом. Алгоритм такой:
- Вывод CS прижать к земле.
- Передать первый байт (0xCF) который содержит адрес считываемого регистра, установленный бит MS (так как мы читаем несколько регистров) и установленый бит RW (ведь мы читаем данные)
- Передать любой байт. Ведь интерфейс SPI так устроен, что слейв (наш акселерометр) не может передавать данные по своей инициативе. Поэтому он начнет передавать значение хранящееся в регистре только в момент передачи микроконтроллером второго байта (который по сути будет просто проигнорирован).
- Прочитать значение из регистра данных SPI (то, что пришло от акселерометра)
- Повторять шаги 3 и 4 пока не прочитаем столько регистров сколько нужно. Адрес чтения будет увеличиваться на единицу сам.
- Установить высокий логический уровень на выводе CS
Запись нескольких регистров
Работает аналогично чтению нескольких регистров т.е. после каждой записи адрес автоматически увеличивается на единицу. Запишем три байта 0x47, 0x10 и 0x40 в регистры с адресами 0x20, 0x21 и 0x22:
- Вывод CS прижать к земле
- Передать первый байт (0x60) который содержит адрес первого записываемого регистра (0x20), установленый бит MS (так как мы пишем в несколько регистров) и сброшенный бит RW (ведь мы пишем данные)
- Передать новое значение (0x47) регистра 0x20
- Передать новое значение (0x10) регистра 0x21
- Передать новое значение (0x40) регистра 0x22
- Установить высокий логический уровень на выводе CS
Осталось только разобраться, какие регистры есть у акселерометра и для чего они нужны.
Who_Am_I (0x0F)
Регистр, содержащий идентификатор акселерометра LIS302DL. Значение можно только прочитать, читаться будет 0x3b. Ничем другим регистр не примечателен
CTRL_REG1 (0x20)
DR (0) |
PD (0) |
FS (0) |
STP (0) |
STM (0) |
Zen (1) |
Yen (1) |
Xen (1) |
STP
|
STM
|
Назначение
|
0
|
0
|
Самодиагностика выключена
|
0
|
1
|
Самодиагностика. Режим P
|
1
|
0
|
Самодиагностика. Режим M
|
1
|
1
|
Запрещенная комбинация
|
CTRL_REG2 (0x21)
SIM (0) |
BOOT (0) |
-- |
FDS (0) |
HP_FF_W U2(0) |
HP_FF_W U1(0) |
HP_coeff2(0) |
HP_coeff1(0) |
HP_coeff2 |
HP_coeff1 |
Частота среза (Бит DR=0) |
Частота среза (Бит DR=1) |
0 |
0 |
2 Гц |
8 Гц |
0 |
1 |
1 Гц |
4 Гц |
1 |
0 |
0.5 Гц |
2 Гц |
1 |
1 |
0.25 Гц |
1 Гц |
CTRL_REG3 (0x21)
IHL(0) |
PP_OD(0) |
I2CFG2(0) |
I2CFG1(0) |
I2CFG0(0) |
I1CFG2(0) |
I1CFG1(0) |
I1CFG0(0) |
I1(2)_CFG2 |
I1(2)_CFG1 |
I1(2)_CFG0 |
Источник |
0 |
0 |
0 |
Прерывание выкл. Нога подключена к земле. |
0 |
0 |
1 |
FF_WU_1 |
0 |
1 |
0 |
FF_WU_2 |
0 |
1 |
1 |
FF_WU_1 или FF_WU_2 |
1 |
0 |
0 |
Данные готовы |
1 |
1 |
1 |
Прерывание от клика |
FF_WU_1 и FF_WU_2 это два независимых блока предназначенных для генерации прерываний в случае если ускорение по одной или нескольким осям, выйдет за заданный порог.
HP_FILTER_RESET (0x23)
STATUS_REG (0x27)
ZXYOR (0) |
ZOR (0) |
YOR (0) |
XOR (0) |
ZYXDA (0) |
ZDA (0) |
YDA(0) |
ZDA (0) |
OUT_X (0x29)
Восьмибитный регистр данных, в который записывается ускорение по оси X
OUT_Y (0x2b)
Восьмибитный регистр данных, в который записывается ускорение по оси Y
OUT_Z (0x2d)
Восьмибитный регистр данных, в который записывается ускорение по оси Z
Теперь начинается самое интересное. У акселерометра есть два независимых модуля, которые умеют самостоятельно читать данные из регистров Out_X, Out_Y, Out_Z и на их основе генерировать прерывания которые будут возникать при определённых условиях. Для настройки каждого модуля используются 4 регистра которые будут описаны ниже. Кстати для удобства отладки можно прицепить к ногам INT1/INT2 по светодиоду через резистор. Тогда если прерывание случится это будет сразу видно.
FF_WU_CFG_1(0x30), FF_WU_CFG_2(0x34)
AOI (0) |
LIR (0) |
ZHIE (0) |
ZLIE (0) |
YHIE (0) |
YLIE (0) |
XHIE (0) |
XLIE (0) |
FF_WU_SRC_1(0x31), FF_WU_SRC_2 (0x35)
---- |
IA (0) |
ZH (0) |
ZL (0) |
YH (0) |
YL (0) |
XH (0) |
XL(0) |
FF_WU_THS_1(0x32), FF_WU_THS_2 (0x36)
D7 (0) |
D6(0) |
D5(0) |
D4(0) |
D3(0) |
D2 (0) |
D1 (0) |
D0(0) |
FF_WU_DURATION_1(0x33), FF_WU_DURATION_1(0x37)
Задаёт минимальную продолжительность события. Если частота выборки 400 Гц, то регистр позволяет задавать значение от 0 до 637.5 мсек с шагом 2.5 мсек. В случае если частота выборки 100 Гц, то значение может изменяться в диапазоне от 0 до 2.55 сек с шагом 10 мсек. Назначение этого регистра можно понять из картинки ниже:
В какой-то момент времени случается событие (FreeFall event) которое должно вызвать прерывание (например ускорение по оси X стало больше порогового). Сразу после этого, где-то внутри акселерометра начинает тикать счётчик. Когда его значение (counter value) сравняется со значением регистра FF_WU_DURATION_1(2) то прерывание возникнет и на ноге INT1(2) сменится логический уровень. Однако может быть так, что счётчик досчитает до какого-то значения, а потом событие вызывающее прерывание исчезнет (ускорение упало ниже порогового). Тут есть два варианта развития событий: Если бит DCRM сброшен, то счётчик сразу же сбросится в ноль и ему придётся начать отсчёт заново, когда ускорение превысит порог. Если же бит был установлен, то счётчик начнет уменьшать свое значение с той же скоростью с какой он его увеличивал. Я так понял что это защита от ложных срабатываний. Если регистр FF_WU_DURATION_1(2) содержит ноль, то прерывание наступит моментально как только произойдет нужное событие. Если же не ноль, то прерывание сработает не сразу, а спустя некоторый промежуток времени в течении которого событие вызвавшее прерывание должно сохраняться.
Все остальные регистры предназначены для настройки блока генерирующего прерывание всякий раз, когда совершают клик или двойной клик. Клик - это событе в ходе которого ускорение по некоторой оси превышает определённый порог (CLICK_THS) и опускается ниже этого порога раньше чем пройдет заданный промежуток времени (CLICK_TIMELIMIT):
На первом графике видно, как ускорение плавно нарастает и превышает пороговый уровень и потом так же плавно снижается. При этом время отвденное на клик не успевает закончиться. В момент когда ускорение упало ниже порога, срабатывает прерывание. На втором график изображен неудавшийся клик. Ускорение слишком долго падало, а когда наконец упало ниже порога, то было уже поздно. Временной интервал отведенный для клика истёк и прерывание не возникло. Ну а двойной клик это два обычных клика, причем второй клик должен быть совершен пока не прошло определённое время (CLICK_WINDOW) с момента первого клика:
На картинке выше показано в какие моменты будет возникать прерывание по двойному (4) и одиночному (3) клику. Для обоих случаев ускорение изменяется одинаково (красный график). На обоих графиках отмечен такой паметр как Latency (CLICK_LATENCY). Он задает врменной интервал в течении которого не будут детектироваться клики и двойные клики. В случае если у нас настроены прерывание по двойному клику, то отсчет времени начинается сразу после совершения второго клика. Ну а для одиночного клика - сразу после его совершения. Примечательно, что если у нас сброшен бит LIR (регистр CLICK_CFG), то активный уровень на ноге INT1(2) будет сохранятся в течении этого временного интервала, а потом сам сбросится.
CLICK_CFG (0x38)
--- |
LIR(0) |
Double_Z(0) |
Single_Z(0) |
Double_Y(0) |
Single_Y(0) |
Double_X(0) |
Single_X(0) |
CLICK_SRC (0x39)
Флаговый регистр
--- |
IA (0) |
Double_Z(0) |
Single_Z(0) |
Double_Y(0) |
Single_Y(0) |
Double_X(0) |
Single_X(0) |
CLICK_THSY_X (0x3B)
Регистр задает пороговые значения ускорения для осей X и Y. Если ускорение превысит порог, то акселерометр активирует режим обнаружения кликов. Другими словами: Если настроить определенный порог, и делать клики в процессе которых ускорение не превысит пороговое значение - акселерометр не посчитает что клик был сделан. По идее если нам не нужно отслеживать появление кликов по некоторым осям, то пороги для них задавать нет смысла. Но на практике я столкнулся со странным поведением когда включил обнаружение кликов по оси Z и не задал пороги для X и Y. Прерывания возникали через раз или вообще переставали срабатывать. Возможно это какая-то бага в самом датчике или я что-то не правильно понял.
THSy3 (0) |
THSy2(0) |
THSy1 (0) |
THSy0 (0) |
THSx3 (0) |
THSx2 (0) |
THSx1(0) |
THSx0(0) |
CLICK_THSZ (0x3C)
--- |
--- |
--- |
--- |
THSz3(0) |
THSz2(0) |
THSz1(0) |
THSz0(0) |
CLICK_TimeLimit (0x3D)
Регистр задающий временной интервал в течении которого ускорение должно успеть упасть ниже порогового чтоб клик был опознан. Отсчёт времени начинается после того как ускорение по выбранной оси превысило порог. Дивпазон от 0 до 127.5 мсек с шагом 0.5 мсек.
CLICK_Latency (0x3E)
Регистр задающий временной интервал в течении которого клики не будут детектироваться. Отсчет времени начинается после совершения двойного или одиночного клика. Диапазон от 0 до 255 мсек.
CLICK_Window (0x3F)
Практическое применение
Пример 3. Программа заставляет акселерометр обнаруживать двойной клик и выдавать прерывание. Скачать полную версию.
Если хотите почитать еще про этот акселерометр, то рекомнедую аппноуты от производителя: AN2335 и AN2579. Ну и даташит конечно же. В качестве заключения могу сказать что акселерометр мне понравился. Разобрался я сравнительно быстро, а вот статью эту писал аж две недели. Поэтому могут быть мелкие ошибки и неточности. Если вы нашли таковые, то пишите в комментарии, будем исправлять. Спасибо за внимание.