第35章_瑞萨MCU零基础入门系列教程之ADXL345三轴传感器驱动实验

news2024/11/27 17:38:42

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id=728461040949

配套资料获取:https://renesas-docs.100ask.net

瑞萨MCU零基础入门系列教程汇总: https://blog.csdn.net/qq_35181236/article/details/132779862


第35章 ADXL345三轴传感器驱动实验

35.1 ADXL345三轴传感器简介

35.1.1 产品概述

ADXL345是一款小而薄的超低功耗3轴加速度计,分辨率高(13位),测量范围达±16g。数字输出数据为16位二进制补码格式,可通过SPI(3线或4线)或I2C数字接口访问。

该器件提供多种特殊检测功能。活动和非活动检测功能:通过比较任意轴上的加速度与用户设置的阈值来检测有无运动发生。敲击检测功能:可以检测任意方向的单振和双振动作。自由落体检测功能:可以检测器件是否正在掉落。这些功能可以独立映射到两个中断输出引脚中的一个。

35.1.2 串行SPI通信

对于SPI,可3线或4线配置,如以下连接图所示:

本书配套开发板使用的是4线SPI,对应的通信时序如下图所示:

从时序图中可知,ADXL345的地址只有6bit[A5:A0],地址字节的最高位为读写控制位,此位为1表示对某个地址写数据;此位为0表示读取某个地址的数据。

35.1.3 寄存器映射表

ADXL345的可访问寄存器多大31个,为了节省篇幅,本书就不在此处一一列举。请读者自行查阅本书配套的资料包中关于ADXL345的手册,里面对其寄存器进行了详细的说明描述。

35.1.4 ADXL345的中断

ADXL345提供两个中断输出引脚:INT1和INT2。这两个中断引脚都是推挽低阻抗引脚。中断引脚默认配置为高电平有效。设置DATA_FORMAT寄存器(地址0x31)中的INT_INVERT位,可以更改为低电平有效。

35.1.5 ADXL345的敲击检测

加速度值超过THRESH_TAP寄存器(地址0x1D)值,并且持续时间小于DUR寄存器(地址0x21)规定的时间范围的时候,SINGLE_TAP中断置位。

两次加速度事件超过THRESH_TAP寄存器(地址0x1D)值,并且持续时间小于DUR寄存器(地址0x21)的规定时间范围的时候,DOUBLE_TAP中断置位。第二次敲击开始于Latent寄存器(地址0x22)规定的时间之后,但在Window寄存器(0x23)规定时间内。详情见手册敲击检测部分。

35.1.6 ADXL345的活动检测

加速度值大于THRESH_ACT寄存器(地址0x24)存储值时,Activity(活动)中断置位,由任一轴参与,通过ACT_INACT_CTL寄存器(0x27)置位。

加速度值小于THRESH_INACT寄存器(地址0x25)的存储值时,Inactivity(静止)位置位,所有轴参与,多于TIME_INACT寄存器(地址0x26)规定的时间,通过ACT_INACT_CTL寄存器(地址0x27)置位。TIME_INACT最大值为255秒。

35.1.7 ADXL345的自由落体检测

加速度值小于THRESH_FF寄存器(地址0x28)的存储值时,FREE_FALL置位,大于TIME_FF寄存器(地址0x29)所有轴(逻辑与)所规定的时间。FREE_FALL中断不同于静止中断,因为:所有轴始终参与,并为逻辑“和”的形式,定时器周期小得多(最大值:1.28秒),始终为直流耦合操作模式。

35.2 ADXL345输出响应

ADXL345的输出响应,相对于XYZ方向的关系如下图所示:

35.3 模块配置

35.3.1 GPIO配置

本次实验使用的是开发板配套扩展板的SPI组,如下图所示:

使用的SPI引脚是P202/P203/P204和P205,SPI引脚对应使用的是RA6M5的Common SPI0:

对于P205,将它配置为通用输出即可。

35.3.2 SPI配置

本次实验中,对于SPI的Stack配置不能直接使用默认的参数了,因为ADXL345的手册中明确指明了SPI的SCLK线在空闲时需要处于高电平,而且采样数据是在SPI的上升沿采样,在下降沿有效,而RASC中对于SPI的默认参数刚好相反,需要用户做修改:

另外还需要使能SPI的“发送buffer为空中断”、“接收中断”,并命名中断回调函数。

35.4 外设驱动程序

35.4.1 GPIO驱动

本次实验的SPI片选信号脚P205,会使用软件方式拉低或拉高此引脚来选中SPI设备或放弃SPI设备,因而需要使用GPIO设备对象。

