SPL: читаем данные с акселерометра

Краткое описание: В примере рассматривается способ снятия показаний с акселерометра датчика MPU9250, и преобразование полученных значений в ускорение g  относительно оси X. 

Задача: Требуется написать с использованием библиотеки StdPeriph код. Программа должна производить настойку аппаратного i2c.  Используя стандартные функции библиотеки StdPeriph для работы с i2c прочитать содержимое регистра ACCEL_XOUT_H, и перевести их в значение ускорения.  

Направление осей гироскопа и акселерометра 

#include "stm32f30x.h"
#include "stm32f30x_gpio.h"
#include "stm32f30x_rcc.h"
#include "stm32f30x_i2c.h"
#include "stm32f30x_tim.h"
#include "stm32f30x_syscfg.h"

#define ADR_MPU9250_W 0xD0 //(hex 0xD0 = bin 1101000 (адрес MPU9250)) + (bin 0 (write / бит записи))
#define ADR_MPU9250_R 0xD1 //(hex 0xD1 = bin 1101000 (адрес MPU9250)) + (bin 1 (read / бит чтения))

#define OWN_ADR 0x15 //Собственный адрес устройства i2c1
#define TIMING_I2C1 200000 //Частота тактового сигнала i2c1
#define PRESCALER_TIM2 720 //Пределитель чистоты для таймера 2
#define PERIOD_TIM2 1000 //Период таймера 10 миллисекунд (1 единица значения равна 0,01 миллисекунде (10 микросекунд))

#define ACCEL_CONFIG 0x1C //Адрес регистра выбора шкалы чувствительности акселерометра

#define ACCEL_FULL_SCALE 0x00 //bin 0x00000000 (ACCEL_FULL_SCALE = hex 0x00)

//Переменная для выбора шкалы чувствительности акселерометра
//Возможные варианты 2 Gs (hex 0x00), 4 Gs (hex 0x08), 8 Gs (hex 0x10), and 16 Gs (hex 0x18)

//Разрешение для шкалы акселерометра DPS / (16 bit ADC -> 2^16=65536/2)=32768
#define ACCEL_RESOLUTION_SCALE_2_DPS 2.0 / 32768.0
#define ACCEL_RESOLUTION_SCALE_4_DPS 4.0 / 32768.0
#define ACCEL_RESOLUTION_SCALE_8_DPS 8.0 / 32768.0
#define ACCEL_RESOLUTION_SCALE_16_DPS 16.0 / 32768.0

#define ACCEL_XOUT_H 0x3D //Адрес регистра акселерометра с измеренными значениями
#define ACCEL_XOUT_L 0x3C //Адрес регистра акселерометра с измеренными значениями

GPIO_InitTypeDef gpio; //Объявляем переменную gpio типа GPIO_InitTypeDef
I2C_InitTypeDef i2c; //Объявляем переменную i2c типа I2C_InitTypeDef
TIM_TimeBaseInitTypeDef timer; //Объявляем переменную timer типа TIM_TimeBaseInitTypeDef

unsigned char A_X_DATA_FROM_MPU9250 = 0; //Переменная для работы c данными акселерометром MPU9250
uint16_t A_X_RESULT = 0; //Переменная для работы c данными акселерометром MPU9250
float A_X_AXIS_POSITION = 0; //Переменная для работы c данными акселерометром MPU9250
int A_X_SIGN; //Переменная для работы c данными акселерометром MPU9250

