Работа с шиной I2C (TWI)

ПРЕДУПРЕЖДЕНИЕ

Для работы с функциями библиотеки подключите ее в Вашу программу с помощью инструкции #include <wire.h>

Подробнее о подключении библиотеки в пользовательскую программу →

Библиотека WiringRP позволяет обмениваться данными с периферийными устройствами по двухпроводному интерфейсу I2C (Inter-Integrated Circuit), также известному как TWI (Two-Wire Interface).

Список I2C-интерфейсов для Repka Pi 3 #

Идентификатор

Интерфейс

SDA

SCL

Вариант распиновки

I2C1_BUS

I2C1

PA12 (пин 3)

PA11 (пин 5)

2 - 8

I2C2_BUS

I2C2

PA19 (пин 27)

PA18 (пин 28)

4

Список I2C-интерфейсов для Repka Pi 4 #

Идентификатор

Интерфейс

SDA

SCL

Вариант распиновки

I2C1_BUS

I2C1

PD26 (пин 3)

PD25 (пин 5)

2 - 4 и 7 - 9

I2C2_BUS

I2C2

PD24 (пин 27)

PD23 (пин 28)

4

I2C3_BUS

I2C3

PH6 (пин 21)

PH5 (пин 19)

11

Список I2C-интерфейсов для Repka Pi 5 #

Идентификатор

Интерфейс

SDA

SCL

Вариант распиновки

I2C2_BUS

I2C2

GPIO0_С0 (пин 27)

GPIO0_B7 (пин 28)

4 и 8

I2C4_BUS

I2C4

GPIO0_C4 (пин 10)

GPIO0_C5 (пин 8)

8

I2C5_BUS

I2C5

GPIO3_D0 (пин 40)

GPIO3_C7 (пин 38)

8

I2C7_BUS

I2C7

GPIO4_B3 (пин 15)

GPIO4_B2 (пин 13)

8

I2C8_BUS

I2C8

GPIO1_D7 (пин 3)

GPIO1_D6 (пин 5)

2, 3, 4, 7 и 8


i2cSetup() #

Инициализирует указанный I2C-интерфейс и подключенное к нему ведомое устройство.

Синтаксис #

int i2cSetup(const int i2cBus, const int devId)

Параметры #

i2cBus - идентификатор I2C-интерфейса.

Доступные значения для каждой версии Repka Pi отображены в таблицах (столбец “идентификатор”).

devId - адрес ведомого устройства.

Возвращаемое значение #

Файловый дескриптор I2C-интерфейса или отрицательное значение в случае ошибки.


i2cRelease() #

Освобождает ресурсы задействованные при работе с I2C-интерфейсом.

Синтаксис #

int i2cRelease(int fd)

Параметры #

fd - файловый дескриптор I2C-интерфейса.

Возвращаемое значение #

Ничего.


i2cRead() #

Возвращает байт данных, полученный от ведомого устройства из I2C-интерфейса.

Синтаксис #

int i2cRead(int fd)

Параметры #

fd - файловый дескриптор I2C-интерфейса.

Возвращаемое значение #

Байт данных, полученный от ведомого устройства.


i2cReadReg8() #

Возвращает значение 8-битного регистра ведомого устройства.

Синтаксис #

int i2cReadReg8(int fd, int reg)

Параметры #

fd - файловый дескриптор I2C-интерфейса.

reg - адрес 8-битного регистра ведомого устройства.

Возвращаемое значение #

Значение 8-битного регистра ведомого устройства.


i2cReadReg16() #

Возвращает значение 16-битного регистра ведомого устройства.

Синтаксис #

int i2cReadReg16(int fd, int reg)

Параметры #

fd - файловый дескриптор I2C-интерфейса.

reg - адрес 16-битного регистра ведомого устройства.

Возвращаемое значение #

Значение 16-битного регистра ведомого устройства.


i2cWrite() #

Выполняет передачу данных на ведомое устройство с помощью I2C-интерфейса.

Синтаксис #

int i2cWrite(int fd, int data)

Параметры #

fd - файловый дескриптор I2C-интерфейса.

data - передаваемые ведомому устройству данные.

Возвращаемое значение #

