• 正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-24.5,6 SPI驱动实验-ICM20608 ADC采样值


    前言:

    本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。

    引用:

    正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com

    《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》

    正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档

    SPI学习参考资料:

    简述SPI通信协议-01_cpha选择为第一个边沿-CSDN博客

    SPI中的CPHA,CPOL详解-CSDN博客

    一文搞懂SPI通信协议_spi协议-CSDN博客

    摩托罗拉 《SPI Block Guide V03.06》 手册

    链接:https://pan.baidu.com/s/1_mvR5AD0-OBI2bYyx2i4Sw?pwd=f4bo 
    提取码:f4bo

    正文:

    本文是 “正点原子[第二期]Linux之ARM(MX6U)裸机篇--第24讲 SPI驱动。本节将参考正点原子的视频教程第24讲和配套的正点原子开发指南文档进行学习。

    0. 概述

    通I2C一样,SPI是很常用的通信接口,也可以通过SPI来连接众多的传感器。相比I2C接口,SPI接口的通信速度很快,I2C最多400KHz,但是SPI可以到达几十MHz。I.MX6U 也有4个SPI接口,可以通过这4个SPI接口来连接一些SPI外设。I.MX6U-ALHPA使用SPI3接口连接了一个6周传感器 ICM-20608,本章我们就来学习如何使用I.MX6U的SPI接口来驱动ICM-20608,读取ICM-20608的六轴数据。

    1. ICM20608 6轴传感器量程

    icm20608 6轴传感器,支持陀螺仪x,y,z三轴的角速度测量和加速度计x,y,z三轴的加速度计测量。icm20608 陀螺仪的量程范围可选配置为±250,±500,±1000和±2000 °/s,角速度计的量程范围可选配置为±2g,±4g,±8g和±16g。icm20608 x,y,z 轴的输出是一个 16位的 ADC 采样值,16位值的有符号数(补码)表示范围为 -32768~32767,一共可以表示65536个数值,那么采样值和量程范围的关系是什么哪?

    参考链接:

    IMX6ULL裸机篇之SPI实验-ICM20608传感器_icm20608数据手册-CSDN博客

    • 陀螺仪量程

    如果陀螺仪所设置的分辨率范围为 ±250,即 -250~+250,也就是 500°/s
    ADC数据的位数为 16位,即 0~65535,也就是 65536。那么一度对应多大的数据呢?
    65536/500 = 131.07

    举例说明:

    如果所设置的分辨率范围为 ±250,读取到的  ADC值是 1000,那么陀螺仪的角速度是多少?

    当前陀螺仪的角速度为:

    1000 / 131  = 7.6°/s

    • 加速度计量程

    加速度计计算公式与陀螺仪相似。

     举例说明:
    如果加速度计设置的分辨率范围为 ±2,即 -2~+2,也就是 4
    ADC值的位数为 16位,即 0~65535,也就是 65536。一度则对应多大的 ADC值呢?
    65536/4 = 16384

    如果此时读取到的 ADC值为 16384,则这时的加速度计的加速度是多少?
    16384 / 16384  = 1g

    2. ICM20608传感器ADC采样值计算

    ADC值是16位,表示值范围为0~65535,一共65536个;量程范围为 ±2000,一共是4000,计算出来多少个ADC值代表一个角速度:

    16.4 = 65536/(4000°/s)

    例如,icm20608 加速度的量程范围选择为±16g,icm20608 加速度计z轴读取出来的值为2041,因为icm20608 ADC的值是16位其中存放的是一个有符号短整型数(unsigned short)的补码形式,计算处量程范围为±16g,icm20608 加速度计z轴ADC的值为2041的实际加速读值为:

    acc_z = (ADC值 * 量程)/ADC位数无符号整型范围

               =(2041 * 32)/65536

               = 0.99g 

    其中ADC的值是从icmp20608寄存器里读取出来相应轴的ADC采样值,是有符号短整型数,有正负符号。

    3. 源码编写

    源码编写读取icm20608传感器6个轴的采样值,源码如下:

    bsp_icm2060.h

    1. #ifndef __BSP_20608_H__
    2. #define __BSP_20608_H__
    3. #include "imx6u.h"
    4. #define ICM20608G_ID 0xAF
    5. #define ICM20608N_ID 0xAE
    6. #define ICM20608_REG_SMLPRT_DIV 0x19
    7. #define ICM20608_REG_CONFIG 0x1A
    8. #define ICM20608_REG_GYRO_CONFIG 0x1B
    9. #define ICM20608_REG_ACCEL_CONFIG 0x1C
    10. #define ICM20608_REG_ACCEL_CONFIG2 0x1D
    11. #define ICM20608_REG_LP_MODE_CFG 0x1E
    12. #define ICM20608_REG_ACCEL_WOM_THR 0x1F
    13. #define ICM20608_REG_FIFO_EN 0x23
    14. #define ICM20608_REG_ACCEL_XOUT_H 0x3B
    15. #define ICM20608_REG_ACCEL_XOUT_L 0x3C
    16. #define ICM20608_REG_ACCEL_YOUT_H 0x3D
    17. #define ICM20608_REG_ACCEL_YOUT_L 0x3E
    18. #define ICM20608_REG_ACCEL_ZOUT_H 0x3F
    19. #define ICM20608_REG_ACCEL_ZOUT_L 0x40
    20. #define ICM20608_REG_TEMP_OUT_H 0x41
    21. #define ICM20608_REG_TEMP_OUT_L 0x42
    22. #define ICM20608_REG_GYRO_XOUT_H 0x43
    23. #define ICM20608_REG_GYRO_XOUT_L 0x44
    24. #define ICM20608_REG_GYRO_YOUT_H 0x45
    25. #define ICM20608_REG_GYRO_YOUT_L 0x46
    26. #define ICM20608_REG_GYRO_ZOUT_H 0x47
    27. #define ICM20608_REG_GYRO_ZOUT_L 0x48
    28. #define ICM20608_REG_PWR_MGMT_1 0x6B
    29. #define ICM20608_REG_PWR_MGMT_2 0x6C
    30. #define ICM20608_REG_WHO_AM_I 0x75
    31. #define ICM20608_REG_XA_OFFSET_H 0x77
    32. #define ICM20608_REG_XA_OFFSET_L 0x78
    33. #define ICM20608_REG_YA_OFFSET_H 0x7A
    34. #define ICM20608_REG_YA_OFFSET_L 0x7B
    35. #define ICM20608_REG_ZA_OFFSET_H 0x7D
    36. #define ICM20608_REG_ZA_OFFSET_L 0x7E
    37. copy from 正点原子示例程序
    38. /* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */
    39. #define ICM20_SELF_TEST_X_GYRO 0x00
    40. #define ICM20_SELF_TEST_Y_GYRO 0x01
    41. #define ICM20_SELF_TEST_Z_GYRO 0x02
    42. #define ICM20_SELF_TEST_X_ACCEL 0x0D
    43. #define ICM20_SELF_TEST_Y_ACCEL 0x0E
    44. #define ICM20_SELF_TEST_Z_ACCEL 0x0F
    45. /* 陀螺仪静态偏移 */
    46. #define ICM20_XG_OFFS_USRH 0x13
    47. #define ICM20_XG_OFFS_USRL 0x14
    48. #define ICM20_YG_OFFS_USRH 0x15
    49. #define ICM20_YG_OFFS_USRL 0x16
    50. #define ICM20_ZG_OFFS_USRH 0x17
    51. #define ICM20_ZG_OFFS_USRL 0x18
    52. #define ICM20_SMPLRT_DIV 0x19
    53. #define ICM20_CONFIG 0x1A
    54. #define ICM20_GYRO_CONFIG 0x1B
    55. #define ICM20_ACCEL_CONFIG 0x1C
    56. #define ICM20_ACCEL_CONFIG2 0x1D
    57. #define ICM20_LP_MODE_CFG 0x1E
    58. #define ICM20_ACCEL_WOM_THR 0x1F
    59. #define ICM20_FIFO_EN 0x23
    60. #define ICM20_FSYNC_INT 0x36
    61. #define ICM20_INT_PIN_CFG 0x37
    62. #define ICM20_INT_ENABLE 0x38
    63. #define ICM20_INT_STATUS 0x3A
    64. /* 加速度输出 */
    65. #define ICM20_ACCEL_XOUT_H 0x3B
    66. #define ICM20_ACCEL_XOUT_L 0x3C
    67. #define ICM20_ACCEL_YOUT_H 0x3D
    68. #define ICM20_ACCEL_YOUT_L 0x3E
    69. #define ICM20_ACCEL_ZOUT_H 0x3F
    70. #define ICM20_ACCEL_ZOUT_L 0x40
    71. /* 温度输出 */
    72. #define ICM20_TEMP_OUT_H 0x41
    73. #define ICM20_TEMP_OUT_L 0x42
    74. /* 陀螺仪输出 */
    75. #define ICM20_GYRO_XOUT_H 0x43
    76. #define ICM20_GYRO_XOUT_L 0x44
    77. #define ICM20_GYRO_YOUT_H 0x45
    78. #define ICM20_GYRO_YOUT_L 0x46
    79. #define ICM20_GYRO_ZOUT_H 0x47
    80. #define ICM20_GYRO_ZOUT_L 0x48
    81. #define ICM20_SIGNAL_PATH_RESET 0x68
    82. #define ICM20_ACCEL_INTEL_CTRL 0x69
    83. #define ICM20_USER_CTRL 0x6A
    84. #define ICM20_PWR_MGMT_1 0x6B
    85. #define ICM20_PWR_MGMT_2 0x6C
    86. #define ICM20_FIFO_COUNTH 0x72
    87. #define ICM20_FIFO_COUNTL 0x73
    88. #define ICM20_FIFO_R_W 0x74
    89. #define ICM20_WHO_AM_I 0x75
    90. /* 加速度静态偏移 */
    91. #define ICM20_XA_OFFSET_H 0x77
    92. #define ICM20_XA_OFFSET_L 0x78
    93. #define ICM20_YA_OFFSET_H 0x7A
    94. #define ICM20_YA_OFFSET_L 0x7B
    95. #define ICM20_ZA_OFFSET_H 0x7D
    96. #define ICM20_ZA_OFFSET_L 0x7E
    97. copy from 正点原子示例程序
    98. struct icm20608_device {
    99. /* ADC 采样值,原始数据 */
    100. signed short accel_x_adc;
    101. signed short accel_y_adc;
    102. signed short accel_z_adc;
    103. signed short temperature_adc;
    104. signed short gyro_x_adc;
    105. signed short gyro_y_adc;
    106. signed short gyro_z_adc;
    107. /* 计算得到的实际值 */
    108. signed short accel_x_act;
    109. signed short accel_y_act;
    110. signed short accel_z_act;
    111. signed short temperature_act;
    112. signed short gyro_x_act;
    113. signed short gyro_y_act;
    114. signed short gyro_z_act;
    115. };
    116. extern struct icm20608_device icm20608_dev;
    117. #define ICM20608_CSN(n) do{n ? gpio_pinwrite(GPIO1, 20, 1) : gpio_pinwrite(GPIO1, 20, 0);} while(0)
    118. void icm20608_init(void);
    119. unsigned char icm20608_read_reg(ECSPI_Type *base, unsigned char reg);
    120. void icm20608_write_reg(ECSPI_Type *base, unsigned char reg, unsigned char value);
    121. void icm20608_read_len(ECSPI_Type *base, unsigned char reg, unsigned char len, unsigned char *buf);
    122. void icm20608_read_dev_accel_gyro(struct icm20608_device *idev);
    123. int icm20608_gyroscale_get(void);
    124. int icm20608_accelscale_get(void);
    125. #endif

    bsp_icm2060.c

    1. #include "bsp_icm20608.h"
    2. #include "bsp_gpio.h"
    3. #include "bsp_spi.h"
    4. #include "bsp_delay.h"
    5. #include "stdio.h"
    6. /* 初始化ICM20608 */
    7. void icm20608_init(void)
    8. {
    9. gpio_pin_config_t config;
    10. /* 1.SPI 引脚的初始化 */
    11. IOMUXC_SetPinMux(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0); /* 复用为ECSPI3_SCLK */
    12. IOMUXC_SetPinMux(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0); /* 复用为ECSPI3_MOSI */
    13. IOMUXC_SetPinMux(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0); /* 复用为ECSPI3_MISO */
    14. IOMUXC_SetPinConfig(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0x10B1); /* IO特性 */
    15. IOMUXC_SetPinConfig(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0x10B1); /* IO特性 */
    16. IOMUXC_SetPinConfig(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0x10B1); /* IO特性 */
    17. IOMUXC_SetPinMux(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0); /* 复用为GPIO1_20 */
    18. IOMUXC_SetPinConfig(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0x10B0); /* IO特性 */
    19. config.direction = kGPIO_DigitalOutput;
    20. config.outputLogic = 0;
    21. gpio_init(GPIO1, 20, &config);
    22. /* 2.SPI控制器的初始化 */
    23. spi_init(ECSPI3);
    24. /* 3.icm20608的初始化 */
    25. /* 睡眠模式,手册中说icm20608复位之后默认是sleep模式 */
    26. icm20608_write_reg(ECSPI3, ICM20608_REG_PWR_MGMT_1, 0x80); /* 复位 */
    27. delay_ms(50);
    28. icm20608_write_reg(ECSPI3, ICM20608_REG_PWR_MGMT_1, 0x01); /* 关闭睡眠模式,时钟选择 */
    29. delay_ms(50);
    30. unsigned char value;
    31. value = icm20608_read_reg(ECSPI3, ICM20608_REG_WHO_AM_I);
    32. printf("ICM20608G ICM20608_REG_WHO_AM_I=0x%02x\r\n", value);
    33. icm20608_write_reg(ECSPI3, ICM20608_REG_SMLPRT_DIV, 0x0); /* 设置采样率输出速率 */
    34. icm20608_write_reg(ECSPI3, ICM20608_REG_GYRO_CONFIG, 0x18); /* 陀螺仪量程FS(Full Scale), ±2000dps*/
    35. icm20608_write_reg(ECSPI3, ICM20608_REG_ACCEL_CONFIG, 0x18); /* 加速度计量程FS(Full Scale), ±16g */
    36. icm20608_write_reg(ECSPI3, ICM20608_REG_CONFIG, 0x04); /* 陀螺仪, BW=20Hz, 低通滤波器DLPF(Digital Low Pass Filter) */
    37. icm20608_write_reg(ECSPI3, ICM20608_REG_ACCEL_CONFIG2, 0x04); /* 加速度, BW=20Hz, 低通滤波器DLPF(Digital Low Pass Filter) */
    38. icm20608_write_reg(ECSPI3, ICM20608_REG_PWR_MGMT_2, 0x00); /* 启用所有陀螺仪,加速度计 X,Y,Z轴 */
    39. icm20608_write_reg(ECSPI3, ICM20608_REG_LP_MODE_CFG, 0x00); /* 关闭低功耗模式,陀螺仪,加速度计的低功耗模式 */
    40. icm20608_write_reg(ECSPI3, ICM20608_REG_FIFO_EN, 0x00); /* 关闭FIFO,陀螺仪,加速度计,温度计,FIFO使能 */
    41. }
    42. unsigned char icm20608_read_reg(ECSPI_Type *base, unsigned char reg)
    43. {
    44. unsigned char rxdata = 0;
    45. reg |= 0x80; /* icmp20608 读,最高位为 1 */
    46. ICM20608_CSN(0); /* 拉低SPI CS片选信号,选中从设备 */
    47. spi_readwrite_onebyte(base, reg); /* icm20608读,寄存器地址 */
    48. rxdata = spi_readwrite_onebyte(base, 0xFF); /* icm20608读,dummy数据*/
    49. ICM20608_CSN(1); /* 拉高SPI CS片选信号,去选中从设备 */
    50. return rxdata;
    51. }
    52. void icm20608_write_reg(ECSPI_Type *base, unsigned char reg, unsigned char value)
    53. {
    54. reg &= ~0x80; /* icmp20608 写,最高位清零 */
    55. ICM20608_CSN(0); /* 拉低SPI CS片选信号,选中从设备 */
    56. spi_readwrite_onebyte(base, reg); /* icm20608写,寄存器地址 */
    57. spi_readwrite_onebyte(base, value); /* icm20608写,数据*/
    58. ICM20608_CSN(1); /* 拉高SPI CS片选信号,去选中从设备 */
    59. }
    60. void icm20608_read_len(ECSPI_Type *base, unsigned char reg, unsigned char len, unsigned char *buf)
    61. {
    62. int i = 0;
    63. // for(i=0; i< len; i++)
    64. // {
    65. // buf[i] = icm20608_read_reg(base, reg+i);
    66. // }
    67. reg |= 0x80; /* icmp20608 读,最高位为 1 */
    68. ICM20608_CSN(0); /* 拉低SPI CS片选信号,选中从设备 */
    69. spi_readwrite_onebyte(base, reg); /* icm20608读,寄存器地址 */
    70. /* 连续读取多个数据 */
    71. for(i=0; i
    72. buf[i] = spi_readwrite_onebyte(base, 0xFF); /* icm20608读,dummy数据*/
    73. }
    74. ICM20608_CSN(1); /* 拉高SPI CS片选信号,去选中从设备 */
    75. }
    76. void icm20608_read_dev_accel_gyro(struct icm20608_device *idev)
    77. {
    78. unsigned char buf[14];
    79. int gyroscale;
    80. int accescale;
    81. icm20608_read_len(ECSPI3, ICM20_ACCEL_XOUT_H, 14, buf);
    82. idev->accel_x_adc = (signed short)((buf[0] << 8) | buf[1]);
    83. idev->accel_y_adc = (signed short)((buf[2] << 8) | buf[3]);
    84. idev->accel_z_adc = (signed short)((buf[4] << 8) | buf[5]);
    85. idev->temperature_adc = buf[6] << 8;
    86. idev->temperature_adc |= buf[7];
    87. idev->gyro_x_adc = (signed short)((buf[8] << 8) | buf[9]);
    88. idev->gyro_y_adc = (signed short)((buf[10] << 8) | buf[11]);
    89. idev->gyro_z_adc = (signed short)((buf[12] << 8) | buf[13]);
    90. gyroscale = icm20608_gyroscale_get();
    91. accescale = icm20608_accelscale_get();
    92. /* 乘上100 用来保存小数,用的时候再除以100 */
    93. idev->accel_x_act = (((signed int)idev->accel_x_adc * accescale)*100)/65536;
    94. idev->accel_y_act = (((signed int)idev->accel_y_adc * accescale)*100)/65536;
    95. idev->accel_z_act = (((signed int)idev->accel_z_adc * accescale)*100)/65536;
    96. idev->gyro_x_act = (((signed int)idev->gyro_x_adc * gyroscale)*100)/65536;
    97. idev->gyro_y_act = (((signed int)idev->gyro_y_adc * gyroscale)*100)/65536;
    98. idev->gyro_z_act = (((signed int)idev->gyro_z_adc * gyroscale)*100)/65536;
    99. }
    100. /*
    101. * @description : 获取陀螺仪分辨率(量程)。
    102. * @param : 无
    103. * @return : 无
    104. */
    105. int icm20608_gyroscale_get(void)
    106. {
    107. unsigned char rxdata;
    108. int gyroScale = 0;
    109. rxdata = icm20608_read_reg(ECSPI3, ICM20608_REG_GYRO_CONFIG);
    110. switch(((rxdata >> 3) & 0x3))
    111. {
    112. case 0:
    113. gyroScale = 500; //gyro_scale ±250dps
    114. break;
    115. case 1:
    116. gyroScale = 1000; //gyro_scale ±500dps
    117. break;
    118. case 2:
    119. gyroScale = 2000; //gyro_scale ±1000dps
    120. break;
    121. case 3:
    122. gyroScale = 4000; //gyro_scale ±2000dps
    123. break;
    124. }
    125. printf("%s() rxdata=%02x gyroScale=%d\r\n", __FUNCTION__, rxdata, gyroScale);
    126. return gyroScale;
    127. }
    128. /*
    129. * @description : 获取加速度计分辨率(量程)。
    130. * @param : 无
    131. * @return : 无
    132. */
    133. int icm20608_accelscale_get(void)
    134. {
    135. unsigned char rxdata;
    136. int accelScale;
    137. rxdata = icm20608_read_reg(ECSPI3, ICM20608_REG_ACCEL_CONFIG);
    138. switch(((rxdata >> 3) & 0x3))
    139. {
    140. case 0:
    141. accelScale = 4; //accel_scale ±2g
    142. break;
    143. case 1:
    144. accelScale = 8; //accel_scale ±4g
    145. break;
    146. case 2:
    147. accelScale = 16; //accel_scale ±8g
    148. break;
    149. case 3:
    150. accelScale = 32; //accel_scale ±16g
    151. break;
    152. }
    153. printf("%s() rxdata=%02x accelScale=%d\r\n", __FUNCTION__, rxdata, accelScale);
    154. return accelScale;
    155. }

    main.c 文件,打印计算并打印出来从icm20608传感器读取出来的6个轴的采样值,并根据icm20608的量程计算出实际的x,y,z轴的加速度,和x,y,z轴的角速度。

    main.c

    1. #include "cc.h"
    2. #include "bsp_clk.h"
    3. #include "bsp_led.h"
    4. #include "bsp_delay.h"
    5. #include "bsp_beep.h"
    6. #include "bsp_gpio.h"
    7. #include "bsp_key.h"
    8. #include "bsp_int.h"
    9. #include "bsp_exti.h"
    10. #include "bsp_epittimer.h"
    11. #include "bsp_keyfilter.h"
    12. #include "bsp_delay.h"
    13. #include "bsp_uart.h"
    14. #include "stdio.h"
    15. #include "bsp_lcd.h"
    16. #include "bsp_lcdapi.h"
    17. #include "bsp_rtc.h"
    18. #include "bsp_ap3216c.h"
    19. #include "bsp_i2c.h"
    20. #include "bsp_spi.h"
    21. #include "bsp_icm20608.h"
    22. char *banner = "========================================================\r\n"
    23. "正点原子I.MX6ULL ALPHA/Mini开发板Linux驱动之ARM裸机开发\r\n" \
    24. "--Date: %s\r\n" \
    25. "--Author: ChenHaoxu, Dimon.chen, 11813202388@qq.com\r\n" \
    26. "========================================================\r\n";
    27. /*
    28. * @description : 使能I.MX6U的硬件NEON和FPU
    29. * @param : 无
    30. * @return : 无
    31. */
    32. void imx6ul_hardfpu_enable(void)
    33. {
    34. uint32_t cpacr;
    35. uint32_t fpexc;
    36. /* 使能NEON和FPU */
    37. cpacr = __get_CPACR();
    38. cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
    39. | (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
    40. __set_CPACR(cpacr);
    41. fpexc = __get_FPEXC();
    42. fpexc |= 0x40000000UL;
    43. __set_FPEXC(fpexc);
    44. }
    45. int main(void)
    46. {
    47. static uint8_t led_state = OFF;
    48. // static uint8_t beep_state = OFF;
    49. imx6ul_hardfpu_enable(); /* 使能I.MX6U的硬件浮点 */
    50. int_init(); /* 中断初始化 */
    51. imx6u_clkinit(); /* 时钟主频初始化,PLL1, PLL2, PLL3 */
    52. clk_init(); /* 使能所有外设时钟 */
    53. led_init(); /* led gpio 初始化 */
    54. beep_init(); /* beep gpio 初始化 */
    55. //key_init(); /* key gpio 初始化 */
    56. exti_init(); /* gpio外设中断初始化 */
    57. //epittimer_init(0, 0, 33000000); /* EPIT分频frac=0 1分频,EPIT1时钟源66MHz,EPIT1->LR加载值计数器=33MHz,定时周期为500ms */
    58. //epittimer_init(1, 0, 66000000/20); /* EPIT分频frac=0 1分频,EPIT1时钟源66MHz,EPIT1->LR加载值计数器=33MHz,定时周期为1000/20=50ms */
    59. keyfilter_init();
    60. delay_init();
    61. uart_init(); /* UART初始化 */
    62. //lcd_init(); /* LCD 初始化 */
    63. led_switch(LED_0, ON);
    64. beep_switch(ON);
    65. delay(200);
    66. beep_switch(OFF);
    67. delay(200);
    68. beep_switch(ON);
    69. delay(200);
    70. beep_switch(OFF);
    71. printf(banner, __DATE__);
    72. printf("Hello World\r\n");
    73. lcd_init(); /* LCD 初始化 */
    74. rtc_init();
    75. i2c_ap3216c_init();
    76. icm20608_init();
    77. // unsigned short ir, ps, als;
    78. struct icm20608_device icm20608_dev;
    79. //启用I.MX6U浮点数计算
    80. while(1){
    81. led_state = !led_state;
    82. led_switch(LED_0, led_state);
    83. delay_ms(1000);
    84. // ap3216c_readdata(&ir, &ps, &als);
    85. // printf("ir=%d\r\n", ir);
    86. // printf("ps=%d\r\n", ps);
    87. // printf("als=%d\r\n", als);
    88. icm20608_read_dev_accel_gyro(&icm20608_dev);
    89. printf("accel %04x x:%hd\r\n", icm20608_dev.accel_x_adc, icm20608_dev.accel_x_adc);
    90. printf("accel %04x y:%hd\r\n", icm20608_dev.accel_y_adc, icm20608_dev.accel_y_adc);
    91. printf("accel %04x z:%hd\r\n", icm20608_dev.accel_z_adc, icm20608_dev.accel_z_adc);
    92. printf("gyro %04x x:%hd\r\n", icm20608_dev.gyro_x_adc, icm20608_dev.gyro_x_adc);
    93. printf("gyro %04x y:%hd\r\n", icm20608_dev.gyro_y_adc, icm20608_dev.gyro_y_adc);
    94. printf("gyro %04x z:%hd\r\n", icm20608_dev.gyro_z_adc, icm20608_dev.gyro_z_adc);
    95. printf("ACT accel %04x x:%hd\r\n", icm20608_dev.accel_x_act, icm20608_dev.accel_x_act);
    96. printf("ACT accel %04x y:%hd\r\n", icm20608_dev.accel_y_act, icm20608_dev.accel_y_act);
    97. printf("ACT accel %04x z:%hd\r\n", icm20608_dev.accel_z_act, icm20608_dev.accel_z_act);
    98. printf("ACT gyro %04x x:%hd\r\n", icm20608_dev.gyro_x_act, icm20608_dev.gyro_x_act);
    99. printf("ACT gyro %04x y:%hd\r\n", icm20608_dev.gyro_y_act, icm20608_dev.gyro_y_act);
    100. printf("ACT gyro %04x z:%hd\r\n", icm20608_dev.gyro_z_act, icm20608_dev.gyro_z_act);
    101. }
    102. return 0;
    103. }

    另一个重要的函数是 imx6ul_hardfpu_enable,这个函数用于开启 I.MX6U 的 NEON 和硬件 FPU(浮点运算单元),因为本章使用到了浮点运算,而 I.MX6U 的Cortex-A7 是支持 NEON 和 FPU(VFPV4_D32)的,但是在使用 I.MX6U 的硬件 FPU 之前是先要开启的。


     

    4. 启用I.MX6U 浮点数运算

    开启I.MX6U的硬件浮点单元,否则执行浮点运算的时候程序会卡死(float),使用I.MX6U的硬件复点单元的有如下两个步骤:

    1. 开启I.MX6U的硬件浮点单元
    2. 编译时指定启用硬件浮点,指定编译的时候使用硬件浮点指令

    VFPFloat Process
    VFPFloat Process
    FPEXCFloat-Point Execute Control 

    通过如下函数写I.MX6U ARM Cortex-A7 协处理器的寄存器来启用硬件浮点运算:

    1. /*
    2. * @description : 使能I.MX6U的硬件NEON和FPU
    3. * @param : 无
    4. * @return : 无
    5. */
    6. void imx6ul_hardfpu_enable(void)
    7. {
    8. uint32_t cpacr;
    9. uint32_t fpexc;
    10. /* 使能NEON和FPU */
    11. cpacr = __get_CPACR();
    12. cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
    13. | (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
    14. __set_CPACR(cpacr);
    15. fpexc = __get_FPEXC();
    16. fpexc |= 0x40000000UL;
    17. __set_FPEXC(fpexc);
    18. }

    在Makefile里加入如下编译选项,指定编译器编译生成汇编源码时使用硬件汇编指令

    -march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard

    5. 编译烧写SD卡验证结果

    编译源码烧录SD卡验证本节的 I.MX6U I2C驱动实验。预期烧录SD卡后正点原子I.MX6ULL ALPHA/Mini 开发板后,可以通过SPI总线读取到ICM20608 6轴传感器的加速度计x,y,z三轴的ADC采集数据和陀螺仪的x,y,z三轴的ADC采样数据,然后根据icm20608配置的量程范围来计算出实际的加速度计x,y,z轴传感器的实际物理值和陀螺仪的x,y,z轴传感器的实际物理值。

    我本地验证的结果是可以通过spi接口读取到 icm20608传感器的 加速度计x,y,z三轴的ADC采集数据和陀螺仪的x,y,z三轴的ADC采样数据,计算并打印到串口。

    5. 总结和实验遇到问题记录

    本实验通过I.MX6U 的硬件spi接口读取到了 icm20608 6轴传感器的寄存器数据,验证了spi可以正常通信,读取了  icm20608 传感器的 chipid 寄存器并打印到串口。本实验也在I.MX6U ARM裸机的情况下直接通过寄存器的操作来初始化spi硬件接口,在实验过程中也熟悉了spi通信的协议,为之后学习打下了基础。

    5.1 问题1:icm20608 ADC 采样值是是有符号短整型

    icm20608 ADC 采样值是是有符号短整型,在结构体声明里应该将icm20608的 x/y/z 轴的ADC数据声明为 'unsigned short’ 型。这样才能计算处正确的±16g,±2000dps 等加速度和角速度物理量,正负号表示方向。

    5.2 问题2:寄存器bit[4:3]应该先右移再和'0x3'进行与运算。

    问题2:寄存器bit[4:3]应该先右移再和'0x3F'进行与操作,原来写错了写成了先与'0x3'进行与运算再右移三位,这样就得到了错误的值。

    5.3 问题3:需要启用I.MX6U 硬件浮点运算单元,才能使用浮点运算否则程序会卡死

    需要启用I.MX6U 硬件浮点运算单元,才能使用浮点运算否则程序会卡死,启用硬件浮点运算之后可以极大的提高浮点运算的速度。

    5.4 问题4:使用硬件浮点单元,同时也需要在Makefile里通过编译选项高速编译器使用硬件浮点运算指令。

    使用硬件浮点单元,除了开启I.MX6U协处理器的硬件浮点运算单元之后,还需要在Makefile里通过编译选项高速编译器使用硬件浮点运算指令。

    -march=armv7-a -mfpu=neon-vfpv4

    6. 结束

    本文至此结束

  • 相关阅读:
    穿透、无底洞、雪崩、击穿 解决方案?
    Java 面试题 —— TCP 粘包、拆包问题
    Enzo CYTO-ID自噬检测试剂盒特点&作用机制
    你要有自己的影响力
    金仓数据库KingbaseES客户端编程接口指南-ado.net(3. KingbaseES 驱动在 .NET 平台的配置)
    分库分表四:shardingjdbc内核解析和核心源码分析
    【计算机网络】三次握手与四次挥手(过程详解)
    路径规划总结(一)
    求一份网页设计结课大作业,要求用到html,css,javascript,的知识
    软考高级系统架构设计师系列论文二十三:论数据中心集中存储架构
  • 原文地址:https://blog.csdn.net/weixin_40289409/article/details/139416167