This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Problems with TWI when using Bluetooth LE simultaneously (taking wrong data from TWI when enable BLE)

Hi. I'm developing IMU system for multiple purpose using ICM20948 & nRF52832.

But there are problems on TWI communications when using BLE simultaneously.

If not use BLE, all the data comes from IMU sensor is pretty well as shown below:

Small deviation is deliberate data for test purpose.

But after enable BLE, wrong data comes out from ICM20948 sensor as below. It is connected with TWI lines.

 Only X-Axis are normal data. I attached source codes which get or send data to ICM20948 sensor.

/*
 *  Roverdyn I2C(TWI) Library
 *  Created at Jan. 14, 2020
 *  Author : Seonguk Jeong
 *  Copyright to 2019 Seonguk Jeong. All rights reserved.
 *  Contact : [email protected]
 */

#include "Wire.h"

void _Wire::begin(){
	// I2C 통신을 활성화 시키는 함수
	// 기본 통신 속도는 400kHz
	// 기본 설정 핀 : SCL(12번), SDA(11번)
	// 설정 핀은 Wire.h 에서 바꿀 수 있음

	// I2C 통신 비활성화
	NRF_TWI0->ENABLE = 0;

	// SCL/SDA핀 설정
	NRF_TWI0->PSELSCL = pinSCL;
	NRF_TWI0->PSELSDA = pinSDA;

	// 통신 주파수 설정(400kHz)
	NRF_TWI0->FREQUENCY = 0x06680000;

	// 인터럽트 비활성화
	NRF_TWI0->INTENCLR = 0x044286;

	// I2C 통신 활성화
	NRF_TWI0->ENABLE = 5;

	// 이벤트 초기화
	NRF_TWI0->EVENTS_RXDREADY = 0;
	NRF_TWI0->EVENTS_TXDSENT = 0;
}

uint8_t _Wire::requestFrom(uint8_t address, uint8_t bytes){
	// Slave device로부터 데이터를 요청하는 함수
	uint8_t recvLength = 0;

	// 통신 주소 및 Read 신호 송신
	NRF_TWI0->ADDRESS = address;

	NRF_TWI0->TASKS_STARTRX = 1;

	for(uint8_t i=0;i<bytes;i++){
		// Byte Boundary 이벤트 확인
		while(!NRF_TWI0->EVENTS_BB){}
		NRF_TWI0->EVENTS_BB = 0;

		// 마지막 데이터가 오면 STOP
		if(i == (bytes - 1)){
			NRF_TWI0->TASKS_STOP = 1;
		}

		// 데이터 읽기
		while(!NRF_TWI0->EVENTS_RXDREADY){}
		NRF_TWI0->EVENTS_RXDREADY = 0;
		rxBuffer[i] = NRF_TWI0->RXD;
	}

	return recvLength;
}

void _Wire::beginTransmission(uint8_t address){
	// TWI 데이터 전송을 시작하는 함수
	NRF_TWI0->ENABLE = 5;
	NRF_TWI0->ADDRESS = address;
	NRF_TWI0->TASKS_STARTTX = 1;
	memset(&rCount, 0x00, sizeof(rCount));
	memset(&rxBuffer, 0x00, sizeof(rxBuffer));
	rCount = 0;
}

uint8_t _Wire::endTransmission(bool toggle){
	// TWI 통신 종료 함수
	uint8_t err_code = 0;
	if(toggle){
		if(!NRF_TWI0->EVENTS_STOPPED){NRF_TWI0->TASKS_STOP = 1;}
		while(!NRF_TWI0->EVENTS_STOPPED){}
		NRF_TWI0->EVENTS_STOPPED = 0;
		err_code = 1;
	}
	NRF_TWI0->ENABLE = 0;
	return err_code;
}

uint8_t _Wire::write(uint8_t data){
	NRF_TWI0->TXD = data;
	while(!NRF_TWI0->EVENTS_TXDSENT){}
	NRF_TWI0->EVENTS_TXDSENT = 0;
	return 0;
}

uint8_t _Wire::available(){
	return 0;
}

uint8_t _Wire::read(){
	static uint8_t output = 0;
	output = rxBuffer[rCount];
	rCount++;
	return output;
}

void _Wire::setClock(uint32_t frequency){
	// I2C 통신 속도를 설정하는 함수
	// 별도로 함수를 호출하지 않을 경우 400kHz로 동작

	switch(frequency){
	case 100000:
		// 100Khz로 설정 시
		NRF_TWI1->FREQUENCY = 0x01980000;
		break;
	case 250000:
		// 250Khz로 설정 시
		NRF_TWI1->FREQUENCY = 0x04000000;
		break;
	case 400000:
		// 400Khz로 설정 시
		NRF_TWI1->FREQUENCY = 0x06680000;
		break;
	default:
		NRF_TWI1->FREQUENCY = 0x06680000;
		break;
	}
}

/*
 *  Roverdyn UART Library
 *  Created at Mar. 6, 2020
 *  Author : Seonguk Jeong
 *  Copyright to 2020 Seonguk Jeong. All rights reserved.
 *  Contact : [email protected]
 */

#include "ICM20948.h"
#include <SEGGER_RTT.h>

_Wire Wire;
extern _Serial Serial;

uint8_t _ICM20948::Init(){
	uint8_t err_code = 0;

	// TWI 통신 초기화 및 시작
	Wire.begin();

	// ICM20948 초기화
	writeData(DEVICE_ID, REG_BANK_SEL, 0x00);
	writeData(DEVICE_ID, PWR_MGMT_1, 0x80);
	nrf_delay_ms(100);
	writeData(DEVICE_ID, PWR_MGMT_1, 0x01);
	writeData(DEVICE_ID, PWR_MGMT_2, 0x00);

	// 바이패스 모드 설정
	writeData(DEVICE_ID, INT_PIN_CFG, (0x01 << 1));

	// Device ID 읽기
	// 제대로 연결이 되어 있지 않으면 0x01(에러)를 리턴
	if(getDeviceId() != 234){
		return ROVERDYN_ERROR_NO_DATA;
	}
	if(getMagId() != 0x09){
		return ROVERDYN_ERROR_NO_DATA;
	}
/*
	// Sample rate
	writeData(DEVICE_ID, REG_BANK_SEL, 0x02);
	writeData(DEVICE_ID, GYRO_SMPLRT_DIV, 0x01);
	writeData(DEVICE_ID, ACCEL_SMPLRT_DIV_2, 0x01);
*/
	// 자이로스코프 설정
	setCfgGyro(DLPF_GYRO_6HZ, GYRO_RANGE_1000DPS, GYRO_AVG_1X, true);

	// 가속도 센서 설정
	setCfgAcc(DLPF_ACC_6HZ, ACCEL_RANGE_2G, ACCEL_AVG_4X, true);

	// 지자기 센서 설정
	setMagMode(MAG_CONTINUOUS_MODE2);

	// 온도 센서 설정
	setCfgTemp(TEMP_DLPF_34HZ);

	return err_code;
}

uint8_t _ICM20948::writeData(uint8_t device, uint8_t addr, uint8_t data){
	// 레지스터에 값을 쓰는 함수
	uint8_t err_code = 0;

	// 통신 시작
	Wire.beginTransmission(device);
	Wire.write(addr);
	Wire.write(data);
	Wire.endTransmission(true);

	return err_code;
}

uint8_t _ICM20948::getDeviceId(){
	// Who AM I 레지스터로부터 데이터를 읽어오는 함수
	uint8_t output = 0;

	Wire.beginTransmission(DEVICE_ID);
	Wire.write(WHO_AM_I);
	Wire.requestFrom(DEVICE_ID, 1);
	output = Wire.read();
	Wire.endTransmission(true);
	if(output != 234){
		return ROVERDYN_ERROR_NO_DATA;
	}
	return output;
}

uint8_t _ICM20948::getMagId(){
	// 지자기 센서의 WIA 레지스터로부터 데이터를 읽어오는 함수
	uint8_t output = 0;

	// Get Data
	Wire.beginTransmission(AK09916);
	Wire.write(WIA2);
	Wire.requestFrom(AK09916, 1);
	output = Wire.read();
	Wire.endTransmission(true);

	return output;
}

uint8_t _ICM20948::setCfgAcc(uint8_t ACCEL_DLPF_SEL, uint8_t ACCEL_FS_SEL, uint8_t ACCEL_AVG_SEL, bool Fchoice_b){
	// 가속도 센서의 측정 범위와 DLPF 적용 유무 설정 함수
	uint8_t err_code = 0;

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x20);

	// Set Accel Config 1
	uint8_t data = (ACCEL_DLPF_SEL << 3) | (ACCEL_FS_SEL << 1) | (Fchoice_b);
	writeData(DEVICE_ID, ACCEL_CONFIG, data);

	// Set Accel Config 2
	writeData(DEVICE_ID, ACCEL_CONFIG_2, ACCEL_FS_SEL);

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x00);

	return err_code;
}

uint8_t _ICM20948::setCfgGyro(uint8_t GYRO_DLPF_SEL, uint8_t GYRO_FS_SEL, uint8_t GYRO_AVG_SEL, bool Fchoice_b){
	// 자이로스코프의 측정 범위와 DLPF 적용 유무 설정 함수
	uint8_t err_code = 0;

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x20);

	// Set Gyro Config 1
	uint8_t data = (GYRO_DLPF_SEL << 3) | (GYRO_FS_SEL << 1) | (Fchoice_b);
	writeData(DEVICE_ID, GYRO_CONFIG_1, data);

	// Set Gyro Config 2
	writeData(DEVICE_ID, GYRO_CONFIG_2, GYRO_AVG_SEL);

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x00);

	return err_code;
}