static struct IODev gSPIDACCSDev = {
    .name = "ADXL345 CS",
    .port = BSP_IO_PORT_02_PIN_05,
    .Init = IODrvInit,
    .Read = IODrvRead,
    .Write = IODrvWrite,
    .next = NULL
};

void IODevicesCreate(void)
{
    IODeviceInsert(&gSPIDACCSDev);
}

对于GPIO的驱动函数参考《32.4.1 GPIO驱动》。

35.4.2 SPI驱动

对于SPI设备而言,需要的驱动功能无非就是:初始化、读、写和同时读写,因而将这些属性需求封装到一个结构体中,源码如下:

typedef struct SPIDev{
	char *name;
    unsigned char channel;
    int (*Init)(struct SPIDev *ptdev);
    int (*Write)(struct SPIDev *ptdev, const unsigned char *buf, unsigned int length);
    int (*Read)(struct SPIDev *ptdev, unsigned char *buf, unsigned int length);
    int (*WriteRead)(struct SPIDev *ptdev, unsigned char * const wbuf, unsigned char *rbuf, unsigned int length);
    struct SPIDev *next;
}SPIDevice;

对于SPI设备对象的管理依然使用链表的方式,实现注册、插入、查找和打印,在dev_spi.c中实现。

  1. 初始化SPI

对于RA6M5而言,初始化SPI控制器,其实就是调用open函数打开指定SPI即可,本书实验做了一点补充,将片选信号也放到初始化函数中:

static int SPIDrvInit(struct SPIDev *ptdev)
{
    if(NULL == ptdev)   return -EINVAL;
    switch(ptdev->channel)
    {
        case 0:
        {
            pCSIO = IODeviceFind("SPIDAC CS");
            if(NULL == pCSIO)      return -ENXIO;
            /* 打开设备 */
            fsp_err_t err = g_spi0.p_api->open(g_spi0.p_ctrl, g_spi0.p_cfg);
            assert(FSP_SUCCESS == err);
            break;
        }
        case 1:case 2:case 3:case 4:
        case 5:
        case 6:case 7:case 8:case 9:
            break;
        default:break;
    }
    return ESUCCESS;
}
  1. SPI写数据

Common SPI不同于SCI SPI,SCI SPI只能输出8bit的数据,而Common SPI还能够输出1~32bit的数据,因而用户完全可以根据要写的数据量来动态调整数据位数,以加速传输速度:

static int SPIDrvWrite(struct SPIDev *ptdev, const unsigned char *buf, unsigned int length)
{
    if(NULL == ptdev)   return -EINVAL;
    if(NULL == buf)     return -EINVAL;
    if(0 == length)     return -EINVAL;
    
    switch(ptdev->channel)
    {
        case 0:
        {
            pCSIO->Write(pCSIO, 0);
            fsp_err_t err = FSP_SUCCESS;
            if((length%4)==0)
            {
                length = length>>2;
                err = g_spi0.p_api->write(g_spi0.p_ctrl, buf, length, SPI_BIT_WIDTH_32_BITS);
            }
            else if((length%2)==0)
            {
                length = length>>1;
                err = g_spi0.p_api->write(g_spi0.p_ctrl, buf, length, SPI_BIT_WIDTH_16_BITS);
            }
            else if(length==1)
            {
                err = g_spi0.p_api->write(g_spi0.p_ctrl, buf, length, SPI_BIT_WIDTH_8_BITS);
            }
            assert(FSP_SUCCESS == err);
            SPI0DrvWaitTxCplt();
            pCSIO->Write(pCSIO, 1);
            break;
        }
        case 1:case 2:case 3:case 4:
            break;
        case 5:
        case 6:case 7:case 8:case 9:
            break;
        default:break;
    }
    return ESUCCESS;
}

需要注意的是SPI传输一次最大能够传输65536个数据,如果传输的数据超过了这个上限,用户需要分包传输。

  1. SPI读数据

Common SPI读取数据也是一样的道理,可以读取32bit以内的任意位数的数据,一次读取多达数据个数也是65536个:

static int SPIDrvRead(struct SPIDev *ptdev, unsigned char *buf, unsigned int length)
{
    if(NULL == ptdev)   return -EINVAL;
    if(NULL == buf)     return -EINVAL;
    if(0 == length)     return -EINVAL;
    
    switch(ptdev->channel)
    {
        case 0:
        {
            pCSIO->Write(pCSIO, 0);
            fsp_err_t err = FSP_SUCCESS;
            if((length%4)==0)
            {
                length = length>>2;
                err = g_spi0.p_api->read(g_spi0.p_ctrl, buf, length, SPI_BIT_WIDTH_32_BITS);
            }
            else if((length%2)==0)
            {
                length = length>>1;
                err = g_spi0.p_api->read(g_spi0.p_ctrl, buf, length, SPI_BIT_WIDTH_16_BITS);
            }
            else if(length==1)
            {
                err = g_spi0.p_api->read(g_spi0.p_ctrl, buf, length, SPI_BIT_WIDTH_8_BITS);
            }
            assert(FSP_SUCCESS == err);
            SPI0DrvWaitTxCplt();
            pCSIO->Write(pCSIO, 1);
            break;
        }
        case 1:case 2:case 3:case 4:
            break;
        case 5:
        case 6:case 7:case 8:case 9:
            break;
        default:break;
    }
    return ESUCCESS;
}
  1. SPI同时读写数据

同时读写也是一个道理:

static int SPIDrvWriteRead(struct SPIDev *ptdev, unsigned char * const wbuf, unsigned char *rbuf, unsigned int length)
{
    if(NULL == ptdev)   return -EINVAL;
    if(NULL == wbuf)    return -EINVAL;
    if(NULL == rbuf)    return -EINVAL;
    if(0 == length)     return -EINVAL;
    
    switch(ptdev->channel)
    {
        case 0:
        {
            pCSIO->Write(pCSIO, 0);
            fsp_err_t err = FSP_SUCCESS;
            if((length%4)==0)
            {
                length = length>>2;
                err = g_spi0.p_api->writeRead(g_spi0.p_ctrl, wbuf, rbuf, length, SPI_BIT_WIDTH_32_BITS);
            }
            else if((length%2)==0)
            {
                length = length>>1;
                err = g_spi0.p_api->writeRead(g_spi0.p_ctrl, wbuf, rbuf, length, SPI_BIT_WIDTH_16_BITS);
            }
            else if(length==1)
            {
                err = g_spi0.p_api->writeRead(g_spi0.p_ctrl, wbuf, rbuf, length, SPI_BIT_WIDTH_8_BITS);
            }
            assert(FSP_SUCCESS == err);
            SPI0DrvWaitTxCplt();
            pCSIO->Write(pCSIO, 1);
            break;
        }
        case 1:case 2:case 3:case 4:
            break;
        case 5:
        case 6:case 7:case 8:case 9:
            break;
        default:break;
    }
    return ESUCCESS;
}
  1. 回调函数和传输完成等待函数

回调函数和传输等待在本书前文对于SPI外设的使用已经讲过,此处仅展示下代码:

static volatile bool gSPI0TxCplt = false;
void spi0_callback(spi_callback_args_t *p_args)
{
    switch(p_args->event)
    {
        case SPI_EVENT_TRANSFER_COMPLETE:
        {
            gSPI0TxCplt = true;
            break;
        }
        default:break;
    }
}
static void SPI0DrvWaitTxCplt(void)
{
    while(!gSPI0TxCplt);
    gSPI0TxCplt = false;
}

35.5 ADXL345模块

对于ADXL345这个处理器,其官方给出了一个非常完备的参考驱动,参考地址:

https://github.com/analogdevicesinc/no-OS/tree/master/drivers/accel/adxl345

本节是对这个驱动代码的移植使用。

35.5.1 ADXL345设备对象

对于ADXL345,常用操作是读取3轴上的加速度值,在有些时候还会需要手动关闭和开启它的测量;另外,还需要设置ADXL345的测量范围以及精度,因而将这些属性统一封装到一个结构体中(dev_adxl345.h):

typedef struct ADXL345Dev{
    char *name;
    /** Measurement range */
    unsigned char   selected_range;
    /** Enable/Disable Full Resolution */
    unsigned char   full_resolution_set;
    MeasureValue    value;
    int (*Init)(struct ADXL345Dev *ptdev);
    int (*Read)(struct ADXL345Dev *ptdev);
    int (*Start)(struct ADXL345Dev *ptdev);
    int (*Stop)(struct ADXL345Dev *ptdev);
}ADXL345Device;

然后在dev_adxl345.c里构造一个ADXL345Device结构体,并给上层代码提高获得这个结构体的函数,代码如下:

static ADXL345Device gADXL345 = {
    .name = "ADXL345",
    .selected_range = 2,
    .full_resolution_set = 0,
    .Init   = ADXLDevInit,
    .Read   = ADXLDevRead,
    .Start  = ADXLDevStart,
    .Stop   = ADXLDevStop
};

struct ADXL345Dev *ADXL345GetDevice(void)
{
    return &gADXL345;
}

35.5.2 寄存器读取函数

