Еще один программный ШИМ или реабилитация Attiny13a при помощи Дзен

    Приветствую Хабр, и всех его многочисленных обитателей!

    Сразу оговорюсь, что то о чём здесь пойдет речь не очень рассчитано на новичков, тем не менее если есть интерес и тяга к изучению, напротив — прошу познать.

    А в этот раз, речь пойдет о реализации гибридного ШИМ, которых наплодилось в сети уже множество. Так что думаю еще один, или два, или три (бонусом), будут не лишними или не будут лишними.

    Итак, ну во-первых почему гибридный: прежде всего, потому что не аппаратный на 100%, но и не софтварный тоже на 100%.
    Что это бы значило: для реализации используется аппаратный счетчик, но вывод формируется не в один канал порта ввода/вывода, а сразу в три, конфигурационной маской, которая маскирует каналы порта в прерывании по таймеру.
    В чем отличие от аппаратного? Да все просто — в числе каналов для микроконтроллера Attiny13a.

    Теперь подробней.
    Кому и зачем это нужно?! Ну основная область применения таких задач, это управление двигателями — асинхронными, биполярными и униполярными — шаговыми, контроллеры световых и звуковых эффектов, ультразвуковые устройства (ультразвуковые ванны, гравёры, смесители и т.п.), и т.д. Конечно для таких целей созданы специализированные контроллеры, но зачем искать, тратиться, и копать ДШ, если есть под рукой маленький питомец.

    Я остановился на первом вопросе, когда интерес к нему появился внезапно, при прочтении одного из форумов, где один разработчик (назовем его «Опечаленный»), был опечален тем фактом, что микроконтроллер Attiny13a, не имея достаточного количества аппаратных каналов ШИМ, не может похвастать необходимой производительностью при программной реализации задуманного.
    «Опечаленный» испробовал множество вариантов, менял константы, оптимизировал код на Си, даже задействовал прерывания, но всё тщетно. А поднятая им тема на форуме так и протухла, в каком-то лохматом году, оставшись неразрешенной и безответной.

    Меня заинтересовало, насколько же медленный программный режим ШИМ у упомянутого микроконтроллера, который работает на 9.6 МГц. И я, набросав маленький исходник, используя по-возможности все аспекты расписанные «Опечаленным» и воплотив их, неспешно получил на трёх лапах Тиника 13-го программный ШИМ, на частоте… около 4 кГц, при программной эмуляции в Proteus.

    Мало… Слишком мало! И как то это все странно… но безумно интересно, потому что если не победить такую простую задачу, то это действительно — одно из первых, маленьких разочарований, и прямая дорога в сторону более производительных и навороченных чипов, которые и стоят соответственно. С другой стороны это скорее плюс, но все же будем разбираться.

    И тут мне подумалось: «А почему, собственно, синхронизация строится на переполнениях таймера, когда задача реализуется через сравнения?!»
    И правда, ведь в режиме переполнения таймер просто обязан вызывать прерывание лишь однажды, при переполнении, пройдя полный цикл. Слегка модифицировав исходник получил около 40 кГц. «Уже лучше, но все равно — тут что-то не так!»

    Вобщем не буду больше утомлять: все переменные были объявлены в регистрах, код был оптимизирован по производительности,
    и размеру (практически весь исчез), а при дальнейших играх были полученны еще два режима с относительными смещениями импульсов.
    А дальше последовали новые оптимизации в результате которых, самый медленный из режимов (6 — фаз) был переписан на инлайновый ассемблер. И сделано это было случайно, когда я обратил внимание на полученный после компиляции код, который мягко просто сказать — сильно меня удивил! Даже при объявлении переменных в регистрах, все операции компилятор решил делать через временные пары, соответственно код раздулся почти вдвое, и это при включенной оптимизации по размеру! Несколько лишних движений и выигрыш в 40 байт, от первого варианта, и почти 10 кГц прироста в частоте.

    В результате получилось три режима:
    1. Импульсы со смещением в фазу, максимальная доступная частота 75.500 кГц при CLK=9.6 МГц (При CLK=20 МГц, частота приближена к 160 кГц ), скважность ШИМ=33.3% период — три фазы:


    2. Импульсы смещены на треть, максимальная частота 37.400 кГц (75 кГц при CLK=20 МГц), скважность=50%, период — шесть фаз (без использования ассемблера не удавалось преодолеть предел в 28 кГц, подробней — см. исходный код):


    3. Импульсы смещены на половину, максимальная частота как в первом режиме, скважность=66.6%, период — три фазы:


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

    Зачем столько режимов? Это еще не все, и все их множество обусловлено особенностями тех или иных устройств.
    Так, например асинхронные двигатели управляются первым режимом, а униполярные-шаговики — вторым (впрочем и каналов им необходимо или меньше, или больше, но при наличии примера, расширить или уменьшить число каналов — пустяк).

    Для получения меньшей частоты программно, необходимо изменять регистр делителя таймера.
    Если и этого слишком много (например для шаговых двигателей), необходимо опустить частоту микроконтроллера.
    Так при частоте чипа 128 кГц, с выставленным делителем CLK, без делителя таймера и минимальной ширине импульса (максимальной частоте), при использовании второго режима, частота импульсов 62 Гц.

    Если же говорить об управлении асинхронным двигателем, то при использовании двигателя с 12-ю полями, получаем максимальную частоту следования импульсов при использовании первого режима около 20 кГц (~78/4), что несомненно так же много, ввиду отсутствия двигателей способных вращаться на таких оборотах (в секунду). :o)

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

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

    Если Вы намереваетесь использовать мой код в своем проекте, когда он станет раздуваться и обрастать Вашими обработчиками, необходимо вооружить все объявления использованных переменных в регистрах ключевым словом «volatile», в результате чего компилятор огрызнётся, что он плевать хотел на Ваши запросы, и что в случае, если ему вздумается, то он задействует эти регистры на свое усмотрение (в случае нехватки таковых).

    Более подробную информацию я постарался как можно полнее отразить в комментариях, стараясь при этом быть максимально кратким при кодировании:


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

    PS: Для новичков: ИМХО, проект не достаточно интересен для начинающих, ввиду использования высоких частот. Однако контроллер прошитый прошивкой с раскоментированной строкой в основном цикле для изменения ширины фаз, на частоте 4.8 МГц с включенным делителем частоты (4.8/8) (вот только не помню до оптимизации кода или после), и с подключенными светодиодами на линиях PB0, PB1, PB2, забавно выглядит в роли настольной мигалки.
    Хотя некоторые, особо нервные индивиды утверждают, что «раздражает эта фигня — до мозга локтей.» :)

    PPS: Для НЕ новичков: в дополнение хочется сказать об оптимизации, и использовании (обойдемся без холивара друзья!) подобного кода:
     byte	A=0,	B=1,	C=2;
     if (A==0 || B==1 || C==2) doSomething(); / if (A==0 && B==1 && C==2) doSomething();
    

    И «неправильное» использование вида:
    if (A==0 | B==1 | C==2) doSomething(); / if (A==0 & B==1 & C==2) doSomething();
    

    Результат будет один и тот же, а вот выполняться второй пример будет ЗНАЧИТЕЛЬНО дольше по времени, и ЗНАЧИТЕЛЬНО раздует код.
    А почему?! А я не скажу, Вы сами угадайте! :)

    На этой ноте предлагаю остановиться, и ознакомиться с прилагаемыми материалами:
    проект для Proteus 8.1 и исходный код созданный в Atmel Studio 6.2
    Спасибо за внимание, до новых встреч!

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

    Similar posts

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

    More
    Ads

    Comments 131

      0
      Дополню, если конфигурацию первого режима организовать следующим образом:
      if (phase==3) phase=0; mask=(1<<phase++);
      

      то F (max) = 54 кГц.
      Это чтобы отпали вопросы по дальнейшей оптимизации.
      Естественно первый и третий режимы можно тоже переварить в инлайновый ассемблер, но здесь прирост будет не таким сказочным (4-5 кГц). Хотя судя по достоверным источникам, это близко к аппаратному ШИМ на
      AVR ATmega -48 -88 -168
        0
        1<<phase++
        

        Сильно смахивает на UB… Да и даже если не оно, все равно небольшая неоднозначность остается
          +1
          Запись постфиксная, приоритет в сдвиге. Можно для страстных и разнести, но сути — снижение барьера частоты на 20 кГц, это не поможет.
            0
            Каюсь, не прав:
            00000019  CPI      R16,0x03		Compare with immediate 
            0000001A  BRNE      PC+0x02		Branch if not equal 
            
            0000001B  LDI     R16,0x00		Load immediate 
            0000001C  MOV     R18,R16    		Copy register 
            0000001D  LDI     R24,0x01		Load immediate 
            0000001E  ADD     R24,R16		Add without carry 
            0000001F  MOV     R16,R24	        Copy register 
            00000020  LDI     R24,0x01		Load immediate 
            00000021  LDI     R25,0x00		Load immediate 
            00000022  RJMP    PC+0x0003		Relative jump 
            00000023  LSL     R24		        Logical Shift Left 
            00000024  ROL     R25		        Rotate Left Through Carry 
            00000025  DEC     R18		        Decrement 
            00000026  BRPL    PC-0x03	        Branch if plus 
            00000027  MOV     R14,R24	        Copy register
            


            Приоритет ++ — всегда выше, хотя пример работает чудным образом :)
              0
              о чем и речь. лучше так не писать, если это не поделка для себя на пять минут, а то потом внезапные косяки вылезают…
                0
                Выражение как раз относилось к поделке для проверки на 5 минут, так что ничего страшного, но спасибо за внимательность.
                  0
                  Вас с толку сбили, нету там никакого UB, код эквивалентен (что даже видно по ассемблеру):
                  int t = phase;
                  ++phase;
                  mask=(1<<t);
                  
                  UB было бы в случае присвоения результата переменной «phase», чего здесь нет.

                  Каких-то неоднозначностей тоже нет, приоритеты всё чётко определяют.
                    0
                    Неоднозначность в том, что ожидалось так:
                    int t = phase;
                    mask=(1<<t);
                    ++phase;
                    

                    так что MaximChistov, абсолютно прав.
                      0
                      Здесь нет неоднозначности, это эквивалентные вычисления. Даже если написать их в такой развёрнутой форме, в ассемблере они могут быть в другом порядке, компилятор волен переставлять инструкции, если это никак не влияет на результат.
                        0
                        Так в том то и дело, что результат отличен от предполагаемого!
                        для:
                        ++phase; mask=(1<<t); маски будут такими: 001, 010, 011,100
                        

                        а тут:
                         mask=(1<<t);++phase; маски будут такими: 000, 001, 010,011
                        

                          0
                          Запутался сам :)

                          в первом варианте:
                          010, 100,…
                          во втором:
                          001,010,100…
                            0
                            Я тоже уже запутался. Смысл пост-операций:
                            1. Скопировать
                            2. Изменить оригинал
                            3. Вернуть для использования в выражении копию

                            Т.е. при «mask = 0», «1<<phase++» === «1<<0» (а «phase» стало 1) и результат первой операции «001», как и должно быть. Обратите внимание на копирование старого значения.
                              0
                              Не верно, у "++" приоритет выше чем у "<<"
                              То есть «1<<0»!=«1<<1»
                                0
                                у "++" приоритет выше чем у "<<"
                                Да.
                                То есть «1<<0»!=«1<<1»
                                Да. НО, если «phase» == 0, то «1 << phase++», это «1 << 0» (см.), такова семантика данного выражения, инкремент «phase» является побочным эффектом, который не участвует в выражении.
                                  0
                                  Так вот же доказательство:
                                  0000001B  LDI     R16,0x00		Load immediate 
                                  0000001C  MOV     R18,R16    		Copy register 
                                  0000001D  LDI     R24,0x01		Load immediate 
                                  0000001E  ADD     R24,R16		Add without carry 
                                  0000001F  MOV     R16,R24	        Copy register 
                                  
                                    0
                                    Не уверен, что всё правильно написал (не знаю этот ассемблер), но вроде очевидно, что используется старое значение переменной:
                                    00000019  CPI      R16,0x03		if (phase == 3)
                                    0000001A  BRNE      PC+0x02		goto A
                                    
                                    0000001B  LDI     R16,0x00		phase = 0
                                    0000001C  MOV     R18,R16       	A: int t = phase
                                    0000001D  LDI     R24,0x01		int c = 1
                                    0000001E  ADD     R24,R16		c += phase
                                    0000001F  MOV     R16,R24   	        phase = c
                                    00000020  LDI     R24,0x01		int tmp_mask = 1
                                    00000021  LDI     R25,0x00		int k = 0
                                    00000022  RJMP    PC+0x0003		goto B
                                    00000023  LSL     R24		        C: tmp_mask <<= 1
                                    00000024  ROL     R25		        tmp_mask += <carry bit>
                                    00000025  DEC     R18		        B: --t
                                    00000026  BRPL    PC-0x03	        if (t >= 0) goto C
                                    00000027  MOV     R14,R24	        mask = mask
                                    
                                    0
                                    эээм, опять запутался, но сдвиг не выполняется тогда :)
                0
                Исправлюсь так:
                if (++phase>3) phase=0; mask=(1<<(phase-1));
                

                72 кГц
                  +1
                  Как был кошмар программиста-читателя, так и остался:

                  Что будет, если перед входом phase=3;

                  Инкремент, phase=4;
                  Усливие, истина, phase=0;
                  Сдвиг на 0-1=-1 БАМ! Получите UB.

                  Обфускация кода ради красивых комментариев, которые должны повышать ясность кода… даже не знаю, как это назвать.
                    0
                    Вы не правы, если условие истина, то сдвиг не выполняется ;)
                    Я не предлагаю примеров, которые не проверил. Извините, кредо.
                      0
                      Как так может быть? Вы хотите сказать, что в данном случае, UB выражается просто в отсутствии сдвига?

                      Так писать на C нельзя. И заменять && на & в условиях «просто так» нельзя, потому как & — просто операция, а && — еще и точка следования.
                        0
                        Не будьте голословны, приводите примеры!
                        Повторюсь, это не повод холиваров, если программист уверен в своем коде, и ему так удобно, это только его проблемы (он делает это на свой страх и риск).
                        Я же стараюсь следовать классике, хоть это старомодно.
                          0
                          void main()
                          {
                              int phase=4;
                              if (++phase>3) phase=0;
                              printf("%d\n",phase);
                          }
                          

                          gcc test.c -O2
                          

                          ./a.out
                          0
                          

                          ISO 9899:1999 6.5.7 Bit-wise shift operators §3
                          If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

                          Если величина правого операнда отрицательна или превышает ширину левого оперенда, поведение неопределено.
                            0
                            Это же очевидно, как и выдержка из стандарта. Мыслите логически почему — не определено!
                            А по поводу примеров, я имел ввиду приведите пример когда && не равно &.
                            Только и я Вас загоню при этом в рамки:
                            Любые использованные переменные должны быть целочисленными, без знаковыми и выровнены разрядности процессора.
                              0
                              Код на C не должен добираться до неопределенного поведения и использовать его.

                              По второму вопросу — если в коде вида:
                              if(connect(...) || die(...))
                              {
                              }
                              

                              заменить оператор || на |, его порядок работы будет не определен, и die() может быть вызвано раньше connect().
                                0
                                Не правильно! Хоть вы и не учли мои требования, я Вам отвечу по поводу Вашего примера:
                                Результатом выражения в условии будет побитовая операция над результатами функций,
                                т.е. хоть порядок и не определен, результатом будет сложение, и в случае если результат отвечает моим требованиям (см предыдущий пост), то условие выполнится при любом результате этого сложения отличным от нуля (сложение побитовое конечно).

                                Когда при использовании ||, значение результата функции die(), для истинности условия не важно, при истинности результата connect().

                                Приведите хороший пример ;)
                                  0
                                  Обратите внимание на мой код. В нем операнды дизъюнкции должны вызываться строго слева направо, и как только будет получен 0, вызовы должны прекратиться.

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

                                  То есть в вашем конкретном случае, побитовая проверка — легитимна. Но про эту оговорку нужно помнить.
                                    0
                                    Согласен с Вами, именно поэтому и говорю — плохой пример ;)
                                      0
                                      По поводу скорости выполнения — отдельный разговор. Если мы будем использовать || и вперед вынесем тот вариант условия, который возникает чаще, код будет выполнятся быстрее, так как остаток выражения даже не будет рассчитываться.

                                      Итого, выбор нужного оператора должен происходить с учетом всех этих факторов, а не потому, что | — якобы быстрее. На «большом» процессоре с конвейером, предвыборкой и спекулятивным исполнением, можно хорошо выиграть, если доступно написать системе, что мы от нее хотим.
                                        0
                                        Про это и речь, только если Вы внимательно читали мою статью, я как раз говорю наоборот:
                                        Результат будет один и тот же, а вот выполняться второй пример будет ЗНАЧИТЕЛЬНО дольше по времени, и ЗНАЧИТЕЛЬНО раздует код.


                                        Предположу, что Вы допустили опечататку.
                                  +1
                                  Мне кажется, что вы что-то выдумываете. Различие между "||" и "|" в том, что "||" это логическая операция сравнения ((оп1) || (оп2)) в которой оп1 и оп2 рассматриваются как true/false, а "|" — это побитовая операция ((оп1) | (оп2)) между операндами оп1 и оп2, результат которой уже оценивается как true/false.
                                    0
                                    Собственно это 2 совершенно разных выражения, а не варианты написания условий. И компилируются они совсем по разному.

                                    if(...)
                                    {
                                    mask+=phase;
                                    }
                                    else
                                    {
                                    mask=1;
                                    phase=0;
                                    }

                                    if((phase == 1) || (mask < 3))
                                    00002A 2F10 MOV R17,R16
                                    00002C 3001 CPI R16,0x01
                                    00002E F019 BREQ 0x36
                                    000030 8100 LD R16,Z
                                    000032 3003 CPI R16,0x03
                                    000034 F420 BRCC 0x3E
                                    mask+=phase;
                                    000036 8100 LD R16,Z
                                    000038 0F01 ADD R16,R17
                                    00003A 8300 ST Z,R16
                                    00003C C004 RJMP 0x046
                                    mask=1;
                                    00003E E001 LDI R16,0x01
                                    000040 8300 ST Z,R16
                                    phase=0;
                                    000042 E000 LDI R16,0x00
                                    000044 8301 STD Z+1,R16
                                    }

                                    if((phase == 1) | (mask < 3))
                                    00002A 2F10 MOV R17,R16
                                    00002C 3001 CPI R16,0x01
                                    00002E F041 BREQ 0x40
                                    000030 8100 LD R16,Z
                                    000032 3003 CPI R16,0x03
                                    000034 F028 BRCS 0x40
                                    mask=1;
                                    000036 E001 LDI R16,0x01
                                    000038 8300 ST Z,R16
                                    phase=0;
                                    00003A E000 LDI R16,0x00
                                    00003C 8301 STD Z+1,R16
                                    00003E C003 RJMP 0x046
                                    mask+=phase;
                                    000040 8100 LD R16,Z
                                    000042 0F01 ADD R16,R17
                                    000044 8300 ST Z,R16
                                    }
                                      0
                                      Главное различие в том, что || — точка следования, а | — не точка следования.
                                        0
                                        И вновь с Вами согласен, но обратите внимание на Ваш код! :)
                                          0
                                          А что не так с моим кодом?
                                            0
                                            Он идентичен в обоих случаях по времени выполнения и размеру.
                                            Загвоздка в том, что результаты обоих сравнений, одного типа.
                                            Но вы уже близко к ответу. :)
                                          0
                                          Это не главное :) Главное то, что это принципиально разные операции. тыц. Обратите внимание на «3.7 Logical Operators» и «3.9 Bitwise Logical Operators»

                                          А для условного перехода это принципиально разные вещи. Как вы видите в моем примере выше если используется || то получив в первой части true не имеет смыслы выполнять вторую, потому что при любом ее значение результат ветвления уже определен. Тогда как при побитовой операции | возможны варианты.
                                            0
                                            Это и означает — точка следования.
                                              0
                                              Это я все к тому, что gbg писал

                                              заменить оператор || на |, его порядок работы будет не определен, и die() может быть вызвано раньше connect().


                                              Тут нету никакого неопределенного порядка. А в самой терминологии, «точка следования» это или нет, я не спорю.
                                                0
                                                Как так нет, когда стандарт не определяет, в каком порядке считать члены выражения или аргументы функции, если они не отделены точками следования или приоритетами операций?
                                                  0
                                                  Вы привели код, замена операторов в котором не очень то и корректна с точки зрения алгоритма.

                                                  Вот вам пример, где вы получите принципиально разное поведение программы.

                                                  if(connect(...) && die(...))
                                                  {
                                                  }

                                                  Если connect(...) возвращает 1, а die(...) возвращает 2.

                                                  в случае "&&" у вас будет и там и там «true», а если заменить на & то побитовая операция 1 & 2 (неважно в каком порядке они выполняться) будет равна 0 (false).
                                                    0
                                                    Замечание интересное, но код приводился для демонстрации того, что в таком случае можно использовать только логический оператор, который является точкой следования.

                                                    Для ясности нужно было конечно же показать, что connect() возвращает int со значениями строго 1 или 0, а die() всегда возвращает int(0).
                                                      0
                                                      Вы как-бы пытаетесь подменить причину и следствие. При использовании разных операторов («логическое или» и «побитовое или») вы принципиально меняете алгоритм работы программы. Если у вас «логическое или» то вариант
                                                      if(connect(...) || die(...))
                                                      {
                                                      оп_xz();
                                                      }
                                                      

                                                      превращается в
                                                      if(connect(...))
                                                      {
                                                      оп_xz();
                                                      }
                                                      else
                                                      {
                                                          if(die(...))
                                                          {
                                                          оп_xz();
                                                          }
                                                      }
                                                      

                                                      А вариант
                                                      if(connect(...) | die(...))
                                                      {
                                                      оп_xz();
                                                      }
                                                      

                                                      в
                                                      int tmp = connect(...) | die(...);
                                                      
                                                      if(tmp != 0)
                                                      {
                                                      оп_xz();
                                                      }
                                                      
                                                        0
                                                        Так, все стоп! Разобрались же уже! )
                                                        Ниже читайте.
                                                          0
                                                          Я и пытаюсь показать, что замена I на II не эквивалентна.
                                                            0
                                                            Мне не дают покоя ваши слова
                                                            Код на C не должен добираться до неопределенного поведения и использовать его.


                                                            В примерах
                                                            if (A==0 || B==1 || C==2) doSomething();
                                                            if (A==0 | B==1 | C==2) doSomething();

                                                            Поведение вполне определенное и даже в вашем примере операция "|" скорее всего будет происходить слева на право.
                                                              0
                                                              Так мои слова про UB, они вот про это:
                                                              if (++phase>3) phase=0; mask=(1<<(phase-1));
                                                              

                                                              Тут phase становится 0, после чего из него вычитают 1 и выполняют сдвиг. Это форменное UB.
                                                                0
                                                                phase объявлена как uint8, беззнаковое размерность 8 бит. 0 — 1 в таком случае будет 0xFF? Вроде ничего страшного не случится :)
                                                                  +1
                                                                  В таком случае, будет int(-1). Даже если там будет 255, в стандарте написано (выдержку из которого я привел выше), что сдвигать на величину, большую чем ширина сдвигаемой переменной в битах — нельзя.
                                                                    0
                                                                    При отрицательном сдвиг не выполняется, да читайте Вы код в конце концов:
                                                                    00000023  LSL     R24		        Logical Shift Left 
                                                                    00000024  ROL     R25		        Rotate Left Through Carry 
                                                                    00000025  DEC     R18		        Decrement 
                                                                    00000026  BRPL    PC-0x03	        Branch if plus 
                                                                    00000027  MOV     R14,R24	        Copy register
                                                                    
                                                                      +1
                                                                      Это в данном конкретном случае, данным конкретным компилятором.

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

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

                                                                      Невозможно гарантировать, что при смене настройки оптимизации этот код не выродится.

                                                                      UB просто не допустимо.
                                                                  +1
                                                                  Если код который вы привели — из проекта, то это crap. Писать в одну строку выражение, означающее
                                                                  if (++phase>3) {
                                                                    phase=0;
                                                                  }
                                                                  mask=(1<<(phase-1));
                                                                  
                                                                  — это не просто плохо, это повод к применению линейки.
                                                                    0
                                                                      0
                                                                      И? Мы ж вроде в ключе одного проекта обсуждаем. Там phase объявлена как uint8.
                                                                        0
                                                                        У автора в комментариях на скриншоте есть ещё более прекрасное:
                                                                        if (++phase<3) mask-=phase; else {mask+=phase; phase=0;}
                                                                        


                                                                        Похоже, это фирменный стиль автора.
                                                                          –1
                                                                          Давайте обойдемся без подобных заявлений, Вы только что обозначились как не читавший статью.
                                                                            +1
                                                                            Ви пr'авда считаете, что меня это сколь-нибудь волнует?

                                                                            Ваше замечание про «условия в одну строку» я видел. Что ж вы всю программу в одну строчку не запихнули, если уже используете по два выражения в одной строке (условие с его веткой, плюс безусловное выражение)? Можно было ещё отступы выкинуть — столько места съедают…

                                                                            В общем, ваши нападки про чтение статьи ничуть не оправдывают говнокод, который вы дали в комментарии выше.
                                                                              0
                                                                              Нападки? Да Ви пг'осто себя пе'геоцениваете :)
                                                                            –1
                                                                            А Вы обидчив… зря.
                                                                          –1
                                                                          Это код не из проекта, это пример.
                                                                          Про линейку вкурсе :)
                                                  0
                                                  Давайте не будем выдумывать, и рассмотрим такой вариант:
                                                  B=0b10;
                                                  if ((A==1) && B ) {} else {}
                                                  if ((A==1) & B ) {} else {}
                                                  

                                                  думаю так проще для понимания.
                                        0
                                        Предлагаю Вам самому понять почему.
                                    0
                                    Возможно ли было избежать ассемблерной вставки на месте обработчика, путем тонкой настройки флагов и компилятора?
                                      0
                                      не возможно, флаги настроены.
                                        0
                                        Ни одного флага, или аттрибута, которые бы заставили компилятор генерировать короткий пролог у обработчика прерывания я не вижу.
                                          0
                                          __attribute__((interrupt)) или вообще __attribute__((naked))
                                            0
                                            Так-то да, но их как раз у автора в исходнике нету, вместо них — кусок на ассемблере.
                                              0
                                              Там используется макрос ISR(), который ставит __attribute__((signal)) по умолчанию (отличается от interrupt тем, что не разрешает прерывания в прологе). И простой или критичный по времени обработчик может иметь смысл написать на асме, как в данном случае, если хочется выжать всё из контроллера.
                                                0
                                                Тут надо конкретный случай смотреть, сможет ли компилятор выдать код того же размера/скорости, или не сможет.
                                                  +1
                                                  С этим сложно поспорить, сильно зависит от компилятора. Я по этому поводу соглашусь с Кнутом: «premature optimization is the root of all evil». Сначала померить/посмотреть objdump, потом уже оптимизировать, если надо.
                                                  0
                                                  Ради интереса написал, отличается от стандартного, как я и говорил ниже, на пару слов, и как Вы сказали — cli и sei
                                                  Максимум для первого и третьего режима получилось 85666 Гц, с учетом сдвига маски через перенос (3 слова). Но и это — уже перебор.
                                                0
                                                в первом варианте:
                                                ISR (TIM0_COMPA_vect, ISR_NOBLOCK)
                                                +2 байта кода,
                                                во втором:
                                                ISR (TIM0_COMPA_vect, ISR_NAKED)
                                                -4
                                                  0
                                                  naked — способ сделать c-compatible символ, пролога-то нет, от слова совсем.
                                                    –1
                                                    Прироста производительности — тоже.
                                          0
                                          А можно код на С, который «плохо» компилировался?
                                            0
                                            Он содержится в комментарии к исходным текстам. А первый вариант не сохранился к сожалению. но он не намного хуже того что приведен.
                                            0
                                            Папа замечаний
                                            1. Утверждение о том, что
                                            if (A==0 || B==1 || C==2) doSomething();
                                            if ((A==0) | (B==1) | (C==2)) doSomething();
                                            дают один и тот же результат, на мой взгляд, спорное. Я не нестолько знаю стандарт описания С, чтобы утверждать, что результат логической операции приводится к одной константе и так будет во всех реализациях, а если принять что всякий не 0 — истина, то последнее выражение может и не дать истины при истинных составляющих.
                                            2. Если фрагмент
                                             if (phase!=6)
                                             { mask = ((phase==1) || (phase==3)) ? (mask+phase+1) : phase; }
                                            else (phase=0; mask=1; }
                                            phase++;
                                            заменить на что-то вроде :
                                             mask=pphase*;
                                             if (pphase==pphaseend) pphase=pphasestart else pphase++;
                                            и создать соответствующий массив и указатель на него, то, скорее всего, никакие оптимизации кода не потребуются и быстродействие существенно поднимется.
                                            Ну и так, между прочим, ассемблерный код в приведенном окне эквивалентен несколько иному коду на С, хотя функционал и идентичен, просто не следует терять точность.
                                              –1
                                              И вновь:
                                              дают один и тот же результат, на мой взгляд, спорное.

                                              Приведенный Вами пример не соответствует тому примеру на который указал я.
                                              При этом я не сторонник таких трюков, но иногда к ним прибегаю при необходимости.
                                              И присоединяйтесь к обсуждению, оно чуть выше в комментариях :)
                                                0
                                                Ну, вообще-то, я взял пример как раз из Вашей статьи.
                                                Наверное, подразумевалось, что Вы на него не указывали? ))

                                                  0
                                                  Ваш код при размещении массива в регистрах (для экономии памяти), для второго режима дал прирост используемой памяти в 120 байт.
                                                    0
                                                    120 байт для хранения 6 элеметов типа целое?
                                                    У Вас по дефолту размер слова 20 байт?
                                                    Мы про Tiny говорим?
                                                    Мне понравилась ТАКАЯ экономия памяти.
                                                    При размещении массива в памяти дополнительный размер не должен превысить 6*(1-2-4) =6-12-24 байта.
                                                      0
                                                      Вы невнимательны, массив в регистрах, память не используется.
                                                      120 — прирост кода при реализации обработки масок с чтением из массива.
                                                      То есть при загонах компилятора с присвоением значений через временные регистры и тп.
                                                        0
                                                        Так вот я и задаю вопрос — какую память мы экономим?
                                                        Насколько я помню, у Tiny есть возможность считывать данные из FLASH памяти через индексный регистр Z.
                                                        Значит должна быть и возможность держать массив констант (а это именно массив констант) в памяти программ и не тратить лишнего.
                                                          0
                                                          И в этом случае Вы получаете как минимум две операции на загрузку значения из памяти в регистр,
                                                          когда из массива регистров, это делается за 1 цикл на значение.
                                                          mov R16, R20 (21,22,23,24,25,26,27)

                                                          В Вашем случае, компилятор сделает загрузку из памяти во временный регистр, потом копирование в R16. А это еще расходы по коду и скорости.

                                                          Да Вы попробуйте, а потом расскажете ))
                                                          Я это уже прошел.
                                                            0
                                                            Попробовал, рассказываю.
                                                            Я уже лет 5 не писал для Tiny, поэтому мог где то накосячить, но вот что получилось
                                                            Ваш вариант:
                                                            cpi r16,0x06  1
                                                            breq newp     1/2
                                                            cpi r16,0x01  1
                                                            breq nextp    1/2
                                                            cpi r16,0x03  1
                                                            breq nextp    1/2
                                                            mov r14,r16   1
                                                            rjmp nextr    2
                                                            newp:
                                                            clr r14       1
                                                            clr 16        1
                                                            nextp:
                                                            add r14,r16   1
                                                            inc r14       1
                                                            nextr:
                                                            inc r16       1
                                                            Итак, считаем — длина модуля 13 слов, количество используемых регистров — 2, время исполнения для 6 — 8 тактов, для 1 — 8 тактов, для 3 — 10 тактов, для остальных — 10 тактов, среднее время 56/6 = 9 тактов (8-10).
                                                            Предлагаемый вариант
                                                            lpm r14,z+     3
                                                            cpi zl,dataend 1
                                                            brne nextr     1/2
                                                            sbiw z,datalen 2
                                                            Считаем еще раз — длина модуля 4 слова + 6 слов массива = 10 слов, количество испльзуемых регистров — 2, время исполнения для 1-5 — 6 тактов, для 6 — 7 тактов среднее время 37/6 = 6 тактов (6-7).
                                                            Вывод — модуль получается компактнее и быстрее. У него есть 1 недостаток — изменение времени исполнения для разных вариантов, что можно устранить вариантом:
                                                            lpm r14,z+        3
                                                            sbiw zl,datalen   1
                                                            cpse zl,datastart 1/3
                                                            adiw z,datalen+1  3
                                                            но он чуть длиннее — 8 тактов всегда.
                                                            Единственно, что смущает — не помню, можно ли использовать регистр z в арифметических операциях. Если нельзя, то придется добавить
                                                            movw r15,zl и 
                                                            movw zl,r15
                                                            и проводить операции с парой (r15,r16), что увеличивает длину модуля на 2 и время исполнения на 2 такта, тогда варианты практически сравниваются по параметрам.
                                                    0
                                                    про пример я имел ввиду выражение:
                                                    а если принять что всякий не 0 — истина, то последнее выражение может и не дать истины при истинных составляющих.
                                                  +1
                                                  За исключением вопроса о точках следования и раннем выходе из условия, второй код — легитимный. В C определено, что операции <, >, == и так далее выдают int(0) и int(1) соответственно.
                                                  C11(ISO/IEC 9899:201x) §6.5.8 Relational operators
                                                  Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false.107) The result has type int.

                                                  Каждый из операторов [...] должен давать 1 в случае истины, и 0 в случае лжи. Тип возвращаемого значения — int
                                                    0
                                                    Прошу прощения за придирку, но в приведенной цитате нет оператора ==.
                                                      0
                                                      Спасибо. Про них написано в 6.5.9 то же самое:
                                                      Each of the operators yields 1 if the specified relation is true and 0 if it is false. The result has type int.
                                                        0
                                                        Вот теперь в точку!
                                                        Результат типа int ;)
                                                        Подведете итог? Без точек следования, потому что с этим разобрались сразу — логика работает быстрее.
                                                          0
                                                          Конечно, логика будет НЕ МЕДЛЕННЕЕ, поскольку отсутствует преобразование результата термов в тип (1,0), которое необходимо во втором варианте.
                                                          Да я в этом и с самого начала не сомневался.
                                                          Поэтому второй вариант изначально выглядел несколько вычурно и я никогда бы его не применил, даже если бы он работал быстрее, чего не наблюдается.
                                                          Битовые операции имеют вполне конкретное назначение и рассматривать их как альтернативу логическим методологически неверно.
                                                          А вот насчет совпадения результатов — разработчики стандарта все предусмотрели, респект и уважуха и им и тем, кто стандарт читает.
                                                          Кстати, интересно, а почему сравнение отдельно описано, а равенство отдельно? Это фича такая?
                                                            0
                                                            Вероятно из-за приоритетов. В AVR ASM тоже рекомендуют не использовать breq, а вычленять бит регистра статуса, хотя по времени — одинаково (не помню где читал, но на англицком).
                                                  +2
                                                  Честно говоря, проект под Attiny13, из которой хочется выжать всё, проще с нуля написать на ассемблере, чем изучать поведение компилятора. Последнее очень полезно в академических целях, но не практических.
                                                  Ассемблер AVR прост как бейсик, день кодинга — и начнёшь на нём разговаривать.
                                                    0
                                                    Так с любым ассемблером.
                                                    Я знаком с разными архитектурами под которые писал на ассемблере, но когда проект выходит за рамки нескольких сот байт, Си становится удобней.
                                                    А конкретно про эту задачу — да, в академических целях ее можно реализовать полностью на ассемблере и это будет удобней.
                                                    Здесь же я преследовал другую идею — получить быстрый ШИМ с заданной конфигурацией, и оставить место для реализации законченного проекта, который замечу, требует много ресурсов. Но это уже не суть данной статьи.

                                                    Я надеюсь, что те кому пригодится мой пример в их разработках, выберут для себя нужный режим, удалят комментарии,
                                                    правильно оформят нужное условие, и с пользой применят в своих разработках, доведя их до состояния завершенности.
                                                    При этом не испытывая трудностей в нехватке памяти и быстродействия.
                                                    А благодарностью мне, послужит полученное ими удовольствие.
                                                    0
                                                    Может я не правильно понял условия, но
                                                    1. я бы включил таймер в режим CTC (сброс при совпадении с OCRA), если он есть в тини, конечно. Ну или хотя бы сбрасывал таймер в начале, иначе буде ждиттер.
                                                    2. Маску рассчитал бы заранее и хранил в ПЗУ.
                                                    Если это перевести в асм, ИМХО, будет быстрее:
                                                    const uint8_t mask[] = {0, 1, 2, 3, 4, 5};
                                                    
                                                    ISR(TIM0_COMPA_vect)
                                                    {
                                                    	static phase = 6;
                                                    	PORTB = mask[--phase];
                                                    	if(!phase) phase = 6;
                                                    }
                                                    
                                                      0
                                                      1. У тини нет CTC, таймер сбрасывается в начале и в конце каждой фазы.
                                                      2. Доступ к ПЗУ медленнее чем к регистрам и к SRAM, а чтение из массива раздувает код.
                                                        0
                                                        Ну тогда из ОЗУ. Я сварщик не настоящий, но, похоже, там для доступа к ОЗУ одна инструкция на два такта: LD Rd, — X // Load Indirect and Pre-Dec. Затем проверить младшую часть пары X на ноль (команда сравнения + перехода), еще тактов 3-6.
                                                        0
                                                        Вот пример:

                                                        volatile uint8_t* current_mask; 
                                                        current_mask = (uint8_t *)0x0A; current_mask[0]=0; // R10 
                                                        current_mask[1]=0b001;current_mask[2]=0b011;current_mask[3]=0b010;current_mask[4]=0b110;current_mask[5]=0b100;current_mask[6]=0b101;
                                                        


                                                        Массив в регистрах с R10 по R16
                                                        0
                                                        Ну если мигать светодиодами, то лучше BAM за юзать. Он и менее напряжный для МК и проще для реализации.
                                                          0
                                                          Бесспорно, но это не мигать светодиодами :)
                                                          Сказанное в статье на эту тему, было попыткой не отталкивать новичков.
                                                          0
                                                          Извиняюсь что не по теме, но вижу тут много хороших специалистов, которые, возможно, увидят мой пост и смогут помочь…

                                                          Я несколько лет работаю с микроконтроллерами AtMega и заметил, что при продолжительной работе их начинает «глючить». Либо что-то я не правильно делаю.
                                                          Вот такой пример. Пару месяцев назад собрал реле времени для бойлера нагрева воды (микроконтролер AtMega 16). Включает бойлер на 2 часа в 5 часов вечера и 5 утра.

                                                          cs622028.vk.me/v622028863/b429/4pqbA4ND30g.jpg
                                                          cs622028.vk.me/v622028863/b417/E34uBXfpNeg.jpg

                                                          2 кнопки сбоку- установка времени, большая красная кнопка- дополнительно включить бойлер на 2 часа. Ну не важно. Устройство работает нормально 2 дня, неделю, 2 недели (вроде бы), а если более (через месяц) его начинает глючить. Время показывает правильно, но устройство перестает реагировать на кнопки. (обработка кнопок- самый простой вариант в цикле, через _delayms() (без прерываний, и не хотел дополн. переменные писать).

                                                          Проверял с большой красной кнопкой (должен загореться желтый светодиод и замыкается реле (которое бойлер запустит) к тому же на экран выводится дополнительная строка таймера обратного отсчета (сколько осталось до выключения)). Бывает нажмешь ее раз в месяц- реле включается, светодиод горит, но на экран строка не выводится. Бойлер через 2 часа не выключается. При повторном ее нажатии- должно отключить дополнительное время и вернутся в нормальное состояние- не реагирует. Или вчера нажимаю- выводит на экран отсчет времени не 2 часа, а 7 часов, n минут…

                                                          Как только перезапускаю устройство- работает нормально…

                                                          Переполнений памяти в принципе быть не может…

                                                          На всякий случай о питании…
                                                          Блок питания- импульсный (от DVD проигрывателя, +5в, +12в) +стабилизатор на 5в от линии 5в (хотя там 6в).
                                                          Питание реле- от 12 вольт этого БП. Запускаю через транзистор кт972а.

                                                          Код (если нужен) pastebin.com/Qb2t7fwH

                                                          В чем может быть проблема? В коде- вряд ли. Несколько дней работает без проблем, потом глючит…
                                                            0
                                                            Проверьте питание в первую очередь. Стабилизатор на 5v линейный? Или buck-boost? И в том, и в другом случае надо смотреть допустимые параметры работы стабилизатора (минимальное напряжение/падение на стабилизаторе), допустимые температуры.

                                                            Также стоит посмотреть, хватает ли вам тиков, не проскакиваете ли вы seconds == 0.

                                                            Кроме того, можно настроить wdt, чтобы перезапускать контроллер автоматически.
                                                              –1
                                                              … и вообще это называется «отладка», самая затратная по времени часть, которую заочно провести не получится. Это как лечить по телефону.
                                                                0
                                                                Со временем нету проблем, идут хорошо.

                                                                Стабилизатор вроде как линейный. Получает 6.2в, выдает 5в и дальше на конденсатор…
                                                                Реле висит на другой линии питания…
                                                                  0
                                                                  Ищите даташит на стабилизатор и смотрите параметры. Если он не LDO (обычный 7805, например), то с питанием уже печаль. Обычное падение на линейных стабилизаторах от 1.2 до 1.7 v.
                                                                +1
                                                                Ни одна из переменных, которые задействованы в таймере (обработчике прерываний) не объявлена volatile. Возможны любые глюки.
                                                                  0
                                                                  Такое поведение очень похоже на утечку памяти. Поскольку логика программы довольно жесткая и линейная, большое количество сбоев с переполнением стека или порчей переменных в памяти остается незамеченной. Тем проблема и трудна к обнаружению. Тут потенциально помог бы отладчик, хотябы выяснить где крутится программа во время сбоя, состояние стека и всех переменных… но ведь это накладно держать контроллер под отладчиком месяцами.
                                                                    0
                                                                    Месяцами не обязательно, у автора включен делитель таймера на 1024, можно ускорить.
                                                                      0
                                                                      Это нарушит чистоту эксперимента, вдруг проблема возникает только при заданном делителе? Конечно, проблему в самом прерывании так можно выявить, но для полной уверенности в отсутствии проблем отладку проводить надо в поле при боевых настройках.
                                                                      0
                                                                      Устройство работает месяцами и вовремя включает и отключает бойлер. С этим проблем нету. Сбоев времени не было ни разу. Странно что потом на кнопки криво реагирует.
                                                                      Подебажить было бы интересно конечно…
                                                                        0
                                                                        Это отлично, но судя по этому:
                                                                        	if(StartTime > EndTime)	{
                                                                        		CalcTime = (86400 - StartTime) + EndTime;
                                                                        	}
                                                                        

                                                                        И Вашим словам:
                                                                        Включает бойлер на 2 часа в 5 часов вечера и 5 утра.

                                                                        Где из этого:
                                                                        	int StartTime= (StartH*3600)+(StartM*60)+StartS;
                                                                        	int EndTime= (EndH*3600)+(EndM*60)+EndS;
                                                                        

                                                                        Следует, что 17*3600=61200 = (int)-28443, а 86400 = (int) -20865
                                                                        После чего Вы полученные значения преобразуете в отрицательные часы, минуты и секунды,
                                                                        а потом вызываете вывод их на дисплей, честно говоря — не знаю как себя поведет весь остальной код.
                                                                        ЗЫ: возможно в переводе на единицу ошибся, тем не менее, попробуйте проверить тут.
                                                                          0
                                                                          Да я как-то нарушил последовательность извлечения регистров из стека при выходе из прерывания — год никаких проблем не было, пока не взял этот кусок кода для другого проекта и заметил очевидное несоответствие.
                                                                          тут ведь понимаешь, работает теория вероятности по принципу «куда бог пошлёт». Вот видимо на ячейки где отсчитывается текущее время он пока еще не посылал…
                                                                        0
                                                                        Не нравится мне вот этот момент:
                                                                        	int CalcTime;
                                                                        	if(StartTime > EndTime)
                                                                        	{
                                                                        		CalcTime = (86400 - StartTime) + EndTime;
                                                                        	}
                                                                        	else
                                                                        	{
                                                                        		CalcTime = EndTime - StartTime;
                                                                        	}
                                                                        
                                                                        
                                                                          0
                                                                          Не помню зачем я это делал, но видимо вместо того, чтобы сделать проверку if(Hours>23)Hours-=23 я написал такое) (я вроде как отказался от этого, но почему-то тут оставил)

                                                                          Вы выделили этот кусок кода, но упустили самый интересный:

                                                                          int StartTime= (StartH*3600)+(StartM*60)+StartS;
                                                                          int EndTime= (EndH*3600)+(EndM*60)+EndS;
                                                                          int CalcTime;
                                                                          


                                                                          Помойму это очень грубо.
                                                                            +1
                                                                            А теперь прикиньте, какая разрядность у Ваших операций, где Вы используете int (16bit max=65535)
                                                                              +1
                                                                              сплю почти, не успел: INT — знаковый -32767...+32767
                                                                              То есть ошибка, скорей всего — погрешности Ваших вычислений времени.
                                                                              Используйте другие типы для этих переменных.
                                                                              Ну и советы выше — не навредят.
                                                                          0
                                                                          Если совсем будет бесить эта глюконутость и будет время- возможно перенесу на ПЛИС. На каком-то сраном Max2 сделаю)

                                                                          Пару месяцев написал AVR микроконтроллер на плисе. Правда программу пришлось на ассемблере писать. Если бы эту программу на ассемблер переписать (сложнее всего будет с драйвером экранчика) и сравнить работу меги и моего мк) Правда памяти не хватит на эту программу… Нужно плис менять… (про внешнюю память не напоминать)).
                                                                          0
                                                                          Прошу прощения за возможно глупый вопрос, но я вставил этот код как он есть ( с раскомментированной строкой) в arduino ide, он скомпилировался и загрузился в тиньку, но все порты в единичку и ничего не происходит. Почему так? АВРСтудии под рукой просто нет :\
                                                                            0
                                                                            А как он туда загрузился без загрузчика? и какой, собственно, код?
                                                                              0
                                                                              Код который в 3x_Soft_PWM.cpp в конце статьи. Загружается посредством программатора USBasp
                                                                                0
                                                                                Ещё такой момент — таймеры по переполнению не срабатывают. Попробовал AVR Студию, зашивал через AVRDUDEPROG — тоже самое (фьюзы dl.dropboxusercontent.com/u/13249224/2015-12-31%20%284%29.png)
                                                                                Разве может быть такое что в МК работает все кроме таймера по переполнению?
                                                                                  0
                                                                                  Что такое таймер по переполнению? неработать он может только если забыли включить обработку этого переполнения, неправильно прописать вектор прерывания(при компиляции указали не тот камень в который прошивали) или вообще забыть разрешить прерывания глобально.
                                                                                    0
                                                                                    Прошу прощения — про таймеры можно временно забыть — я ещё икспириминтирую :)
                                                                                    И по коду статьи — никаких действий она не производит, хотя должно быть «настольной мигалкой».
                                                                              0
                                                                              Не понял какая строка раскомментирована, та что с задержкой для тестов?
                                                                              Частота слишком высокая, попробуйте испытать в «протеусе», с картинкой на осцилографе.

                                                                              Не уверен что в ардуинке заведется, впрочем не проверял.
                                                                                0
                                                                                от ардуины только ide. в аврстудии тоже самое. А вот это из протеуса. И это неизменно. Только желтая линия по миллиметру в год сползает вниз, но не в стороны.
                                                                                  0
                                                                                  Вновь не понял, сформулируйте чего вы хотите достичь, судя по картинке шота из протеуса, код работает так же как у меня. Вы хотите изменение скважности? Ну то есть чтобы можно было увеличивать-уменьшать яркость например?
                                                                                  Если так то это не тот проект.
                                                                                    0
                                                                                    В смысле, опишите задачу, я Вам помогу с исходником, который Вам нужен.
                                                                                      0
                                                                                      В данном случае хотел поглазеть на моргалку описанную в PS для новичков и потыкать код. Но ничего не выходит — все как в протеусе. Проверял в железе, с разной оптимизацией и без оной.
                                                                                      Ну и да, хотелось бы немного понять про изменение яркости.
                                                                                        0
                                                                                        Я понял, изменю исходник и скину сюда в комменты, немного подождать, скажем — до завтра ;)
                                                                                          +1
                                                                                          Вот вариант с мигалкой, для 4.8 MHz (изменить в настройках МК в протеусе)
                                                                                          На 9.6 MHz тоже заведется, только мигать будет быстрее.

                                                                                          Нашел быстрее чем думал, успел уже отправить в дальний угол :)
                                                                                        0
                                                                                        У вас канал А настроен на переменное напряжение, потому и сползает вниз — включена развязка входа по постоянному току.

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