STM32 и LCD через I2C


    Для использования в дальнейшем понадобилось связать, используя I2C микроконтроллер STM32 с экраном 2004. Не найдя аналогичного решения в сети, публикую здесь. Данный рецепт подойдёт также для экранов 1602. Далее под катом. (Осторожно, картинки).

    Игрушечная касса, купленная сыну, оказалось с дефектом, и работала через раз. Появилась идея переделать её внутренности, и момент выбора микроконтроллера совпал с публикацией статьи RaJa про STM32 [1]. Немного прикинув и сравнив цены: STM32+LCD2004+I2C = ArduinoMega (причина была в том, что нужно было реализовать клавиатуру, динамик, устройство ввода штрих-кода и экран, поэтому каждый вывод микроконтроллера на счету) я выбрал первый набор.

    Были сделаны покупки, и наступило время ожидания. Для прошивки купил ещё USB-USART переходник.
    Что и где покупалось.
    1. STM32F103C8T6
    2. 2004 LCD HD44780. Оказался без кириллицы. Обращайте внимание на данную особенность при поиске, если нужен русский язык на экране.
    3. IIC/I2C/TWI/SP​​I Serial Interface Board Module Port For Arduino 1602LCD Display По описанию совместим с 2004. Но думаю подойдёт любой аналогичный.
    4. USB to UART TTL CP2012 для прошивки и отладки. Можно воспользоваться и другими поддерживаемыми способами прошивки и отладки, но этот вариант самый дешевый.


    Средства для программирования, прошивки и отладки, используемые мною:
    1. EmBlocks.
    2. Прошивальщик с оригинального сайта: STM32 and STM8 Flash loader demonstrator.
    3. Терминал для чтения сигналов от MK через USB2UART: Terminal v1.91b. Но подойдёт и Putty (Connection->Serial).

    После получения микроконтроллера попробовал поиграть с светодиодами, получилось. А потом были несколько часов попыток связать экран с МК. Всё это описывать скучно, попробую вспомнить грабли, на которые напоролся.

    Первым опишу подключение. Странно, описывая использование STM32 мало где рисуют схемы, в основном код, сам догадайся, что и как подключить.
    Подключение изображу на фотографии (по клику — крупнее).

    Данное подключение актуально для STM32F103C8. Для других плат МК проверьте пины подключения I2C1 по даташиту.
    USART переходник в USB. Тут понятно. Далее — USART подключаем к STM32 к выведенному около разъема miniUSB USART1. TX к RX и соответственно RX к TX. У меня на USART есть вывод 3v3, я от него и запитал МК. Землю я подключил отдельно, для удобного её отключения во время переключения режимов прошивки и работы. К экрану я припаял I2C (так же на ebay есть экраны с припаянными I2C). Питание для I2C и экрана берётся от 3v3 МК или 5В от USART. Ниже написал про настройку контраста при различном напряжении питании. Далее: SCL от I2C подключается к PB6, SDA от I2C к PB7. Притягивать SCL и SDA к питанию при использовании одного данного устройства нет необходимости.

    Первыми граблями был USART. Его я использовал для отладки, в приведённом здесь коде строки работы с ним закомментированы. Но с ним проблему так и не решил. Такое впечатление, что нет синхронизации между компьютером и микроконтроллером до посылки первого символа. Причем если использовать код из примера [4] — то МК прекрасно дублирует получаемый текст, а сам писать не может. Я добился наиболее приемлемого для отладки вывода строк, добавив Delay(500) после каждого символа.

    Потом попытался реализовать работу с I2C. Взял код из примера [3], обратил внимание на комментарии про подвисание МК, проанализировав исходники, увидел что как и автору комментариев, мне необходим сдвиг адреса устройства влево:

    //http://microtechnics.ru/stm32-ispolzovanie-i2c/#comment-8109
    I2C_Send7bitAddress(I2Cx, slaveAddress<<1, transmissionDirection);
    

    Вставил код и попробовал запустить. Программа повисала на моменте ожидания освобождения шины:

    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    

    Тут грабли в адресе I2C устройства. Судя из описания продавца, у меня был адрес 0x20. Вот тут я и потерял 15 минут впустую, но вчитавшись в описание разных моделей I2C переходников, ссылку [6] на которое привёл в своей статье [5] romanvl, обратил внимание на последнюю модель и попробовал поменять адрес на 0x27. Всё заработало. Вывод такой: если у Вас на переходнике запаяны A0 A1 A2 — адрес 0x20, не запаяны — 0x27.
    Сравните:

    Далее — экран. Оказалось, что он прекрасно работает и от 3.3 Вольт, как и переходник I2C (в даташите микросхемы переходника — от 2.5 до 6 В). Но сначала я его проверял от 5В. И контраст был выкручен на максимум. В итоге в результате запуска программы экран был полностью заполнен. Я расстроился и продолжил ковырять код. Но спустя полчаса проснулся и подбежал виновник разработки, я ему показал экран и случайно увидел под углом сбоку, что там что-то написано. Причиной этому является неправильная регулировка контраста. (Извините, если описал тут очевидные вещи, может найдутся такие же, кто этого не знал.)
    Ничего не видно
    То же самое, но под углом

    При 5В питания контраст нужно немного уменьшить. А при 3.3В поставить на максимум, на настройке от 5В ничего не видно. Результат представлен на первой картинке в посте. Мой оказался без русского языка, я это увидел, пролистав символы. Попробовал нарисовать кляксу, не зная, что максимум можно определить 8 своих символов, написал для кляксы 12. Подобрал похожие из китайских, вроде получилось.

    Код представлен на гихабе, так как для достижения результата переписал библиотеку от Ардуины: STM32_LCD_I2C.

    Использованные материалы:
    1. Причина выбора микроконтроллера: STM32 vs Arduino.
    2. Отсюда взял реализацию Delay: STM32 I2C EEPROM 24СXX.
    3. Cтатья про I2C STM32. Использование I2C.microtechnics.ru/stm32-ispolzovanie-i2c. Тут же комментарий про сдвиг, без которого я бы наверное так же поймал грабли что и обсуждающие.
    4. Пример работы с USART STM32. USART. Часть 4 — Финал.
    5. Уменьшаем количество проводов в Arduino — I2C LCD экран и RTC часы по двум проводам.
    6. LCD Displays (Blue and YELLOW) with I2C/TWI Interface.
    7. Для понимания логики работы STM32 с внешними устройствами Руководство к быстрому старту по работе с периферией STM32F10x.

    Спасибо за внимание.
    Поделиться публикацией

    Комментарии 22

      +1
      Вот жеж совпадение. Как раз сейчас проектирую схему с дисплеем 20x4 и платой STM32F0 Discovery (хочу контроллер для продольной оси токарного станка сделать). Про I2C как-то и не подумал.

      Пошёл покупать I2C интерфейс.
        +1
        Рад помочь. Я думаю, что по I2C даже проще, чем напрямую с экранами работать. Хотя вопрос приёма информации я не затрагивал.
          0
          Подумав ещё чуть-чуть, решил, что обойдусь без I2C. Мой дисплей почему-то не завёлся от 3v (от 3.3v тоже не завёлся), соответственно, если подключать по I2C то надо как-то выравнивать уровни между 3v и 5v.

          Если же подключать напрямую, то в теории можно обойтись без конверсии, 3v с точки зрения 5v логики должно восприниматься как «1». А чтоб обезопасить MCU от 5v со стороны LCD, LCD подключить в write-only режиме. Ножек у меня до чёртиков, хотел немного провода сэкономить, но получается тоже смысла мало — минимально можно на 6 проводах подключить (4 на данные, E и RS).
            0
            Странно, может подсветка просто не включилась? Если по схеме ниже подать питание и подсветку должна включиться самодиагностика с заполненной верхней строкой. Сейчас нет возможности проверить, какое напряжение на эти выводы подал i2c, постараюсь вечером посмотреть.
            И на вашем МК должны быть 5В толерантные выводы, посмотрите документацию, к ним и подключите.
              0
              Я так примерно и делал. Единственное, что я V0 сразу на GND замкнул. Ну и подсветку не включал. Но это в худшем случае должно было выдать хотя бы квадраты. Подаешь 5v — квадраты есть, подаешь 3v — ничего.
                0
                проблема может заключаться в том что для контраста на 3.3В питании надо будет сформировать отрицательное напряжение. Попробуйте подать на V0 = -5В относительно положительного вывода питания индикатора, то есть -1.7В относительно общего провода.
                  0
                  Да я на 5v лучше подключу. Оказывается, большая часть ножек чипа, который я использую, толерантна к 5v.

                  Всё равно основное питание будет 5v (драйвер шаговика требует 5v), а 3v будет от 5v регулятором на плате STM32F0DISCOVERY получаться.
                  0
                  Проверил, на V0 нулевое напряжение. Видимо да, подключено к земле. Наверное особенность вашего экрана, что не завелся он от 3В. Но ведь можно i2c запитать от 5В. Я думаю в моем случае придётся так и поступить, потому что совсем потускнеет экран, если напряжение упадёт до не критичных для МК 2.7В.
          0
          А что в итоге с кассой?
            +1
            Пока только экран связал с МК. Далее нужно думать о клавиатуре и устройстве чтения штрих-кодов. В это устройство конечно много заложено (6 ИК фото-диодов), но думаю реализовать, как было сделано в оригинале — просто при срабатывании любого писать рандомную цену. Постараюсь по возможности описывать шаги, если будет интересно получаться.
            +4
            The PCF8574 extender is available in two versions, the PCF8574 and the PCF8574A.
            The only difference between the two is the I2C base address.
            The base address for the PCF8574 is 0x20 and the base address for the PCF8574A is 0x38.

            Так что, если паяете сами или не можете найти адрес устройства, нужно А0-А2 припаять к земле и искать по этим адресам — 0x20 и 0x38.
            PCF8574 стоит рублей 20-30, кроме переменного резистора и макетки в принципе, ничего не нужно.
            Для ардуины схема примерно такая image
            Эта схема работает с этой библиотекой http://www.xs4all.nl/~hmario/arduino/LiquidCrystal_I2C/LiquidCrystal_I2C.zip
              0
              Я переписывал библиотеку, на которую указал в топике romanvl, что под номером 5 в списке использованного. Спасибо за разъяснения про PCF8574.
                +2
                Для того, чтобы найти правильный адрес можно воспользоваться i2c сканером, например таким playground.arduino.cc/Main/I2cScanner
                Для подключения толпы устройств по i2c экономит много времени на поиски адресов.
                +2
                Кстати, LCD с кириллицей можно купить поискав в интернетах по названию — MT-20S4A
                  0
                  Насчет простоты использования я бы так не сказал, единственное преимущество — экономятся выводы контроллера. А состороны программы нужна еще дополнительная прослойка в виде поддержки такого переходника, ведь ногодрыг выводами индикатора никуда не девается и все равно нужно реализовывать весь протокол обмена с конкретным индикатором, только в данном случае накладные расходы по тактам только увеличиваются — на каждое изменение линии приходится полноценный обмен по I2С шине. Хоть и происходит это быстрее(быстрее ли? для I2C предел в 500кбод), в итоге данные на индикатор уходят медленней чем прямым выводом, хотя это на быстром камне идет в плюс — не нужны дополнительные задержки чтобы подстроить быстродействие индикатора под шустрый камушек.

                  Польза от такого конвертора(помимо экономии выводов) состоит в том чтобы подключать несколько индикаторов в массив адреса преобразователей задаются перемычками и поидее позволяют выставить до 8 различных адресов, так же включить в эту же сеть и другие периферийные узлы — часы, термометр и т.д.
                    0
                    Насчёт прослойки для поддержки I2C: я как-то решил поработать с LCD через SPI и написал библиотеку. В ней работа с пинами дисплея идёт через драйверы, так что перенос LCD с GPIO на SPI или I2C сильно упрощается: пишешь драйвер для дрыгания пинами через нужную шину — и всего делов. А если со всей периферией работать через единый интерфейс, то можно вообще без проблем переключать девайсы туда-сюда по шинам, один раз написав драйвера для GPIO, SPI и I2C.

                    Что касается скорости, то тут, как обычно: меняем скорость на удобство или наоборот. Всякому известно, что кодом на ассемблере можно выжать 100% из камня, да вот только писать на нём — тот ещё геморрой. То же и здесь.
                    +1
                    Первыми граблями был USART
                    А всё потому, что документацию читать не нужно, а библиотека для работы с периферией — для слабаков.

                    //Функция передачи символа
                    void Usart1_Send_symbol(uint8_t data)
                    {
                      while(!(USART1->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
                      USART1->DR = data; //Записываем значение в регистр данных - передаем символ
                    }
                    
                    //Проверяем установку флага TC — завершения предыдущей передачи
                    Вы в своём комментарии сами себе сообщаете о причине ошибки. Надо так:

                    void Usart1_Send_symbol(uint8_t data)
                    {
                      USART_SendData(USART1, data);
                      do {} while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // ждём, пока не очистится буфер передачи (TX Empty)
                    }
                    
                      0
                      Спасибо, но видимо все статьи по USART c STM32 в рунете с этой ошибкой, только пример из статьи №4 из ссылок завелся.
                        0
                        Всегда смотрите в документацию на нужное семейство МК — Reference Manual — особенно, если что-то не работает или работает не так, как задумано. Благо документация по STM32 — образец для подражания. Ну и Application Notes поглядывайте — примеры на всякую периферию. На st.com, когда смотрите инфу по конкретному МК, обратите внимание на «вкладку» Design Resources — там и Reference Manual, и Appnotes. Errata тоже смотрите на всякий случай; ошибок в камнях у ST очень-очень мало, и они, как правило, проявляются при весьма специфических условиях, но перестраховка никогда не бывает лишней.
                      0
                      И ещё.

                      Я правильно понимаю, что USART используется для загрузки прошивки через bootloader? А USB на плате для этого не используеться, т.к у этого конкретного чипа bootloader работает только через USART?
                        0
                        Да, в данном случае для меня это было самым простым способом загрузки прошивки. Данная модель, насколько я помню, прошивается через USART, J-TAG, SW.
                          +1
                          Причём, на платах STM32xxxxDiscovery уже есть ST-Link, который можно использовать и для прошивки других МК.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое