Форум РадиоКот https://radiokot.ru/forum/ |
|
STM32 библиотека работы с ИК пультом (NEC) https://radiokot.ru/forum/viewtopic.php?f=59&t=128357 |
Страница 1 из 2 |
Автор: | Ican [ Пн мар 07, 2016 21:26:17 ] | ||
Заголовок сообщения: | STM32 библиотека работы с ИК пультом (NEC) | ||
Здравствуйте, читатели форума:) 13 страниц форума и ни одного вопроса по управлению с пульта ДУ=) Уже много времени хотелось разобраться и найти библиотеку декодирования сигналов NEC пультов. Прочитал теорию, вроде бы всё хорошо и понятно. С помощью программы RCExplorer (кстати, она сейчас бесплатная. Связался с её автором и он через некоторое время сделал её бесплатной) и давным-давно собранного WinLirc убедился что мой пульт использует NEC протокол. Даже не расширенный. НО, найденная библиотека почему-то не работает корректно. Зато, как ни странно, декодирует сигналы протокола SANYO (похож на NEC). Прикладываю "осциллограммы" кнопок пультов. В коде реализуется прием 32 байт. У sanyo их явно больше, но принимаются же!!! а у NEC идет какое-то недополучение (байт 10 не успевают проскочить) Вложение: Вложение: Может посмотрите в чём дело? Прикладываю проект в CooCox. Или поделитесь библиотекой для декодирования NEC протокола? Описание проекта: Используется таймер TIM2, ИК-фотоприемник подключен по типовой схеме, сигнальный вывод к ножке GPIOA0 (TIM2_CH1). К отладочной плате подключен 1602 дисплей по четырёхбитной шине. Добавлю. Принятый код с SANYO на картинке RCExplorer Вложение: Как мы видим, теряются 10 байт. Что делать - не знаю)
|
Автор: | AVI-crak [ Вт мар 08, 2016 01:29:42 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Очень странная библиотека, в своё время у меня не завелась, пришлось стряпать своё. Вся обработка в прерывании по месту события, в последнем правильно принятом бите - вызов задачи декодирования из спячки. Тупой перебор до совпадения. При зажатой кнопке и верности принимаемых таймингов - декодируется повтор. В случае посторонних помех - декодирование срывается но не искажается, то-есть ложных срабатываний - ноль. Чего собственно мне и требовалось. Для иной частоты мк -проще менять TIM2->PSC = 720; в пересчёте процентах от 72мгц. Ось своя собственная,http://forum.ixbt.com/topic.cgi?id=48:11735. Хотя здесь она применяется всего в одном месте sTask_wake(&table_simvol_bits) , можно заменить на свой метод. Но не рекомендую использование без оси - тупой метод перебора жрёт много времени мк. Код: /*
/// таймер TIM2 72мгц - IR , вход CH1 + CH4 TIM2->PSC = 720; TIM2->CCMR1|=TIM_CCMR1_CC1S_0 | TIM_CCMR1_IC2F_3; //TI1FP1+фильтр TIM2->CCMR1 |= (TIM_CCMR1_OC2M_2|TIM_CCMR1_OC2M_1); TIM2->CCMR2|=TIM_CCMR2_CC4S_1 | TIM_CCMR2_IC4F_3; //TI1FP2+фильтр TIM2->CCR2 = 15800 ; // детектор тишины TIM2->SMCR = (5<<4) | 4; //TI1FP1+Reset Mode TIM2->CCER|= TIM_CCER_CC1E | TIM_CCER_CC4P | TIM_CCER_CC4E | TIM_CCER_CC2E; TIM2->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE; // прерывание от захвата и тишины TIM2->CR1 = TIM_CR1_CEN; */ volatile uint32_t data_IR; volatile char *time2_data; _bitFlag table_simvol_bits; _bitFlag time2_data_IR_RE; #define IR_ZERO_L 56 // время передачи нуля void TIM2_IRQHandler (void) { uint16_t tmp_H; uint16_t tmp_L; static uint8_t nomer_bit = 32; static uint8_t status_bit = 0; static uint32_t time2_data_IR; if (TIM2->SR & TIM_SR_CC2IF) { TIM2->SR = 0; nomer_bit = 32;status_bit = 0; return;} else { tmp_L = TIM2-> CCR4; tmp_H = (TIM2->CCR1) - tmp_L; if (status_bit ==0 ) { if ((tmp_H < ((uint16_t)IR_ZERO_L * 1.25)) && (tmp_L > ((uint16_t)IR_ZERO_L * 7.75)) && (tmp_L < ((uint16_t)IR_ZERO_L * 8.25))) { nomer_bit = 32; time2_data_IR =0;status_bit++;}else; return; }else; if (status_bit == 1 ) { if (tmp_H < ((uint16_t)IR_ZERO_L * 1.25)) { if (tmp_L < ((uint16_t)IR_ZERO_L * 1.25)) nomer_bit--; else { if (tmp_L < ((uint16_t)IR_ZERO_L * 3.25)) { nomer_bit--; time2_data_IR |= (uint32_t) 1 << nomer_bit;} else; } } else status_bit = 0; if (nomer_bit == 0) {data_IR = time2_data_IR; sTask_wake(&table_simvol_bits); status_bit++; return;} else; }else; if (status_bit == 2 ) // H903-L4000; (H56-L224; H900-L9627;) // (H900-L4000; H56-L228) lg { if ((tmp_H > ((uint16_t)IR_ZERO_L * 15.75)) && (tmp_L > ((uint16_t)IR_ZERO_L * 69)) && (tmp_L < ((uint16_t)IR_ZERO_L * 73))) status_bit++; else status_bit = 0; return; }else; if (status_bit == 3 ) { if ((tmp_H < ((uint16_t)IR_ZERO_L * 1.25)) && (tmp_L > ((uint16_t)IR_ZERO_L * 3.75)) && (tmp_L < ((uint16_t)IR_ZERO_L * 4.25))) status_bit++; else status_bit = 0; return; }else; if (status_bit == 4 ) { if ((tmp_H > ((uint16_t)IR_ZERO_L * 15.75)) && (tmp_L > ((uint16_t)IR_ZERO_L * 166)) && (tmp_L < ((uint16_t)IR_ZERO_L * 176))) {status_bit = 3; time2_data_IR_RE = 1;} else status_bit = 0; return; }else; } } void table_simvol (void) { for(;;) { if (data_IR == 0) {time2_data = ".........."; } else; if (( data_IR >> 16) == 0x00FF) // пульт от плеера { data_IR = data_IR & 0xFFFF; if (data_IR == 0x38c7) {time2_data = "POWER"; } else; if (data_IR == 0xd22d) {time2_data = "LCD_ON/OF"; } else; if (data_IR == 0xda25) {time2_data = "MODE"; } else; if (data_IR == 0x20df) {time2_data = "3D"; } else; if (data_IR == 0x5aa5) {time2_data = "PROG"; } else; if (data_IR == 0x50af) {time2_data = "MITE"; } else; if (data_IR == 0x00ff) {time2_data = "REPEAT"; } else; if (data_IR == 0x08f7) {time2_data = "A-B"; } else; if (data_IR == 0xa25d) {time2_data = "1"; } else; if (data_IR == 0xe817) {time2_data = "2"; } else; if (data_IR == 0x48b7) {time2_data = "3"; } else; if (data_IR == 0xb847) {time2_data = "TITLE"; } else; if (data_IR == 0x28d7) {time2_data = "4"; } else; if (data_IR == 0xe01f) {time2_data = "5"; } else; if (data_IR == 0xb04f) {time2_data = "6"; } else; if (data_IR == 0x1ae5) {time2_data = "ANGLE"; } else; if (data_IR == 0xd827) {time2_data = "7"; } else; if (data_IR == 0x926d) {time2_data = "8"; } else; if (data_IR == 0x22dd) {time2_data = "9"; } else; if (data_IR == 0x3ac5) {time2_data = "SUBTITLE"; } else; if (data_IR == 0x9867) {time2_data = "+10"; } else; if (data_IR == 0x7887) {time2_data = "0"; } else; if (data_IR == 0x7a85) {time2_data = "MENU"; } else; if (data_IR == 0xc837) {time2_data = "OSD"; } else; if (data_IR == 0xf00f) {time2_data = "ZOOM"; } else; if (data_IR == 0x728d) {time2_data = "CH+"; } else; if (data_IR == 0x629d) {time2_data = "PBC"; } else; if (data_IR == 0x9a65) {time2_data = "SLOW"; } else; if (data_IR == 0x30cf) {time2_data = "<<-"; } else; if (data_IR == 0x609f) {time2_data = "ENTER"; } else; if (data_IR == 0xa05f) {time2_data = "->>"; } else; if (data_IR == 0xc23d) {time2_data = "RETURN"; } else; if (data_IR == 0xf20d) {time2_data = "AUDIO"; } else; if (data_IR == 0xb24d) {time2_data = "CH-"; } else; if (data_IR == 0x32cd) {time2_data = "GOTO"; } else; if (data_IR == 0xe21d) {time2_data = "SETUP"; } else; if (data_IR == 0x10ef) {time2_data = "VOL+"; } else; if (data_IR == 0x42bd) {time2_data = "<<"; } else; if (data_IR == 0x02fd) {time2_data = ">>"; } else; if (data_IR == 0xc03f) {time2_data = "STOP"; } else; if (data_IR == 0x0af5) {time2_data = "VOL-"; } else; if (data_IR == 0x807f) {time2_data = "|<<"; } else; if (data_IR == 0x40bf) {time2_data = ">>|"; } else; if (data_IR == 0x8877) {time2_data = "PLEY"; } else; }else; if (( data_IR >> 16) == 0x20DF) // пульт от телека LG { data_IR = data_IR & 0xFFFF; if (data_IR == 0x10ef) {time2_data = "POWER"; } else; if (data_IR == 0x0ff0) {time2_data = "TV/RADIO"; } else; if (data_IR == 0x9c63) {time2_data = "SUBTITLE"; } else; if (data_IR == 0x06f9) {time2_data = "AD(PIP/*)"; } else; if (data_IR == 0xd926) {time2_data = "TV/PC"; } else; if (data_IR == 0xd02f) {time2_data = "INPUT"; } else; if (data_IR == 0x8877) {time2_data = "1"; } else; if (data_IR == 0x48b7) {time2_data = "2"; } else; if (data_IR == 0xc837) {time2_data = "3"; } else; if (data_IR == 0x28d7) {time2_data = "4"; } else; if (data_IR == 0xa857) {time2_data = "5"; } else; if (data_IR == 0x6897) {time2_data = "6"; } else; if (data_IR == 0xe817) {time2_data = "7"; } else; if (data_IR == 0x18e7) {time2_data = "8"; } else; if (data_IR == 0x9867) {time2_data = "9"; } else; if (data_IR == 0xca35) {time2_data = "LIST"; } else; if (data_IR == 0x08f7) {time2_data = "0"; } else; if (data_IR == 0x58a7) {time2_data = "Q.VIEW"; } else; if (data_IR == 0x40bf) {time2_data = "VOL+"; } else; if (data_IR == 0x7887) {time2_data = "FAV"; } else; if (data_IR == 0x00ff) {time2_data = "CH+"; } else; if (data_IR == 0xd52a) {time2_data = "GUIDE"; } else; if (data_IR == 0xc03f) {time2_data = "VOL-"; } else; if (data_IR == 0x906f) {time2_data = "MITE"; } else; if (data_IR == 0x807f) {time2_data = "CH-"; } else; if (data_IR == 0x4fb) {time2_data = "TEXT"; } else; if (data_IR == 0x55aa) {time2_data = "INFO"; } else; if (data_IR == 0x847b) {time2_data = "T.OPT"; } else; if (data_IR == 0xc23d) {time2_data = "SETTINGS"; } else; if (data_IR == 0x02fd) {time2_data = "TOP"; } else; if (data_IR == 0xa25d) {time2_data = "Q.MENU"; } else; if (data_IR == 0xe01f) {time2_data = "LEFT"; } else; if (data_IR == 0x22dd) {time2_data = "OK"; } else; if (data_IR == 0x609f) {time2_data = "RIGHT"; } else; if (data_IR == 0x14eb) {time2_data = "BACK"; } else; if (data_IR == 0x827d) {time2_data = "BOTTOM"; } else; if (data_IR == 0xda25) {time2_data = "EXIT"; } else; if (data_IR == 0x7e81) {time2_data = "smplink"; } else; if (data_IR == 0xbd42) {time2_data = "RES/*"; } else; if (data_IR == 0x8d72) {time2_data = "STOP"; } else; if (data_IR == 0xf10e) {time2_data = "<<"; } else; if (data_IR == 0x0df2) {time2_data = "PLEY"; } else; if (data_IR == 0x5da2) {time2_data = "PAUSE"; } else; if (data_IR == 0x718e) {time2_data = ">>"; } else; if (data_IR == 0x4eb1) {time2_data = "RED"; } else; if (data_IR == 0x8e71) {time2_data = "GREEN"; } else; if (data_IR == 0xc639) {time2_data = "YELLOW"; } else; if (data_IR == 0x8679) {time2_data = "BLUE"; } else; }else; sTask_wait (&table_simvol_bits); } |
Автор: | ARV [ Вт мар 08, 2016 11:12:41 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
трындец какой-то ![]() |
Автор: | AVI-crak [ Вт мар 08, 2016 13:35:17 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
У меня работает, а у вас? |
Автор: | Ican [ Вт мар 08, 2016 13:46:03 ] | ||
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) | ||
AVI-crak писал(а): У меня работает, а у вас? А у меня не работает ![]() Правда, я про свой найденный код. С ОС сталкиваться ой как не хочется - это ж дебри. Если я с портами и таймерами еще не совсем освоился, куда уж ОС.... Ну вот почему он может пропускать 10 байт-то? Код: NEC remote power button NC 0xF4 Инверсия комманды C 0x0B Комманда NA 0xF7 инверсия адреса A 0x08 адрес received bites | LOST Bites | F 4 | 0 B | F X | X X 1111 0100 | 0000 1011 | 1111 01LL | LLLL LLLL Заполнение идет с конца, т.е. потерянные байты L находятся после стартовой посылки и паузы.
|
Автор: | AVI-crak [ Вт мар 08, 2016 14:50:53 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Ican писал(а): AVI-crak писал(а): У меня работает, а у вас? А у меня не работает ![]() Заполнение идет с конца, т.е. потерянные байты L находятся после стартовой посылки и паузы. Ну собственно это и есть проблема, ик передача начинается со старших битов посылки. И первыми есно прилетают 16 бит кода фирмы - уникальный идентификатор принадлежности пульта к железяке, а уже после летит код команды кнопки. Игнорировать идентификатор не желательно, с его помощью можно различать разные пульты. У меня void TIM2_IRQHandler (void) - это банальное прерывание из cmsis, и не имеет зависимости от ос. На выхлопе volatile uint32_t data_IR; - готовая посылка пульта. Защита от сбоев, левых пультов, засветок и так далее. При любом отклонении от стандарта - процесс декодирования прерывается. Флагами выставляется состояние декодирования: table_simvol_bits - новые данные, time2_data_IR_RE - повтор команды (зажатая кнопка). Весь список кнопок - это просто вариант, без ос нужно просто опрашивать флаг table_simvol_bits в бесконечном цикле, и проверять на совместимость чего там прилетело. Хотя бесконечные циклы - огромное зло, и прежде всего в плане энергопотребления. Алгоритм? - Дык его итак видно, в коде сплошные IF - куда уж проще. Тут главное понять, что прилетевший на ик датчик тон 38кгц - событие в прошедшем времени, его нельзя отменить каким-либо образом. Это не радиоприёмник, где можно селекцией отстроится от помехи, тут всё топорно и прямолинейно. Есть тон - ик датчик роняет выход в ноль. Собственно по этому принципу работает счётчик таймера, в момент прерывания по спаду - мы уже имеем на компараторах 1и4 - два значения, общее время и время единицы. Это тоже событие в прошедшем времени, остаётся просто проверить - насколько оно подходит под наши условия. Компаратор таймера CH2 для того - чтобы поймать событие: "кнопка больше не нажата". Это тоже в прошедшем времени, потому как общее время при не нажимании кнопок получается огромным, есть шанс пропустить стартовую посылку. Число в CH2 - это 1,5 максимального времени в посылке, а именно - задержка перед повтором символа "кнопка зажата". Если срабатывает компаратор CH2 - то процесс декодирования устанавливается в начальное время, и так до момента пока не прилетит первая корректная посылка. Несколько свободных ик библиотек, в том числе и аурдино - все они измеряют время единицы и нуля в реальном времени, собственно мартышкин труд. Особо упорные измеряют время программными задержками. Причём эти библиотеки живут ещё со времён восмибитных пиков, практически без изменений. |
Автор: | Ican [ Чт мар 10, 2016 21:39:57 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Разобрался. Нашел место, где грабли лежат. было pos = -10; // change (ТЕРЯЛОСЬ 10 первых байт!!!!) стало pos = 0; // change принимается все на ура. Неоптимально, зато принимается. Всем спасибо, расходимся) |
Автор: | Andry_67 [ Чт апр 07, 2016 13:49:45 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Внесу свои 5 копеек. Мне тоже понадобилось подключить пульт ДУ. Я все сделал через захват таймера. У меня плата stm32 Mini, там был свободный вход PA8. Это канал захвата №1 таймера 1. Я видел ваши и другие библиотеки и мягко говоря в шоке. Декодирование пульта это довольно простая задача. У меня больше времени ушло на настройку таймера и прерывания. Выкладываю здесь свой вариант декодирования (только основные моменты). На выходе имеем переменную NecEnd которая равна нулю или 32-битному коду принятому от пульта. После чтения переменной ее необходимо снова сбросить в 0. Спойлерuint32_t NecEnd, NecB; // Эти переменные нужно объявить в началеuint16_t NecData, NecDataOld, NecD; uint8_t NecA; // Пример обработки кода пульта // Хотя бы 10 раз в секунду выполнять этот код switch (NecEnd) { case 0: // Ничего не принято break; case 2653519744: // 0x9E297F80 // Обработка клавиши "1" NecEnd=0; // Обнуляем переменную NecEnd break; case 2653536064: // 0x9E29BF40 // Обработка клавиши "2" NecEnd=0; break; case 2653503424: // 0x9E293FC0 // Обработка клавиши "3" NecEnd=0; break; default: NecEnd=0; } // ====================== // Настройка таймера void TIM1_Configuration(void) { // создаём переменную (структуру) для определения режима работы таймера TIM_TimeBaseInitTypeDef Timert1_Base; TIM_ICInitTypeDef Timert1_ICI; // TIM1 clock enable RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); // Базовые настройки таймера TIM_TimeBaseStructInit(&Timert1_Base); Timert1_Base.TIM_CounterMode = TIM_CounterMode_Up; // Выбираем режим работы счетчика Timert1_Base.TIM_Prescaler = 720 - 1; // пред делитель Timert1_Base.TIM_Period = 0xFFFF; // Коэффициент деления таймера Timert1_Base.TIM_ClockDivision = TIM_CKD_DIV1; // Определяем частоту для фильтров (tDTS) Timert1_Base.TIM_RepetitionCounter = 0; // Этот параметр только для Т1 и Т8 TIM_TimeBaseInit(TIM1, &Timert1_Base); // Записываем настройки в регистры // Настройки каналов таймера на захват Timert1_ICI.TIM_Channel = TIM_Channel_1; // Выбираем канал (1-4) Timert1_ICI.TIM_ICPolarity = TIM_ICPolarity_Falling; // Определяем полярность входа Timert1_ICI.TIM_ICSelection = TIM_ICSelection_DirectTI; // источник: напрямую со входа Timert1_ICI.TIM_ICPrescaler = TIM_ICPSC_DIV1; // Значение предделителя канала (отключен) Timert1_ICI.TIM_ICFilter = 15; // Значение фильтра от 0x0 до 0xF TIM_ICInit(TIM1, &Timert1_ICI); // Записываем настройки в регистры // На всякий случай сбрасываем флаги TIM_ClearFlag(TIM1, TIM_IT_CC1); // Разрешаем таймеру генерировать прерывание по захвату 1ый канал TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE); // Разрешаем ядру принимать прерывания NVIC_EnableIRQ(TIM1_CC_IRQn); // ЗАПУСК таймера TIM1 TIM_Cmd(TIM1, ENABLE); } // ====================== // Настройка прерывания void TIM1_CC_IRQHandler(void) { // Смотрим что прерывание от таймера именно по событию захвата 1-ого канала if (TIM_GetITStatus(TIM1, TIM_IT_CC1) != RESET) { NecDataOld = NecData; NecData = TIM_GetCapture1(TIM1); // Читаем значение из регистра захвата TIM_ClearITPendingBit(TIM1, TIM_IT_CC1); // Очищаем флаг этого прерывания NecD=NecData-NecDataOld; switch (NecA) { case 0: if (1115<NecD && 1135>NecD){ // повтор // NecEnd=1; // Раскомментировать строку если нужен повтор } else { if (1340<NecD && 1360>NecD) NecA=1; // стартовый импульс } break; case 1: if (100<NecD && 235>NecD){ NecA=2; } else { NecA=0; return; // ошибка выходим } default: if (100<NecD && 235>NecD){ NecA++; NecB = (NecB<<1)+NecD/175; // принимаем 32 бита данных if (NecA==34){ NecEnd=NecB; // данные приняты успешно NecA=0; } } else { NecA=0; return; // ошибка выходим } } } } |
Автор: | AVI-crak [ Чт апр 07, 2016 19:55:58 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Andry_67 Нет защиты от помех. Поиграйся двумя разными пультами одновременно, должно быть либо честное декодирование - либо сброс посылки. |
Автор: | Andry_67 [ Пт апр 08, 2016 13:36:05 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Защита от помех есть. В строке if (100<NecD && 235>NecD){ проверяется длительность между двумя импульсами и если длительность выходит за пределы то декодирование прекращается и ожидается новый стартовый импульс. Цитата: Поиграйся двумя разными пультами одновременно А не надо одновременно жать на разные пульты, в этом случае ни у кого ничего работать не будет. |
Автор: | AVI-crak [ Сб май 14, 2022 21:44:41 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Допрограмировался, в поисках нахожу своё старое. В гитхаб теперь только через телефон, bitbucket.org просто стёр всё мои каракули, ещё парочка репозитариев успела пять раз в прыжке переобуться, а один уникальный склад кода - умудрился буквально сгореть, вместе с датацентром. А тут даже пылью не покрылось. ik_nec.с СпойлерКод: /// IK protocol NEC /// TIMx->PSC = clock frequency of the timer / 50526 #include <stdint.h> #include "ik_nec.h" #include "stm32f030x6.h" #define IK_N 0xFFU /* unnamed china */ //#define IK_N 0xDFU /* LG */ #define DECODER ((uint16_t)((~IK_N)<<8)|IK_N) volatile uint8_t ik_out; const uint16_t range_z[] ={ 628,1167,35,66,35,66,634,1177,0,0, 76,278,76,278,2890,12759,798,1483,2890,12759,949,1764}; void TIM3_IRQHandler (void) //220 { uint32_t tmp; static uint8_t status = 0; static uint8_t poz; static uint32_t stor; if ((TIM3->SR & (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF)) != (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF)) goto uno; tmp = TIM3->CCR2; if (tmp < range_z[status << 1]) goto uno; if (tmp > range_z[(status << 1) + 1]) goto uno; switch (status) { case 0: status = 1; poz = 32; stor = 0; tmp = 10; break; case 1: tmp = stor << 1; if (TIM3->CCR1 > 170) tmp |= 1; stor = tmp; poz--; tmp = 12; if (poz == 0) { /// watch raw IR code /// stor = (~IK_N<<24)|(IK_N<<16)|(~ik_out<<8)|(ik_out) if ((DECODER == (stor >> 16)) && ((((stor >> 8) ^ stor) & 0xFF) == 0xFF)) { stor &= 0xFF; ik_out = stor; tmp = 14; status = 2; }else goto uno; }; break; case 2: status = 3; tmp = 16; break; case 3: status = 2; tmp = 18; ik_out = stor; break; default: uno: status = 0; tmp = 20; break; }; TIM3->SR = 0; TIM3->CCR3 = range_z[tmp++]; TIM3->CCR4 = range_z[tmp]; }; void ik_tim_init(void) { RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; RCC->AHBENR |= RCC_AHBENR_GPIOAEN; GPIOA->MODER |= _VAL2FLD(GPIO_MODER_MODER6, 2); //Alternate function mode GPIOA->AFR[0] |= _VAL2FLD(GPIO_AFRL_AFSEL6, 1); TIM3->PSC = 950; /// clock frequency of the timer / 50526 (509us = 51L) TIM3->CCMR1 = _VAL2FLD(TIM_CCMR1_CC1S, 1)| _VAL2FLD(TIM_CCMR1_CC2S, 2); TIM3->CCER = _VAL2FLD(TIM_CCER_CC1P, 1)| _VAL2FLD(TIM_CCER_CC2P, 0)| _VAL2FLD(TIM_CCER_CC1E, 1)| _VAL2FLD(TIM_CCER_CC2E, 1); TIM3->SMCR = _VAL2FLD(TIM_SMCR_TS, 5)| // Filtered Timer Input 1 (TI1FP1) _VAL2FLD(TIM_SMCR_SMS, 4); // Reset Mode (TRGI) TIM3->CCMR2 = _VAL2FLD(TIM_CCMR2_OC3M,6)| _VAL2FLD(TIM_CCMR2_OC4M,6); TIM3->CCR3 = 949; // 1357 start P TIM3->CCR4 = 1764; TIM3->DIER = TIM_DIER_CC1IE; TIM3->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(TIM3_IRQn); } ik_nec.h СпойлерКод: /// #include "ik_nec.h" #ifdef _ik_nec_ extern "C" { #endif /* _ik_nec_ */ extern volatile uint8_t ik_out; void ik_tim_init(void); #ifdef _ik_nec_ } #endif /* _ik_nec_ */ #define _ik_nec_ Написано для мелкого stm32f030f, требует таймера с поддержкой PWM Input и одним свободным контактом CH1. По протоколу NEC - необходимо выполнить реверс битов в каждом байте, с сохранением порядка байтов. Но мне лень. |
Автор: | zenon [ Ср дек 14, 2022 18:22:21 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
AVI-crak, спасибо тебе добрый человек за рабочий код для cmsis! Оно заработало. Дефайны только дёрнул, не знаю можно так? СпойлерКод: #define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) // #define GPIO_MODER_MODER6_Pos (12U) #define GPIO_MODER_MODER6_Msk (0x3U << GPIO_MODER_MODER6_Pos) /*!< 0x00003000 */ #define GPIO_AFRL_AFSEL6_Pos (24U) #define GPIO_AFRL_AFSEL6_Msk (0xFU << GPIO_AFRL_AFSEL6_Pos) /*!< 0x0F000000 */ // #define TIM_SMCR_TS_Pos (4U) #define TIM_SMCR_TS_Msk (0x7UL << TIM_SMCR_TS_Pos) /*!< 0x00000070 */ #define TIM_CCMR1_CC1S_Pos (0U) #define TIM_CCMR1_CC1S_Msk (0x3UL << TIM_CCMR1_CC1S_Pos) /*!< 0x00000003 */ #define TIM_CCMR1_CC2S_Pos (8U) #define TIM_CCMR1_CC2S_Msk (0x3UL << TIM_CCMR1_CC2S_Pos) /*!< 0x00000300 */ #define TIM_CCER_CC1P_Pos (1U) #define TIM_CCER_CC1P_Msk (0x1UL << TIM_CCER_CC1P_Pos) /*!< 0x00000002 */ #define TIM_CCER_CC2P_Pos (5U) #define TIM_CCER_CC2P_Msk (0x1UL << TIM_CCER_CC2P_Pos) /*!< 0x00000020 */ #define TIM_CCER_CC1E_Pos (0U) #define TIM_CCER_CC1E_Msk (0x1UL << TIM_CCER_CC1E_Pos) /*!< 0x00000001 */ #define TIM_CCER_CC2E_Pos (4U) #define TIM_CCER_CC2E_Msk (0x1UL << TIM_CCER_CC2E_Pos) /*!< 0x00000010 */ #define TIM_SMCR_SMS_Pos (0U) #define TIM_SMCR_SMS_Msk (0x7UL << TIM_SMCR_SMS_Pos) /*!< 0x00000007 */ #define TIM_CCMR2_OC3M_Pos (4U) #define TIM_CCMR2_OC3M_Msk (0x7UL << TIM_CCMR2_OC3M_Pos) /*!< 0x00000070 */ #define TIM_CCMR2_OC4M_Pos (12U) #define TIM_CCMR2_OC4M_Msk (0x7UL << TIM_CCMR2_OC4M_Pos) /*!< 0x00007000 */ В моей cmsis их не нашлось, надо бы обновить, только не разобрался откуда правильно взять. И TIM3->PSC = 570; поставить для 48MHz пришлось. У тебя 950 стоит, с таким значением попадаю только при 80MHz (RCC_CFGR_PLLMUL10) . STM32F030K6T6. А, вот ещё, сырой код (stor) без декодирования не получилось принять , поковыряю ещё... |
Автор: | azhel12 [ Ср дек 14, 2022 20:53:16 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
На правах рекламы, можно сказать, тоже предложу вариант решения (подсмотрено на easyelectronics) СпойлерКод: /** * @brief Class for IR receiver * * @tparam _Timer GP timer instance * @tparam _Pin Input pin * @tparam _Decoder Decoder */ template <typename _Timer, typename _Pin, typename _Decoder> class IrReceiver { using InputCaptureFalling = typename _Timer::InputCapture<0>; using InputCaptureRising = typename _Timer::InputCapture<1>; using TimeoutOCChannel = typename _Timer::OutputCompare<2>; public: /** * @brief Init receiver * * @par Returns * Nothing */ static void Init() { _Timer::Enable(); _Timer::SetPrescaler(_Timer::GetClockFreq() / 1000000 * 2 - 1); // 1us period _Timer::SetPeriod(0xffff); _Timer::SlaveMode::SelectTrigger(_Timer::SlaveMode::Trigger::FilteredTimerInput1); _Timer::SlaveMode::EnableSlaveMode(_Timer::SlaveMode::Mode::ResetMode); InputCaptureFalling::SetCapturePolarity(InputCaptureFalling::CapturePolarity::FallingEdge); InputCaptureFalling::SetCaptureMode(InputCaptureFalling::CaptureMode::Direct); InputCaptureFalling::EnableInterrupt(); InputCaptureFalling::Enable(); InputCaptureRising::SetCapturePolarity(InputCaptureRising::CapturePolarity::RisingEdge); InputCaptureRising::SetCaptureMode(InputCaptureRising::CaptureMode::Indirect); InputCaptureRising::EnableInterrupt(); InputCaptureRising::Enable(); TimeoutOCChannel::SetPulse(15'000); _Pin::Port::Enable(); _Pin::template SetConfiguration<_Pin::Configuration::In>(); _Pin::template SetPullMode<_Pin::PullMode::PullUp>(); _Timer::Start(); } /** * @brief Timer IRQ handler (call this method in TIMx_IRQHandler) * * @par Returns * Nothing */ static void IRQHandler() { static bool idleState = true; if(InputCaptureFalling::IsInterrupt()) { InputCaptureFalling::ClearInterruptFlag(); uint16_t width = InputCaptureFalling::GetValue(); uint16_t pulse = InputCaptureRising::GetValue(); if(idleState) { idleState = false; TimeoutOCChannel::EnableInterrupt(); } else { if(IsSimilar<_Decoder::StartWidth>(width) && IsSimilar<_Decoder::StartPulse>(pulse)) { _Decoder::Start(); } else if(IsSimilar<_Decoder::Width0>(width) && IsSimilar<_Decoder::Pulse0>(pulse)) { _Decoder::Add0(); } else if(IsSimilar<_Decoder::Width1>(width) && IsSimilar<_Decoder::Pulse1>(pulse)) { _Decoder::Add1(); } else { idleState = true; } } } if (TimeoutOCChannel::IsInterrupt()) { TimeoutOCChannel::DisableInterrupt(); TimeoutOCChannel::ClearInterruptFlag(); if(!idleState) { idleState = true; _Decoder::Handle(); } } } private: /** * @brief Compare received value with decoder constant with given accuracy * * @tparam _TargetValue Constant * @param value Received value * * @return true If value ~ _TargetValue * @return false If value != _TargetValue */ template<uint16_t _TargetValue> inline static bool IsSimilar(uint16_t value) { return ((_TargetValue * (100 - _Decoder::EpsilonInPercent) / 100) < value) && (value < (_TargetValue * (100 + _Decoder::EpsilonInPercent) / 100)); } }; /** * @brief NEC decoder */ class NecDecoder { public: static const uint16_t StartWidth = 13500; static const uint16_t StartPulse = 9000; static const uint16_t Width0 = 1125; static const uint16_t Pulse0 = 562; static const uint16_t Width1 = 2250; static const uint16_t Pulse1 = 562; static const uint16_t EpsilonInPercent = 20; /// Command /// @todo Add commands enum Command : uint16_t { NoCommand = 0 }; using Callback = std::add_pointer_t<void(Command command)>; /** * @brief Set the callback for command receive * * @param callback Callback * * @par Returns * Nothing */ static void SetCallback(Callback callback) { _callback = callback; } /** * @brief Start new frame * * @par Returns * Nothing */ static void Start() { _frame = 0; } /** * @brief Add bit 0 to frame * * @par Returns * Nothing */ static void Add0() { _frame >>= 1; } /** * @brief Add bit 1 to frame * * @par Returns * Nothing */ static void Add1() { _frame = (_frame >> 1) | 0x80000000; } /** * @brief End of frame (pause detect) handler * * @par Returns * Nothing */ static void Handle() { if ((_frame & 0xff000000) != ( (~(_frame) & 0x00ff0000) << 8)) { return; } if ((_frame & 0xff00) != (((~_frame) & 0x00ff) << 8)) { return; } uint16_t command = ((_frame & 0xff000000) >> 16) | ((_frame & 0xff00) >> 8); if(_callback) _callback(static_cast<Command>(command)); } private: static uint32_t _frame; static Callback _callback; }; uint32_t NecDecoder::_frame; NecDecoder::Callback NecDecoder::_callback; Использовать максимально просто, вот пример: СпойлерКод: #include <clock.h> #include <iopins.h> #include <timer.h> #include <drivers/ir.h> using namespace Zhele; using namespace Zhele::Drivers; using namespace Zhele::IO; using namespace Zhele::Timers; using namespace Zhele::Clock; void ConfigureClock(); void ConfigureOutputPwm(); void ConfigureInputCapture(); using Receiver = IrReceiver<Timer4, Pb6, NecDecoder>; int main() { ConfigureClock(); Receiver::Init(); NecDecoder::SetCallback([](NecDecoder::Command command) { // Do smth }); for (;;) { } } void ConfigureClock() { PllClock::SelectClockSource(PllClock::ClockSource::External); PllClock::SetMultiplier(9); Apb1Clock::SetPrescaler(Apb1Clock::Div2); SysClock::SelectClockSource(SysClock::Pll); } extern "C" { void TIM4_IRQHandler() { Receiver::IRQHandler(); } } |
Автор: | AVI-crak [ Чт дек 15, 2022 01:02:21 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Дефайны только дёрнул, не знаю можно так? Раньше-бы посоветовал дёрнуть из кубика от st, но сейчас всё свежее приходится искать на гитхабе. https://github.com/STMicroelectronics/c ... er/Include |
Автор: | zenon [ Сб янв 21, 2023 22:04:22 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
А можно ли перекинуть на другой таймер? Попробовал в лоб изменить на 14 или 16 ... не вышло. Инит: Код: del Обработчик: Код: del
|
Автор: | azhel12 [ Сб янв 21, 2023 22:20:53 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Если контроллер F100RB, то у таймера 16 всего 1 канал (у 15 их два, если что), так что эта реализация неприменима. |
Автор: | zenon [ Сб янв 21, 2023 22:31:07 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Хм. У меня F030K6T6, о 32-х ногах, те, если я правильно понял на PA2 можно повесить? фиг там, нет у меня 15-го |
Автор: | AVI-crak [ Вс янв 22, 2023 01:22:41 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
А можно ли перекинуть на другой таймер? При работе с st чипами необходимо иметь STM32Cube, скачивать любым доступным способом. Это приложение необходимо для наглядного выбора периферии, и ротации ног. Что-бы не гадать на чайной гуще, и не курить тонны документации в поиске доступного варианта решения. Таймер должен уметь работать в режиме "PWM input", и иметь контакт на ногу чипа. |
Автор: | Reflector [ Вс янв 22, 2023 04:00:44 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
Таймер должен уметь работать в режиме "PWM input", и иметь контакт на ногу чипа. У меня все ограничения в либах прописаны, по старинке обхожуcь без кубов ![]() СпойлерКод: template<TimCh Channel> class Remote { using Tim = Channel::Timer; static constexpr uint32_t chNum = Channel::number(); static_assert(chNum <= 3); static_assert(Tim::hasFeatures(TimFeature::SlaveCtrl), "TimFeature::SlaveCtrl is Required!"); static_assert(chNum == 1 || Tim::hasFeatures(TimFeature::Xor), "TimFeature::Xor is Required!"); ......... }; Remote<Tim3Ch2<PA7>> remote; Скомпилируется только если подставить подходящий таймер, канал и пин. |
Автор: | zenon [ Вс янв 22, 2023 09:58:40 ] |
Заголовок сообщения: | Re: STM32 библиотека работы с ИК пультом (NEC) |
AVI-crak, да ну его этот куб, у меня от него зуд... лучше покопашусь по-своему, задач каких-то у меня нет, так потихоньку то с одним, то с другим разобраться... Ноги поменять глянув в даташит вообще труда не составляет. В общем на TIM1 перекинул, теперь TIM3 освободился для энкодера. TIM14 вроде умеет PWM Input, но на него не перекинуть. СпойлерКод: void ik_tim_init(void) { RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // 1-й таймер // TIM1_CH1 PA8 AF2 RCC->AHBENR |= RCC_AHBENR_GPIOAEN; GPIOA->MODER |= _VAL2FLD(GPIO_MODER_MODER8, 2); // Ногу в альтернативную функцию GPIOA->AFR[1] |= _VAL2FLD(GPIO_AFRH_AFSEL8, 2); // TIM1->PSC = 480;//340;//950;//400; //950; /// clock frequency of the timer / 50526 (509us = 51L) // TIM prescaler register TIM1->CCMR1 = _VAL2FLD(TIM_CCMR1_CC1S, 1)| _VAL2FLD(TIM_CCMR1_CC2S, 2); TIM1->CCER = _VAL2FLD(TIM_CCER_CC1P, 1)| _VAL2FLD(TIM_CCER_CC2P, 0)| _VAL2FLD(TIM_CCER_CC1E, 1)| _VAL2FLD(TIM_CCER_CC2E, 1); TIM1->SMCR = _VAL2FLD(TIM_SMCR_TS, 5)| // Filtered Timer Input 1 (TI1FP1) _VAL2FLD(TIM_SMCR_SMS, 4); // Reset Mode (TRGI) TIM1->CCMR2 = _VAL2FLD(TIM_CCMR2_OC3M,6)| _VAL2FLD(TIM_CCMR2_OC4M,6); TIM1->CCR3 = 949; //949; // 1357 start P TIM1->CCR4 = 1764; TIM1->DIER = TIM_DIER_CC1IE; TIM1->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(TIM1_CC_IRQn); } Код: void TIM1_CC_IRQHandler(void) { // uint32_t tmp; static uint8_t status = 0; static uint8_t poz; static uint32_t stor; if ((TIM1->SR & (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF)) != (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF)) goto uno; tmp = TIM1->CCR2; if (tmp < range_z[status << 1]) goto uno; if (tmp > range_z[(status << 1) + 1]) goto uno; switch (status) { case 0: status = 1; poz = 32; stor = 0; tmp = 10; break; case 1: tmp = stor << 1; if (TIM1->CCR1 > 170) tmp |= 1; stor = tmp; poz--; tmp = 12; if (poz == 0) { /// watch raw IR code /// stor = (~IK_N<<24)|(IK_N<<16)|(~ik_out<<8)|(ik_out) //if ((DECODER == (stor >> 16)) && ((((stor >> 8) ^ stor) & 0xFF) == 0xFF)) { stor &= 0xFF; ik_out = stor; tmp = 14; status = 2; //} else goto uno; }; break; case 2: status = 3; tmp = 16; break; case 3: status = 2; tmp = 18; ik_out = stor; break; default: uno: status = 0; tmp = 20; break; }; TIM1->SR = 0; TIM1->CCR3 = range_z[tmp++]; TIM1->CCR4 = range_z[tmp]; } Reflector писал(а): Скомпилируется только если подставить подходящий таймер, канал и пин. Для TIM14 и TIM16 у меня компилируется норм, даже не ругается не на что, но не заводится... наверное всё-так потому что одноканальные, как сказали выше, только не понял зачем два канала? |
Страница 1 из 2 | Часовой пояс: UTC + 3 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |