搜索
bottom↓
回复: 0
打印 上一主题 下一主题

《ESP32-S3使用指南—MicroPython版 V1.0》第二十八章 三轴加速度实验

[复制链接]

出0入234汤圆

跳转到指定楼层
1

1)实验平台:正点原子ESP32S3开发板
2)购买链接:https://detail.tmall.com/item.htm?id=768499342659
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-347618-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学ESP32S3快速入门视频教程:https://www.bilibili.com/video/BV1sH4y1W7Tc
6)正点原子FPGA交流群:132780729



第二十八章 三轴加速度实验


       前面,我们介绍了IIC驱动XL9555、AP3216C、AT24C02等器件,本章我们将向大家介绍如何使用IIC来驱动QMA6100P三轴加速度计。在本章中,作者将使用MicroPython C模块程序来驱动QMA6100P,从而获取X,Y,Z的原始数据,并把原始数据转化为pitch俯仰角和roll翻滚角。
       本章分为如下几个小节:
       28.1 QMA6100P简介
       28.2 QMA6100P C模块解析
       28.3 硬件设计
       28.4 程序设计
       28.5 下载验证

       28.1 QMA6100P简介
       QMA6100P是一款三轴加速度传感器,具有高集成、小尺寸封装的特点。它集成了信号调节ASIC的加速度传感器,可以感知倾斜、运动、冲击和振动。QMA6100P基于先进的高分辨率单晶硅MEMS技术,配合定制设计的14位ADC专用集成电路,具有低噪声、高精度、低功耗、偏置微调等优点。它支持数字接口I2C和SPI,内置硬件计步器,支持多种不同中断模式。QMA6100P的最大可支持64级FIFO,待机电流为5μA,计步器工作电流为44μA。主要应用市场与优势是手机,手环,手表,各类低功耗IOT设备,集成各类应用算法,计步器,抬手亮屏,垂手亮屏,久坐提醒,跌倒报警,跌落报警,睡眠检测,平衡检测,倾斜检测,睡眠唤醒等超过20种不同应用。QMA6100P还具有低成本和与市场主流传感器兼容的优点,以及超低功耗、可靠性高的特点。下图是QMA6100P内部框图。

图28.1.1 QMA6100P框图

       根据上文,QMA6100P三轴加速度计支持SPI和IIC两种通信接口。接口的实现流程可参考《13-52-20 QMA6100P Datasheet Rev. D.pdf》数据手册。本章节以ESP32-S3开发板电路为基准,该开发板使用IIC通信接口来获取QMA6100P三轴加速度计的相关参数。

       1,引脚描述
       QMA6100P的引脚说明如下表所示。

图28.1.1 QMA6100P管脚描述

       2,设备地址
       从规格书的章节5.4所示,QMA6100P在IIC通信下,具有两种设备地址设置,它们分别为0x12和0x13(7位串行地址)。这两个设备地址的选择是根据QMA6100P的第1号管脚确定,如下图所示。

图28.1.2 设备地址的选择

       从上图可知,当第1号管脚(AD0)拉低时,QMA6100P设备地址被设置为0x12,反次,该设备地址为0x13。本开发板是把AD0管脚拉低,所以在QMA6100P设备地址为0x12。

       3,写寄存器
       QMA6100P的写寄存器时序如下图所示。

图28.1.3 QMA6100P写寄存器时序

       图中,先发送QMA6100P的地址(7位,0X12,左移一位后为:0X24),最低位W=0表示写数据,随后发送8位寄存器地址,最后发送8位寄存器值。其中:START,表示IIC起始信号;R/W,表示读/写标志位(R/W =0表示写,R/W =1表示读);SACK,表示应答信号;STOP,表示IIC停止信号。

       4,读寄存器
       QMA6100P的读寄存器时序如下图所示。

