Мультиплексирование шины данных дисплея с параллельным выводом и последовательного порта Ардуино.

Статья описывает способ мультиплексного использования порта D микропроцессора ATMEL 328P (Ардуино НАНО) с целью обеспечения попеременного побайтного вывода в дисплей и обмена по последовательному каналу.

Собрал я как-то прибор для контроля уровня угарного газа (СО) из ненужных элементов – дисплей от Нокии N95, Ардуино НАНО с несправными портами (D3 и D11, пробиты в результате неудачного замыкания на +400 вольт при отладке генератора высокого напряжения), платы воспроизведения звуковых фрагментов и датчика на угарный газ MQ7. Все эти детали в той или иной степени были неисправны (кроме датчика) и никакого применения в других проектах найти не могли. Как ни странно, оказалось, что прибор очень полезен при использовании печки на даче. Лето 2019 года выдалось нежарким и печку я топил практически каждый день в течении пары недель в июле, соединяя приятное (медитирование на пламя) с полезным (утилизацией попиленных мусорных деревьев). Контролировать режимы горения оказалось очень легко, все манипуляции с заслонками сразу отражались на показаниях прибора, что позволяло управлять печкой разумно. Прибор в этой статье не описывается, в интернете таких устройств предостаточно на любой вкус. Отличительной особенностью моего прибора является функция постоянного контроля исправности датчика СО на основе сравнения запомненной эталонной кривой и получаемой в реальном масштабе времени, а также высокая скорость реакции на изменение уровня СО, достигнутая сравнением запомненных на предыдущем цикле данных с текущими.

Фокус этой статьи на увеличении скорости обмена процессора и дисплея с параллельным байтовым обменом данными.

Дисплей имеет параллельный байтовый обмен и, несмотря на использование всех известных мне способов увеличения скорости обмена, вывод на него оказался довольно медленным. Основная причина – необходимость побитного вывода байта данных на разные биты разных портов, так как Ардуино Нано не имеет ни одного полноценного порта шириной в один байт. Этот режим вывода требует грубо в 8 раз больше времени по сравнению с командой записи байта в регистр. У НАНО имеется единственный полноценный порт D, но его младшие биты используются для аппаратного последовательного порта, по которому происходит загрузка скетчей в процессор и обмен скетча с хост-машиной.

Я нашел относительно простой способ использования побайтного вывода на дисплей. Способ этот заключается в попеременном использовании порта D для вывода данных на дисплей и в обмене данными по последовательному каналу.

Предлагаемый способ позволяет значительно увеличить скорость интегрального обмена с дисплеем (около 3-х раз по моим измерениям). Под словом «интегральный» имеется в виду, что измерялись суммарные времена выполнения операций отрисовки экрана на макро-уровне. Вероятно, что измерение времен на уровне атомарных операций ввода-вывода дало бы существенно больший выигрыш (в районе одного порядка).

Измерения выполнялись на специально собранном макете (см. рисунок 1) следующим способом:

  1. В тестовой программе расставлялись метки времени с выводом на хост-машину.
  2. Дисплей присоединялся к выводам D2 — D9, загружалась тестовая программа, в которой вывод байта осуществлялся путем распределения байта по битам.
  3. Дисплей присоединялся к выводам D0 – D7, загружалась тестовая программа, в которой вывод байта осуществлялся командой PORTD=data.

image

image
Рисунок 1. Фотографии макета для отработки мультиплексирования вывода

Программы совершенно одинаковые, переключение способа вывода осуществлялось сменой имен подпрограмм SendDat и SendCom на SendDat1 и SendCom1 соответственно.
Вывод программы на встроенный сериал монитор записывался в OneNote и анализировался.

image
Рисунок 2. Измерение времени вывода на экран в режиме побайтного вывода

image
Рисунок 3. Измерение времени вывода на экран в режиме побитного вывода

Результаты измерений сведены в таблицу 1.

image
Таблица 1. Интегральный выигрыш в скорости обмена

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

Исследование мануала по процессору дало следующую информацию: включение режима последовательного порта перехватывает управление ножками D0 и D1 на аппаратном уровне. Это значит, что попытки управления ножками из скетча не дадут нужного результата.

Дальнейшее изучение вопроса показало, что, если не включать в скетче последовательный порт командой Serial.open(), то весь порт D остается в распоряжении пользователя. Можно перевести порт в режим вывода по всем ногам командой DDRD=0xFF и выводить весь байт одновременно командой PORTD=data, где переменная data содержит выводимые данные.