0 при успешном выполнении или отрицательное значение в случае ошибки.


i2cWriteReg8() #

Записывает данные в 8-битный регистр ведомого устройства.

Синтаксис #

int i2cWriteReg8(int fd, int reg, int data)

Параметры #

fd - файловый дескриптор I2C-интерфейса.

reg - адрес 8-ми битного регистра.

data - значение для записи.

Возврат #

0 при успешном выполнении или отрицательное значение в случае ошибки.


i2cWriteReg16() #

Записывает данные в 16-битный регистр ведомого устройства.

Синтаксис #

int i2cWriteReg16(int fd, int reg, int data)

Параметры #

fd - файловый дескриптор I2C-интерфейса.

reg - адрес 16-ти битного регистра.

data - значение для записи.

Возврат #

0 при успешном выполнении или отрицательное значение в случае ошибки.


Пример использования: Подключение датчика давления и температуры BME280 #

Этот пример демонстрирует, как использовать функции библиотеки для настройки и получения данных от датчика давления и температуры BME280 по интерфейсу I2C.

Что выполняет этот пример:

  • Подключение к ведомому устройству по указанному I2C-интерфейсу.
  • Чтение калибровочных коэффициентов для преобразования значения температуры.
  • Отображение текущей температуры окружающей среды в режиме реального времени.

Схема подключения (на примере Repka Pi 5):

Схема подключения Repka Pi 5 и датчика давления и температуры BME280

ПРЕДУПРЕЖДЕНИЕ

Данный пример может быть запущен ТОЛЬКО на определенных вариантах распиновки GPIO-разъема. Подробнее - в таблицах.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include "wiringRP.h"
#include "wire.h"

/*
 * Пример предназначен для иллюстрации возможности подключения устройств по интерфейсу I2C1 на Repka Pi 3.
 * В данном примере подключается датчик давления и температуры BME280.
 * Опрос датчика происходит с интервалом в 1 секунду.
*/

// Глобальные переменные и константы
int i2c1_fd; // Файловый дескриптор интерфейса I2C1

// Глобальные переменные и константы BME280
const int PORT_I2C = I2C1_BUS;
const uint8_t BME280_ADR = 0x76;
const uint8_t BME280_CHIP_ID = 0x60;

const uint8_t REG_CHIP_ID = 0xD0;
const uint8_t REG_CTRL_MEAS = 0xF4;
const uint8_t REG_CONFIG = 0xF5;
const uint8_t REG_DATA_START = 0xF7;
const uint8_t REG_CALIB_START = 0x88;
const uint8_t REG_CALIB_END = 0xA1;

static uint8_t raw_data[32];
static uint8_t status;

static int T1, T2, T3;

// Чтение диапазона регистров датчика BMP280
static bool loadReg(uint8_t firstReg, uint8_t lastReg) {
	for (uint8_t ind = firstReg; ind <= lastReg; ind++)	{
		raw_data[ind - firstReg] = i2cReadReg8(i2c1_fd, ind);
		if (raw_data[ind - firstReg] < 0) {
			printf("Регистр 0x%02X не доступен! Идентификатор интерфейса I2C: %d.\n", ind, PORT_I2C);
			return false;
		}
	}
	return true;
}

void setup() {
    // Инициализация библиотеки WiringRP
    // Используется режим нумерации SUNXI
    if (setupWiringRP(WRP_MODE_SUNXI) < 0) {
        goto error;
    }
        
    // Инициализация пользовательских объектов
    i2c1_fd = i2cSetup(PORT_I2C, BME280_ADR);

    if (i2c1_fd < 0) {
        goto error;
	}

    delay(100);
    // Чтение идентификатор датчика
    unsigned int chipId = i2cReadReg8(i2c1_fd, REG_CHIP_ID);
    if (chipId != BME280_CHIP_ID) {
        printf("Не удалось получить правильный идентификатор датчика! Ожидалось значение 0x%02X, а получено 0x%02X.\n", BME280_CHIP_ID, chipId);
        goto error;
    }
    
	// Чтение регистров калибровочных коэффициентов
	if (!loadReg(REG_CALIB_START, REG_CALIB_END)) {
        printf("Не удалось прочитать калибровочные коэффициенты.\n");
        goto error;
    }

	// Формирование калибровочных коэффициентов, согласно datasheet BMP280
	T1 = raw_data[0] + (raw_data[1] << 8);
	T2 = raw_data[2] + (raw_data[3] << 8);
	T3 = raw_data[4] + (raw_data[5] << 8);
	
	/* Запись конфигурации в управляющие регистры датчика.
	 * Конфигурация устанавливает режим работы, частоту и точность измерений.
	 * Регистр CTRL_MEAS (0xF4):
	 * - osrs_t = x1 (0b001),
     * - osrs_p = x1 (0b001),
     * - mode = normal (0b11).
	 * Итоговая команда: 0b00100111 = 0x27
	*/
	i2cWriteReg8(i2c1_fd, REG_CTRL_MEAS, 0x27);
	
    /* Регистр CONFIG (0xF5):
     * - t_sb = 1000ms (0b101),
     * - filter = off (0b000).
     * Итоговая команда: 0b10100000 = 0xA0
    */        
	i2cWriteReg8(i2c1_fd, REG_CONFIG, 0xA0);

    return;
error:
    onClose(2);
}

void loop() {
    // Основной цикл программы
    // Чтение регистров текущего значения температуры
    if (!loadReg(REG_DATA_START, REG_DATA_START + 5)) {
        printf("Не удалось прочитать значение температуры. Повтор попытки через 1 секунду...\n");
    } else {
        // Расчет по предустановленным значениям, согласно datasheet BMP280 	
        int adc_P = (raw_data[0] << 12) | (raw_data[1] << 4) | (raw_data[2] >> 4);
        int adc_T = (raw_data[3] << 12) | (raw_data[4] << 4) | (raw_data[5] >> 4);
        
        int var1_t = (adc_T / 16384.0 - T1 / 1024.0) * T2;
        int var2_t = ((adc_T / 131072.0 - T1 / 8192.0) * (adc_T / 131072.0 - T1 / 8192.0)) * T3;
        int t_fine = var1_t + var2_t;
        int temperature = t_fine / 5120.0;

        printf("[BME280] Температура: %d °C\n", temperature);
    }
	
    delay(1000);
}


ONDESTROY() {
    i2cRelease(i2c1_fd); 
    
    // Завершение работы библиотеки   
    releaseWiringRP();
    // Выход из программы
    exit(0);    
}

MAIN_WIRINGRP();
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include "wiringRP.h"
#include "wire.h"

/*
 * Пример предназначен для иллюстрации возможности подключения устройств по интерфейсу I2C1 на Repka Pi 4.
 * В данном примере подключается датчик давления и температуры BME280.
 * Опрос датчика происходит с интервалом в 1 секунду.
*/

// Глобальные переменные и константы
int i2c1_fd; // Файловый дескриптор интерфейса I2C1

// Глобальные переменные и константы BME280
const int PORT_I2C = I2C1_BUS;
const uint8_t BME280_ADR = 0x76;
const uint8_t BME280_CHIP_ID = 0x60;

const uint8_t REG_CHIP_ID = 0xD0;
const uint8_t REG_CTRL_MEAS = 0xF4;
const uint8_t REG_CONFIG = 0xF5;
const uint8_t REG_DATA_START = 0xF7;
const uint8_t REG_CALIB_START = 0x88;
const uint8_t REG_CALIB_END = 0xA1;

static uint8_t raw_data[32];
static uint8_t status;

static int T1, T2, T3;

// Чтение диапазона регистров датчика BMP280
static bool loadReg(uint8_t firstReg, uint8_t lastReg) {
	for (uint8_t ind = firstReg; ind <= lastReg; ind++)	{
		raw_data[ind - firstReg] = i2cReadReg8(i2c1_fd, ind);
		if (raw_data[ind - firstReg] < 0) {
			printf("Регистр 0x%02X не доступен! Идентификатор интерфейса I2C: %d.\n", ind, PORT_I2C);
			return false;
		}
	}
	return true;
}

void setup() {
    // Инициализация библиотеки WiringRP
    // Используется режим нумерации SUNXI
    if (setupWiringRP(WRP_MODE_SUNXI) < 0) {
        goto error;
    }
        
    // Инициализация пользовательских объектов
    i2c1_fd = i2cSetup(PORT_I2C, BME280_ADR);

    if (i2c1_fd < 0) {
        goto error;
	}

    delay(100);
    // Чтение идентификатор датчика
    unsigned int chipId = i2cReadReg8(i2c1_fd, REG_CHIP_ID);
    if (chipId != BME280_CHIP_ID) {
        printf("Не удалось получить правильный идентификатор датчика! Ожидалось значение 0x%02X, а получено 0x%02X.\n", BME280_CHIP_ID, chipId);
        goto error;
    }
    
	// Чтение регистров калибровочных коэффициентов
	if (!loadReg(REG_CALIB_START, REG_CALIB_END)) {
        printf("Не удалось прочитать калибровочные коэффициенты.\n");
        goto error;
    }

	// Формирование калибровочных коэффициентов, согласно datasheet BMP280
	T1 = raw_data[0] + (raw_data[1] << 8);
	T2 = raw_data[2] + (raw_data[3] << 8);
	T3 = raw_data[4] + (raw_data[5] << 8);
	
	/* Запись конфигурации в управляющие регистры датчика.
	 * Конфигурация устанавливает режим работы, частоту и точность измерений.
	 * Регистр CTRL_MEAS (0xF4):
	 * - osrs_t = x1 (0b001),
     * - osrs_p = x1 (0b001),
     * - mode = normal (0b11).
	 * Итоговая команда: 0b00100111 = 0x27
	*/
	i2cWriteReg8(i2c1_fd, REG_CTRL_MEAS, 0x27);
	
    /* Регистр CONFIG (0xF5):
     * - t_sb = 1000ms (0b101),
     * - filter = off (0b000).
     * Итоговая команда: 0b10100000 = 0xA0
    */        
	i2cWriteReg8(i2c1_fd, REG_CONFIG, 0xA0);

    return;
error:
    onClose(2);
}

void loop() {
    // Основной цикл программы
    // Чтение регистров текущего значения температуры
    if (!loadReg(REG_DATA_START, REG_DATA_START + 5)) {
        printf("Не удалось прочитать значение температуры. Повтор попытки через 1 секунду...\n");
    } else {
        // Расчет по предустановленным значениям, согласно datasheet BMP280 	
        int adc_P = (raw_data[0] << 12) | (raw_data[1] << 4) | (raw_data[2] >> 4);
        int adc_T = (raw_data[3] << 12) | (raw_data[4] << 4) | (raw_data[5] >> 4);
        
        int var1_t = (adc_T / 16384.0 - T1 / 1024.0) * T2;
        int var2_t = ((adc_T / 131072.0 - T1 / 8192.0) * (adc_T / 131072.0 - T1 / 8192.0)) * T3;
        int t_fine = var1_t + var2_t;
        int temperature = t_fine / 5120.0;

        printf("[BME280] Температура: %d °C\n", temperature);
    }
	
    delay(1000);
}


ONDESTROY() {
    i2cRelease(i2c1_fd); 
    
    // Завершение работы библиотеки   
    releaseWiringRP();
    // Выход из программы
    exit(0);    
}

MAIN_WIRINGRP();
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include "wiringRP.h"
#include "wire.h"

/*
 * Пример предназначен для иллюстрации возможности подключения устройств по интерфейсу I2C8 на Repka Pi 5.
 * В данном примере подключается датчик давления и температуры BME280.
 * Опрос датчика происходит с интервалом в 1 секунду.
*/

// Глобальные переменные и константы
int i2c8_fd; // Файловый дескриптор интерфейса I2C8

// Глобальные переменные и константы BME280
const int PORT_I2C = I2C8_BUS;
const uint8_t BME280_ADR = 0x76;
const uint8_t BME280_CHIP_ID = 0x60;

const uint8_t REG_CHIP_ID = 0xD0;
const uint8_t REG_CTRL_MEAS = 0xF4;
const uint8_t REG_CONFIG = 0xF5;
const uint8_t REG_DATA_START = 0xF7;
const uint8_t REG_CALIB_START = 0x88;
const uint8_t REG_CALIB_END = 0xA1;

static uint8_t raw_data[32];
static uint8_t status;

static int T1, T2, T3;

// Чтение диапазона регистров датчика BMP280
static bool loadReg(uint8_t firstReg, uint8_t lastReg) {
	for (uint8_t ind = firstReg; ind <= lastReg; ind++)	{
		raw_data[ind - firstReg] = i2cReadReg8(i2c8_fd, ind);
		if (raw_data[ind - firstReg] < 0) {
			printf("Регистр 0x%02X не доступен! Идентификатор интерфейса I2C: %d.\n", ind, PORT_I2C);
			return false;
		}
	}
	return true;
}

void setup() {
    // Инициализация библиотеки WiringRP
    // Используется режим нумерации SUNXI
    if (setupWiringRP(WRP_MODE_SUNXI) < 0) {
        goto error;
    }
        
    // Инициализация пользовательских объектов
    i2c8_fd = i2cSetup(PORT_I2C, BME280_ADR);

    if (i2c8_fd < 0) {
        goto error;
	}

    delay(100);
    // Чтение идентификатор датчика
    unsigned int chipId = i2cReadReg8(i2c8_fd, REG_CHIP_ID);
    if (chipId != BME280_CHIP_ID) {
        printf("Не удалось получить правильный идентификатор датчика! Ожидалось значение 0x%02X, а получено 0x%02X.\n", BME280_CHIP_ID, chipId);
        goto error;
    }
    
	// Чтение регистров калибровочных коэффициентов
	if (!loadReg(REG_CALIB_START, REG_CALIB_END)) {
        printf("Не удалось прочитать калибровочные коэффициенты.\n");
        goto error;
    }

	// Формирование калибровочных коэффициентов, согласно datasheet BMP280
	T1 = raw_data[0] + (raw_data[1] << 8);
	T2 = raw_data[2] + (raw_data[3] << 8);
	T3 = raw_data[4] + (raw_data[5] << 8);
	
	/* Запись конфигурации в управляющие регистры датчика.
	 * Конфигурация устанавливает режим работы, частоту и точность измерений.
	 * Регистр CTRL_MEAS (0xF4):
	 * - osrs_t = x1 (0b001),
     * - osrs_p = x1 (0b001),
     * - mode = normal (0b11).
	 * Итоговая команда: 0b00100111 = 0x27
	*/
	i2cWriteReg8(i2c8_fd, REG_CTRL_MEAS, 0x27);
	
    /* Регистр CONFIG (0xF5):
     * - t_sb = 1000ms (0b101),
     * - filter = off (0b000).
     * Итоговая команда: 0b10100000 = 0xA0
    */        
	i2cWriteReg8(i2c8_fd, REG_CONFIG, 0xA0);

    return;
error:
    onClose(2);
}

void loop() {
    // Основной цикл программы
    // Чтение регистров текущего значения температуры
    if (!loadReg(REG_DATA_START, REG_DATA_START + 5)) {
        printf("Не удалось прочитать значение температуры. Повтор попытки через 1 секунду...\n");
    } else {
        // Расчет по предустановленным значениям, согласно datasheet BMP280 	
        int adc_P = (raw_data[0] << 12) | (raw_data[1] << 4) | (raw_data[2] >> 4);
        int adc_T = (raw_data[3] << 12) | (raw_data[4] << 4) | (raw_data[5] >> 4);
        
        int var1_t = (adc_T / 16384.0 - T1 / 1024.0) * T2;
        int var2_t = ((adc_T / 131072.0 - T1 / 8192.0) * (adc_T / 131072.0 - T1 / 8192.0)) * T3;
        int t_fine = var1_t + var2_t;
        int temperature = t_fine / 5120.0;

        printf("[BME280] Температура: %d °C\n", temperature);
    }
	
    delay(1000);
}


ONDESTROY() {
    i2cRelease(i2c8_fd); 
    
    // Завершение работы библиотеки   
    releaseWiringRP();
    // Выход из программы
    exit(0);    
}

MAIN_WIRINGRP();

3677 просмотров0 комментариев
0

Комментарии (0)

Для участия в обсуждении Вы должны быть авторизованным пользователем
Разделы

Навигация

ВойтиРегистрация