图28.1.4 QMA6100P读寄存器时序

       图中,同样是先发送7位地址+写操作,然后再发送寄存器地址,随后,重新发送起始信号(Sr),再次发送7位地址+读操作,然后读取寄存器值。其中:SA,表示重新发送IIC起始信号;MACK,表示MCU应答;NACK,表示设备应答;其他简写同上。

       5,寄存器描述
       QMA6100P有一些列寄存器,由这些寄存器来控制QMA6100P的工作模式,以及中断配置和数据输出等。这里我们仅介绍我们在本章需要用到的一些寄存器,其他寄存器的描述和说明,请大家参考QMA6100P的数据手册。
       本章需要用到QMA6100P的寄存器如下表所示。

表28.1.1 QMA6100P相关寄存器及其说明

       其余的寄存器可在数据手册下找到相关描述和配置信息。

       28.2 QMA6100P C模块解析

       28.2.1 C模块解析
       在先前的章节里,作者已经详述了C模块的添加流程以及整体架构,接下来作者将简要介绍正点原子QMA6100P C模块驱动。这个讲解内容会分为几个部分:QMA6100P构造函数、IIC写入数据、IIC读取数据,以及如何获取三轴的数据并转化为pitch俯仰角和roll翻滚角。QMA6100P C模块驱动可在A盘6,软件资料1,软件2,MicroPython开发工具01-Windows2,正点原子MicroPython驱动CModules_LibIIC路径下找到。

       1,QMA6100P构造函数
  1. /* qma6100p结构体 */
  2. typedef struct _qma6100p_obj_t
  3. {
  4.     mp_obj_base_t   base;       /* 基地址 */
  5.     mp_obj_base_t   *iic_obj;   /* 指向IIC控制块 */
  6. } qma6100p_obj_t;

  7. mp_obj_t qma6100p_make_new(const mp_obj_type_t *type,size_t n_args,
  8. size_t n_kw,const mp_obj_t *all_args )
  9. {
  10.     /* 创建对象的参数 */
  11.     enum
  12.     {
  13.         ARG_iic,
  14.     };
  15.     /* 创建对象参数的默认值 */
  16.     static const mp_arg_t allowed_args[] = {
  17.         { MP_QSTR_iic, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} },
  18.     };
  19.     mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
  20. mp_arg_parse_all_kw_array(n_args, n_kw, all_args,
  21.                           MP_ARRAY_SIZE(allowed_args), allowed_args, args);
  22.         /*创建qma6100p对象*/
  23.     qma6100p_self = m_new_obj(qma6100p_obj_t);
  24.     qma6100p_self->base.type = &qma6100p_type;
  25.     /* 设置对象参数 */
  26. mp_obj_base_t *qma6100p_obj   =
  27. (mp_obj_base_t*)MP_OBJ_TO_PTR(args[ARG_iic].u_obj);
  28.     qma6100p_self->iic_obj        = qma6100p_obj;
  29.         /* 初始化qma6100p */
  30.     qma6100p_init();
  31.    
  32.     return MP_OBJ_FROM_PTR(qma6100p_self);
  33. }
