Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

Когда только начался век мобильных технологий, все старались не отставать от жизни и постоянно меняли гаджеты. Вначале дубовые кирпичи Nokia3310, потом Simens M65 с полифонией, потом window mobile, там уж и андроид подоспел… Для меня все закончилось на Xperia play, знатный был телефонец, жаль что помер 🙂 В итоге, так и не найдя ему достойной замены, вернулся к обычным трэшевым кнопочным «лишь бы звонил».

Но осталась одна не решенная проблема — отсутствие камеры. Конечно, фотоаппарат с вменяемой камерой имеется, но его гемморой с батарейками просто сводит все удовольствие от съемки на нет. В итоге вылезло еще пару «за», поэтому в доме оказался планшет. В начале никто даже не догадался для каких «грязных» целей мне он понадобился 🙂

Нет, игры на андроиде меня не впечатляют, а эмуляторы без кнопок это полный кал, просмотр видео и прочие шалобушки в виде интернет серфинга тоже не интересны. Идея была подсмотрена на сайте марсохода, смысл таков что у любого телефона нету внешнего выхода, кроме USB и выхода на наушники, USB слишком сложен для начинающих, но зато можно юзать выход с наушников!!! Просто напросто воспроизводишь сигнал определенной частоты, а на принимающей стороне смотришь какая это частота. Пишешь какую нибудь управляющую софтинку под андроид и радуешься.

Но сегодня не об этом, как оказалось все намного проще. Совершенно случайно у моего девайса на борту оказался ИК выход, я прослезился вспомнив времена Qtek S200, у нас в универе стояли телевизоры с обучающими каналами, а мы ходили и переключали на спортивные каналы. Надо было видеть бешенство преподов 🙂

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

Как решается подобная задача? Рекомендую вначале ознакомиться тут. Берется TSOP4836, подается питанию, а выход подключается к осциллографу. «Заглянул я под кусточек что увидел там… 🙂 В смысле видим вот такое
1

Пишем в гугле: «протоколы инфракрасных пультов», смотрим на что похож наш сигнал. Искать долго не пришлось, ибо понятно что это NEC протокол.

Теоретически вначале идут 8 бит адреса, затем те же 8 бит, но инверсные, затем 8 бит команда, затем 8 бит таже инверсная команда. Но если внимательно посмотреть, то мои старшие 8бит не похожи на инверсию младших 8 бит, их чего можно сделать вывод что адрес задается 16 битным числом. Далее идет 8 битная команда, затем эта же команда, но в инверсии(наоборот). Тут уже все похоже
2

Осталось разобраться как это дело работает. Еще в прошлый раз, когда разбирался с NRF24L01 запалил на сайте MCS Electronics AN #157 — Implementation of IR NEC protocol. Основная идея работы взята чуть менее чем полностью оттуда.

Логика работы: ждем от внешнего прерывания сигнал по спаду, если пришел — значит запускаем таймер. В следующее внешнее прерывание считаем количество тиков, которые сделал таймер, если их достаточно, то значит пришел старт бит, выставляем флаг, сбрасываем таймер, ждем следующего прерывания. Если тики в нужном диапазоне, то записываем данные, от нуля до 32 бит. Если же данные вне диапазона — останавливаем таймер, обнуляемся и ждем до следующего прерывания.

Обратите внимание данные 1 и 0 кодируются длительностью импульса, короткий импульс — ноль, длинный — единица. Сперва код не заработал, пришлось потыкаться осциллографом, очень выручил недавно испеченный uart terminal, в итоге оказалось что тайминги не соответствуют моим, сделал чуток поменьше и все заработало.

Прошивка состоит чуть менее чем полностью из прерываний. Адрес не учитывается, проверки тоже, если пришла команда Vol+ — зажигаем светодиод, если Vol- то тушим его, по пути выводим значения команд в терминал. Таким образом, можно смотреть через терминалку команды для всех кнопок пульта.

#include <mega8.h>
#include <delay.h>
#include <stdio.h>
 
