Трехмерная графика на STM32F103

    image

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

    В далеком 2017 году (судя по дате модификации файла) решил перейти я с контроллеров AVR на более мощные STM32. Естественно, первым контроллером был выбран широко распиаренный F103. Не менее естественно, что использование готовых отладочных плат было отвергнуто в пользу изготовления своей с нуля согласно своим требованиям. Как ни странно, обошлось почти без косяков (разве что UART1 стоило бы вывести на нормальный разъем, а не костылить на проводках).

    По сравнению с AVR характеристики камня довольно приличные: 72 МГц тактовой (на практике можно разогнать до 100 МГц, а то и больше, но только на свой страх и риск!), 20 кБ оперативки и 64 кБ флеша. Плюс тонна периферии, при использовании которой главная проблема — не испугаться этого изобилия и осознать, что для запуска не нужно лопатить все десять регистров, достаточно выставить три бита в нужных. По крайней мере до тех пор, пока не захотите странного.

    Когда прошла первая эйфория от обладания такой мощью, возникло желание прощупать ее пределы. В качестве эффектного примера я выбрал расчет трехмерной графики со всеми этими матрицами, освещением, полигональными моделями и Z-буфером с выводом на дисплей 320х240 на контроллере ili9341. Две самые очевидные проблемы, которые предстоит решить — скорость и объем. Размер экрана 320х240 при 16 битах на цвет дает 150 кБ на один кадр. А всего оперативки у нас всего 20 кБ… И эти 150 кБ надо передавать на дисплей хотя бы 10 раз в секунду, то есть скорость обмена должна составлять хотя бы 1.5 МБ/с или 12 Мб/с, что уже выглядит существенной нагрузкой на ядро. К счастью, в данном контроллере присутствует модуль ПДП (прямой доступ к памяти, он же Direct Memory Access, DMA), который позволяет не нагружать ядро операциями переливания из пустого в порожнее. То есть можно подготовить буфер, сказать модулю «вот тебе буфер данных, работай!», а в это время готовить данные для следующей передачи. А если учесть способность дисплея принимать данные потоком, вырисовывается следующий алгоритм: выделяется передний буфер, из которого DMA передает данные на дисплей, задний буфер, в который происходит рендеринг, и Z-буфер, используемый для отсечения по глубине. Буферы представляют собой одну строку (или столбец, неважно) дисплея. И вот вместо 150 кБ нам требуется всего 1920 байт (320 пикселей на строку * 3 буфера * 2 байта на точку), что отлично помещается в память. Второй хак основан на том, что расчет матриц преобразования и координат вершин нельзя проводить для каждой строки, иначе изображение будет искажаться самыми причудливыми способами, да и по скорости это невыгодно. Вместо этого «внешние» расчеты, то есть перемножение матриц трансформации и их применение к вершинам пересчитываются на каждом кадре, после чего преобразуются в промежуточное представление, которое оптимизировано для рендеринга в картинку 320х1.

    Из хулиганских соображений снаружи библиотека будет напоминать OpenGL. Как и в оригинальной OpenGL отрисовка начинается с формирования матрицы преобразования — очистка glLoadIdentity() делает текущую матрицу единичной, потом набор преобразований glRotateXY(...), glTranslate(…), каждое из которых умножается на текущую матрицу. Поскольку эти расчеты будут проводиться только один раз за кадр, особых требований к скорости нет, можно обойтись простыми float, без извращений с числами с фиксированной точкой. Сама матрица представляет собой массив float[4][4], отображенный на одномерный массив float[16] — вообще-то этот способ обычно применяется для динамических массивов, но и из статических можно извлечь немного выгоды. Еще один стандартный хак: вместо постоянного вычисления синусов и косинусов, которых в матрицах поворота немало, посчитаем их заранее и запишем в табличку. Для этого поделим полный круг на 256 частей, вычислим значение синуса для каждой и свалим его в массив sin_table[]. Ну а получить из синуса косинус сможет любой, кто учился в школе. Стоит отметить, что функции поворота принимают угол не в радианах, а в долях от полного оборота, после приведения к диапазону [0… 255]. Впрочем, реализованы и «честные» функции, выполняющие преобразование из угла в доли под капотом.

    Когда матрица готова, можно приступить к отрисовке примитивов. Вообще, в трехмерной графике есть три типа примитивов — точка, линия и треугольник. Но если нас интересуют полигональные модели, внимание следует уделить только треугольнику. Его «отрисовка» происходит в функции glDrawTriangle() или glDrawTriangleV(). Слово «отрисовка» поставлено в кавычки потому что никакой отрисовки на данном этапе не происходит. Мы всего лишь умножаем все точки примитива на матрицу трансформации, после чего выковыриваем из них аналитические формулы ребер y = ky*x + by, которые позволяют найти пересечения всех трех ребер треугольника с текущей выводимой строкой. Одну из них отбросим, поскольку она лежит не на отрезке между вершин, а на его продолжении. То есть для отрисовки кадра нужно всего лишь пройти по всем строкам и для каждой закрасить область между точками пересечения. Но если применить этот алгоритм «в лоб», каждый примитив будет перекрывать те, что были нарисованы раньше. Нам же нужно учитывать Z-координату (глубину), чтобы треугольники пересекались красиво. Вместо простого вывода точки за точкой будем считать ее Z-координату и по сравнению с Z-координатой, хранимой в буфере глубины, либо выводить (обновляя Z-буфер), либо игнорировать. А для расчета Z-координаты каждой точки интересующей нас строки воспользуемся той же формулой прямой линии z = kz*y + bz, вычисленной по тем самым двум точкам пересечения с ребрами. В результате объект «полуфабрикатного» треугольника struct glTriangle состоит из трех X-координат вершин (хранить Y и Z-координаты смысла нет, они будут вычисляться) и k, b коэффициентов прямых, ну и цвет до кучи. Вот здесь, в отличие от расчета матриц преобразования, скорость критична, поэтому уже используем числа с фиксированной точкой. Причем если для слагаемого b достаточно той же точности, что для координат (2 байта), то точность множителя k чем больше, тем лучше, поэтому берем 4 байта. Но не float, поскольку работа с целыми числами все равно быстрее, даже при одинаковом размере.

    Итак, вызвав кучу glDrawTriangle() мы подготовили массив полуфабрикатных треугольников. В моей реализации треугольники выводятся по одному явными вызовами функции. На самом деле было бы логично завести массив треугольников с адресами вершин, но здесь я решил не усложнять. Все равно функция отрисовки пишется роботами, а им без разницы, заполнять ли константный массив или писать триста одинаковых вызовов. Пришло время перевести полуфабрикаты треугольников в красивую картинку на экране. Для этого вызывается функция glSwapBuffers(). Как и было описано выше, она проходит по строкам дисплея, ищет для каждой точки пересечения со всеми треугольниками и рисует отрезки в соответствии с фильтрацией по глубине. После рендеринга каждой строки нужно эту строку отправить на дисплей. Для этого запускается DMA, которому указывается адрес строки и ее размер. А пока DMA работает, можно переключиться на другой буфер и рендерить уже следующую строку. Главное не забыть дождаться окончания передачи если вдруг закончили отрисовку раньше. Для визуализации соотношения скоростей я добавил включение красного светодиода после окончания рендеринга и выключение после завершения ожидания DMA. Получается что-то вроде ШИМ, который регулирует яркость в зависимости от времени ожидания. Теоретически вместо «тупого» ожидания можно было бы использовать прерывания DMA, но тогда я не умел ими пользоваться, да и алгоритм существенно бы усложнился. Для демонстрационной программы это излишне.

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

    Что ж, если ядро простаивает, надо его нагрузить. А нагружать его будем более качественными моделями. Правда, не стоит забывать, что память все-таки сильно ограничена, так что слишком много полигонов контроллер не потянет физически. Простейший расчет показал, что после вычитания памяти на буфер строки и тому подобное, осталось место на 378 треугольников. Как показала практика, под этот размер отлично подходят модели из старой, но интересной игры Gothic. Собственно, оттуда были выдернуты модельки шныга и кровавой мухи (а уже при написании этой статьи и мракориса, красующегося на КДПВ), после чего у контроллера кончилась флеш-память. Но игровые модельки не предназначены для использования микроконтроллером.

    Скажем, они содержат анимацию, текстуры и тому подобное, что нам не пригодится, да и не поместится в память. К счастью, blender позволяет не только пересохранить их в *.obj, более поддающийся парсингу, но и уменьшить количество полигонов если нужно. Дальше при помощи простенькой самописной программы obj2arr *.obj файлы разбираются на координаты, из которых впоследствии формируется *.h файл для непосредственного включения в прошивку.

    Но пока что модельки выглядят просто как одноцветные фигурные кляксы. На тестовой модели это нас не волновало, поскольку все грани были раскрашены в свои цвета, но не прописывать же цвета каждому полигону модельки. Нет, можно, конечно и муху раскрасить в рандомные цвета, но смотреться это будет довольно вырвиглазно, я проверял. Особенно когда цвета еще и меняются на каждом кадре… Вместо этого применим еще капельку векторной магии и добавим освещение.

    Расчет освещения в его примитивном варианте заключается в расчете скалярного произведения нормали и направления на источник света с последующим умножением на «родной» цвет грани.
    Моделек у нас теперь три — две из игры и одна тестовая, с которой начинали. Для их переключения воспользуемся одной из двух кнопок, распаянных на плате. Заодно можно добавить контроль загруженности процессора. Один контроль у нас уже есть — красный светодиод, связанный с временем ожидания DMA. А вторым, зеленым, светодиодом, будем мигать при каждом обновлении кадра — так мы сможем оценить частоту кадров. Для невооруженного глаза она составила что-то около 15 fps.


    В общем-то, я полученным результатом удовлетворен: приятно реализовать что-то, что принципиально невозможно решить в лоб. Конечно, тут еще есть куда оптимизировать и улучшать, но смысла в этом немного. Объективно контроллер для трехмерной графики слаб, причем речь даже не про скорость, а скорее про оперативную память. Впрочем, как и любой образец демосцены, этот проект ценен не результатом, а процессом.

    Если вдруг кому-то будет интересно, исходный код доступен тут.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 63

      +6
      как впихнуть невпихуемое и отобразить в реальном времени трехмерную графику при помощи контроллера, у которого недостаточно ни скорости, ни памяти для этого.

      Это громко сказано… про недостаточность памяти и скорости. Если сравнить со "спектрум" Z80.
      Игрушки были приличные.

        0
        Не воспринимайте излишне серьезно. Будь у контроллера объективно недостаточно ресурсов, это и сделать было бы невозможно.
        А на счет Спектрума — мне казалось, у него все-таки была какая-то аппаратная видеокарта. Да и вообще специфика разная.
          0

          У Спектрума аппаратная часть на рассыпухе "отображала" часть ОЗУ в видеосигналы (если упрощенно).
          С учетом того, что ili9341 это то же память + аппаратная часть, а запись через SPI + DMA в ОЗУ ili9341, в общем то, сравнима с прямой записью в ОЗУ на типичных 8Мгц Z80…
          Те же яйца, только вид сбоку.


          И я не принял серьезно :) Вот если бы на AVR 8 битовом… что гораздо ближе к Спектруму по быстродействию и памяти.

            0
            >Вот если бы на AVR 8 битовом… что гораздо ближе к Спектруму по быстродействию и памяти.

            Так-то AVR8, без зазрения совести:
            ЭМУЛИРУЕТ Спектрум!


            Ну и сравнивать RISC с z80…
              0
              Там, помнится, было два AVR. Один — эмуляция Z80, а второй эмуляция периферии.
                0
                Видео на втором, все остальное на первом.
                И для звука еще один мелкий вроде.
            +3
            Нет, у спектрума нет вообще ничего для аппаратного ускорения. Но весь экран 6144 байта (чёрно-белый)+768 байт атрибуты цветов.
          +2
          Не было ли желания замахнуться на реализацию реймаршера на таком аппарате?
            0
            Нет. Эта поделка — разовая, она не для какой-то цели или отработки алгоритма для дальнейшего применения. Просто было интересно что можно выжать, и то без фанатизма. Изучать другие низкоуровневые алгоритмы вывода изображений на примере stm-ки смысла нет: мощность невелика, а все оптимизации будут жестко завязаны на ее же специфику.
              0
              Ну, многие трюки показались небезынтересными в плане ускорения вычислений. Я иногда пробую всякие волюнтаристские штуки, вроде приравнивания числа пи четырём и возведения в половинную степень вместо извлечения корня.
                +2
                и возведения в половинную степень вместо извлечения корня

                Это разные записи одной и той же операции

                  –2
                  Так-то да. Только скорость выполнения разная.
                    0

                    А есть бенчмарки?
                    Я сильно сомневаюсь, что более универсальная функция pow() оптимизирована лучше специализированной sqrt(). Тем более, что последняя на многих платформах реализована аппаратно.
                    Если уж говорить про "грязную скорость" sqrt() (вернее 1/sqrt(), что более востребовано), то вспоминать нужно про 0x5f3759df

                      0
                      Эта квейковская загогулина работает не только лишь везде, а вообще мало где. Она расчитана под чистый, незамутнённый 32-битный float, и ни на что больше. Бенчмарки надо разумеется писать. Вполне может оказаться, что для ваших случаев функция отработает быстрее арифметического оператора возведения в степень "^", который я использовал вместо math.pow(). В моих поделиях оператор отработал даже быстрее переопределённой функции local pow = math.pow
                      Заодно вопхну сюда ответ на вопрос про число PI, поскольку набежавшие в срачик про мед. маски «специалисты по всем вопросам» подслили карму и писать комменты получается теперь не чаще раза в час.
                      PI=4
                      image

                      PI=3
                      image

                      PI=8
                      image

                      PI=3.1
                      image

                        0
                        Эта квейковская загогулина работает не только лишь везде, а вообще мало где. Она расчитана под чистый, незамутнённый 32-битный float, и ни на что больше

                        Именно поэтому это грязный хак изящная чертова магия.
                        В принципе подобную магию можно сотворить и под float64, но особого смысла это не имеет, т.к. метод дает приближенное значение обратного корня и точности float32 более чем достаточно.


                        Бенчмарки надо разумеется писать

                        раз вы утверждаете, что a быстрее b, то либо вы бенчмарки писали/проводили, либо ваше утверждение голословно


                        оператора возведения в степень "^", который я использовал вместо math.pow()

                        я не знаю, про какой именно язык программирования вы говорите, но в любом случае бинарный оператор это просто сахар для вызова функции/метода.

                          0
                          я не знаю, про какой именно язык программирования вы говорите, но в любом случае бинарный оператор это просто сахар для вызова функции/метода.

                          Я там опечатался в ответе. Не math.pow() имелось в виду, а math.sqrt(), по сравнению с оператором "^". Ну, немного подредактировав бенчмарки, можно сравнить например «math.pow(a)» c «a*a» и с «a^2». Касательно квадратного корня получилось вот так:
                          Бенчмарки
                          local a = os.clock()
                          local b = 0.0
                          for i = 1, 100000000 do
                          	b = math.sqrt(35.73)
                          end
                          a = os.clock() - a
                          print (a)

                          lua ./sqrt_lua.lua
                          14.738681
                          luajit ./sqrt_lua.lua
                          0.073986
                          

                          local sqrt = math.sqrt
                          local a = os.clock()
                          local b = 0.0
                          for i = 1, 100000000 do
                          	b = sqrt(35.73)
                          end
                          a = os.clock() - a
                          print (a)
                          

                          lua ./local_sqrt_lua.lua
                          9.867505
                          luajit ./local_sqrt_lua.lua
                          0.073703
                          

                          local a = os.clock()
                          local b = 0.0
                          for i = 1, 100000000 do
                          	b = 35.73^0.5
                          end
                          a = os.clock() - a
                          print (a)

                          lua ./test_operator.lua
                          2.276411
                          luajit ./test_operator.lua
                          0.073202
                          

                            +1

                            Это прекрасно во всем: и использование lua для теста числодробильни и надежда на то, что 35.73^0.5 вычисляется более одного раза при интерпретации (и хотябы один раз в скомпилированном коде).


                            замените код вашего бенчмарка хотябы на


                            local a = os.clock()
                            local b = 100.0
                            for i = 1, 100000000 do
                                b = math.sqrt(b)*10.0
                            end
                            a = os.clock() - a
                            print (a)

                            local a = os.clock()
                            local b = 100.0
                            for i = 1, 100000000 do
                                b = (b^0.5)*10.0
                            end
                            a = os.clock() - a
                            print (a)

                            и поделитесь результатом )

                              0
                              Забавно. В luajit все три варианта показали примерно одинаковую скорость. А вот в lua, как таковом, внезапно быстрее оказалась локально объявленная функция.
                                0
                                luajit все три варианта показали примерно одинаковую скорость

                                Вы не догадываетесь, почему?
                                Скорее всего ситуацию поменяет банальный print(b) в самом конце программы (даже после print(a)). Если же компилятор уапще умный, то добавьте ввод b, как параметра командной строки

                                0
                                Судя по всему, вы хорошо знакомы с lua. :) Не подскажете ли вы мне такую штуку? Вот я взял модуль NodeMCU с lua-прошивкой. Открыл книжку по lua. И решил накатать преобразователь uart<->wifi. И возникли вопросы:
                                1) Как в lua из массива чисел (иногда с части элементов массива, идущих подряд) сделать строку из их символьных представлений? Сейчас я прогоняю циклом и каждый раз прибавляю к строке один символ, полученный через string.char от элемента массива (как-то так функция называется — пишу по памяти, так как прошивка на другом компьютере). Память кушает хорошо. Это нужно потому, что функции передачи данных работают почему-то со строками. А у меня массив байт.
                                2) Можно ли создать массив не задавая его элементы в цикле? А то приходится for n=1,1000,1 do a[n]=0 end делать. Как-то это не очень красиво. Память массивы тоже кушают отлично… А постоянно расширять массив a[#a+1]=byte не вариант по скорости. Я теперь сразу выделяю кусок под принятые данные в размер максимального пакета.
                                3) Если вы работали с модулем esp8266, то вызывает удивление передача на сервер данных через conn:send. Секундная задержка между вызовом функции send (я передаю где-то по 450 байт за раз) и вызовом функции обратного вызова, подтверждающей приём — это как-то странно. На прошивке с AT-командами такого не было (там сама прошивка вешалась периодически). Я догадываюсь, что дело может быть в том, что модуль ждёт не наберётся ли полный пакет, но как бы это отключить (такое я видел, например, в сокетном API QNX) и инициировать передачу сразу же. Или есть какой-то другой способ передачи без задержек из lua (в AT-прошивке как-то же это сделано)?
                                Спасибо.
                                  0
                                  Судя по всему, вы хорошо знакомы с lua

                                  В основном на уровне написания скриптиков для openwrt. Python что-то более серьезное там развернуть ресурсов маловато, а писать на баше у меня борода недостаточно густая. На вопросы попробую ответить, но порядок придется нарушить.


                                  2) Можно ли создать массив не задавая его элементы в цикле?

                                  В Lua в принципе нельзя создать массив (в понимании C). Нет там такого типа данных. Есть ассоциативные массивы (tables), т.е. хранилища пар ключ(любого типа) — значение(любого типа). Если в качестве ключа вы будете использовать number, то получите типа массив. Но это не будут последовательно расположенные в памяти элементы. оверхэд такого решения вы почувствовали и сами. Инициализируете вы такой "массив" правильно.


                                  1) Как в lua из массива чисел (иногда с части элементов массива, идущих подряд) сделать строку из их символьных представлений?

                                  Как вы понимаете, раз нет никаких массивов, то нет и никакого 'подряд', поэтому и строку вы получаете правильно.


                                  3) Если вы работали с модулем esp8266

                                  Если речь про NodeMCU, то нет


                                  ps:
                                  Вышесказанное относилось к 'прикладному' Lua. Используя функции Lua C API, предназначенные для манипуляции с данными получаемыми/передаваемыми из внешних библиотек, теоретически возможно все это серьезно заоптимизировать. Но это (на мой взгляд) подобно сексу в гамаке стоя. Ибо зачем так извращаться, когда можно просто написать нужное на C/C++. Под ту же ESP есть ряд фреймворков, в том числе с низким порогом входа. Та же Arduino.

                                    0
                                    Спасибо за информацию!
                                    Что касается С++ для ESP, то что-то я подозреваю, что запуск TCP и WIFI не будут простыми. Впрочем, надо посмотреть. :)
                                      0
                                      Поковыряйте Luajit с FFI.
                                      habr.com/ru/post/113804
                                      habr.com/ru/post/113804
                                      Цикл можно ускорить при помощи простого трюка
                                      for i=0, 1000, 10 do
                                      a[i]=0
                                      a[i+1]=0
                                      ...
                                      a[i+9]=0
                                      end
                                      

                                        0

                                        О, специалист по оптимизации подтянулся.


                                        Поковыряйте Luajit с FFI

                                        А что, на встроенную в NodeMCU Lua завезли Luajit ?


                                        Цикл можно ускорить при помощи простого трюка

                                        И как же в данном случае разворачивание цикла его ускорит ?

                                          –1
                                          Откуда же мне знать-то? Моё дело — набросить. Да и захардкодить если массив a ={0,… 0,} и потом require або dofile его, тоже наверно всё зря. Или использовать local. Или всё сразу. Но лучше наверно не надо.
                                        0

                                        Сложность работы в первую очередь не от языка зависит, а от используемого фреймворка. Это не синонимы. Если писать на C, используя espressif sdk — будет не очень легко, если использовать arduino, то весь пример "мост tcp-serial" https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino по мне выглядит проще, чем заняться с творческой перепаковкой данных на lua.

                                          0
                                          Если писать на C, используя espressif sdk — будет не очень легко


                                          Я, собственно, это и имею в виду. :) Если в SDK есть компоненты достаточно высокоуровневой сокетной библиотеки, то всё будет просто. А вот если нет…

                                          по мне выглядит проще, чем заняться с творческой перепаковкой данных на lua.


                                          Так я не использую Arduino. :) А простое соединение я сделал и так, но меня не устроила скорость работы в lua функции send.

                                          Впрочем, пример мигания на Си у меня откомпилировался и заработал. :)

                                          А на lua я такое написал пока что:
                                          Программа на lua
                                          --****************************************************************************************************
                                          --глобальные переменные
                                          --****************************************************************************************************
                                          ReceivedArray={}--принятые байты данных
                                          ReceivedArraySize=0;--количество принятых данных
                                          StuffingOn=false--включёна ли замена байт
                                          
                                          BEGIN_PACKAGE=0xff--начало пакета
                                          END_PACKAGE=0x00--конец пакета
                                          STUFFING_ON=0xfe--включение стаффинга
                                          MAX_RECEIVED_ARRAY=512--максимальный размер принятого пакета с данными
                                          SCK=nil
                                          
                                          --****************************************************************************************************
                                          --функции
                                          --****************************************************************************************************
                                          
                                          ------------------------------------------------------------------------------------------------------
                                          --произошло подключение к серверу
                                          ------------------------------------------------------------------------------------------------------
                                          function OnConnect(sck,c)
                                           SCK=sck
                                           local answer={string.byte("C",1),string.byte("O",1)}
                                           UARTTransmitt(answer)
                                          end
                                          ------------------------------------------------------------------------------------------------------
                                          --произошло отключение от сервера
                                          ------------------------------------------------------------------------------------------------------
                                          function OnDisconnect(sck,c)
                                           SCK=nil
                                           local answer={string.byte("D",1),string.byte("O",1)}
                                           UARTTransmitt(answer)  
                                          end
                                          ------------------------------------------------------------------------------------------------------
                                          --завершена передача данных
                                          ------------------------------------------------------------------------------------------------------
                                          function OnSend(sck,c)
                                           local answer={string.byte("S",1),string.byte("O",1)}
                                           UARTTransmitt(answer)
                                          end
                                          ------------------------------------------------------------------------------------------------------
                                          --получены данные
                                          ------------------------------------------------------------------------------------------------------
                                          function OnReceive(sck,c)
                                          end
                                          ------------------------------------------------------------------------------------------------------
                                          --настройка UART
                                          ------------------------------------------------------------------------------------------------------
                                          function UARTSetting()
                                           uart.setup(0,115200*8,8,0,1,0)
                                           uart.on("data",1,UARTReceive,0) 
                                          end
                                          ------------------------------------------------------------------------------------------------------
                                          --передача данных по UART с заменой байт
                                          ------------------------------------------------------------------------------------------------------
                                          function UARTTransmitt(data)  
                                           local length=#data
                                           local send=""
                                           send=send..string.char(BEGIN_PACKAGE)
                                           for n=1,length,1 do
                                            local byte=data[n]
                                            if (byte==BEGIN_PACKAGE or byte==END_PACKAGE or byte==STUFFING_ON) then
                                             send=send..string.char(STUFFING_ON)   
                                            end
                                            send=send..string.char(byte)
                                           end
                                           send=send..string.char(END_PACKAGE)
                                           uart.write(0,send) 
                                           send=nil
                                           collectgarbage()
                                          end
                                          
                                          ------------------------------------------------------------------------------------------------------
                                          --анализ полученной команды
                                          ------------------------------------------------------------------------------------------------------
                                          function ExecuteCommand() 
                                           local command=ReceivedArray[1]
                                           if (command==string.byte("C",1)) then--Connect
                                            if (SCK~=nil) then
                                             local answer={command,string.byte("O",1)}--уже подключено
                                             UARTTransmitt(answer)
                                             return
                                            end
                                            local port=ReceivedArray[2]+ReceivedArray[3]*256;
                                            local ip=string.format("%i.%i.%i.%i",ReceivedArray[4],ReceivedArray[5],ReceivedArray[6],ReceivedArray[7]);      
                                            conn=net.createConnection(net.TCP,0)
                                            conn:on("connection",OnConnect)
                                            conn:on("receive",OnReceive)
                                            conn:on("disconnection",OnDisconnect)
                                            conn:on("sent",OnSend)
                                            conn:connect(port,ip)
                                            return
                                           end
                                           if (command==string.byte("D",1)) then--Disconnect
                                            if (SCK==nil) then
                                             local answer={command,string.byte("E",1)}
                                             UARTTransmitt(answer)
                                             return
                                            end
                                            SCK:close()
                                            SCK=nil
                                            collectgarbage()
                                            local answer={command,string.byte("O",1)}
                                            UARTTransmitt(answer)
                                            return
                                           end
                                           
                                           if (command==string.byte("S",1)) then--Send
                                            if (SCK==nil) then
                                             local answer={command,string.byte("E",1)}
                                             UARTTransmitt(answer)
                                             return
                                            end
                                            --for n=2,ReceivedArraySize,1 do
                                            --SCK:send(string.char(ReceivedArray[n]))   
                                            --end
                                            local send=""
                                            for n=2,ReceivedArraySize,1 do
                                             send=send..string.char(ReceivedArray[n])
                                            end
                                            SCK:send(send)
                                            send=nil
                                            collectgarbage()
                                            return
                                           end  
                                            if (command==string.byte("T",1)) then--Test
                                             local answer={command,string.byte("O",1)}
                                             UARTTransmitt(answer)
                                           end
                                          end
                                          
                                          ------------------------------------------------------------------------------------------------------
                                          --получение данных с UART
                                          ------------------------------------------------------------------------------------------------------
                                          function UARTReceive(data)
                                           --используем протокол с байтстаффингом
                                           if (ReceivedArraySize>MAX_RECEIVED_ARRAY) then--начинаем приём заново при превышении размера массива
                                            ReceivedArraySize=0
                                            StuffingOn=false
                                           end
                                            
                                           local byte=string.byte(data,1) 
                                           if (StuffingOn==false) then
                                            if (byte==BEGIN_PACKAGE) then--начало приёма
                                             ReceivedArraySize=0
                                             return
                                            end
                                            if (byte==END_PACKAGE) then--конец приёма
                                             if (ReceivedArraySize>0) then
                                              ExecuteCommand()
                                             end 
                                             ReceivedArraySize=0
                                             return
                                            end
                                            if (byte==STUFFING_ON) then--включение замены байт
                                             StuffingOn=true
                                             return
                                            end
                                            ReceivedArray[ReceivedArraySize+1]=byte
                                            ReceivedArraySize=ReceivedArraySize+1
                                            return
                                           end
                                           --замена байт включена
                                           StuffingOn=false
                                           --если принят неизвестный код, начинаем приём заново
                                           if (byte~=BEGIN_PACKAGE) and (byte~=END_PACKAGE) and (byte~=STUFFING_ON) then
                                            ReceivedArraySize=0
                                            return
                                           end 
                                           ReceivedArray[ReceivedArraySize+1]=byte
                                           ReceivedArraySize=ReceivedArraySize+1
                                          end
                                          
                                          ------------------------------------------------------------------------------------------------------
                                          --вывод полученного IP-адреса
                                          ------------------------------------------------------------------------------------------------------
                                          function ShowIP(params)
                                           print("My IP is " .. params.IP) 
                                          end
                                          ------------------------------------------------------------------------------------------------------
                                          --вывод списка найденных сетей
                                          ------------------------------------------------------------------------------------------------------
                                          function ShowWiFiNet(table)
                                           for ssid,v in pairs(table) do
                                            authmode,rssi,bssid,channel=string.match(v,"(%d),(-?%d+),(%x%x:%x%x:%x%x:%x%x:%x%x:%x%x),(%d+)")
                                            print(ssid,authmode,rssi,bssid,channel)  
                                           end
                                          end
                                          
                                          ------------------------------------------------------------------------------------------------------
                                          --произведено подключение к роутеру
                                          ------------------------------------------------------------------------------------------------------
                                          function RouterConnect(params)
                                           ShowIP(params)
                                           wifi.sta.getap(ShowWiFiNet)
                                           UARTSetting()
                                          end
                                          
                                          --****************************************************************************************************
                                          --начало программы
                                          --****************************************************************************************************
                                          tmr.softwd(-1)
                                          tmr.wdclr()
                                          --создаём массив принятых данных
                                          for n=1,MAX_RECEIVED_ARRAY,1 do
                                           ReceivedArray[n]=0
                                          end
                                          ReceivedArraySize=0
                                          
                                          local heap_size=node.heap()
                                          print(" Free memory:",heap_size,"\r\n")
                                          
                                          wifi.setmode(wifi.STATION)
                                          station_cfg={}
                                          station_cfg.ssid="FPV_WIFI__D49A"
                                          station_cfg.pwd=""
                                          station_cfg.save=false
                                          station_cfg.got_ip_cb=RouterConnect
                                          wifi.sta.config(station_cfg)
                                          --local heap_size=node.heap()
                                          --print("ReceivedArray:",#ReceivedArray," Free:",heap_size,"\r\n")


                                          Но поскольку я использую си++ и компилятор для esp8266 запустился, то я на си++ дальше и попробую сделать прошивку. :) А lua останется так, как рудимент.
                                            0
                                            Так я не использую Arduino

                                            По религиозным соображениям?
                                            На всякий случай уточню — под arduino я имел в виду не отдельную плату (на AVR), к которой подключается ESP, а ESP8266 Arduino core. Где есть все обертки над espressif SDK в привычной ардуинщикам форме + возможность использовать кучу библиотек для разной периферии.
                                            Да, конечно, используя голый SDK теоретически можно делать интереснее и лучше. Но подозреваю, что практически это будет наполнено существенно большей болью.

                                              0
                                              По религиозным соображениям?


                                              Потому что, обычно, могу позволить себе не использовать. :)

                                              ESP8266 Arduino core.


                                              Спасибо, гляну, что там внутри.

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


                                              Пока не знаю — ещё не смотрел SDK.
                                                0
                                                Всё, разобрался с wi-fi и uart из Си для ESP8266. Ничего сложного, благо примеры в инете есть. Правда, там для UDP, а у меня TCP — но там просто немного меняется логика и вместо create ставится connect. Ну и нужно оформить callback для события connect и в нём уже можно начать передавать. Теперь надо мешанину кода переделать в нормальный красивый вид и можно использовать. :)
                                                0
                                                Всё, получилось полностью требуемое на Си. Скорость возросла колоссально (мне надо было примерно пол-мегабайта передавать на сервер раз в минуту). :) Впрочем, может быть, дело было в том, что нужно было отключить энергосберегающий режим WiFi. Потом проверю эту гипотезу. Но на Си всё гораздо приятнее работает, это да. :)
                                                  0

                                                  С энергосберегающим режимом очень правдоподобная гипотеза. При отправке первого пакета. И второго, если он отправляется через продолжительное время. Но отправка полмегабайта в минуту — явно не тот случай. Да и на памятежор, как вы понимаете, powersave влиять не может.
                                                  Рад, что у вас получилось.

                                                    0
                                                    Выложу-ка я тут предварительный код. Вдруг кому пригодится — например, поиском сюда попадёт. Я так часто натыкался на форумах на вопрос и ответ через некоторое время «спасибо, всё решилось». А как решилось, не написано. На github выкладывать не буду, чтобы не засорять свои репозитории.

                                                    Этот код подключается по WiFi к роутеру и по командам по UART соединяется/отключается с заданным сервером и передаёт данные. А вот приём ответа от сервера мне не нужен — я событие вывел, но по UART обратно данные не отправляю.

                                                    user_main.с
                                                    //****************************************************************************************************
                                                    //подключаемые библиотеки
                                                    //****************************************************************************************************
                                                    #include <stdio.h>
                                                    #include <string.h>
                                                    
                                                    #include <osapi.h>
                                                    #include <os_type.h>
                                                    #include <uart.h>
                                                    //#include <mem.h>
                                                    #include <ip_addr.h>
                                                    #include <espconn.h>
                                                    
                                                    #include "user_interface.h"
                                                    #include "led.h"
                                                    #include "uart.h"
                                                    
                                                    //****************************************************************************************************
                                                    //макроопределения
                                                    //****************************************************************************************************
                                                    
                                                    #define WIFI_CLIENTSSID   "name"
                                                    #define WIFI_CLIENTPASSWORD "password"
                                                    
                                                    
                                                    //максимальный размер принятого пакета с данными
                                                    #define MAX_RECEIVED_ARRAY 512
                                                    //максимальный размер передаваемого пакета с данными
                                                    #define MAX_TRANSMITT_ARRAY 512
                                                    
                                                    //****************************************************************************************************
                                                    //перечисления
                                                    //****************************************************************************************************
                                                    
                                                    typedef enum
                                                    {
                                                     WIFI_STATE_CONNECTING,//WiFi подключается к роутеру
                                                     WIFI_STATE_CONNECTING_ERROR,//ошибка подключения WiFi к роутеру
                                                     WIFI_STATE_CONNECTED,//WiFi подключён к роутеру
                                                     WIFI_STATE_TCP_CONNECTED//установлено TCP-соединение
                                                    } WIFI_STATE;
                                                    
                                                    //****************************************************************************************************
                                                    //константы
                                                    //****************************************************************************************************
                                                    
                                                    static const uint8_t BEGIN_PACKAGE=0xff;//начало пакета
                                                    static const uint8_t END_PACKAGE=0x00;//конец пакета
                                                    static const uint8_t STUFFING_ON=0xfe;//включение замены байт
                                                    
                                                    //****************************************************************************************************
                                                    //глобальные переменные
                                                    //****************************************************************************************************
                                                    static uint8_t ReceivedArray[MAX_RECEIVED_ARRAY];//принятые байты данных
                                                    static uint8_t TransmittArray[MAX_TRANSMITT_ARRAY];//буфер данных для отправки
                                                    static size_t ReceivedArraySize=0;//количество принятых данных
                                                    static bool StuffingOn=false;//включёна ли замена байт
                                                    
                                                    static WIFI_STATE WiFi_State=WIFI_STATE_CONNECTING;
                                                    static os_timer_t WiFi_CheckIPTimer;//таймер проверки IP сети WiFi
                                                    static struct espconn ESPConnection;
                                                    static esp_tcp ESPConnectionTCP;
                                                    
                                                    //****************************************************************************************************
                                                    //прототипы функций
                                                    //****************************************************************************************************
                                                    
                                                    static void ICACHE_FLASH_ATTR UART_Transmitt(const uint8_t *data,size_t size);//отправить по UART данные с заменой байт
                                                    static void ICACHE_FLASH_ATTR UART_TransmittMessage(const char *str);//передать по UART текствовое сообщение с заменой байт
                                                    static void ICACHE_FLASH_ATTR UART_ExecuteCommand(const uint8_t *command_ptr,size_t size);//выполнение команды, пришедшей по UART
                                                    static void ICACHE_FLASH_ATTR UART_PushByte(uint8_t byte);//разбор принятых данных по UART
                                                    static void UART_OnReceived(const uint8_t *data,size_t size);//пришли новые данные по UART
                                                    static void UART_OnSendDone(void);//UART закончил передачу данных
                                                    
                                                    static void ICACHE_FLASH_ATTR WiFi_StopTimer(void);//отключить проверку WiFi
                                                    static void ICACHE_FLASH_ATTR WiFi_StartTimer(size_t interval_ms);//запустить таймер проверки WiFi
                                                    static void ICACHE_FLASH_ATTR WiFi_OnSendDone(void* arg);//завершена передача данных по TCP
                                                    static void ICACHE_FLASH_ATTR WiFi_OnReceived(void* arg, char* pusrdata, uint16_t length);//пришли данные по TCP
                                                    static void ICACHE_FLASH_ATTR WiFi_OnConnectedTCP(void* arg);//произведено подключение по TCP
                                                    static void ICACHE_FLASH_ATTR WiFi_OnDisconnectedTCP(void* arg);//произведено отключение по TCP
                                                    static void ICACHE_FLASH_ATTR WiFi_CheckIP(void *arg);//периодическая проверка IP
                                                    
                                                    //****************************************************************************************************
                                                    //реализация функций
                                                    //****************************************************************************************************
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //реализация системной функции, необходимой SDK
                                                    //----------------------------------------------------------------------------------------------------
                                                    uint32 ICACHE_FLASH_ATTR user_rf_cal_sector_set(void)
                                                    {
                                                     enum flash_size_map size_map=system_get_flash_size_map();
                                                     uint32 rf_cal_sec=0;
                                                    
                                                     switch(size_map)
                                                     {
                                                      case FLASH_SIZE_4M_MAP_256_256:
                                                       rf_cal_sec=128-8;
                                                       break;
                                                      case FLASH_SIZE_8M_MAP_512_512:
                                                       rf_cal_sec=256-5;
                                                       break;
                                                      case FLASH_SIZE_16M_MAP_512_512:
                                                      case FLASH_SIZE_16M_MAP_1024_1024:
                                                       rf_cal_sec=512-5;
                                                       break;
                                                      case FLASH_SIZE_32M_MAP_512_512:
                                                      case FLASH_SIZE_32M_MAP_1024_1024:
                                                       rf_cal_sec=1024-5;
                                                       break;
                                                      default:
                                                       rf_cal_sec=0;
                                                       break;
                                                     }
                                                     return(rf_cal_sec);
                                                    }
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //отправить по UART данные с заменой байт
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR UART_Transmitt(const uint8_t *data,size_t size)
                                                    {
                                                     size_t transmitt_size=0;
                                                     size_t n;
                                                     TransmittArray[transmitt_size]=BEGIN_PACKAGE;
                                                     transmitt_size++;
                                                     for(n=0;n<size;n++)
                                                     {
                                                      uint8_t byte=data[n];
                                                      if (byte==BEGIN_PACKAGE || byte==END_PACKAGE || byte==STUFFING_ON)
                                                      {
                                                       TransmittArray[transmitt_size]=STUFFING_ON;
                                                       if (transmitt_size+3>MAX_TRANSMITT_ARRAY) break;
                                                       transmitt_size++;
                                                      }
                                                      TransmittArray[transmitt_size]=byte;
                                                      if (transmitt_size+2>MAX_TRANSMITT_ARRAY) break;
                                                      transmitt_size++;
                                                     }
                                                     TransmittArray[transmitt_size]=END_PACKAGE;
                                                     transmitt_size++;
                                                    
                                                     UART_SendData(TransmittArray,transmitt_size);
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //передать по UART текствовое сообщение с заменой байт
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR UART_TransmittMessage(const char *str)
                                                    {
                                                     UART_Transmitt((uint8_t*)str,strlen(str));
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //отключить проверку WiFi
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR WiFi_StopTimer(void)
                                                    {
                                                     os_timer_disarm(&WiFi_CheckIPTimer);
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //запустить таймер проверки WiFi
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR WiFi_StartTimer(size_t interval_ms)
                                                    {
                                                     os_timer_setfn(&WiFi_CheckIPTimer,(os_timer_func_t *)WiFi_CheckIP,NULL);
                                                     os_timer_arm(&WiFi_CheckIPTimer,interval_ms,0);
                                                    }
                                                    //------------------------------------------------------
                                                    //завершена передача данных по TCP
                                                    //------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR WiFi_OnSendDone(void* arg)
                                                    {
                                                     UART_TransmittMessage("SO");
                                                    }
                                                    //------------------------------------------------------
                                                    //пришли данные по TCP
                                                    //------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR WiFi_OnReceived(void* arg, char* pusrdata, uint16_t length)
                                                    {
                                                    }
                                                    //------------------------------------------------------
                                                    //произведено подключение по TCP
                                                    //------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR WiFi_OnConnectedTCP(void* arg)
                                                    {
                                                     UART_TransmittMessage("CO");
                                                     UART_SendMessage("TCP connected\r\n");
                                                     WiFi_State=WIFI_STATE_TCP_CONNECTED;
                                                    }
                                                    //------------------------------------------------------
                                                    //произведено отключение по TCP
                                                    //------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR WiFi_OnDisconnectedTCP(void* arg)
                                                    {
                                                     UART_TransmittMessage("DO");
                                                     WiFi_State=WIFI_STATE_CONNECTED;
                                                    }
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //выполнение команды, пришедшей по UART
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR UART_ExecuteCommand(const uint8_t *command_ptr,size_t size)
                                                    {
                                                     if (size==7)
                                                     {
                                                      if (command_ptr[0]=='C')//подключение к серверу
                                                      {
                                                       if (WiFi_State==WIFI_STATE_TCP_CONNECTED)//соединение уже было произведено
                                                       {
                                                        UART_TransmittMessage("CO");
                                                        return;
                                                       }
                                                       if (WiFi_State!=WIFI_STATE_CONNECTED)//нет соединения с роутером
                                                       {
                                                        UART_TransmittMessage("CE");
                                                        return;
                                                       }
                                                       //подключаемся к серверу
                                                       WiFi_StopTimer();
                                                    
                                                       ESPConnection.proto.tcp=&ESPConnectionTCP;
                                                       ESPConnection.type=ESPCONN_TCP;
                                                       ESPConnection.state=ESPCONN_NONE;
                                                    
                                                       uint16_t port=command_ptr[2];
                                                       port<<=8;
                                                       port|=command_ptr[1];
                                                       uint8_t ip_str[4*4+1];//4 раза 4 символа (3 цифры с точкой) и завершающий ноль
                                                       os_sprintf(ip_str,"%i.%i.%i.%i",command_ptr[3],command_ptr[4],command_ptr[5],command_ptr[6]);
                                                       UART_SendMessage("Connected:");
                                                       UART_SendMessage(ip_str);
                                                       UART_SendMessage("\r\n");
                                                       uint32_t ip=ipaddr_addr(ip_str);
                                                    
                                                       os_memcpy(ESPConnection.proto.tcp->remote_ip,&ip,sizeof(uint32_t));
                                                       ESPConnection.proto.tcp->local_port=espconn_port();
                                                       ESPConnection.proto.tcp->remote_port=port;
                                                       //espconn_create(&pConn);
                                                    
                                                       espconn_regist_sentcb(&ESPConnection,WiFi_OnSendDone);
                                                       espconn_regist_recvcb(&ESPConnection,WiFi_OnReceived);
                                                       espconn_regist_connectcb(&ESPConnection,WiFi_OnConnectedTCP);
                                                       espconn_regist_disconcb(&ESPConnection,WiFi_OnDisconnectedTCP);
                                                       //espconn_regist_write_finish(pespconn,user_tcp_write_finish);
                                                    
                                                      //espconn_regist_write_finish(pespconn, user_tcp_write_finish); // register write
                                                      //espconn_regist_disconcb(pespconn, user_tcp_discon_cb);
                                                      //espconn_set_opt(pespconn, 0x04); // enable write buffer
                                                      //espconn_regist_write_finish(pespconn, user_tcp_write_finish); // register write
                                                    
                                                       espconn_connect(&ESPConnection);
                                                    
                                                       WiFi_StartTimer(1000);
                                                       return;
                                                      }
                                                     }
                                                     if (size==1)
                                                     {
                                                      if (command_ptr[0]=='D')//отключение от сервера
                                                      {
                                                       if (WiFi_State!=WIFI_STATE_TCP_CONNECTED)
                                                       {
                                                        UART_TransmittMessage("DE");
                                                        return;
                                                       }
                                                       espconn_disconnect(&ESPConnection);
                                                       return;
                                                      }
                                                     }
                                                     if (size>1)
                                                     {
                                                      if (command_ptr[0]=='S')//передача данных на сервер
                                                      {
                                                       if (WiFi_State!=WIFI_STATE_TCP_CONNECTED)
                                                       {
                                                        UART_TransmittMessage("SE");
                                                        return;
                                                       }
                                                       espconn_send(&ESPConnection,(uint8_t*)(command_ptr+1),size-1);
                                                       return;
                                                      }
                                                     }
                                                    }
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //разбор принятых данных по UART
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR UART_PushByte(uint8_t byte)
                                                    {
                                                     if (ReceivedArraySize>MAX_RECEIVED_ARRAY)//начинаем приём заново при превышении размера массива
                                                     {
                                                      ReceivedArraySize=0;
                                                      StuffingOn=false;
                                                     }
                                                     if (StuffingOn==false)
                                                     {
                                                      if (byte==BEGIN_PACKAGE)//начало приёма
                                                      {
                                                       ReceivedArraySize=0;
                                                       return;
                                                      }
                                                      if (byte==END_PACKAGE)//конец приёма
                                                      {
                                                       if (ReceivedArraySize>0) UART_ExecuteCommand(ReceivedArray,ReceivedArraySize);
                                                       ReceivedArraySize=0;
                                                       return;
                                                      }
                                                      if (byte==STUFFING_ON)//включение замены байт
                                                      {
                                                       StuffingOn=true;
                                                       return;
                                                      }
                                                      ReceivedArray[ReceivedArraySize]=byte;
                                                      ReceivedArraySize++;
                                                      return;
                                                     }
                                                     //замена байт включена
                                                     StuffingOn=false;
                                                     //если принят неизвестный код, начинаем приём заново
                                                     if ((byte!=BEGIN_PACKAGE) && (byte!=END_PACKAGE) && (byte!=STUFFING_ON))
                                                     {
                                                      ReceivedArraySize=0;
                                                      return;
                                                     }
                                                     ReceivedArray[ReceivedArraySize]=byte;
                                                     ReceivedArraySize++;
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //пришли новые данные по UART
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void UART_OnReceived(const uint8_t *data,size_t size)
                                                    {
                                                     size_t n;
                                                     for(n=0;n<size;n++) UART_PushByte(data[n]);
                                                    }
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //UART закончил передачу данных
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void UART_OnSendDone(void)
                                                    {
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //системная функция: пред-инициализация
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR user_rf_pre_init(void)
                                                    {
                                                    }
                                                    
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //периодическая проверка IP
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR WiFi_CheckIP(void *arg)
                                                    {
                                                     struct ip_info ipConfig;
                                                    
                                                     WiFi_StopTimer();
                                                     uint8_t wifi_status=wifi_station_get_connect_status();//узнаем состояние соединения с точкой доступа
                                                     if (wifi_status==STATION_GOT_IP)
                                                     {
                                                      wifi_get_ip_info(STATION_IF,&ipConfig);
                                                      if (ipConfig.ip.addr!=0)
                                                      {
                                                       if (WiFi_State!=WIFI_STATE_CONNECTED && WiFi_State!=WIFI_STATE_TCP_CONNECTED)
                                                       {
                                                        WiFi_State=WIFI_STATE_CONNECTED;
                                                        LED_On();
                                                    
                                                        char str[255];
                                                        os_sprintf(str,"My IP:%i.%i.%i.%i\r\n",ipConfig.ip.addr&0xff,(ipConfig.ip.addr>>8)&0xff,(ipConfig.ip.addr>>16)&0xff,(ipConfig.ip.addr>>24)&0xff);
                                                        UART_SendMessage(str);
                                                        return;
                                                       }
                                                       else
                                                       {
                                                        LED_Off();
                                                       }
                                                      }
                                                     }
                                                     else
                                                     {
                                                      LED_Off();
                                                      WiFi_State=WIFI_STATE_CONNECTING;
                                                      espconn_delete(&ESPConnection);
                                                      if (wifi_station_get_connect_status()==STATION_WRONG_PASSWORD)
                                                      {
                                                       WiFi_State=WIFI_STATE_CONNECTING_ERROR;
                                                       UART_SendMessage("STATION_WRONG_PASSWORD\r\n");
                                                      }
                                                      if(wifi_station_get_connect_status()==STATION_NO_AP_FOUND)
                                                      {
                                                       WiFi_State=WIFI_STATE_CONNECTING_ERROR;
                                                       UART_SendMessage("STATION_NO_AP_FOUND\r\n");
                                                      }
                                                      if(wifi_station_get_connect_status()==STATION_CONNECT_FAIL)
                                                      {
                                                       WiFi_State=WIFI_STATE_CONNECTING_ERROR;
                                                       UART_SendMessage("STATION_CONNECT_FAIL\r\n");
                                                      }
                                                      if (wifi_station_get_connect_status()!=STATION_WRONG_PASSWORD &&
                                                          wifi_station_get_connect_status()!=STATION_NO_AP_FOUND &&
                                                          wifi_station_get_connect_status()==STATION_CONNECT_FAIL)
                                                      {
                                                       WiFi_State=WIFI_STATE_CONNECTING;
                                                      }
                                                      WiFi_StartTimer(500);
                                                     }
                                                    }
                                                    
                                                    
                                                    
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //инициализация WiFi
                                                    //----------------------------------------------------------------------------------------------------
                                                    void WiFi_Init(void)
                                                    {
                                                     wifi_set_opmode_current(STATION_MODE);
                                                     wifi_station_disconnect();
                                                     wifi_station_dhcpc_stop();
                                                    
                                                     WiFi_StopTimer();
                                                    
                                                     struct station_config stationConf;
                                                     os_memset(&stationConf,0,sizeof(struct station_config));
                                                    
                                                     os_memset(&stationConf, 0, sizeof(struct station_config));
                                                     os_sprintf(stationConf.ssid, "%s", WIFI_CLIENTSSID);
                                                     os_sprintf(stationConf.password, "%s", WIFI_CLIENTPASSWORD);
                                                     wifi_station_set_config_current(&stationConf);
                                                     wifi_set_sleep_type(NONE_SLEEP_T);
                                                    
                                                     WiFi_StartTimer(1000);
                                                    
                                                     wifi_station_connect();
                                                     wifi_station_dhcpc_start();
                                                    
                                                     if (wifi_get_phy_mode() != PHY_MODE_11N) wifi_set_phy_mode(PHY_MODE_11N);//включим протокол физического уровня типа 802.11n, если таковой уже не включен
                                                     if(wifi_station_get_auto_connect()==0) wifi_station_set_auto_connect(1);//включим автоматическое соединение с точкой доступа в случае разрыва, если не включено
                                                    }
                                                    
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //системная функция: инициализация
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR user_init(void)
                                                    {
                                                     ReceivedArraySize=0;
                                                     StuffingOn=false;
                                                     WiFi_State=WIFI_STATE_CONNECTING;
                                                    
                                                     os_delay_us(10000);
                                                     UART_Init();
                                                     UART_RegisterReceivedFunction(&UART_OnReceived);
                                                     UART_RegisterSendDoneFunction(&UART_OnSendDone);
                                                    
                                                     LED_Init();//если поставить перед UART_Init, то не работает
                                                     LED_Off();
                                                     WiFi_Init();//инициализация WiFi
                                                    }
                                                    



                                                    user_config.h
                                                    #ifndef __USER_CONFIG_H__
                                                    #define __USER_CONFIG_H__
                                                    
                                                    #include <stdio.h>
                                                    
                                                    enum
                                                    {
                                                     EVENT_UART_RECEIVED_DATA=0,//пришли данные по UART
                                                     EVENT_UART_SEND_DONE,//завершена передача по UART
                                                    };
                                                    static const size_t EVENT_QUEUE_ID=0;//идентификатор очереди
                                                    
                                                    #endif
                                                    



                                                    uart.h
                                                    #ifndef UART_H
                                                    #define UART_H
                                                    
                                                    //****************************************************************************************************
                                                    //подключаемые библиотеки
                                                    //****************************************************************************************************
                                                    #include <inttypes.h>
                                                    #include <stdio.h>
                                                    #include <os_type.h>
                                                    
                                                    //****************************************************************************************************
                                                    //типы
                                                    //****************************************************************************************************
                                                    typedef void (*uart_received_func_ptr_t)(const uint8_t*,size_t);//указатель на функцию наличия новых данных
                                                    typedef void (*uart_send_done_func_ptr_t)(void);//указатель на функцию завершения передачи данных
                                                    
                                                    //****************************************************************************************************
                                                    //макроопределения
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //константы
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //глобальные переменные
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //прототипы функций
                                                    //****************************************************************************************************
                                                    
                                                    void ICACHE_FLASH_ATTR UART_Init(void);//инициализация
                                                    void ICACHE_FLASH_ATTR UART_SendMessage(const char *str);//передать текстовое сообщение
                                                    void ICACHE_FLASH_ATTR UART_SendData(const uint8_t* data,size_t size);//передать данные
                                                    void ICACHE_FLASH_ATTR UART_RegisterReceivedFunction(uart_received_func_ptr_t func_ptr);//зарегистрировать функцию обратного вызова на приём данных
                                                    void ICACHE_FLASH_ATTR UART_RegisterSendDoneFunction(uart_send_done_func_ptr_t func_ptr);//зарегистрировать функцию обратного вызова на завершение передачи данных
                                                    
                                                    #endif
                                                    



                                                    uart.c
                                                    //****************************************************************************************************
                                                    //подключаемые библиотеки
                                                    //****************************************************************************************************
                                                    #include "uart.h"
                                                    #include "drvuart.h"
                                                    #include "user_interface.h"
                                                    #include <string.h>
                                                    #include <os_type.h>
                                                    
                                                    //****************************************************************************************************
                                                    //макроопределения
                                                    //****************************************************************************************************
                                                    
                                                    //размер очереди событий UART
                                                    #define UART_EVENT_QUEUE_LENGTH 100
                                                    
                                                    //размер буфера приёма данных
                                                    #define UART_RECEIVED_BUFFER_SIZE 4096
                                                    
                                                    //****************************************************************************************************
                                                    //константы
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //глобальные переменные
                                                    //****************************************************************************************************
                                                    
                                                    static uart_received_func_ptr_t ReceivedFunctionPtr=NULL;//указатель на функцию обратного вызова по приёму данных
                                                    static uart_send_done_func_ptr_t SendDoneFunctionPtr=NULL;//указатель на функцию обратного вызова по завершению передачи данных
                                                    static os_event_t EventQueue[UART_EVENT_QUEUE_LENGTH];//очередь событий UART
                                                    
                                                    static uint8_t ReceivedBuffer[UART_RECEIVED_BUFFER_SIZE];//буфер приёма данных UART
                                                    
                                                    //****************************************************************************************************
                                                    //прототипы функций
                                                    //****************************************************************************************************
                                                    
                                                    static void ICACHE_FLASH_ATTR UART_EventQueueFunction(os_event_t *events);//обработчик очереди сообщений
                                                    
                                                    //****************************************************************************************************
                                                    //реализация функций
                                                    //****************************************************************************************************
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //инициализация
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR UART_Init(void)
                                                    {
                                                     ReceivedFunctionPtr=NULL;
                                                     SendDoneFunctionPtr=NULL;
                                                    
                                                     //uart_init(BIT_RATE_115200);
                                                     uart_init(BIT_RATE_921600);
                                                     //регистрируем очередь событий
                                                     system_os_task(UART_EventQueueFunction,EVENT_QUEUE_ID,EventQueue,UART_EVENT_QUEUE_LENGTH);
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //передать текстовое сообщение
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR UART_SendMessage(const char *str)
                                                    {
                                                     UART_SendData((uint8_t*)str,strlen(str));
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //передать данные
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR UART_SendData(const uint8_t* data,size_t size)
                                                    {
                                                     tx_buff_enq(data,size,true);
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //зарегистрировать функцию обратного вызова на приём данных
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR UART_RegisterReceivedFunction(uart_received_func_ptr_t func_ptr)
                                                    {
                                                     ReceivedFunctionPtr=func_ptr;
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //зарегистрировать функцию обратного вызова на завершение передачи данных
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR UART_RegisterSendDoneFunction(uart_send_done_func_ptr_t func_ptr)
                                                    {
                                                     SendDoneFunctionPtr=func_ptr;
                                                    }
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //обработчик очереди сообщений
                                                    //----------------------------------------------------------------------------------------------------
                                                    static void ICACHE_FLASH_ATTR UART_EventQueueFunction(os_event_t *events)
                                                    {
                                                     if (events==NULL) return;
                                                     if (events->sig==EVENT_UART_RECEIVED_DATA)
                                                     {
                                                      size_t data_length=rx_buff_get_data_len();
                                                      if (data_length>UART_RECEIVED_BUFFER_SIZE) data_length=UART_RECEIVED_BUFFER_SIZE;
                                                      rx_buff_deq(ReceivedBuffer,data_length);//получаем данные
                                                      if (ReceivedFunctionPtr!=NULL) ReceivedFunctionPtr(ReceivedBuffer,data_length);
                                                      return;
                                                     }
                                                     if (events->sig==EVENT_UART_SEND_DONE)
                                                     {
                                                      if (SendDoneFunctionPtr!=NULL) SendDoneFunctionPtr();
                                                      return;
                                                     }
                                                    }
                                                    



                                                    led.h
                                                    #ifndef LED_H
                                                    #define LED_H
                                                    
                                                    //****************************************************************************************************
                                                    //подключаемые библиотеки
                                                    //****************************************************************************************************
                                                    #include <os_type.h>
                                                    
                                                    //****************************************************************************************************
                                                    //макроопределения
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //константы
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //глобальные переменные
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //прототипы функций
                                                    //****************************************************************************************************
                                                    
                                                    void ICACHE_FLASH_ATTR LED_Init(void);//инициализация
                                                    void ICACHE_FLASH_ATTR LED_On(void);//включить светодиод
                                                    void ICACHE_FLASH_ATTR LED_Off(void);//отключить светодиод
                                                    
                                                    #endif
                                                    



                                                    led.c
                                                    //****************************************************************************************************
                                                    //подключаемые библиотеки
                                                    //****************************************************************************************************
                                                    #include "led.h"
                                                    #include <gpio.h>
                                                    
                                                    //****************************************************************************************************
                                                    //макроопределения
                                                    //****************************************************************************************************
                                                    
                                                    #define LED_GPIO 2
                                                    #define LED_GPIO_MUX PERIPHS_IO_MUX_GPIO2_U
                                                    #define LED_GPIO_FUNC FUNC_GPIO2
                                                    
                                                    //****************************************************************************************************
                                                    //константы
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //глобальные переменные
                                                    //****************************************************************************************************
                                                    
                                                    //****************************************************************************************************
                                                    //реализация функций
                                                    //****************************************************************************************************
                                                    
                                                    //----------------------------------------------------------------------------------------------------
                                                    //инициализация
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR LED_Init(void)
                                                    {
                                                     gpio_init();
                                                     PIN_FUNC_SELECT(LED_GPIO_MUX,LED_GPIO_FUNC);
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //включить светодиод
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR LED_On(void)
                                                    {
                                                     GPIO_OUTPUT_SET(LED_GPIO,0);
                                                    }
                                                    //----------------------------------------------------------------------------------------------------
                                                    //отключить светодиод
                                                    //----------------------------------------------------------------------------------------------------
                                                    void ICACHE_FLASH_ATTR LED_Off(void)
                                                    {
                                                     GPIO_OUTPUT_SET(LED_GPIO,1);
                                                    }
                                                    

                                                      0
                                                      Дальше не мои файлы: эти файлы взяты из директорий с примерами в папках drivers. Но файлы uart.h и uart.c я переименовал в drvuart, так как мой файл тоже называется uart, а его название я менять не захотел.

                                                      drvuart_register.h
                                                      /******************************************************************************
                                                       * Copyright 2013-2014 Espressif Systems
                                                       *
                                                       * FileName: uart_register.h
                                                       *
                                                       * Description: entry file of user application
                                                       *
                                                       * Modification history:
                                                       *     2015/9/24, v1.0 create this file.
                                                      *******************************************************************************/
                                                      
                                                      #ifndef UART_REGISTER_H_
                                                      #define UART_REGISTER_H_
                                                      
                                                      #define REG_UART_BASE(i)                (0x60000000ul + (i)*0xf00)
                                                      //version value:32'h062000
                                                      
                                                      #define UART_FIFO(i)                    (REG_UART_BASE(i) + 0x0)
                                                      #define UART_RXFIFO_RD_BYTE                 0x000000FF
                                                      #define UART_RXFIFO_RD_BYTE_S               0
                                                      
                                                      #define UART_INT_RAW(i)                 (REG_UART_BASE(i) + 0x4)
                                                      #define UART_RXFIFO_TOUT_INT_RAW            (BIT(8))
                                                      #define UART_BRK_DET_INT_RAW                (BIT(7))
                                                      #define UART_CTS_CHG_INT_RAW                (BIT(6))
                                                      #define UART_DSR_CHG_INT_RAW                (BIT(5))
                                                      #define UART_RXFIFO_OVF_INT_RAW             (BIT(4))
                                                      #define UART_FRM_ERR_INT_RAW                (BIT(3))
                                                      #define UART_PARITY_ERR_INT_RAW             (BIT(2))
                                                      #define UART_TXFIFO_EMPTY_INT_RAW           (BIT(1))
                                                      #define UART_RXFIFO_FULL_INT_RAW            (BIT(0))
                                                      
                                                      #define UART_INT_ST(i)                  (REG_UART_BASE(i) + 0x8)
                                                      #define UART_RXFIFO_TOUT_INT_ST             (BIT(8))
                                                      #define UART_BRK_DET_INT_ST                 (BIT(7))
                                                      #define UART_CTS_CHG_INT_ST                 (BIT(6))
                                                      #define UART_DSR_CHG_INT_ST                 (BIT(5))
                                                      #define UART_RXFIFO_OVF_INT_ST              (BIT(4))
                                                      #define UART_FRM_ERR_INT_ST                 (BIT(3))
                                                      #define UART_PARITY_ERR_INT_ST              (BIT(2))
                                                      #define UART_TXFIFO_EMPTY_INT_ST            (BIT(1))
                                                      #define UART_RXFIFO_FULL_INT_ST             (BIT(0))
                                                      
                                                      #define UART_INT_ENA(i)                 (REG_UART_BASE(i) + 0xC)
                                                      #define UART_RXFIFO_TOUT_INT_ENA            (BIT(8))
                                                      #define UART_BRK_DET_INT_ENA                (BIT(7))
                                                      #define UART_CTS_CHG_INT_ENA                (BIT(6))
                                                      #define UART_DSR_CHG_INT_ENA                (BIT(5))
                                                      #define UART_RXFIFO_OVF_INT_ENA             (BIT(4))
                                                      #define UART_FRM_ERR_INT_ENA                (BIT(3))
                                                      #define UART_PARITY_ERR_INT_ENA             (BIT(2))
                                                      #define UART_TXFIFO_EMPTY_INT_ENA           (BIT(1))
                                                      #define UART_RXFIFO_FULL_INT_ENA            (BIT(0))
                                                      
                                                      #define UART_INT_CLR(i)                 (REG_UART_BASE(i) + 0x10)
                                                      #define UART_RXFIFO_TOUT_INT_CLR            (BIT(8))
                                                      #define UART_BRK_DET_INT_CLR                (BIT(7))
                                                      #define UART_CTS_CHG_INT_CLR                (BIT(6))
                                                      #define UART_DSR_CHG_INT_CLR                (BIT(5))
                                                      #define UART_RXFIFO_OVF_INT_CLR             (BIT(4))
                                                      #define UART_FRM_ERR_INT_CLR                (BIT(3))
                                                      #define UART_PARITY_ERR_INT_CLR             (BIT(2))
                                                      #define UART_TXFIFO_EMPTY_INT_CLR           (BIT(1))
                                                      #define UART_RXFIFO_FULL_INT_CLR            (BIT(0))
                                                      
                                                      #define UART_CLKDIV(i)                  (REG_UART_BASE(i) + 0x14)
                                                      #define UART_CLKDIV_CNT                     0x000FFFFF
                                                      #define UART_CLKDIV_S                       0
                                                      
                                                      #define UART_AUTOBAUD(i)                (REG_UART_BASE(i) + 0x18)
                                                      #define UART_GLITCH_FILT                    0x000000FF
                                                      #define UART_GLITCH_FILT_S                  8
                                                      #define UART_AUTOBAUD_EN                    (BIT(0))
                                                      
                                                      #define UART_STATUS(i)                  (REG_UART_BASE(i) + 0x1C)
                                                      #define UART_TXD                            (BIT(31))
                                                      #define UART_RTSN                           (BIT(30))
                                                      #define UART_DTRN                           (BIT(29))
                                                      #define UART_TXFIFO_CNT                     0x000000FF
                                                      #define UART_TXFIFO_CNT_S                   16
                                                      #define UART_RXD                            (BIT(15))
                                                      #define UART_CTSN                           (BIT(14))
                                                      #define UART_DSRN                           (BIT(13))
                                                      #define UART_RXFIFO_CNT                     0x000000FF
                                                      #define UART_RXFIFO_CNT_S                   0
                                                      
                                                      #define UART_CONF0(i)                   (REG_UART_BASE(i) + 0x20)
                                                      #define UART_DTR_INV                        (BIT(24))
                                                      #define UART_RTS_INV                        (BIT(23))
                                                      #define UART_TXD_INV                        (BIT(22))
                                                      #define UART_DSR_INV                        (BIT(21))
                                                      #define UART_CTS_INV                        (BIT(20))
                                                      #define UART_RXD_INV                        (BIT(19))
                                                      #define UART_TXFIFO_RST                     (BIT(18))
                                                      #define UART_RXFIFO_RST                     (BIT(17))
                                                      #define UART_IRDA_EN                        (BIT(16))
                                                      #define UART_TX_FLOW_EN                     (BIT(15))
                                                      #define UART_LOOPBACK                       (BIT(14))
                                                      #define UART_IRDA_RX_INV                    (BIT(13))
                                                      #define UART_IRDA_TX_INV                    (BIT(12))
                                                      #define UART_IRDA_WCTL                      (BIT(11))
                                                      #define UART_IRDA_TX_EN                     (BIT(10))
                                                      #define UART_IRDA_DPLX                      (BIT(9))
                                                      #define UART_TXD_BRK                        (BIT(8))
                                                      #define UART_SW_DTR                         (BIT(7))
                                                      #define UART_SW_RTS                         (BIT(6))
                                                      #define UART_STOP_BIT_NUM                   0x00000003
                                                      #define UART_STOP_BIT_NUM_S                 4
                                                      #define UART_BIT_NUM                        0x00000003
                                                      #define UART_BIT_NUM_S                      2
                                                      #define UART_PARITY_EN                      (BIT(1))
                                                      #define UART_PARITY                         (BIT(0))
                                                      
                                                      #define UART_CONF1(i)                   (REG_UART_BASE(i) + 0x24)
                                                      #define UART_RX_TOUT_EN                     (BIT(31))
                                                      #define UART_RX_TOUT_THRHD                  0x0000007F
                                                      #define UART_RX_TOUT_THRHD_S                24
                                                      #define UART_RX_FLOW_EN                     (BIT(23))
                                                      #define UART_RX_FLOW_THRHD                  0x0000007F
                                                      #define UART_RX_FLOW_THRHD_S                16
                                                      #define UART_TXFIFO_EMPTY_THRHD             0x0000007F
                                                      #define UART_TXFIFO_EMPTY_THRHD_S           8
                                                      #define UART_RXFIFO_FULL_THRHD              0x0000007F
                                                      #define UART_RXFIFO_FULL_THRHD_S            0
                                                      
                                                      #define UART_LOWPULSE(i)                (REG_UART_BASE(i) + 0x28)
                                                      #define UART_LOWPULSE_MIN_CNT               0x000FFFFF
                                                      #define UART_LOWPULSE_MIN_CNT_S             0
                                                      
                                                      #define UART_HIGHPULSE(i)               (REG_UART_BASE(i) + 0x2C)
                                                      #define UART_HIGHPULSE_MIN_CNT              0x000FFFFF
                                                      #define UART_HIGHPULSE_MIN_CNT_S            0
                                                      
                                                      #define UART_PULSE_NUM(i)               (REG_UART_BASE(i) + 0x30)
                                                      #define UART_PULSE_NUM_CNT                  0x0003FF
                                                      #define UART_PULSE_NUM_CNT_S                0
                                                      
                                                      #define UART_DATE(i)                    (REG_UART_BASE(i) + 0x78)
                                                      #define UART_ID(i)                      (REG_UART_BASE(i) + 0x7C)
                                                      
                                                      #endif // UART_REGISTER_H_INCLUDED
                                                      



                                                      drvuart.h
                                                      /******************************************************************************
                                                       * Copyright 2013-2014 Espressif Systems
                                                       *
                                                       * FileName: uart.h
                                                       *
                                                       * Description: entry file of user application
                                                       *
                                                       * Modification history:
                                                       *     2015/9/24, v1.0 create this file.
                                                      *******************************************************************************/
                                                      #ifndef UART_APP_H
                                                      #define UART_APP_H
                                                      
                                                      #include "drvuart_register.h"
                                                      #include <eagle_soc.h>
                                                      #include <c_types.h>
                                                      
                                                      #define UART0   0
                                                      #define UART1   1
                                                      
                                                      typedef enum {
                                                          FIVE_BITS = 0x0,
                                                          SIX_BITS = 0x1,
                                                          SEVEN_BITS = 0x2,
                                                          EIGHT_BITS = 0x3
                                                      } UartBitsNum4Char;
                                                      
                                                      typedef enum {
                                                          ONE_STOP_BIT             = 0x1,
                                                          ONE_HALF_STOP_BIT        = 0x2,
                                                          TWO_STOP_BIT             = 0x3
                                                      } UartStopBitsNum;
                                                      
                                                      typedef enum {
                                                          NONE_BITS = 0x2,
                                                          ODD_BITS   = 1,
                                                          EVEN_BITS = 0
                                                      } UartParityMode;
                                                      
                                                      typedef enum {
                                                          STICK_PARITY_DIS   = 0,
                                                          STICK_PARITY_EN    = 1
                                                      } UartExistParity;
                                                      
                                                      typedef enum {
                                                          UART_None_Inverse = 0x0,
                                                          UART_Rxd_Inverse = UART_RXD_INV,
                                                          UART_CTS_Inverse = UART_CTS_INV,
                                                          UART_Txd_Inverse = UART_TXD_INV,
                                                          UART_RTS_Inverse = UART_RTS_INV,
                                                      } UART_LineLevelInverse;
                                                      
                                                      
                                                      typedef enum {
                                                          BIT_RATE_300 = 300,
                                                          BIT_RATE_600 = 600,
                                                          BIT_RATE_1200 = 1200,
                                                          BIT_RATE_2400 = 2400,
                                                          BIT_RATE_4800 = 4800,
                                                          BIT_RATE_9600   = 9600,
                                                          BIT_RATE_19200  = 19200,
                                                          BIT_RATE_38400  = 38400,
                                                          BIT_RATE_57600  = 57600,
                                                          BIT_RATE_74880  = 74880,
                                                          BIT_RATE_115200 = 115200,
                                                          BIT_RATE_230400 = 230400,
                                                          BIT_RATE_460800 = 460800,
                                                          BIT_RATE_921600 = 921600,
                                                          BIT_RATE_1843200 = 1843200,
                                                          BIT_RATE_3686400 = 3686400,
                                                      } UartBautRate;
                                                      
                                                      typedef enum {
                                                          NONE_CTRL,
                                                          HARDWARE_CTRL,
                                                          XON_XOFF_CTRL
                                                      } UartFlowCtrl;
                                                      
                                                      typedef enum {
                                                          USART_HardwareFlowControl_None = 0x0,
                                                          USART_HardwareFlowControl_RTS = 0x1,
                                                          USART_HardwareFlowControl_CTS = 0x2,
                                                          USART_HardwareFlowControl_CTS_RTS = 0x3
                                                      } UART_HwFlowCtrl;
                                                      
                                                      typedef enum {
                                                          EMPTY,
                                                          UNDER_WRITE,
                                                          WRITE_OVER
                                                      } RcvMsgBuffState;
                                                      
                                                      typedef struct {
                                                          uint32     RcvBuffSize;
                                                          uint8     *pRcvMsgBuff;
                                                          uint8     *pWritePos;
                                                          uint8     *pReadPos;
                                                          uint8      TrigLvl; //JLU: may need to pad
                                                          RcvMsgBuffState  BuffState;
                                                      } RcvMsgBuff;
                                                      
                                                      typedef struct {
                                                          uint32   TrxBuffSize;
                                                          uint8   *pTrxBuff;
                                                      } TrxMsgBuff;
                                                      
                                                      typedef enum {
                                                          BAUD_RATE_DET,
                                                          WAIT_SYNC_FRM,
                                                          SRCH_MSG_HEAD,
                                                          RCV_MSG_BODY,
                                                          RCV_ESC_CHAR,
                                                      } RcvMsgState;
                                                      
                                                      typedef struct {
                                                          UartBautRate 	     baut_rate;
                                                          UartBitsNum4Char  data_bits;
                                                          UartExistParity      exist_parity;
                                                          UartParityMode 	    parity;    // chip size in byte
                                                          UartStopBitsNum   stop_bits;
                                                          UartFlowCtrl         flow_ctrl;
                                                          RcvMsgBuff          rcv_buff;
                                                          TrxMsgBuff           trx_buff;
                                                          RcvMsgState        rcv_state;
                                                          int                      received;
                                                          int                      buff_uart_no;  //indicate which uart use tx/rx buffer
                                                      } UartDevice;
                                                      
                                                      void uart_init(UartBautRate uart0_br);
                                                      ///////////////////////////////////////
                                                      #define UART_FIFO_LEN  128  //define the tx fifo length
                                                      //
                                                      #define UART_TX_EMPTY_THRESH_VAL 0x10
                                                      #define UART_TX_BUFFER_SIZE 8192
                                                      #define URAT_TX_LOWER_SIZE 1024*7
                                                      #define URAT_TX_UPPER_SIZE  8000*1
                                                      
                                                      #define UART_RX_BUFFER_SIZE  (10*1024) //201
                                                      
                                                      //#define UART_TX_DEBUG
                                                      #define DBG(...)
                                                      #define DBG1(...) //uart1_sendStr_no_wait
                                                      #define DBG2(...) //os_printf
                                                      
                                                      struct UartBuffer {
                                                          uint16     UartBuffSize;
                                                          uint8     *pUartBuff;
                                                          uint8     *pInPos;
                                                          uint8     *pOutPos;
                                                          STATUS  BuffState;
                                                          uint16    Space;  //remanent space of the buffer
                                                      //    uint8  TcpControl;
                                                          struct  UartBuffer       *nextBuff;
                                                      };
                                                      
                                                      struct UartRxBuff {
                                                          uint32     UartRxBuffSize;
                                                          uint8     *pUartRxBuff;
                                                          uint8     *pWritePos;
                                                          uint8     *pReadPos;
                                                          STATUS RxBuffState;
                                                          uint32    Space;  //remanent space of the buffer
                                                      } ;
                                                      
                                                      void tx_buff_enq(const char *pdata, uint16 data_len, bool force);
                                                      uint16 rx_buff_deq(char *pdata, uint16 data_len);
                                                      //==============================================
                                                      #define FUNC_UART0_CTS 4
                                                      #define UART_LINE_INV_MASK  (0x3f<<19)
                                                      
                                                      #endif
                                                      



                                                      drvuart.c
                                                      /******************************************************************************
                                                       * Copyright 2013-2014 Espressif Systems
                                                       *
                                                       * FileName: uart.c
                                                       *
                                                       * Description: entry file of user application
                                                       *
                                                       * Modification history:
                                                       *     2015/9/24, v1.0 create this file.
                                                      *******************************************************************************/
                                                      #include <ets_sys.h>
                                                      #include <osapi.h>
                                                      #include <mem.h>
                                                      #include "drvuart.h"
                                                      #include "drvuart_register.h"
                                                      #include "user_interface.h"
                                                      
                                                      #include <espconn.h>
                                                      
                                                      #define FUNC_U1TXD_BK 2
                                                      #define FUNC_U0CTS 4
                                                      extern UartDevice UartDev;
                                                      
                                                      static struct UartBuffer *pTxBuffer = NULL;
                                                      static struct UartBuffer *pRxBuffer = NULL;
                                                      
                                                      LOCAL void uart0_rx_intr_handler(void *para);
                                                      
                                                      void uart1_sendStr_no_wait(const char *str);
                                                      struct UartBuffer * Uart_Buf_Init(uint32 buf_size);
                                                      void rx_buff_enq(void);
                                                      void tx_start_uart_buffer(uint8 uart_no);
                                                      void uart_rx_intr_disable(uint8 uart_no);
                                                      void uart_rx_intr_enable(uint8 uart_no);
                                                      /******************************************************************************
                                                       * FunctionName : uart_config
                                                       * Description  : Internal used function
                                                       *                UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled
                                                       *                UART1 just used for debug output
                                                       * Parameters   : uart_no, use UART0 or UART1 defined ahead
                                                       * Returns      : NONE
                                                       *******************************************************************************/
                                                      LOCAL void ICACHE_FLASH_ATTR
                                                      uart_config(uint8 uart_no)
                                                      {
                                                          if (uart_no == UART1) {
                                                              PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
                                                          } else {
                                                              /* rcv_buff size if 0x100 */
                                                              ETS_UART_INTR_ATTACH(uart0_rx_intr_handler,  &(UartDev.rcv_buff));
                                                              PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
                                                              PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
                                                              PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS);
                                                              PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_U0CTS);
                                                              PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTCK_U);
                                                          }
                                                      
                                                          uart_div_modify(uart_no, (uint16)(UART_CLK_FREQ / (uint32)(UartDev.baut_rate)));
                                                      
                                                          WRITE_PERI_REG(UART_CONF0(uart_no), (uint32)UartDev.exist_parity
                                                                  | (uint32)UartDev.parity
                                                                  | (((uint8)UartDev.stop_bits) << UART_STOP_BIT_NUM_S)
                                                                  | (((uint8)UartDev.data_bits) << UART_BIT_NUM_S));
                                                      
                                                          //clear rx and tx fifo,not ready
                                                          SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
                                                          CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
                                                      
                                                          if (uart_no == UART0) {
                                                              //set rx fifo trigger
                                                              WRITE_PERI_REG(UART_CONF1(uart_no),
                                                                      ((100 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) |
                                                                      ((110 & UART_RX_FLOW_THRHD) << UART_RX_FLOW_THRHD_S) |
                                                                      UART_RX_FLOW_EN |
                                                                      (0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S |
                                                                      UART_RX_TOUT_EN |
                                                      
                                                                      ((0x10 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S)); //wjl
                                                                      //SET_PERI_REG_MASK( UART_CONF0(uart_no),UART_TX_FLOW_EN);  //add this sentense to add a tx flow control via MTCK( CTS )
                                                      
                                                              SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_TOUT_INT_ENA |
                                                                      UART_FRM_ERR_INT_ENA);
                                                          } else {
                                                              WRITE_PERI_REG(UART_CONF1(uart_no),
                                                                      ((UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S));
                                                          }
                                                      
                                                          //clear all interrupt
                                                          WRITE_PERI_REG(UART_INT_CLR(uart_no), 0xffff);
                                                          //enable rx_interrupt
                                                          SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_OVF_INT_ENA);
                                                      }
                                                      
                                                      /******************************************************************************
                                                       * FunctionName : uart1_tx_one_char
                                                       * Description  : Internal used function
                                                       *                Use uart1 interface to transfer one char
                                                       * Parameters   : uint8 TxChar - character to tx
                                                       * Returns      : OK
                                                       *******************************************************************************/
                                                      STATUS
                                                      uart_tx_one_char(uint8 uart, uint8 TxChar)
                                                      {
                                                          for(;;) {
                                                              uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S);
                                                      
                                                              if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) {
                                                                  break;
                                                              }
                                                          }
                                                      
                                                          WRITE_PERI_REG(UART_FIFO(uart) , TxChar);
                                                          return OK;
                                                      }
                                                      
                                                      /******************************************************************************
                                                       * FunctionName : uart1_write_char
                                                       * Description  : Internal used function
                                                       *                Do some special deal while tx char is '\r' or '\n'
                                                       * Parameters   : char c - character to tx
                                                       * Returns      : NONE
                                                       *******************************************************************************/
                                                      LOCAL void ICACHE_FLASH_ATTR
                                                      uart1_write_char(char c)
                                                      {
                                                          if (c == '\n') {
                                                              (void)uart_tx_one_char(UART1, '\r');
                                                              (void)uart_tx_one_char(UART1, '\n');
                                                          } else if (c == '\r') {
                                                          } else {
                                                              (void)uart_tx_one_char(UART1, (uint8)c);
                                                          }
                                                      }
                                                      
                                                      /******************************************************************************
                                                       * FunctionName : uart0_rx_intr_handler
                                                       * Description  : Internal used function
                                                       *                UART0 interrupt handler, add self handle code inside
                                                       * Parameters   : void *para - point to ETS_UART_INTR_ATTACH's arg
                                                       * Returns      : NONE
                                                       *******************************************************************************/
                                                      LOCAL void
                                                      uart0_rx_intr_handler(void *para)
                                                      {
                                                          uint8 uart_no = UART0;
                                                          if (UART_FRM_ERR_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_FRM_ERR_INT_ST)) {
                                                              uart1_sendStr_no_wait("FRM_ERR\r\n");
                                                              WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR);
                                                          }
                                                      
                                                          if (UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)) {
                                                              uart_rx_intr_disable(uart_no);
                                                              rx_buff_enq();
                                                          } else if (UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST)) {
                                                              uart_rx_intr_disable(uart_no);
                                                              rx_buff_enq();
                                                          } else if (UART_TXFIFO_EMPTY_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_TXFIFO_EMPTY_INT_ST)) {
                                                              CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_TXFIFO_EMPTY_INT_ENA);
                                                              tx_start_uart_buffer(uart_no);
                                                              WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_TXFIFO_EMPTY_INT_CLR);
                                                         } else if (UART_RXFIFO_OVF_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_OVF_INT_ST)) {
                                                              uart1_sendStr_no_wait("RX OVF!\r\n");
                                                              WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_OVF_INT_CLR);
                                                          }
                                                      }
                                                      
                                                      /******************************************************************************
                                                       * FunctionName : uart_init
                                                       * Description  : user interface for init uart
                                                       * Parameters   : UartBautRate uart0_br - uart0 bautrate
                                                       *                UartBautRate uart1_br - uart1 bautrate
                                                       * Returns      : NONE
                                                       *******************************************************************************/
                                                      void ICACHE_FLASH_ATTR
                                                      uart_init(UartBautRate uart0_br)
                                                      {
                                                          UartDev.baut_rate = uart0_br;
                                                          uart_config(UART0);
                                                          UartDev.baut_rate = BIT_RATE_115200;
                                                          uart_config(UART1);
                                                          ETS_UART_INTR_ENABLE();
                                                      
                                                          os_install_putc1(uart1_write_char);
                                                      
                                                          pTxBuffer = Uart_Buf_Init(UART_TX_BUFFER_SIZE);
                                                          pRxBuffer = Uart_Buf_Init(UART_RX_BUFFER_SIZE);
                                                      }
                                                      
                                                      /******************************************************************************
                                                       * FunctionName : uart_tx_one_char_no_wait
                                                       * Description  : uart tx a single char without waiting for fifo
                                                       * Parameters   : uint8 uart - uart port
                                                       *                uint8 TxChar - char to tx
                                                       * Returns      : STATUS
                                                       *******************************************************************************/
                                                      STATUS
                                                      uart_tx_one_char_no_wait(uint8 uart, uint8 TxChar)
                                                      {
                                                          uint8 fifo_cnt = ((READ_PERI_REG(UART_STATUS(uart)) >> UART_TXFIFO_CNT_S)& UART_TXFIFO_CNT);
                                                      
                                                          if (fifo_cnt < 126) {
                                                              WRITE_PERI_REG(UART_FIFO(uart) , TxChar);
                                                          }
                                                      
                                                          return OK;
                                                      }
                                                      
                                                      /******************************************************************************
                                                       * FunctionName : uart1_sendStr_no_wait
                                                       * Description  : uart tx a string without waiting for every char, used for print debug info which can be lost
                                                       * Parameters   : const char *str - string to be sent
                                                       * Returns      : NONE
                                                       *******************************************************************************/
                                                      void
                                                      uart1_sendStr_no_wait(const char *str)
                                                      {
                                                          if(str == NULL) {
                                                              return;
                                                          }
                                                      
                                                          while (*str) {
                                                              (void)uart_tx_one_char_no_wait(UART1, (uint8)*str);
                                                              str++;
                                                          }
                                                      }
                                                      /******************************************************************************
                                                       * FunctionName : Uart_Buf_Init
                                                       * Description  : tx buffer enqueue: fill a first linked buffer
                                                       * Parameters   : char *pdata - data point  to be enqueue
                                                       * Returns      : NONE
                                                       *******************************************************************************/
                                                      struct UartBuffer *ICACHE_FLASH_ATTR
                                                      Uart_Buf_Init(uint32 buf_size)
                                                      {
                                                          uint32 heap_size = system_get_free_heap_size();
                                                      
                                                          if(buf_size > 65535) { // now not support
                                                              DBG1("no buf for uart\n\r");
                                                              return NULL;
                                                          }
                                                          if (heap_size <= buf_size) {
                                                              DBG1("no buf for uart\n\r");
                                                              return NULL;
                                                          } else {
                                                              DBG("test heap size: %d\n\r", heap_size);
                                                              struct UartBuffer *pBuff = (struct UartBuffer *)os_malloc((uint32)sizeof(struct UartBuffer));
                                                              pBuff->UartBuffSize = (uint16)buf_size; // THIS OK
                                                              pBuff->pUartBuff = (uint8 *)os_malloc(pBuff->UartBuffSize);
                                                              pBuff->pInPos = pBuff->pUartBuff;
                                                              pBuff->pOutPos = pBuff->pUartBuff;
                                                              pBuff->Space = pBuff->UartBuffSize;
                                                              pBuff->BuffState = OK;
                                                              pBuff->nextBuff = NULL;
                                                              //        pBuff->TcpControl = RUN;
                                                              return pBuff;
                                                          }
                                                      }
                                                      
                                                      LOCAL void
                                                      Uart_Buf_Cpy(struct UartBuffer *pCur, const char *pdata , uint16 data_len)
                                                      {
                                                          if ((pCur == NULL) || (pdata == NULL) || (data_len == 0)) {
                                                              return ;
                                                          }
                                                      
                                                          uint16 tail_len = (uint16)(pCur->pUartBuff + pCur->UartBuffSize - pCur->pInPos); // THIS OK
                                                      
                                                          if (tail_len >= data_len) { //do not need to loop back  the queue
                                                              os_memcpy(pCur->pInPos , pdata , data_len);
                                                              pCur->pInPos += (data_len);
                                                              pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
                                                              pCur->Space -= data_len;
                                                          } else {
                                                              os_memcpy(pCur->pInPos, pdata, tail_len);
                                                              pCur->pInPos += (tail_len);
                                                              pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
                                                              pCur->Space -= tail_len;
                                                              os_memcpy(pCur->pInPos, pdata + tail_len , data_len - tail_len);
                                                              pCur->pInPos += (data_len - tail_len);
                                                              pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
                                                              pCur->Space -= (data_len - tail_len);
                                                          }
                                                      
                                                      }
                                                      
                                                      /******************************************************************************
                                                       * FunctionName : uart_buf_free
                                                       * Description  : deinit of the tx buffer
                                                       * Parameters   : struct UartBuffer* pTxBuff - tx buffer struct pointer
                                                       * Returns      : NONE
                                                       *******************************************************************************/
                                                      void ICACHE_FLASH_ATTR
                                                      uart_buf_free(struct UartBuffer *pBuff)
                                                      {
                                                          if(pBuff != NULL) {
                                                              if(pBuff->pUartBuff != NULL) {
                                                                  os_free(pBuff->pUartBuff);
                                                              }
                                                              os_free(pBuff);
                                                          }
                                                      }
                                                      
                                                      uint16 ICACHE_FLASH_ATTR
                                                      rx_buff_get_data_len(void)
                                                      {
                                                      	uint16 buf_len = pRxBuffer->UartBuffSize - pRxBuffer->Space;
                                                      	return buf_len;
                                                      }
                                                      
                                                      uint16 ICACHE_FLASH_ATTR
                                                      rx_buff_deq(char *pdata, uint16 data_len)
                                                      {
                                                          uint16 len_tmp = 0;
                                                      
                                                          uint16 buf_len = pRxBuffer->UartBuffSize - pRxBuffer->Space;
                                                          uint16 tail_len = (uint16)(pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize - pRxBuffer->pOutPos); // THIS OK
                                                          len_tmp = ((data_len > buf_len) ? buf_len : data_len);
                                                      
                                                          if (pRxBuffer->pOutPos <= pRxBuffer->pInPos) {
                                                              if(pdata != NULL) {
                                                                  os_memcpy(pdata, pRxBuffer->pOutPos, len_tmp);
                                                              }
                                                              pRxBuffer->pOutPos += len_tmp;
                                                              pRxBuffer->Space += len_tmp;
                                                          } else {
                                                              if (len_tmp > tail_len) {
                                                                  if(pdata != NULL) {
                                                                      os_memcpy(pdata, pRxBuffer->pOutPos, tail_len);
                                                                  }
                                                                  pRxBuffer->pOutPos += tail_len;
                                                                  pRxBuffer->pOutPos = (pRxBuffer->pUartBuff + (pRxBuffer->pOutPos - pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize);
                                                                  pRxBuffer->Space += tail_len;
                                                      
                                                                  if(pdata != NULL) {
                                                                      os_memcpy(pdata + tail_len , pRxBuffer->pOutPos, len_tmp - tail_len);
                                                                  }
                                                                  pRxBuffer->pOutPos += (len_tmp - tail_len);
                                                                  pRxBuffer->pOutPos = (pRxBuffer->pUartBuff + (pRxBuffer->pOutPos - pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize);
                                                                  pRxBuffer->Space += (len_tmp - tail_len);
                                                              } else {
                                                                  if(pdata != NULL) {
                                                                      os_memcpy(pdata, pRxBuffer->pOutPos, len_tmp);
                                                                  }
                                                                  pRxBuffer->pOutPos += len_tmp;
                                                                  pRxBuffer->pOutPos = (pRxBuffer->pUartBuff + (pRxBuffer->pOutPos - pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize);
                                                                  pRxBuffer->Space += len_tmp;
                                                              }
                                                          }
                                                      
                                                          // os_printf("recv:%d\r\n",pRxBuffer->Space);
                                                          return len_tmp;
                                                      }
                                                      
                                                      void
                                                      rx_buff_enq(void)
                                                      {
                                                          uint8 fifo_len = 0, buf_idx = 0,loop;
                                                          ETSParam par = 0;
                                                          uint8 fifo_data;
                                                          uint8* tail = (pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize);
                                                          fifo_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
                                                      
                                                          if(fifo_len > pRxBuffer->Space) {
                                                              buf_idx = pRxBuffer->Space;
                                                          } else {
                                                              buf_idx = fifo_len;
                                                          }
                                                      
                                                          loop = buf_idx;
                                                          while (loop--) {
                                                              *(pRxBuffer->pInPos++) = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
                                                              if (pRxBuffer->pInPos == tail) {
                                                                  pRxBuffer->pInPos = pRxBuffer->pUartBuff;
                                                              }
                                                          }
                                                      
                                                          fifo_len -= buf_idx;
                                                      
                                                          while (fifo_len--) {
                                                              fifo_data = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; // discard data
                                                          }
                                                      
                                                          pRxBuffer->Space -= buf_idx ;
                                                          par = buf_idx;
                                                          if(system_os_post(EVENT_QUEUE_ID,(ETSSignal)EVENT_UART_RECEIVED_DATA, par) != TRUE) {
                                                              os_printf("post fail!!!\n\r");
                                                          }
                                                          WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR);
                                                          uart_rx_intr_enable(UART0);
                                                      }
                                                      
                                                      void ICACHE_FLASH_ATTR
                                                      tx_buff_enq(const char *pdata, uint16 data_len, bool force)
                                                      {
                                                          CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
                                                          //DBG2("len:%d\n\r",data_len);
                                                          if(pdata != NULL) {
                                                              if (pTxBuffer == NULL) {
                                                                  DBG1("\n\rnull, create buffer struct\n\r");
                                                                  pTxBuffer = Uart_Buf_Init(UART_TX_BUFFER_SIZE);
                                                      
                                                                  if (pTxBuffer != NULL) {
                                                                      Uart_Buf_Cpy(pTxBuffer ,  pdata,  data_len);
                                                                  } else {
                                                                      DBG1("uart tx MALLOC no buf \n\r");
                                                                  }
                                                              } else {
                                                                  if (data_len <= pTxBuffer->Space) {
                                                                      Uart_Buf_Cpy(pTxBuffer ,  pdata,  data_len);
                                                                  } else if (force) {
                                                                      for(;;) {
                                                                          tx_start_uart_buffer(UART0);
                                                                          CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
                                                                          if (data_len <= pTxBuffer->Space) {
                                                                              Uart_Buf_Cpy(pTxBuffer ,  pdata,  data_len);
                                                                              break;
                                                                          }
                                                                          ets_delay_us(70);
                                                                          WRITE_PERI_REG(0X60000914, 0x73);
                                                                      };
                                                                  } else {
                                                                      DBG1("UART TX BUF FULL!!!!\n\r");
                                                                  }
                                                              }
                                                      
                                                              if ((pTxBuffer != NULL) && (pTxBuffer->Space <= URAT_TX_LOWER_SIZE))
                                                              {
                                                              //set_tcp_block();
                                                              }
                                                          }
                                                      
                                                          SET_PERI_REG_MASK(UART_CONF1(UART0), (UART_TX_EMPTY_THRESH_VAL & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S);
                                                          SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
                                                      }
                                                      
                                                      //--------------------------------
                                                      LOCAL void tx_fifo_insert(struct UartBuffer *pTxBuff, uint8 data_len,  uint8 uart_no)
                                                      {
                                                          uint8 i;
                                                      
                                                          if(pTxBuff == NULL) {
                                                              return;
                                                          }
                                                      
                                                          for (i = 0; i < data_len; i++) {
                                                              WRITE_PERI_REG(UART_FIFO(uart_no) , *(pTxBuff->pOutPos++));
                                                      
                                                              if (pTxBuff->pOutPos == (pTxBuff->pUartBuff + pTxBuff->UartBuffSize)) {
                                                                  pTxBuff->pOutPos = pTxBuff->pUartBuff;
                                                              }
                                                          }
                                                      
                                                          pTxBuff->pOutPos = (pTxBuff->pUartBuff + (pTxBuff->pOutPos - pTxBuff->pUartBuff) % pTxBuff->UartBuffSize);
                                                          pTxBuff->Space += data_len;
                                                      }
                                                      
                                                      /******************************************************************************
                                                       * FunctionName : tx_start_uart_buffer
                                                       * Description  : get data from the tx buffer and fill the uart tx fifo, co-work with the uart fifo empty interrupt
                                                       * Parameters   : uint8 uart_no - uart port num
                                                       * Returns      : NONE
                                                       *******************************************************************************/
                                                      void tx_start_uart_buffer(uint8 uart_no)
                                                      {
                                                          uint8 tx_fifo_len = (READ_PERI_REG(UART_STATUS(uart_no)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT;
                                                          uint8 fifo_remain = UART_FIFO_LEN - tx_fifo_len ;
                                                          uint8 len_tmp;
                                                          uint32 data_len;
                                                      
                                                          if (pTxBuffer) {
                                                              data_len = (pTxBuffer->UartBuffSize - pTxBuffer->Space);
                                                      
                                                              if (data_len > fifo_remain) {
                                                                  len_tmp = fifo_remain;
                                                                  tx_fifo_insert(pTxBuffer, len_tmp, uart_no);
                                                                  SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
                                                              } else {
                                                                  len_tmp = (uint8)data_len; // THIS OK
                                                                  tx_fifo_insert(pTxBuffer, len_tmp, uart_no);
                                                      
                                                              }
                                                      
                                                              if (pTxBuffer->Space >= URAT_TX_UPPER_SIZE) {
                                                                  (void)system_os_post(EVENT_QUEUE_ID,(ETSSignal)EVENT_UART_SEND_DONE,(ETSParam)0);
                                                              }
                                                          }
                                                      }
                                                      
                                                      void uart_rx_intr_disable(uint8 uart_no)
                                                      {
                                                          CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA);
                                                      }
                                                      
                                                      void uart_rx_intr_enable(uint8 uart_no)
                                                      {
                                                          SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA);
                                                      }
                                                      //========================================================
                                                      

                                            0
                                            Используя функции Lua C API, предназначенные для манипуляции с данными получаемыми/передаваемыми из внешних библиотек, теоретически возможно все это серьезно заоптимизировать. Но это (на мой взгляд) подобно сексу в гамаке стоя. Ибо зачем так извращаться, когда можно просто написать нужное на C/C++.

                                            Насколько я помню работу с Lua C API, написание сишной библиотеки для использования человеческих сишных массивов из lua займет строк 20-50 на С. Там всё очень близко к С. Разве это не так?

                                              0

                                              Я имел в виду не написание C-библиотеки (подозреваю, что для NodeMCU это не невозможно, но несколько сложнее того, ради чего используют NodeMCU), а использования функционала C API для более эффективного преобразования структур данных: сначала преобразуем входной "массив" в userdata, как будто бы мы хотели отправлять его в C, затем эту userdata преобразуем в string. Скорее всего это получится сделать гораздо эффективнее, чем прикладными методами.

                                  0
                                  Кстати, вспомнил штуку, которую так и не попробовал: twitter.com/Soukhinov/status/823506003018862594
                                  image
                                    0

                                    Хм, надо попробовать аппроксимацию простой нейросетью с ReLU.

                              0

                              А что будет с картинкой, если пи принять равным 4?

                          0
                          Подскажите, возможно есть русскоязычные ссылки как на STM32 реализовать DMA с участием внешней микросхемы статической или динамической ОЗУ? Задача стоит связать STM32+ SRAM 32Kx8 + АЦП 20мегавыборок/сек с параллельной шиной данных + вывод результатов из SRAM 32Кх8 в режиме DMA на ЖК экран (128х160 или 240х320 и подобный из диапазона 150-200/шт). Как реализуемо впринципе такое?
                            0

                            На русском… сомневаюсь.


                            Смотрите в сторону FSMC функционала. Как раз для работы с SRAM и для вывода на экран в через параллельную шину.
                            Я такое делал.
                            https://github.com/mmMikeKn/CNC/tree/master/src/liblcd

                              0
                              А надо ли тут DMA? Если не ошибаюсь, у некоторых контроллеров есть аппаратный интерфейс специально для микросхема памяти, FSMC.
                              То, что вы описали про АЦП, память и дисплей похоже на цифровой осциллограф, но тут хорошо бы начать с приблизительной оценки — а потянет ли контроллер столько? Что он с этими данными делать будет? Если просто отобразить на дисплее, так там 32 кБ не нужно, хватит тех же 320 байт. Ну пусть 640 если по 2 байта на отсчет. Если передавать в компьютер, то по какому интерфейсу? USB тоже не потянет.
                              Да даже если АЦП будет выдавать по 20 мегасемплов в секунду, сколько тактов у контроллера уйдет чтобы их забрать и хотя бы сложить в память, не говоря уж об обработке?
                              +1

                              Эпично.


                              Я правильно понимаю, что у вас сцена рисуется построчно, по 240 раз на кадр?


                              А вы не хотите заморочиться и далее, релиазовав поддержку чтения формата Binary GLTF? Этот формат более дружелюбен к современным графическим api, чем obj.

                                0
                                Не совсем. То есть сцена действительно рисуется построчно и строк 240, но на каждую строку рисуется только та строка примитива, которая в нее попадает. В отличие от «нормальной» графики, где примитив рисуется сразу целиком, потом другой примитив, тоже целиком. Тут в чем-то даже похоже на трассировку лучей. Хотя скорее даже «трассировка веера» если так можно сказать.
                                На счет glTF, то до вашего вопроса даже не знал что это такое :) Как бы то ни было, развивать именно трехмерку на stm32 я не планирую. Но не исключено, что воспользуюсь в более «взрослой» графике. Спасибо что упомянули про такой формат.
                                  +1
                                  Этот формат дружелюбен к современным графическим api

                                  Он дружелюбен (прибит гвоздями) к OpenGL, а не к современным графическим API. JSON-часть этого формата не выглядит хорошим решением для микроконтроллеров. Если выкинуть JSON, то остаётся просто блоб, который загружать даже проще, чем Wavefront OBJ. Достаточно memcpy или аналога, идентичного натуральному.


                                  OpenGL к современным графическим API я не отношу в силу того, что архитектура OpenGL идёт в разрез с тем, как устроенны современные GPU.

                                  +1
                                  Спасибо. Если добавить акселерометр-гироскоп-магнитомер, то возможно было бы получить прикольное управление моделью — как если ее через некое окошко рассматриваешь с разных сторон.
                                    0
                                    Ну в чем-то да :)
                                    Правда, чтобы рассмотреть модель снизу придется встать в очень интересную позу
                                      0
                                      Достаточно лечь на спину. Для меня — это самая естественная поза :)
                                    0
                                    Раз уж есть расчёт освещения, можно отбрасывать все полигоны, у которых вектор нормали направлен от экрана — их всё равно не видно. Примерно вполовину количество полигонов для отрисовки уменьшится.
                                      0
                                      Файл src/gl.c, строка 282 именно это и делает. Но это вообще стандартный метод удаления невидимых граней при обходе по часовой стрелке или против, тут вообще нет специфики применения контроллера.
                                      +1
                                      Спасибо за статью, прочиталось запоем до последней точки. Ну и для себя надо взять на заметку такую искусную работу с младшей STM32, как пример «Вижу цель, не вижу препятствия!» ;)
                                        –3
                                        Залез в код, ожидал сначала увидеть ужасный калокуб… Но увидел отличные исходники!
                                        Спасибо!!! Хоть немного людей в этом мире еще не превратились в хомячков…
                                          –1
                                          Люблю воинственные заявления, спасибо. Только в вашем же журнале вы регулярно жалуетесь на то, что бьётесь-бьётесь, а в итоге железо было неправильно инициализировано…
                                          0
                                          Спасибо за статью!
                                          Хороший и показательный пример использования ресурсов.

                                          От себя добавлю:
                                          По сравнению с AVR характеристики камня довольно приличные: 72 МГц тактовой (на практике можно разогнать до 100 МГц, а то и больше, но только на свой страх и риск!), 20 кБ оперативки и 64 кБ флеша.

                                          Разгоняется до 144 МГц (из периферии становится недоступным только USB), в работе на такой частоте — чуть теплый.
                                          А флеша у этого чипа 128кб. :) (Для демки — самое то).
                                            0
                                            144 МГц и 128 кБ это все же как повезет. То есть производитель не только из жадности промаркировал камень меньшим номиналом, что-то на предельных режимах работает недостаточно устойчиво. А значит, в серьезной разработке полагаться на эти возможности нельзя. Для себя же, когда проверил конкретный камень — почему бы и нет, на свой страх и риск.
                                              0
                                              Частота официальна, не заявлена ввиду отсутствия делителя для USB.
                                              И само-собой, это не «в продакшн».
                                              Хотя все протестированные экземпляры пилюль, показали полную работоспособность флеша выше 64кб.
                                              Но я не настаиваю, на нет и суда нет ;)
                                              Просто для такой задачи, это «самое оно».
                                            +4
                                            Забавно, я пишу сейчас как раз собственный 3д движок под stm32f103 на том же железе. Чтобы прыгнуть выше 15 фпс надо отказаться от z-буффера в пользу сортировки полигонов. Ну и да, я извратился с числами с фиксированной точкой, чтобы избежать флоатов. Модельки пока не грузил. Пока как-то так, на кубах, есть куда оптимизировать. Пилю ещё серию видео на эту тему для ютубчика. ФПС где-то под 30 на глаз, бодро работает.

                                            image
                                              +5
                                              Ну, раз уж пошла такая пьянка, то вот мой рейтрейсинг:


                                                0
                                                Оставлю на всякий случай это здесь


                                                0
                                                Если вы обратили внимание, в моей демке при выводе пересекающихся квадратов довольно ярко горит красный светодиод, то есть узкое место не в математике, а в пересылке по SPI. Вот на модельках из 200 треугольников (если вычесть отброшенные по отрицательной нормали) там да, проблема в обсчете. Но и то только из-за того что я не заморачивался с оптимизацией. Точки пересечения со строкой считаются в лоб, через умножения, глубина тоже. По-хорошему, стоило бы применить алгоритм Брезенхема и проводить расчет итеративно, за одно сложение и одно сравнение.
                                                Z-координату вообще можно вытянуть из уравнения плоскости:
                                                Z = (D — x*nx — y*ny)/nz,
                                                D = px*nx + py*ny + pz*nz,
                                                где nx,ny,nz — нормаль
                                                pz,py,pz — опорная точка (скажем, любая вершина), но она пересчитывается в одну скалярную константу D
                                                x,y — координаты на плоскости, z — глубина
                                                — Но в целом необходимости в этом немного: в моем варианте память контроллера кончилась ненамного позже запаса быстродействия, выигрыш будет хорошо если в 2 раза на сложных модельках
                                                  0
                                                  SPI у него тоже разгоняется до 195/2 MHz, и работает без нареканий. (Неофициально, само-собой)
                                                0
                                                Ловите фаната gothic — использованы модельки шныга и кровавой мухи :)
                                                  0
                                                  Там еще на превьюшке третья моделька
                                                  А вообще, ловите второго фаната Готики — он узнал эти модельки :)

                                                Only users with full accounts can post comments. Log in, please.