Содержание
Урок 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

Прежде чем приступить к изучению таймера определимся с базовым понятием «частота». Простым языком, это количество повторений, в секунду. Это значит, что если вы за секунду хлопнете в ладошки 2 раза, то частота хлопков будет равна 2Гц. Если за 3 раза, значит 3Гц.

Каждый микроконтроллер работает на определенной частоте. Большинство инструкций выполняется за один такт, поэтому чем выше частота, тем быстрее работает микроконтроллер. Если нет источника тактирования, соответственно ничего работать не будет. На случай отсутствия внешнего источника тактирования, в большинстве микроконтроллеров имеется свой внутренний генератор. Обычно на него «с завода» настроены.

Частота внутреннего источника может изменяться («плавать») из за температуры и т.п., поэтому считается непригодным для серьезных проектов, а у нас ведь именно такие 🙂 Поэтому применяется стабильный источник внешней частоты — кварцевый резонатор (кварц). Один из вариантов исполнения кварцевого резонатора:

Теперь, кое что о таймере. Таймер работает на той же частоте, что и микроконтроллер. Иногда это может быть слишком быстро, поэтому используют предделитель который уменьшает количество тиков в 8/64/256/1024… раз. Включается это все программно.

Допустим, мы выбрали предделитель 1024, частота микроконтроллера 8 МГц, значит после предделителя частота таймера станет:
8 000 000 / 1024 = 7813 Гц — это частота, на которой работает наш таймер. По простому говоря, за одну секунду таймер тикнет 7813 раз.

К количеству тиков можно привязать выполнение кода. Эта фича есть не для всех таймеров, читайте документацию на свой камень. Допустим, нам нужно, чтобы раз в 0,5 секунды выполнялся наш код. За одну секунду 7813 тиков, за пол секунды в 2 раза меньше — 3906. Это значение вносится в регистр сравнения, и с каждым тиком проверяется достаточно ли оттикало или нет, как в будильнике, только очень быстро.

Но вот у нас совпали эти 2 значения и что дальше? Для этого существует такая полезная штука как прерывание по совпадению. Это значит, что при совпадении таймера и регистра сравнения, ваша текущая программа остановится. После этого выполнится участок кода, который абсолютно не связан с основной программой. Внутри этого участка вы можете писать что угодно и не беспокоиться о том, что он как то повлияет на программу, выполнится он только когда значение таймера совпадет с регистром сравнения.

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

Вот теперь мы готовы написать нашу программу. Поэтому создаем проект с помощью мастера проектов. Сразу прицепим LCD, мы же уже это умеем).

Переходим на вкладку Timers и тут остановимся поподробнее:

Выбираем частоту 7813 и устанавливаем галочку напротив пункта Interrupt on: Compare A Match. Таким образом мы указали, что при совпадении значения выполнять прерывание (то о чем было написано выше). Прерывание будем выполнять 1 раз в секунду, т.е. нам нужно тикнуть 7813 раз, поэтому переводим число 7813 в шестнадцатеричную систему и получим 1e85. Именно его и записываем в регистр сравнения Comp A. Регистр сравнения Comp A 16 битный, поэтому число больше 2^16=65536 мы записать не можем.

Генерим, сохраняем, вычищаем наш код. Появится новый непонятный кусок кода

// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{

}

Это то самое прерывание. Именно внутри этих скобок мы можем писать тот код, который мы хотели бы выполнять через определенные промежутки времени. У нас это одна секунда. Итак логично создать переменную, которую мы будем увеличивать 1 раз в секунду, т.е. 1 раз за прерывание. Поэтому проинициализируем переменную int s =0; а в прерывании будем ее увеличивать от 0 до 59. Значение переменной выведем на жк дисплей. Никаких хитростей, все очень просто.
Получившийся код.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <mega8.h>
 
#asm
   .equ __lcd_port=0x18 ;PORTB
#endasm
#include <lcd.h>
 
int s = 0; // переменная для хранения секунд
 
// Обработка прерывания по совпадению
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
   s++; // увеличиваем переменную каждую секунду
   if(s>59) // обнуляем секунды после 59
   {
      s=0;
   }
  TCNT1=0; //обнуляем таймер
}
 
