搜索
bottom↓
回复: 0

《DNK210使用指南 -SDK版 V1.0》第十七章 六轴传感器

[复制链接]

出0入234汤圆

发表于 前天 09:42 | 显示全部楼层 |阅读模式
2.jpg
1)实验平台:正点原子DNK210开发板
2)购买链接:https://detail.tmall.com/item.htm?id=782801398750
3)全套实验源码+手册+视频下载地址:http://openedv.com/thread-348335-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学DNK210快速入门视频教程:https://www.bilibili.com/video/BV1kD421G7fu
6)正点原子FPGA交流群:132780729
1.png
3.png

第十七章 六轴传感器——原始数据读取实验


       本章将介绍板载六轴传感器的使用,Kendryte K210官方SDK提供了IIC接口的API函数能很方便地帮助我们驱动板载的六轴传感器。通过本章的学习,读者将学习到板载六轴传感器的基本使用。
       本章分为如下几个小节:
       17.1 SH3001驱动介绍及使用方法
       17.2 硬件设计
       17.3 程序设计
       17.4 运行验证

       17.1 SH3001驱动介绍及使用方法
       SH3001是正点原子DNK210板载的六轴传感器芯片,是一款六轴IMU(Internal measurement unit,惯性从测量单元)。SH3001内部集成有三轴加速度计和三轴陀螺仪(角速度计),相较于多组件方案,免除了组合加速度计和陀螺仪时可能遇到的轴间差问题,并减小了体积和功耗。
       本章将介绍Kendryte K210使用IIC接口驱动开发板板载的SH3001六轴传感器。

       17.1.1 IIC介绍
       IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器以及其外围设备。它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据,在CPU与被控IC之间、IC与IC之间进行双向传送。
       IIC总线有如下特点:
       ①总线由数据线SDA和时钟线SCL构成的串行总线,数据线用来传输数据,时钟线用来同步数据收发。
       ②总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时序就可以实现微控制器与器件之间的通信。
       ③数据线SDA和时钟线SCL都是双向线路,都通过一个电流源或上拉电阻连接到正的电压,所以当总线空闲的时候,这两条线路都是高电平。
       ④总线上数据的传输速率在标准模式下可达100kbit/s,在快速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s。
       ⑤总线支持设备连接。在使用IIC通信总线时,可以有多个具备IIC通信能力的设备挂载在上面,同时支持多个主机和多个从机,连接到总线的接口数量只由总线电容400pF的限制决定。IIC总线挂载多个器件的示意图,如下图所示。

第十七章 六轴传感器900.png
图17.1.1.1 IIC总线挂载多个器件

       下面来学习IIC总线协议,IIC总线时序图如下所示:

第十七章 六轴传感器977.png
图17.1.1.2 IIC总线时序图

       为了便于大家更好的了解IIC协议,我们从起始信号、停止信号、应答信号、数据有效性、数据传输以及空闲状态等6个方面讲解,大家需要对应图14.1.1.2的标号来理解。
       ① 起始信号
       当SCL为高电平期间,SDA由高到低的跳变。起始信号是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在起始信号产生后,总线就处于被占用状态,准备数据传输。
       ② 停止信号
       当SCL为高电平期间,SDA由低到高的跳变。停止信号也是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在停止信号发出后,总线就处于空闲状态。
       ③ 应答信号
       发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
       观察上图标号③就可以发现,有效应答的要求是从机在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主机,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主机接收器发送一个停止信号。
       ④ 数据有效性
       IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。数据在SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定。
       ⑤ 数据传输
       在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
       ⑥ 空闲状态
       IIC总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
       了解前面的知识后,下面介绍一下IIC的基本的读写通讯过程,包括主机写数据到从机即写操作,主机到从机读取数据即读操作。下面先看一下写操作通讯过程图,如下图所示。

第十七章 六轴传感器1911.png
图17.1.1.3 写操作通讯过程图

       主机首先在IIC总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的数据。主机接着发送从机地址+0(写操作)组成的8bit数据,所有从机接收到该8bit数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机在总线上接收到有应答信号后,才能继续向从机发送数据。注意:IIC总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
       接着讲解一下IIC总线的读操作过程,先看一下读操作通讯过程图,如下图所示。

第十七章 六轴传感器2165.png
图17.1.1.4 读操作通讯过程图

       主机向从机读取数据的操作,一开始的操作与写操作有点相似,观察两个图也可以发现,都是由主机发出起始信号,接着发送从机地址+1(读操作)组成的8bit数据,从机接收到数据验证是否是自身的地址。那么在验证是自己的设备地址后,从机就会发出应答信号,并向主机返回8bit数据,发送完之后从机就会等待主机的应答信号。假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(n byte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。
       Kendryte K210有3个I²C总线接口,根据用户的配置,总线接口可以用作I²C MASTER 或SLAVE模式。I²C接口支持:
       1. 标准模式(0到100Kb/s)
       2. 快速模式(<= 400Kb/s)
       3. 7-位/10-位寻址模式
       4. 批量传输模式
       5. 中断或轮询模式操作
       Kendryte K210官方SDK提供了多个操作IIC接口的API函数,这里我们只讲述本实验用到的函数,这些函数介绍如下:

       1,i2c_init函数
       该函数主要用于IIC的配置初始化,该函数原型及参数描述如下代码所示:
  1. void i2c_init(i2c_device_number_t i2c_num, uint32_t slave_address, uint32_t address_width,
  2.               uint32_t i2c_clk);
  3. /* IIC设备号配置参数 */
  4. typedef enum _i2c_device_number
  5. {
  6.     I2C_DEVICE_0,
  7.     I2C_DEVICE_1,
  8.     I2C_DEVICE_2,
  9.     I2C_DEVICE_MAX,
  10. } i2c_device_number_t;
复制代码
       该函数共有四个配置参数,第一个为IIC设备号,第二个为从机设备地址,第三个是配置地址宽度为7bit还是10bit,我们一般使用的是7bit,第四个参数为配置IIC时钟,IIC初始化完成之后就能够使用IIC对应的引脚发送和接收数据。
       下面介绍IIC发送数据的API函数。

       2,i2c_send_data函数
       该函数用于发送数据,如下代码所示:
  1. int i2c_send_data(i2c_device_number_t i2c_num, const uint8_t *send_buf, size_t send_buf_len);
复制代码
       函数一共有三个参数,第一个参数是发送数据的IIC设备号,第二个参数为要发送的数据,第三个数据是发送数据的长度,此函数是普通发送模式,SDK也提供用DMA发送数据的API函数,发送成功函数返回0,返回其他说明发送异常。

       3,i2c_recv_data函数
       该函数用于接收数据,如下代码所示:
  1. int i2c_recv_data(i2c_device_number_t i2c_num, const uint8_t *send_buf, size_t send_buf_len, uint8_t *receive_buf,size_t receive_buf_len);
复制代码
       函数一共有五个参数,第一个参数是发送数据的IIC设备号,第二个参数为要发送的数据,第三个数据是发送数据的长度,第四个参数为接收数据的buf,第五个参数为接收数据的长度。这里发送的数据通常为从机的寄存器命令,用于读取相关数据。

       17.1.2 SH3001介绍
       SH3001内部集成有三轴加速度计和三轴陀螺仪,输出都是16位的数字量,可通过I2C接口与之进行数据交互。加速度计的测量范围最大可配置为±16g(g为重力加速度),静态测量精度高。陀螺仪的角速度测量范围最大可配置为±2000(dps),具有良好的动态相应特性。
       SH3001的特点如下:
       1. 加速度计量程(g):±2、±4、±8、±16
       2. 加速度计灵敏度(LSB/g):16384、8192、4096、2048
       3. 陀螺仪量程(dps):±125、±250、±500、±1000、±2000
       4. 陀螺仪灵敏度(LSB、dps):262、131、65.5、32.8、16.4
       5. 封装:LGA14,2.5*3.0*0.9mm3
       SH3001传感器的检测轴,如下图所示:

第十七章 六轴传感器3961.png
图17.1.2.1 SH3001检测轴方向

       更多有关SH3001的芯片特性以及内部寄存器描述,请参考SH3001的数据手册——《SH3001.pdf》,读者可在A盘硬件资料芯片资料下找到这份文档。
       为了方便大家使用正点原子DNK210开发板板载的SH3001六轴传感器,正点原子团队为SH3001编写了一份驱动代码,我们会在17.3小节介绍。
       SH3001的内部框图如图所示:

2.png
图17.1.2.2 SH3001框图

       其中,SCL和SDA可以连接MCU的IIC接口,MCU通过这个IIC接口来控制SH3001,另外还有一个IIC接口:MSCK和MSDA,这个接口可用来连接外部从设备,比如气压传感器。VDDIO是IO口电压,该引脚最低可以到1.8V我们一般直接接VDD即可。SDO是从IIC接口(接MCU)的地址控制引脚,该引脚控制IIC地址的最低位。如果接GND,则SH3001的IIC地址是:0X36,如果接VDD,则是0X37,注意:这里的地址是不包含数据传输的最低位的(最低位用来表示读写)!!
       下面简单介绍一下本实验用到的SH3001比较重要的寄存器。
       陀螺仪配置寄存器(GYRO_CONFIG)
       陀螺仪配置寄存器有6个,寄存器地址分别是0x28、0x29、0x2B、0x8F、0x9F、0xAF,用来配置陀螺仪量程等参数,描述如下图所示;

第十七章 六轴传感器4542.png
图17.1.2.3 GYRO_CONFIG_0寄存器

第十七章 六轴传感器4571.png
图17.1.2.4 GYRO_CONFIG_1寄存器

第十七章 六轴传感器4600.png
图17.1.2.5 GYRO_CONFIG_2寄存器

第十七章 六轴传感器4628.png
图17.1.2.6 GYRO_CONFIG_3寄存器

第十七章 六轴传感器4656.png
图17.1.2.7 GYRO_CONFIG_4寄存器

第十七章 六轴传感器4685.png
图17.1.2.8 GYRO_CONFIG_5寄存器

       其中,我们介绍一些比较重要的配置,GYRO_CONFIG_1的[3:0]位用于配置陀螺仪的输出数据频率:0000:1000Hz;0001:500Hz;0010:250Hz;0011:125Hz;0100:63Hz;0101:31Hz;1000:2kHz;1001:4kHz;1010:8kHz;1011:16kHz;1100:32kHz;我们一般设置为0001,即500Hz。
       GYRO_CONFIG_2的bit 4位用于设置是否使能陀螺仪数字低通滤波器(LPF)。一般我们设置为1,即使能LPF。
       GYRO_CONFIG_3、GYRO_CONFIG_4和GYRO_CONFIG_5寄存器的[2:0]位分别用于设置陀螺仪X轴、Y轴和Z轴的满量程范围:010::125dps;011:250dps;100:500dps;101:1000dps;110:2000dps;我们一般设置X轴、Y轴和Z轴为110,即2000dps。
       加速度即配置寄存器(ACC_CONFIG)
       加速度配置寄存器有4个,寄存器地址分别是:0x22、0x23、0x25、0x26,用来配置加速度计量程等参数,描述如下图所示:

第十七章 六轴传感器5212.png
图17.1.2.9 ACC_CONFIG_0寄存器

第十七章 六轴传感器5240.png
图17.1.2.10 ACC_CONFIG_1寄存器

第十七章 六轴传感器5269.png
图17.1.2.11 ACC_CONFIG_2寄存器

第十七章 六轴传感器5298.png
图17.1.2.12 ACC_CONFIG_3寄存器

       这里,我们要配置的是,ACC_CONFIG_1的[3:0]位用于设置加速度计的输出数据频率:0000:1000Hz;0001:500Hz;0010:250Hz;0011:125Hz;0100:63Hz;0101:31Hz;0110:16Hz;1000:2000Hz;1001:4000Hz;1010:8000Hz。我们一般设置为0001,即500Hz。
       ACC_CONFIG_2的[2:0]位用于设置加速度计的满量程范围:010:±16g;011:±8g;100:±4g;101:±2g;一般我们设置为010,即±16g。
       ACC_CONFIG_3的[7:5]位用于设置加速度计低通滤波器的截止频率:000:ODR×0.40;001:ODR×0.25;010:ODR×0.11;011:ODR×0.04;100:ODR×0.02;一般我们设置为001,即低通滤波器的截止频率为ODR×0.25=500Hz×0.25=125Hz。ACC_CONFIG_3的bit 3位用于设置加速度计是否使能低通滤波器,一般我们设置为1,即使能。

       17.2 硬件设计

       17.2.1 例程功能

       1. 初始化SH3001等外设后,在死循环里面不停读取:加速度传感器和陀螺仪等原始数据,通过串口调试助手显示数据,在LCD模块上面显示温度、六轴原始数据等信息。

       17.2.2 硬件资源

       1. SH3001
              IIC_SCL - IO22
              IIC_SDA - IO23

       17.2.3 原理图
       本章实验内容,需要使用到板载的SH3001芯片,正点原子DNK210开发板上的SH3001芯片连接原理图,如下图所示:

第十七章 六轴传感器6020.png
图17.2.3.1 SH3001连接原理图

       17.3 程序设计

       17.3.1 IIC驱动
       为了方便使用IIC进行数据收发,我们重新构建了IIC相关驱动函数存放于iic.c和iic.h文件(区别IIC的库文件i2c.c和i2c.h),
  1. #define ADDRESS_WIDTH   7
  2. #define I2C_CLK_SPEED   400000
复制代码
       这两个宏分别用于设置IIC地址的宽度和IIC的时钟频率。
  1. /**
  2. * @brief       向寄存器reg写入数据
  3. * @param       addr    : 从机地址
  4. * @param       reg     : 寄存器命令
  5. * @param       length  : 数据长度
  6. * @param       data_buf: 要写入的数据
  7. * @retval      返回值  : 0,成功
  8. *                        1,失败
  9. */
  10. uint16_t i2c_hd_write(uint8_t addr, uint8_t reg, uint16_t length, uint8_t *data_buf)
  11. {
  12.     uint16_t error = 1;
  13.     uint8_t data[length + 1];

  14.     if (_current_addr != addr)
  15.     {
  16.         i2c_hardware_init(addr);
  17.     }
  18.    
  19.     data[0] = reg;
  20.     for (size_t i = 0; i < length; i++)
  21.     {
  22.         data[i+1] = *data_buf;
  23.         data_buf++;
  24.     }

  25.     error = i2c_send_data(I2C_DEVICE_0, data, length + 1);
  26.     return error;
  27. }
复制代码
       我们通过向SH3001寄存器写数据便可配置传感器的功能,SH3001的寄存器都由相应的指令去访问,参考SH3001的数据手册——《SH3001.pdf》了解,所以我们写数据前需要先发送一个8bit的寄存器指令,等待传感器应答后才能发送我们要写入的数据。
  1. /**
  2. * @brief       从寄存器reg读取数据
  3. * @param       addr    : 从机地址
  4. * @param       reg     : 寄存器命令
  5. * @param       length  : 数据长度
  6. * @param       data_buf: 要读取的数据
  7. * @retval      返回值  : 0,成功
  8. *                        1,失败
  9. */
  10. uint16_t i2c_hd_read(uint8_t addr, uint8_t reg, uint16_t length, uint8_t *data_buf)
  11. {
  12.     if (_current_addr != addr)
  13.     {
  14.         i2c_hardware_init(addr);
  15.     }
  16.     uint16_t error = 1;
  17.    
  18.     error = i2c_recv_data(I2C_DEVICE_0, ®, 1, data_buf, length);
  19.     return error;
  20. }
复制代码
       读函数和写函数的前一部分执行过程有点相似,我们在发送指令后进入接收状态,MCU每次接收到数据后发送应答信号,从机设备便会继续发送数据,等待接收到我们所需的数据长度时发送非应答信号便可停止接收。

       17.3.2 SH3001驱动
       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。SH3001驱动源码包括两个文件:sh3001.c和
  1. sh3001.h。
  2. /*****************************HARDWARE-PIN*********************************/
  3. /* 硬件IO口,与原理图对应 */
  4. #define PIN_IMU_SCL             (22)
  5. #define PIN_IMU_SDA             (23)
  6. #define PIN_IMU_INT             (20)

  7. /*****************************SOICMWARE-GPIO********************************/
  8. /* 软件GPIO口,与程序对应 */
  9. #define IMU_INT_GPIONUM         (0)

  10. /*****************************FUNC-GPIO************************************/
  11. /* GPIO口的功能,绑定到硬件IO口 */
  12. #define FUNC_IMU_INT             (FUNC_GPIOHS0 + IMU_INT_GPIONUM)
  13. #define FUNC_ICM_SCL             (FUNC_I2C0_SCLK)
  14. #define FUNC_ICM_SDA             (FUNC_I2C0_SDA)
复制代码
       这里是DNK210开发板SH3001传感器的IIC引脚定义和功能编号,下面介绍SH3001的相关寄存器,其定义如下:
  1. #define SH3001_ACC_XL                (0x00)
  2. #define SH3001_ACC_XH                (0x01)
  3. #define SH3001_ACC_YL                (0x02)
  4. #define SH3001_ACC_YH                (0x03)
  5. #define SH3001_ACC_ZL                (0x04)
  6. #define SH3001_ACC_ZH                (0x05)
  7. …/* 此处省略部分代码 */
  8. #define SH3001_GYRO_CONF3            (0x8F)
  9. #define SH3001_GYRO_CONF4            (0x9F)
  10. #define SH3001_GYRO_CONF5            (0xAF)
  11. #define SH3001_CHIP_ID1              (0xDF)
复制代码
       下面来看一下sh3001_read_nbytes函数,实现SH3001芯片指定地址读取N字节数据,代码如下:
  1. /**
  2. * @brief       从SH3001读取N字节数据
  3. * @note        SH3001的命令发送,也是用该函数实现(不带参数的命令, 也会有一个状态寄存器需要读取)
  4. * @param       devaddr  : 寄存器地址
  5. * @param       regaddr  : 寄存器命令
  6. * @param       length   : 读取长度
  7. * @param       readbuf  : 数据存储buf
  8. * @retval      0, 操作成功
  9. *              其他, 操作失败
  10. */
  11. unsigned char sh3001_read_nbytes(uint8_t devaddr, uint8_t regaddr, uint16_t length, uint8_t *readbuf)
  12. {
  13.     i2c_hd_read(devaddr, regaddr, length, readbuf);
  14.     return (SH3001_TRUE);
  15. }
复制代码
       该函数直接调用i2c_hd_read函数即可,非常方便使用,成功返回0;IIC读取过程可参考17.1.1小节。
       接下来看一下sh3001_write_nbytes函数,实现从SH3001芯片指定地址写入数据,其定义如下:
  1. /**
  2. * @brief       SH3001写入N字节数据
  3. * @param       devaddr  : 寄存器地址
  4. * @param       regaddr  : 寄存器命令
  5. * @param       length   : 写入长度
  6. * @param       writebuf : 数据存储buf
  7. * @retval      0, 操作成功
  8. *              其他, 操作失败
  9. */
  10. unsigned char sh3001_write_nbytes(uint8_t devaddr, uint8_t regaddr, uint16_t length, uint8_t *writebuf)
  11. {

  12.     i2c_hd_write(devaddr, regaddr, length, writebuf);
  13.     return (SH3001_TRUE);
  14. }
复制代码
       该函数直接调用i2c_hd_write函数即可,函数返回0表示发送成功,这里也不多介绍。
       最后,我们介绍一下读取数据函数,代码如下:
  1. /**
  2. * @brief       读温度值
  3. * @param       无
  4. * @retval      温度值,单位为℃(float类型)
  5. */
  6. float sh3001_get_tempdata(void)
  7. {
  8.     unsigned char regdata[2] = {0};
  9.     unsigned short int tempref[2] = {0};

  10.     sh3001_read_nbytes(SH3001_ADDRESS, SH3001_TEMP_CONF0, 2, ®data[0]);
  11.     tempref[0] = ((unsigned short int)(regdata[0] & 0x0F) << 8) | regdata[1];

  12.     sh3001_read_nbytes(SH3001_ADDRESS, SH3001_TEMP_ZL, 2, ®data[0]);
  13.     tempref[1] = ((unsigned short int)(regdata[1] & 0x0F) << 8) | regdata[0];

  14.     return ( (((float)(tempref[1] - tempref[0])) / 16.0f) + 25.0f );
  15. }

  16. /**
  17. * @brief       读取补偿后SH3001陀螺仪和加速度的数据(推荐使用)
  18. * @param       accdata[3]  : acc X,Y,Z;
  19. * @param       gyrodata[3] : gyro X,Y,Z;
  20. * @retval      无
  21. */
  22. void sh3001_get_imu_compdata(short accdata[3], short gyrodata[3])
  23. {
  24.     unsigned char regdata[15] = {0};
  25.     unsigned char paramp;
  26.     int acctemp[3], gyrotemp[3];

  27.     sh3001_read_nbytes(SH3001_ADDRESS, SH3001_ACC_XL, 15, regdata);
  28.     accdata[0] = ((short)regdata[1] << 8) | regdata[0];
  29.     accdata[1] = ((short)regdata[3] << 8) | regdata[2];
  30.     accdata[2] = ((short)regdata[5] << 8) | regdata[4];
  31.     gyrodata[0] = ((short)regdata[7] << 8) | regdata[6];
  32.     gyrodata[1] = ((short)regdata[9] << 8) | regdata[8];
  33.     gyrodata[2] = ((short)regdata[11] << 8) | regdata[10];
  34.     paramp = regdata[14] & 0x1F;
  35.    
  36.     acctemp[0] = (int)( accdata[0] + \
  37.                         accdata[1] * ((float)g_compcoef.cXY / 1024.0f) + \
  38.                         accdata[2] * ((float)g_compcoef.cXZ / 1024.0f) );

  39.     acctemp[1] = (int)( accdata[0] * ((float)g_compcoef.cYX / 1024.0f) + \
  40.                         accdata[1] + \
  41.                         accdata[2] * ((float)g_compcoef.cYZ / 1024.0f) );

  42.     acctemp[2] = (int)( accdata[0] * ((float)g_compcoef.cZX / 1024.0f) + \
  43.                         accdata[1] * ((float)g_compcoef.cZY / 1024.0f) + \
  44.                         accdata[2] );

  45.     if (acctemp[0] > 32767)
  46.     {
  47.         acctemp[0] = 32767;
  48.     }
  49.     else if (acctemp[0] < -32768)
  50.     {
  51.         acctemp[0] = -32768;
  52.     }

  53.     if (acctemp[1] > 32767)
  54.     {
  55.         acctemp[1] = 32767;
  56.     }
  57.     else if (acctemp[1] < -32768)
  58.     {
  59.         acctemp[1] = -32768;
  60.     }

  61.     if (acctemp[2] > 32767)
  62.     {
  63.         acctemp[2] = 32767;
  64.     }
  65.     else if (acctemp[2] < -32768)
  66.     {
  67.         acctemp[2] = -32768;
  68.     }

  69.     accdata[0] = (short)acctemp[0];
  70.     accdata[1] = (short)acctemp[1];
  71.     accdata[2] = (short)acctemp[2];

  72.     gyrotemp[0] = gyrodata[0] - (paramp - g_compcoef.paramP0) * g_compcoef.jX * g_compcoef.xMulti;
  73.     gyrotemp[1] = gyrodata[1] - (paramp - g_compcoef.paramP0) * g_compcoef.jY * g_compcoef.yMulti;
  74.     gyrotemp[2] = gyrodata[2] - (paramp - g_compcoef.paramP0) * g_compcoef.jZ * g_compcoef.zMulti;

  75.     if (gyrotemp[0] > 32767)
  76.     {
  77.         gyrotemp[0] = 32767;
  78.     }
  79.     else if (gyrotemp[0] < -32768)
  80.     {
  81.         gyrotemp[0] = -32768;
  82.     }

  83.     if (gyrotemp[1] > 32767)
  84.     {
  85.         gyrotemp[1] = 32767;
  86.     }
  87.     else if (gyrotemp[1] < -32768)
  88.     {
  89.         gyrotemp[1] = -32768;
  90.     }

  91.     if (gyrotemp[2] > 32767)
  92.     {
  93.         gyrotemp[2] = 32767;
  94.     }
  95.     else if (gyrotemp[2] < -32768)
  96.     {
  97.         gyrotemp[2] = -32768;
  98.     }

  99.     gyrodata[0] = (short)gyrotemp[0];
  100.     gyrodata[1] = (short)gyrotemp[1];
  101.     gyrodata[2] = (short)gyrotemp[2];

  102.     //printf("%d %d %d %d %d %d\r\n", accdata[0], accdata[1], accdata[2], gyrodata[0], gyrodata[1], gyrodata[2]);
  103. }
复制代码
       sh3001_gettemp_data函数:读取转换后的温度数据。
       sh3001_getimucompdata函数:调用sh3001_read_nbytes读数据,读取加速度计和陀螺仪的x,y,z轴原始数据。
       下面看一下SH3001的初始化函数,其定义如下:
  1. /**
  2. * @brief       初始化SH3001接口
  3. * @param       无
  4. * @retval      SH3001_TRUE, 成功
  5. *              SH3001_FALSE, 异常
  6. */
  7. unsigned char sh3001_init(void)
  8. {
  9.     unsigned char i = 0;
  10.     unsigned char regdata = 0;

  11.     sh3001_hardware_init();        /* 绑定IIC功能引脚 */
  12.     i2c_hardware_init(SH3001_ADDRESS); /* 初始化IIC */
  13.     msleep(10);
  14.    
  15.     /* 读取CHIP ID */
  16.     do
  17.     {
  18.         sh3001_read_nbytes(SH3001_ADDRESS, SH3001_CHIP_ID, 1, ®data);
  19.     }while ((regdata != 0x61) && (i++ < 3));

  20.     if ((regdata != 0x61))
  21.     {
  22.         printf("SH3001 CHIP ID:0X%X\r\n", regdata); /* 打印错误的ID */
  23.         return SH3001_FALSE;
  24.     }

  25.     sh3001_module_reset();                  /* 重置内部模块 */
  26.    
  27.     /* ACC配置: 500Hz, 16G, cut off Freq(BW)=500*0.25Hz=125Hz, enable filter; */
  28.     sh3001_acc_config(SH3001_ODR_500HZ,
  29.                       SH3001_ACC_RANGE_16G,
  30.                       SH3001_ACC_ODRX025,
  31.                       SH3001_ACC_FILTER_EN);

  32.   /* GYRO配置: 500Hz, X\Y\Z 2000deg/s, cut off Freq(BW)=181Hz, enable filter; */
  33.     sh3001_gyro_config( SH3001_ODR_500HZ,
  34.                         SH3001_GYRO_RANGE_2000,
  35.                         SH3001_GYRO_RANGE_2000,
  36.                         SH3001_GYRO_RANGE_2000,
  37.                         SH3001_GYRO_ODRX00,
  38.                         SH3001_GYRO_FILTER_EN);
  39.    
  40.     /* 温度配置: 输出速率63Hz, 使能温度测量 */
  41.     sh3001_temp_config(SH3001_TEMP_ODR_63, SH3001_TEMP_EN);

  42.     /* SH3001进入正常工作模式 */
  43.     sh3001_switch_powermode(SH3001_NORMAL_MODE);

  44.     /* 读取补偿系数 */
  45.     sh3001_comp_init(&g_compcoef);

  46.     return SH3001_TRUE;
  47. }
复制代码
       在初始化函数中,我们主要对该模块的加速度、陀螺仪,还有温度传感器的使能和设置,并且选择normal模式。

       17.3.3 图片处理代码
       上一章我们讲解了LCD显示字符串的实验,细心的小伙伴们可以发现LCD上显示一个一个字符的速度非常慢,因为我们在显示字符串过程中需要一个一个点的定位和LCD内存的读写,两个设备的频繁通讯非常耗时,所以我们K210采用以图片的形式显示各种信息,我们创建一个RGB565图片缓存区,要显示什么内容直接对缓存区的数据操作即可,最后一次写入到LCD显示的GRAM中。
       正点原子团队编写了一份图片处理代码用于RGB565和RGB888的图片处理,源码存放在image_process.c和image_process.h文件,内容较多,我们将函数的功能以表格的形式简单介绍下。读者如果对图片处理过程感兴趣,可自行查看源码学习。
       函数名及功能如下表所示:

1.png
表17.3.3.1 图片处理函数功能表

       17.3.4 main.c代码
       main.c中的代码如下所示:
  1. #include "sleep.h"
  2. #include "sysctl.h"
  3. #include "image_process.h"
  4. #define LCD_SPI_CLK_RATE 15000000
  5. #include "./BSP/SH3001/sh3001.h"
  6. #include "./BSP/IIC/iic.h"
  7. #include "./BSP/LCD/lcd.h"
  8. #include "./BSP/LCD/lcdfont.h"

  9. static uint16_t lcd_gram[320 * 240] __attribute__((aligned(32))); /* 定义一个LCD显示缓存区 */

  10. /**
  11. * @brief       显示数据
  12. *  @note       以图片显示的方式更快  
  13. * @param       x, y : 坐标
  14. * @param       title: 标题
  15. * @param       data: 角度
  16. * @retval      无
  17. */
  18. void user_show_data(uint16_t x, uint16_t y, char * title, short data)
  19. {
  20.     char buf[15];

  21.     sprintf(buf,"%s%d", title, data);          /* 格式化输出 */
  22. draw_fill_rectangle_image(lcd_gram, 320, x, y, x + 120, y + 16, WHITE);  
  23. /* 清除上次缓存数据 */
  24. draw_string_rgb565_image(lcd_gram, 320, 240, x, y, buf, BLUE);
  25. /* 将字符串写入LCD缓存区 */

  26.     // lcd_draw_fill_rectangle(x, y, x + 120, y + 16, WHITE);   /* 清除上次数据(最多显示15个字符,15*8=120) */
  27.     // lcd_draw_string(x, y, buf, BLUE);  /* 显示字符串 */
  28. }

  29. int main(void)
  30. {
  31.     uint8_t t = 0;
  32.     float temperature;                          /* 温度值 */
  33.     short acc_data[3];                          /* 加速度传感器原始数据 */
  34.     short gyro_data[3];                         /* 陀螺仪原始数据 */

  35.     sysctl_pll_set_freq(SYSCTL_PLL0, 800000000);
  36.     sysctl_pll_set_freq(SYSCTL_PLL1, 400000000);
  37.     sysctl_pll_set_freq(SYSCTL_PLL2, 45158400);
  38.     sysctl_set_power_mode(SYSCTL_POWER_BANK6, SYSCTL_POWER_V18);
  39.     sysctl_set_power_mode(SYSCTL_POWER_BANK7, SYSCTL_POWER_V18);
  40.     sysctl_set_spi0_dvp_data(1);

  41.     lcd_init();
  42.     lcd_set_direction(DIR_YX_LRUD);

  43.     /* 初始化 IMU */
  44.     while (sh3001_init())     /* 检测不到SH3001 */
  45.     {
  46.         msleep(10);
  47.     }
  48.     msleep(500);

  49.     /* 清空LCD缓存区 */
  50.     for (size_t i = 0; i < 320 * 240; i++)
  51.     {
  52.         lcd_gram = 0xFFFF;
  53.     }
  54.                               
  55.     while (1)
  56.     {
  57.         t++;
  58.         sh3001_get_imu_compdata(acc_data, gyro_data); /* 读取原始数据 */

  59.         if (t == 20)
  60.         {        
  61.             temperature = sh3001_get_tempdata();    /* 读取温度值 */
  62.             printf("\r\ntemp=%.2f\r\n", temperature);
  63.             // lcd_clear(WHITE);
  64.             user_show_data(30, 10, "TEMP  :", temperature);
  65.             user_show_data(30, 30, "ACC_X :", acc_data[0]);
  66.             user_show_data(30, 50, "ACC_Y :", acc_data[1]);
  67.             user_show_data(30, 70, "ACC_Z :", acc_data[2]);
  68.             user_show_data(30, 90, "GYRO_X:", gyro_data[0]);
  69.             user_show_data(30, 110, "GYRO_Y:", gyro_data[1]);
  70.             user_show_data(30, 130, "GYRO_Z:", gyro_data[2]);
  71.             
  72.             printf("ACC_X:%d\r\n", acc_data[0]);
  73.             printf("ACC_Y:%d\r\n", acc_data[1]);
  74.             printf("ACC_Y:%d\r\n", acc_data[2]);
  75.             printf("GYRO_X:%d\r\n", gyro_data[0]);
  76.             printf("GYRO_Y:%d\r\n", gyro_data[1]);
  77.             printf("GYRO_Z:%d\r\n", gyro_data[2]);
  78.             lcd_draw_picture(0, 0, 320, 240, (uint16_t *)lcd_gram);
  79.             t = 0;
  80.         }
  81.     }
  82. }
复制代码
       可以看到我们先定义一个uint16_t 类型,长度为320*240的数组,用于LCD的图像缓存。
       user_show_data函数用于格式化显示我们需要显示的数据,里面我们也保留了LCD用打点的方式显示图片,大家可以取消注释对比下显示的速度差异。
       最后是main函数的内容,我们先定义温度和六轴原始数据的变量,接着是初始化SH3001,SH3001初始化后面有个清空LCD缓存区的操作,因为申请的内存里面数据的初值是不确定的,所以我们需要格式化下。
       最后在一个循环中从SH3001读取温度值、加速度值和陀螺仪值并通过LCD显示和打印输出。

       17.4 运行验证
       将DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中,可以看到“串行终端”窗口中输出了一系列信息,如下图所示:

第十七章 六轴传感器19231.png
图17.4.2 LCD显示数据

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

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-10-16 16:07

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

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