void initAll()
{
    RCC_DeInit(); //Сбрасываем настройки тактирования
    RCC_HSICmd(DISABLE); //Отключаем внутренний источник тактирования
    RCC_HSEConfig(RCC_HSE_ON); //Включаем внешний источник тактирования
    RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE); //Подаем тактовый сигнал на системную шину

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); //Тактирование i2c1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //Тактирование таймера 2

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); //Тактирование порта C
    gpio.GPIO_Mode = GPIO_Mode_OUT; //Выбираем режим работы пина
    gpio.GPIO_Pin = GPIO_Pin_6; //Выбираем нужны пин к которому подключен светодиод
    GPIO_Init(GPIOC, &gpio); //Инициализируем структуру с настройками

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //Тактирование порта B
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_4); //(SCL) Настраиваем ножку для работы в режиме альтернативной функции
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_4); //(SDA) Настраиваем ножку для работы в режиме альтернативной функции

    //Настройки ножек i2c1
    gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; //Выбираем нужные пины SCL/SDA
    gpio.GPIO_Mode = GPIO_Mode_AF; //Выбираем режим работы пинов
    gpio.GPIO_Speed = GPIO_Speed_50MHz; //Выбираем максимальную скорость
    gpio.GPIO_OType = GPIO_OType_OD; //Выход с открытым стоком
    gpio.GPIO_PuPd = GPIO_PuPd_UP; //Конфигурируем подтяжку
    GPIO_Init(GPIOB, &gpio); //Инициализируем структуру с настройками

    //Настройки модуля i2c1
    i2c.I2C_Timing = TIMING_I2C1; //Частота тактового сигнала i2c1
    i2c.I2C_Mode = I2C_Mode_I2C; //Режим работы
    i2c.I2C_OwnAddress1 = OWN_ADR; //Собственный адрес устройства
    i2c.I2C_Ack = I2C_Ack_Enable; //Включение/отключения бита подтверждения ACK
    i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //Выбор формата адреса: 7 бит / 10 бит.
    I2C_Init(I2C1, &i2c); //Инициализация настроек
    I2C_Cmd(I2C1, ENABLE); //Включение модуля i2c1

    TIM_TimeBaseStructInit(&timer); //Инициализация структуры

    timer.TIM_Prescaler = PRESCALER_TIM2; //Выбор пределителя
    timer.TIM_Period = PERIOD_TIM2; //Период досчитав до которого произойдет прерывание
    TIM_TimeBaseInit(TIM2, &timer); //Инициализация выбранных значений

    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //Настраивает таймер на генерирования прерывания по переполнению
    NVIC_EnableIRQ(TIM2_IRQn); //Разрешаем соответствующие прерывания
    TIM_Cmd(TIM2, ENABLE); //Запускаем таймер
}

int main()
{

    initAll(); //Вызов функции инициализации i2c1
    ok_enable_irq(); //Разрешаются прерывания с линии IRQ на контроллере " disable_irq и enable_irq управляeт битом в регистре PRIMASK (запрет/разрешение)"

    while (1) {
    }
}

void TIM2_IRQHandler() //Функция прерывания по переполнению таймера 2
{
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //Функция очищает бит прерывания

    /*!!!!!!!!!!!!!Для работы с I2C1!!!!!!!!!!!!!*/
    //*************************************************************************************************************
    /*!!!!!!!!!!!!!Настраиваем чувствительность осей акселерометра через регистр ACCEL_CONFIG!!!!!!!!!!!!!*/
    //*************************************************************************************************************

    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) {}; //Ждем пока освободится шина i2c1

    /*Формируем первую посылку с адресом ведомого устройства и битом записи (ADR_MPU9250_W = hex 0xD0)/передаем два байта /выбираем режим работы модуля /генерируем старт записи*/

    I2C_TransferHandling(I2C1, ADR_MPU9250_W, 2, I2C_AutoEnd_Mode, I2C_Generate_Start_Write);

    while (I2C_GetFlagStatus(I2C1, I2C_ISR_TXE) == RESET) {}; //Ждем, установки флага TXE

    I2C_SendData(I2C1, ACCEL_CONFIG); //Отправляем адрес регистра в который хотим записать данные (ACCEL_CONFIG = hex 0x1B)

    while (I2C_GetFlagStatus(I2C1, I2C_ISR_TXE) == RESET) {}; //Ждем, установки флага TXE

    I2C_SendData(I2C1, ACCEL_FULL_SCALE); //Отправляем данные в регистр ACCEL_FULL_SCALE -> bin 0x00000000 (ACCEL_FULL_SCALE = hex 0x00)

    while (I2C_GetFlagStatus(I2C1, I2C_ISR_TXE) == RESET) {}; //Ждем, установки флага TXE

    while (I2C_GetFlagStatus(I2C1, I2C_ISR_STOPF) == RESET); //Ждем установки флага STOPF

    I2C_ClearFlag(I2C1, I2C_ICR_STOPCF); //Очистить флаг STOPF

    //******************************************************************************
    /*!!!!!!!!!!!!!Читаем данные из регистра ACCEL_XOUT_H = hex 0x3B!!!!!!!!!!!!!*/
    //******************************************************************************
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) {}; //Ждем пока освободится шина i2c1

    /*Формируем первую посылку с адресом ведомого устройства и битом записи (ADR_MPU9250_W = hex 0xD0) /выбираем режим работы модуля /генерируем старт записи*/

    I2C_TransferHandling(I2C1, ADR_MPU9250_W, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Write);

    while (I2C_GetFlagStatus(I2C1, I2C_ISR_TXE) == RESET) {}; //Ждем, установки флага TXE

    I2C_SendData(I2C1, ACCEL_XOUT_H); //Отправляем адрес регистра из которого хотим прочитать данные (ACCEL_XOUT_H = hex 0x3D)

    while (I2C_GetFlagStatus(I2C1, I2C_ISR_TXE) == RESET) {}; //Ждем, установки флага TXE

    /*Формируем посылку с адресом ведомого устройства и битом чтения (ADR_MPU9250_R = hex 0xD1) /выбираем режим работы модуля /генерируем старт чтения*/

    I2C_TransferHandling(I2C1, ADR_MPU9250_R, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Read);

    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET) {}; //Ждем, установки флага RXEN

    A_X_DATA_FROM_MPU9250 = I2C_ReceiveData(I2C1); //Запись данных в переменную Data

    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) != RESET) {}; //Ждем, установки флага RXEN

    /*!!!!!!!!!!!!!По окончанию работы с I2С вызываем следующие функции!!!!!!!!!!!!!*/

    while (I2C_GetFlagStatus(I2C1, I2C_ISR_STOPF) == RESET); //Ждем установки флага STOPF

    I2C_ClearFlag(I2C1, I2C_ICR_STOPCF); //Очистить флаг STOPF

    A_X_RESULT = (A_X_DATA_FROM_MPU9250 << 8) | A_X_DATA_FROM_MPU9250; //Записываем полученные данные в переменную A_X_RESULT

    if ((A_X_RESULT & 0x8000) == 0)
        A_X_SIGN = 0; //Проверяем направление ускорения

    else {
        A_X_SIGN = 1;
        A_X_RESULT &= 0x7FFF;
        A_X_RESULT = 0x7FFF - A_X_RESULT;
    }

    if (A_X_SIGN == 0) {
        A_X_AXIS_POSITION = A_X_RESULT * ACCEL_RESOLUTION_SCALE_2_DPS; //Записываем значение ускорения в переменную A_X_AXIS_POSITION
    }

    else {
        A_X_AXIS_POSITION = ((A_X_RESULT * ACCEL_RESOLUTION_SCALE_2_DPS) * (-1)); //Записываем значение ускорения в переменную A_X_AXIS_POSITION
    }

    if (A_X_AXIS_POSITION > 0.9) {
        GPIO_SetBits(GPIOC, GPIO_Pin_6); //Если значение ускорения близко к значению ускорения свободного падения зажигаем индикационный светодиод
    }

    if (A_X_AXIS_POSITION < 0.9) {
        GPIO_ResetBits(GPIOC, GPIO_Pin_6); //Если значение ускорения значительно меньше ускорения свободного падения гасим индикационный светодиод
    }
}

     Скомпилируйте программу нажав кнопку Build, прошейте микроконтроллер, нажмите RESET. При запуске программы, произойдет чтение регистра  ACCEL_XOUT_H, значения будут представлены в виде ускорения относительно оси x, если повернуть плату таким образом, что бы ускорение свободного падения земли было направленно в доль оси x, загорится индикационный светодиод, показывая что ускорение по данной оси составляет больше 0.9 g.

     ВНИМАНИЕ: Приведенный листинг кода не является единственной и оптимальной реализацией поставленной задачи. Пример опубликован с демонстрационной целью. Также автор во избежание переписывания чужих статей, пропускает теоретические основы необходимые для понимания примера, связывая это с тем что всю необходимую информацию можно найти в интернете.

Комментариев (0)

Написать комментарий

Имя *
E-mail
Введите комментарий *
Капча
35 + ? = 45