volatile int tik = 0;  // счетчик тиков
volatile char Byt; //счетчик количества прочитанных битов
volatile bit Repeat_flag; //флаг повтора
volatile bit Start_flag=0; //флаг старт бита
volatile char Address;  //адрес
volatile char Command;  //команда
volatile char Address_1; //прямой адрес 
volatile char Command_1; //прямая команда
volatile char Address_0; //инверсный адрес
volatile char Command_0; //инверсная команда
 
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
    TCNT0=254; //31250/(256-254)=15625 кHz 64us
    tik++;     
    if(tik >= 1200)  
    {
        tik = 0;
        Repeat_flag = 0;
        Start_flag = 0;
        Address_1 = 0;
        Command_1 = 0;
        Address_0 = 0;
        Command_0 = 0;
        Address = 0;
        Command = 0;
        TCCR0=(0<<CS02) | (0<<CS01) | (0<<CS00);           
    }    
}
 
// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
    TCCR0=(1<<CS02) | (0<<CS01) | (0<<CS00);    //timer on     
 
    if((tik >= 139) && (tik < 150))  
    { //если происходит от 139 до 150 тиков, значит старт бит
        Address = 1;
        Repeat_flag = 0;
        Start_flag = 1;
        Address_1 = 0;
        Command_1 = 0;
        Address_0 = 0;
        Command_0 = 0; 
 
    }          
 
    if((tik >= 116) && (tik < 139)) //  11.1- 13.3ms
    { //если от 116 до 138, значит повтор
        Address = 2;
        Repeat_flag = 1;
        Start_flag = 0;
    }
 
 
    // от 22 до 115 - "1"
    if((tik >= 22) && (tik < 116) && (Start_flag == 1))   //2,1- 11.1ms
    {
        Byt++;
        if(Byt < 9) 
        {
            Address_1 = Address_1 << 1;
            Address_1 = Address_1 + 1;
        }
 
        if(Byt >= 9 && Byt < 17) 
        {
            Address_0 = Address_0 << 1;
            Address_0 = Address_0 + 1;
        }
 
        if(Byt >= 17 && Byt < 25)
        {
            Command_1 = Command_1 << 1;
            Command_1 = Command_1 + 1;
        }
 
        if(Byt >= 25) 
        {
            Command_0 = Command_0 << 1;
            Command_0 = Command_0 + 1;
        }
    }
 
 
    // 10 - 21 teaks - "0"
    if(tik >= 10 && tik < 22 && Start_flag == 1)
    { 
        Byt++;
 
        if(Byt < 9) 
        {
            Address_1 = Address_1 << 1;
        }
 
        if(Byt >= 9 && Byt < 17) 
        {
            Address_0 = Address_0 << 1;
        }
 
        if(Byt >= 17 && Byt < 25)
        {
            Command_1 = Command_1 << 1;
        }
 
        if (Byt >= 25) 
        {
            Command_0 = Command_0 << 1;
        }
    }
 
    tik = 0;
 
    if(Byt == 32)
    { 
        Address = Address_1;
        Command = Command_1;
        Byt = 0;
        Repeat_flag = 0;
        Start_flag = 0;
        TCCR0=(0<<CS02) | (0<<CS01) | (0<<CS00); 
        putchar(Command);
 
    }
 
}
 
