VladislavS 39 9 марта, 2023 Опубликовано 9 марта, 2023 · Жалоба 4 часа назад, Mark16 сказал: ну мне без автостопа как-то надо. Реально надо или вы себе это придумали и героически боретесь? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Mark16 0 9 марта, 2023 Опубликовано 9 марта, 2023 · Жалоба 21 час назад, Obam сказал: Каких (что значит) пакетов? перепутал, написал пакетов, на самом деле байт 21 час назад, Obam сказал: за один заход в состоянии получать? да 18 часов назад, VladislavS сказал: Реально надо или вы себе это придумали и героически боретесь? Пытаюсь этот исходник на stm32 перенести . И там такая функция есть Спойлер void SSD1306Device::ssd1306_fillscreen(uint8_t fill) { ssd1306_setpos(0, 0); ssd1306_send_data_start(); // Initiate transmission of data for (uint16_t i = 0; i < 128 * 8 / 4; i++) { ssd1306_send_byte(fill); ssd1306_send_byte(fill); ssd1306_send_byte(fill); ssd1306_send_byte(fill); } ssd1306_send_data_stop(); // Finish transmission } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Obam 38 9 марта, 2023 Опубликовано 9 марта, 2023 (изменено) · Жалоба после передачи нескольких байт затыкается И что, NACK от ведомого? При заполнении видеопамяти? Смотреть что внутри ssd1306_send_byte(fill). Ещё: занятная SSD1306 м/с (глянул datasheet). void SSD1306Device::ssd1306_fillscreen(uint8_t fill) - мутная по смыслу, т.к. не заполнит дисплей при страничной адресации (а только при вертикальной или горизонтальной адресации) 128 * 8 / 4 - зачем, и по 4 байта? Когда надо либо 8 строк по 128 байт (страничной адресацией), либо одним чохом 1024 байта (вертикальной\горизонтальной адресацией). Изменено 9 марта, 2023 пользователем Obam 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlanDrakes 1 10 марта, 2023 Опубликовано 10 марта, 2023 · Жалоба Я подключал этот дисплей на шину SPI и отправлял данные блоками по 128 байт (дополнительно обрамляя их командами записи в определённые ячейки памяти, ибо похожий контроллер SH1106 не умел переходить на следующую строку - приходилось самому. А SSD1306 умеет, но команда ему не мешает - так что оставил как есть). И, да. Присоединяюсь к рекомендации Obam - слать блоками по 128 байт. Вообще, перепишите функцию отправки. У вас исходник под AVR, а тут всё же более продвинутая периферия - можно много чего сделать удобнее. VladislavS привёл в пример готовый код. Если хотите максимально просто - приложу свой. Не использует плюсы, всё на регистрах. Заточено под F401 (но I2C периферия в L05x не особенно отличается - проверил по референсу). У меня НЕ обрабатываются пограничные случаи, как отправка OVER 255 байт в устройство (т.к. столько не требовалось, да и микросхема EEPROM, под которую писал код, всего на 256 байт. Функции в случае ошибки при передаче возвращают -1 (0xFF), в случае успеха - 0. Спойлер Сигнатуры функций: DevAddr - Адрес устройства на шине. 7 бит - 0x00~0x7F MemAddr - Адрес в устройстве (идёт вслед за DevAddr - так микросхемы памяти получают начальный адрес) *Mem - Указатель на массив байт, откуда (или куда) будет читаться (писаться) в (из) ведомого Len - Количество байт в массиве. НЕ суммарное количество байт. Не учитывается адрес, заголовок и стопы-старты. Чтение одного байта из [DevAddr] по адресу [MemAddr]. Результат будет положен по адресу указателя [*Mem] uint8_t I2C_RdByte(uint8_t DevAddr, uint8_t MemAddr, uint8_t *Mem) { I2C1->CR1 |= I2C_CR1_PE; I2C1->CR1 |= I2C_CR1_START; while (!(I2C1->SR1 & I2C_SR1_SB)){}; (void)I2C1->SR1; (void)I2C1->SR2; // send device address I2C1->DR = (DevAddr << 1); while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){}; if (I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) { I2C1->SR1 = 0; I2C1->CR1 |= I2C_CR1_STOP; return -1; }; (void)I2C1->SR1; (void)I2C1->SR2; I2C1->DR = MemAddr; while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();}; I2C1->CR1 &= ~I2C_CR1_ACK; I2C1->CR1 |= I2C_CR1_START; while (!(I2C1->SR1 & I2C_SR1_SB)){}; (void)I2C1->SR1; (void)I2C1->SR2; // send device address + RD I2C1->DR = (DevAddr << 1) | 1; while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){}; (void)I2C1->SR1; (void)I2C1->SR2; if (I2C1->SR1 & I2C_SR1_RXNE) { *Mem = I2C1->DR; }; I2C1->CR1 |= I2C_CR1_STOP; return 0; } Чтение массива из [DevAddr], начиная с адреса в его памяти [MemAddr], количеством [Len]. Будет помещено в массив по указателю [*Mem] uint8_t I2C_RdArray(uint8_t DevAddr, uint8_t MemAddr, uint8_t *Mem, uint8_t Len) { uint8_t i = 0; I2C1->CR1 |= I2C_CR1_PE; I2C1->CR1 |= I2C_CR1_START; while (!(I2C1->SR1 & I2C_SR1_SB)){}; (void)I2C1->SR1; (void)I2C1->SR2; // send device address I2C1->DR = (DevAddr << 1); while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){}; if (I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) { I2C1->SR1 = 0; I2C1->CR1 |= I2C_CR1_STOP; return -1; }; (void)I2C1->SR1; (void)I2C1->SR2; I2C1->DR = MemAddr; while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();}; I2C1->CR1 |= I2C_CR1_START; while (!(I2C1->SR1 & I2C_SR1_SB)){}; (void)I2C1->SR1; (void)I2C1->SR2; I2C1->CR1 |= I2C_CR1_ACK; // Going to read some data. // send device address + RD I2C1->DR = (DevAddr << 1) | 1; while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){}; (void)I2C1->SR1; (void)I2C1->SR2; while (i < Len) { if (I2C1->SR1 & I2C_SR1_RXNE) { Mem[i] = I2C1->DR; i++; if (i == (Len - 1)) { I2C1->CR1 &= ~I2C_CR1_ACK; }; }; }; I2C1->CR1 |= I2C_CR1_STOP; return 0; } Запись одного байта в [DevAddr] по адресу [MemAddr]. Байт читается по указателю [*Mem] uint8_t I2C_WrByte(uint8_t DevAddr, uint8_t MemAddr, uint8_t *Mem) { I2C1->CR1 |= I2C_CR1_PE; I2C1->CR1 |= I2C_CR1_START; while (!(I2C1->SR1 & I2C_SR1_SB)){}; (void)I2C1->SR1; (void)I2C1->SR2; // send device address I2C1->DR = (DevAddr << 1); while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){}; if (I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) { I2C1->SR1 = 0; I2C1->CR1 |= I2C_CR1_STOP; return -1; }; (void)I2C1->SR1; (void)I2C1->SR2; // Send Memory position to write start. I2C1->DR = MemAddr; while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();}; (void)I2C1->SR1; (void)I2C1->SR2; I2C1->DR = *Mem; while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();}; (void)I2C1->SR1; (void)I2C1->SR2; I2C1->CR1 |= I2C_CR1_STOP; uint8_t i; while (I2C_RdByte(DevAddr, 0, &i)) {delay_ms(1);}; return 0; } Запись массива в [DevAddr], начиная с адреса в его памяти [MemAddr], количеством [Len]. Указатель на память - [*Mem] uint8_t I2C_WrArray(uint8_t DevAddr, uint8_t MemAddr, uint8_t *Mem, uint8_t Len) { uint8_t i = 0; I2C1->CR1 |= I2C_CR1_PE; I2C1->CR1 |= I2C_CR1_START; while (!(I2C1->SR1 & I2C_SR1_SB)){}; (void)I2C1->SR1; (void)I2C1->SR2; // send device address I2C1->DR = (DevAddr << 1); while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){}; if (I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) { I2C1->SR1 = 0; I2C1->CR1 |= I2C_CR1_STOP; return -1; }; (void)I2C1->SR1; (void)I2C1->SR2; I2C1->DR = MemAddr; while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();}; (void)I2C1->SR1; (void)I2C1->SR2; while (i < (Len)) { (void)I2C1->SR1; (void)I2C1->SR2; I2C1->DR = Mem[i]; i++; while (!(I2C1->SR1 & I2C_SR1_TXE)) {__NOP();}; }; I2C1->CR1 |= I2C_CR1_STOP; while (I2C_RdByte(DevAddr, 0, &i)) {delay_ms(1);}; return 0; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 10 марта, 2023 Опубликовано 10 марта, 2023 · Жалоба 7 часов назад, Mark16 сказал: Пытаюсь этот исходник на stm32 перенести . Не вижу тут требование отсутствия Autoend. Количество данных заранее известно. В моей функции просто заменяете отправку из буфера base()->TXDR = *data++; на отправку сначала одной константы I2C1->TXDR = SSD1306_DATA;, а потом в цикле I2C1->TXDR = fill;. И вообще, подобные вещи с DMA делают. Но это можно следующим шагом переделать после того как просто заработает. Ну и осциллоглядом контролировать обязательно. 23 минуты назад, AlanDrakes сказал: Заточено под F401 (но I2C периферия в L05x не особенно отличается - проверил по референсу). При всём уважении, но у F401 реализация I2C старая как у F103 скорее. У L051 новая как в моих функциях. Спойлер Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlanDrakes 1 10 марта, 2023 Опубликовано 10 марта, 2023 (изменено) · Жалоба 19 минут назад, VladislavS сказал: При всём уважении, но у F401 реализация I2C старая как у F103 скорее. У L051 новая как в моих функциях. Вы правы, я что-то не те функции взял. И, да. У 401 как раз старый блок I2C. Но фукнкции таки работают. Исправлюсь. Тут проект был меньше и в основном читает данные с датчиков. Собирается как раз под L051. I2C_ReadArray - читает как есть - это работает с AHT10. ИМХО - забавный датчик. Отправляем команду через запись трёх байт, ждём, читаем тупо массивом. I2C_ReadArray_from8 - аналогично EEPROM'у. Уже нормально работает с BMP(BME)280. Вычитывает массив, начиная с адреса [Addr] и складывает в память. Спойлер uint8_t I2C_ReadArray(uint8_t DevAddr, uint8_t *Array, uint8_t DataToRead) { uint8_t Indx=0; (void) (I2C1->ISR); I2C1->ICR = I2C_ICR_STOPCF; I2C1->ICR = 0x3F38; I2C1->CR2 = (I2C_CR2_AUTOEND | DevAddr << 1 | I2C_CR2_RD_WRN | (DataToRead<<16) | I2C_CR2_START); while (Indx < DataToRead) { while (!(I2C1->ISR & I2C_ISR_RXNE)) {}; Array[Indx] = (I2C1->RXDR); Indx++; }; return Indx; } uint8_t I2C_ReadArray_from8(uint8_t DevAddr, uint8_t Addr, uint8_t *Array, uint8_t DataToRead) { uint8_t Indx=0; uint8_t result = 0; I2C1->TXDR = Addr; I2C1->ICR = 0x3F38; I2C1->CR2 = (((DevAddr << 1) & 0xFE) | (1<<16) | I2C_CR2_START); while (!(I2C1->ISR & (I2C_ISR_TXE | I2C_ISR_NACKF))) {}; if (I2C1->ISR & I2C_ISR_NACKF) { result = 1; } else { I2C1->CR2 = (((DevAddr << 1) & 0xFE) | I2C_CR2_RD_WRN | (DataToRead<<16) | I2C_CR2_START); while (Indx < DataToRead) { while (!(I2C1->ISR & I2C_ISR_RXNE)) {}; Array[Indx] = (I2C1->RXDR); Indx++; } } I2C1->CR2 = I2C_CR2_STOP; return result; } // Запись "Сырого" массива. Если нужно отправить адрес - то перед циклом While (i < DataToWrite) отправить байты адреса. uint8_t I2C_WriteArray(uint8_t DevAddr, uint8_t *Array, uint8_t DataToWrite) { uint8_t i; I2C1->ICR = 0x3F38; I2C1->TXDR = *Array; Array++; I2C1->CR2 = (DevAddr << 1 | ((DataToWrite)<<16) | I2C_CR2_START); if (DataToWrite > 1) { i = 1; while (i < DataToWrite) { while (!(I2C1->ISR & (I2C_ISR_TXE | I2C_ISR_NACKF))) {}; if (I2C1->ISR & I2C_ISR_NACKF) { I2C1->ICR = I2C_ICR_STOPCF | I2C_ICR_NACKCF; delay_ms(1); return -1; } else { I2C1->TXDR = *Array; Array++; i++; }; }; }; while (!(I2C1->ISR & (I2C_ISR_TXE | I2C_ISR_NACKF))) {}; I2C1->CR2 = I2C_CR2_STOP; return 0; } ЗЫ: Вообще не люблю шину I2C. Кроме экономии проводов даёт только проблемы. То после подачи питания контроллер слишком быстро начинает работу с шиной (а некоторые хитро вы.... сделанные микросхемы ещё не очухались), то какой-то чип воспринял переходный процесс как START, то ещё что... Уууууу. То подтяжки слабые, то подтяжки сильные... И гнать шину нельзя. Хотя пара MAX17043 + M24C02 на шине пока что уживаются хорошо. Изменено 10 марта, 2023 пользователем AlanDrakes Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
113 3 3 апреля, 2023 Опубликовано 3 апреля, 2023 · Жалоба Действительно, вывод на дисплей лучше через ДМА. Но это хорошо работает когда есть кадровый буфер, а под него памяти надо много. Ну и да, i2c - не лучший видеоинтерфейс. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Obam 38 3 апреля, 2023 Опубликовано 3 апреля, 2023 · Жалоба ...работает когда есть кадровый буфер, а под него памяти надо много В рамках данной темы целый килобайт (((-8Ж - дисплей 128*64 точки (8 строчек по 8 бит). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
113 3 4 апреля, 2023 Опубликовано 4 апреля, 2023 · Жалоба Ну при таком раскладе я бы не раздумывал. Посмотрел, оказывается я с ним когда-то работал, правда через SPI void SSD1306_UpdateScreen(void) { _CS_0(); for (uint8_t m = 0; m < 8; m++) { SSD1306_WRITECOMMAND(0xB0 + m); SSD1306_WRITECOMMAND(0x00); SSD1306_WRITECOMMAND(0x10); _DAT(); HAL_SPI_Transmit(&hspi1, &SSD1306_Buffer[SSD1306_WIDTH * m], SSD1306_WIDTH, 100); } _CS_1(); } И что самого удивляет, без ДМА и даже без прерываний. Так делать не надо... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться