В этом уроке мы научимся определять адреса устройств на шине I2C, аппаратно устанавливая состояния Start/Stop на шине I2C и передавая байт данных с адресом устройства и битом RW.
Нам понадобится:
- Arduino х 1шт.
- LCD дисплей LCD1602 IIC/I2C(синий) или LCD1602 IIC/I2C(зелёный) х 1шт.
- Trema Shield х 1шт.
- Trema-модуль i2C Hub х 1шт.
- Trema-модуль кнопка c проводом х 1шт.
- Шлейф «мама-мама»для шины I2С х 2шт.
- Устройства и модули с шиной I2C, адреса которых Вы хотите узнать, до 126 шт.
Для реализации проекта нам необходимо установить одну библиотеку:
- Библиотека LiquidCrystal_I2C_V112 (для работы с дисплеем LCD1602 по шине I2C)
О том как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE, а о том, как работать с LCD дисплеями, на странице Wiki - Работа с символьными ЖК дисплеями.
Видео:
Схема подключения:
- Подключение LCD дисплея осуществляется к аппаратным выводам шины I2C.
- Кнопка подключается к цифровому выводу 2.
Алгоритм работы:
- При однократном нажатии на кнопку - выводится очередной адрес.
- При удержании кнопки - производится чтение адресов, всех подключенных к шине I2C устройств.
Код программы:
// Подключаем библиотеки: #include <Wire.h> // подключаем библиотеку для работы с шиной I2C (для работы с дисплеем) #include <LiquidCrystal_I2C.h> // подключаем библиотеку для работы с LCD дисплеем // Объявляем переменные и константы: LiquidCrystal_I2C lcd(0x27,16,2); // объявляем переменную для работы с LCD дисплеем, указывая параметры дисплея (адрес I2C = 0x27, количество столбцов = 16, количество строк = 2) const uint8_t PIN_button_SET = 2; // указываем номер вывода arduino, к которому подключена кнопка SET uint8_t SEC_button_SET; // переменная содержащая десятые доли секунд времени удержания кнопки SET uint8_t NUM_found_ADDR; // количество найденных адресов uint8_t MAS_found_ADDR[127]; // массив найденных адресов uint8_t OUT_found_ADDR = 1; // выводимый адрес из списка найденных void setup(){ pinMode(PIN_button_SET, INPUT); // конфигурируем вывод кнопки, как вход lcd.init(); // инициируем LCD дисплей lcd.backlight(); // включаем подсветку LCD дисплея lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("iArduino.ru")); // выводим текст "iArduino.ru" delay(2000); // ждём 2 секунды lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("Please click...")); // выводим текст "Please click..." } void loop(){ if(digitalRead(PIN_button_SET)){ // Если нажата кнопка, то ... lcd.clear(); // Стираем экран SEC_button_SET=0; // Обнуляем счётчик удержания кнопки while(digitalRead(PIN_button_SET)){ // Создаём цикл, пока кнопка не будет отпушена delay(10); // Пропускаем 10мс if(SEC_button_SET<100){SEC_button_SET++;} // Если прошло меньше 1 секунды, инкрементируем счетчик удержания кнопки else{lcd.setCursor(0, 0); lcd.print(F("Start Search..."));} // Выводим на экран текст "Start Search..." } // Как только кнопка отпущена, выполняем одно из двух действий ... if(SEC_button_SET<100){ // Если кнопка SET удерживалась меньше 1 секунды, то показываем значение очередного адреса ... OUT_found_ADDR++; if(OUT_found_ADDR>NUM_found_ADDR){OUT_found_ADDR=1;} // Инкрементируем номер выводимого адреса }else{ // Если кнопка SET удерживалась дольше 1 секунды, то читаем адреса устройств на шине I2C... NUM_found_ADDR=0; // Сбрасываем количество найденных адресов OUT_found_ADDR=1; // Сбрасываем номер выводимого адреса for(uint8_t i=1; i<127; i++){ // Проходим по всем доступным адресам (от 1 до 126) if(func_START()){ // Устанавливаем состояние СТАРТ if(func_SEND(i)){ // Отправляем байт адреса с битом RW=0 (операция записи) if(i!=0x27){ // Если найден адрес 0x27, то пропускаем его, т.к. это адрес LCD дисплея MAS_found_ADDR[NUM_found_ADDR]=i; NUM_found_ADDR++; // Добавляем адрес в массив }}} func_STOP();} // Устанавливаем состояние СТОП } // Выводим данные на LCD дисплей lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("ALL")); lcd.setCursor(4, 0); lcd.print(F("ADDR = " )); lcd.print(NUM_found_ADDR); if(NUM_found_ADDR){lcd.setCursor(0, 1); lcd.print(OUT_found_ADDR); lcd.setCursor(4, 1); lcd.print(F("ADDR = 0x")); lcd.print(MAS_found_ADDR[(OUT_found_ADDR-1)],HEX);} } } bool func_START() {uint16_t i=0; TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTA); while(!(TWCR & _BV(TWINT))){i++; if(i>=60000){return false;}} if((TWSR & 0xF8)!=0x08){return false;} return true;} bool func_SEND (uint8_t j){uint16_t i=0; TWDR = j<<1; TWCR = _BV(TWINT) | _BV(TWEN); while(!(TWCR & _BV(TWINT))){i++; if(i>=60000){return false;}} if((TWSR & 0xF8)!=0x18){return false;} return true;} void func_STOP () {uint16_t i=0; TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); while(!(TWCR & _BV(TWSTO))){i++; if(i>=60000){break; }} delayMicroseconds(20); }
Алгоритм работы программы:
Код в функции loop() начинает выполняться только при условии, если нажата кнопка. Если таковое событие произошло, то начинает исполняться цикл while() выход из которого осуществляется при отпускании кнопки. В самом цикле происходит подсчёт времени удержания кнопки.
После отпускания кнопки, программа выполняет одно из двух действий:
- Если кнопка удерживалась меньше 1 секунды, то происходит увеличение порядкового номера выводимого адреса.
- Если кнопка удерживалась дольше 1 секунды, то происходит чтение всех доступных адресов на шине I2C.
Чтение адресов на шине I2C происходит в цикле for() от 1 до 127, где индекс цикла соответствует проверяемому адресу. Проверка на существование адреса осуществляется следующим образом:
- Вызывается функция func_START, которая устанавливает состояние СТАРТ на шине I2C и возвращает результат установки данного состояния.
- Если состояние СТАРТ установлено, то вызывается функция func_SEND, которая передает свой аргумент как байт адреса с битом RW=0 и если на шине зафиксирован ответ ACK, то возвращает результат true, иначе false.
- Если на шине зафиксирован результат ACK, то переданный адрес сохраняется в массив найденных адресов, за исключением адреса 0x27 который является адресом LCD дисплея.
- Вызывается функция func_STOP, которая устанавливает состояние СТОП на шине I2C.
И последнее действие программы - вывод данных на дисплей, не зависимо от того, как долго удерживалась кнопка.
Так как передача данных осуществляется только после отпускания кнопки, то допускается «горячее» подключение проверяемых устройств к шине I2C, если кнопка не нажата.
Обсуждение