uint8_t _ICM20948::setCfgTemp(uint8_t TEMP_DLPF_SET){
	// 온도 센서의 DLPF 설정
	uint8_t err_code = 0;

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x20);

	// Set DLPF
	writeData(DEVICE_ID, TEMP_CONFIG, TEMP_DLPF_SET);

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x00);

	return err_code;
}

uint8_t _ICM20948::setMagMode(uint8_t MODE){
	// 지자기 센서 설정 함수
	uint8_t err_code = 0;

	// Mode 설정
	writeData(AK09916, CNTL2, MODE);
	return err_code;
}

int16_t* _ICM20948::getMagASA(){
	// 지자기 센서 ASA 데이터 읽기 함수
	static int16_t output[3];

	return output;
}

int16_t* _ICM20948::getRawAcc(){
	// 가속도 센서 Raw 데이터 읽기 함수
	static int16_t output[3] = {0};

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x00);

	// Get Data
	Wire.beginTransmission(DEVICE_ID);
	Wire.write(ACCEL_XOUT_H);
	Wire.requestFrom(DEVICE_ID, 6);
	for(uint8_t i=0;i<3;i++){
		output[i] = Wire.read() << 8 | Wire.read();
	}
	Wire.endTransmission(true);
	return output;
}

int16_t* _ICM20948::getRawGyro(){
	// 자이로스코프 Raw 데이터 읽기 함수
	static int16_t output[3] = {0};

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x00);

	// Get Data
	Wire.beginTransmission(DEVICE_ID);
	Wire.write(GYRO_XOUT_H);
	Wire.requestFrom(DEVICE_ID, 6);
	for(uint8_t i=0;i<3;i++){
		output[i] = Wire.read() << 8 | Wire.read();
	}
	Wire.endTransmission(true);

	return output;
}

int16_t* _ICM20948::getRawMag(){
	// 지자기 센서 Raw 데이터 취득 함수
	static int16_t output[3] = {0};

	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x00);

	// Get Data
	Wire.beginTransmission(AK09916);
	Wire.write(ST1);
	Wire.requestFrom(AK09916, 1);
	bool st1 = Wire.read() & 0x01;
	Wire.endTransmission(true);
	if(st1){
		// Read Mag Data
		Wire.beginTransmission(AK09916);
		Wire.write(HXL);
		Wire.requestFrom(AK09916, 6);
		for(uint8_t i=0;i<3;i++){
			output[i] = Wire.read();
			output[i] = (Wire.read() << 8) | output[i];
		}
		Wire.endTransmission(true);

		// Read ST2 register
		Wire.beginTransmission(AK09916);
		Wire.write(ST2);
		Wire.requestFrom(AK09916, 1);
		bool st2 = (Wire.read() >> 3) & 0x01;
		if(st2){
			return 0;
		}
		Wire.endTransmission(true);
	}
	return output;
}

float _ICM20948::getTemp(float TEMP_OFFSET){
	// 온도 데이터 수신 함수
	float output = 0;
	SEGGER_RTT_printf(0, "TEST\n");
	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x00);

	// Get Data
	Wire.beginTransmission(DEVICE_ID);
	Wire.write(TEMP_OUT_H);
	Wire.requestFrom(DEVICE_ID, 2);
	output = Wire.read() << 8 | Wire.read();
	Wire.endTransmission(true);

	// 측정한 데이터를 Degree 단위로 변환
	output = ((output - TEMP_OFFSET)/TEMP_SENSITIVITY) + 21.0f;

	return output;
}

uint8_t _ICM20948::calibrateAccel(){
	// 가속도 센서 보정 함수
	uint8_t err_code = 0;
	uint16_t count = 0, itinary = 2000;
	int16_t avg[3] = {0}, accOffsetDefault[3] = {0};

	// Low 레지스터 1번 비트 받아옴
	// Select Bank
	writeData(DEVICE_ID, REG_BANK_SEL, 0x10);

	// Get Data
	for(uint8_t i=0;i<3;i++){
		Wire.beginTransmission(DEVICE_ID);
		Wire.write(XA_OFFS_H + i);
		Wire.requestFrom(DEVICE_ID, 2);
		accOffsetDefault[i] = Wire.read() << 8 | Wire.read();
		Wire.endTransmission(true);
	}

	// 초기값 평가
	int16_t *ACCEL;
	do{
		ACCEL = getRawAcc();
		count++;
		for(uint8_t i=0;i<3;i++){
			avg[i] = (float)((float)( (float)(count - 1)/(float)count ) * (float)avg[i - 1]) + ((float)((float)ACCEL[i] / count));
		}
	}while(count  <= itinary);

	while(1){

	}

	return err_code;
}
uint8_t _ICM20948::calibrateGyro(){
	// 자이로스코프 보정 함수
	uint8_t err_code = 0;
	return err_code;
}
uint8_t _ICM20948::calibrateMag(){
	// 지자기센서 보정 함수
	uint8_t err_code = 0;
	return err_code;
}

Please check it.

Thank you.

Related