复制代码
       从上述源代码中可以得知,该构造函数只有一个参数,即传入IIC驱动的控制块。我们可以通过这个控制块调用IIC驱动下的收发函数。然后,作者创建了一个QMA6100P对象,用于实例化对象并引用类的方法。最后,调用了qma6100p_init函数来初始化QMA6100P三轴传感器。以下是该初始化代码的示例:
  1. /**
  2. * @brief       初始化qma6100p
  3. * @param       无
  4. * @retval      0, 成功;
  5.                 1, 失败;
  6. */
  7. uint8_t qma6100p_comfig(void)
  8. {
  9. static uint8_t id_data[2];
  10. /* 读取设备ID,正常是0x90 */
  11.     qma6100p_register_read(QMA6100P_REG_CHIP_ID,id_data,1);
  12.    
  13.     /* qma6100p的初始化序列,请看手册“6.3 Initial sequence”章节 */
  14.     qma6100p_register_write_byte(QMA6100P_REG_RESET,QMA6100P_RESET);
  15.     mp_hal_delay_ms(5);
  16.     qma6100p_register_write_byte(QMA6100P_REG_RESET,QMA6100P_RESET_END);
  17.     mp_hal_delay_ms(10);
  18.    
  19.     qma6100p_register_write_byte(0x11, 0x80);
  20.     qma6100p_register_write_byte(0x11, 0x84);
  21.     qma6100p_register_write_byte(0x4a, 0x20);
  22.     qma6100p_register_write_byte(0x56, 0x01);
  23.     qma6100p_register_write_byte(0x5f, 0x80);
  24.     mp_hal_delay_ms(1);
  25.     qma6100p_register_write_byte(0x5f, 0x00);
  26.     mp_hal_delay_ms(10);
  27.         /*设置满量程刻度*/
  28. qma6100p_register_write_byte(QMA6100P_REG_RANGE,QMA6100P_RANGE_8G);
  29. /*带宽设置*/
  30. qma6100p_register_write_byte(QMA6100P_REG_BW_ODR,QMA6100P_BW_100);
  31. /*设置时钟和模式*/
  32.     qma6100p_register_write_byte(QMA6100P_REG_POWER_MANAGE,
  33. QMA6100P_MCLK_51_2K|0x80);
  34.         /*中断配置 */
  35.     qma6100p_register_write_byte(0x21, 0x03);
  36.    
  37.     if (id_data[0] == 0x90)
  38.     {
  39.         ESP_LOGI("qma6100p", "qma6100p success!!!");
  40.         return 0;               /* qma6100p正常 */
  41.     }
  42.     else
  43.     {
  44.         ESP_LOGE("qma6100p", "qma6100p fail!!!");
  45.         return 1;               /* qma6100p失败 */
  46.     }

  47. }

  48. /**
  49. * @brief       qma6100p初始化
  50. * @param       无
  51. * @retval      无
  52. */
  53. void qma6100p_init(void)
  54. {   
  55.     while (qma6100p_comfig())   /* 检测不到qma6100p */
  56.     {
  57.         ESP_LOGE("qma6100p", "qma6100p init fail!!!");
  58.         mp_hal_delay_ms(500);
  59.     }
  60. }
复制代码
       我们可以看到,init函数中调用了qma6100p_config函数,用于初始化和配置QMA6100P传感器模块。在qma6100p_config函数中,我们首先读取0x00寄存器来获取设备ID。然后,我们复位该设备并执行初始化序列(请参考规格书的6.3小节)。接下来,我们配置量程刻度、带宽、中断等参数。最后,我们检查读取的ID是否为0x90。如果是,则设备通信成功;否则,通信失败。

       2,写入函数
       根据上一节中作者对QMA6100P的IIC写时序流程的讲解,下面我们根据这个流程来编写QMA6100P的IIC写时序代码,如下所示:
  1. /**
  2. * @brief       向qma6100p寄存器写数据
  3. * @param       reg     :要写入的寄存器地址
  4. * @param       data    :要写入的数据
  5. * @retval      错误值   :ESP_OK:成功;其他值:错误
  6. */
  7. static esp_err_t qma6100p_register_write_byte(uint8_t reg, uint8_t data)
  8. {
  9.     uint8_t memaddr_buf[1];
  10.     memaddr_buf[0]  = reg;
  11. mp_obj_base_t *self=(mp_obj_base_t *)MP_OBJ_TO_PTR(qma6100p_self->iic_obj);
  12. mp_machine_i2c_p_t *i2c_p=
  13. (mp_machine_i2c_p_t *)MP_OBJ_TYPE_GET_SLOT(self->type, protocol);

  14.     mp_machine_i2c_buf_t bufs[2] = {
  15.         {.len = 1, .buf = memaddr_buf},
  16.         {.len = 1, .buf = &data},
  17.     };

  18.     i2c_p->transfer(self, QMA6100P_ADDR, 2, bufs,MP_MACHINE_I2C_FLAG_STOP);
  19.     return ESP_OK;
  20. }
