Урок 45
Часть 1
Подключаем гироскоп LSM6DS0
Сегодня мы ещё раз поработаем с датчиком, который в себе объединяет сразу два функционала – акселерометр и гироскоп – LSM6DS0. Выполнен он с использованием технологии MEMS. Установлен на плате расширения X-NUCLEO-IKS01A1, предназначенной для работы с отладочной платой Nucleo. Мы будем подключать данную оценочную плату к плате Nucleo STM32F401RE.
Данный акселерометр-гироскоп также наряду с интерфейсом I2C может подключиться и с использованием интерфейса SPI. Но мы будем использовать подключение именно по I2C, так как именно такое подключение имеет место в оценочной плате X-NUCLEO-IKS01A.
Также использовать в рамках данного занятия мы данный датчик будем только как гироскоп, так как в качестве акселерометра мы его уже подключали.
Гироскоп в данном датчике имеет следующие технические характеристики:
Диапазон показаний ±245/±500/±2000 dps;
Чувствительность 8.75 – 70 mdps/LSb ;
Отклонение от нуля ±30 dps при установке диапазона 2000 dps.
Частота измерений 14,9 – 952 Гц.
С некоторыми остальными показателями, регистрами, значениями и другими тонкостями гироскопа мы познакомимся в ходе его программирования.
Проект мы создадим из готового проекта, в котором мы работали с акселерометром данного датчика – из проекта Accel_LSM6DS0, только назовём мы данный проект теперь соответственно Gyro_LSM6DS0.
Запустим проект Cube MX. Изменим мы здесь только скорость USART.
Сгенерируем проект, откроем его. Настроим программатор на авторезет. Добавим файл lsm6ds0.c. Скомпилируем проект.
В бесконечном цикле пока закомментируем код вызова функции считывания данных и отправки их в USART
/* USER CODE BEGIN 3 */
//Accel_ReadAcc();
}
Для универсальности проекта, так как, возможно, позже мы объединим работу с акселерометром и гироскопом в один проект, переименуем функцию Accel_Ini в файле lsm6ds0.c на Accel_Gyro_Ini. То же самое проделаем с прототипом и с вызовом данной функции в main().
Закомментируем в функции Accel_Gyro_Ini вот эту строку
LD2_OFF;
// AccInit(ctrl);
LD2_ON;
Добавим функцию инициализации гироскопа по подобию функции инициализации акселерометра
//———————————————
void GyroInit(uint16_t InitStruct)
{
uint8_t value = 0;
}
//———————————————
Вызовем её в функции общей инициализации
// AccInit(ctrl);
GyroInit(ctrl);
LD2_ON;
В файле lsm6ds0.h заранее добавим несколько макросов, необходимых для работы с гироскопом. Код данного файла после всех изменений примет следующий вид:
#ifndef LIS3DSH_H_
#define LIS3DSH_H_
#include «stm32f4xx_hal.h»
#include <string.h>
//————————————————
#define ABS(x) (x < 0) ? (-x) : x
//————————————————
#define LD2_Pin GPIO_PIN_5
#define LD2_GPIO_Port GPIOA
#define LD2_ON HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET) //GREEN
#define LD2_OFF HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET)
//————————————————
#define LSM6DS0_ACC_GYRO_CTRL_REG5_XL 0X1F
#define LSM6DS0_ACC_GYRO_CTRL_REG6_XL 0X20
#define LSM6DS0_ACC_GYRO_CTRL_REG8 0X22
#define LSM6DS0_ACC_GYRO_CTRL_REG1_G 0X10
#define LSM6DS0_ACC_GYRO_CTRL_REG2_G 0X11
#define LSM6DS0_ACC_GYRO_CTRL_REG4 0X1E
//————————————————
#define LSM6DS0_ACC_GYRO_BDU_DISABLE 0x00
#define LSM6DS0_ACC_GYRO_BDU_ENABLE 0x40
#define LSM6DS0_ACC_GYRO_BDU_MASK 0x40
//————————————————
#define LSM6DS0_ACC_GYRO_ODR_XL_POWER_DOWN 0x00
#define LSM6DS0_ACC_GYRO_ODR_XL_10Hz 0x20
#define LSM6DS0_ACC_GYRO_ODR_XL_50Hz 0x40
#define LSM6DS0_ACC_GYRO_ODR_XL_119Hz 0x60
#define LSM6DS0_ACC_GYRO_ODR_XL_238Hz 0x80
#define LSM6DS0_ACC_GYRO_ODR_XL_476Hz 0xA0
#define LSM6DS0_ACC_GYRO_ODR_XL_952Hz 0xC0
#define LSM6DS0_ACC_GYRO_ODR_XL_MASK 0xE0
//————————————————
#define LSM6DS0_ACC_GYRO_ODR_G_POWER_DOWN 0x00
#define LSM6DS0_ACC_GYRO_ODR_G_15Hz 0x20
#define LSM6DS0_ACC_GYRO_ODR_G_60Hz 0x40
#define LSM6DS0_ACC_GYRO_ODR_G_119Hz 0x60
#define LSM6DS0_ACC_GYRO_ODR_G_238Hz 0x80
#define LSM6DS0_ACC_GYRO_ODR_G_476Hz 0xA0
#define LSM6DS0_ACC_GYRO_ODR_G_952Hz 0xC0
#define LSM6DS0_ACC_GYRO_ODR_G_MASK 0xE0
//————————————————
#define LSM6DS0_ACC_GYRO_FS_XL_2g 0x00
#define LSM6DS0_ACC_GYRO_FS_XL_16g 0x08
#define LSM6DS0_ACC_GYRO_FS_XL_4g 0x10
#define LSM6DS0_ACC_GYRO_FS_XL_8g 0x18
#define LSM6DS0_ACC_GYRO_FS_XL_MASK 0x18
//————————————————
#define LSM6DS0_ACC_GYRO_FS_G_245dps 0x00
#define LSM6DS0_ACC_GYRO_FS_G_500dps 0x08
#define LSM6DS0_ACC_GYRO_FS_G_1000dps 0x10
#define LSM6DS0_ACC_GYRO_FS_G_2000dps 0x18
#define LSM6DS0_ACC_GYRO_FS_G_MASK 0x18
//————————————————
#define LSM6DS0_ACC_GYRO_OUT_SEL_BYPASS_HPF_AND_LPF2 0x00
#define LSM6DS0_ACC_GYRO_OUT_SEL_BYPASS_LPF2 0x01
#define LSM6DS0_ACC_GYRO_OUT_SEL_USE_HPF_AND_LPF2 0x02
#define LSM6DS0_ACC_GYRO_OUT_SEL_MASK 0x03
//————————————————
#define LSM6DS0_ACC_GYRO_BW_G_LOW 0x00
#define LSM6DS0_ACC_GYRO_BW_G_NORMAL 0x01
#define LSM6DS0_ACC_GYRO_BW_G_HIGH 0x02
#define LSM6DS0_ACC_GYRO_BW_G_ULTRA_HIGH 0x03
#define LSM6DS0_ACC_GYRO_BW_G_MASK 0x03
//————————————————
#define LSM6DS0_ACC_GYRO_XEN_XL_ENABLE 0x08
#define LSM6DS0_ACC_GYRO_YEN_XL_ENABLE 0x10
#define LSM6DS0_ACC_GYRO_ZEN_XL_ENABLE 0x20
#define LSM6DS0_ACC_GYRO_XEN_XL_MASK 0x08
#define LSM6DS0_ACC_GYRO_YEN_XL_MASK 0x10
#define LSM6DS0_ACC_GYRO_ZEN_XL_MASK 0x20
//————————————————
#define LSM6DS0_ACC_GYRO_XEN_G_DISABLE 0x00
#define LSM6DS0_ACC_GYRO_XEN_G_ENABLE 0x08
#define LSM6DS0_ACC_GYRO_YEN_G_DISABLE 0x00
#define LSM6DS0_ACC_GYRO_YEN_G_ENABLE 0x10
#define LSM6DS0_ACC_GYRO_ZEN_G_DISABLE 0x00
#define LSM6DS0_ACC_GYRO_ZEN_G_ENABLE 0x20
#define LSM6DS0_ACC_GYRO_XEN_G_MASK 0x08
#define LSM6DS0_ACC_GYRO_YEN_G_MASK 0x10
#define LSM6DS0_ACC_GYRO_ZEN_G_MASK 0x20
//————————————————
#define LSM6DS0_ACC_GYRO_OUT_X_L_XL 0X28
#define LSM6DS0_ACC_GYRO_OUT_X_H_XL 0X29
#define LSM6DS0_ACC_GYRO_OUT_Y_L_XL 0X2A
#define LSM6DS0_ACC_GYRO_OUT_Y_H_XL 0X2B
#define LSM6DS0_ACC_GYRO_OUT_Z_L_XL 0X2C
#define LSM6DS0_ACC_GYRO_OUT_Z_H_XL 0X2D
//————————————————
#define LSM6DS0_ACC_GYRO_OUT_X_L_G 0X18
#define LSM6DS0_ACC_GYRO_OUT_X_H_G 0X19
#define LSM6DS0_ACC_GYRO_OUT_Y_L_G 0X1A
#define LSM6DS0_ACC_GYRO_OUT_Y_H_G 0X1B
#define LSM6DS0_ACC_GYRO_OUT_Z_L_G 0X1C
#define LSM6DS0_ACC_GYRO_OUT_Z_H_G 0X1D
//————————————————
void Accel_Gyro_Ini(void);
void AccelGyro_Read(void);
//————————————————
#endif /* LIS3DSH_H_ */
Скопировав из другой функции, добавим в функцию GyroInit следующий код:
uint8_t value = 0;
//установим бит BDU
value = Accel_IO_Read(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG8);
value&=~LSM6DS0_ACC_GYRO_BDU_MASK;
value|=LSM6DS0_ACC_GYRO_BDU_ENABLE;
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG8,value);
Так как в коде ничего не изменилось, объяснение не требуется.
Теперь добавим следующий код сюда же:
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG8,value);
//пока выключим датчик (ODR_G = 000)
value = Accel_IO_Read(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G);
value&=~LSM6DS0_ACC_GYRO_ODR_G_MASK;
value|=LSM6DS0_ACC_GYRO_ODR_G_POWER_DOWN;
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G,value);
В данном коде мы работаем с регистром CTRL_REG1_G (адрес 0X10), с его битами, отвечающим за частоту снятия данных именно с гироскопа
Пока мы здесь отключаем датчик (все биты выставляем в 0).
Дальше добавим следующий код
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G,value);
//Full scale selection 500 dps
value = Accel_IO_Read(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G);
value&=~LSM6DS0_ACC_GYRO_FS_G_MASK;
value|=LSM6DS0_ACC_GYRO_FS_G_500dps;
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G,value);
Дальше мы работаем с тем же регистром, только с другими битами, отвечающими за максимально измеряемую датчиком угловую скорость относительно оси. Думаю, нам достаточно 500 градусов в секунду, быстрее мы не разгонимся.
Пишем код дальше
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G,value);
//Включим оси
value = Accel_IO_Read(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG4);
value&=~(LSM6DS0_ACC_GYRO_XEN_G_MASK|\
LSM6DS0_ACC_GYRO_YEN_G_MASK|\
LSM6DS0_ACC_GYRO_ZEN_G_MASK);
value|=(LSM6DS0_ACC_GYRO_XEN_G_ENABLE|\
LSM6DS0_ACC_GYRO_YEN_G_ENABLE|\
LSM6DS0_ACC_GYRO_ZEN_G_ENABLE);
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG4,value);
Как и указано в комментарии, здесь мы включаем оси гироскопа. Включим мы все 3 оси
Продолжаем писать исходный код
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG4,value);
//Включим HPF и LPF2 (фильтрация)
value = Accel_IO_Read(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG2_G);
value&=~LSM6DS0_ACC_GYRO_OUT_SEL_MASK;
value|=LSM6DS0_ACC_GYRO_OUT_SEL_USE_HPF_AND_LPF2;
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG2_G,value);
В регистре 2 (адрес 0x11) включим бит OUT_SEL1, тем самым включив все фильтры
Дальше включим следующее:
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG2_G,value);
//Включим BW (пропускная способность). При 952 Гц настроек будет пропускная способность 100 Гц
value = Accel_IO_Read(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G);
value&=~LSM6DS0_ACC_GYRO_BW_G_MASK;
value|=LSM6DS0_ACC_GYRO_BW_G_ULTRA_HIGH;
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G,value);
Здесь мы в регистре 1 включили оба бита BW_G, тем самым задав такую пропускную способность, которая позволит всем фильтрам успеть сработать.
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG4,value);
//Включим Data Rate 952 Гц
value = Accel_IO_Read(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G);
value&=~LSM6DS0_ACC_GYRO_ODR_G_MASK;
value|=LSM6DS0_ACC_GYRO_ODR_G_952Hz;
Accel_IO_Write(0xD6,LSM6DS0_ACC_GYRO_CTRL_REG1_G,value);
В данном коде мы включим частоту опроса данных с осей 952 Гц. Реальная частота при использовании всех возможных фильтров и пропускной способности получится 100 Гц.
На этом Инициализацию можно считать завершенной.
Соберем код, прошьем контроллер и убедимся, что зеленый светодиод у нас светится.
В следующей части нашего занятия мы напишем функции, отвечающие за снятие данных с датчика и, отображая их в различных программах визуализации, увидим на деле работу гироскопа.
Предыдущий урок Программирование МК STM32 Следующая часть
Техническая документация на датчик
Техническая документация на плату расширения
Отладочную плату можно приобрести здесь Nucleo STM32F401RE
Оценочную плату можно приобрести здесь STM32 X-NUCLEO-IKS01A1
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Добавить комментарий