对于SPI传输,发送N个字节,就会读到N个字节。要访问ADXL345的寄存器,需要发出2个字节:第1个字节表示寄存器地址(它的最高位为1,表示要读寄存器;为0表示要写寄存器)。

针对寄存器的读写,定义了2个宏:

#define ADXL345_SPI_READ        (1 << 7)
#define ADXL345_SPI_WRITE       (0 << 7)

读寄存器时,将寄存器的地址跟ADXL345_SPI_READ进行或运算,就得到要发出的第一个字节,要发出的第二个字节可以设为0。SPI传输中发送2个字节,就会读到2个字节,读到的第2个字节就是要得到的数据:

static uint8_t adxl345_get_register_value(uint8_t register_address)
{
    if(NULL == pADXLSPI)    return 0;

    uint8_t data_buffer[2] = {0, 0};
    uint8_t rxbuffer[2] = {0, 0};
    uint8_t register_value = 0;
    data_buffer[0] = ADXL345_SPI_READ | register_address;
    data_buffer[1] = 0;
    if(ESUCCESS != pADXLSPI->WriteRead(pADXLSPI, data_buffer, rxbuffer, 2))  return 0;
    register_value = rxbuffer[1];
    return register_value;
}

35.5.3 寄存器写函数

写寄存器时,将寄存器的地址跟ADXL345_SPI_WRITE进行或运算,就得到要发出的第一个字节,要发出的第二个字节就是要发出的数值。代码如下:

static void adxl345_set_register_value(uint8_t register_address, uint8_t register_value)
{
    if(NULL == pADXLSPI)    return;

    uint8_t data_buffer[2] = {0, 0};
    uint8_t rxbuffer[2] = {0, 0};
    data_buffer[0] = ADXL345_SPI_WRITE | register_address;
    data_buffer[1] = register_value;
    pADXLSPI->WriteRead(pADXLSPI, data_buffer, rxbuffer, 2);
}

35.5.4 ADXL345指令定义

ADXL345的指令比较多,详细的请查看本章配套源码,本书此处仅展示部分代码:

/* ADXL345 Register Map */
#define ADXL345_DEVID           0x00 // R   Device ID.
#define ADXL345_THRESH_TAP      0x1D // R/W Tap threshold.
#define ADXL345_OFSX            0x1E // R/W X-axis offset.
#define ADXL345_OFSY            0x1F // R/W Y-axis offset.
#define ADXL345_OFSZ            0x20 // R/W Z-axis offset.
#define ADXL345_DUR             0x21 // R/W Tap duration.
#define ADXL345_LATENT          0x22 // R/W Tap latency.
#define ADXL345_WINDOW          0x23 // R/W Tap window.
#define ADXL345_THRESH_ACT      0x24 // R/W Activity threshold.
#define ADXL345_THRESH_INACT    0x25 // R/W Inactivity threshold.
#define ADXL345_TIME_INACT      0x26 // R/W Inactivity time.
#define ADXL345_ACT_INACT_CTL   0x27 // R/W Axis enable control for activity
// and inactivity detection.
#define ADXL345_THRESH_FF       0x28 // R/W Free-fall threshold.
#define ADXL345_TIME_FF         0x29 // R/W Free-fall time.
#define ADXL345_TAP_AXES        0x2A // R/W Axis control for tap/double tap.
#define ADXL345_ACT_TAP_STATUS  0x2B // R   Source of tap/double tap.

35.5.5 设置功耗模式

ADXL345的功耗模式分为标准模式和测量模式,通过寄存器地址0x2D设置:

#define ADXL345_POWER_CTL       0x2D // R/W Power saving features control.
static void adxl345_set_power_mode(uint8_t pwr_mode)
{
    uint8_t old_power_ctl = 0;
    uint8_t new_power_ctl = 0;

    old_power_ctl = adxl345_get_register_value(ADXL345_POWER_CTL);
    new_power_ctl = old_power_ctl & ~ADXL345_PCTL_MEASURE;
    new_power_ctl = new_power_ctl | (pwr_mode * ADXL345_PCTL_MEASURE);
    adxl345_set_register_value(ADXL345_POWER_CTL, new_power_ctl);
}

35.5.6 读取每个通道的坐标

读取坐标值的寄存器起始地址是0x32:

#define ADXL345_DATAX0          0x32 // R   X-Axis Data 0.
static void adxl345_get_xyz(int16_t *x, int16_t *y, int16_t *z)
{
    if(NULL == pADXLSPI)    return;

    uint8_t first_reg_address = ADXL345_DATAX0;
    uint8_t read_buffer[7]    = {0, 0, 0, 0, 0, 0, 0};
    
    read_buffer[0] = ADXL345_SPI_READ | ADXL345_SPI_MB | first_reg_address;
    pADXLSPI->WriteRead(pADXLSPI, read_buffer, read_buffer, 7);
    /* x = ((ADXL345_DATAX1) << 8) + ADXL345_DATAX0 */
    *x = (int16_t)((read_buffer[2] << 8) + read_buffer[1]);
    /* y = ((ADXL345_DATAY1) << 8) + ADXL345_DATAY0 */  
    *y = (int16_t)((read_buffer[4] << 8) + read_buffer[3]);
    /* z = ((ADXL345_DATAZ1) << 8) + ADXL345_DATAZ0 */  
    *z = (int16_t)((read_buffer[6] << 8) + read_buffer[5]);
}

35.5.7 读取每个通道的加速度

加速度值是通过3轴坐标值计算转换过来的,计算方法在手册的【偏移校准】节有详细解释,此处参考官网给出的计算方法:

/* ADXL345 Full Resolution Scale Factor */
#define ADXL345_SCALE_FACTOR    0.0039
static void adxl345_get_g_xyz(float *x, float *y, float *z)
{
    int16_t x_data = 0;  // X-axis's output data.
    int16_t y_data = 0;  // Y-axis's output data.
    int16_t z_data = 0;  // Z-axis's output data.

    adxl345_get_xyz(&x_data, &y_data, &z_data);
    *x = (float)(gADXL345.full_resolution_set  ? (x_data * ADXL345_SCALE_FACTOR) :
                (x_data * ADXL345_SCALE_FACTOR * (gADXL345.selected_range >> 1)));
    *y = (float)(gADXL345.full_resolution_set  ? (y_data * ADXL345_SCALE_FACTOR) :
                (y_data * ADXL345_SCALE_FACTOR * (gADXL345.selected_range >> 1)));
    *z = (float)(gADXL345.full_resolution_set  ? (z_data * ADXL345_SCALE_FACTOR) :
                (z_data * ADXL345_SCALE_FACTOR * (gADXL345.selected_range >> 1)));
}

35.5.8 使能/关闭敲击检测

敲击检测涉及一系列的寄存器读写控制:

#define ADXL345_THRESH_TAP      0x1D // R/W Tap threshold.
#define ADXL345_DUR             0x21 // R/W Tap duration.
#define ADXL345_LATENT          0x22 // R/W Tap latency.
#define ADXL345_WINDOW          0x23 // R/W Tap window.
#define ADXL345_TAP_AXES        0x2A // R/W Axis control for tap/double tap.

然后通过设置这些寄存器的值来配置ADXL345的敲击检测功能:

static void adxl345_set_tap_detection(uint8_t tap_type,
                                      uint8_t tap_axes,
                                      uint8_t tap_dur,
                                      uint8_t tap_latent,
                                      uint8_t tap_window,
                                      uint8_t tap_thresh,
                                      uint8_t tap_int)
{
    uint8_t old_tap_axes = 0;
    uint8_t new_tap_axes = 0;
    uint8_t old_int_map = 0;
    uint8_t new_int_map = 0;
    uint8_t old_int_enable = 0;
    uint8_t new_int_enable = 0;

    old_tap_axes = adxl345_get_register_value(ADXL345_TAP_AXES);
    new_tap_axes = old_tap_axes & ~(ADXL345_TAP_X_EN |ADXL345_TAP_Y_EN |ADXL345_TAP_Z_EN);
    new_tap_axes = new_tap_axes | tap_axes;
    adxl345_set_register_value(ADXL345_TAP_AXES, new_tap_axes);
    adxl345_set_register_value(ADXL345_DUR, tap_dur);
    adxl345_set_register_value(ADXL345_LATENT, tap_latent);
    adxl345_set_register_value(ADXL345_WINDOW, tap_window);
    adxl345_set_register_value(ADXL345_THRESH_TAP, tap_thresh);
    old_int_map = adxl345_get_register_value(ADXL345_INT_MAP);
    new_int_map = old_int_map & (~(ADXL345_SINGLE_TAP | ADXL345_DOUBLE_TAP));
    new_int_map = new_int_map | tap_int;
    adxl345_set_register_value(ADXL345_INT_MAP, new_int_map);
    old_int_enable = adxl345_get_register_value(ADXL345_INT_ENABLE);
    new_int_enable = old_int_enable & (~(ADXL345_SINGLE_TAP | ADXL345_DOUBLE_TAP));
    new_int_enable = new_int_enable | tap_type;
    adxl345_set_register_value(ADXL345_INT_ENABLE, new_int_enable);
}