Перевести порт D в режим вывода достаточно один раз (в Setup). Последующие включения-выключения режима последовательного обмена не влияют на режим порта D – он так и остается в режиме параллельного вывода 8 бит. При включении режима последовательного обмена выводы D0 и D1 перейдут в режим приема и передачи соответственно. На выводе D1 появится «1» независимо от предыдущего состояния бита D1, и эта «1» будет на этом выводе все время пока включен режим последовательной передачи, кроме моментов передачи символов. При выключении режима последовательной передачи выводы D0 и D1 перейдут в состояние вывода и на них появятся сигналы из регистра вывода. Если в регистре вывода на месте D1 имеется «0», то на выводе будет сформирован отрицательный перепад, который приведет к передаче паразитного символа в последовательный канал.

Рассмотрим теперь вопрос – а не помешает ли такое использование порта D загрузке программ? При загрузке программы процессор сбрасывается импульсом, который генерируется контроллером USB порта FT232RL (либо его аналогом CH340) при выставлении сигнала DTR. Сигнал DTR переходит из 1 в 0 и отрицательный перепад через конденсатор сбрасывает процессор. После сброса процессор включает последовательный порт, запускает загрузчик и принимает код программы. Итак – нормальной загрузке скетча изменение режима работы порта D не мешает.
Если в скетче требуется вывод в сериал порт, то достаточно команды Serial.open() перед командами вывода.

Однако есть тонкость. Заключается она в том, что вход RxD микросхемы FT232RL остается присоединенным к выводу TxD и данные, идущие на дисплей, принимаются и пересылаются далее в хост-машину. Данные эти выглядят как шум, хотя на самом деле им не являются (рисунок 4).

image
Рисунок 4. Вид экрана в режиме побайтного вывода без блокирования

Бороться с этим ненужным сигналом можно двумя путями.

Первый путь – программный. Заключается он в том, что в скетче перед выводом используется команда Serial.println() для создания новой строки перед выводом полезной информации. Это облегчит программе в хост машине анализ входящих строк и выделение полезной информации от скетча.

Второй путь – аппаратный. Вход RxD FT232RL подсоединен к выходу TxD через резистор 1 кОм. Чтобы заблокировать передачу информации достаточно присоединить вход RxD FT232RL к «1». Сделать это проще всего одним из свободных выводов Ардуино. Я использовал вывод D8. Для выполнения этого действия я припаял к выводу 7 резистора RP1B номиналом 1 кОм проводок с разъемом на конце, проведя его через отверстия в плате с целью механической фиксации. На рисунке 5 это соединение показано красной линией, на рисунке 6 приведена фотография места пайки.

image
Рисунок 5. Часть схемы Ардуино нано

image
Рисунок 6. Место пайки дополнительного провода в Ардуино НАНО

Механизм этот работает так: после сброса ножка D8 находится в режиме высокоимпедансного входа и штатной работе механизма загрузки программ в плату Ардуино не мешает.

Когда в скетче надо начать управлять дисплеем, то вывод D8 переводится в режим активного вывода, на нем выставляется «1» (это блокирует передачу данных от вывода TxD Atmel328P на вывод RxD FT232RL) и после этого выполняется команда Serial.end();. Порядок действий важен, так как после выключения режима последовательной передачи на выводе TxD появится бит D1, который сохранился в выходном регистре порта D от предыдущей записи байта в этот порт. Если бит D1 был «0», то при выключении режима последовательной передачи ножка процессора переключится из «1» в «0» и это породит передачу паразитного символа по последовательному каналу.

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

Когда в скетче требуется включить передачу данных по сериал порту, надо включить режим последовательной передачи и выключить блокировку прохождения сериал данных путем установки режима чтения на выводе D8.

Для выполнения этих задач в скетч добавлены две подпрограммы:

void s_begin()
{
  Serial.begin(115200); // Включаем управление выводом TxD от модуля USART. Нога TxD переходит в "1", нога RxD становится входом
  pinMode(8, INPUT); // Отключаем подтяжку входа RxD FT232RL к "1", разрешая прохождение сериал данных на вход RxD FT232RL
 }

void s_end()
{
  Serial.flush(); //Ждем конца передачи 
  pinMode(8, OUTPUT); //Подтягиваем вход FT232RL к "1" отключая передачу данных пока идет управление дисплеем. Без этого 
  D8_High;            //будут передаваться паразитные данные 
  Serial.end(); // закрываем сериал канал. В этот момент на ноги TxD и RxD начинают выводится биты D0(RxD) и D1(TxD) порта D 
}

Полностью тестовую программу можно взять тут.

Комментарии (39)


  1. Alyoshka1976
    22.11.2019 11:54

    Если Вы выводили в побитном режиме посредством digitalWrite, то конечно, функция весьма медленная из-за множества всяких проверок (да и с режимом обработки прерываний она вольно обращается :-)
    Если точно знать, что делать, то можно вот так оформить побитный вывод, который займет считанные машинные циклы (в проекте "нанокомпьютера", откуда этот отрывок, мне была важна именна неприкосновенность прерываний):


    void pinWrite(uint8_t pin, uint8_t val) {
      switch (pin) {
        case 3://1 PD3 0x0B
          if (val == 1) {
            asm volatile ("sbi 0x0B, 3 \n"); //HIGH
          }
          else {
            asm volatile ("cbi 0x0B, 3 \n"); //LOW
          }
          break;
        case 4://2 PD4
          if (val == 1) {
            asm volatile ("sbi 0x0B, 4 \n"); //HIGH
          }
          else {
            asm volatile ("cbi 0x0B, 4 \n"); //LOW
          }
          break; 
    ....


    1. Winnie_The_Pooh Автор
      22.11.2019 11:56

      Я использовал макроподстановки типа #define D0_High PORTD |=B00000001, которые делают то же самое, что и предложенный Вами способ.


      1. Alyoshka1976
        22.11.2019 12:20

        Нет, не совсем тоже самое.
        PORTD |=B00000001 эквивалентно PORTD = PORTD | B00000001, т.е. требуется дополнительная операция.


        Это можно проверить, отключив через platform.txt оптимизацию кода:


        запись в порт с учетом его состояния


        void setup() {
          // put your setup code here, to run once:
          PORTD |= 0x58;
        }
        void loop() {
          // put your main code here, to run repeatedly:
        }

        Скетч использует 514 байт (1%) памяти устройства


        прямая запись в порт


        void setup() {
          // put your setup code here, to run once:
          PORTD = 0x58;
        }
        void loop() {
          // put your main code here, to run repeatedly:
        }

        Скетч использует 506 байт (1%) памяти устройства


        А sbi и cbi меняют конкретные биты порта, совсем не трогая остальные.


        1. Winnie_The_Pooh Автор
          22.11.2019 12:39

          Не буду спорить :) в любом случае параллельный вывод 1 байта быстрее 8-ми выводов бита с логическими операциями определения какой бит надо выставить.


        1. Serge78rus
          22.11.2019 15:24
          +1

          Команда PORTD |=B00000001 и будет оттранслирована компилятором в SBI, а PORTD |= 0x58 нет, так как затрагивает более одного бита. Во всяком случае, это точно работает с компилятором GCC, но в Arduino, вроде, он и используется.


          1. Alyoshka1976
            22.11.2019 16:03

            Спасибо, интересно посмотреть, как g++ будет вести себя в этом случае в различных режимах оптимизации.


            1. Serge78rus
              22.11.2019 17:17

              Ну, если интересно, то смотрите (правда я пользовался не g++, а gcc, но думаю в данном случае без разницы). Исходный код:

              #include <avr/io.h>
              
              int main(void)
              {
              	PORTD |= 0x01;
              
              	return 0;
              }
              

              Вообще без оптимизации (O0):
              0000007a <main>:
                7a:	cf 93       	push	r28
                7c:	df 93       	push	r29
                7e:	cd b7       	in	r28, 0x3d	; 61
                80:	de b7       	in	r29, 0x3e	; 62
                82:	8b e2       	ldi	r24, 0x2B	; 43
                84:	90 e0       	ldi	r25, 0x00	; 0
                86:	2b e2       	ldi	r18, 0x2B	; 43
                88:	30 e0       	ldi	r19, 0x00	; 0
                8a:	f9 01       	movw	r30, r18
                8c:	20 81       	ld	r18, Z
                8e:	21 60       	ori	r18, 0x01	; 1
                90:	fc 01       	movw	r30, r24
                92:	20 83       	st	Z, r18
                94:	80 e0       	ldi	r24, 0x00	; 0
                96:	90 e0       	ldi	r25, 0x00	; 0
                98:	df 91       	pop	r29
                9a:	cf 91       	pop	r28
                9c:	08 95       	ret
              

              С любой включенной опцией оптимизации (O1, O2, O3, Os) получается одно и то же:
              0000007a <main>:
                7a:	58 9a       	sbi	0x0b, 0	; 11
                7c:	80 e0       	ldi	r24, 0x00	; 0
                7e:	90 e0       	ldi	r25, 0x00	; 0
                80:	08 95       	ret
              


              1. Alyoshka1976
                22.11.2019 17:23

                ))))))) Мой результат на 9 минут раньше.


                1. Serge78rus
                  22.11.2019 17:46

                  Так мне и лет больше. Зато у меня сравнение разных режимов оптимизации, как Вы и просили, а у Вас только O0 и Os.

                  Я контроллерами AVR редко пользуюсь, поэтому долго искал, где же у меня лежит среда разработки для них. А ведь еще параллельно и работать приходится.


                  1. Alyoshka1976
                    22.11.2019 17:53

                    image


                  1. Alyoshka1976
                    24.11.2019 21:04

                    P.S. Оптимизатор не всегда придерживается требуемой стратегии s
                    (хотя конечно gnu.org дает уклончивый ответ — "except those that often increase code size" :-)


                    PORTD |= 0x03;

                    дешевле (по размеру) было бы заменить (4 байта, 4 цикла)


                    sbi    0x0b, 0
                    sbi    0x0b, 1

                    а не: (6 байтов, но 3 цикла)


                    in    r24, 0x0b
                    ori    r24, 0x03
                    out    0x0b, r24 


                    1. Serge78rus
                      25.11.2019 10:31

                      Тут может быть гораздо более неприятный момент: две интсрукции sbi атомарны (каждая по отдельности) и не чуствительны к изменению в процессе выполнения других битов регистра из какого-нибудь прерывания. Чего нельзя сказать о конструкции in — ori — out. Именно в подобных случаях и приходится периодически контролировать код, генерируемый компилятором.


          1. Alyoshka1976
            22.11.2019 17:08

            Так и есть при включенной оптимизации (результаты моей проверки):


            PORTD = 0x01;

            -O0


              ae:    8b e2           ldi    r24, 0x2B    ; 43
              b0:    90 e0           ldi    r25, 0x00    ; 0
              b2:    21 e0           ldi    r18, 0x01    ; 1
              b4:    fc 01           movw    r30, r24
              b6:    20 83           st    Z, r18

            Скрытый текст

            -Os


              a6:    81 e0           ldi    r24, 0x01    ; 1
              a8:    8b b9           out    0x0b, r24    ; 11

            PORTD |= 0x03;

            -O0


              ae:    8b e2           ldi    r24, 0x2B    ; 43
              b0:    90 e0           ldi    r25, 0x00    ; 0
              b2:    2b e2           ldi    r18, 0x2B    ; 43
              b4:    30 e0           ldi    r19, 0x00    ; 0
              b6:    f9 01           movw    r30, r18
              b8:    20 81           ld    r18, Z
              ba:    23 60           ori    r18, 0x03    ; 3
              bc:    fc 01           movw    r30, r24
              be:    20 83           st    Z, r18

            -Os


              a6:    8b b1           in    r24, 0x0b    ; 11
              a8:    83 60           ori    r24, 0x03    ; 3
              aa:    8b b9           out    0x0b, r24    ; 11
            

            PORTD |= 0x01;
            

            -O0


              ae:    8b e2           ldi    r24, 0x2B    ; 43
              b0:    90 e0           ldi    r25, 0x00    ; 0
              b2:    2b e2           ldi    r18, 0x2B    ; 43
              b4:    30 e0           ldi    r19, 0x00    ; 0
              b6:    f9 01           movw    r30, r18
              b8:    20 81           ld    r18, Z
              ba:    21 60           ori    r18, 0x01    ; 1
              bc:    fc 01           movw    r30, r24
              be:    20 83           st    Z, r18

            -Os


              a6:    58 9a           sbi    0x0b, 0    ; 11


        1. Winnie_The_Pooh Автор
          24.11.2019 12:30

          Проверил, заменил макроподстановки на прямой ассемблерный код, время цикла изменилось незначительно: вместо 213 стало 195 мС.


    1. swa111
      23.11.2019 12:26

      Как то давно для своих проектов использовал такие макросы

      #define digitalWriteC(pin,val) if (val) { *((volatile uint8_t *) port_to_output_PGM[digital_pin_to_port_PGM[pin]]) |= (digital_pin_to_bit_mask_PGM[pin]);} else {*((volatile uint8_t *) port_to_output_PGM[digital_pin_to_port_PGM[pin]]) &= ~(digital_pin_to_bit_mask_PGM[pin]);}
      
      #define pinModeC(pin,mode)  if (mode == INPUT) {     *((volatile uint8_t *) port_to_mode_PGM[digital_pin_to_port_PGM[pin]]) &= ~(digital_pin_to_bit_mask_PGM[pin]);    *((volatile uint8_t *) port_to_output_PGM[digital_pin_to_port_PGM[pin]]) &= ~(digital_pin_to_bit_mask_PGM[pin]);  } else if (mode == INPUT_PULLUP) {    *((volatile uint8_t *) port_to_mode_PGM[digital_pin_to_port_PGM[pin]]) &= ~(digital_pin_to_bit_mask_PGM[pin]);    *((volatile uint8_t *) port_to_output_PGM[digital_pin_to_port_PGM[pin]]) |= (digital_pin_to_bit_mask_PGM[pin]);  } else {    *((volatile uint8_t *) port_to_mode_PGM[digital_pin_to_port_PGM[pin]]) |= (digital_pin_to_bit_mask_PGM[pin]);  };
      
      inline uint8_t digitalReadC (uint8_t pin) __attribute__((always_inline));
      uint8_t digitalReadC (uint8_t pin)
       {
         if (*((volatile uint8_t *) port_to_input_PGM[digital_pin_to_port_PGM[pin]]) & (digital_pin_to_bit_mask_PGM[pin])) {return HIGH;} else {return LOW;};  
       };
      


      Работают корректно только если номера пинов и портов — константы. GCC всегда транслирует в sbi и cbi если это возможно. При этом Изменений в программе минимум. Всего то нужно использовать digitalWriteC вместо digitalWrite.


      1. Alyoshka1976
        24.11.2019 21:08

        Забавно, что SBI/CBI таки не всегда быстрее, хотя и компактнее.


  1. GarryC
    22.11.2019 12:32

    У Константина Чижова есть восхитительный шаблонный класс в его библиотеке mcucpp, который генерит минимально возможную последовательность записи данных в произвольный набор ножек.


  1. clawham
    22.11.2019 12:50

    Вот ещё один человек скоро прозреет от ардуиновых либ и окажется что все фокусы и приемы на ардуине дают 2-3 прирост а нормальный подход сразу дал бы увеличение скорости раз так в 30 не менее :)
    Иногда таки полезно знать ассемблерные команды которые в один такт делают то что адруновая либа детает за примерно полторы тысячи тактов а результат то тот же.
    Вы делаете по сути программную эмуляцию готовой апаратной функции а как все знают программная эмуляция всегда впринципе медленнее апаратной а тут ещё и ардуино который и в программных эмуляциях лидер по тормозам :)


    1. Winnie_The_Pooh Автор
      22.11.2019 12:56
      +1

      Поясните свою мысль: хотите ли Вы сказать, что команда PORTD=data транслируется в полторы тысячи тактов из-за ардуиновых либ? Я пока уверен, что эта команда транслируется в одну ассемблерную :)


      1. Polaris99
        22.11.2019 15:28

        А Ваша уверенность зиждется на анализе полученного в результате программного кода или на твердой уверенности в мощи ардуины?


        1. Winnie_The_Pooh Автор
          22.11.2019 15:31

          Ну раз Вы отвечаете вопросом на вопрос — и я поступлю так же: Вы конечно же проанализировали уже, во что превращается команда PORTD=data и готовы показать обществу результат своих изысканий?


          1. Polaris99
            22.11.2019 16:26

            Я пока уверен, что эта команда транслируется в одну ассемблерную
            Это вопрос был? Ну тогда ок, вопросов больше нет.
            Значит, Вас точно не должно смущать, что формат вывода в порт у AVR выглядит так: OUT A,Rr, где Rr — регистр, а не данные. А уж во что там сконвертит GCC в итоге — это вообще дело десятое.


            1. Alyoshka1976
              22.11.2019 17:45

              Как показала проверка, фантазии компилятору не занимать :-) — в одном случае он использует доступ к портам через общее адресное пространство (смещение на 0x20), а в другом — как к порта ввода-вывода.


      1. clawham
        22.11.2019 15:32

        digitalwrite транслируется в ту самую тыщу тактов :)
        а portD=data явно не то что нужно! мы же не всем портом разом рулить хотим а только одним битом так? значить сначала надо отключить прерывания(в них ведь могут дергать порты и биты портов так?) потом считать кудато состояние порта на сейчас, потом применить битовую маску разную для установки или снятия бита, потом записать полученный байт назад в порт и снова разрешить прерывания… видите как не просто даже простое изменение бита? cbi sbi делает это все за один такт. Ещё вопросы?


        1. Winnie_The_Pooh Автор
          22.11.2019 15:36

          Вы статью читали или просто по мотивам выступаете? :)

          Я рулю всем байтом разом одной командой. Статья именно об этом: как на Нано использовать весь порт D одновременно.

          Так понятно?


  1. reticular
    22.11.2019 13:48

    совсем не ясно Зачем вы накладываете на себя ограничения в виде полноценного порта?
    ну нет порта: -возьмите полпорта В
    ну будет две команды:
    PORTD=dataD & maskD;
    PORTB=dataB & maskB;
    зато без «огорода»


    1. Winnie_The_Pooh Автор
      22.11.2019 13:52

      Я захотел сделать байтовый вывод максимально быстро — т.е. одной командой. Вывод в два полубайта — две команды и они выполняются в два раза дольше.


      1. GarryC
        22.11.2019 14:28

        Если Вы хотели сделать вывод максимально быстро, то не следует перед выводом каждого байта подтверждать состояние управляющей ноги.


        1. Winnie_The_Pooh Автор
          22.11.2019 14:30

          Я не понял Вас. ПОясните. О какой управляющей ноге Вы пишете?


          1. GarryC
            22.11.2019 16:14

            D11_High; в SendDat()


            1. Winnie_The_Pooh Автор
              22.11.2019 16:18

              Эта нога управляет режимом Data\Command при управлении дисплеем.
              Обычно за байтом команды идут несколько байт данных.
              Действительно, наверно можно оптимизировать и выставлять ногу один раз.
              Подумаю на досуге :)


            1. Winnie_The_Pooh Автор
              24.11.2019 12:26

              Проверил, перенес манипуляции D11 в SendCom:
              3.1 раза перешло в 3.6 раза.


      1. reticular
        22.11.2019 14:54

        дада дольше на 1 микросекунду
        стоит ли однамиллионная секунды того огорода что вы нагородили?
        стоит ли она того времени, что вы затратили на отладку мультиплексирования?
        зачем усложнять? (возможно это вызов? я например для увлекательности, при решении, понижаю себе IQ, возможно без мультиплексирования не было бы этой статьи)


        1. Winnie_The_Pooh Автор
          22.11.2019 15:02

          Это просто развлечение техногика. Исследование возможностей железки. Мне это доставляет удовольствие.
          Если же подходить к вопросу с точки зрения здравого смысла, то ни использованный процессор, ни дисплей не оптимальны. Сейчас использовать Ардуино Нано практического смысла нет — его прекрасно заменяет Blue Pill на STM32F103, у которого цена меньше, размер незначительно больше, процессор существенно мощнее.


          1. iig
            22.11.2019 23:32

            Даже без эксперимента понятно, что вывод байта ассемблерным кодом будет работать быстрее, чем побитовый ногодрыг на С++ :)


            1. Winnie_The_Pooh Автор
              23.11.2019 12:27

              «Практика — критерий истины» (с) В.И.Ленин ;)


              1. Serge78rus
                24.11.2019 09:22

                Вот кто-кто, а В.И.Ленин уж лучше бы ограничился теоретическими измышлениями, а не проверял их на практике.


  1. shadrap
    23.11.2019 18:42

    Спасибо, интересно, как раз думал поэкспериментировать с таким дисплеем…
    А MQ-7 не используйте для таких целей, на что он реагирует и что измеряет это большой вопрос.)) Из всей этой серии как-то приспособил MQ2 под контроль утечки баллонного газа, помог найти утечку в подводных шлангах. А для таких серьезных вещей как СО\СО2 использую Winsen-овские датчики, они конечно подороже, но хоть понятно что измеряют.


    1. Winnie_The_Pooh Автор
      23.11.2019 19:04

      Про свойства MQ7 я в курсе — некогда провел достаточно подробный цикл измерений в лаборатории газов с эталонными газами. ЕГо конечно нельзя использовать в качестве измерителя концентрации конкретного газа, но как показометр общего загрязнения — вполне можно.
      Для контроля СО2 использую датчики К-30 на NDIR технологии.