本帖最后由 正点原子官方 于 2024-10-9 09:59 编辑
第十七章 六轴传感器——原始数据读取实验
本章将介绍板载六轴传感器的使用,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总线挂载多个器件的示意图,如下图所示。 图17.1.1.1 IIC总线挂载多个器件 下面来学习IIC总线协议,IIC总线时序图如下所示: 图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的基本的读写通讯过程,包括主机写数据到从机即写操作,主机到从机读取数据即读操作。下面先看一下写操作通讯过程图,如下图所示。 图17.1.1.3 写操作通讯过程图 主机首先在IIC总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的数据。主机接着发送从机地址+0(写操作)组成的8bit数据,所有从机接收到该8bit数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机在总线上接收到有应答信号后,才能继续向从机发送数据。注意:IIC总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。 接着讲解一下IIC总线的读操作过程,先看一下读操作通讯过程图,如下图所示。 图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的配置初始化,该函数原型及参数描述如下代码所示: void i2c_init(i2c_device_number_t i2c_num, uint32_t slave_address, uint32_t address_width, uint32_t i2c_clk); /* IIC设备号配置参数 */ typedef enum _i2c_device_number { I2C_DEVICE_0, I2C_DEVICE_1, I2C_DEVICE_2, I2C_DEVICE_MAX, } i2c_device_number_t; 该函数共有四个配置参数,第一个为IIC设备号,第二个为从机设备地址,第三个是配置地址宽度为7bit还是10bit,我们一般使用的是7bit,第四个参数为配置IIC时钟,IIC初始化完成之后就能够使用IIC对应的引脚发送和接收数据。 下面介绍IIC发送数据的API函数。 2,i2c_send_data函数 该函数用于发送数据,如下代码所示: 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函数 该函数用于接收数据,如下代码所示: 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传感器的检测轴,如下图所示: 图17.1.2.1 SH3001检测轴方向 更多有关SH3001的芯片特性以及内部寄存器描述,请参考SH3001的数据手册——《SH3001.pdf》,读者可在A盘à硬件资料à芯片资料下找到这份文档。 为了方便大家使用正点原子DNK210开发板板载的SH3001六轴传感器,正点原子团队为SH3001编写了一份驱动代码,我们会在17.3小节介绍。 SH3001的内部框图如图所示: 图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比较重要的寄存器。 l 陀螺仪配置寄存器(GYRO_CONFIG) 陀螺仪配置寄存器有6个,寄存器地址分别是0x28、0x29、0x2B、0x8F、0x9F、0xAF,用来配置陀螺仪量程等参数,描述如下图所示; 图17.1.2.3 GYRO_CONFIG_0寄存器 图17.1.2.4 GYRO_CONFIG_1寄存器 图17.1.2.5 GYRO_CONFIG_2寄存器 图17.1.2.6 GYRO_CONFIG_3寄存器 图17.1.2.7 GYRO_CONFIG_4寄存器
图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。 l 加速度即配置寄存器(ACC_CONFIG) 加速度配置寄存器有4个,寄存器地址分别是:0x22、0x23、0x25、0x26,用来配置加速度计量程等参数,描述如下图所示: 图17.1.2.9 ACC_CONFIG_0寄存器 图17.1.2.10 ACC_CONFIG_1寄存器 图17.1.2.11 ACC_CONFIG_2寄存器 图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芯片连接原理图,如下图所示: 图17.2.3.1 SH3001连接原理图 17.3 程序设计 17.3.1 IIC驱动 为了方便使用IIC进行数据收发,我们重新构建了IIC相关驱动函数存放于iic.c和iic.h文件(区别IIC的库文件i2c.c和i2c.h), #define ADDRESS_WIDTH 7 #define I2C_CLK_SPEED 400000 这两个宏分别用于设置IIC地址的宽度和IIC的时钟频率。 /** * @param addr : 从机地址 * @param reg : 寄存器命令 * @param length : 数据长度 * @param data_buf: 要写入的数据 * @retval 返回值 : 0,成功 * 1,失败 */ uint16_t i2c_hd_write(uint8_t addr, uint8_t reg, uint16_t length, uint8_t *data_buf) { uint16_t error = 1; uint8_t data[length + 1];
if (_current_addr != addr) { i2c_hardware_init(addr); }
data[0] = reg; for (size_t i = 0; i < length; i++) { data[i+1] = *data_buf; data_buf++; }
error = i2c_send_data(I2C_DEVICE_0, data, length + 1); return error; } 我们通过向SH3001寄存器写数据便可配置传感器的功能,SH3001的寄存器都由相应的指令去访问,参考SH3001的数据手册——《SH3001.pdf》了解,所以我们写数据前需要先发送一个8bit的寄存器指令,等待传感器应答后才能发送我们要写入的数据。 /** * @param addr : 从机地址 * @param reg : 寄存器命令 * @param length : 数据长度 * @param data_buf: 要读取的数据 * @retval 返回值 : 0,成功 * 1,失败 */ uint16_t i2c_hd_read(uint8_t addr, uint8_t reg, uint16_t length, uint8_t *data_buf) { if (_current_addr != addr) { i2c_hardware_init(addr); } uint16_t error = 1;
error = i2c_recv_data(I2C_DEVICE_0, ®, 1, data_buf, length); return error; } 读函数和写函数的前一部分执行过程有点相似,我们在发送指令后进入接收状态,MCU每次接收到数据后发送应答信号,从机设备便会继续发送数据,等待接收到我们所需的数据长度时发送非应答信号便可停止接收。 17.3.2 SH3001驱动 这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。SH3001驱动源码包括两个文件:sh3001.c和sh3001.h。 /*****************************HARDWARE-PIN*********************************/ /* 硬件IO口,与原理图对应 */ #define PN_IMU_SCL (22) #define PIN_IMU_SDA (23) #define PIN_IMU_INT (20)
/*****************************SOICMWARE-GPIO********************************/ /* 软件GPIO口,与程序对应 */ #define IMU_INT_GPIONUM (0)
/*****************************FUNC-GPIO************************************/ /* GPIO口的功能,绑定到硬件IO口 */ #define FUNC_IMU_INT (FUNC_GPIOHS0 + IMU_INT_GPIONUM) #define FUNC_ICM_SCL (FUNC_I2C0_SCLK) #define FUNC_ICM_SDA (FUNC_I2C0_SDA) 这里是DNK210开发板SH3001传感器的IIC引脚定义和功能编号,下面介绍SH3001的相关寄存器,其定义如下: #define SH3001_ACC_XL (0x00) #define SH3001_ACC_XH (0x01) #define SH3001_ACC_YL (0x02) #define SH3001_ACC_YH (0x03) #define SH3001_ACC_ZL (0x04) #define SH3001_ACC_ZH (0x05) …/* 此处省略部分代码 */ #define SH3001_GYRO_CONF3 (0x8F) #define SH3001_GYRO_CONF4 (0x9F) #define SH3001_GYRO_CONF5 (0xAF) #define SH3001_CHIP_ID1 (0xDF) 下面来看一下sh3001_read_nbytes函数,实现SH3001芯片指定地址读取N字节数据,代码如下: /** * @brief 从SH3001读取N字节数据 * @NOTE SH3001 的命令发送,也是用该函数实现(不带参数的命令, 也会有一个状态寄存器需要读取) * @param devaddr : 寄存器地址 * @param regaddr : 寄存器命令 * @param length : 读取长度 * @param readbuf : 数据存储buf * @retval 0, 操作成功 * 其他, 操作失败 */ unsigned char sh3001_read_nbytes(uint8_t devaddr, uint8_t regaddr, uint16_t length, uint8_t *readbuf) { i2c_hd_read(devaddr, regaddr, length, readbuf); return (SH3001_TRUE); } 该函数直接调用i2c_hd_read函数即可,非常方便使用,成功返回0;IIC读取过程可参考17.1.1小节。 接下来看一下sh3001_write_nbytes函数,实现从SH3001芯片指定地址写入数据,其定义如下: /** * @brief SH3001写入N字节数据 * @param devaddr : 寄存器地址 * @param regaddr : 寄存器命令 * @param length : 写入长度 * @param writebuf : 数据存储buf * @retval 0, 操作成功 * 其他, 操作失败 */ unsigned char sh3001_write_nbytes(uint8_t devaddr, uint8_t regaddr, uint16_t length, uint8_t *writebuf) {
i2c_hd_write(devaddr, regaddr, length, writebuf); return (SH3001_TRUE); } 该函数直接调用i2c_hd_write函数即可,函数返回0表示发送成功,这里也不多介绍。 最后,我们介绍一下读取数据函数,代码如下: /** * @brief 读温度值 * @param 无 * @retval 温度值,单位为℃(float类型) */ float sh3001_get_tempdata(void) { unsigned char regdata[2] = {0}; unsigned short int tempref[2] = {0};
sh3001_read_nbytes(SH3001_ADDRESS, SH3001_TEMP_CONF0, 2, ®data[0]); tempref[0] = ((unsigned short int)(regdata[0] & 0x0F) << 8) | regdata[1];
sh3001_read_nbytes(SH3001_ADDRESS, SH3001_TEMP_ZL, 2, ®data[0]); tempref[1] = ((unsigned short int)(regdata[1] & 0x0F) << 8) | regdata[0];
return ( (((float)(tempref[1] - tempref[0])) / 16.0f) + 25.0f ); }
/** * @brief 读取补偿后SH3001陀螺仪和加速度的数据(推荐使用) * @param accdata[3] : acc X,Y,Z; * @param gyrodata[3] : gyro X,Y,Z; * @retval 无 */ void sh3001_get_imu_compdata(short accdata[3], short gyrodata[3]) { unsigned char regdata[15] = {0}; unsigned char paramp; int acctemp[3], gyrotemp[3];
sh3001_read_nbytes(SH3001_ADDRESS, SH3001_ACC_XL, 15, regdata); accdata[0] = ((short)regdata[1] << 8) | regdata[0]; accdata[1] = ((short)regdata[3] << 8) | regdata[2]; accdata[2] = ((short)regdata[5] << 8) | regdata[4]; gyrodata[0] = ((short)regdata[7] << 8) | regdata[6]; gyrodata[1] = ((short)regdata[9] << 8) | regdata[8]; gyrodata[2] = ((short)regdata[11] << 8) | regdata[10]; paramp = regdata[14] & 0x1F;
acctemp[0] = (int)( accdata[0] + \ accdata[1] * ((float)g_compcoef.cXY / 1024.0f) + \ accdata[2] * ((float)g_compcoef.cXZ / 1024.0f) );
acctemp[1] = (int)( accdata[0] * ((float)g_compcoef.cYX / 1024.0f) + \ accdata[1] + \ accdata[2] * ((float)g_compcoef.cYZ / 1024.0f) );
acctemp[2] = (int)( accdata[0] * ((float)g_compcoef.cZX / 1024.0f) + \ accdata[1] * ((float)g_compcoef.cZY / 1024.0f) + \ accdata[2] );
if (acctemp[0] > 32767) { acctemp[0] = 32767; } else if (acctemp[0] < -32768) { acctemp[0] = -32768; }
if (acctemp[1] > 32767) { acctemp[1] = 32767; } else if (acctemp[1] < -32768) { acctemp[1] = -32768; }
if (acctemp[2] > 32767) { acctemp[2] = 32767; } else if (acctemp[2] < -32768) { acctemp[2] = -32768; }
accdata[0] = (short)acctemp[0]; accdata[1] = (short)acctemp[1]; accdata[2] = (short)acctemp[2];
gyrotemp[0] = gyrodata[0] - (paramp - g_compcoef.paramP0) * g_compcoef.jX * g_compcoef.xMulti; gyrotemp[1] = gyrodata[1] - (paramp - g_compcoef.paramP0) * g_compcoef.jY * g_compcoef.yMulti; gyrotemp[2] = gyrodata[2] - (paramp - g_compcoef.paramP0) * g_compcoef.jZ * g_compcoef.zMulti;
if (gyrotemp[0] > 32767) { gyrotemp[0] = 32767; } else if (gyrotemp[0] < -32768) { gyrotemp[0] = -32768; }
if (gyrotemp[1] > 32767) { gyrotemp[1] = 32767; } else if (gyrotemp[1] < -32768) { gyrotemp[1] = -32768; }
if (gyrotemp[2] > 32767) { gyrotemp[2] = 32767; } else if (gyrotemp[2] < -32768) { gyrotemp[2] = -32768; }
gyrodata[0] = (short)gyrotemp[0]; gyrodata[1] = (short)gyrotemp[1]; gyrodata[2] = (short)gyrotemp[2];
//printf("%d %d %d %d %d %d\r\n", accdata[0], accdata[1], accdata[2], gyrodata[0], gyrodata[1], gyrodata[2]); } sh3001_gettemp_data函数:读取转换后的温度数据。 sh3001_getimucompdata函数:调用sh3001_read_nbytes读数据,读取加速度计和陀螺仪的x,y,z轴原始数据。 下面看一下SH3001的初始化函数,其定义如下: /** * @brief 初始化SH3001接口 * @param 无 * @retval SH3001_TRUE, 成功 * SH3001_FALSE, 异常 */ unsigned char sh3001_init(void) { unsigned char i = 0; unsigned char regdata = 0;
sh3001_hardware_init(); /* 绑定IIC功能引脚 */ i2c_hardware_init(SH3001_ADDRESS); /* 初始化IIC */ msleep(10);
/* 读取CHIP ID */ do { sh3001_read_nbytes(SH3001_ADDRESS, SH3001_CHIP_ID, 1, ®data); }while ((regdata != 0x61) && (i++ < 3));
if ((regdata != 0x61)) { printf("SH3001 CHIP ID:0X%X\r\n", regdata); /* 打印错误的ID */ return SH3001_FALSE; }
sh3001_module_reset(); /* 重置内部模块 */
/* ACC配置: 500Hz, 16G, cut off Freq(BW)=500*0.25Hz=125Hz, enable filter; */ sh3001_acc_config(SH3001_ODR_500HZ, SH3001_ACC_RANGE_16G, SH3001_ACC_ODRX025, SH3001_ACC_FILTER_EN);
/* GYRO配置: 500Hz, X\Y\Z 2000deg/s, cut off Freq(BW)=181Hz, enable filter; */ sh3001_gyro_config( SH3001_ODR_500HZ, SH3001_GYRO_RANGE_2000, SH3001_GYRO_RANGE_2000, SH3001_GYRO_RANGE_2000, SH3001_GYRO_ODRX00, SH3001_GYRO_FILTER_EN);
/* 温度配置: 输出速率63Hz, 使能温度测量 */ sh3001_temp_config(SH3001_TEMP_ODR_63, SH3001_TEMP_EN);
/* SH3001进入正常工作模式 */ sh3001_switch_powermode(SH3001_NORMAL_MODE);
/* 读取补偿系数 */ sh3001_comp_init(&g_compcoef);
return SH3001_TRUE; } 在初始化函数中,我们主要对该模块的加速度、陀螺仪,还有温度传感器的使能和设置,并且选择normal模式。 17.3.3 图片处理代码 上一章我们讲解了LCD显示字符串的实验,细心的小伙伴们可以发现LCD上显示一个一个字符的速度非常慢,因为我们在显示字符串过程中需要一个一个点的定位和LCD内存的读写,两个设备的频繁通讯非常耗时,所以我们K210采用以图片的形式显示各种信息,我们创建一个RGB565图片缓存区,要显示什么内容直接对缓存区的数据操作即可,最后一次写入到LCD显示的GRAM中。 正点原子团队编写了一份图片处理代码用于RGB565和RGB888的图片处理,源码存放在image_process.c和image_process.h文件,内容较多,我们将函数的功能以表格的形式简单介绍下。读者如果对图片处理过程感兴趣,可自行查看源码学习。 函数名及功能如下表所示: | | | | | | | | | | | | | | | | | | | | | | | | | | draw_fill_rectangle_image | | | |
表17.3.3.1 图片处理函数功能表 以上函数基本满足我们例程中涉及的图片处理所需要的功能。 17.3.4 main.c代码 main.c中的代码如下所示: #include "sleep.h" #include "sysctl.h" #include "image_process.h" #define LCD_SPI_CLK_RATE 15000000 #include "./BSP/SH3001/sh3001.h" #include "./BSP/IIC/iic.h" #include "./BSP/LCD/lcd.h" #include "./BSP/LCD/lcdfont.h"
static uint16_t lcd_gram[320 * 240] __attribute__((aligned(32))); /* 定义一个LCD显示缓存区 */
/** * @brief 显示数据 * @param x, y : 坐标 * @param title: 标题 * @param data: 角度 * @retval 无 */ void user_show_data(uint16_t x, uint16_t y, char * title, short data) { char buf[15];
sprintf(buf,"%s%d", title, data); /* 格式化输出 */ draw_fill_rectangle_image(lcd_gram, 320, x, y, x + 120, y + 16, WHITE); /* 清除上次缓存数据 */ draw_string_rgb565_image(lcd_gram, 320, 240, x, y, buf, BLUE); /* 将字符串写入LCD缓存区 */
// lcd_draw_fill_rectangle(x, y, x + 120, y + 16, WHITE); /* 清除上次数据(最多显示15个字符,15*8=120) */ // lcd_draw_string(x, y, buf, BLUE); /* 显示字符串 */ }
int main(void) { uint8_t t = 0; float temperature; /* 温度值 */ short acc_data[3]; /* 加速度传感器原始数据 */ short gyro_data[3]; /* 陀螺仪原始数据 */
sysctl_pll_set_freq(SYSCTL_PLL0, 800000000); sysctl_pll_set_freq(SYSCTL_PLL1, 400000000); sysctl_pll_set_freq(SYSCTL_PLL2, 45158400); sysctl_set_power_mode(SYSCTL_POWER_BANK6, SYSCTL_POWER_V18); sysctl_set_power_mode(SYSCTL_POWER_BANK7, SYSCTL_POWER_V18); sysctl_set_spi0_dvp_data(1);
lcd_init(); lcd_set_direction(DIR_YX_LRUD);
/* 初始化 IMU */ while (sh3001_init()) /* 检测不到SH3001 */ { msleep(10); } msleep(500);
/* 清空LCD缓存区 */ for (size_t i = 0; i < 320 * 240; i++) { lcd_gram = 0xFFFF; }
while (1) { t++; sh3001_get_imu_compdata(acc_data, gyro_data); /* 读取原始数据 */
if (t == 20) { temperature = sh3001_get_tempdata(); /* 读取温度值 */ printf("\r\ntemp=%.2f\r\n", temperature); // lcd_clear(WHITE); user_show_data(30, 10, "TEMP :", temperature); user_show_data(30, 30, "ACC_X :", acc_data[0]); user_show_data(30, 50, "ACC_Y :", acc_data[1]); user_show_data(30, 70, "ACC_Z :", acc_data[2]); user_show_data(30, 90, "GYRO_X:", gyro_data[0]); user_show_data(30, 110, "GYRO_Y:", gyro_data[1]); user_show_data(30, 130, "GYRO_Z:", gyro_data[2]);
printf("ACC_X:%d\r\n", acc_data[0]); printf("ACC_Y:%d\r\n", acc_data[1]); printf("ACC_Y:%d\r\n", acc_data[2]); printf("GYRO_X:%d\r\n", gyro_data[0]); printf("GYRO_Y:%d\r\n", gyro_data[1]); printf("GYRO_Z:%d\r\n", gyro_data[2]); lcd_draw_picture(0, 0, 320, 240, (uint16_t *)lcd_gram); t = 0; } } } 可以看到我们先定义一个uint16_t 类型,长度为320*240的数组,用于LCD的图像缓存。 user_show_data函数用于格式化显示我们需要显示的数据,里面我们也保留了LCD用打点的方式显示图片,大家可以取消注释对比下显示的速度差异。 最后是main函数的内容,我们先定义温度和六轴原始数据的变量,接着是初始化SH3001,SH3001初始化后面有个清空LCD缓存区的操作,因为申请的内存里面数据的初值是不确定的,所以我们需要格式化下。 最后在一个循环中从SH3001读取温度值、加速度值和陀螺仪值并通过LCD显示和打印输出。 17.4 运行验证 将DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中,可以看到“串行终端”窗口中输出了一系列信息,如下图所示: 图17.4.1 “串行终端”窗口打印输出 可以看到,“串行终端”不断输出从SH3001获取到的温度值、加速度值和陀螺仪值。 同时LCD也不断刷新数据显示,如下图所示: 图17.4.2 LCD显示数据 |