35.5.9 使能/关闭活动检测

配置活动检测的寄存器地址有:

#define ADXL345_ACT_INACT_CTL   0x27 // R/W Axis enable control for activity
#define ADXL345_THRESH_ACT      0x24 // R/W Activity threshold.
#define ADXL345_INT_ENABLE      0x2E // R/W Interrupt enable control.
#define ADXL345_INT_MAP         0x2F // R/W Interrupt mapping control.

通过配置这些寄存器来选择活动检测的方法和参数:

static void adxl345_set_activity_detection(uint8_t act_on_off,
                                           uint8_t act_axes,
                                           uint8_t act_ac_dc,
                                           uint8_t act_thresh,
                                           uint8_t act_int)
{
    uint8_t old_act_inact_ctl = 0;
    uint8_t new_act_inact_ctl = 0;
    uint8_t old_int_map = 0;
    uint8_t new_int_map = 0;
    uint8_t old_int_enable = 0;
    uint8_t new_int_enable = 0;

    old_act_inact_ctl = adxl345_get_register_value(ADXL345_INT_ENABLE);
    new_act_inact_ctl = old_act_inact_ctl & ~(ADXL345_ACT_ACDC | ADXL345_ACT_X_EN | ADXL345_ACT_Y_EN | ADXL345_ACT_Z_EN);
    new_act_inact_ctl = new_act_inact_ctl | (act_ac_dc | act_axes);
    adxl345_set_register_value(ADXL345_ACT_INACT_CTL, new_act_inact_ctl);
    adxl345_set_register_value(ADXL345_THRESH_ACT, act_thresh);
    old_int_map = adxl345_get_register_value(ADXL345_INT_MAP);
    new_int_map = old_int_map & ~(ADXL345_ACTIVITY);
    new_int_map = new_int_map | act_int;
    adxl345_set_register_value(ADXL345_INT_MAP, new_int_map);
    old_int_enable = adxl345_get_register_value(ADXL345_INT_ENABLE);
    new_int_enable = old_int_enable & ~(ADXL345_ACTIVITY);
    new_int_enable = new_int_enable | (ADXL345_ACTIVITY * act_on_off);
    adxl345_set_register_value(ADXL345_INT_ENABLE, new_int_enable);
}

35.5.10 初始化ADXL345

初始化ADXL345时,需要初始化SPI控制器,另外还需要设置ADXL345自身的工作模式和检测属性:

static int ADXLDevInit(struct ADXL345Dev *ptdev)
{
    if(NULL == ptdev)       return -EINVAL;
    
    pINT1IO = IODeviceFind("ADXL345 INT1");
    if(NULL == pINT1IO)    return -ENXIO;

    pADXLSPI = SPIDeviceFind("ADXL345 SPI");
    if(NULL == pADXLSPI)    return -ENXIO;
    if(ESUCCESS != pADXLSPI->Init(pADXLSPI))    return -EIO;

    if (adxl345_get_register_value(ADXL345_DEVID) != ADXL345_ID)
    {
        xprintf("Failed to read ADXL345's ID!\r\n");
        return -EIO;
    }
    
    adxl345_set_power_mode(0x01);   /* measure(1)/standby(0) mode.*/
    adxl345_set_range_resolution(ADXL345_RANGE_PM_16G,  /* Range option. +- 16g */
                                 ADXL345_FULL_RES);     /*Enables full resolution*/
    adxl345_set_tap_detection(ADXL345_SINGLE_TAP | ADXL345_DOUBLE_TAP,    /* Tap type (none, single, double) */
                              ADXL345_TAP_X_EN | ADXL345_TAP_Y_EN | ADXL345_TAP_Z_EN, /* tap_axes */
                              0x0D,   /* tap_dur */
                              0x50,   /* tap_latent */
                              0xF0,   /* tap_window */
                              0x20,   /* Tap threshold */
                              0x00);    /* Interrupts pin.0x0 - interrupts on INT1 pin */
    adxl345_set_activity_detection(0x01,    /* enables(1)/disable(0) the activity detection. */
                                   ADXL345_ACT_X_EN | ADXL345_ACT_Y_EN | ADXL345_ACT_Z_EN,  /* Axes which participate in detecting activity. */
                                   0x00,    /* dc(0)/ac(ADXL345_ACT_ACDC)-coupled operation. */
                                   0x08,    /* Threshold value for detecting activity */
                                   0x00);   /* Interrupts pin.0-int1 */
    adxl345_set_free_fall_detection(0x01,   /* disables(0)/enable(1) the free-fall detection. */
                                    0x08,   /* Threshold value for free-fall detection. The scale factor */
                                    0x0A,   /* Time value for free-fall detection */
                                    0x00);  /* Interrupts pin. */
	adxl345_get_all_axes();

    return ESUCCESS;
}

