NXP S32K146 FLEXI2C底层驱动+IAM-20680(一)

IAM-20680

概述:
IAM-20680是一个用于汽车应用的6轴运动跟踪设备,它将一个3轴陀螺仪和一个3轴加速度计结合在一个小型3x3x0.75mm(16针LGA)包中。它还具有一个512字节的FIFO,可以降低串行总线接口上的流量,然后进入低功耗模式,允许系统处理器突发读取传感器数据的流量,并降低功耗。IAM-20680通过其6轴集成,使制造商能够消除昂贵而复杂的选择、认证和系统级集成,从而保证最佳的运动性能。
该陀螺仪有一个可编程的全规模范围的±250dps,±500dps,±1000dps,和±2000dps。加速度计具有用户可编程加速度计全尺寸范围±2g、±4g、±8g和±16g。两个传感器的初始灵敏度降低了生产线的校准要求。其他行业领先的功能包括片上的16位adc、可编程数字滤波器、嵌入式温度传感器和可编程中断。该设备具有I2C和SPI串行接口,VDD操作范围为1.71V到3.6V,以及一个独立的数字IO电源,VDDIO从1.71V到3.6V。
特点:
①应用程序处理器的低功耗操作中的运动唤醒中断
②根据AEC-Q100标准进行的可靠性测试(车规级)
③使用40 0kHz的I2C或8MHz的SPI与设备的所有寄存器进行通信。

在这里插入图片描述
注:①FS_SEL=0 ±250 dps 是一个寄存器中的两个bit配置角速度的选择
在这里插入图片描述
AFS_SEL=0是加速的配置寄存器ACCEL_CONFIG的ACCEL_FS_SEL[1:0]
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
关于FIFO
IAM-20680包含一个512字节的FIFO寄存器,可以通过串行接口访问。FIFO配置寄存器决定哪些数据被写入FIFO。可能的选择包括陀螺仪数据、加速度计数据、温度读数和FSYNC输入。FIFO计数器跟踪FIFO中包含多少字节。FIFO寄存器支持突发读取。中断功能可用于确定何时有新数据。IAM-20680允许在低功耗加速计模式下进行FIFO读取。

传感器初始化及初始配置

IAM-20680的基本配置包括以下步骤:
①传感器的初始化和时钟源的选择
②输出数据速率(即采样频率)的选择
③全尺寸范围选择
④滤波器频率选择
⑤电源模式选择
传感器的初始化和时钟源的选择
要初始化传感器,请执行复位,并让IAM-20680通过将寄存器PWR_MGMT_1(地址0x6B)设置为0x81来选择最佳时钟源。
输出数据速率选择
要将输出数据速率(ODR)设置为所需的频率,请通过将寄存器SMPLRT_DIV(地址0x19)设置为所需的值来选择采样速率分隔器。例如,要将输出数据速率设置为100Hz,请将0x09写入SMPLRT_DIV。
全尺寸范围选择
要设置加速度计的全比例范围(FSR),请将寄存器ACCEL_CONFIG(地址0x1C)设置为所需值。例如,要将加速度计的FSR设置为2g,请将0x00写入ACCEL_CONFIG。要设置陀螺仪的FSR,请将寄存器GYRO_CONFIG(地址0x1B)设置为所需的值。例如,要将陀螺仪的FSR设置为250dps,请将0x00写入GYRO_CONFIG。
过滤器选择
要设置加速度计的数字低通滤波器(DLPF)的转角频率,请将寄存器ACCEL_CONFIG2(地址0x1D)设置为所需值。例如,要将加速度计DLPF的角频率设置为10.2Hz,将0x05写入ACCEL_CONFIG2。要设置陀螺仪DLPF的角频率,请将寄存器配置(地址0x1A)设置为所需值。例如,要将陀螺仪DLPF的角频率设置为10Hz,将0x05写入配置。


可编程中断

IAM-20680有一个可编程的中断系统,它可以在INT引脚上产生一个中断信号。状态标志表示中断的来源。可以单独启用和禁用中断的源。
在这里插入图片描述
运动检测/FIFO溢出/数据准备
运动唤醒中断
这个功能应该和MPU6050自由落地中断,加速度中断,静止中断一样。即IAM-20680提供了运动检测能力。一个合格的运动样本是指来自任何轴的高传递的样本具有超过用户可编程阈值的绝对值。以下步骤说明如何配置运动中唤醒中断。
在这里插入图片描述

大概了解功能之后我们先撘一下底层的代码,用的是FLEXI2C,SDK3.0.0.开发环境是S32DS,平台是S32K146。代码还是之前UART+CAN的那个为基础。前面的文章有介绍!!!
NXP S32K146 CAN通讯 TJA1043(二)

SDK封装

/*flex io i2c*/
/* func:   mcu flexioi2c open API
 * prara:  none
 * return: BOOLEAN
 */
BOOLEAN hal_flexio_i2c_open(void)
{
	FLEXIO_DRV_InitDevice(INST_FLEXIO_I2C1, &flexIODeviceState);
	INT_SYS_SetPriority(FLEXIO_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1);
	if (FLEXIO_I2C_DRV_MasterInit(INST_FLEXIO_I2C1, &flexio_i2c1_MasterConfig0, &i2cMasterState))
	{
		return FALSE;
	}
	if (hal_exrtc_handle.tranf_mutex == NULL)
	{
		hal_exrtc_handle.tranf_mutex = xSemaphoreCreateMutex();
		if (hal_exrtc_handle.tranf_mutex == NULL)
		{
			return FALSE;
		}
	}
	hal_exrtc_handle.ch_status = I2C_STATUS_OPEN;
	return TRUE;
}
/* func:   mcu flexioi2c read API
 * prara:  
 * return: BOOLEAN
 */
BOOLEAN hal_flexio_i2c_write(i2c_data_t *data)
{
	INT8U* buf;
	INT16U len=data->len+1;
	if (hal_exrtc_handle.ch_status==I2C_STATUS_CLOSE)
	{
		return false;
	}
	len = len - (len % 4) + 4;
	buf = os_malloc(len * sizeof(INT8U));
	if (NULL == buf)
	{
		return FALSE;
	}
	BOOLEAN status = xSemaphoreTake(hal_exrtc_handle.tranf_mutex, 1000);
	if (status == FALSE)
	{
		return FALSE;
	}
	FLEXIO_I2C_DRV_MasterInit(INST_FLEXIO_I2C1, &flexio_i2c1_MasterConfig0, &i2cMasterState);
	buf[0] = data->regaddr;
	memcpy(&buf[1], data->data, data->len);
	if (FLEXIO_I2C_DRV_MasterSendDataBlocking(&i2cMasterState, &buf, data->len + 1, true, 100ul))
	{
		xSemaphoreGive(hal_exrtc_handle.tranf_mutex);
		vPortFree(buf);
		return FALSE;
	}
	xSemaphoreGive(hal_exrtc_handle.tranf_mutex);
	vPortFree(buf);
	return TRUE;

}

BOOLEAN hal_flexio_i2c_read(i2c_data_t *data)
{
    INT8U dev_regaddr = data->regaddr;

    if (hal_exrtc_handle.ch_status == I2C_STATUS_CLOSE)
    {
        return FALSE;
    }
    BOOLEAN status = xSemaphoreTake(hal_exrtc_handle.tranf_mutex, 1000);
    if (status == FALSE)
    {
        return FALSE;
    }
    FLEXIO_I2C_DRV_MasterInit(INST_FLEXIO_I2C1, &flexio_i2c1_MasterConfig0, &i2cMasterState);

    if (FLEXIO_I2C_DRV_MasterSendDataBlocking(&i2cMasterState, &dev_regaddr, 1, true, 100UL))
    {
        xSemaphoreGive(hal_exrtc_handle.tranf_mutex);
        return FALSE;
    }
    if (FLEXIO_I2C_DRV_MasterReceiveDataBlocking(&i2cMasterState, data->data, data->len, true, 100UL))
    {
        xSemaphoreGive(hal_exrtc_handle.tranf_mutex);
        return FALSE;
    }
    xSemaphoreGive(hal_exrtc_handle.tranf_mutex);

    return TRUE;
}

在这里插入图片描述

目前是在freertos基础上使用flexIO I2c,因为公司的PCBA没有寄过来,现在先在已有的硬件基础上调驱动,现在的IIC SLAVE是PCF8563 一颗时钟芯片, 写地址是A2H 读地址是A3H。为什么slave addr是81呢?是因为从机地址前七位为设备地址最后一位为读写方向,即1010001是前7位在没加上读写位之前是01010001即0x51 十进制是81,加上读写位之后是10100010/10100011即A2/A3。

在这里插入图片描述

在这里插入图片描述

void vRtcDataProcessingTask(void)
{   
    rtc_time_t time_R;
    uint8_t buf[5]={01,02,03,04,05};
    rtc_time_t time_s =
    {
        .year   = 22,
        .month  = 1,
        .day    = 23,
        .hour   = 20,
        .minute = 51,

       .second = 0,
    };
		count5++;
    //hal_rtc_write(&time_s);
    extern flexio_i2c_master_state_t i2cMasterState;
    FLEXIO_I2C_DRV_MasterSendDataBlocking(&i2cMasterState, &buf, 5+1, true, 100ul);
    for (;;)
    {
     PINS_DRV_TogglePins(PTE,1<<12);
        hal_rtc_read(&time_R);
        vTaskDelay(5000);
    }
}

先忽略我的封装函数,直接用底层SDKFLEXIO_I2C_DRV_MasterSendDataBlocking测试一下,用逻辑分析仪看一下波形。
在这里插入图片描述
关于这个SDK函数我在调试的时候出现过进入硬件中断,当时我把SDKFLEXIO_I2C_DRV_MasterSendDataBlocking放在创建任务与开启定时器之前来写IIC,在该函数内部OSIF_SemaWait用来等待总线空闲信号量,一直等不到进入硬件中断,后来我把这个函数放到任务里就好了。
rtc相关函数封装

static INT8U hal_rtc_DecToBcd2(uint8_t BINValue)
{
    return ((BINValue / 10) << 4) + (BINValue % 10);
}


BOOLEAN hal_rtc_write(rtc_time_t* time)
{
    BOOLEAN status = TRUE;

    i2c_data_t rtc_data;
    INT8U buf[7] = {0};

    if (time->month > 12 || time->month == 0  || time->day > 31 || time->day == 0 || \
        time->hour > 23  || time->minute > 59 || time->second > 59)
    {
        return FALSE;
    }

    rtc_data.regaddr = TIME_SECONDS;
    rtc_data.len = 7;
    rtc_data.devaddr = RTC_DEV_ADDR;
    buf[6] = hal_rtc_DecToBcd2(time->year % 100);
    buf[5] = hal_rtc_DecToBcd2(time->month);
    buf[4] = 0; /* Sunday */
    buf[3] = hal_rtc_DecToBcd2(time->day);
    buf[2] = hal_rtc_DecToBcd2(time->hour);
    buf[1] = hal_rtc_DecToBcd2(time->minute);
    buf[0] = hal_rtc_DecToBcd2(time->second);
    rtc_data.data = buf;
    status = hal_flexio_i2c_write(&rtc_data);

    return status;
}
static INT8U hal_rtc_Bcd2ToDec(INT8U BCDValue)
{
    INT8U tmp = 0;

    tmp = ((BCDValue & 0xF0) >> 4) * 10;
    tmp = tmp + (BCDValue & 0x0F);
    return tmp;
}

BOOLEAN hal_rtc_read(rtc_time_t* time)
{
	BOOLEAN status = FALSE;
	i2c_data_t rtc_data;
	INT8U buf[7] = {0};

	rtc_data.regaddr = TIME_SECONDS;
	rtc_data.len = 7;
	rtc_data.devaddr = RTC_DEV_ADDR;
	rtc_data.data = buf;
	if(TRUE == hal_flexio_i2c_read(&rtc_data))
	{
		time->year	 = hal_rtc_Bcd2ToDec(buf[6] & 0xFF) + 2000;
		time->month  = hal_rtc_Bcd2ToDec(buf[5] & 0x1F);
		time->day	 = hal_rtc_Bcd2ToDec(buf[3] & 0x3F);
		time->hour	 = hal_rtc_Bcd2ToDec(buf[2] & 0x3F);
		time->minute = hal_rtc_Bcd2ToDec(buf[1] & 0x7F);
		time->second = hal_rtc_Bcd2ToDec(buf[0] & 0x7F);
		status = TRUE;
	}
}

从02H寄存器开始连续写7个寄存器。
在这里插入图片描述
效果
在这里插入图片描述
在这里插入图片描述

enum GyroState_e
{
    GYRO_INIT_C,	    //初始化控制寄存器
    GYRO_SET_T,	        //初始化时间寄存器
    GYRO_WORKING,		//正常工作状态
};
void PCF8563_Init_C(void)
{
	i2c_data_t init_data;
	INT8U buf[2] = {0};
	buf[0]=0x00;
	buf[1]=0x00;
	init_data.regaddr = CONTROL_REG;
	init_data.len = 2;
	init_data.devaddr = RTC_DEV_ADDR;
	init_data.data = buf;

	hal_flexio_i2c_write(&init_data);
	eGyro_State = GYRO_SET_T;
}
enum GyroState_e eGyro_State= GYRO_INIT_C;

/**
  * @Description  PCF8563模块时间初始化
  * @param  无
  * @retval 无
  */
void PCF8563_Init_T(void)
{
	rtc_time_t time_s =
    {
        .year   = 22,
        .month  = 01,
        .day    = 24,
        .hour   = 14,
        .minute = 23,
        .second = 11,
    };
    hal_rtc_write(&time_s);
	eGyro_State = GYRO_WORKING;
}
void vRtcDataProcessingTask(void)
{   
	eGyro_State = GYRO_INIT_C;
    for (;;)
    {
        if(eGyro_State==GYRO_INIT_C)
    	{
		    PCF8563_Init_C();
    	}
		else if(eGyro_State==GYRO_SET_T)
		{
		    PCF8563_Init_T();
		}
		else if(eGyro_State == GYRO_WORKING)
		{
	        PINS_DRV_TogglePins(PTE,1<<12);
	        hal_rtc_read(&time_R);
	        vTaskDelay(5000);
		}
    }
}

初始化控制寄存器
在这里插入图片描述
初始化时间数据
在这里插入图片描述
每隔5S读一下数据
在这里插入图片描述
在这里插入图片描述

结论:
任务处理读写地址数据API完成。接下来我们在(一)的基础上写接口/应用层代码,处理六轴陀螺仪的数据,及零点漂移的问题。

好的,下面是一个基本的S32K144 LPI2C驱动的代码框架,供您参考: ```c #include "S32K144.h" // 定义I2C时钟频率 #define I2C_CLOCK_FREQ 100000 // 100 kHz // 定义I2C读写操作标志 #define I2C_WRITE 0 #define I2C_READ 1 // 定义I2C传输状态标志 #define I2C_IDLE 0 #define I2C_BUSY 1 // 定义I2C错误代码 #define I2C_OK 0 #define I2C_ERROR -1 // 定义I2C传输结构体 typedef struct { uint8_t addr; // 7位I2C设备地址 uint8_t dir; // 读写方向 uint8_t *data; // 数据缓冲区指针 uint32_t len; // 数据长度 uint32_t index; // 当前传输数据的下标 uint8_t status; // 传输状态 uint8_t error; // 错误代码 } i2c_transfer_t; // 定义LPI2C模块指针 LPI2C_Type *i2c = LPI2C0; // 定义I2C传输结构体实例 i2c_transfer_t i2c_transfer; // I2C初始化函数 void i2c_init() { // 使能LPI2C时钟 PCC->PCCn[PCC_LPI2C0_INDEX] |= PCC_PCCn_CGC_MASK; // 设置LPI2C引脚复用 PORTD->PCR[8] = PORT_PCR_MUX(2) | PORT_PCR_ODE_MASK; // SCL PORTD->PCR[9] = PORT_PCR_MUX(2) | PORT_PCR_ODE_MASK; // SDA // 重置LPI2C模块 i2c->MCR |= LPI2C_MCR_RST_MASK; while (i2c->MCR & LPI2C_MCR_RST_MASK); // 配置LPI2C时钟 uint32_t clock_freq = (uint32_t)(CLOCK_GetBusClkFreq() / 1000000); i2c->MCCR0 = (clock_freq / (5 * I2C_CLOCK_FREQ)) - 1; i2c->MCCR1 = (clock_freq / (5 * I2C_CLOCK_FREQ)) - 1; // 配置LPI2C控制寄存器 i2c->MCR |= LPI2C_MCR_MEN_MASK; // 使能LPI2C模块 i2c->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0); // 设置预分频器 i2c->MCFGR2 = LPI2C_MCFGR2_BUSIDLE(10); // 设置总线空闲时间 i2c->MCFGR3 = LPI2C_MCFGR3_PINLOW(10) | LPI2C_MCFGR3_SDAHOLD(10); // 配置SDA低电平时间和SDA保持时间 i2c->MCR |= LPI2C_MCR_FRZ_MASK; // 冻结LPI2C模块 } // 发起I2C传输函数 int i2c_transfer(uint8_t addr, uint8_t dir, uint8_t *data, uint32_t len) { // 判断是否有其他传输正在进行 if (i2c_transfer.status == I2C_BUSY) { return I2C_ERROR; } // 保存传输信息 i2c_transfer.addr = (addr << 1) | dir; i2c_transfer.dir = dir; i2c_transfer.data = data; i2c_transfer.len = len; i2c_transfer.index = 0; i2c_transfer.status = I2C_BUSY; i2c_transfer.error = I2C_OK; // 配置LPI2C传输 i2c->MTDR = i2c_transfer.addr; i2c->MCR &= ~LPI2C_MCR_FRZ_MASK; // 解冻LPI2C模块 i2c->MCR |= LPI2C_MCR_TX_MASK; // 设置为发送模式 i2c->MCR &= ~LPI2C_MCR_MEN_MASK; // 禁止LPI2C模块 i2c->MCR |= LPI2C_MCR_MEN_MASK; // 使能LPI2C模块 return I2C_OK; } // LPI2C中断服务函数 void LPI2C0_IRQHandler() { // 判断是否是传输完成中断 if (i2c->MSR & LPI2C_MSR_MBF_MASK) { // 判断是否是读取模式 if (i2c_transfer.dir == I2C_READ) { i2c_transfer.data[i2c_transfer.index++] = i2c->MRDR; } else { // 判断是否还有数据需要发送 if (i2c_transfer.index < i2c_transfer.len) { i2c->MTDR = i2c_transfer.data[i2c_transfer.index++]; } else { // 传输完成,禁用LPI2C中断 i2c_transfer.status = I2C_IDLE; i2c->MIER &= ~LPI2C_MIER_TDIE_MASK; } } } // 判断是否是传输错误中断 if (i2c->MSR & LPI2C_MSR_NDF_MASK) { i2c_transfer.status = I2C_IDLE; i2c_transfer.error = I2C_ERROR; i2c->MIER &= ~LPI2C_MIER_TDIE_MASK; } } ``` 这是一个基本的I2C驱动框架,实现了基本的读写操作和错误处理。需要注意的是,该代码只是一个框架,实际使用时还需要根据具体的需求进行修改和优化。同时,该代码中使用了LPI2C模块来实现I2C通信,如果您使用的是别的I2C控制器,代码需要进行相应的修改。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值