From 34b88ad099600f5d3e3f8080bb20728f7ee6e67b Mon Sep 17 00:00:00 2001 From: Sovichea Tep Date: Sun, 14 Jul 2019 12:48:31 +0700 Subject: [PATCH] Updated Readme with example --- README.md | 89 ++++++++++++++++++++- twi_master.c => twi/twi_master.c | 9 ++- twi_master.h => twi/twi_master.h | 6 +- twi_test.c | 128 +++++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 7 deletions(-) rename twi_master.c => twi/twi_master.c (97%) rename twi_master.h => twi/twi_master.h (77%) create mode 100644 twi_test.c diff --git a/README.md b/README.md index 551f3a4..93d2ec1 100644 --- a/README.md +++ b/README.md @@ -1 +1,88 @@ -# avr-i2c-library \ No newline at end of file +# TWI/I2C Library for AVR + +## Description + +This library is tested on ATMega328p, but it should work with any AVR compatible microcontroller. To use with other type of ATMega, change the I2C pin definition in `twi_master.h` according the datasheet. In ATMega328p, I2C pins are describe as follows: + +```c +#define TW_SCL_PIN PORTC5 +#define TW_SDA_PIN PORTC4 +``` + + + +## Usage + +There are 3 functions available for this library: + +```c +void tw_init(twi_freq_mode_t twi_freq, bool pullup_en); +``` + +Initialize I2C with predefined frequency and enable internal pull-up resistors. There are 3 mode of frequency to choose from: `TW_FREQ_100K`, `TW_FREQ_250K` and `TW_FREQ_400K`. Then set `pullup_en` bit to `true` to enable internal pull-up resistors on `SCL` and `SDA` pin, or set it to `false` if external pull-up is used. + + + +```c +ret_code_t tw_master_transmit(uint8_t slave_addr, uint8_t* p_data, uint8_t len, bool repeat_start); +``` + +This function transmits data bytes to a desired slave address. If `repeat_start` bit is set to `true`, I2C module will send REPEATED START condition instead of STOP condition. This is useful in multi-master environment where one Master sends a command and wait for response from slave without having to release the Bus, thus preventing the other Master from taking control of the Bus in between the transaction. + +`ret_code_t` is return to the user to handle the state of transmission such as transmission success, arbitration lost, slave acknowledge, etc. + + + +```c +ret_code_t tw_master_receive(uint8_t slave_addr, uint8_t* p_data, uint8_t len); +``` + +This functions receives the data bytes from the desired slave address and return `ret_code_t` for error handling as have been discussed in `tw_master_transmit`. + +## Example + +The example provided with this library is tested with MPU6050 to read the value of acceleration in X, Y and Z axis. In order to activate MPU6050, we have to write `0` to `PWR_MGMT_1` register as shown below: + +```c +void mpu_init(void) +{ + ret_code_t error_code; + puts("Write 0 to PWR_MGMT_1 reg to wakeup MPU."); + uint8_t data[2] = {PWR_MGMT_1, 0}; + error_code = tw_master_transmit(MPU6050_ADDR, data, sizeof(data), false); + ERROR_CHECK(error_code); +} +``` + +In this situation, we want to write the value but don't read back the response; therefore, `repeat_start` bit is set to `false`. + +To read the value of accelerometer, we have to transmit the register that we want to read, which is `ACCEL_XOUT_H` then wait for the response using `tw_master_receive` as shown below: + +```c +void mpu_get_accel_raw(mpu_data_t* mpu_data) +{ + ret_code_t error_code; + /* 2 registers for each of accel x, y and z data */ + uint8_t data[6]; + + data[0] = ACCEL_XOUT_H; + error_code = tw_master_transmit(MPU6050_ADDR, data, 1, true); + ERROR_CHECK(error_code); + + error_code = tw_master_receive(MPU6050_ADDR, data, sizeof(data)); + ERROR_CHECK(error_code); + + /* Default accel config +/- 2g */ + mpu_data->x = (int16_t)(data[0] << 8 | data[1]) / 16384.0; + mpu_data->y = (int16_t)(data[2] << 8 | data[3]) / 16384.0; + mpu_data->z = (int16_t)(data[4] << 8 | data[5]) / 16384.0; +} +``` + +In this situation, since we write and then want to wait for the response from MPU, `repeat_start` bit is set to `true` to prevent other Master from pulling the Bus in between write and read process. This step is, however, not necessary if there is only one Master on the Bus. + + + +## Debugging I2C Transaction + +Found in `tw_master.h`, you can set `DEBUG_LOG` variable to `1` or `0` to enable or disable debug functionality of the library. However, this requires that you have UART library included in your project. \ No newline at end of file diff --git a/twi_master.c b/twi/twi_master.c similarity index 97% rename from twi_master.c rename to twi/twi_master.c index c712732..345cf3b 100644 --- a/twi_master.c +++ b/twi/twi_master.c @@ -165,7 +165,7 @@ void tw_init(twi_freq_mode_t twi_freq_mode, bool pullup_en) } -ret_code_t tw_master_transmit(uint8_t slave_addr, uint8_t* p_data, uint8_t len) +ret_code_t tw_master_transmit(uint8_t slave_addr, uint8_t* p_data, uint8_t len, bool repeat_start) { ret_code_t error_code; @@ -193,8 +193,11 @@ ret_code_t tw_master_transmit(uint8_t slave_addr, uint8_t* p_data, uint8_t len) } } - /* Send STOP condition */ - tw_stop(); + if (!repeat_start) + { + /* Send STOP condition */ + tw_stop(); + } return SUCCESS; } diff --git a/twi_master.h b/twi/twi_master.h similarity index 77% rename from twi_master.h rename to twi/twi_master.h index 300c26a..86fa3e8 100644 --- a/twi_master.h +++ b/twi/twi_master.h @@ -8,10 +8,10 @@ #ifndef TWI_MASTER_H_ #define TWI_MASTER_H_ + #include #include #include -#include "uart.h" #define DEBUG_LOG 0 #define SUCCESS 0 @@ -33,7 +33,7 @@ typedef enum { } twi_freq_mode_t; void tw_init(twi_freq_mode_t twi_freq, bool pullup_en); -ret_code_t tw_master_transmit(uint8_t addr, uint8_t* p_data, uint8_t len); -ret_code_t tw_master_receive(uint8_t addr, uint8_t* p_data, uint8_t len); +ret_code_t tw_master_transmit(uint8_t slave_addr, uint8_t* p_data, uint8_t len, bool repeat_start); +ret_code_t tw_master_receive(uint8_t slave_addr, uint8_t* p_data, uint8_t len); #endif /* TWI_MASTER_H_ */ \ No newline at end of file diff --git a/twi_test.c b/twi_test.c new file mode 100644 index 0000000..f50eed9 --- /dev/null +++ b/twi_test.c @@ -0,0 +1,128 @@ +/* + * TWI_Test.c + * + * Created: 08-Jun-19 10:06:47 AM + * Author : TEP SOVICHEA + */ + +#include "uart.h" +#include "twi_master.h" +#include +#include + +/************************************************************************/ +/* Initializations */ +/************************************************************************/ + +#define MPU6050_ADDR 0x68 + +/* MPU6050 register address */ +#define ACCEL_XOUT_H 0x3B +#define ACCEL_XOUT_L 0x3C +#define ACCEL_YOUT_H 0x3D +#define ACCEL_YOUT_L 0x3E +#define ACCEL_ZOUT_H 0x3F +#define ACCEL_ZOUT_L 0x40 +#define PWR_MGMT_1 0x6B + +typedef struct +{ + float x; + float y; + float z; +} mpu_data_t; + + +/************************************************************************/ +/* Prototype functions */ +/************************************************************************/ + +void ERROR_CHECK(ret_code_t error_code); +void mpu_init(void); +void mpu_get_accel_raw(mpu_data_t* mpu_data); +void mpu_get_accel(mpu_data_t* mpu_data); + + +/************************************************************************/ +/* Function definitions */ +/************************************************************************/ + +void ERROR_CHECK(ret_code_t error_code) +{ + if (error_code != SUCCESS) + { + /* Print error code and loop indefinitely until reset */ + printf(BR "App error! error_code = 0x%02X\n" RESET, error_code); + while (1); // loop indefinitely + } +} + + +void mpu_init(void) +{ + ret_code_t error_code; + puts("Write 0 to PWR_MGMT_1 reg to wakeup MPU."); + uint8_t data[2] = {PWR_MGMT_1, 0}; + error_code = tw_master_transmit(MPU6050_ADDR, data, sizeof(data), false); + ERROR_CHECK(error_code); +} + + +void mpu_get_accel_raw(mpu_data_t* mpu_data) +{ + ret_code_t error_code; + /* 2 registers for each of accel x, y and z data */ + uint8_t data[6]; + + data[0] = ACCEL_XOUT_H; + error_code = tw_master_transmit(MPU6050_ADDR, data, 1, true); + ERROR_CHECK(error_code); + + error_code = tw_master_receive(MPU6050_ADDR, data, sizeof(data)); + ERROR_CHECK(error_code); + + /* Default accel config +/- 2g */ + mpu_data->x = (int16_t)(data[0] << 8 | data[1]) / 16384.0; + mpu_data->y = (int16_t)(data[2] << 8 | data[3]) / 16384.0; + mpu_data->z = (int16_t)(data[4] << 8 | data[5]) / 16384.0; +} + + +void mpu_get_accel(mpu_data_t* mpu_data) +{ + mpu_get_accel_raw(mpu_data); + mpu_data->x = mpu_data->x * 9.81; + mpu_data->y = mpu_data->y * 9.81; + mpu_data->z = mpu_data->z * 9.81; +} + + +/************************************************************************/ +/* Main application */ +/************************************************************************/ + +int main(void) +{ + /* Initialize UART */ + uart_init(250000); // bps + cli_reset(); + puts(BY "Initializing TWI_Test Project...\n" RESET); + + /* Initialize project configuration */ + tw_init(TW_FREQ_400K, true); // set I2C Frequency, enable internal pull-up + mpu_init(); + mpu_data_t accel; + + puts(BG CURSOR_RIGHT("14") + "--------------- Application Started ---------------\n" RESET); + + while (1) + { + puts("Read accelerometer data."); + mpu_get_accel(&accel); + printf("Accel X: %5.2f\n", accel.x); + printf("Accel Y: %5.2f\n", accel.y); + printf("Accel Z: %5.2f\n", accel.z); + _delay_ms(200); + } +}