当前位置: 首页 > article >正文

40. SPI实验

一、SPI协议详解

1、SPI相比I2C最大的优势有两点:一个是速度快,最高可以大几十M,甚至上百MHz,第二个就是SPI是个全双工。
2、SPI接口和I2C一样,一个SPI接口可以连接多个SPI外设,SPI通过CS引脚/数据线,片选引脚来选择和哪个SPI外设通信。SPI通信前先将指定的SPI外设对应的CS引脚拉低来选中此设备。
3、ALPHA开发板上通过ECSPI3接口连接了一个6轴传感器,引脚如下:
ECSPI3_SCLK : UART2_RX
ECSPI3_MOSI:UART2_CTS
ECSPI3_SS0:UART2_TXD
ECSPI3_MISO: UART2_RTS
6ULL一个SPI主接口有4个硬件片选,分别为SS0~SS3。
4、根据CPOL和CPHA可以设置四种工作模式,一般使用CPOL=0、CPHA=0。

二、6ULL SPI接口详解

1、6ULL的SPI接口叫做ECSPI,支持全双工、主丛可配置。
2、4个硬件片选信号,可以使用软件片选,这样一个SPI接口所能连接的外设就无限制了。
1、RXDATA寄存器为接收到的数据。
2、TXDATA寄存器为发送数据寄存器。
3、CONREG寄存器为配置寄存器,bit0置1,使能SPI。Bit3置1,表示当向TXFIFO写入数据以后马上开启SPI突发访问,也就是发送数据。Bit7:4设置SPI通道主从模式,bit7为通道3,bit4为通道0,我们使用到了SS0,也就是通道0,因此需要设置bit4为1。Bit19:18设置为00,我们使用到SS0,也就是通道0。Bit31:30设置突发访问长度,我们设置为7,也就是8bit突发长度,一个字节。
4、CONFIGREG寄存器的bit0为PHA,设置为0,表示 串行时钟的第一个跳变沿开始采集数据。设置bit4为PO,设置为0,表示SCLK空闲的时候为低电平。Bit8设置0。Bit12设置 为0。Bit16设置为0,表示空闲的时候数据线为高。Bit20设置为0,表示SCLK空闲的时候为低。
5、STATREG寄存器,bit0表示TXFIFO为空,我们在发送数据之前要等待TXFIFO为空,也就是等待bit0为1。Bit3表示RXFIFO是否有数据,为1的时候示RXFIFO至少有1个字的数据,我们在接收数据的时候要等到bit3为1。
6、PERIODREG寄存器,bit14:0设置wait states时间,我们设置为0X2000。Bit15设置wait states的时钟源为SPI CLK,将此位设置0。Bit21:16表示片选信号的延时,可设置0-63,这里设置为0.
7、SPI时钟设置!
SPI时钟源最终来源于pll3_sw_clk=480MHz/8=60MHz,设置CSCDR2寄存器的bit18为0,也就是ECSPI时钟源为60MHz。bit24:19设置为0,表示1分频,因此最终进入到SPI外设的时钟源为60MHz
ECSPI模块还需要对时钟进行两级分频,由ECSPI_CONREG寄存器设置。Bit15:12设置前级分频,可以设置00xf,表示116分频。Bit11:8设置2级分频,设置2^n分频,n=0~15.

四、实验程序编写

1、我们在使用浮点计算的时候程序卡死了,因为我们没有开始6UL的硬件浮点运算。我们在编译的时候没有使用浮点。解决此问题需要两点:
①、开启6UL的硬件浮点单元

/*
 * @description : 使能I.MX6U的硬件NEON和FPU
 * @param       : 无
 * @return      : 无
 */
 void imx6ul_hardfpu_enable(void)
{
    uint32_t cpacr;
    uint32_t fpexc;

    /* 使能NEON和FPU */
    cpacr = __get_CPACR();
    cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
           |  (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
    __set_CPACR(cpacr);
    fpexc = __get_FPEXC();
    fpexc |= 0x40000000UL;  
    __set_FPEXC(fpexc);
}

②、编译的是时候指定硬件浮点。

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

//main.c

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "bsp_lcd.h"
#include "bsp_rtc.h"
#include "bsp_icm20608.h"
#include "bsp_spi.h"
#include "stdio.h"

/*
 * @description : 指定的位置显示整数数据
 * @param - x   : X轴位置
 * @param - y   : Y轴位置
 * @param - size: 字体大小
 * @param - num : 要显示的数据
 * @return      : 无
 */
void integer_display(unsigned short x, unsigned short y, unsigned char size, signed int num)
{
    char buf[200];
    
    lcd_fill(x, y, x + 50, y + size, tftlcd_dev.backcolor);
    
    memset(buf, 0, sizeof(buf));
    if(num < 0)
        sprintf(buf, "-%d", -num);
    else 
        sprintf(buf, "%d", num);
    lcd_show_string(x, y, 50, size, size, buf); 
}

/*
 * @description : 指定的位置显示小数数据,比如5123,显示为51.23
 * @param - x   : X轴位置
 * @param - y   : Y轴位置
 * @param - size: 字体大小
 * @param - num : 要显示的数据,实际小数扩大100倍,
 * @return      : 无
 */
void decimals_display(unsigned short x, unsigned short y, unsigned char size, signed int num)
{
    signed int integ;   /* 整数部分 */
    signed int fract;   /* 小数部分 */
    signed int uncomptemp = num; 
    char buf[200];

    if(num < 0)
        uncomptemp = -uncomptemp;
    integ = uncomptemp / 100;
    fract = uncomptemp % 100;

    memset(buf, 0, sizeof(buf));
    if(num < 0)
        sprintf(buf, "-%d.%d", integ, fract);
    else 
        sprintf(buf, "%d.%d", integ, fract);
    lcd_fill(x, y, x + 60, y + size, tftlcd_dev.backcolor);
    lcd_show_string(x, y, 60, size, size, buf); 
}

/*
 * @description : 使能I.MX6U的硬件NEON和FPU
 * @param       : 无
 * @return      : 无
 */
 void imx6ul_hardfpu_enable(void)
{
    uint32_t cpacr;
    uint32_t fpexc;

    /* 使能NEON和FPU */
    cpacr = __get_CPACR();
    cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
           |  (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
    __set_CPACR(cpacr);
    fpexc = __get_FPEXC();
    fpexc |= 0x40000000UL;  
    __set_FPEXC(fpexc);
}

/*
 * @description : main函数
 * @param       : 无
 * @return      : 无
 */
int main(void)
{
    unsigned char state = OFF;

    imx6ul_hardfpu_enable();    /* 使能I.MX6U的硬件浮点            */
    int_init();                 /* 初始化中断(一定要最先调用!) */
    imx6u_clkinit();            /* 初始化系统时钟                  */
    delay_init();               /* 初始化延时                    */
    clk_enable();               /* 使能所有的时钟                  */
    led_init();                 /* 初始化led                   */
    beep_init();                /* 初始化beep                  */
    uart_init();                /* 初始化串口,波特率115200 */
    lcd_init();                 /* 初始化LCD                   */      

    tftlcd_dev.forecolor = LCD_RED;
    lcd_show_string(50, 10, 400, 24, 24, (char*)"IMX6U-ZERO SPI TEST");  
    lcd_show_string(50, 40, 200, 16, 16, (char*)"ICM20608 TEST");  
    lcd_show_string(50, 60, 200, 16, 16, (char*)"ATOM@ALIENTEK");  
    lcd_show_string(50, 80, 200, 16, 16, (char*)"2019/3/27");  
    
    while(icm20608_init())      /* 初始化ICM20608              */
    {
        lcd_show_string(50, 100, 200, 16, 16, (char*)"ICM20608 Check Failed!");
        delayms(500);
        lcd_show_string(50, 100, 200, 16, 16, (char*)"Please Check!        ");
        delayms(500);
    }   

    lcd_show_string(50, 100, 200, 16, 16, (char*)"ICM20608 Ready");
    
    lcd_show_string(50, 130, 200, 16, 16, (char*)"accel x:");  
    lcd_show_string(50, 150, 200, 16, 16, (char*)"accel y:");  
    lcd_show_string(50, 170, 200, 16, 16, (char*)"accel z:");  
    lcd_show_string(50, 190, 200, 16, 16, (char*)"gyro  x:"); 
    lcd_show_string(50, 210, 200, 16, 16, (char*)"gyro  y:"); 
    lcd_show_string(50, 230, 200, 16, 16, (char*)"gyro  z:"); 
    lcd_show_string(50, 250, 200, 16, 16, (char*)"temp   :"); 

    lcd_show_string(50 + 181, 130, 200, 16, 16, (char*)"g");  
    lcd_show_string(50 + 181, 150, 200, 16, 16, (char*)"g");  
    lcd_show_string(50 + 181, 170, 200, 16, 16, (char*)"g");  
    lcd_show_string(50 + 181, 190, 200, 16, 16, (char*)"o/s"); 
    lcd_show_string(50 + 181, 210, 200, 16, 16, (char*)"o/s"); 
    lcd_show_string(50 + 181, 230, 200, 16, 16, (char*)"o/s"); 
    lcd_show_string(50 + 181, 250, 200, 16, 16, (char*)"C");
    
    tftlcd_dev.forecolor = LCD_BLUE;

    while(1)                    
    {       
        icm20608_getdata();
        integer_display(50 + 70, 130, 16, icm20608_dev.accel_x_adc);
        integer_display(50 + 70, 150, 16, icm20608_dev.accel_y_adc);
        integer_display(50 + 70, 170, 16, icm20608_dev.accel_z_adc);
        integer_display(50 + 70, 190, 16, icm20608_dev.gyro_x_adc);
        integer_display(50 + 70, 210, 16, icm20608_dev.gyro_y_adc);
        integer_display(50 + 70, 230, 16, icm20608_dev.gyro_z_adc);
        integer_display(50 + 70, 250, 16, icm20608_dev.temp_adc);

        decimals_display(50 + 70 + 50, 130, 16, icm20608_dev.accel_x_act);
        decimals_display(50 + 70 + 50, 150, 16, icm20608_dev.accel_y_act);
        decimals_display(50 + 70 + 50, 170, 16, icm20608_dev.accel_z_act);
        decimals_display(50 + 70 + 50, 190, 16, icm20608_dev.gyro_x_act);
        decimals_display(50 + 70 + 50, 210, 16, icm20608_dev.gyro_y_act);
        decimals_display(50 + 70 + 50, 230, 16, icm20608_dev.gyro_z_act);
        decimals_display(50 + 70 + 50, 250, 16, icm20608_dev.temp_act);

#if 0       
        printf("accel x = %d\r\n",icm20608_dev.accel_x_adc);
        printf("accel y = %d\r\n",icm20608_dev.accel_y_adc);
        printf("accel z = %d\r\n",icm20608_dev.accel_z_adc);
        printf("gyrp  x = %d\r\n",icm20608_dev.gyro_x_adc);
        printf("gyro  y = %d\r\n",icm20608_dev.gyro_y_adc);
        printf("gyro  z = %d\r\n",icm20608_dev.gyro_z_adc);
        printf("temp    = %d\r\n",icm20608_dev.temp_adc);
#endif
        delayms(120);
        state = !state;
        led_switch(LED0,state); 
    }
    return 0;
}

//Makefile

CROSS_COMPILE   ?= arm-linux-gnueabihf-
TARGET          ?= icm20608

CC              := $(CROSS_COMPILE)gcc
LD              := $(CROSS_COMPILE)ld
OBJCOPY         := $(CROSS_COMPILE)objcopy
OBJDUMP         := $(CROSS_COMPILE)objdump

LIBPATH         := -lgcc -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4

INCDIRS         := imx6ul \
                   stdio/include \
                   bsp/clk \
                   bsp/led \
                   bsp/delay  \
                   bsp/beep \
                   bsp/gpio \
                   bsp/key \
                   bsp/exit \
                   bsp/int \
                   bsp/epittimer \
                   bsp/keyfilter \
                   bsp/uart \
                   bsp/lcd \
                   bsp/rtc \
                   bsp/i2c \
                   bsp/ap3216c \
                   bsp/spi \
                   bsp/icm20608
                               
SRCDIRS         := project \
                   stdio/lib \
                   bsp/clk \
                   bsp/led \
                   bsp/delay \
                   bsp/beep \
                   bsp/gpio \
                   bsp/key \
                   bsp/exit \
                   bsp/int \
                   bsp/epittimer \
                   bsp/keyfilter \
                   bsp/uart \
                   bsp/lcd \
                   bsp/rtc \
                   bsp/i2c \
                   bsp/ap3216c \
                   bsp/spi \
                   bsp/icm20608
                   
                   
INCLUDE         := $(patsubst %, -I %, $(INCDIRS))

SFILES          := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES          := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

SFILENDIR       := $(notdir  $(SFILES))
CFILENDIR       := $(notdir  $(CFILES))

SOBJS           := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS           := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS            := $(SOBJS) $(COBJS)

VPATH           := $(SRCDIRS)

.PHONY: clean
    
$(TARGET).bin : $(OBJS)
    $(LD) -Timx6ul.lds -o $(TARGET).elf $^ $(LIBPATH) 
    $(OBJCOPY) -O binary -S $(TARGET).elf $@
    $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis

$(SOBJS) : obj/%.o : %.S
    $(CC) -Wall -nostdlib -fno-builtin -c -O2  $(INCLUDE) -o $@ $<

$(COBJS) : obj/%.o : %.c
    $(CC) -Wall -march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard -Wa,-mimplicit-it=thumb -nostdlib -fno-builtin -c -O2  $(INCLUDE) -o $@ $<
    
clean:
    rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

//bsp_spi.c

#include "bsp_spi.h"
#include "bsp_gpio.h"
#include "stdio.h"

/*
 * @description     : 初始化SPI
 * @param - base    : 要初始化的SPI
 * @return          : 无
 */
void spi_init(ECSPI_Type *base)
{
    /* 配置CONREG寄存器
     * bit0 :       1   使能ECSPI
     * bit3 :       1   当向TXFIFO写入数据以后立即开启SPI突发。
     * bit[7:4] :   0001 SPI通道0主模式,根据实际情况选择,
     *                  开发板上的ICM-20608接在SS0上,所以设置通道0为主模式
     * bit[19:18]:  00  选中通道0(其实不需要,因为片选信号我们我们自己控制)
     * bit[31:20]:  0x7 突发长度为8个bit。 
     */
    base->CONREG = 0; /* 先清除控制寄存器 */
    base->CONREG |= (1 << 0) | (1 << 3) | (1 << 4) | (7 << 20); /* 配置CONREG寄存器 */

    /*
     * ECSPI通道0设置,即设置CONFIGREG寄存器
     * bit0:    0 通道0 PHA为0
     * bit4:    0 通道0 SCLK高电平有效
     * bit8:    0 通道0片选信号 当SMC为1的时候此位无效
     * bit12:   0 通道0 POL为0
     * bit16:   0 通道0 数据线空闲时高电平
     * bit20:   0 通道0 时钟线空闲时低电平
     */
    base->CONFIGREG = 0;        /* 设置通道寄存器 */
    
    /*  
     * ECSPI通道0设置,设置采样周期
     * bit[14:0] :  0X2000  采样等待周期,比如当SPI时钟为10MHz的时候
     *              0X2000就等于1/10000 * 0X2000 = 0.8192ms,也就是连续
     *              读取数据的时候每次之间间隔0.8ms
     * bit15     :  0  采样时钟源为SPI CLK
     * bit[21:16]:  0  片选延时,可设置为0~63
     */
    base->PERIODREG = 0X2000;       /* 设置采样周期寄存器 */

    /*
     * ECSPI的SPI时钟配置,SPI的时钟源来源于pll3_sw_clk/8=480/8=60MHz
     * 通过设置CONREG寄存器的PER_DIVIDER(bit[11:8])和POST_DIVEDER(bit[15:12])来
     * 对SPI时钟源分频,获取到我们想要的SPI时钟:
     * SPI CLK = (SourceCLK / PER_DIVIDER) / (2^POST_DIVEDER)
     * 比如我们现在要设置SPI时钟为6MHz,那么PER_DIVEIDER和POST_DEIVIDER设置如下:
     * PER_DIVIDER = 0X9。
     * POST_DIVIDER = 0X0。
     * SPI CLK = 60000000/(0X9 + 1) = 60000000=6MHz
     */
    base->CONREG &= ~((0XF << 12) | (0XF << 8));    /* 清除PER_DIVDER和POST_DIVEDER以前的设置 */
    base->CONREG |= (0X9 << 12);                    /* 设置SPI CLK = 6MHz */
}

/*
 * @description     : SPI通道0发送/接收一个字节的数据
 * @param - base    : 要使用的SPI
 * @param - txdata  : 要发送的数据
 * @return          : 无
 */
unsigned char spich0_readwrite_byte(ECSPI_Type *base, unsigned char txdata)
{ 
    uint32_t  spirxdata = 0;
    uint32_t  spitxdata = txdata;

    /* 选择通道0 */
    base->CONREG &= ~(3 << 18);
    base->CONREG |= (0 << 18);

    while((base->STATREG & (1 << 0)) == 0){} /* 等待发送FIFO为空 */
        base->TXDATA = spitxdata;
    
    while((base->STATREG & (1 << 3)) == 0){} /* 等待接收FIFO有数据 */
        spirxdata = base->RXDATA;
    return spirxdata;
}

//bsp_spi.h

#ifndef _BSP_SPI_H
#define _BSP_SPI_H
#include "imx6ul.h"

/* 函数声明 */
void spi_init(ECSPI_Type *base);
unsigned char spich0_readwrite_byte(ECSPI_Type *base, unsigned char txdata);

#endif

//bsp_icm20608.c

#include "bsp_icm20608.h"
#include "bsp_delay.h"
#include "bsp_spi.h"
#include "stdio.h"

struct icm20608_dev_struc icm20608_dev; /* icm20608设备 */

/*
 * @description : 初始化ICM20608
 * @param       : 无
 * @return      : 0 初始化成功,其他值 初始化失败
 */
unsigned char icm20608_init(void)
{   
    unsigned char regvalue;
    gpio_pin_config_t cs_config;

    /* 1、ESPI3 IO初始化 
     * ECSPI3_SCLK  -> UART2_RXD
     * ECSPI3_MISO  -> UART2_RTS
     * ECSPI3_MOSI  -> UART2_CTS
     */
    IOMUXC_SetPinMux(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0);
    IOMUXC_SetPinMux(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0);
    IOMUXC_SetPinMux(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0);
    
    /* 配置SPI   SCLK MISO MOSI IO属性  
     *bit 16: 0 HYS关闭
     *bit [15:14]: 00 默认100K下拉
     *bit [13]: 0 keeper功能
     *bit [12]: 1 pull/keeper使能 
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 驱动能力为R0/6
     *bit [0]: 1 高转换率
     */
    IOMUXC_SetPinConfig(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0x10B1);
    IOMUXC_SetPinConfig(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0x10B1);
    IOMUXC_SetPinConfig(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0x10B1);

    
    IOMUXC_SetPinMux(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0);
    IOMUXC_SetPinConfig(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0X10B0);
    cs_config.direction = kGPIO_DigitalOutput;
    cs_config.outputLogic = 0;
    gpio_init(GPIO1, 20, &cs_config);
    
    /* 2、初始化SPI */
    spi_init(ECSPI3);   

    icm20608_write_reg(ICM20_PWR_MGMT_1, 0x80);     /* 复位,复位后为0x40,睡眠模式             */
    delayms(50);
    icm20608_write_reg(ICM20_PWR_MGMT_1, 0x01);     /* 关闭睡眠,自动选择时钟                  */
    delayms(50);

    regvalue = icm20608_read_reg(ICM20_WHO_AM_I);
    printf("icm20608 id = %#X\r\n", regvalue);
    if(regvalue != ICM20608G_ID && regvalue != ICM20608D_ID)
        return 1;
        
    icm20608_write_reg(ICM20_SMPLRT_DIV, 0x00);     /* 输出速率是内部采样率                   */
    icm20608_write_reg(ICM20_GYRO_CONFIG, 0x18);    /* 陀螺仪±2000dps量程                */
    icm20608_write_reg(ICM20_ACCEL_CONFIG, 0x18);   /* 加速度计±16G量程                   */
    icm20608_write_reg(ICM20_CONFIG, 0x04);         /* 陀螺仪低通滤波BW=20Hz               */
    icm20608_write_reg(ICM20_ACCEL_CONFIG2, 0x04);  /* 加速度计低通滤波BW=21.2Hz            */
    icm20608_write_reg(ICM20_PWR_MGMT_2, 0x00);     /* 打开加速度计和陀螺仪所有轴                */
    icm20608_write_reg(ICM20_LP_MODE_CFG, 0x00);    /* 关闭低功耗                        */
    icm20608_write_reg(ICM20_FIFO_EN, 0x00);        /* 关闭FIFO                       */
    return 0;
}

    
/*
 * @description  : 写ICM20608指定寄存器
 * @param - reg  : 要读取的寄存器地址
 * @param - value: 要写入的值
 * @return       : 无
 */
void icm20608_write_reg(unsigned char reg, unsigned char value)
{
    /* ICM20608在使用SPI接口的时候寄存器地址
     * 只有低7位有效,寄存器地址最高位是读/写标志位
     * 读的时候要为1,写的时候要为0。
     */
    reg &= ~0X80;   
    
    ICM20608_CSN(0);                        /* 使能SPI传输          */
    spich0_readwrite_byte(ECSPI3, reg);     /* 发送寄存器地址      */ 
    spich0_readwrite_byte(ECSPI3, value);   /* 发送要写入的值          */
    ICM20608_CSN(1);                        /* 禁止SPI传输          */
}   

/*
 * @description : 读取ICM20608寄存器值
 * @param - reg : 要读取的寄存器地址
 * @return      : 读取到的寄存器值
 */
unsigned char icm20608_read_reg(unsigned char reg)
{
    unsigned char reg_val;      

    /* ICM20608在使用SPI接口的时候寄存器地址
     * 只有低7位有效,寄存器地址最高位是读/写标志位
     * 读的时候要为1,写的时候要为0。
     */
    reg |= 0x80;    
    
    ICM20608_CSN(0);                                /* 使能SPI传输          */
    spich0_readwrite_byte(ECSPI3, reg);             /* 发送寄存器地址          */ 
    reg_val = spich0_readwrite_byte(ECSPI3, 0XFF);  /* 读取寄存器的值          */
    ICM20608_CSN(1);                                /* 禁止SPI传输          */
    return(reg_val);                                /* 返回读取到的寄存器值 */
}

/*
 * @description : 读取ICM20608连续多个寄存器
 * @param - reg : 要读取的寄存器地址
 * @return      : 读取到的寄存器值
 */
void icm20608_read_len(unsigned char reg, unsigned char *buf, unsigned char len)
{  
    unsigned char i;
    
    /* ICM20608在使用SPI接口的时候寄存器地址,只有低7位有效,
     * 寄存器地址最高位是读/写标志位读的时候要为1,写的时候要为0。
     */
    reg |= 0x80; 
        
    ICM20608_CSN(0);                            /* 使能SPI传输          */
    spich0_readwrite_byte(ECSPI3, reg);         /* 发送寄存器地址          */         
    for(i = 0; i < len; i++)                    /* 顺序读取寄存器的值            */
    {
        buf[i] = spich0_readwrite_byte(ECSPI3, 0XFF);   
    }
    ICM20608_CSN(1);                            /* 禁止SPI传输          */
}

/*
 * @description : 获取陀螺仪的分辨率
 * @param       : 无
 * @return      : 获取到的分辨率
 */
float icm20608_gyro_scaleget(void)
{
    unsigned char data;
    float gyroscale;
    
    data = (icm20608_read_reg(ICM20_GYRO_CONFIG) >> 3) & 0X3;
    switch(data) {
        case 0: 
            gyroscale = 131;
            break;
        case 1:
            gyroscale = 65.5;
            break;
        case 2:
            gyroscale = 32.8;
            break;
        case 3:
            gyroscale = 16.4;
            break;
    }
    return gyroscale;
}

/*
 * @description : 获取加速度计的分辨率
 * @param       : 无
 * @return      : 获取到的分辨率
 */
unsigned short icm20608_accel_scaleget(void)
{
    unsigned char data;
    unsigned short accelscale;
    
    data = (icm20608_read_reg(ICM20_ACCEL_CONFIG) >> 3) & 0X3;
    switch(data) {
        case 0: 
            accelscale = 16384;
            break;
        case 1:
            accelscale = 8192;
            break;
        case 2:
            accelscale = 4096;
            break;
        case 3:
            accelscale = 2048;
            break;
    }
    return accelscale;
}

/*
 * @description : 读取ICM20608的加速度、陀螺仪和温度原始值
 * @param       : 无
 * @return      : 无
 */
void icm20608_getdata(void)
{
    float gyroscale;
    unsigned short accescale;
    unsigned char data[14];
    
    icm20608_read_len(ICM20_ACCEL_XOUT_H, data, 14);
    
    gyroscale = icm20608_gyro_scaleget();
    accescale = icm20608_accel_scaleget();

    icm20608_dev.accel_x_adc = (signed short)((data[0] << 8) | data[1]); 
    icm20608_dev.accel_y_adc = (signed short)((data[2] << 8) | data[3]); 
    icm20608_dev.accel_z_adc = (signed short)((data[4] << 8) | data[5]); 
    icm20608_dev.temp_adc    = (signed short)((data[6] << 8) | data[7]); 
    icm20608_dev.gyro_x_adc  = (signed short)((data[8] << 8) | data[9]); 
    icm20608_dev.gyro_y_adc  = (signed short)((data[10] << 8) | data[11]);
    icm20608_dev.gyro_z_adc  = (signed short)((data[12] << 8) | data[13]);

    /* 计算实际值 */
    icm20608_dev.gyro_x_act = ((float)(icm20608_dev.gyro_x_adc)  / gyroscale) * 100;
    icm20608_dev.gyro_y_act = ((float)(icm20608_dev.gyro_y_adc)  / gyroscale) * 100;
    icm20608_dev.gyro_z_act = ((float)(icm20608_dev.gyro_z_adc)  / gyroscale) * 100;

    icm20608_dev.accel_x_act = ((float)(icm20608_dev.accel_x_adc) / accescale) * 100;
    icm20608_dev.accel_y_act = ((float)(icm20608_dev.accel_y_adc) / accescale) * 100;
    icm20608_dev.accel_z_act = ((float)(icm20608_dev.accel_z_adc) / accescale) * 100;

    icm20608_dev.temp_act = (((float)(icm20608_dev.temp_adc) - 25 ) / 326.8 + 25) * 100;
}

//bsp_icm20608.h

#ifndef _BSP_ICM20608_H
#define _BSP_ICM20608_H
#include "imx6ul.h"
#include "bsp_gpio.h"

/* 宏定义 */
#define ICM20608_CSN(n)    (n ? gpio_pinwrite(GPIO1, 20, 1) : gpio_pinwrite(GPIO1, 20, 0))   /* SPI片选信号  */

#define ICM20608G_ID            0XAF    /* ID值 */
#define ICM20608D_ID            0XAE    /* ID值 */

/* ICM20608寄存器 
 *复位后所有寄存器地址都为0,除了
 *Register 107(0X6B) Power Management 1     = 0x40
 *Register 117(0X75) WHO_AM_I               = 0xAF或0xAE
 */
/* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */
#define ICM20_SELF_TEST_X_GYRO      0x00
#define ICM20_SELF_TEST_Y_GYRO      0x01
#define ICM20_SELF_TEST_Z_GYRO      0x02
#define ICM20_SELF_TEST_X_ACCEL     0x0D
#define ICM20_SELF_TEST_Y_ACCEL     0x0E
#define ICM20_SELF_TEST_Z_ACCEL     0x0F

/* 陀螺仪静态偏移 */
#define ICM20_XG_OFFS_USRH          0x13
#define ICM20_XG_OFFS_USRL          0x14
#define ICM20_YG_OFFS_USRH          0x15
#define ICM20_YG_OFFS_USRL          0x16
#define ICM20_ZG_OFFS_USRH          0x17
#define ICM20_ZG_OFFS_USRL          0x18

#define ICM20_SMPLRT_DIV            0x19
#define ICM20_CONFIG                0x1A
#define ICM20_GYRO_CONFIG           0x1B
#define ICM20_ACCEL_CONFIG          0x1C
#define ICM20_ACCEL_CONFIG2         0x1D
#define ICM20_LP_MODE_CFG           0x1E
#define ICM20_ACCEL_WOM_THR         0x1F
#define ICM20_FIFO_EN               0x23
#define ICM20_FSYNC_INT             0x36
#define ICM20_INT_PIN_CFG           0x37
#define ICM20_INT_ENABLE            0x38
#define ICM20_INT_STATUS            0x3A

/* 加速度输出 */
#define ICM20_ACCEL_XOUT_H          0x3B
#define ICM20_ACCEL_XOUT_L          0x3C
#define ICM20_ACCEL_YOUT_H          0x3D
#define ICM20_ACCEL_YOUT_L          0x3E
#define ICM20_ACCEL_ZOUT_H          0x3F
#define ICM20_ACCEL_ZOUT_L          0x40

/* 温度输出 */
#define ICM20_TEMP_OUT_H            0x41
#define ICM20_TEMP_OUT_L            0x42

/* 陀螺仪输出 */
#define ICM20_GYRO_XOUT_H           0x43
#define ICM20_GYRO_XOUT_L           0x44
#define ICM20_GYRO_YOUT_H           0x45
#define ICM20_GYRO_YOUT_L           0x46
#define ICM20_GYRO_ZOUT_H           0x47
#define ICM20_GYRO_ZOUT_L           0x48

#define ICM20_SIGNAL_PATH_RESET     0x68
#define ICM20_ACCEL_INTEL_CTRL      0x69
#define ICM20_USER_CTRL             0x6A
#define ICM20_PWR_MGMT_1            0x6B
#define ICM20_PWR_MGMT_2            0x6C
#define ICM20_FIFO_COUNTH           0x72
#define ICM20_FIFO_COUNTL           0x73
#define ICM20_FIFO_R_W              0x74
#define ICM20_WHO_AM_I              0x75

/* 加速度静态偏移 */
#define ICM20_XA_OFFSET_H           0x77
#define ICM20_XA_OFFSET_L           0x78
#define ICM20_YA_OFFSET_H           0x7A
#define ICM20_YA_OFFSET_L           0x7B
#define ICM20_ZA_OFFSET_H           0x7D
#define ICM20_ZA_OFFSET_L           0x7E

/*
 * ICM20608结构体
 */
struct icm20608_dev_struc
{
    signed int gyro_x_adc;      /* 陀螺仪X轴原始值             */
    signed int gyro_y_adc;      /* 陀螺仪Y轴原始值             */
    signed int gyro_z_adc;      /* 陀螺仪Z轴原始值             */
    signed int accel_x_adc;     /* 加速度计X轴原始值            */
    signed int accel_y_adc;     /* 加速度计Y轴原始值            */
    signed int accel_z_adc;     /* 加速度计Z轴原始值            */
    signed int temp_adc;        /* 温度原始值                */

    /* 下面是计算得到的实际值,扩大100倍 */
    signed int gyro_x_act;      /* 陀螺仪X轴实际值             */
    signed int gyro_y_act;      /* 陀螺仪Y轴实际值             */
    signed int gyro_z_act;      /* 陀螺仪Z轴实际值             */
    signed int accel_x_act;     /* 加速度计X轴实际值            */
    signed int accel_y_act;     /* 加速度计Y轴实际值            */
    signed int accel_z_act;     /* 加速度计Z轴实际值            */
    signed int temp_act;        /* 温度实际值                */
};

struct icm20608_dev_struc icm20608_dev; /* icm20608设备 */

/* 函数声明 */
unsigned char icm20608_init(void);
void icm20608_write_reg(unsigned char reg, unsigned char value);
unsigned char icm20608_read_reg(unsigned char reg);
void icm20608_read_len(unsigned char reg, unsigned char *buf, unsigned char len);
void icm20608_getdata(void);

#endif

http://www.kler.cn/a/528892.html

相关文章:

  • leetcode 844 比较含退格的字符串
  • 早期车主告诉后来者,很后悔买电车,一辈子都被车企拿捏了
  • 书生大模型实战营3
  • 17.2 图形绘制7
  • 实现网站内容快速被搜索引擎收录的方法
  • 运算符重载(输出运算符<<) c++
  • 《解锁AI黑科技:数据分类聚类与可视化》
  • 1979-2021年 全国各省、地级市、区县空气流通系数
  • Google Chrome-便携增强版[解压即用]
  • DeepSeek模型与OpenAI模型原理和技术架构的异同分析
  • 深度学习 Pytorch 神经网络的学习
  • npm 和 pip 安装中常见问题总结
  • xss-labs靶场
  • 基于 STM32 的智能电动车防盗与管理系统
  • 基于YOLO11的肺结节检测系统
  • 【博弈论 学习】Chapter1. 策略式博弈与Nash均衡
  • sqli-labs靶场通关
  • 深入理解Java中的String
  • 2025年1月个人工作生活总结
  • 86.(2)攻防世界 WEB PHP2
  • 14-9-1C++STL的set容器
  • Android --- CameraX讲解
  • AI开发之 ——Anaconda 介绍
  • Easy系列PLC尺寸测量功能块ST代码(激光微距仪应用)
  • 动态规划DP 背包问题 完全背包问题(题目分析+C++完整代码)
  • 算法【有依赖的背包】