Из того что вряд ли понадобится, но интересно узнать. Странные результаты наблюдений обнаруживаются порой при изучении предельных возможностей МК. Например, измерял при помощи анализатора максимальную частоту с которой может переключаться пин на примере пина 13 Меги2560. Сперва при помощи конструкции их стандартных функций digitalWrite(13,!digitalRead(13));. Период получился ожидаемые примерно 14us, частота 35КГц. При использовании переменной (убрав чтение порта) результат улучшился почти вдвое: 8.4us. Далее при помощи вот такой нехитрой программки в цикле: Код (C++): PORTB = PINB ^ (1<<7); PORTB = PINB ^ (1<<7); PORTB = PINB ^ (1<<7); _delay_us(5); три раза подряд чтобы отличить разницу между потоком и итерацией. Вот тут начались странности. Внутри цикла переключалось не всегда! Примерно через раз. Пробовал сериями по 8, срабатывало 4 раза. Через итерацию цикла все срабатывает, с периодом 1us (без delay, разумеется), а подряд - пропуски. Вот, например, картина из 8 переключений: Затем я попробовал не совсем легальный способ: Код (C++): PORTB = PORTB ^ (1<<7); PORTB = PORTB ^ (1<<7); PORTB = PORTB ^ (1<<7); _delay_us(5); и вы знаете, все заработало очень четко, без сбоев внутри цикла. Период 0.18us, частота около 3МГц. Ни единого пропуска! Отсюда неожиданный, но логичный вопрос: информация о реальном состоянии пинов (как минимум находящихся в режиме вывода) находится таки в регистре PORT и передается в регистр PIN с некоторой задержкой? Или что? Пробовал смотреть двумя разными анализаторами, один из них на канал обещает 100МГц, результат един. При сомнении и желании перепроверить. подтвердить или опровергнуть этот факт может любой желающий.
Значение регистра PINB устанавливается в соответствии с уровнем пина минимум через один такт. А в соответствии с первым примером в этом такте производится чтение из регистра PINB, который хранит предыдущее состояние. Именно поэтому и происходит: Вот только скорее "не примерно", а точно через раз. Попробуйте следующий код: Код (C++): PORTB = PINB ^ (1<<7); asm ("nop"); PORTB = PINB ^ (1<<7); asm ("nop"); PORTB = PINB ^ (1<<7); asm ("nop"); Что касается: С чего Вы взяли, что это не легальный способ в этом конкретном случае (переключение состояния пина)? В Вашем случае из вне на пин никаких сигналов не подаётся поэтому состояние пина можно узнать и по значению регистра PORTB. Есть ещё одна инересная фишка. Правда сам не проверял. Если верить документации, переключать состояние пина можно следующей командой: Код (C++): PINB = (1<<7); Т.е. каждый раз когда в PINB записывается единица в определённый бит, в регистре PORTB соответствующий бит меняет своё значение. Делать это можно только с одним битом одновременно.
Куда поместить? PINx -- это входной регистр, он показывает состояние пинов (которые обслуживает регистр PINx). В его биты можно поместить единички, если подать логические единицы на входы соответствующих пинов. Но программно записать скорее нельзя. PORTx -- выходной регистр. Так понимаю, Вы знаете, что будет, если в его биты записывать единички. То что я привёл про PINx -- это документированное, но скорее не стандартное использование регистра PINx, которое позволяет быстро менять состояние пина.
Попробовал. Код (C++): PINB = (1<<7); Скорость срабатывания действительная увеличилась в 2,5 раза, частота поднялась с 3 до 8МГц. Я думал что 3МГц это уже предел для пина на этой конфигурации. Нефига себе blink...
Очень похоже, что это действительно предел для данного МК на данной тактовой частоте. Если верить моему простому осциллографу, сигнал между переключениями даже не успевает выйти на номинальное напряжение. Семь переключений: Восемь на другом простом осциллографе:
DSO 203, он же Quad. Параметры все на экране. На втором Hantek 6022BE. Для них это тоже предел, похоже.
Улучшить уже некуда, про то и речь. Да и не нужно пока. Задача чисто теоретическая - выжать максимум возможно ли.
Пожалуй максимально минимальный секундный blink для Меги на IDE (702 байта): Код (C++): void setup() { DDRB = 0xFF; TCCR1A = 0; TCCR1B = 0b00001101; OCR1A = 0x3D08; TIMSK1 = 0b0000110; } void loop() { } ISR(TIMER1_COMPA_vect) { PINB = (1 << 7); }
Понятно. С этими двумя вы на такой частоте ничего лучше недосинуса и не увидите, нужен аппарат хотя бы на порядок быстрее, а лучше на 1.5-2.