Управление lis302dl через spi
Цифровой акселерометр LIS302DL
В этой статье я постораюсь поделится опытом работы с цифровым акселерометром от ST Microelectronics.
Такой датчик позволяет получать данные о своем ускорении. В отличии от аналоговых датчиков, где для чтения нужен АЦП, здесь необходимо лишь настроить сенсор на нужный режим работы и считать готовую инфу. Чтение происходит посредством либо SPI, либо I2C интерфейса, на выбор. Получив данные, микроконтроллер может использовать их как угодно.
Мне доводилось работать с 2-мя датчиками семейства — lis202dl и lis302dl . Отличаются они только количеством осей, поэтому далее буду говорить о 3-х осевом.
Основные фичи:
Программируемый предел измерения. Можно выбрать 2 фиксированных значения: 2G/8G. Нужно выставлять в зависимости от применения. Если поставить 2G, то можно точнее измерять не очень большие ускорения и наоборот для 8G.
Внутренний программный фильтр. Мега полезная вещь. Меняет характер выдаваемых данных с относительного на абсолютный и наоборот. Поясню на примере: при выключенном фильтре сенсор будет постоянно «чувствовать» притяжение земли и если его ось Z будет направлена перпендикулярно земле, то по ней мы будем постоянно иметь максимальные значение ускорения. А если включить фильтр и оставить датчик в покое, то пока мы не приложим к нему ускорение, на всех осях будет 0.
2 программируемых выхода прерываний. Если не нужно постоянно получать точное значение ускорения, а лишь превышение какого-либо порога, то можно задать этот самый порог и ждать изменения на соответствующей ноге.
Основной и сильно заметный (для начинающего) минус датчика — размер. Он очень маленький. Корпус (LGA14) совершенно не паябельный, но это только на первый взгляд. Я паял его по методу уважаемого DI HALT’а из его статьи о аналоговом акселерометре MMA7260Q . Нарисовал footprint с направляющими уголками. Нанес припоя на дорожки, обильно смазал флюсом, отцентрировал по уголкам, и касаясь дорожек паяльником запаял датчик. Если кто-то захочет повторить, то footprint’ы для Sprint Layout 5 и DipTrace можно найти в конце статьи.
Если же вы не уверены в твердости рук или просто не хотите парится, то можно найти готовые платки переходники, где все уже запаяно до нас. Также есть отладочные платы и от самих ST в том числе.
Схемы подключения на примере Tiny2313:
I2C — не забываем подтяжку обоих линий до VCC. Чтобы чип понял что мы хотим пообщаться с ним по 2-м проводам, нужно седьмую ногу (CS) подтянуть к VCC. Нога SDO в этом режиме служит для задания I2C адреса датчика: если её притянуть к VCC, то датчик отзовется на адрес 0b0011101, если к земле (как в примере) — на 0b0011100.
SPI — вывод CS (Chip Select) можно подключать к любой ноге МК, аппаратный интерфейс его не контролирует и приходится дергать его программно.
На обоих схемах подключены выводы прерываний (INT1, INT2 — восьмая и девятая ноги). Это необязательно, их нужно подключать по необходимости.
Датчик питается напряжением от 2,16 до 3,6 В. Если у МК тоже низкое напряжения питания (в моем случае он с индексом «V», то есть с пониженным VCC), то оптимально питать их от одного источника, например в 3,3 В. Если же у МК VCC = 5В, то варианта 2: либо согласовывать I/O линии по уровням, либо забить и не согласовывать. В одном из девайсов я так и поступил, все работало, но так делать не стоит. Я вас предупредил:)
Что касается программы, то используются аппаратная реализация интерфейса (USI). В прилагаемом архиве лежит проект на СИ для AVR STUDIO. МК — Tiny2313. Подключение к датчику по SPI. Программа читает регистр «WHO I AM» и шлет данные о ускорении по оси X через USART.
STM Урок 41. Подключаем акселерометр LIS3DSH. Часть 1
Урок 41
Подключаем акселерометр LIS3DSH
Тема нашего сегодняшнего занятия – подключение более нового акселерометра по сравнению с тем, который мы использовали для изучения в уроке 39. Данный акселерометр – это также акселерометр, выполненный с использованием технологии MEMS – LIS3DSH .
Во-первых, данный акселерометр наряду с интерфейсом I2C может подключиться и с использованием интерфейса SPI, что делает более надёжной передачу данных и их использование. Во-вторых, данный акселерометр установлен на плате STM32F4 Discovery, с которой мы уже очень продолжительное время работаем, и она уже стала как родная. И это я считаю немаловажной мотивацией. Ну а в-третьих, имеет более совершенные технические характеристики:
Диапазон показаний ±2g/±4g/±6g/±8g/±16g;
Чувствительность 0.06-0.73 mg/digit;
Отклонение от нуля ±60 mg.
Ну а с остальными характеристиками, тонкостями, регистрами и другими подводными камнями акселерометра мы познакомимся в ходе его программирования.
Проект для Cube MX мы создадим из одного из прошлых проектов USB_OTG_CDC, так как вместо USART для передачи показаний акселерометра на ПК мы попробуем воспользоваться USB CDC Device, так как с ним работать более удобно ввиду необязательности использования каких-то промежуточных переходников. Назовем проект ACCEL407.
Запустим проект в Cube MX, отключим I2C, включим SPI1 в режим Full-Duplex Master
Ножки SPI оставим по умолчанию и никуда не перенаправляем
Включим еще 3 ножки. Одну для выбора чипа (CS), другие две для обнаружения прерываний. Возможно последние нам не понадобятся, но для порядка включим, чтобы по ошибки их не задействовать впоследствии на что-то еще.
Также включим на выход лапки портов для управления разноцветными светодиодами на плате
В настройках SPI изменим только скорость передачи
Также изменим настройки в GPIO у лапки порта PE3, выставив ему скорость Medium.
Зайдем в Project -> Settings и изменим там значения стека и кучи, чтобы USB-устройство после установки драйвера нам не давало ошибку (Код 10).
Сгенерируем проект и откроем его в Keil 5. Также по традиции настроим программатор на авторезет. Соберем проект.
Уберем всё, что касается символьного дисплея, так как он нам, скорее всего не потребуется. Уберем подключение библиотеки из main.h:
Здесь оставим только передачу в USB, чтобы проверить, работает ли она.
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
В бесконечном цикле также оставим только то, что касается передачи в USB
/* USER CODE END WHILE */
Прошьем контроллер, чтобы убедиться в работоспособности порта USB и удалим теперь всю передачу, массив строки и подготовку строки:
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* USER CODE END WHILE */
/* USER CODE END 1 */
Также из файла usbd_cdc_if.c удалим следующие строки, а одну строку раскомментируем обратно:
/* USER CODE BEGIN PRIVATE_VARIABLES */
extern char str_rx[21];
/* USER CODE END PRIVATE_VARIABLES */
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
/* USER CODE BEGIN 6 */
/* USER CODE END 6 */
Пересоберём на всякий случай проект, чтобы убедиться, что ошибок у нас нет.
Два файла для работы с акселерометром можно использовать с прошлого занятия, переименовав их подобающим образом, подключив к проекту и внеся некоторые изменения.
Названия, соответственно, изменятся на lis3dsh.h и lis3dsh.c. Заодно удалим из Inc и Src файлы lcd.h и lcd.c. Подключим данные файлы, добавив lis3dsh.c в проекте в группу Application/User, а lis3dsh.h подключив в main.h. Также внесем макроподстановки, наподобие тех, которые мы вносили в проекте под 3 discovery для удобного управления светодиодами.
#define LD_PORT GPIOD
#define LD3 GPIO_PIN_13 //ORANGE
#define LD4 GPIO_PIN_12 //GREEN
#define LD5 GPIO_PIN_14 //RED
#define LD6 GPIO_PIN_15 //BLUE
#define LD3_ON HAL_GPIO_WritePin(LD_PORT, LD3, GPIO_PIN_SET) //ORANGE
#define LD4_ON HAL_GPIO_WritePin(LD_PORT, LD4, GPIO_PIN_SET) //GREEN
#define LD5_ON HAL_GPIO_WritePin(LD_PORT, LD5, GPIO_PIN_SET) //RED
#define LD6_ON HAL_GPIO_WritePin(LD_PORT, LD6, GPIO_PIN_SET) //BLUE
#define LD3_OFF HAL_GPIO_WritePin(LD_PORT, LD3, GPIO_PIN_RESET) //ORANGE
#define LD4_OFF HAL_GPIO_WritePin(LD_PORT, LD4, GPIO_PIN_RESET) //GREEN
#define LD5_OFF HAL_GPIO_WritePin(LD_PORT, LD5, GPIO_PIN_RESET) //RED
#define LD6_OFF HAL_GPIO_WritePin(LD_PORT, LD6, GPIO_PIN_RESET) //BLUE
Состав данных файлов после внесения изменений.
Содержимое файла lis3dsh.h:
#define LD_PORT GPIOD
#define LD3 GPIO_PIN_13 //ORANGE
#define LD4 GPIO_PIN_12 //GREEN
#define LD5 GPIO_PIN_14 //RED
#define LD6 GPIO_PIN_15 //BLUE
#define LD3_ON HAL_GPIO_WritePin(LD_PORT, LD3, GPIO_PIN_SET) //ORANGE
#define LD4_ON HAL_GPIO_WritePin(LD_PORT, LD4, GPIO_PIN_SET) //GREEN
#define LD5_ON HAL_GPIO_WritePin(LD_PORT, LD5, GPIO_PIN_SET) //RED
#define LD6_ON HAL_GPIO_WritePin(LD_PORT, LD6, GPIO_PIN_SET) //BLUE
#define LD3_OFF HAL_GPIO_WritePin(LD_PORT, LD3, GPIO_PIN_RESET) //ORANGE
#define LD4_OFF HAL_GPIO_WritePin(LD_PORT, LD4, GPIO_PIN_RESET) //GREEN
#define LD5_OFF HAL_GPIO_WritePin(LD_PORT, LD5, GPIO_PIN_RESET) //RED
#define LD6_OFF HAL_GPIO_WritePin(LD_PORT, LD6, GPIO_PIN_RESET) //BLUE
Содержимое файла lis3dsh.c:
static void Error (void)
uint8_t Accel_IO_Read(uint16_t DeviceAddr, uint8_t RegisterAddr)
void Accel_IO_Write(uint16_t DeviceAddr, uint8_t RegisterAddr, uint8_t Value)
uint8_t ctrl = 0x00;
void Accel_AccFilterConfig(uint8_t FilterStruct)
void AccInit(uint16_t InitStruct)
void Accel_GetXYZ(int16_t* pData)
Вызовем инициализацию датчика в main();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
В функции-обработчике ошибки включим красный светодиод
static void Error (void)
Добавим хэндл нашего SPI:
extern SPI_HandleTypeDef hspi1;
В следующей части занятия мы мы попытаемся считать идентификатор данных, тем самым убедимся в правильных настройках шины и то, что мы работаем именно с этим датчиком.
Также напишем функцию записи данных в регистры акселерометра.
Миландр
Текущее время: 2021-июл-30 01:23 |
Часовой пояс: UTC+03:00
вопрос по SPI
здравствуйте Уважаемые разработчики
столкнулся с непонятной проблемой при использовании связки из контроллеров 1986ВЕ1Т и 1986ВЕ9х связанных по SPI
помимо этого используется DMA и УАПП на 1986ВЕ1Т все передачи осуществляются через ДМА
причем если мы берем 2 контроллера 1986ВЕ9х, то у нас нет никаких проблем с передачей,
а при использовании разных контроллеров после определенного количества 16битных слов сообщение выдаваемое 1986ВЕ1Т начинает искажаться следующим образом:
последнее слово в посылке перемещается на 1 место в посылку остальное выдается по порядку;
для подтверждения прикрепляю пример, в примере использовались: 1986ВЕ1Т в керамическом корпусе ревизия 3, 1986ВЕ9х ревизия 3 корпус пластиковый,
а также картинки с осциллографа и съёмка памяти кейла.
итак система должна работать следующим образом: с 1986ВЕ9х приходит чипселект и клок на 1986ВЕ1Т, а 1986ВЕ1Т работает 4 каналами ДМА (2 выдает 2 принимает) и выдает не то что требуется и не то что записано у него в памяти, причем до 4 байт он выдает нормально, а после начинаются глюки;
такое ощущение что где-то не успевает до передать и отправляет следующей посылкой.
в общем что я делаю не так или какие есть рекомендации по исправлению этого глюка?
да точно. на снимках: желтая и синяя линии выдачи 1986ВЕ1Т(ssp1, ssp2), красная клок, зеленая чипселект
rubygoblin писал(а): |
здравствуйте Уважаемые разработчики
столкнулся с непонятной проблемой при использовании связки из контроллеров 1986ВЕ1Т и 1986ВЕ9х связанных по SPI
помимо этого используется DMA и УАПП на 1986ВЕ1Т все передачи осуществляются через ДМА
причем если мы берем 2 контроллера 1986ВЕ9х, то у нас нет никаких проблем с передачей,
а при использовании разных контроллеров после определенного количества 16битных слов сообщение выдаваемое 1986ВЕ1Т начинает искажаться следующим образом:
последнее слово в посылке перемещается на 1 место в посылку остальное выдается по порядку;
для подтверждения прикрепляю пример, в примере использовались: 1986ВЕ1Т в керамическом корпусе ревизия 3, 1986ВЕ9х ревизия 3 корпус пластиковый,
а также картинки с осциллографа и съёмка памяти кейла.
итак система должна работать следующим образом: с 1986ВЕ9х приходит чипселект и клок на 1986ВЕ1Т, а 1986ВЕ1Т работает 4 каналами ДМА (2 выдает 2 принимает) и выдает не то что требуется и не то что записано у него в памяти, причем до 4 байт он выдает нормально, а после начинаются глюки;
такое ощущение что где-то не успевает до передать и отправляет следующей посылкой.
в общем что я делаю не так или какие есть рекомендации по исправлению этого глюка?
да точно. на снимках: желтая и синяя линии выдачи 1986ВЕ1Т(ssp1, ssp2), красная клок, зеленая чипселект
ну . это не совсем решение проблемы, да и второй в конечной системе не требуется, в основном 1986ВЕ1Т и идет на замену 1986ВЕ9х из-за наличия аппаратного ARINC
поэтому используются разные МК.
может дело в том, что они не могут быть состыкованы по SPI?
или у них связка ДМА-SPI работают по разному?
если так то по каким правилам идет перетасовка в сообщении и на какой длине сообщения она начинается?
и сколько таких «переломов» существует, по которым меняются правила выдачи сообщений?
второго к сожалению у меня нету, но я думаю что проблема сохранится потому как с 1986ВЕ9х на 1986ВЕ1Т идет 2 провода: это чипселект и клок больше они ничем, кроме питания, не связаны; выдает в воздух и принимает воздух, ну с приёмом кстати тоже глюки если, но я их пока не отслеживал, сначала с выдачей разберусь, потому как принимаю «эхо»
rubygoblin писал(а): |
rubygoblin писал(а): |
Последний раз редактировалось rubygoblin 2015-янв-28 14:31, всего редактировалось 1 раз.
rubygoblin писал(а): |
менял это значение зависит от SIZE_SPI1_AND_SPI2 изменяя это значение изменяется SIZE_DMA_SPI1_TX_MASSIVE поэтому точно менял. пробовал передавать 4 и 8 байтов в различных режимах 8, 16, 32 ошибка в первых двух случаях последний выдаваемый байт переезжает на первое место а в третьем сообщение набивается в начале и в конце недостоверными данными
ну тогда еще можно попробовать выровнять посылку до 4 и отключить единичные передачи так? не помогло.
Последний раз редактировалось rubygoblin 2015-янв-28 15:04, всего редактировалось 1 раз.
rubygoblin писал(а): |
менял это значение зависит от SIZE_SPI1_AND_SPI2 изменяя это значение изменяется SIZE_DMA_SPI1_TX_MASSIVE поэтому точно менял. пробовал передавать 4 и 8 байтов в различных режимах 8, 16, 32 ошибка в первых двух случаях последний выдаваемый байт переезжает на первое место а в третьем сообщение набивается в начале и в конце недостоверными данными
ну тогда еще можно попробовать выровнять посылку до 4 и отключить единичные передачи так?
вот еще такая штука. я когда 1 раз столкнулся с этой проблемой подумал, тактовой частоты недостаточно для выдачи и 1 слово остается в фифо и при следующей передаче выдается как 1 . в связи с этим добавил тактовой частоты на 1 слово итог слово перестало переноситься в начало и выдавалось в положенном месте, но все посылка сдвигалась на 1 слово в право и 1 словом становилось всегда 1 и тоже слово или 0x5555 или 0xAAAA это позволяло с учетом сдвинутого слова передавать 24 байта на 32 байтах посылка теряла стабильность и начинала постоянно переключаться добавление тактовой частоты ни к чему не приводило больше
и я так понял Вы считаете что проблема в ДМА? ну последние адреса я просчитывал и писал на них определенные значения. размер массива тоже считал в памяти запись значений по этим адресам тоже говорила что все правильно ну и количество передач учтено в макросе (итоговое значение = значение — 1) адреса источников и назначений в виртуальных структурах совпадает с тем что записано в программе. очень интересно получается. 9х и 1Т имеют одинаковое описание на ДМА(ну кроме того что 1Тработает с областью 0x20100000) на 9х все стабильно передается и принимается, а на 1Т возникают необъяснимые ошибки. непонятно.
STM32F4Discovery + Modbus RTU/ASCII + MEMS LIS302DL
1. Предварительная подготовка
В этой заметке предполагается, что читатель в качестве среды разработки использует Eclipse. Подробнее можно узнать по ссылке.
После первичной подготовки проекта и, в частности, развёртывания библиотеки Standard Peripherals Library, необходимо скачать и установить исходные тексты библиотеки FreeMODBUS. Несмотря на то, что в списках для загрузки фигурируют версии библиотеки вплоть до 1.5, на главной странице проекта в правом верхнем углу последнее упоминание касается библиотеки версии 1.3, поэтому именно эту версию и возьмём за основу для портирования.
После загрузки необходимо распаковать библиотеку в подкаталог проекта lib. Портирование протокола Modbus TCP выходит за рамки данной заметки, поэтому можно сразу же удалить подкаталог lib/freemodbus/modbus/tcp. За основу-шаблон для портирования возьмём AVR-порт из каталога demo/AVR/port, который скопируем в lib/freemodbus. Итоговая структура проекта в Eclipse принимает вид:
Добавим пути до всех имеющих отношение к проекту заголовочных файлов Project -> Properties -> C/C++ General -> Paths and Symbols -> вкладка Includes:
Аналогичным образом на вкладке Source Location пропишем в проекте файлы с исходными текстами:
2. Портирование
2.1. Настройка mbconfig.h
В файле lib/freemodbus/modbus/include/mbconfig.h находим строки:
и изменяем их на:
2.2. Изменения в port.h
Открываем lib/freemodbus/port/port.h и в разделе «Platform includes» добавляем:
Далее находим строчки с макроопределениями ENTER_CRITICAL_SECTION и EXIT_CRITICAL_SECTION и определяем их следующим образом:
2.3. Изменения в porttimer.c
Для измерения различных временных промежутков в рамках работы Modbus протокола, не нарушая общности, условимся использовать Таймер 2. Его необходимо настроить таким образом, чтобы он формировал прерывания с периодом, равным 50 мкс * N, где N — целое положительное число, указанное пользователем.
Шаг 1. Открываем файл lib/freemodbus/port/porttimer.c
Шаг 2. В разделе «Platform includes» открытого файла добавляем:
Шаг 3. Переопределяем функцию xMBPortTimersInit:
Шаг 4. Переопределяем функцию vMBPortTimersEnable:
Шаг 5. Переопределяем функцию vMBPortTimersDisable:
Шаг 6. Добавляем обработчик прерываний Таймера 2:
2.4. Изменения в portserial.c
В качестве приёмо-передающего устройства, опять же не нарушая общности, выберем USART2.
Шаг 1. Открываем файл lib/freemodbus/port/portserial.c
Шаг 2. Переопределяем функцию xMBPortSerialInit, отвечающей за инициализацию USART2:
Шаг 3. Переопределение функции vMBPortSerialEnable, отвечающей за разрешение/запрет прерываний от модуля USART2:
Шаг 4. Переопределение обработчика прерываний USART2:
Шаг 5. Переопределение функции xMBPortSerialPutByte, предназначенной для отправки 1 байта данных в USART2:
Шаг 6. Переопределение функции xMBPortSerialGetByte, предназначенной для приёма 1 байта данных из USART2:
3. Тестирование
3.1. Подготовка
Для запуска успешного обмена информацией через протокол Modbus RTU/ASCII нам необходимо переопределить следующие функции:
имеющие соответственно следующие назначения:
- чтение значений из нескольких регистров ввода (0x04 Read Input Registers)
- чтение значений из нескольких регистров хранения (0x03 Read Holding Registers)
- чтение значений из нескольких регистров флагов (0x01 Read Coil Status)
- чтение значений из нескольких дискретных входов (0x02 Read Discrete Inputs)
Переопределим функцию eMBRegHoldingCB таким образом, чтобы в зависимости от содержимого регистров хранения включался или выключался один из установленных на плате STM32F4Discovery светодиодов.
Шаг 1. Сделаем необходимые подключения в main.c:
Шаг 2. Библиотека FreeModbus не резервирует память под регистры ввода/хранения/флагов/дискретных входов, т.е. подразумевается ручное/самостоятельное управление ОЗУ. Поэтому выделим память для 4 регистров хранения, начиная с адреса 40001, в файле main.c следующим образом:
Шаг 3. Собственно реализация функции eMBRegHoldingCB:
Шаг 4. Для оставшихся функций добавим соответствующие заглушки:
Шаг 5. Установленные на плате STM32F4Discovery светодиоды подключены к ногам PD12, PD13, PD14 и PD15 микроконтроллера таким образом, что включаются при помощи лог.»1″, а выключаются при помощи лог.»0″. Функция инициализации ног I/O выглядит следующим образом:
Шаг 6. Добавим функцию, выполняющую привязку каждого из четырёх регистров хранения к каждому из четырёх установленных светодиодов:
Из кода видно, что каждый из светодиодов будет гореть только в том случае, если значение соответствующего регистра хранения будет отличным от нуля.
Шаг 7. Файл main.c приобретает следующий вид:
3.2. Запуск
В качестве ПО Modbus master выберем Modbus Poll, имеющий 30-дневный бесплатный тестовый период. Ключевым аргументом в пользу выбора данного ПО стала его одновременная поддержка Modbus/RTU и Modbus/ASCII. Чего, к сожалению, не скажешь об open-source решениях типа QModbus или QModMaster.
Настройки Modbus Poll включают:
Setup -> Read/Write Definition
Connection -> Connect -> кнопка Advanced
В итоге после начала сбора данных Вы должны увидеть окно, похожее на:
4. Пойдём ещё дальше
На отладочной плате STM32F4Discovery, как известно, установлен 3-осевой акселерометр LIS302DL. В составе STM32F4DISCOVERY board firmware package поставляется всё необходимое ПО для инициализации и обмена данными с акселерометром. В частности, нас интересуют файлы:
- stsw-stm32068/STM32F4-Discovery_FW_V1.1.0/Utilities/STM32F4-Discovery/stm32f4_discovery_lis302dl.h
- stsw-stm32068/STM32F4-Discovery_FW_V1.1.0/Utilities/STM32F4-Discovery/stm32f4_discovery_lis302dl.c
- stsw-stm32068/STM32F4-Discovery_FW_V1.1.0/Utilities/STM32F4-Discovery/stm32f4_discovery.h
Скопируем их во вновь созданный подкаталог проекта src/mems. Структура проекта после этого преобразуется к виду:
4.1. Добавление кода для MEMS
Шаг 1. Добавляем описание для четырёх регистров ввода протокола Modbus:
Шаг 2. Добавляем подключение в main.c:
Шаг 3. Добавляем объявление необходимых глобальных переменных в main.c:
Шаг 4. Добавляем функции, реализующие задержку и циклический опрос MEMS-акселерометра:
Шаг 5. Добавляем код инициализации акселерометра LIS302DL:
Шаг 6. Переопределяем метод eMBRegInputCB:
Шаг 7. Добавляем вызов lis302dl_init() в начале функции main, собираем проект и перепрошиваем плату.
4.2. Тестирование
После настроек Modbus Poll, аналогичных предыдущим, и запуска обмена данными окно программы должно стать похожим на:
Самодиагностика МЕМС акселерометра, гироскопа и компаса (self test)
Изучая спецификацию (datasheet) на МЕМС-датчик (акселерометр, гироскоп и проч.) мы сталкиваемся с такой процедурой, как самопроверка (self-test) или самодиагностика. Обычно в спецификациях есть описание, как это делать. Кому интересно: что это и как это правильно делать? — добро пожаловать под кат.
Введение
Самопроверка позволяет определить, что данные с датчика показывают то, что должны показывать. Датчики не всегда имели режим самодиагностики. Чтобы определить соответствие измеряемых данных тому, что указано в спецификации, разработчик должен был проверять это специальными экспериментами. В случае акселерометра — совершать наклоны на определённые углы и снимать изменение показаний. Анализируя результаты, можно было оценить погрешность, выдаваемую датчиком. Если показатели не соответствовали спецификации, датчик признавался бракованным.
Это усложняло массовое производство и во все датчики стали включать процедуру самодиагностики. Замысел процедуры достаточно прост — создать условия, при которых мы будем знать, что должен показывать датчик, и сравнить с тем, что он показывает. Отклонения выше заданных забраковывают датчик. Для компасов делают элемент, создающий магнитное поле (прямо в корпусе датчика). Включение режима самодиагностики включает этот элемент — эталонный источник. Для акселерометра и гироскопа элемент генерирует электростатическое поле, которое отклоняет грузик (см. фото ниже) и имитирует физическое движение или вращение. Величина смещения известна, соответственно известна величина, которую должен показать датчик. Если погрешность снятых показаний в заявленных пределах — значит с датчиком всё в порядке и он работает в соответствии со спецификацией.
(статья из интернет-журнала, откуда я позаимствовал картинку содержит ещё много красивых увеличенных фоток, ссылки см. внизу)
1. Влияние на самопроверку внешних сил
Рассмотрим акселерометр. Как указано ранее, в режиме самодиагностики на микромеханический грузик воздействует электростатическая сила, отклоняющая его на определённое расстояние, и имитирующая тем самым ускорение. Однако ожидаемую величину на выходе датчика мы не увидим. Дело в том, что в спокойном состоянии на грузик действуют другие силы (напр. сила тяжести). Они дополнительно отклоняют грузик.
Для того, чтобы учесть внешние силы, в процедуре самопроверки снимаются показания датчика в обычном режиме. Затем включается источник отклонения (переход в режим «самопроверка») и снова снимаются показания датчика.
Разница показаний этих двух режимов должна показывать величину с эталонного источника. Если величина находится в допустимом спецификацией диапазоне, то это значит, что датчик работает должным образом.
aвнутр. ист. = aST — aнорм
MIN 2 . Значит возможный разбег величины самопроверки = ±0,0784 м/с 2 . Здесь тоже нужно быть внимательным, т. к. иногда уровень шума указывают с включенным фильтром. Это значит, что замеры нужно делать с теми же настройками фильтра.
В одном положении получились такие результаты:
Затем плату с датчиком положил на бок и получил такие результаты:
Разбег значений получился 11, 2 и 5 g * 10 -3 . Если провести ещё несколько испытаний, то можно уточнить диапазон для конкретно этого датчика:
380 Пример кода самопроверки LIS302DL
В одном положении получились такие результаты:
В другом положении такие результаты:
Разбег значений получился в районе 1 единицы. А это второй режим работы внутреннего источника, когда он тянет в другую сторону (Self-test M):
Здесь мы видим показания другого датчика («Self-test P»):
И второй режим с поворотом платы («Self-test M»):
Согласно спецификации, он не проходит самопроверку — значения по оси Z выступают за заявленный предел: максимум 32, а мы имеем 35.
В одном положении получились такие результаты:
В другом положении:
В этом датчике успешность прохождения самопроверки определяется допустимым отклонением от эталонного значения. Допуск отклонения равен ±3% (стр. 9 первой части спецификации). Т. е. этот датчик в порядке.
Browse the source code of linux/drivers/misc/lis3lv02d/lis3lv02d_spi.c
Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /* |
---|---|
2 | * lis3lv02d_spi — SPI glue layer for lis3lv02d |
3 | * |
4 | * Copyright (c) 2009 Daniel Mack |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as |
8 | * publishhed by the Free Software Foundation. |
9 | */ |
10 | |
11 | #include |
12 | #include |
13 | #include |
14 | #include |
15 | #include |
16 | #include |
17 | #include |
18 | #include |
19 | #include |
20 | #include |
21 | #include |
22 | |
23 | #include «lis3lv02d.h» |
24 | |
25 | #define «lis3lv02d_spi» |
26 | #define 0x80 |
27 | |
28 | static int (struct lis3lv02d *, int , u8 *) |
29 | < |
30 | struct spi_device * = lis3->bus_priv; |
31 | int = spi_w8r8(spi, reg | LIS3_SPI_READ); |
32 | if (ret 0 ) |
33 | return -EINVAL; |
34 | |
35 | *v = (u8) ret; |
36 | return 0 ; |
37 | > |
38 | |
39 | static int (struct lis3lv02d *, int , u8 ) |
40 | < |
41 | u8 [ 2 ] = < reg, val >; |
42 | struct spi_device * = lis3->bus_priv; |
43 | return spi_write(spi, tmp, sizeof(tmp)); |
44 | > |
45 | |
46 | static int (struct lis3lv02d *) |
47 | < |
48 | u8 ; |
49 | int ; |
50 | |
51 | /* power up the device */ |
52 | ret = lis3->read(lis3, CTRL_REG1, ®); |
53 | if (ret 0 ) |
54 | return ret; |
55 | |
56 | reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; |
57 | return lis3->write(lis3, CTRL_REG1, reg); |
58 | > |
59 | |
60 | static union axis_conversion = |
61 | < .as_array = < 1 , 2 , 3 >>; |
62 | |
63 | # ifdef CONFIG_OF |
64 | static const struct of_device_id [] = < |
65 | < .compatible = "st,lis302dl-spi" >, |
66 | <> |
67 | >; |
68 | MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids); |
69 | # endif |
70 | |
71 | static int (struct spi_device *) |
72 | < |
73 | int ; |
74 | |
75 | spi->bits_per_word = 8 ; |
76 | spi->mode = SPI_MODE_0; |
77 | ret = spi_setup(spi); |
78 | if (ret 0 ) |
79 | return ret; |
80 | |
81 | lis3_dev.bus_priv = spi; |
82 | lis3_dev.init = lis3_spi_init; |
83 | lis3_dev.read = lis3_spi_read; |
84 | lis3_dev.write = lis3_spi_write; |
85 | lis3_dev.irq = spi->irq; |
86 | lis3_dev.ac = lis3lv02d_axis_normal; |
87 | lis3_dev.pdata = spi->dev.platform_data; |
88 | |
89 | # ifdef CONFIG_OF |
90 | if (of_match_device(lis302dl_spi_dt_ids, &spi->dev)) < |
91 | lis3_dev.of_node = spi->dev.of_node; |
92 | ret = lis3lv02d_init_dt(&lis3_dev); |
93 | if (ret) |
94 | return ret; |
95 | > |
96 | # endif |
97 | spi_set_drvdata(spi, &lis3_dev); |
98 | |
99 | return lis3lv02d_init_device(&lis3_dev); |
100 | > |
101 | |
102 | static int (struct spi_device *) |
103 | < |
104 | struct lis3lv02d * = spi_get_drvdata(spi); |
105 | lis3lv02d_joystick_disable(lis3); |
106 | lis3lv02d_poweroff(lis3); |
107 | |
108 | return lis3lv02d_remove_fs(&lis3_dev); |
109 | > |
110 | |
111 | # ifdef CONFIG_PM_SLEEP |
112 | static int (struct device *) |
113 | < |
114 | struct spi_device * = to_spi_device(dev); |
115 | struct lis3lv02d * = spi_get_drvdata(spi); |
116 | |
117 | if (!lis3->pdata || !lis3->pdata->wakeup_flags) |
118 | lis3lv02d_poweroff(&lis3_dev); |
119 | |
120 | return 0 ; |
121 | > |
122 | |
123 | static int (struct device *) |
124 | < |
125 | struct spi_device * = to_spi_device(dev); |
126 | struct lis3lv02d * = spi_get_drvdata(spi); |
127 | |
128 | if (!lis3->pdata || !lis3->pdata->wakeup_flags) |
129 | lis3lv02d_poweron(lis3); |
130 | |
131 | return 0 ; |
132 | > |
133 | # endif |
134 | |
135 | static SIMPLE_DEV_PM_OPS(, lis3lv02d_spi_suspend, |
136 | lis3lv02d_spi_resume); |
137 | |
138 | static struct spi_driver = < |
139 | .driver = < |
140 | .name = DRV_NAME, |
141 | .pm = &lis3lv02d_spi_pm, |
142 | .of_match_table = of_match_ptr(lis302dl_spi_dt_ids), |
143 | >, |
144 | .probe = lis302dl_spi_probe, |
145 | .remove = lis302dl_spi_remove, |
146 | >; |
147 | |
148 | module_spi_driver(lis302dl_spi_driver); |
149 | |
150 | MODULE_AUTHOR( «Daniel Mack » ); |
151 | MODULE_DESCRIPTION( «lis3lv02d SPI glue layer» ); |
152 | MODULE_LICENSE( «GPL» ); |
153 | MODULE_ALIAS( «spi:» DRV_NAME); |
154 |
Warning: That file was not part of the compilation database. It may have many parsing errors.
Generated on 2019-Mar-29 from project linux revision v5.1-rc2
Powered by Code Browser 2.1
Generator usage only permitted with license.