Инфракрасная низкочастотная передача

У меня есть некоторый базовый код, который я скоро расширю для своего проекта, представляющего собой универсальный пульт дистанционного управления для кондиционеров. У меня есть все коды, но проблема в том, что 3 кода ниже 1 кГц (500 Гц). Мой скетч не допускает снижения чего-либо, и частота в реальном коде, похоже, не настолько низкая. 108 - это 38 кГц.

uint16_t fuck[] = {0,108,0,0,347,173,22,22,22,22,22,22,22,65,22,65,22,65,22,22,22,22,22,65,22,65,22,65,22,22,22,22,22,22,22,65,22,65,22,22,22,65,22,22,22,22,22,65,22,22,22,22,22,22,22,65,22,22,22,65,22,65,22,22,22,65,22,65,22,65,22,1527,347,87,22,3692};
static uint16_t sent_array_numbers;
static uint8_t ir_seq_index = 4;
static uint8_t pin_state;

void ir_on()
{
  TCCR1A |= 192;
  pin_state = 1;
}

void ir_off()
{
  TCCR1A &= -193;
  pin_state = 0;
}

void ir_toggle()
{
  if (pin_state)
    ir_off();
  else
    ir_on();
}

void ir_start()
{
  DDRB |= 10;
  ICR1 = 416;
  OCR1A = 104;
  TCCR1A = 2;
  TCCR1B = 24;
  TIMSK1 = 1;
  ir_on();
  TCCR1B |= 1;
}


ISR(TIMER1_OVF_vect) {

  sent_array_numbers++;

  if (sent_array_numbers == fuck[ir_seq_index]) {
    ir_toggle();
    sent_array_numbers = 0;
    ir_seq_index++;



       }
}

void setup() {
  ir_start();
}

void loop() {
  
}

, 👍0

Обсуждение

в чем заключается ваш вопрос?, @jsotola

что работает на частоте 500 Гц, и как вы это определили?, @jsotola

Вам нечего сбрасывать ir_seq_index, поэтому, как только вы пройдете через массив, он все равно будет увеличиваться. Тогда вы получите мусор, так как вы читаете извне массива., @chrisl

Если вы хотите, чтобы таймер работал медленнее, вы можете изменить его настройки, например, увеличить предскалер, @chrisl

Откуда вы взяли этот код? Кроме того, массив для ИК-кода выглядит очень странно. В нем не должно быть никаких нулей. Кроме того, большинство ИК-кодов начинаются с длинного импульса, в то время как ваш вид * заканчивается * на * два *., @Gerben

@jsotola Я не могу снизиться до 500 Гц, как бы мне решить эту проблему? И я определил это, потому что я проверил частоту: O, @Macaroni

@chrisl это пример кода для получения концепции, а не самого проекта. Кроме того, что вы подразумеваете под прескалером?, @Macaroni

@Gerben некоторые части ик-кодов на самом деле не требуются, некоторые ик-спамеры фактически вырезают половину кода и работают нормально. Кроме того, код является реальным ИК-кодом. Фактические отправленные микросекунды. Это просто prontohex, преобразованный в uint16 (uint16, похоже, только рабочий). Это для того, чтобы мне не приходилось тратить время и пространство на декодирование pronto hex обратно в uint. Когда я могу просто использовать меньше памяти и т.д. и отправлять реальный код., @Macaroni


1 ответ


Лучший ответ:

1

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

Давайте посмотрим на ir_start(), где вы настраиваете Timer1 для операции IR:

void ir_start()
{
  DDRB |= 10;
  ICR1 = 416;
  OCR1A = 104;
  TCCR1A = 2;
  TCCR1B = 24;
  TIMSK1 = 1;
  ir_on();
  TCCR1B |= 1;
}

Во-первых, вам лучше не использовать здесь десятичное представление чисел. Большинство из них состоят из одного или нескольких битов, которые привязаны к определенному параметру конфигурации. Вы можете лучше видеть, что происходит, когда используете шестнадцатеричный или лучше двоичный код. Таким образом, вместо DDRB |= 10; вы бы написали DDRB |= 0b1010; (0b обозначает двоичное представление, а 1010 - двоичное представление 10). Это облегчает чтение другим людям или вам, когда вы оставляете этот код на некоторое время и возвращаетесь к нему. Использование десятичной дроби хорошо в тех случаях, когда вы пишете регистры, представляющие одно значение (например, ICR1).

Теперь давайте пройдемся по каждой строке. Здесь я только укажу, что делает строка, а не если это имеет смысл или если это правильно.

DDRB |= 10;

равно DDRB |= 0b1010 и установит настройку PB1 и PB3 (контакты 9 и 11 на Uno) в качестве выходных контактов.

ICR1 = 416;

устанавливает регистр захвата ввода 1, который - когда он не находится в режиме захвата ввода - устанавливает ВЕРХНЕЕ значение таймера, то есть максимальное значение, до которого он будет отсчитываться до переполнения.