复制代码
       在上述源代码中,作者根据传入的IIC控制块,调用了IIC收发函数来发送QMA6100P的命令和数据。发送完成后,函数返回了ESP_OK状态。

       3,读取函数
       根据上一节中作者对QMA6100P的IIC读时序流程的讲解,下面我们根据这个流程来编写QMA6100P的IIC读时序代码,如下所示:
  1. /**
  2. * @brief       读取qma6100p寄存器的数据
  3. * @param       reg_addr    :要读取的寄存器地址
  4. * @param       data        :读取的数据
  5. * @param       len         :数据大小
  6. * @retval      错误值       :ESP_OK:成功;其他值:错误
  7. */
  8. esp_err_t qma6100p_register_read(const uint8_t reg, uint8_t *data,
  9. const size_t len)
  10. {
  11.     uint8_t memaddr_buf[1];
  12.     memaddr_buf[0]  = reg;
  13.     mp_obj_base_t *self=(mp_obj_base_t *)MP_OBJ_TO_PTR(qma6100p_self->iic_obj);
  14. mp_machine_i2c_p_t *i2c_p =
  15. (mp_machine_i2c_p_t *)MP_OBJ_TYPE_GET_SLOT(self->type, protocol);

  16.     mp_machine_i2c_buf_t bufs[2] = {
  17.         {.len = 1, .buf = memaddr_buf},
  18.         {.len = len, .buf = data},
  19.     };

  20. i2c_p->transfer(self, QMA6100P_ADDR, 2, bufs,MP_MACHINE_I2C_FLAG_WRITE1
  21. | MP_MACHINE_I2C_FLAG_READ | MP_MACHINE_I2C_FLAG_STOP);
  22.     return ESP_OK;
  23. }
复制代码
       同样地,QMA6100P的读时序也是利用IIC收发函数来实现的。写时序和读时序的唯一区别在于最后的flag标志位不同,从而导致发送流程有所不同。如果读者想了解i2c_p->transfer函数的收发流程,可以在MicroPython源代码中找到machine_i2c.c文件(位于micropython\ports\esp32路径下)。

       4,读取三轴xyz原始数据和pitch俯仰角和roll翻滚角数据函数
       下面是根据XYZ原始数据,使用特定的算法来pitch俯仰角和roll翻滚角,如下所示:
  1. /**
  2. * @brief       读取三轴数据(原始数据、加速度、俯仰角和翻滚角)
  3. * @param       rawdata:qma6100p数据结构体
  4. * @retval      无
  5. */
  6. void qma6100p_read_rawdata(qma6100p_rawdata_t *rawdata)
  7. {
  8.     /* 读取三轴原始数据 */
  9.     qma6100p_register_read(QMA6100P_REG_XOUTL, xyz_data, 6);
  10.    
  11.     raw_data[0] = (short)(((xyz_data[1] << 8)) | (xyz_data[0]));
  12.     raw_data[1] = (short)(((xyz_data[3] << 8)) | (xyz_data[2]));
  13.     raw_data[2] = (short)(((xyz_data[5] << 8)) | (xyz_data[4]));
  14.    
  15.     rawdata->acc_x = (float)((raw_data[0] >> 2) * M_G) / (1024);
  16.     rawdata->acc_y = (float)((raw_data[1] >> 2) * M_G) / (1024);
  17.     rawdata->acc_z = (float)((raw_data[2] >> 2) * M_G) / (1024);
  18.    
  19.     /* 计算pitch俯仰角和roll翻滚角 */
  20. rawdata->acc_g = sqrt(rawdata->acc_x*rawdata->acc_x +
  21. rawdata->acc_y * rawdata->acc_y + rawdata->acc_z*rawdata->acc_z);
  22.    

  23. acc_normal = sqrtf(rawdata->acc_x * rawdata->acc_x + rawdata->acc_y
  24. * rawdata->acc_y + rawdata->acc_z * rawdata->acc_z);
  25.     accl_data[0] = rawdata->acc_x / acc_normal;
  26.     accl_data[1] = rawdata->acc_y / acc_normal;
  27.     accl_data[2] = rawdata->acc_z / acc_normal;
  28.    

  29.     rawdata->pitch = -atan2f(rawdata->acc_x,rawdata->acc_z) * RAD_TO_DEG;
  30.    
  31. acc_normal = sqrtf(accl_data[0] * accl_data[0] + accl_data[1]
  32. * accl_data[1] + accl_data[2] * accl_data[2]);
  33.    
  34.     rawdata->roll = asinf((accl_data[1]/acc_normal)) * RAD_TO_DEG ;
  35. }
复制代码
       上述源码中,作者先读取三轴的XYZ原始数据,然后经过特定的算法计算出pitch俯仰角和roll翻滚角。

       28.2.2 C模块构造与类的方法

       1,atk_qma6100p类的构造方法
       qma6100p的构造对象方法如下:
  1. class atk_qma6100p.init(iic)
  2. 使用示例:qma6100p = atk_qma6100p.init(iic)
复制代码
       该构造函数的参数描述,如下表所示。

表28.2.1 atk_qma6100p.init构造方法参数描述

       返回值:qma6100p对象。

       2,qma6100p类的方法

       ①:读取测量数据。
       其函数原型如下:
  1. qma6100p.qma6100p_read()
复制代码
       返回值:无。

       ②:读取X轴原始数据。
       其函数原型如下:
  1. qma6100p.qma6100p_acc_x()
复制代码
       返回值:X轴原始数据。

       ③:读取Y轴原始数据。
       其函数原型如下:
  1. qma6100p.qma6100p_acc_y()
复制代码
       返回值:Y轴原始数据。

       ④:读取Z轴原始数据。
       其函数原型如下:
  1. qma6100p.qma6100p_acc_z()
复制代码
       返回值:Z轴原始数据。

       ⑤:读取pitch俯仰角数据。
       其函数原型如下:
  1. qma6100p.qma6100p_acc_pitch()
复制代码
       返回值:pitch俯仰角数据。

       ⑥:读取roll翻滚角数据。
       其函数原型如下:
  1. qma6100p.qma6100p_acc_roll()
复制代码
       返回值:roll翻滚角数据。

       28.3 硬件设计

       1. 例程功能
       本章实验功能简介:在SPILCD显示屏上,我们能够看到XYZ的数据。当我们翻转开发板时,这些数据会根据开发板的翻转角度来计算出pitch俯仰角和roll翻滚角。

       2. 硬件资源

       1)XL9555
              IIC_INT-IO0(需在P5连接IO0)
              IIC_SDA-IO41
              IIC_SCL-IO42

       2)SPILCD
              CS-IO21
              SCK-IO12
              SDA-IO11
              DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
              PWR- IO1_3(XL9555)
              RST- IO1_2(XL9555)

       3)QMA6100P
              IIC_SDA-IO41
              IIC_SCL-IO42
              QMA_INT-IO0_1(XL9555)

       3. 原理图
       QMA6100P接口与ESP32-S3的连接关系,如下图所示:

图27.2.1 QMA6100P接口与ESP32-S3的连接电路图

       28.4 程序设计

       28.3.1 程序流程图
       程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图。