35.5.11 读取加速度

此函数调用获取三轴加速度的adxl345_get_g_xyz函数,只是将其进行了再次封装:

static int ADXLDevRead(struct ADXL345Dev *ptdev)
{
    if(NULL == ptdev)       return -EINVAL;
    if(NULL == pADXLSPI)    return -EIO;

    adxl345_get_g_xyz(&ptdev->value.x, &ptdev->value.y, &ptdev->value.z);
    return ESUCCESS;
}

35.6 测试程序

将获取到的ADXL345设备对象初始化完毕之后,每隔500ms读一次3轴加速度数据,并将它们打印出来:

void DeviceTest(void)
{
    UartDevicesRegister();
    TimerDevicesRegister();
    SPIDevicesRegister();
    IODevicesRegister();

    ADXL345Device *pADXL345 = ADXL345GetDevice();
    if(NULL == pADXL345)
    {
        xprintf("Failed to Find ADXL345 Device!\r\n");
        return;
    }
    pADXL345->Init(pADXL345);
    while(1)
    {
        if(pADXL345->Read(pADXL345) == ESUCCESS)
        {
            xprintf("x:%.4fg\ty:%.4fg\tz:%.4fg\r\n", pADXL345->value.x, pADXL345->value.y, pADXL345->value.z);
        }
        mdelay(500);
    }
}

35.7 测试结果

将程序烧写到开发板上运行,打开串口助手并且插上扩展板以及接上ADXL345模块,可以观察到下图所示的结果:


本章完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1008737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虹科案例 | Zuellig Pharma和ELPRO通过符合GDP标准的温度监测和高效的温度数据管理为未来发展奠定基础

在本案例研究中&#xff0c;您将了解Zuellig Pharma 实施了温度监测解决方案&#xff0c;以一致的数据结构获取各国和各种运输方式的数据; 通过将温度数据上传到其数据库管理系统&#xff0c;显著提高了其效率; 并建立了为未来管理决策提供数据增值使用的基础。 项目合作伙伴 …

《AI新时代:大一新生如何快速入门IT专业?》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

《使用AADL的模型基工程》读书笔记(一)

1. 什么是模型基工程&#xff1f; 模型基工程(Model-Based Engineering&#xff0c;MBE)旨在建立和分析系统模型&#xff0c;这样就能够预测和了解该系统的能力和工作质量属性 (如性能、可靠性或信息安全性)。在系统集成和验收试验之前&#xff0c;很难发现一些系统级的问题&a…

【数据结构】——排序算法的相关习题

目录 一、选择题题型一 &#xff08;插入排序&#xff09;1、直接插入排序2、折半插入排序3、希尔排序 题型二&#xff08;交换排序&#xff09;1、冒泡排序2、快速排序 题型三&#xff08;选择排序&#xff09;1、简单选择排序~2、堆排序 ~题型四&#xff08;归并排序&#xf…

测试平台项目部署二(手动部署改成Dockerfile)

测试平台项目部署二(手动部署改成Dockerfile) 一、Dockerfile制作1、entrypoint.sh制作2、构建镜像3、启动容器二、遇到的问题1、pip install --no-cache-dir -r requirements.txt安装第三方库时,报Installing build dependencies: started2、安装第三方库文件比较慢,考虑更…

可以在图片上编辑文字的软件推荐?来试试这几款

在图片上编辑文字的优势之一是可以更好地传达信息。有时候&#xff0c;图片本身可能不足以清楚地说明重点&#xff0c;但是添加文字可以强调或澄清要点。此外&#xff0c;对于社交媒体等视觉重要的平台&#xff0c;图像上的文字可以更好地吸引用户的注意力和交流。那么有哪些可…

TypeScript类型兼容:结构化类型

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 1. 鸭子类型&#xff1a;定义和示例 2. 鸭子类型的优点 2.1 代码的灵活性 2.2 代码的复用 2.3 与 JavaScript 的…

ACM模板二:树、图、并查集、DancingLink

目录 〇&#xff0c;全文说明、宏定义代码 一&#xff0c;二叉树 二&#xff0c;树状数组、线段树 三&#xff0c;多叉树 四&#xff0c;并查集、DancingLink、无向图、最小生成树 五&#xff0c;有向图、单源最短路径、连通分量、拓扑排序 六&#xff0c;网格图、回路链…

关于 Resolution(分辨率、解析力)各单位的意义及相互之间的换算

1、问题背景 最近在调试的项目&#xff0c;有关于对解析力的要求&#xff0c;用 imatest 软件测试 MTF50 的值&#xff0c;如下图所示&#xff0c;可以看到他有不同的单位表示&#xff0c;LW/PH、Cycles/pixel 。另外关于解析力的单位还有LP/mm、L/mm、Cycles/mm、LP/PH&#…

学生护眼灯用白炽灯还是led?专业的学生护眼灯推荐

现在的护眼灯逐渐成为了孩子们学习路上必不可少的一盏灯具&#xff0c;它比普通的台灯光线更加均匀舒适&#xff0c;而且更加护眼。因此也成为了家长们呵护孩子视力健康的一大“帮手”&#xff0c;不过护眼台灯的种类也有很多&#xff0c;最近就有家长问孩子使用的护眼灯是白炽…

用GPT干的18件事,能够真正提高学习生产力,建议收藏

用GPT干的18件事&#xff0c;能够真正提高学习生产力&#xff0c;建议收藏。 语法更正 文本翻译 语言转换 代码解释 修复代码错误 作为百科全书 信息提取 好友聊天 创意生成器 采访问题 论文大纲 故事创作 问题类比 创建 SQL 需求 情感分析 将产品描述转变为广告 关键字提取 闲…

Tailwind CSS 初学者指南

Tailwind CSS是一个实用程序优先的CSS框架&#xff0c;允许您快速构建现代网站&#xff0c;而无需离开HTML。它是 Web 开发社区中最流行和使用最广泛的 CSS 框架之一&#xff0c;每月下载量超过 250 万次1。在本文中&#xff0c;我们将探讨 Tailwind CSS 2023 的路线图&#xf…

go string类型简叙

字符串赋值后就不能修改 var str string "abcd" str[0] f //这里就有能修改str内容字符串的两种表示形式 双引号&#xff0c;会识别转义字符反引号&#xff0c;以字符串的原生形式输出&#xff0c;包括换行和特殊字符&#xff0c;可以实现防止攻击、输出输出源代…

el-table表格中加入输入框

<template><div class"box"><div class"btn"><el-button type"primary">发送评委</el-button><el-button type"primary" click"flag true" v-if"!flag">编辑</el-button…

Android EditText筛选+选择功能开发

在日常开发中经常会遇到这种需求&#xff0c;EditText既需要可以筛选&#xff0c;又可以点击选择。这里筛选功能用的是AutoCompleteTextView&#xff0c;选择功能使用的是第三方库https://github.com/kongzue/DialogX。 Android AutoCompleteTextView(自动完成文本框)的基本使用…

Mozilla 紧急修补 Firefox 和 Thunderbird 中的 WebP 严重零日漏洞

Mozilla 周二发布了安全更新&#xff0c;修复了 Firefox 和 Thunderbird 中的一个关键零日漏洞。 该漏洞被标记为 CVE-2023-4863&#xff0c;是 WebP 图像格式中的堆缓冲区溢出漏洞&#xff0c;在处理特制图像时可能导致任意代码执行。 Mozilla 在一份公告中说&#xff0c;打…

MyBatisPlus(二)基础Mapperr接口:增删改查

MyBatisPlus&#xff1a;基础Mapper接口&#xff1a;增删改查 插入一条数据 代码 Testpublic void insert() {User user new User();user.setId(6L);user.setName("张三");user.setAge(25);user.setEmail("zhangsanexample.com");userMapper.insert(use…

系统安全漏洞检测技术第三方检测机构

安全测试报告 建立一个安全的Web系统一直是很多企业的目标&#xff0c;一个比较实用的方法就是建立比较容易实现的相对安全的系统&#xff0c;同时按照一定的安全策略建立相应的安全辅助系统&#xff0c;系统安全漏洞检测就是这样一类安全辅助系统。 系统安全漏洞检测技术 1、…

Kali Linux基础篇(一) 信息收集

一、前言 1、信息收集分主动信息收集和被动信息收集 主动方式&#xff1a;攻击者直接访问网站&#xff0c;对网站做出扫描、探测等行为&#xff0c;目标系统可能会记录操作信息被动方式&#xff1a;利用第三方的服务访问目标&#xff0c;被动信息收集不会留下访问痕迹&#x…

华三路由交换技术基础——计算机网络基础

计算机网络&#xff1a; 定义&#xff1a;一组具有自治权的计算机互联的集合 作用&#xff1a; 1.共享信息资源 2.分解式处理信息 4.负载均衡 5.综合信息服务 它是计算机技术与通信技术的两个领域的结合 一&#xff0c;计算机网络中的基本概念&#xff1a; 局域网&#xff…