OCR1A = 104;

устанавливает значение таймера, при котором таймер переключит вывод сравнения вывода A таймера 1 (PB1, он же вывод 9) (конечно, только в том случае, если активирован режим сравнения вывода).

TCCR1A = 2;

эквивалентно TCR1A = 0b00000010. Он отключает режим сравнения выходных данных, тем самым обеспечивая нормальную работу порта. Также бит WGM11 установлен в 1, а WGM10 установлен в 0 (объяснение в следующей строке).

TCCR1B = 24;

равно TCR1B = 0b00011000. Он устанавливает WGM12 и WGM13 равными единицам. Вместе с другими битами последней строки это устанавливает режим таймера на FastPWM, считая до значения в ICR1, сбрасывая его. Кроме того, все биты источника синхронизации установлены на ноль, поэтому отключите таймер.

TIMSK1 = 1;

Это включает прерывание переполнения Timer1 и, конечно, равно TIMSK1 = 0b1.

В ir_on() вы используете

TCCR1A |= 192;

который равен TCR1A |= 0b11000000. Он включает выходное сравнение для OC1A, настроенное для его установки при сопоставлении сравнения и очистки при сбросе (также называемый инвертированным режимом).

TCCR1B |= 1;

установит источник синхронизации таймера на основные часы без какого-либо предварительного скейлинга. Предварительный калькулятор будет масштабировать значения счетчика. Например, с прескалером 8 (установка битов источника синхронизации на 0b010) будет означать, что значение таймера будет увеличиваться на единицу каждые 8 тактовых переходов, а не при каждом переходе. Здесь у вас нет прескалера, поэтому таймер работает на полной скорости. Вы можете сделать это медленнее, изменив прескалер. Затем тонкую настройку можно выполнить, изменив значение ICR1, которое используется как ВЕРХНЕЕ. Установка источника синхронизации также приведет к включению таймера.

Затем в ir_off() вы используете

TCCR1A &= -193;

Десятичное число -193 - это значение, для которого требуется 2 байта в виде целого числа со знаком (двоичное дополнение). Его двоичное значение равно 0b11111111 00111111. Вы присваиваете регистру 1 байт, поэтому старший байт будет отсечен. Таким образом, вы на самом деле используете десятичное значение 63. Это отключит сравнение выходных данных для OC1A, поэтому отключите ШИМ-выход на выводе OC1A.

Итак, подведем итог фактической функции этого кода: таймер настроен на запуск от 0 до 416 без какого-либо предварительного скейлера. При значении 104 устанавливается вывод OC1A, при сбросе/переполнении таймера он сбрасывается. Частота сигнала будет составлять 16 МГц/416 = 38,5 кГц, с рабочим циклом (416 - 104) / 416 = 75%.

Изменив ICR1 и OCR1A, вы можете перейти к 16 МГц / 65535 = 244 Гц. Таким образом, для частоты 500 Гц вы могли бы использовать значение ICR1 32000. Для того же рабочего цикла вам потребуется значение OCR1A, равное 8000.

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

Примечание: Вы используете переполнение таймера в качестве наименьшего показателя времени (поскольку вы увеличиваете sent_array_numbers в ISR). Таким образом, изменяя частоту таймера, вы также изменяете количество времени, которое представляет одно приращение sent_array_numbers . Хотя это время составляет 1/38, 5 кГц = 26,3us при 38,5 кГц, оно будет составлять 1/500 Гц = 2 мс для 500 Гц. Просто имейте это в виду, записывая значения ИК-импульсов в массив.

Вы можете прочитать обо всем этом в техническом описании Atmega328p, которое используется в Arduino Uno.


Примечания, помимо настройки таймера:

  • Убедитесь, что все переменные, которые изменяются внутри ISR, объявлены как изменяемые, чтобы компилятор знал, что переменная может измениться в любое время и что он не может оптимизировать доступ к ней.

  • Убедитесь, что индекс, который вы используете с массивом, не превышает размер массива. В противном случае вы будете читать из того, что было помещено за этим массивом в памяти.

  • Каждая переменная должна иметь подходящее говорящее имя, которое точно говорит о том, что это такое. Его функция может оказаться не столь очевидной, когда вы посмотрите на свой код через несколько лет.

  • Первые 4 значения в массиве с ИК-кодами не используются. Из вашего кода я не могу понять, почему. (Хотя вы предположили, что это не полный код).


Я надеюсь, это поможет. Если нет, то вам нужно быть более точным в своем вопросе. И предоставьте полный компилируемый минимальный пример кода.

,

Я пока не могу его протестировать (Arduino сломался), но, судя по всему, это выглядит правильно. Большое спасибо <3. Я отправлю код, когда смогу снова заставить свой arduino работать :), @Macaroni