void main(void)
{
 
TCCR1A=0x00; //настройка таймера
TCCR1B=0x05;
TCNT1=0x00; //здесь увеличиваются тики
OCR1A=0x1E85; //записываем число в регистр сравнения
 
TIMSK=0x10; //запускаем таймер
 
lcd_init(8);
 
#asm("sei")
 
while (1)
      {
        lcd_gotoxy(0,0);  //вывод в 0 координате X и Y
        lcd_putchar(s/10+0x30); //вывод десятков секунд
        lcd_putchar(s%10+0x30); //вывод секунд
      };
}

Прикрутив еще 2 переменные можно получить часы на микроконтроллере).
Файл прошивки и протеуса

212 комментариев: Урок 5. Использование таймера в AVR микроконтроллерах

  • Да, видимо при копировании ошибка вкралась.
    На само деле код в теле основного цикла выглядит так
    while (1)
    {
    if (temp > 23)
    {
    PORTB.2=0x01;
    PORTB.1=0x00;
    PORTB.0=0x00;
    }
    else if (temp 18)
    {
    PORTB.1=0x01;
    PORTB.2=0x00;
    PORTB.0=0x00;
    }
    else if (temp 59)
    {
    temp=ds18b20_temperature(0);
    s=0;
    }
    TCNT1=0;
    }
    инициализация датчика в main
    w1_init();
    delay_ms(500);
    ds18b20_init(0,0,50,DS18B20_12BIT_RES);
    delay_ms(1000);

    сейчас в протеусе ситуация такая, сразу после запуска загорается диод на ноге PB0
    если менять температуру на датчике то загораются соответствующие температуре диоды, но при этом не гаснут остальные, т.е. например при установленной на датчике температуре 27 градусов, при старте сразу загорается диод на ноге PB0 и через примерно 3 секунды диод на ноге PB2. Если понизить температуру до 22 градусов то загорится диод на ноге PB1 и при этом диоды на PB0 и PB2 останутся гореть.
    Не могу понять в чем дело. Фьюзы такие SKEL=0010 SUT=10 CLKDIV8=0

  • Почему то знаки сравнения не отобразились 0о
    второе условие
    «else if (temp «» 18)»
    третье
    «else if (temp «<" 18)"
    (кавычки только в сообщении))

  • Попробуйте расставить скобки на местах, помойму cavr не поддерживает else if

  • Все else if{} заменил на просто if {}, не помогло. Забыл написать, а в предыдущем листинге почему то не отобразилось — контроллер у меня attiny45, может здесь какая то тонкость?

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

  • Благодарю за создание этого замечательного ресурса и прошу помощи. Проблема заключается в том, что мне никак не удается «разделить» по прерываниям вывод на экран текста при одновременном воспроизведении звука. Не стану отрицать, код у меня кривой, потому прошу помощи. Заранее искренне благодарю.

    #include
    #include

    #define C0 30581
    #define C1 15288
    #define C2 7644
    #define C3 3822
    #define C4 1911
    #define C5 955
    #define C6 477
    #define C7 238

    #define C_SHARP0 28860
    #define C_SHARP1 14430
    #define C_SHARP2 7215
    #define C_SHARP3 3607
    #define C_SHARP4 1803
    #define C_SHARP5 901
    #define C_SHARP6 450
    #define C_SHARP7 225

    #define D0 27240
    #define D1 13620
    #define D2 6810
    #define D3 3405
    #define D4 1702
    #define D5 851
    #define D6 425
    #define D7 212

    #define D_SHARP0 25713
    #define D_SHARP1 12856
    #define D_SHARP2 6428
    #define D_SHARP3 3214
    #define D_SHARP4 1607
    #define D_SHARP5 803
    #define D_SHARP6 401
    #define D_SHARP7 200

    #define E0 24271
    #define E1 12134
    #define E2 6067
    #define E3 3033
    #define E4 1516
    #define E5 758
    #define E6 379
    #define E7 189

    #define F0 22909
    #define F1 11453
    #define F2 5727
    #define F3 2863
    #define F4 1431
    #define F5 715
    #define F6 357
    #define Ff7 178

    #define F_SHARP0 21621
    #define F_SHARP1 10810
    #define F_SHARP2 5405
    #define F_SHARP3 2702
    #define F_SHARP4 1351
    #define F_SHARP5 675
    #define F_SHARP6 337
    #define F_SHARP7 168

    #define G0 20408
    #define G1 10204
    #define G2 5102
    #define G3 2551
    #define G4 1275
    #define G5 637
    #define G6 318
    #define G7 159

    #define G_SHARP0 19264
    #define G_SHARP1 9631
    #define G_SHARP2 4815
    #define G_SHARP3 2407
    #define G_SHARP4 1203
    #define G_SHARP5 601
    #define G_SHARP6 300
    #define G_SHARP7 150

    #define A0 18181
    #define A1 9090
    #define A2 4545
    #define A3 2272
    #define A4 1136
    #define A5 568
    #define A6 284
    #define A7 142

    #define A_SHARP0 17161
    #define A_SHARP1 8580
    #define A_SHARP2 4290
    #define A_SHARP3 2145
    #define A_SHARP4 1072
    #define A_SHARP5 536
    #define A_SHARP6 268
    #define A_SHARP7 134

    #define H0 16196
    #define H1 8099
    #define H2 4049
    #define H3 2024
    #define H4 1012
    #define H5 506
    #define H6 253
    #define H7 126

    #define P 0
    #define End 1

    #include
    #asm
    .equ __lcd_port=0x12 ;PORTD
    #endasm
    // прерывание таймера 1
    interrupt [TIM1_COMPA] void timer1_compa_isr(void)
    {
    PORTC.0=!PORTC.0;
    }
    int Bit = 0x00;

    int i = 0;

    int korob[]= {C_SHARP1, C_SHARP1, C_SHARP1, C_SHARP1, D1, D1, C_SHARP1, C_SHARP1,
    H0, H0, A0, A0, P, P, A0, H0,
    C_SHARP1, E1, E1, A0, A0, C_SHARP2, C_SHARP2, G_SHARP1,
    G_SHARP1, G_SHARP1, G_SHARP1, P, G_SHARP1, G_SHARP1, G_SHARP1, A1,
    F_SHARP1, F_SHARP1, F_SHARP1, F_SHARP1, P, P, F_SHARP1, G_SHARP1,
    E1, E1, E1, E1, P, P, A1, G_SHARP1,
    G_SHARP1, G_SHARP1, F_SHARP1, F_SHARP1, F_SHARP1, P, F_SHARP1, E1,
    E1, E1, E1, E1, P, A0, H0, C_SHARP1 ,
    C_SHARP1, C_SHARP1, C_SHARP1, C_SHARP1, P, P, D1, C_SHARP1 ,
    H0, H0, A0, A0, P, P, A0, H0 ,
    C_SHARP1, C_SHARP1, E1, E1, A1, A1, C_SHARP2, C_SHARP2 ,
    G_SHARP1, G_SHARP1, G_SHARP1, P, G_SHARP1, G_SHARP1, G_SHARP1, A1 ,
    F_SHARP1, F_SHARP1, F_SHARP1, F_SHARP1, P, P, F_SHARP1, G_SHARP1 ,
    E1, E1, E1, E1, A1, A1, A1, G_SHARP1 ,
    G_SHARP1, G_SHARP1, G_SHARP1, F_SHARP1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, E1, E1, P, P, P, P ,
    P, P, P, P, C_SHARP1, D1, E1, E1 ,
    C_SHARP2, C_SHARP2, C_SHARP2, C_SHARP2, C_SHARP1, D1, E1, E1 ,
    H1, H1, H1, H1, C_SHARP1, D_SHARP1, F1, F1 ,
    P, P, A1, A1, A1, A1, G_SHARP1, F_SHARP1 ,
    F_SHARP1, F_SHARP1, G_SHARP1, G_SHARP1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, E1, E1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, F_SHARP1, F_SHARP1, E1, E1, D1, D1 ,
    D1, D1, C_SHARP1, C_SHARP1, D1, D1, E1, E1 ,
    E1, D1, C_SHARP1, H0, H0, H0, H0, H0 ,
    P, P, P, P, C_SHARP1, D1, E1, E1 ,
    C_SHARP2, C_SHARP2, C_SHARP2, C_SHARP2, C_SHARP1, D1, E1, E1 ,
    H1, H1, H1, H1, C_SHARP1, D_SHARP1, F1, F1 ,
    P, P, A1, A1, A1, A1, G_SHARP1, F_SHARP1 ,
    F_SHARP1, F_SHARP1, G_SHARP1, G_SHARP1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, E1, E1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, F_SHARP1, F_SHARP1, E1, E1, D1, D1 ,
    D1, D1, C_SHARP1, C_SHARP1, D1, D1, E1, E1 ,
    E1, D1, C_SHARP1, H0, H0, H0, H0, H0 ,
    P, P, P, P, D1, D1, D1, D1 ,
    D1, C_SHARP1, H0, A0, A0, A0, A0, A0,End}; //мелодия «Happy New Year»
    void play()
    {
    OCR1AH = (char)(Bit>>8); //записываем текущую ноту в OCR1A
    OCR1AL = (char)Bit;
    delay_ms(130);
    #asm(«cli») //запрещаем прерывание чтобы оборвать ноту
    delay_ms(40);
    #asm(«sei»)
    i++;
    }
    void pause()
    {
    #asm(«cli») //пауза между нотами
    delay_ms(200);
    #asm(«sei»)
    i++;
    }

    void stop()
    {
    #asm(«cli»)
    PORTB=0x00;
    i=0;
    }
    interrupt [TIM1_COMPB] void timer1_compb_isr(void)
    {
    #asm(«sei»)
    lcd_init(40);
    }

    void main(void)
    {

    PORTB=0xFF;
    DDRB=0xFF;

    PORTD=0xFF;
    DDRD=0x00;

    // Timer/Counter 1 initialization
    // Clock source: System Clock
    // Clock value: 8000,000 kHz
    // Mode: CTC top=OCR1A
    // OC1A output: Discon.
    // OC1B output: Discon.
    // Noise Canceler: Off
    // Input Capture on Falling Edge
    // Timer 1 Overflow Interrupt: Off
    // Input Capture Interrupt: Off
    // Compare A Match Interrupt: On
    // Compare B Match Interrupt: Off
    TCCR1A=0x00;
    TCCR1B=0x09;
    TCNT1H=0x00;
    TCNT1L=0x00;
    ICR1H=0x00;
    ICR1L=0x00;

    OCR1AH=0x00;
    OCR1AL=0xFB;

    OCR1BH=0x00;
    OCR1BL=0x00;

    TIMSK=0x10;

    while (1)
    {
    Bit= korob[i]; //массив с нотами

    if(Bit==1)
    {
    stop();
    }
    if(Bit==0)
    {
    pause();
    }
    else
    {
    play();
    }

    /* lcd_gotoxy(0,0);
    lcd_putsf(«No more champagne»);
    lcd_gotoxy(0,1);
    lcd_putsf («And the fireworks are through»);
    lcd_clear();
    lcd_gotoxy(0,0);
    lcd_putsf(«Here we are, me and you»);
    lcd_gotoxy(0,1);
    lcd_putsf («Feeling lost and feeling blue»);
    lcd_clear();
    */

    }
    }

  • Во первых инициализацию достаточно сделать 1 раз, поэтому lcd_init(40); можно запихать куда то до while(1), во вторых подумайте, прерывание это что то важное, на мой взгляд все взаимодействие с пользователем: кнопки, дисплейчики и т.п. это самое что может быть низкоприоритетное, ибо вы никак не заметите разницы выведется текст за полсекунды или на 0.75 секунды. Поэтому лучше вообще не пользоваться для этого прерываниями, максимум что вы можете сделать это дать «разрешение» на какое либо действие. Например:
    interrupt() //какое то прерывание
    {
    enable_lcd = 1;
    }

    где то внутри main кода
    if(enable_lcd == 1)
    {
    lcd_puts(«track1»);
    }

  • Все равно не понимаю, извините. У меня получается такая ситуация, что когда я включаю код для вывода на экран текста с задержкой каждых двух строк хотя бы в 5 секунд, то звук начинает невообразимо тормозить, у меня никак не получается вывести звук с одной задержкой, а текст на экран — с другой задержкой, т.е. логически «параллельным путем». Прошу помощи, потому что остатки мозга уже сломаны.

  • Микроконтроллер не умеет выполнять код параллельно, весь код выполняется последовательно. Поэтому если вы зашли в прерывание и выполняете операцию с дисплеем, которая условно выполняется 200мс, то все 200мс вы будете торчать внутри прерывания, остальной код выполняться не будет, т.е. те прерывания которые выводят звук выполняться не будут. Отсюда и лаги

  • Спасибо. А есть способ сделать эти вещи параллельно? Я хочу сделать гирлянду, включающую в себя светодиоды (рандомное мерцание), воспроизведение мелодии и вывод на экран какого-либо текста. Можно такое реализовать на одном микроконтроллере?

  • Параллельно это на ПЛИС. Тем не менее, ресурсов мк, хватит на то чтобы, сделать все настолько быстро, что вам будет казаться как одновременно. Представьте себе, за секунду у вас выполняется 8 000 000 операций, причем из вашей задачи важно только, чтобы звук играл четко в установленные промежутки, а остальное не суть важно. Поэтому оставьте звук в прерывании, остальное выводите в основном цикле.

  • А можете показать код (точнее, мой подправить)? Простите за наглость, но я уже одурел от решения этой задачи. Я понимаю, что основной приоритет — это звук, но как его «вогнать» НЕ В ОСНОВНОЙ ЦИКЛ, я не понимаю.

  • все что внутри while(1){….} это основной цикл, внутри interrupt(){….} это внутри прерывания

  • То есть, для звука должно выглядеть так:
    nterrupt [TIM1_COMPA] void timer1_compa_isr(void)
    {
    {
    PORTC.0=!PORTC.0;
    }
    int Bit = 0×00;
    int i = 0;
    int korob[]= {C_SHARP1, C_SHARP1, C_SHARP1, C_SHARP1, D1, D1, C_SHARP1, C_SHARP1,
    H0, H0, A0, A0, P, P, A0, H0,
    C_SHARP1, E1, E1, A0, A0, C_SHARP2, C_SHARP2, G_SHARP1,
    G_SHARP1, G_SHARP1, G_SHARP1, P, G_SHARP1, G_SHARP1, G_SHARP1, A1,
    F_SHARP1, F_SHARP1, F_SHARP1, F_SHARP1, P, P, F_SHARP1, G_SHARP1,
    E1, E1, E1, E1, P, P, A1, G_SHARP1,
    G_SHARP1, G_SHARP1, F_SHARP1, F_SHARP1, F_SHARP1, P, F_SHARP1, E1,
    E1, E1, E1, E1, P, A0, H0, C_SHARP1 ,
    C_SHARP1, C_SHARP1, C_SHARP1, C_SHARP1, P, P, D1, C_SHARP1 ,
    H0, H0, A0, A0, P, P, A0, H0 ,
    C_SHARP1, C_SHARP1, E1, E1, A1, A1, C_SHARP2, C_SHARP2 ,
    G_SHARP1, G_SHARP1, G_SHARP1, P, G_SHARP1, G_SHARP1, G_SHARP1, A1 ,
    F_SHARP1, F_SHARP1, F_SHARP1, F_SHARP1, P, P, F_SHARP1, G_SHARP1 ,
    E1, E1, E1, E1, A1, A1, A1, G_SHARP1 ,
    G_SHARP1, G_SHARP1, G_SHARP1, F_SHARP1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, E1, E1, P, P, P, P ,
    P, P, P, P, C_SHARP1, D1, E1, E1 ,
    C_SHARP2, C_SHARP2, C_SHARP2, C_SHARP2, C_SHARP1, D1, E1, E1 ,
    H1, H1, H1, H1, C_SHARP1, D_SHARP1, F1, F1 ,
    P, P, A1, A1, A1, A1, G_SHARP1, F_SHARP1 ,
    F_SHARP1, F_SHARP1, G_SHARP1, G_SHARP1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, E1, E1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, F_SHARP1, F_SHARP1, E1, E1, D1, D1 ,
    D1, D1, C_SHARP1, C_SHARP1, D1, D1, E1, E1 ,
    E1, D1, C_SHARP1, H0, H0, H0, H0, H0 ,
    P, P, P, P, C_SHARP1, D1, E1, E1 ,
    C_SHARP2, C_SHARP2, C_SHARP2, C_SHARP2, C_SHARP1, D1, E1, E1 ,
    H1, H1, H1, H1, C_SHARP1, D_SHARP1, F1, F1 ,
    P, P, A1, A1, A1, A1, G_SHARP1, F_SHARP1 ,
    F_SHARP1, F_SHARP1, G_SHARP1, G_SHARP1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, E1, E1, F_SHARP1, F_SHARP1, E1, E1 ,
    E1, E1, F_SHARP1, F_SHARP1, E1, E1, D1, D1 ,
    D1, D1, C_SHARP1, C_SHARP1, D1, D1, E1, E1 ,
    E1, D1, C_SHARP1, H0, H0, H0, H0, H0 ,
    P, P, P, P, D1, D1, D1, D1 ,
    D1, C_SHARP1, H0, A0, A0, A0, A0, A0,End}; //мелодия «Happy New Year»
    void play()
    {
    OCR1AH = (char)(Bit>>8); //записываем текущую ноту в OCR1A
    OCR1AL = (char)Bit;
    delay_ms(130);
    #asm(«cli») //запрещаем прерывание чтобы оборвать ноту
    delay_ms(40);
    #asm(«sei»)
    i++;
    }
    void pause()
    {
    #asm(«cli») //пауза между нотами
    delay_ms(200);
    #asm(«sei»)
    i++;
    }
    void stop()
    {
    #asm(«cli»)
    PORTB=0×00;
    i=0;
    }
    while (1)
    {
    Bit= korob[i]; //массив с нотами
    if(Bit==1)
    {
    stop();
    }
    if(Bit==0)
    {
    pause();
    }
    else
    {
    play();
    }
    }

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

  • Но тогда проблема появляется в основном while (1), и заключается она в следующем:

    while (1)
    {
    lcd_gotoxy(0,0);
    lcd_putsf(«No more champagne»);
    lcd_gotoxy(0,1);
    lcd_putsf («And the fireworks are through»);
    lcd_clear();
    lcd_gotoxy(0,0);
    lcd_putsf(«Here we are, me and you»);
    delay_ms(5000);
    lcd_gotoxy(0,1);
    lcd_putsf («Feeling lost and feeling blue»);
    delay_ms(5000)
    lcd_clear();
    }

    И проблема опять та же — тормоза при выводе звука.

  • Такс я понял суть проблемы, в общем если очень хочется совместить, то нужно длительности звука формировать таймером, т.е. в прерывании. Вам понадобится 2 таймера, первый будет формировать высоту ноты, второй ее длительность. Сейчас первый у вас уже реализован.

  • Мне стоит читать урок 13 и курить гугл?

  • Я думаю вся проблема в непонимании того как формируется звук, т.е. просто физика. Когда это станет понятно, то 5 урока будет более чем достаточно. В конце концов, вы можете сначала вывести текст, далее войти в цикл while где проиграть мелодию, по окончании снова вывести текст и снова войти в цикл, тогда не нужно будет второго прерывания. В общем подойти творчески

  • Спасибо! Как только получится — выложу готовый полностью код!

  • Добрый день!
    Уважаемый Админ,подскажите,как мне организовать такую идею:
    я пишу программу в среде flowcode avr,и мне нужно туда вставить кусочек си-кода,который будет:
    1.включать таймер-счетчик с предделителем на 1024
    2.по прерыванию по PORTB останавливать таймер-счетчик.
    3.содержимое счетчика переместить в переменную типа INT
    4.запустить счетчик заново.
    И еще.Если я правильно понял,во время выполнения программы таймер-счетчик работает постоянно?

  • Включаете генератор кода, задаете свои делители, настраиваете внешнее прерывание, пишите в соответствии с алгоритмом…PROFIT! Собственно в чем вопросы? Таймер работает постоянно, если его не выключить.

  • Доброго времени суток!
    Считаю частоту с помощью юнита input capture таймера 1. Частота таймера 512000. В прерывании сохраняется значение регистра ICR1 — предыдущее и текущее. В основной программе берется их разность, на основе чего считается частота.
    Проблемы в том, что Время получается в 4 раза меньше, чем на самом деле. К примеру частота 50 Гц, время — 20 мс, делю разность двух «штампов времени» на частоту тактирования (512000) и получается 5мс. К тому же возникает погрешность, около 0,0235 на 1 Гц. Приходится ее тупо отнимать.
    Потом, если моделировать, подавая на ножку ICP1 прямоугольный сигнал, то все ничего, если не считать сказанного выше. Но если подавать синусоиду через диод, или синусоидальный сигнал к триггеру Шмитта а с него на ICP1, не важно, то частота начинает колебаться в пределах 1Гц. В моделировании подключение этого юнита к компаратору частично решает проблему — колебания уменьшаются, но все равно большие.
    В реальном устройстве (сигнал с триггера Шмитта) колебания 0,5Гц, а проверить поведение при подключении юнита к компаратору не получилось — спалил я его.
    В даташите написано, что этот юнит используется для расчера частоты, создания журналов событий, а работает как-то криво, хотя все делаю верно, перечитал кучу раз регистры, все настроено верно, а разобраться в чем проблема никак не могу. Надеюсь на Вашу светлую голову!

    И подскажите пожалуйста про 16-битные регистры. Вот например

    sfrb ADCL=4;
    sfrb ADCH=5;
    sfrw ADCW=4; // 16 bit access

    CjdeVisioAVR создает функцию работы АЦП, в которой используется регистр ADCW, а адрес у него как и у ADCL. Он отбрасывает 2 бита или регистр с 16 bit access как-то сам все там учитывает.
    И как из двух чисел char собрать одно число int?
    Спасибо

  • По поводу основного вопроса сложно сказать, нужно посмотреть проект, схему, так на словах сложно въехать в суть вопроса, можно выложить проект на форуме. По поводу регистров, CAVR учитывает обращение к ним как к 16 битным. Из двух char можно собрать int http://avr-start.ru/?p=1966

  • Тупой вопросик. Возня с таймерами в avr, конкретно с T0 на atmega88 кварц 32768 Гц. Хочу добиться интервала 1000 ms(1 сек.)
    Посчитал. Проверил. Должно получиться, но по факту, в отладчике avr studio не выходит 1 сек.

    1. вариант (действие по переполнению)

    #define F_CPU 32768
    #include
    #include

    int main(void)
    {
    setupT0();
    asm(«sei»);
    while(1)
    {
    asm(«sei»);
    }
    }

    void setupT0(void)
    {
    TCCR0A&=~(1«WGM02);
    TCCR0A&=~(1«WGM01);
    TCCR0A&=~(1«WGM00);
    TCCR0B|=(1«CS02); // предделитель 256
    TIMSK0|=(1«TOIE0);
    TCNT0=0x00;
    }

    ISR(TIMER0_OVF_vect){
    asm(«nop»);
    }

    Получается остановка через 1 985,94 ms хотя должна быть через 1 сек

    2. вариант (действие по совпадению)
    #define F_CPU 32768
    #include
    #include

    int main(void)
    {
    setupT0();
    asm(«sei»);

    while(1)
    {
    asm(«sei»);
    }
    }

    void setupT0(void)
    {
    TCCR0A|=(1«WGM01); // включаем режим СТС
    TCCR0B|=(1«CS02); // предделитель 256
    TIMSK0|=(1«OCIE0A); // разрешаем таймер по совпадению
    TCNT0=0x00;
    OCR0A=0x80;
    }

    ISR(TIMER0_COMPA_vect)
    {
    asm(«nop»);
    }

    Получаю значение больше 1000 ms

    В чем проблема?

  • думаю все норм

  • Добрый день, admin. Спасибо за сайт, очень познавательно.
    Вопрос: я делаю свой первый циклический таймер на ATTiny 2313. Использую таймеры 0А (звук) и 1А (интервалы) в режиме СТС, mode: CTC top = OCR0A и OCR1A соответственно. Для полной картины не хватает меандра 1 Гц на какой-нибудь из свободных ног для индикации работы. Начал смотреть в сторону OCR1B, но похоже ничего не выйдет, если я правильно понял, всем рулит OCR1A. Возможно ли решение в моем случае?
    Заранее спасибо за любой ответ.

  • Можно извратиться и сделать прерывание более частым, тогда внутри увеличивать несколько переменных. Когда 1 переменная накопит определенное значение — зажигать светодиод, когда переменная 2 — задавать интервалы.

  • Подскажите как в мастере проектов cv avr создать простой таймер считающий не внутренние сигналы? а внешние. точнее 15 тиков на входной ножке и 1 тик на выходной ножке в attiny13.
    ну и выходной тик не шипко короткий, а то тахометр не опознает.

  • Правильно ли написана программа часов
    // Timer1 output compare A interrupt service routine
    interrupt [TIM1_COMPA] void timer1_compa_isr(void)
    {
    s++;
    if (s>59)
    {
    m++;
    if (m>59)
    {
    h++;
    m=0;
    if (h>23)
    {
    h=0;
    }
    }
    s=0;
    }
    TCNT1=0;
    }

    lcd_init(20);
    lcd_clear();
    lcd_gotoxy(3,0);
    lcd_puts(«-=#(LOKO41)#=-«);

    // Global enable interrupts
    #asm(«sei»)

    while (1)
    {
    lcd_gotoxy(12,1);
    lcd_putchar(s/10+0x30);
    lcd_putchar(s%10+0x30);
    lcd_gotoxy(11,1);
    lcd_puts(«:»);
    lcd_gotoxy(9,1);
    lcd_putchar(m/10+0x30);
    lcd_putchar(m%10+0x30);
    lcd_gotoxy(8,1);
    lcd_puts(«:»);
    lcd_gotoxy(6,1);
    lcd_putchar(h/10+0x30);
    lcd_putchar(h%10+0x30);
    lcd_gotoxy(7,1);
    if (PIND.0==0)
    {
    m++;
    delay_ms(100);
    if (m>59)
    {
    h++;
    if (h>23)
    {
    h=0;
    }
    m=0;
    }

    }
    if (PIND.1==0)
    {
    h++;
    delay_ms(100);
    if (h>23)
    {
    h=0;
    }
    }
    }
    }

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

  • нет

  • Здравствуйте. Подскажите как можно реализовать следующую идею. При кратковременном нажатии на кнопку с вывода микроконтроллера выходила лог.1 около 3 сек., но при долговременном нажатии на кнопку (более 5 сек.) и при дальнейшем отпускании, вывод микроконтроллера моментально отключился. Есть одна ATtiny13a планирую сделать на ней. Заранее спасибо.

  • считайте время, которое нажата кнопка.

  • Здравствуйте. Очень хороший сайт и единственный по урокам в CodeVisionAVR,больше не встречал всё доходчиво и понятно .Начал с таймера на ATmega8,с 5 урока прикрутил две переменные и получились часы добавил ещё кнопки всё окей. А вот на ATtiny13 всё вроде бы так же но не работает светодиод на порту 0 загорается сразу а не через 5 секунд.

    /*****************************************************
    Project :
    Version :
    Date : 03.04.2015
    Author : F4CG
    Company : F4CG
    Comments:

    Chip type : ATtiny13
    Clock frequency : 4,800000 MHz
    Memory model : Tiny
    External SRAM size : 0
    Data Stack size : 16
    *****************************************************/

    #include

    unsigned char sec;
    // Timer 0 output compare A interrupt service routine
    interrupt [TIM0_COMPA] void timer0_compa_isr(void)
    {
    TCNT0=0x00;
    {
    sec++;
    if(sec==5){
    sec=0;
    PORTB|=1<<0;
    }
    }
    }
    void main(void)
    {
    // Crystal Oscillator division factor: 1
    #pragma optsize-
    CLKPR=0x80;
    CLKPR=0x00;
    #ifdef _OPTIMIZE_SIZE_
    #pragma optsize+
    #endif

    // Input/Output Ports initialization
    // Port B initialization
    // Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
    // State5=T State4=T State3=T State2=T State1=T State0=T
    PORTB=0b10000000;
    DDRB=0b01111111;
    // Timer/Counter 0 initialization
    // Clock source: System Clock
    // Clock value: 4,688 kHz
    // Mode: Normal top=FFh
    // OC0A output: Disconnected
    // OC0B output: Disconnected
    TCCR0A=0x00;
    TCCR0B=0x05;
    TCNT0=0x00;
    OCR0A=0x12;//1250 это в шестнадцатитеричной системе предделитель 1024
    OCR0B=0x50;

    // External Interrupt(s) initialization
    // INT0: Off
    // Interrupt on any change on pins PCINT0-5: Off
    GIMSK=0x00;
    MCUCR=0x00;

    // Timer/Counter 0 Interrupt(s) initialization
    TIMSK0=0x04;

    // Analog Comparator initialization
    // Analog Comparator: Off
    ACSR=0x80;
    ADCSRB=0x00;

    // Global enable interrupts
    #asm("sei")

    while (1)
    {
    // Place your code here

    };
    }

  • OCR0 — 8битный счетчик, вы пытаетесь его использовать как 16битный

  • В Вашем примере OCR0A=1E85;ЭТО 16 битка а 8 битку то ка записывать

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

  • Здравствуйте.А могу я использовать внутренний генератор на частоту 32768/512=64 64=40 OCR0A=0x40 я правильно иду?

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

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

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