图28.3.1.1 程序流程图

       28.3.2 程序解析
       本书籍的代码都在main.py脚本下编写的,读者可在光盘资料下找到对应的源码。QMA6100P实验main.py源码如下:
  1. from machine import Pin,SPI,I2C
  2. import atk_xl9555 as io_ex
  3. import atk_lcd as lcd
  4. import atk_qma6100p as qma6100p
  5. import time


  6. """
  7. * @brief       程序入口
  8. * @param       无
  9. * @retval      无
  10. """
  11. if __name__ == '__main__':
  12.    
  13.     # 初始化LED并输出高电平
  14.     led = Pin(1,Pin.OUT,value = 1)
  15.     # IIC初始化
  16.     i2c0 = I2C(0, scl = Pin(42), sda = Pin(41), freq = 400000)
  17.     # XL9555初始化
  18.     xl9555 = io_ex.init(i2c0)
  19.    
  20.     # 复位LCD
  21.     xl9555.write_bit(io_ex.SLCD_RST,0)
  22.     time.sleep_ms(100)
  23.     xl9555.write_bit(io_ex.SLCD_RST,1)
  24.     time.sleep_ms(100)
  25.    
  26.     # 初始化SPI
  27.     spi = SPI(2,baudrate = 80000000, sck = Pin(12), mosi=Pin(11), miso=Pin(13))
  28.     # 初始化LCD,lcd = 0为正点原子2.4寸屏幕;lcd = 1为正点原子1.3寸SPILCD屏幕;
  29. display = lcd.init(spi,dc = Pin(40,Pin.OUT,Pin.PULL_UP,value = 1),
  30. cs = Pin(21,Pin.OUT,Pin.PULL_UP,value = 1),dir = 1,lcd = 0)
  31.     # 开启LCD背光
  32.     xl9555.write_bit(io_ex.SLCD_PWR,1)
  33.     time.sleep_ms(100)
  34.     # 初始化qma6100p
  35.     qma6100 = qma6100p.init(i2c0)
  36.    
  37.     # 显示实验信息
  38.     display.string(30, 50, 200, 16, 16, "ESP32-S3",lcd.RED)
  39.     display.string(30, 70, 200, 16, 16, "QMA6100P TEST", lcd.RED)
  40.     display.string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", lcd.RED)
  41.     display.string(30, 110, 200, 16, 16, "acc_x:", lcd.RED)
  42.     display.string(30, 130, 200, 16, 16, "acc_y:", lcd.RED)
  43.     display.string(30, 150, 200, 16, 16, "acc_z:", lcd.RED)
  44.     display.string(30, 170, 200, 16, 16, "acc_g:", lcd.RED)
  45.     display.string(30, 190, 200, 16, 16, "pitch:", lcd.RED)
  46.     display.string(30, 210, 200, 16, 16, "roll:", lcd.RED)
  47.    
  48.     while True :
  49.         
  50.         qma6100.qma6100p_read()
  51.         display.string(110,110,200,16,16,str(qma6100.qma6100p_acc_x()),lcd.BLUE)
  52.         display.string(110,130,200,16,16,str(qma6100.qma6100p_acc_y()),lcd.BLUE)
  53.         display.string(110,150,200,16,16,str(qma6100.qma6100p_acc_z()),lcd.BLUE)
  54.         display.string(110,170,200,16,16,str(qma6100.qma6100p_acc_g()),lcd.BLUE)
  55.         display.string(110,190,200,16,16,
  56. str(qma6100.qma6100p_acc_pitch()),lcd.BLUE)
  57.         display.string(110,210,200,16,16,
  58. str(qma6100.qma6100p_acc_roll()),lcd.BLUE)
  59.         led_state = led.value()
  60.         led.value(not led_state)
  61.         time.sleep_ms(100)         # 延时100ms
复制代码
       从上述源码可知,我们首先初始化各个外设,如IIC、SPI、XL9555、QMA6100P和LCD等驱动,然后调用qma6100.qma6100p_read函数测量数据,最后调用qma6100p_acc_x等函数获取XYZG、pitch俯仰角和roll翻滚角数据,并在SPILCD上显示。

       28.5 下载验证
       程序下载到开发板后,LCD不断刷新三轴的原始数据、pitch俯仰角和roll翻滚角。当用户转动或翻转开发板时,pitch俯仰角和roll翻滚角会随之变化,如下图所示。

图28.5.1 LCD显示效果图

阿莫论坛20周年了!感谢大家的支持与爱护!!

该献的血还是要献的。你不献他不献。难道让我去献? --- 出自坛友:lovejp1981
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-28 13:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表