Может кто-нибудь объяснить этот странный код, используемый для настройки таймеров?

При взгляде на эскизы, написанные другими людьми, я иногда нахожу код, который выглядит примерно так:

TCCR1A = 0;
TCCR1B = 0;

TCNT1 = 34286;
TCCR1B |= (1 << CS12);
TIMSK1 |= (1 << TOIE1);

Все, что я знаю, это то, что есть с таймингами /таймерами (я думаю). Как я могу расшифровать и создать такой код? Что такое TCCR1A, TCCR1B, TCNT1, CS12, TIMSK1 и TOIE1

8 голосов | спросил The Guy with The Hat 24 FebruaryEurope/MoscowbMon, 24 Feb 2014 06:06:06 +0400000000amMon, 24 Feb 2014 06:06:06 +040014 2014, 06:06:06

2 ответа


13

Это не странно выглядит. Это нормальный код MCU на самом деле выглядит.

Что вы здесь видите, это пример концепции памяти с отображением периферийных устройств . В основном, аппаратное обеспечение MCU имеет специальные местоположения в адресном пространстве SRAM назначенного ему MCU. Если вы пишете на эти адреса, биты байта, записанные на адрес n , управляют поведением периферийного устройства m .

В принципе, некоторые банки памяти буквально имеют мало проводов, идущих от ячейки SRAM к аппаратным средствам. Если вы пишете «1» на этот бит в этом байте, он устанавливает эту ячейку SRAM в логический максимум, а затем включает некоторую часть аппаратного обеспечения.

Если вы заглянете в заголовки для MCU, есть большие большие таблицы сопоставлений ключевых слов и <->. Вот как такие вещи, как TCCR1B и т. Д. ... разрешаются во время компиляции.

Этот механизм отображения памяти чрезвычайно широко используется в MCU. ATmega MCU в ардуине использует его, как и PIC, ARM, MSP430, STM32 и STM8 серии MCU, а также множество MCU, с которыми я не знаком.


Код Arduino - это странный материал с функциями, которые косвенно контролируют регистры управления MCU. Хотя это несколько «приятнее», он также намного медленнее и использует намного больше пространства программ.

Таинственные константы описаны подробно в ATmega328P datasheet , который вы действительно должны прочитать, если вы заинтересованы в том, чтобы делать что-то более, чем заодно переключать контакты на arduino.

Выберите выдержки из приведенного выше таблицы данных:

введите описание изображения здесь

Итак, например, TIMSK1 |= (1 << TOIE1); устанавливает бит TOIE1 в TIMSK1. Это достигается путем смещения бинарного 1 (0b00000001) влево с помощью бит TOIE1, причем TOIE1 определяется в файле заголовка как 0. Это затем побитовое ИЛИ вводится в текущее значение TIMSK1, которое эффективно устанавливает этот бит бит.

Глядя на документацию для бит 0 из TIMSK1, мы видим, что он описывается как

  

Когда этот бит записывается в один, а I-флаг в регистре состояния   (прерывания глобально включены), переполнение таймера /счетчика1   прерывание включено. Соответствующий вектор прерывания (см.   «Прерывания» на стр. 57) выполняется, когда флаг TOV1, расположенный в   TIFR1.

Все остальные строки должны интерпретироваться одинаково.


Некоторые примечания:

Вы также можете видеть такие вещи, как TIMSK1 |= _BV(TOIE1);. _BV() является обычно используемым макрос , первоначально из реализации AVR libc . _BV(TOIE1) функционально идентичен (1 << TOIE1), с лучшей читабельностью.

Кроме того, вы также можете видеть строки, такие как: TIMSK1 &= ~(1 << TOIE1); или TIMSK1 &= ~_BV(TOIE1);. Это имеет противоположную функцию TIMSK1 |= _BV(TOIE1);, в которой unsets бит TOIE1 в TIMSK1. Это достигается с помощью бит-маски, созданной _BV(TOIE1), выполняющей побитовое НЕ-операцию (~), а затем ANDing TIMSK1 этим значением NOTED (которое равно 0b11111110).

Обратите внимание, что во всех этих случаях значение таких вещей, как (1 << TOIE1) или _BV(TOIE1) полностью разрешены в время компиляции , поэтому они функционально сводятся к простой константе и, следовательно, не выполняют время выполнения для вычисления на во время выполнения.


Правильно написанный код обычно имеет встроенные комментарии с кодом, который детализирует то, что назначают регистры. Вот довольно простая программа soft-SPI, которую я написал недавно:

uint8_t transactByteADC(uint8_t outByte)
{
    // Transfers one byte to the ADC, and receives one byte at the same time
    // does nothing with the chip-select
    // MSB first, data clocked on the rising edge

    uint8_t loopCnt;
    uint8_t retDat = 0;

    for (loopCnt = 0; loopCnt < 8; loopCnt++)
    {
        if (outByte & 0x80)         // if current bit is high
            PORTC |= _BV(ADC_MOSI);     // set data line
        else
            PORTC &= ~(_BV(ADC_MOSI));  // else unset it

        outByte <<= 1;              // and shift the output data over for the next iteration
        retDat <<= 1;               // shift over the data read back

        PORTC |= _BV(ADC_SCK);          // Set the clock high

        if (PINC & _BV(ADC_MISO))       // sample the input line
            retDat |= 0x01;         // and set the bit in the retval if the input is high

        PORTC &= ~(_BV(ADC_SCK));       // set clock low
    }
    return retDat;
}

PORTC - это регистр, который управляет значением выходных контактов в PORTC для ATmega328P. PINC - это регистр, в котором доступны значения input PORTC. По сути, такие вещи происходят внутри, когда вы используете функции digitalWrite или digitalRead. Тем не менее, есть поисковая операция, которая преобразует «номера контактов» arduino в фактические номера аппаратных выводов, которые занимают место в области 50 тактов. Как вы, вероятно, можете догадаться, если вы пытаетесь идти быстро, потратив 50 тактовых циклов на операцию, которая требует только 1, это немного смешно.

Вышеуказанная функция, вероятно, занимает где-то в области 100-200 тактов для передачи 8 бит. Это влечет за собой 24-выводные записи и 8-ти чтения. Это намного, во много раз быстрее, чем с помощью функций digital{stuff}.

ответил Connor Wolf 24 FebruaryEurope/MoscowbMon, 24 Feb 2014 07:54:06 +0400000000amMon, 24 Feb 2014 07:54:06 +040014 2014, 07:54:06
2

TCCR1A - таймер /счетчик 1 регистр управления A

TCCR1B - таймер /счетчик 1 управляющий регистр B

TCNT1 - значение счетчика счетчика /счетчика 1

CS12 - бит четвертого такта для таймера /счетчика 1

TIMSK1 - регистр маски прерываний таймера /счетчика

TOIE1 - это разрешение прерывания по таймеру /счетчику 1

Таким образом, код включает таймер /счетчик 1 на частоте 62,5 кГц и устанавливает значение 34286. Затем он разрешает прерывание переполнения, поэтому, когда оно достигает 65535, оно вызывает функцию прерывания, скорее всего обозначаемую как ISR(timer0_overflow_vect)

ответил TheDoctor 24 FebruaryEurope/MoscowbMon, 24 Feb 2014 07:07:29 +0400000000amMon, 24 Feb 2014 07:07:29 +040014 2014, 07:07:29

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132