void main(void)
{
DDRB = 0;
PORTB = 0;
 
// Port D initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=Out 
DDRD=(0<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (1<<DDD0);
PORTD=0;
 
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 31,250 kHz
TCCR0=(1<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0xFD;
 
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=(0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (0<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (1<<TOIE0);
 
// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Falling Edge
// INT1: Off
GICR|=(0<<INT1) | (1<<INT0);
MCUCR=(0<<ISC11) | (0<<ISC10) | (1<<ISC01) | (0<<ISC00);
GIFR=(0<<INTF1) | (1<<INTF0);
 
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: Off
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 9600
UCSRA=(0<<RXC) | (0<<TXC) | (0<<UDRE) | (0<<FE) | (0<<DOR) | (0<<UPE) | (0<<U2X) | (0<<MPCM);
UCSRB=(0<<RXCIE) | (0<<TXCIE) | (0<<UDRIE) | (0<<RXEN) | (1<<TXEN) | (0<<UCSZ2) | (0<<RXB8) | (0<<TXB8);
UCSRC=(1<<URSEL) | (0<<UMSEL) | (0<<UPM1) | (0<<UPM0) | (0<<USBS) | (1<<UCSZ1) | (1<<UCSZ0) | (0<<UCPOL);
UBRRH=0x00;
UBRRL=0x33;
 
 
// Global enable interrupts
#asm("sei")
 
while (1)
      {        
       if(Command == 0xe0)PORTD.0=1;   //если нажата "Vol+" - включаем светодиодc
       if(Command == 0xd0)PORTD.0=0;  //если "Vol-" -выключаем светодиод                       
       delay_ms(100); 
      }
}

Видео работы

В общем то хотел сделать одно, а получилось совсем другое, но в принципе цель достигнута. Все таки неплохо было бы если из каждого телефона торчал разъем с парочкой GPIO, наверное когда нибудь так будет, а пока можно развлекаться с разъемом для наушников, IRDA, блютузом, GSM.

Для желающих — прошивка

14 комментариев: Управление микроконтроллером по NEC протоколу.

  • Не заработало, однако..

  • А хотел просто полученную команду в порт вывести, да не тут то было. Видать лажа где то. Использовал TSOP38338.

  • посмотрите осциллом, может фьюзы

  • нет осцилла фьюзы в норме. Сможете скомпилить для 168Атмеги? Я зашью и протестирую?

  • дожили))) уже нажать кнопку скомпилить нет сил. протестируйте в протеусе

  • Доброе время суток!!! Код рабочий. Только это протокол SAMSUNG. С протоколом NEC вряд ли заработает для старт бита нужно примерно 210 тиков.

  • да сейчас вспомнил, принципиальное отличие было! В моем протоколе в прерывании по переполнению нужно установить TCNT0=254; чтобы с NEC зафурыкало нужно изменить TCNT0=253;

  • Повторил проект. Все заработало (пульт NEC).

  • Добрый вечер. Я правильно понял, что длительное нажатие кнопки тут не реализовано и как это можно сделать?

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

  • Привет admin, со своей программой пока большие трудности, адаптировать готовый проект, да еще и с хорошими комментариями с трудом еще получается, за что вам большое спасибо. При удержании кнопки пульта, команда передается только один раз, затем передаются короткие посылки, состоящие из преамбулы длительностью 9 мс и единичного интервала. Такие посылки передаются с периодичностью 110 мс.

  • Внезапно столкнулся с непонятной проблемой…
    Использую прерывания, по-всякому- не работает от слова «вообще». Никак. Прерывание- по нулю на ноге. В Протеусе все красиво. На железе- ни в какую… Все регистры, ответственные за прерывания перемучал- и никак. Отдельные импульсы вылавливает, код целиком- не хочет. Даже две единички последовательно не ловит…
    Прокол- NEC, железки- Attiny13 и какой-то китайский tsop.
    Попробовал без прерываний, завелось сразу.
    Если кто с таким косяком сталкивался- мобыть объясните в чем проблема?

    Прилагаю кусок своего кода (для CodeVisioAVR), максимально упрощен (ловлю код целиком, без разбития на адреса и прочее, одиночный импульс «повторной передачи» не ловлю, мне надо вкл/выкл- в этом случае «повторная передача» не имеет смысла).

    unsigned char cnt=0; // счетчик длительности паузы
    unsigned long int code=0; // принятый код

    // Timer 0 overflow interrupt service routine
    interrupt [TIM0_OVF] void timer0_ovf_isr(void)
    {
    TCNT0=0xBD; //примерно 56 мкс, подставить нужное с учетом своей тактовой частоты
    cnt++; //увеличиваем счетчик длительности паузы
    }

    void main(void)
    {
    //тута всякие установки портов и регистров
    // PORTB: вывод 1- как вход, подтянут.

    while (1)
    {
    if (!PINB.1) //вот этот кусок кода стоял в функции прерывания- не работал ваще.
    {
    if (cnt>30) //если насчитали больше длительности нуля (до середины единичной паузы)
    code|=1; //пишем единичку в младший разряд

    if (code==0b00000001111111100100100010110111) //если выловили код (нужный код подставить)
    PORTB.2=!PORTB.2; //переключение лампочки (для примера, или что хотим- то и делаем).

    cnt=0; //обнуление счетчика каждый раз
    code<<=1; //сдвиг на единичку влево каждый раз
    while (!PINB.1) {} //а вот этой строки в функции прерывания не стояло.
    }

    }

  • Написал почти тоже самое, только в Atmel Studio. Протокол пульта — NEC. Таймер тоже выставил на 64us. Однако работает через раз или через 2

  • Добил до конца наконец-то ))) кому инетресно — пишите — дам исходник со всеми коментами под Atmel Studio

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика