2016-01-09 3 views
3

Я хотел бы использовать Arduino в качестве ведомого i2c. Но я требую, чтобы Arduino действовал как несколько устройств, зарегистрировавшись на несколько адресов i2c.Arduino как подчиненный с несколькими адресами i2c

Это, вероятно, не то, что можно было бы нормально сделать, но вот моя причина сделать это:

Я хочу использовать Arduino, чтобы действовать в качестве датчиков телеизмерения для Spektrum телеметрии. Приемник Telemetry имеет несколько разъемов i2c, которые соединяются с несколькими датчиками (ток 0x02, напряжение 0x03, воздушная скорость 0x11 и т. Д.), Каждый из которых имеет фиксированный адрес i2c, который ожидает приемник Telemetry.

Я хотел бы использовать один Arduino, чтобы действовать как все эти устройства, зарегистрировавшись со всеми вышеуказанными адресами и соответствующим образом отредактировав показания.

Я мог бы использовать один Arduino на датчик, который кажется глупым, поскольку я могу выполнять все эти показания с помощью одного Arduino pro-mini.

Я знаю, что вы можете зарегистрировать Arduino с помощью

Wire.begin(0x02); 

Но я требую что-то вроде этого (псевдо код)

Wire.begin(0x02, 0x03, 0x11); 

И когда запрос получен, мне нужно знать, с какой адрес Ардуино был задан.

Например (псевдокод)

void receiveEvent(byte address, int bytesReceived){ 
    if(address == 0x02){ 
    // Current reading 
    } 
    else if(address == 0x03){ 
    // Voltage reading 
    } 
    else if(address == 0x11){ 
    // Airspeed reading 
    } 
} 

Любые советы будут оценены.

+0

При использовании платформы кроме оригинального Arduino, есть опция, устройства ATxmegaA1 оснащены 4 интерфейсами I2C и есть [ATxmega fork Arduino] (https://github.com/Xmegaduino), называемый Xmegaduino на github. – vega8

ответ

5

Невозможно заставить Arduino прослушивать несколько подчиненных адресов, используя библиотеку Wire, поскольку Wire.begin() позволяет передавать только один подчиненный адрес.


Даже микроконтроллер Atmel ATmega, на которых основано большинство Arduinos допускает только его аппаратный 2-проводной последовательный интерфейс (TWI), должны быть установлен в одном 7-битовом адрес через его 2-проводной адресный регистр TWAR. Однако можно обойти это ограничение, маскируя один или несколько адресных битов, используя регистр маски адреса TWI TWAMR, как документировано (несколько кратко), например. это ATmega datasheet раздел 22.9.6:

TWAMR может быть загружен 7-разрядной масляной картой сальва (sic!). Каждый из битов в TWAMR может маскировать (отключать) соответствующие адресные биты в регистре адресов TWI (TWAR). Если бит маски установлен в один, тогда логика совпадения адресов игнорирует сравнение между битом входящего адреса и соответствующим битом в TWAR.

Таким образом, мы должны сначала установить биты маски на основе всех I2C адресов, которые мы хотим, чтобы ответить на по OR'ing их и сдвигая право соответствовать макету TWAMR регистра (TWAMR держит маску: 1 7 регистра, bit0 не используется):

TWAMR = (sensor1_addr | sensor2_addr | sensor3_addr) << 1; 

Основная проблема здесь в том, чтобы найти, какой конкретный I2C адрес был задан вопрос (мы только знаем, что это был один, который соответствует маску адреса). Если я интерпретирую раздел 22.5.3, указывая

TWDR содержит адрес или данные, которые должны быть переданы, или полученные байты адреса или данных.

Мы должны быть в состоянии получить адрес маскируемого адреса I2C из регистра TWDR.

Операция ATmega TWI основана на прерываниях, в частности, используется один вектор прерывания для множества различных событий TWI, обозначенных кодами состояния в регистре состояния TWSR. В подпрограмме обслуживания прерывания TWI, мы должны

  1. убедитесь, что причина, почему мы вошли в ISR, потому что мы уже опрошены. Это может быть сделано путем проверки TWSR для кода состояния 0xA8 (собственный SLA + R было получено)
  2. решить, какие данные датчика для отправки обратно к мастеру на основе того, что I2C адрес фактически запрашиваться проверки последнего байта на автобус в TWDR.

Эта часть ISR может выглядеть примерно так (непроверенные):

if (TWSR == 0xA8) { // read request has been received 
    byte i2c_addr = TWDR >> 1; // retrieve address from last byte on the bus 
    switch (i2c_addr) { 
    case sensor1_addr: 
     // send sensor 1 reading 
     break; 
    case sensor2_addr: 
     // send sensor 2 reading 
     break; 
    case sensor3_addr: 
     // send sensor 3 reading 
     break; 
    default: 
     // I2C address does not match any of our sensors', ignore. 
     break; 
    } 
} 

Спасибо за задаю этот интересный вопрос!

+0

Thankyou @ vega8, ваш ответ очень описательный! Я дам вам попробовать и обновить, если он будет работать. – trojanc

+0

Отличный ответ. Один вопрос; Есть ли что-то, ограничивающее вас количеством адресов (кроме 7-битного адреса)? Можно ли конкретно указать 6 адресов? Например: '(0x28 | 0x29 | 0x2A | 0x2B | 0x2C | 0x2D) << 1'. Я хотел бы сделать это, чтобы иметь возможность получать данные от конкретных датчиков на плате, или лучше просто написать адрес на доске, чтобы указать, какой датчик я бы хотел запросить, прежде чем на самом деле запросить его? –

4

Мне действительно нравится ответ vega8, но я также хотел бы упомянуть, что если ваш мастер I2C не будет синхронизировать вещи невероятно быстро, использование программной реализации I2C также будет осуществимо и даст вам свободы, которую вы хотите.

Возможно, вы захотите рассмотреть этот подход, если приблизительный расчет показывает, что время, проведенное в TWI ISR, слишком велико, и прерывания могут начать перекрываться.

+1

Хороший комментарий, thx!Это означало бы реализовать большую часть того, что уже есть в аппаратном обеспечении (включая увеличение размера программы + дополнительное тестирование). Конечно, более гибкая, типичная ситуация с компромиссом. – vega8

1
void setup() 
{ 
    Wire.begin(0x11 | 0x12);  // Adr 11 and 12 are used for Alt and Speed by Spectrum DX 
    Wire.onRequest(requestEvent); // register callback function 
    TWAMR = (0x11 | 0x12) << 1; // set filter for given adr 
} 
+1

Добро пожаловать в SO. Не забудьте добавить 4-пространственный отступ в ваш код, чтобы он отображался правильно. Кроме того, я бы рекомендовал добавить к нему некоторую аннотацию. – YakovL

1
void requestEvent() { 
    int adr = TWDR >> 1; // move 1 bit to align I2C adr 
    if (adr == 0x12)  // check for altitude request at adr 12 
     Wire.write(tmpSpektrumDataAlt, 16); // send buffer 
    if (adr == 0x11)  // check for speed request at adr 11 
     Wire.write(tmpSpektrumDataSpd, 16); // send buffer 
} 

Это работает с использованием спектра DX8 с модулем телеметрии. Интерфейс Spectrum был опубликован на домашней странице Sectrums. Технические документы.

+0

Добро пожаловать в SO. Не забудьте добавить 4-пространственный отступ в ваш код, чтобы он отображался правильно. – YakovL

0

На шине I2C могут быть другие устройства, TWAMR должен быть установлен как можно меньше бит. Так что я думаю, что лучший способ для расчета маски:

AddrOr = Addr1 | Addr2 | Addr3 | Addr4 ... 
AddrAnd = Addr1 & Addr2 & Addr3 & Addr4 ... 
TWAMR = (AddrOr^AddrAnd) << 1 

в то время как TWAR может быть установлен либо как AddrOr или AddrAnd Таким образом, мы можем ограничить возможность адресного конфликта к минимальным