加速度传感器(STK8321)的初始化
阅读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, ®_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
上一篇: Redis高效检索地理位置的原理解析
下一篇: Win10专业版如何使用分屏快捷键