Чтение оперативной памяти с OLED-контроллера SH1106 через I2C
Вывод: у меня возникли проблемы с написанием программы для получения данных ОЗУ от OLED-контроллера SH1106 через i2c. В первой части вопроса я написал предположения, которые я сделал, чтобы получить информацию, которую я не смог найти в таблице данных, во второй части я разместил программу, которая должна читать эти данные, но не (очевидно). , так как я пишу здесь...).
Я работаю над программой для управления OLED-дисплеем SH1106. Я прочитал техническое описание, чтобы узнать, как отправлять на него команды, и оно работает, я могу писать на свой дисплей.
Теперь в даташите есть один момент, которого я не понимаю: как я могу прочитать оперативную память?
Рассмотрите рисунок ниже, взятый из командной таблицы на страницах 30 и 31 описания SH1106; последняя строка посвящена чтению оперативной памяти.
Этот чип можно подключить к микроконтроллеру через разные интерфейсы: 6800 и 8800 8-битный параллельный, 3-х и 4-х проводной SPI и i2c. Поскольку я использую SPI, у меня нет контактов A0
, RD
или WR
, и я не нашел их нигде в таблице данных. «перевод» логического значения, присвоенного этим сигналам в таблице команд, в некоторый сигнал i2c. Я предполагаю, что WR cor отвечает младшим битом адреса i2c, поскольку он равен 0 для записи и 1 для чтения. И мое предположение кажется верным, поскольку я могу прочитать статус (строка 25).
Затем я предположил, что A0
должен соответствовать DC
(данные/команда, см. второй снимок экрана), бит контрольного байта. Байт управления — это байт, который должен быть отправлен перед каждым байтом или группой байтов, чтобы сообщить чипу, как его интерпретировать (команду или данные). Но я пытался отправить его перед чтением i2c с другими значениями, и я всегда получаю байт состояния (никогда не данные ОЗУ).
Как я мог прочитать память микросхемы SH1106 через i2c? В даташите не указано, что это невозможно (и то, что это возможно...), может быть, я просто не могу?
Таблица команд
(Я удалил строки 1-16, которые содержат команды, подобные тем, что находятся в строках 17-23)
Байт управления
Вот самая короткая работающая программа, которую я смог написать, которая показывает проблему (т.е. содержит ошибки моей реализации). Я протестировал его на Arduino UNO.
#define ADDR 0x78
void i2c_start() {
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
}
void i2c_stop() {
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
}
void i2c_send(uint8_t data) {
TWDR = data;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
}
uint8_t i2c_read(bool isLastByte) {
if (isLastByte) TWCR = (1 << TWINT) | (1 << TWEN);
else TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
return TWDR;
}
void display_init() {
i2c_start();
i2c_send(ADDR);
i2c_send(0x00); //................ байт управления
i2c_send(0xAE); //................ displayOff
i2c_send(0x8D); //................ *
i2c_send(0xD5); i2c_send(0x80); // тактовая частота
i2c_send(0x32); //................ насосНапряжение0123
i2c_send(0xA1); //................сегментПереназначить
i2c_send(0xC8); //................флипВертикально
i2c_send(0xDA); i2c_send(0x12); // comConfiguration
i2c_send(0xA8); i2c_send(0x3F); // мультиплекс
i2c_send(0xD3); i2c_send(0x3F); // отображениеСмещение
i2c_send(0x10); i2c_send(0x00); // адрес столбца
i2c_send(0x40); //................ стартовая строка
i2c_send(0xB0); //................адрес_страницы
i2c_send(0xAF); //................ displayOn
i2c_stop();
}
void setup() {
// Инициализация
Serial.begin(115200);
display_init();
// Запрос данных ОЗУ
i2c_start();
i2c_send(ADDR);
i2c_send(0x80); // управляющий байт
i2c_send(0xB0); // адрес страницы
i2c_send(0x80); // управляющий байт
i2c_send(0x10); // старший адрес столбца
i2c_send(0x80); // управляющий байт
i2c_send(0x00); // низкий адрес столбца
i2c_send(0x40); // управляющий байт
i2c_send(0xE3); // нет
i2c_stop();
// получить данные оперативной памяти
uint8_t a[10];
i2c_start();
i2c_send(ADDR + 1);
for (int i = 0; i < 9; i++) a[i] = i2c_read(false);
a[9] = i2c_read(false);
i2c_stop();
// печать результатов
for (int i = 0; i < 10; i++) {
Serial.print("0x");
Serial.println(a[i], HEX);
}
}
void loop() {}
В разделе // Request RAM data
я перепробовал столько возможностей, сколько мог себе представить. Чтобы избежать путаницы, я оставляю только опубликованный для вашего анализа, но, конечно, если кто-то хочет их увидеть, просто напишите это в комментарии, и я опубликую все ( неудачные) тесты, которые я сделал.
Вывод на последовательный монитор всегда представляет собой список 0x3
s.
Я не стал сокращать процедуру инициализации, которую использую в своем коде, поскольку, возможно, проблема связана с этим. Кстати, я понятия не имею, что такое i2c_send(0x8D); //................ строка *
есть, я добавил ее, потому что она есть во всех других библиотеках SH1106, которые я нашел, но я не нашел ее в мой техпаспорт. Может быть, есть несколько версий чипа или даташита?
@noearchimede, 👍1
2 ответа
Несколько сложно разобраться в протоколе I2C. Немного, чтобы посмотреть:
Если бит R/W установлен в единицу в адресе подчиненного устройства, микросхема будет выводить данные сразу после адреса подчиненного устройства в соответствии с битом D/C, который был отправлен во время последнего доступа для записи. Если мастер не генерирует подтверждения после байта, драйвер прекращает передачу данных мастеру.
Поэтому вам нужно установить бит D/C в транзакции до того, как вы захотите получить доступ к ОЗУ.
Из таблицы неясно, нужно ли отправлять байт данных после управляющего байта, чтобы он был принят, но если вы это делаете, вы можете использовать команду NOP.
Итак, вы выполняете запись NOP с D/C (что, да, соответствует A0 в других интерфейсах), установленным в 1 в управляющем байте (т. е. записываете байты 0x40 0xE3), а затем вы можете выполнить чтение который должен возвращать содержимое ОЗУ по текущему адресу страницы/столбца, который будет автоматически увеличиваться для каждого последующего байта.
Это довольно нечеткое техническое описание, когда дело доходит до объяснения того, как работает чтение I2C...
В моей версии таблицы данных есть блок-схема (рис. 10) выполнения операций "чтение-изменение-запись". В этом есть подсказка - должно быть фиктивное чтение, прежде чем будет выполнено фактическое чтение. Вот моя функция для построения пикселя, которая требует чтения-изменения-записи для добавления пикселя к существующим данным. Неясно, нужно ли фиктивное чтение перед каждым прочитанным байтом или только с первым из последовательности байтов, но это достаточно легко проверить.
#define CTRL_LAST_CMD 0x00
#define CTRL_NEXT_CMD 0x80
#define CTRL_LAST_RAM 0x40
#define CTRL_NEXT_RAM 0xC0
void MySH1106_PlotPixel(char x, char y, char on) {
unsigned char curDisp;
MySH1106_SetPage(y/8);
MySH1106_SetCol(x);
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(CTRL_LAST_CMD);
Wire.write(0xE0); //РМВ включить
Wire.endTransmission(1);
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(CTRL_LAST_RAM);
Wire.endTransmission(1);
Wire.requestFrom(SLAVE_ADDR, 2, 1);
while(Wire.available()<2);
curDisp = Wire.read(); //фиктивное чтение
curDisp = Wire.read(); //настоящее чтение.
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(CTRL_LAST_RAM);
if (on) {
Wire.write((0x1<<(y%8)) | curDisp);
} else {
Wire.write(~(0x1<<(y%8)) & curDisp);
}
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(CTRL_LAST_CMD);
Wire.write(0xEE);//конец RMW
Wire.endTransmission(1);
}
Это ужасно неэффективно для построения графика в один пиксель, но для начала положено.
Редактировать: впоследствии я подтвердил, что требуется только 1 байт фиктивного чтения, даже если чтение остановлено/перезапущено. Пока вы выполняете только чтение из ОЗУ, дальнейшие фиктивные чтения не нужны.
- Как отображать переменные на 0,96-дюймовом OLED-дисплее с библиотекой u8glib?
- Как заставить текст мигать на экране ssd1306 i2c
- Отправка и получение различных типов данных через I2C в Arduino
- Как работают функции вне цикла void?
- OVF в последовательном мониторе вместо данных
- ЖК-дисплей I2C отображает странные символы
- Соединение I2C зависает Ведущий если ведомый отключается
- Экран LCD 16*02 I2C показывает только первый напечатанный символ
Спасибо за ваш ответ. Я протестировал его (в частности, я не рассматривал возможность использования NOP после управляющего байта
0x40
), но я все еще не могу прочитать данные. Я отредактировал свой вопрос, добавив кратчайший код, который я мог написать, который все еще содержит и показывает проблему., @noearchimede