I'm having a lot of confusion with using TWI on this chip. I have been running example code that I found in this tutorial nRF5 SDK - Tutorial for Beginners Pt 35 B - TWI - I2C with MPU6050 Accelerometer & Gyro Sensor - YouTube. It basically copies the project file from the TWI_scanner and modified the main file to call code in mpu6050.c which actually creates the I2C module and read registers. Currently I've noticed that the scanner example itself worked (ie. writing the device address seemed to function correctly as it returned with no errors and it was in blocking mode so the communication definitely worked "err_code = nrf_drv_twi_rx(&m_twi, address, &sample_data, sizeof(sample_data));". But when I moved to trying to actually get the who_am_i register of the sensor to see the product ID I got a NACK returned by my TWI handler. I tried switching to blocking mode to replicate the conditions of the scanner example but the code never returned it was stuck in the inline nrf_drv_twi_tx() function (I attached the scanner file as well just in case that is of value when referencing "scannerEx.c").
An additional source of confusion for me is that when hooking up to a logic analyzer, the scannerEx.c code shows up just fine with the right data being sent but no signal is found on the main.c file where I try to access the who_am_i register (which doesnt make sense to me because it should be showing a signal with a Nack returned as the error from the handler shows). I'm very confused how this discrepancy happened, is this a tx vs. rx problem where I can receive but not send? I had the same issue with a Max30102 which is why i moved to the Mpu6050 to use a sensor people have gotten working before. Should I just ditch this function? I noticed in the SDK theres another option with nrf_drv_twi_xfer. Maybe that works better? Or maybe switch altogether to TWIM with easy DMA? Honestly I'm very confused about TWI on Nordic's platform, I feel like you have allowed for all these different options to allow for more versatility for the developer to fit their architecture but I feel pulled in so many directions that I don't know the best place to start in dealing with this problem. I feel like the other MCUs I've worked with have simpler architectures so I can understand the low level interactions with the hardware enough to debug applications. I'm on SDK 17.0.2. I'm just very confused. I'm using pins SCL 22 and SDA 23 on the nRF52840 DK.
I have enclosed the main file as well as the mpu6050.c file I call which actually runs the TWI code (calling the nrf_drv_twi_tx() function when the "verify_product_id" function calls register_read() ). I also included the sdk_config.h file in case there is a project configuration misstep but I believe the modules should be enabled as I have all nrfx / nrf twi/twim/ useEasyDMA enables all set to 1.
#include <stdio.h> #include "boards.h" #include "app_util_platform.h" #include "app_error.h" #include "nrf_drv_twi.h" #include "mpu6050.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" // main code //SCL 22, SDA 23+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ int main(void) { // initialize the logger APP_ERROR_CHECK(NRF_LOG_INIT(NULL)); NRF_LOG_DEFAULT_BACKENDS_INIT(); // create arrays which will hold x,y & z co-ordinates values of acc and gyro static int16_t AccValue[3], GyroValue[3]; printf("start\n"); bsp_board_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS); // initialize the leds and buttons twi_master_init(); // initialize the twi nrf_delay_ms(1000); // give some delay //while (1) { //} while(mpu6050_init() == false) // wait until MPU6050 sensor is successfully initialized { printf("MPU_6050 initialization failed!!!\n"); // if it failed to initialize then print a message nrf_delay_ms(1000); } printf("MPU6050 Init Successfully!!!\n"); printf("Reading Values from ACC & GYRO\n"); // display a message to let the user know that the device is starting to read the values nrf_delay_ms(2000); while (true) { if(MPU6050_ReadAcc(&AccValue[0], &AccValue[1], &AccValue[2]) == true) // Read acc value from mpu6050 internal registers and save them in the array { printf("ACC Values: x = %d y = %d z = %d\n", AccValue[0], AccValue[1], AccValue[2]); // display the read values } else { printf("Reading ACC values Failed!!!\n"); // if reading was unsuccessful then let the user know about it } if(MPU6050_ReadGyro(&GyroValue[0], &GyroValue[1], &GyroValue[2]) == true) // read the gyro values from mpu6050's internal registers and save them in another array { printf("GYRO Values: x = %d y = %d z = %d\n", GyroValue[0], GyroValue[1], GyroValue[2]); // display then values } else { printf("Reading GYRO values Failed!!!\n"); } nrf_delay_ms(100); // give some delay } } /** @} */
#include <stdbool.h> #include <stdint.h> #include <string.h> #include "nrf_drv_twi.h" #include "mpu6050.h" //Initializing TWI0 instance #define TWI_INSTANCE_ID 0 // A flag to indicate the transfer state static volatile bool m_xfer_done = false; // Create a Handle for the twi communication static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID); //Event Handler void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context) { printf("handled!\n"); //Check the event to see what type of event occurred switch (p_event->type) { //If data transmission or receiving is finished case NRF_DRV_TWI_EVT_DONE: m_xfer_done = true;//Set the flag printf("event done\n"); break; case NRF_DRV_TWI_EVT_ADDRESS_NACK: printf("Address nack\n"); nrf_delay_ms(500); ret_code_t err_code; uint8_t register_address = MPU_DEVICE_ID_REG;// change this as youre debugging, comment out otherwise err_code = nrf_drv_twi_tx(&m_twi, MPU6050_ADDRESS, ®ister_address, 1, true); break; case NRF_DRV_TWI_EVT_DATA_NACK: printf("data nack\n"); break; default: break; } } //Initialize the TWI as Master device void twi_master_init(void) { ret_code_t err_code; // Configure the settings for twi communication const nrf_drv_twi_config_t twi_config = { .scl = TWI_SCL_M, //SCL Pin .sda = TWI_SDA_M, //SDA Pin .frequency = NRF_DRV_TWI_FREQ_400K, //Communication Speed .interrupt_priority = APP_IRQ_PRIORITY_HIGH, //Interrupt Priority(Note: if using Bluetooth then select priority carefully) .clear_bus_init = false //automatically clear bus }; //A function to initialize the twi communication err_code = nrf_drv_twi_init(&m_twi, &twi_config, twi_handler, NULL); APP_ERROR_CHECK(err_code); //Enable the TWI Communication nrf_drv_twi_enable(&m_twi); } /* A function to write a Single Byte to MPU6050's internal Register */ bool mpu6050_register_write(uint8_t register_address, uint8_t value) { ret_code_t err_code; uint8_t tx_buf[MPU6050_ADDRESS_LEN+1]; //Write the register address and data into transmit buffer tx_buf[0] = register_address; tx_buf[1] = value; //Set the flag to false to show the transmission is not yet completed m_xfer_done = false; //Transmit the data over TWI Bus err_code = nrf_drv_twi_tx(&m_twi, MPU6050_ADDRESS, tx_buf, MPU6050_ADDRESS_LEN+1, false); //Wait until the transmission of the data is finished while (m_xfer_done == false) { } // if there is no error then return true else return false if (NRF_SUCCESS != err_code) { return false; } return true; } /* A Function to read data from the MPU6050 */ bool mpu6050_register_read(uint8_t register_address, uint8_t * destination, uint8_t number_of_bytes) { ret_code_t err_code; //Set the flag to false to show the receiving is not yet completed m_xfer_done = false; // Send the Register address where we want to write the data err_code = nrf_drv_twi_tx(&m_twi, MPU6050_ADDRESS, ®ister_address, 1, false); if (NRF_SUCCESS != err_code) { return false; } // // If transmission was not successful, exit the function with false as return value //if (NRF_SUCCESS != err_code) //{ // return false; //} //Wait for the transmission to get completed while (m_xfer_done == false){} // If transmission was not successful, exit the function with false as return value if (NRF_SUCCESS != err_code) { return false; } //set the flag again so that we can read data from the MPU6050's internal register m_xfer_done = false; // Receive the data from the MPU6050 err_code = nrf_drv_twi_rx(&m_twi, MPU6050_ADDRESS, destination, number_of_bytes); //wait until the transmission is completed while (m_xfer_done == false){} // if data was successfully read, return true else return false if (NRF_SUCCESS != err_code) { return false; } return true; } /* A Function to verify the product id (its a basic test to check if we are communicating with the right slave, every type of I2C Device has a special WHO_AM_I register which holds a specific value, we can read it from the MPU6050 or any device to confirm we are communicating with the right device) */ bool mpu6050_verify_product_id(void) { uint8_t who_am_i; // create a variable to hold the who am i value // Note: All the register addresses including WHO_AM_I are declared in // MPU6050.h file, you can check these addresses and values from the // datasheet of your slave device. if (mpu6050_register_read(ADDRESS_WHO_AM_I, &who_am_i, 1)) { if (who_am_i != MPU6050_WHO_AM_I) { return false; } else { return true; } } else { return false; } } /* Function to initialize the mpu6050 */ bool mpu6050_init(void) { bool transfer_succeeded = true; //Check the id to confirm that we are communicating with the right device transfer_succeeded &= mpu6050_verify_product_id(); if(mpu6050_verify_product_id() == false) { return false; } // Set the registers with the required values, see the datasheet to get a good idea of these values (void)mpu6050_register_write(MPU_PWR_MGMT1_REG , 0x00); (void)mpu6050_register_write(MPU_SAMPLE_RATE_REG , 0x07); (void)mpu6050_register_write(MPU_CFG_REG , 0x06); (void)mpu6050_register_write(MPU_INT_EN_REG, 0x00); (void)mpu6050_register_write(MPU_GYRO_CFG_REG , 0x18); (void)mpu6050_register_write(MPU_ACCEL_CFG_REG,0x00); return transfer_succeeded; } /* Read the Gyro values from the MPU6050's internal Registers */ bool MPU6050_ReadGyro(int16_t *pGYRO_X , int16_t *pGYRO_Y , int16_t *pGYRO_Z ) { uint8_t buf[6]; bool ret = false; if(mpu6050_register_read(MPU6050_GYRO_OUT, buf, 6) == true) { *pGYRO_X = (buf[0] << 8) | buf[1]; if(*pGYRO_X & 0x8000) *pGYRO_X-=65536; *pGYRO_Y= (buf[2] << 8) | buf[3]; if(*pGYRO_Y & 0x8000) *pGYRO_Y-=65536; *pGYRO_Z = (buf[4] << 8) | buf[5]; if(*pGYRO_Z & 0x8000) *pGYRO_Z-=65536; ret = true; } return ret; } /* A Function to read accelerometer's values from the internal registers of MPU6050 */ bool MPU6050_ReadAcc( int16_t *pACC_X , int16_t *pACC_Y , int16_t *pACC_Z ) { uint8_t buf[6]; bool ret = false; if(mpu6050_register_read(MPU6050_ACC_OUT, buf, 6) == true) { mpu6050_register_read(MPU6050_ACC_OUT, buf, 6); *pACC_X = (buf[0] << 8) | buf[1]; if(*pACC_X & 0x8000) *pACC_X-=65536; *pACC_Y= (buf[2] << 8) | buf[3]; if(*pACC_Y & 0x8000) *pACC_Y-=65536; *pACC_Z = (buf[4] << 8) | buf[5]; if(*pACC_Z & 0x8000) *pACC_Z-=65536; ret = true; } return ret; }
#include <stdio.h> #include "boards.h" #include "app_util_platform.h" #include "app_error.h" #include "nrf_drv_twi.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" /* TWI instance ID. */ #define TWI_INSTANCE_ID 0 #define TWI_INSTANCE_ID_1 1 /* Number of possible TWI addresses. */ #define TWI_ADDRESSES 127 /* TWI instance. */ static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID); static const nrf_drv_twi_t m_twi_1 = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID_1); /** * @brief TWI initialization. ADU SCL =27, ADU SDA = 26 */ void twi_init (void) { ret_code_t err_code; const nrf_drv_twi_config_t twi_config = { .scl = 27, .sda = 26, .frequency = NRF_DRV_TWI_FREQ_100K, .interrupt_priority = APP_IRQ_PRIORITY_HIGH, .clear_bus_init = false }; err_code = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL); APP_ERROR_CHECK(err_code); nrf_drv_twi_enable(&m_twi); } /** * @brief TWI initialization. inst 1 ScL 25, SDA 24 */ void twi_init_1 (void) { ret_code_t err_code; const nrf_drv_twi_config_t twi_config_1 = { .scl = 25, .sda = 24, .frequency = NRF_DRV_TWI_FREQ_100K, .interrupt_priority = APP_IRQ_PRIORITY_HIGH, .clear_bus_init = false }; err_code = nrf_drv_twi_init(&m_twi_1, &twi_config_1, NULL, NULL); APP_ERROR_CHECK(err_code); nrf_drv_twi_enable(&m_twi_1); } /** * @brief Function for main application entry. */ int main(void) { ret_code_t err_code; uint8_t address; uint8_t sample_data; bool detected_device = false; APP_ERROR_CHECK(NRF_LOG_INIT(NULL)); NRF_LOG_DEFAULT_BACKENDS_INIT(); NRF_LOG_INFO("TWI scanner started"); NRF_LOG_FLUSH(); twi_init(); address = 0x57; //change to temp sensor err_code = nrf_drv_twi_rx(&m_twi, address, &sample_data, sizeof(sample_data)); if (err_code == NRF_SUCCESS) { detected_device = true; NRF_LOG_INFO("TWI device detected at address 0x%x.", address); } NRF_LOG_FLUSH(); if (!detected_device) { NRF_LOG_INFO("No device was found."); NRF_LOG_FLUSH(); } NRF_LOG_INFO("TWI second scanner started"); NRF_LOG_FLUSH(); twi_init_1(); address = 0x57; err_code = nrf_drv_twi_rx(&m_twi_1, address, &sample_data, sizeof(sample_data)); if (err_code == NRF_SUCCESS) { detected_device = true; NRF_LOG_INFO("TWI device detected at address 0x%x.", address); } NRF_LOG_FLUSH(); if (!detected_device) { NRF_LOG_INFO("No device was found."); NRF_LOG_FLUSH(); } while (true) { //empty loop } } /** @} */