STM Урок 42. Подключаем акселерометр LSM6DS0. Часть 1



Урок 42

 

Часть 1

 

Подключаем акселерометр LSM6DS0

 

Сегодня мы рассмотрим датчик, который в себе объединяет сразу два функционала – акселерометр и гироскоп. Данный акселерометр – это также акселерометр, выполненный с использованием технологии MEMS – LSM6DS0. Установлен он на плате расширения X-NUCLEO-IKS01A1, предназначенной для работы с отладочной платой Nucleo. Мы будем подключать данную оценочную плату к плате Nucleo STM32F401RE.

Данный акселерометр-гироскоп также наряду с интерфейсом I2C может подключиться и с использованием интерфейса SPI. Но мы будем использовать подключение именно по I2C, так как именно такое подключение имеет место в оценочной плате X-NUCLEO-IKS01A.

Также использовать в рамках данного занятия мы данный датчик будем только как акселерометр. В качестве гироскопа мы его подключим на более поздних занятиях.

Акселерометр в данном датчике имеет следующие технические характеристики:

        Диапазон показаний ±2g/±4g/±8g/±16g;

Чувствительность 0.061 – 0.73 mg/digit;

Отклонение от нуля ±90 mg.

С некоторыми остальными показателями, регистрами, значениями и другими тонкостями акселерометра мы познакомимся в ходе его программирования.

Создадим проект для в Cube MX. Выбирать мы будем не контроллер, а отладочную плату (нажмите на картинку для увеличения изображения)

 

image00_0500

 

Отключим ножки портов PH0, PH1, PC14, PC15. USART 2 Включим в режим «Asynchronous» (нажмите на картинку для увеличения изображения)

 

image02_0500

 

Включим I2C1, только лапки SCL и SDA переопределим на лапки портов PB8 и PB9. Делается это нажатием левой кнопки мыши на ножку с зажатой клавишей Ctrl и дальнейшим переносом ее на возможную лапку (нажмите на картинку для увеличения изображения)

 

image01_0500

 

PA13 также отключим

 

image04

 

 

В Clock Configuration ничего не трогаем. В Configuration I2C настроим на 400 кГц, а USART на 256000 bps и включим на USART прерывания и DMA.

 

image03

image06

image05

image09

 image07

 

Настроим Project Settings, задав имя Acce_lLSM6DS0, среду программирования и путь к проекту. Путь у всех может быть разный.

 

image13

 

Сгенерируем проект, откроем его. Настроим программатор на авторезет. Скомпилируем проект.

Для дальнейшего составления проекта мы можем воспользоваться опытом занятий с предыдущими датчиками. Файлы библиотек функций датчика мы возьмем с Вами с 39 урока, когда мы подключали датчик, установленный на плате STM32F303 Discovery, так как там мы также пользовались шинами I2C и USART.

В папку Inc проекта мы скопируем файл main.h. Также в соответствующие папки Inc и Src мы скопируем файлы lsm303dlh.h lsm303dlh.c, соответственно переименовав их согласно наименованию нашего датчика в lsm6ds0.h и lsm6ds0.c.

Подключим файл main.h в файле main.c

 

/* USER CODE BEGIN Includes */

#include «main.h»

/* USER CODE END Includes */

 

Подключим также файл lsm6ds0.c

 

image10

 

 

Файл main.h после некоторых исправлений станет вот такого содержания:

 

#ifndef MAIN_H_

#define MAIN_H_

 

#include «stm32f4xx_hal.h»

#include «lsm6ds0.h»

 

#endif /* MAIN_H_ */

 

Также подправим заголовочный файл 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)

//————————————————

void Accel_Ini(void);

void Accel_ReadAcc(void);

//————————————————

#endif /* LIS3DSH_H_ */

 

Также подправим и уберем лишнее в файле lsm6ds0.c

 

#include «lsm6ds0.h»

//———————————————

extern I2C_HandleTypeDef hi2c1;

extern UART_HandleTypeDef huart2;

uint8_t buf2[14]={0};

char str1[30]={0};

//———————————————

void Error(void)

{

        LD2_OFF;

}

//———————————————

static uint8_t I2Cx_ReadData(uint16_t Addr, uint8_t Reg)

{

        HAL_StatusTypeDef status = HAL_OK;

        uint8_t value = 0;

        status = HAL_I2C_Mem_Read(&hi2c1, Addr, Reg, I2C_MEMADD_SIZE_8BIT, &value, 1, 0x10000);

        if(status != HAL_OK) Error();

        return value;

}

//———————————————

static void I2Cx_WriteData(uint16_t Addr, uint8_t Reg, uint8_t Value)

{

        HAL_StatusTypeDef status = HAL_OK;

        status = HAL_I2C_Mem_Write(&hi2c1, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, &Value, 1, 0x10000);

        if(status != HAL_OK) Error();

}

//———————————————

uint8_t Accel_IO_Read(uint16_t DeviceAddr, uint8_t RegisterAddr)

{

        return I2Cx_ReadData(DeviceAddr, RegisterAddr);

}

//———————————————

void Accel_IO_Write(uint16_t DeviceAddr, uint8_t RegisterAddr, uint8_t Value)

{

        I2Cx_WriteData(DeviceAddr, RegisterAddr, Value);

}

//———————————————

void Accel_GetXYZ(int16_t* pData)

{

        uint8_t buffer[6];

        uint8_t i=0;

 

        for(i=0;i<3;i++)

        {

                

        }

}

//———————————————

uint8_t Accel_ReadID(void)

{

        uint8_t ctrl = 0x00;

        return ctrl;

}

//———————————————

void Accel_ReadAcc(void)

{

        int16_t buffer[3] = {0};

        int16_t xval, yval, zval;

        Accel_GetXYZ(buffer);

        xval=buffer[0];

        yval=buffer[1];

        zval=buffer[2];

//        sprintf(str1,»X:%06d Y:%06d Z:%06d\r\n», xval, yval, zval);

//        HAL_UART_Transmit(&huart2, (uint8_t*)str1,strlen(str1),0x1000);

        buf2[0]=0x12;

        buf2[1]=0x10;

        buf2[2]=(uint8_t)(xval>>8);

        buf2[3]=(uint8_t)xval;

        buf2[4]=0x10;

        buf2[5]=0x10;

        buf2[6]=(uint8_t)(zval>>8);

        buf2[7]=(uint8_t)zval;

        buf2[8]=0x13;

        HAL_UART_Transmit(&huart2,buf2,9,0×1000);

        if(xval>1500)

        {

        }

        HAL_Delay(20);

}

//———————————————

void AccInit(uint16_t InitStruct)

{

}

//———————————————

void Accel_Ini(void)

{

        uint16_t ctrl = 0x0000;

        HAL_Delay(1000);

        AccInit(ctrl);

}

 

В функцию main добавим следующий код. Мы зажжем светодиод заранее, так как он у нас единственный на плате и вызовем функцию инициализации

 

  /* USER CODE BEGIN 2 */

         HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);

        Accel_Ini();

  /* USER CODE END 2 */

 

Начнем, как обычно с попытки считать идентификатор датчика. Обратимся к технической документации. Найдем сначала адрес, необходимый для обращения к устройству по I2C. Если ножка SDO у нас соединена с землей, то 1 бит у нас 0, если с питанием, то 1. 0-й бит у нас будет 0, т.к. мы обращаемся сначала в режиме записи, если потребуется режим воспроизведения, то библиотека HAL сама возьмет на себя заботу об установке его в 1. Остается только узнать, куда же у нас подключена ножка SDO. Схему мне найти не удалось. Осталось 2 варианта, либо пробовать разные адреса, либо взять его из примера. Я выбрал 2й путь как более простой. Поэтому у нас адрес будет последний из таблицы 15 даташита — 11010110 (D6h). Также нам необходимо знать из какого регистра брать идентификатор и какой он должен быть у нашего датчика, чтобы применить в условии операцию сравнения. Данная информация находится на странице 38.

 

image11

 

Поэтому добавим строку в функцию Accel_ReadID

 

uint8_t Accel_ReadID(void)

{

        uint8_t ctrl = 0x00;

        ctrl = Accel_IO_Read(0xD6,0x0F);

        return ctrl;

}

//———————————————

 

Также вставим строки в функцию инициализации датчика

 

        HAL_Delay(1000);

        if(Accel_ReadID()==0x68) LD2_ON;

        else Error();

        AccInit(ctrl);

 

Соберем код и прошьем контроллер и проверим тем самым, тот ли у нас датчик, сравнив с образцовым идентификатором и включив единственный зеленый светодиод. Но так как светодиод у нас уже зажжен был на этапе старта программы, то нам теперь главное, чтобы она не погас.

Если светодиод у нас остался гореть, значит у нас все нормально и можно дальше продолжить писать код инициализации акселерометра

 

image22

 

Добавим переменную в функцию AccInit

 

void AccInit(uint16_t InitStruct)

{

  uint8_t value=0;

 

Добавим некоторые макросы в заголовочный файл lsm6ds0.h, скопировав их из заранее подготовленного файла

 

#define LD2_OFF HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET)

//————————————————

#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_CTRL_REG5_XL   0X1F

#define LSM6DS0_ACC_GYRO_CTRL_REG6_XL   0X20

#define LSM6DS0_ACC_GYRO_CTRL_REG8    0X22

//————————————————

#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_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_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_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

//————————————————

void Accel_Ini(void);

 

Что означают данные настройки и регистры, мы будем разбирать по мере написания функций.

 

В следующей части занятия мы продолжим писать функцию инициализации датчика, затем напишем функцию считывания данных, и, более того, мы в данной части закончим работу с данным акселерометром.
Мы увидим показания и в текстовом виде и визуально с помощью программы NS Port Monitor.

 

 

Предыдущий урок Программирование МК STM32 Следующая часть

 

Техническая документация на датчик

Техническая документация на плату расширения

Программа NS Port Monitor

 

 

Отладочную плату можно приобрести здесь Nucleo STM32F401RE

Оценочную плату можно приобрести здесь STM32 X-NUCLEO-IKS01A1

 

 

Смотреть ВИДЕОУРОК

 

STM32 Подключаем акселерометр LSM6DS0

 

3 комментария на “STM Урок 42. Подключаем акселерометр LSM6DS0. Часть 1
  1. Евгений:

    Добрый день.
    Подскажите, пожалуйста, почему в настройках I2C Primary slave address = 51, а не D6?

  2. Григорий:

    static uint8_t I2Cx_ReadData(uint16_t Addr, uint8_t Reg)

    {

    HAL_StatusTypeDef status = HAL_OK;

    uint8_t value = 0;

    status = HAL_I2C_Mem_Read(&hi2c1, Addr, Reg, I2C_MEMADD_SIZE_8BIT, &value, 1, 0x10000);

    if(status != HAL_OK) Error();

    return value;

    }

    Не совсем понял откуда вы берете значения здесь, функция библиотеки хал status = HAL_I2C_Mem_Write(&hi2c1, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, &Value, 1, 0x10000); возвращает значение HAL_OK и все. Данные откуда берутся? у меня программа 0 выводит постоянно.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*