欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

加速度传感器(STK8321)的初始化

程序员文章站 2024-01-01 17:11:28
阅读STK8321的数据手册,我们知道STK8321是一颗具有 ±2g/±4g/±8g的3轴线性加速度传感器,它支持i²c和spi接口通信,具有极低的电流消耗(微安级),同时具有32级深度的FIFO,因此广泛应用与各类电子设备。我这里将其应用在智能手环上,主控MCU采用NRF52832,通过SPI读取3轴加速度数据,进行常规的步行统计、方向识别等。根据使用场景要求,需要把STK8321设置在低功耗模式,采用等时采样的FIFO方式,并通过中断信号,通知主芯片NRF52832读取FIFO缓存。软件流程如下:...

阅读STK8321的数据手册,我们知道STK8321是一颗具有 ±2g/±4g/±8g的3轴线性加速度传感器,它支持i²c和spi接口通信,具有极低的电流消耗(微安级),同时具有32级深度的FIFO,因此广泛应用与各类电子设备。
我这里将其应用在智能手环上,主控MCU采用NRF52832,通过SPI读取3轴加速度数据,进行常规的步行统计、方向识别等。根据使用场景要求,需要把STK8321设置在低功耗模式,采用等时采样的FIFO方式,并通过中断信号,通知主芯片NRF52832读取FIFO缓存。

软件流程如下:
1,初始化相关GPIO管脚
2,初始化MCU的SPI接口及中断配置
3,传感器检测
4,传感器配置
5,NRF52832中断读取FIFO
6,NRF52832数据处理
7,异常检测处理

详细代码:
1,GPIO相关,包含SPI的SCK, MISO,MOSI,CS,电源控制管脚,初始化之前,所有管脚先设置为输出低电平并延时一段时间,以释放芯片及管脚上残留电量。

    /**
     *  Init SPI Pin of Platform
     */
	/**
	 * SPI MOSI
	 */
	nrf_gpio_cfg(
            BMA_SPI_MOSI_PIN,
            NRF_GPIO_PIN_DIR_OUTPUT,
            NRF_GPIO_PIN_INPUT_DISCONNECT,
            NRF_GPIO_PIN_PULLUP,
            NRF_GPIO_PIN_S0S1,
            NRF_GPIO_PIN_NOSENSE);
    
	nrf_gpio_pin_clear(STK_SPI_MOSI_PIN);

	/**
	 * SPI SCL
	 */
	nrf_gpio_cfg(
            BMA_SPI_SCL_PIN,
            NRF_GPIO_PIN_DIR_OUTPUT,
            NRF_GPIO_PIN_INPUT_DISCONNECT,
            NRF_GPIO_PIN_PULLUP,
            NRF_GPIO_PIN_S0S1,
            NRF_GPIO_PIN_NOSENSE);
    
	nrf_gpio_pin_clear(STK_SPI_SCL_PIN);
	
	/**
	 * SPI MISO
	 */	
    nrf_gpio_cfg(
            BMA_SPI_MISO_PIN,
            NRF_GPIO_PIN_DIR_OUTPUT,
            NRF_GPIO_PIN_INPUT_DISCONNECT,
            NRF_GPIO_PIN_PULLUP,
            NRF_GPIO_PIN_S0S1,
            NRF_GPIO_PIN_NOSENSE);	
	nrf_gpio_pin_clear(STK_SPI_MISO_PIN);		
			
	/** SPI CS */
	nrf_gpio_cfg_output(STK_SPI_CS_PIN);
	nrf_gpio_pin_clear(STK_SPI_CS_PIN);	
	
	/** POWER PIN */
	nrf_gpio_cfg_output(STK_POWER_PIN);
	cil_power_down();
	
	nrf_delay_ms(50);

2,初始化SPI,及MCU中断配置,SPI采用硬件SPI,中断触发类型为上升沿触发。

	nrf_gpio_cfg_output(BMA_POWER_PIN);
	cil_power_down();
	
	nrf_delay_ms(50);
	
	cil_power_on();
	nrf_delay_ms(5);	
	
	nrf_gpio_pin_set(BMA_SPI_MOSI_PIN);
	nrf_gpio_pin_set(BMA_SPI_SCL_PIN);
	nrf_gpio_pin_set(BMA_SPI_CS_PIN);
	
	/**
	 * SPI MISO
	 */	
    nrf_gpio_cfg(
            BMA_SPI_MISO_PIN,
            NRF_GPIO_PIN_DIR_INPUT,
            NRF_GPIO_PIN_INPUT_CONNECT,
            NRF_GPIO_PIN_PULLUP,
            NRF_GPIO_PIN_S0S1,
            NRF_GPIO_PIN_NOSENSE);

    /**
     * Init INT of Platform
     */ 
	nrf_gpio_pin_pull_t config = NRF_GPIO_PIN_PULLDOWN;
    nrf_gpio_cfg_input(STK_INT2_PIN, config);
	
	// GPIOTE0 chanenl 0 as event
	// for GPIO pin STK_INT2_PIN (23)
	// Rasing trigger interrupt
    NRF_GPIOTE->CONFIG[0] = 1 << 0
                     |(STK_INT2_PIN << 8)
                     |(1 << 16);
	// Enable interrupt
	NRF_GPIOTE->INTENSET = 0x1;
	  
	// Set interrupt priority
    NVIC_SetPriority(GPIOTE_IRQn, 1);
    NVIC_ClearPendingIRQ(GPIOTE_IRQn);
    NVIC_EnableIRQ(GPIOTE_IRQn);
    
    /**
     * Init SPI of Platform
     */
	spi_configure(NRF_SPI0, STK_SPI_SCL_PIN, STK_SPI_MOSI_PIN, STK_SPI_MISO_PIN);

SPI分为两层,下层对接NRF52832硬件SPI接口,上层对接STK8321的调用:

static signed char STK_SPI_bus_write(unsigned char dev_addr,
	unsigned char reg_addr, unsigned char *reg_data, unsigned char cnt)
{
	int iError = 0;
	unsigned int i = 0;

	CS_STK_ENABLE();
	NRF_SPI0->ENABLE = 1; 
    
	//When 0, the data SDI is written into the chip
	spi_transfer( NRF_SPI0, (reg_addr)|0x00 ); 
	for( i = 0; i < cnt; i++ )
	{
		spi_transfer( NRF_SPI0, reg_data[i]);
	}
    
    NRF_SPI0->ENABLE = 0;
	CS_STK_DISABLE();
	return (signed char)iError;
}

static signed char STK_SPI_bus_read(unsigned char dev_addr, 
	unsigned char reg_addr, unsigned char *reg_data, unsigned char cnt)
{
	int iError = 0;
	unsigned int i = 0;
	CS_STK_ENABLE();
	 
    NRF_SPI0->ENABLE = 1; 
    
	//When 1, the data SDO from the chip is read.
	spi_transfer( NRF_SPI0, (reg_addr)|0x80); 
	for ( i = 0; i < cnt; i++ )
	{
		reg_data[i] = spi_transfer( NRF_SPI0, 0xff) & 0xFF;
	}

	NRF_SPI0->ENABLE = 0;	
	CS_STK_DISABLE();
	return (signed char)iError;
}

void stk8321_spi_write_reg(unsigned char reg_addr, unsigned char reg_data)
{
	STK_SPI_bus_write(STK832x_slave_addr, reg_addr, &reg_data, 1);
}

void stk8321_spi_read_reg(unsigned char reg_addr, unsigned char* reg_data,unsigned char len)
{	
	STK_SPI_bus_read(STK832x_slave_addr, reg_addr, reg_data, len);
}

3,传感器检测,主要是通过读取芯片ID,来判断SPI通讯是否成功。

uint8_t get_chip_id_stk8321(void)
{
	uint8_t chip_id = 0;
	
	stk8321_spi_read_reg(0x00, &chip_id,1);    
    
    return chip_id;
}

STK8321的寄存器0x00为只读类型,它的值就是芯片ID,根据手册,它的值为0x23,只要读取到的值一致,说明SPI通讯没问题了。
4,传感器配置,最重要、最繁琐的步骤,需要仔细的阅读芯片手册。

int stk8321_init( void )
{
    unsigned char  cnt = 0;
    unsigned char BackRead = 0;
	
	stk8321_error_cnt = 0;
			
	stk8321_spi_write_reg(0x14,0xB6); // soft reset
	nrf_delay_ms(10);
	
	cnt = 0;
    do{
        stk8321_spi_write_reg(0x00,0x23);  
		stk8321_spi_read_reg (0x00, &BackRead,1);
	}while( BackRead != 0x23 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;

	cnt = 0;
    do{
		stk8321_spi_write_reg(0x0F,0x03); // set Range +/= 2G
		stk8321_spi_read_reg (0x0F, &BackRead,1);
	}while( BackRead != 0x03 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
         
	cnt = 0;
    do{
		stk8321_spi_write_reg(0x20,0x04); // push-pull, active high
		stk8321_spi_read_reg (0x20, &BackRead,1);
	}while( BackRead != 0x04 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
	
	cnt = 0;
    do{
		stk8321_spi_write_reg(0x27,0x00);
		stk8321_spi_read_reg (0x27, &BackRead,1);
	}while( BackRead != 0x00 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
	
	cnt = 0;
    do{
		stk8321_spi_write_reg(0x28,0x14);
		stk8321_spi_read_reg (0x28, &BackRead,1);
	}while( BackRead != 0x14 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
	
	cnt = 0;
    do{
		stk8321_spi_write_reg(0x16,0x07);
		stk8321_spi_read_reg (0x16, &BackRead,1);
	}while( BackRead != 0x07 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;

	cnt = 0;
    do{
		stk8321_spi_write_reg(0x2A,0x04);
		stk8321_spi_read_reg (0x2A, &BackRead,1);
	}while( BackRead != 0x04 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
      
	cnt = 0;
    do{
		stk8321_spi_write_reg(0x19,0x04);
		stk8321_spi_read_reg (0x19, &BackRead,1);    
	}while( BackRead != 0x04 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;

	cnt = 0;
    do{
		stk8321_spi_write_reg(0x17,0x40); // enable fifo watermark interrupt
		stk8321_spi_read_reg (0x17, &BackRead,1);
	}while( BackRead != 0x40 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
	
	cnt = 0;
    do{
		stk8321_spi_write_reg(0x1A,0x40); // fifo interrupt mapping to INT2
		stk8321_spi_read_reg (0x1A, &BackRead,1);
	}while( BackRead != 0x40 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
	
	cnt = 0;
    do{
		stk8321_spi_write_reg(0x3D,FIFO_DEPTH); // FIFO watermark level, max. is 16
		stk8321_spi_read_reg (0x3D, &BackRead,1);
	}while( BackRead != FIFO_DEPTH && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
	
	
	cnt = 0;
    do{
		stk8321_spi_write_reg(0x3E,0xC0); // FIFO mode selection, set FIFO buffer in stream mode
		stk8321_spi_read_reg (0x3E, &BackRead,1);
	}while( BackRead != 0xC0 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;

	cnt = 0;
    do{
		stk8321_spi_write_reg(0x11,0x76); // set low-power mode ODR~34Hz (sleep dur=25ms), equidistant sampling mode for fifo
		stk8321_spi_read_reg (0x11, &BackRead,1);
	}while( BackRead != 0x76 && (++cnt<32));
    if( cnt >= 32 )
      stk8321_error_cnt++;
	
    return ( stk8321_error_cnt != 0 )?(-1):(0);    
}

每个寄存器的配置,都采用写入-读取-验证的方式,以确保写入成功,写入失败后,可以继续尝试32次,增加一定的容错处理。

5,当以上步骤完成时,STK8321就可以进入正常工作, 它会每隔25ms采集一个XYZ数据,存放在FIFO寄存器中,当采集满16组数据后,STK8321会在中断引脚上输出一个高电平信号,MCU捕捉到这信号之后即可读取FIFO数据:

int stk8321_read_accel_xyz_fifo(struct stk8321_accel_data_fifo * fifo_buffer, int length)
{
	int com_rslt = 0;
    
	stk8321_spi_read_reg( 0x3F, gsensor_buff, FIFO_DEPTH*6);
	
	for ( int j = 0; j < FIFO_DEPTH; j++ )
	{
		fifo_buffer[j].x = (gsensor_buff[(j*6)+1] *16) + (gsensor_buff[j*6]/16);
		fifo_buffer[j].y = (gsensor_buff[(j*6)+3] *16) + (gsensor_buff[(j*6)+2]/16);
		fifo_buffer[j].z = (gsensor_buff[(j*6)+5] *16) + (gsensor_buff[(j*6)+4]/16);
		
		if(fifo_buffer[j].x>2047)
			fifo_buffer[j].x = fifo_buffer[j].x-4096;
		if(fifo_buffer[j].y>2047)
			fifo_buffer[j].y = fifo_buffer[j].y-4096;
		if(fifo_buffer[j].z>2047)
			fifo_buffer[j].z = fifo_buffer[j].z-4096;
        fifo_buffer[j].x /= 1; fifo_buffer[j].y /= 1; fifo_buffer[j].z /= 1;
	}
	
	return com_rslt;
}

6,至此,程序已经拿到三轴原始数据,可以将该原始数据传入具体的算法去处理。

7,实际使用中,STK8321有可能会出现故障,例如中断的频率变快、或中断信号消失,我们需要一个机制检测这种情况,作出相应的处理。 我这里采用的是监控中断周期的方法,即程序记录一段时间内的中断个数,正常来说,中断个数应该保持一个固定的数,一旦超出了范围,即认为发生了错误,可以对传感器进行重新复位初始化,使之恢复正常。代码片段:

	/** Read the 3-accel data from fifo buffer */
	stk8321_read_accel_xyz_fifo((struct stk8321_accel_data_fifo *)fifo_buf, FIFO_DEPTH*6 );
	/** 
	 * Less than 8*50=400ms indicate a error ocurr, we
	 * need to re-configure the gsensor.
	 */
	d4time = osal_systemClock - stepInterruptTime ;
	
	stepInterruptTime = osal_systemClock;
	
    if ( (d4time < (8-1)) || (d4time > 10) ) /* 8*system tick(50ms) = 400ms */
	{
		osal_set_event( task_id, TASK_STEP_BMA_RE_INIT_EVT );
		return ( events ^ STEP_TASK_DECT_EVT );
	}

频率过慢过快,或者5秒内没有任何中断信号产生,程序需要对STK8321重新初始化。

	/***************************************************************************
	 *                                                                         *
	 * To reiniatize the gsensor stk8321(or bma250e) if it not interruption     *
	 * out of 5s                                                              *
	 *                                                                         *
	 ***************************************************************************/	
	if ( events & TASK_STEP_STK8321_RE_INIT_EVT )
	{		
		osal_start_timerEx( task_id, TASK_STEP_STK8321_RE_INIT_EVT , 5000);	
		 
		stk8321_init();
		
		return ( events ^ TASK_STEP_BMA_RE_INIT_EVT ); 
	}

上面的仅仅介绍了数据采集流程,具体参数如何配置,需要根据自己的业务需求配合datasheet才行。

本文地址:https://blog.csdn.net/wenshifang/article/details/110185021

上一篇:

下一篇: