AVR. Учебный Курс. Работа на прерываниях

Распечатать

Одним из серьезных достоинств контроллеров AVR является дикое количество прерываний. Фактически, каждое периферийное устройство имеет по вектору, а то и не по одному. Так что на прерываних можно замутить кучу параллельных процессов. Работа на прерываниях является одним из способов сделать псевдо многозадачную среду.

 
Идеально для передачи данных и обработки длительных процессов.

 
Для примера покажу буфферизированный вывод данных по USART на прерываниях.

 
В прошлых примерах был такой код:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Отправка строки
void SendStr(char *string)
{
while (*string!='\0')
	{
	SendByte(*string);
	string++;
	}
}
 
// Отправка одного символа
void SendByte(char byte)
{
while(!(UCSRA & (1<<UDRE)));
UDR=byte;
}

 
Данный метод, очевидно, совершенно неэффективен. Дело в том, что у нас тут есть тупейшее ожидание события — поднятие флага готовности USART. А это зависит, в первую очередь, от скорости передачи данных. Например, на скорости 600 бод передача каких то 600 знаков будет длиться 9 секунд, блокируя работу всей программы, что ни в какие ворота не лезет.

 
Как быть?

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

 
Поэтому прерывания, пожалуй, будет единственным адекватным вариантов.

 
Итак, если брать в пример USART то у него есть три прерывания:

  • RXT — прием байта. С этим понятно, мы его уже использовали
  • TXC — завершение отправки
  • UDRE — опустошение приемного буфера

 
Байты TXC и UDRE обычно вызывают путаницу. Поясню разницу.

 
Дело в том, что регистр передачи данных UDR в AVR на самом деле куда хитрей чем кажется, он двухэтажный. На первом ярусе, собственно UDR, а ниже находится конвейер сдвигового регистра. Первый байт, попавший в пустой регистр UDR тут же проваливается на конвейер, а UDR снова опустошается. После чего конвейер неторопливо, в соответствии с битрейтом, выплевывает данные в линию, а потом снова зажевывает байт из UDR. Поэтому, фактически, в UDR за короткое время влезает сразу два байта — первый тут же проваливается, а второй ждет.

 
Так вот,

  • Флаг пустого регистра UDRE выставляется тогда, когда мы можем загнать байт в UDR,
  • Флаг окончания передачи TXC появляется только тогда, когда у нас конвейер опустел, а новых данных в UDR нет.

 
Да, можно слать данные и по флагу TXC, но тогда у нас будет лишняя пауза между двумя разными байтами — время на опустошение буфера. Некошерно.

 
Вот как это можно сделать корректней.

 
Вначале выводим данные в массив, либо берем его из флеша — не важно. Для простоты запихну массив в ОЗУ. Код возьму из прошлой статьи:

 

1
2
3
#define buffer_MAX 16	// Длина текстового буффера
char buffer[buffer_MAX] = "0123456789ABCDEF"; 	// А вот и он сам
u08 buffer_index=0;				// Текущий элемент буффера

 
Инициализация интерфейса выглядит стандартно:

1
2
3
4
5
6
7
8
//InitUSART
UBRRL = LO(bauddivider);
UBRRH = HI(bauddivider);
UCSRA = 0;
UCSRB = 1<<RXEN|1<<TXEN|0<<RXCIE|0<<TXCIE;
UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
 
sei(); 		// Разрешаем прерывания.

Обратите внимание, что прерывания UDRE мы не разрешаем. Это делается потом. Иначе сразу же, на старте, контроллер ускачет на это прерывание, т.к. при пуске UDR пуст и мы получим черти что.

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

1
2
3
buffer_index=0;		// Сбрасываем индекс
UDR = buffer[0];		// Отправляем первый байт
UCSRB|=(1<<UDRIE);	// Разрешаем прерывание UDRE

Дальше можно затупить или делать вообще что угодно:

1
2
3
4
while(1) 
{
NOP();
}

В течении нескольких тактов выскочит прерывание UDRE

 

Да, кстати, я один раз словил гадский баг который отловил только трассировкой ассемблерного листнига. У меня была такая последовательность:

1
2
3
UDR = X; 
UCSRB|=(1<<UDRIE);
buffer_index = 1;

 
И вот тут почему то первым байтом шел мусор. А дальше все нормально. Причем если менять уровень оптимизации, то баг то вылезал то нет. Причиной такого поведения являлось то, что я то надеялся на то, что прерывание UDRE выскочит гораздо поздней чем я присвою индексу буфера нужное значение (buffer_index = 1;) Но индюк тоже думал, а по факту я
пихаю байт в UDR, он в тот момент естественно пуст и уже следующим тактом, на выполнении команды UCSRB|=(1<<UDRIE) данные проваливались в сдвиговый регистр, а UDR тотчас пустел и выставлял бит прерывания.
А дальше, в зависимости от оптимизации, этот бит успевал выставиться к моменту когда я выставлял верный номер индекса либо не успевал.
Проблема решилась перестановкой строк:

1
2
3
UDR = X;
buffer_index = 1; 
UCSRB|=(1<<UDRIE);

 
Отсюда правило:
Готовь все необходимые данные ПЕРЕД разрешением прерываний.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Прерывание по опустошению буффера УАПП
ISR (USART_UDRE_vect)		
{
buffer_index ++;			// Увеличиваем индекс
 
if(buffer_index == buffer_MAX)  	// Вывели весь буффер? 
	{
	UCSRB &=~(1<<UDRIE);	// Запрещаем прерывание по опустошению - передача закончена
	}
	else 
	{
	UDR = buffer[buffer_index];	// Берем данные из буффера. 
	}
}

 
Все, автоматика! Каждый раз когда UDRE пустеет прерывание срабатывает и бросает туда новых дров. Когда же буфер пустеет и индекс достигает максимума, то мы просто запрещаем прерывание UDRE и успокаиваемся.

 
Осталось только дать понять головной программе, что мы отработали. Для этого и есть флаг TXC можно разрешить его прерывание и тогда он сбросится при обработке прерывания USART_TXC_vect, а в самом обработчике сделать заброс задачи на диспетчер или еще что нибудь умное. Либо периодически проверять главным циклом наличие флага TXC и вручную его стереть (записью единицы).

 
Вот полный код:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#define F_CPU 8000000L
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avrlibdefs.h>
#include <avrlibtypes.h>
 
#define buffer_MAX 16				// Длина текстового буффера
char buffer[buffer_MAX] = "0123456789ABCDEF"; 	// А вот и он сам
u08 buffer_index=0;
 
//Прерывание по опустошению буффера УАПП
ISR (USART_UDRE_vect)		
{
buffer_index++;			// Увеличиваем индекс
 
if(buffer_index == buffer_MAX)  	// Вывели весь буффер? 
	{
	UCSRB &=~(1<<UDRIE);	// Запрещаем прерывание по опустошению - передача закончена
	}
	else 
	{
	UDR = buffer[buffer_index];	// Берем данные из буффера. 
	}
}
 
int main(void)
{
 
#define baudrate 9600L
#define bauddivider (F_CPU/(16*baudrate)-1)
#define HI(x) ((x)>>8)
#define LO(x) ((x)& 0xFF)
 
//Init UART
UBRRL = LO(bauddivider);
UBRRH = HI(bauddivider);
UCSRA = 0;
UCSRB = 1<<RXEN|1<<TXEN|0<<RXCIE|0<<TXCIE;
UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
 
//Это так, просто помигать. 
#define LED1 4
#define LED_PORT PORTD
#define LED_DDR DDRD
 
LED_DDR = 1<<LED1;
 
sei();
 
buffer_index=0;		// Сбрасываем индекс
UDR = buffer[0];		// Отправляем первый байт
UCSRB|=(1<<UDRIE);	// Разрешаем прерывание UDRE
 
while(1) 
	{
	LED_PORT=0<<LED1;
 	_delay_ms(1000);
	LED_PORT=1<<LED1;
 	_delay_ms(1000);
	}
}

 
Если грузануть его в Pinboard, предварительно подключив USART к FT232 и законнектиться терминалкой, то будет мигать наш LED4, а в терминалку от стрелятся байты ASCII кодов нашей строки. В это же время будет неторопливо тикать наш цикл с мигалкой.

 
Ну и, как всегда, пример кода в архиве.

Запись опубликована в рубрике AVR. Учебный курс с метками , , , . Добавьте в закладки постоянную ссылку.

73 комментария: AVR. Учебный Курс. Работа на прерываниях

  1. Esperanto говорит:

    UDRE — опустошение приемного буфера
    Может «передающего» или имелось ввиду приёмный для отправки?

  2. Cluster говорит:

    buffer_index дожен быть помечен как volatile, если мы работам с ним из прерывания. Это очень важный момент. Если данная программа скорее всего будет нормально работать, то при работе с buffer_index вне прерывания при включенной оптимизации могут начаться косяки. ИМХО, стоит указать это в статье и объяснить, почему так. Нубы, которые только начинают изучать Си, часто с этим сталкиваются :)

    • DI HALT говорит:

      В данном случае нет. Т.к. не меняется нигде кроме как в прерывании :)

      • dimfair говорит:

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

        • DI HALT говорит:

          Нифига. Переменная глобальная. Так что любые модификации ее затрагивают именно ее изменения.

          Мы вошли в прерывание, изменили и вышли. Компилятор не может ее сныкать в регистр, т.к. ему четко сказано записать ее в память — глобальную переменную. Она тут никак не останется только на уровне регистров. Волатиль тут не нужна!

          А вот если бы была конструкция

          ISR {I++ }

          main { while(I<100){} }

          То в этом случае волатиль нужна обязательно, т.к. оптимизатор цикл разложит по регистрам, а то что I меняется в ISR ему невдомек.

          • Ну в данном случае, может и покатит, но если вдруг захочется в main сделать что-то ещё с этой переменной могут вылезти неожиданные глюки =) Так что лучше сразу объявить её volatile и не бояться подземных грабель.
            Самое смешное, что GCC оптимизирует даже регистровые переменные — если интересно, я как-то писал про то, как наступил на эти грабли: http://gremlinable.livejournal.com/6808.html

            Да, забавно ещё заметить, что в IAR ключевое слово volatile обозначает вообще совершенно другое… =)

          • DI HALT говорит:

            Захочу в майн ее вкорячить, то тогда и добавлю.

            А на всякий случай можно вообще оптимизацию вырубить ;)

          • Ну что ты добавишь, я не сомневаюсь, а вот ньюб, который учится по твоим статьям — не факт, только если наши комменты читать будет, а на это не у каждого хватит времени и нервов… =)

          • DI HALT говорит:

            Читать статьи надо по порядку ;) Вначале Сишного курса я рассказывал про эту фишку.

  3. Cluster говорит:

    И мне кажется более красивым способ, если сделать buffer_index типа char*.
    Тогда вначале мы присваиваем ему адрес буфера: buffer_index=buffer;
    А потом делаем так:
    ISR (USART_UDRE_vect)
    {
    if(*buffer_index) // Вывели весь буффер?
    {
    UDR = *buffer_index; // Берем данные из буффера.
    buffer_index++; // Увеличиваем индекс
    } else {
    UCSRB &=~(1<<UDRIE); // Запрещаем прерывание по опустошению — передача закончена
    }
    }

  4. Объяснение базисов, что как работает это очень хорошо, но в жизни советую использовать что нибудь вроде usart из gcc-libavr

    http://homepage.hispeed.ch/peterfleury/group__pfleury__uart.html

  5. Jyraf говорит:

    Спасибо за статью. В свое время очень нужна была эта информация, потом разобрался сам, писал на ассемблере.

  6. gleb говорит:

    а вот очень простая реализация буфера FIFO на асме. bufferstart и bufferend — адрес начала и конца буфера в СРАМ, buffervalue — регистр для обмена данными с буфером. буфер закольцован, когда он полон данные перестают записываться и выставляется флаг bufferfull, а когда пуст перестают считываться и выставляется bufferempty.

    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
    
    ;
    BUFFER_WRITE:
     
    	sbrc flag,fbufferfull  	;если не установлен флаг "буфер полон" пропускаем
    	rjmp BUFFER_WRITE_EXIT	;иначе на выход
     
    	st X+,buffervalue	;заносим значение в буфер, увеличиваем указатель записи
    	cbr flag,(1<<fbufferempty)	;сбрасываем флаг "буфер пуст"
    	cpi XL,bufferend		;проверяем указатель записи на достижение максимального адреса
    	brne BUFFER_WRITE_01	;если не достиг пропускаем
    	ldi XL,bufferstart		;иначе перескакиваем на минимальный адрес
    BUFFER_WRITE_01:
    	cp XL,YL		;проверяем, указатель записи догнал указатель чтения?
    	brne BUFFER_WRITE_EXIT	;если не догнал пропускаем
    	sbr flag,(1<<fbufferfull)	;устанавливаем флаг "буфер полон"
     
    BUFFER_WRITE_EXIT:
    	ret
     
     
    BUFFER_READ:
     
    	sbrc flag,fbufferempty 	;если не установлен флаг "буфер пуст" пропускаем
    	rjmp BUFFER_READ_EXIT	;иначе на выход
     
    	ld buffervalue,Y+	;читаем значение из буфера, увеличиваем указатель чтения
    	cbr flag,(1<<fbufferfull)	;сбрасываем флаг "буфер полон"
    	cpi YL,bufferend		;проверяем указатель чтения на достижение максимального адреса
    	brne BUFFER_READ_01	;если не достиг пропускаем
    	ldi YL,bufferstart		;иначе перескакиваем на минимальный адрес
    BUFFER_READ_01:
    	cp YL,XL		;проверяем, указатель чтения догнал указатель записи?
    	brne BUFFER_READ_EXIT	;если не догнал пропускаем
    	sbr flag,(1<<fbufferempty)	;устанавливаем флаг "буфер пуст"
     
    BUFFER_READ_EXIT:
    	ret
    • gleb говорит:

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

  7. gleb говорит:

    блин, как его отформатировать…

  8. tchicago говорит:

    В серьезных системах возникает дополнительная проблема — сложность полного обработчика прерывания. Например, по прерыванию нужно выкачать из устройства пакет данных, или сделать с ним еще что-то, требующее сложного ввода-вывода. Размещение такого кода непосредственно в обработчике прерывания будет мешать принятию и обработке прерываний от других устройств. Поэтому, в ядре ОС есть специальная очередь задач обработки прерываний (в одной из известных ОС такие задачи называются IRP — Interrupt Request Packet). Приоритет исполнения задач в этой очереди ниже приоритета собственно обработчиков прерываний, но выше приоритета обычных пользовательских процессов. Собственно обработчик вектора прерывания в драйвере устройства очень прост с точки зрения нагрузки на процессор. Он уведомляет устройство и контроллер прерываний, что прерывание принято к сведению, и добавляет задачу обработки прерывания (IRP) в эту очередь. IRP — это довольно простая структура данных, содержащая базовые данные о том, какому драйверу и устройству оно соответствует, и указатели на функции в драйвере, которые нужно исполнить.

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

    • SWG говорит:

      Гораздо удобнее система многоуровневых приоритетных прерываний, реализованная, например, в Интеловском контроллере прерываний I8059A еще в конце 70х годов. 8 векторов и 8 уровней приоритетов с возможностью наращивания каскадированием контроллеров, фиксированные или циклически меняющиеся приоритеты (например, уже обработанному прерыванию можно было присваивать самый низкий приоритет, или сдвигать приоритеты по кольцу), удобные маски, 4х или 8ми байтовый шаг таблицы векторов, и много еще всякого… С ним я не имел проблем с обработкой событий реального времени. Прерывание с более высоким приоритетом могло прервать обработку более низких, которая после будет опять продолжена, и само могло прерваться для обработки еще более высоких. Как я по ним скучаю…
      Все, что сейчас реализовано в наиболее распространенных микроконтроллерах — лишь слабое подобие той системы. А ведь это сильно упрощало программы реального времени и снимало массу проблем… Городить же программно еще отдельный диспетчер для формирования и обработки очереди прерываний — только еще больше запутывать и тормозить программу.
      Все равно эта конструкция никогда не заменит нормального аппаратно реализованного полноценного многоуровневого приоритетного контроллера прерываний… Не понимаю, почему бы не встроить такую штуку в современные микроконтроллеры. Схема его (по современным меркам) довольно проста, и сильно кристалл не усложнит.

  9. Apik говорит:

    Это все работает, если:

    sei();
    buffer_index=0; // Сбрасываем индекс
    UDR = buffer[0]; // Отправляем первый байт
    UCSRB|=(1<<UDRIE); // Разрешаем прерывание UDRE

    находится до цикла WHILE

    А если помести его за циклом — не работает. Как быть?

    • DI HALT говорит:

      Ну так он и не выполняется. Цикл то бесконечный и за себя не пустит. Трассировать то код хоть пытался?

      • Apik говорит:

        Ааа, вот оно что….
        Да, смотрел код асм — и конечно все верно — он прыгает вечно от строчки к строчке, зацикливается (сомтрел на ассме, просто написал тут на си):
        98: sei(); // Разрешаем прерывания
        99: UDR = buffer[0]; // Отправляем первый байт
        100: UCSRB|=(1<<UDRIE); // Разрешаем прерывание UDRE
        100: UCSRB|=(1<<UDRIE); // Разрешаем прерывание UDRE

        Но почему тогда выполняется передача первого числа — а только потом уже входит в цикл… Ведь и передача первого числа в цикле уже стоит…

        • DI HALT говорит:

          Стоп, ты про какой вайл говоришь, про этот?

          1
          2
          3
          4
          5
          6
          7
          
          while(1) 
          	{
          	LED_PORT=0<<LED1;
           	_delay_ms(1000);
          	LED_PORT=1<<LED1;
           	_delay_ms(1000);
          	}

          Напиши как ты сделал и что у тебя не получилось?

          • Apik говорит:

            Да, наверное сразу надо было начинать с этого, чтобы не вводить в заблуждение.

            Мне нужно проверить, замкнут ли контакт на землю, и если замкнут — отсылать сообщение по UART. Как только замыкание на землю исчезает — передача прекращается.

            Сам только недавно начал копаться в контроллерах, и что успел прочитать — что основной код должен выполняться в цикле, а вся инициализация до цикла.

            Очень понравился этот урок — и начал делать на его основе.
            Для начала (чтобы не наделать ошибок) решил бесконечно отсылать по UART строку, ну только что добавил в обработчик прерывание проверку:

            buffer_index++; // Увеличиваем индекс
            if(buffer[buffer_index] == 0×00)
            {
            UCSRB &=~(1<<UDRIE); // UCSRB &=~(1<<UDRIE)- Установка бита: запрещаем прерывание по опустошению — передача закончена
            }

            Я как бы конечно буду знать заранее, что я отправляю, т.е. количество символов — но все же я одновременно и изучаю еще си (а с подачки этого сайта одновременно и асм :) и решил сделать такую проверку — буфер у нас 16, а мне нужно передать например 3 символа — после 3 символов начнется передача символа NULL (0×00) — и если такое замечаем — прекращаем передачу.
            Все работало.

            Но как я понял из кучи литературы (видимо неправильно понял) — основная программа должна выполняться в цикле, и я просто перенес строки:

            sei(); // Разрешаем прерывания
            UDR = buffer[0]; // Отправляем первый байт
            UCSRB|=(1<<UDRIE); // Разрешаем прерывание UDRE

            в цикл while (1)
            Мигание диодами я убрал, не нужно оно мне пока что тут, в AVRStudio и так отлично видно, что получается.

            И вот в симуляции получается так: доходит до цикла while, отправляет в UDR первый байт, т.е. buffer[0], перескакивает на обработчик, отрабатывает его 1 раз (!!!) и позвращается к циклу, т.е. фактически передается только первое число из строки.
            Ну а в цикле от уже крутится бесконечно — прерывание не обрабатывается… А в цикле у нас передача первого символа — и он постоянно его пихает в UDR, но на обработчик прерывания не переходит…

            Интуитивно я догадываюсь, создавать функцию отправки до бесконечного цикла, и вызывать ее в цикле.. Так?

          • DI HALT говорит:

            ты не путай теплое с мягким.

            Твой цикл вращается бешеное число раз, заваливая буффер UDR символами в то время как он не готов принять байт, т.к. прожевыает предыдущий.

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

          • DI HALT говорит:

            А прерывание у тебя не вызывается потому, что не успело еще. Отправка байта это весьма долгий процесс.

  10. AJ26 говорит:

    Доброго времени суток! возникла такая задача: построить клавиатуру по распределенной схеме. 1ый МК (atmega16) совершает опрос клавиатуры (сканирование) и передает код нажатой кнопки по uart. планируется передача 1-3 байт за сеанс передачи. (один байт описывает состояние ctrl, alt, shift, второй содержит номер нажатой клавиши, третий — номер второй нажатой клавиши, если нажато 2 одновременно ) Постоянно пересылать состояние всех клавиш счел нецелесообразным. Второй МК (pic18f2550) принимает данные по уарт и передает в компьютер по usb, определяясь как стандартное hid устройство. Реализация usb клавиатуры подошла от фреймворка microchip, с этим нет вопросов — работает стабильно. На стороне и принимающего, и передающего МК будет создан циклический буфер.

    ПОСТАНОВКА ВОПРОСА: как лучше реализовать обмен по UART — с прерываниями или без ( чтобы не было конфликта прерываний usb и uart ) Больше всего интересует со стороны принимающего мк. Как это вижу я. в цикле ждем завершения очередного приема, проверяя флаг. По завершении приема записываем полученное значение в регистр usb. usb сам с нужной ему частотой инициирует прерывание и передачу данных в ПК. Какие рекомендации и советы можете дать?

  11. oleg_harp говорит:

    Объясните, пожалуйста, почему #define LO(x) ((x)& 0xFF), а не просто #define LO(x) (x)?

    • DI HALT говорит:

      Чтобы отрезать старшие байты маской 0хFF

      • oleg_harp говорит:

        Так регистр же 8-ми битный, он их сам отрежет. Тогда, по вашей логике при определении макроса Hi старший байт тоже нужно отрезать. Вы же не отрезаете?!

        • DI HALT говорит:

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

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

          • oleg_harp говорит:

            Спасибо за оперативные ответы и за ресурс! Ответ «в целях совместимости» все прояснил.

  12. MariMax говорит:

    Добрый день, может кто объяснит
    ситуация такая, купил новую ATmega32, по идее F_CPU у нее с завода 1000000, в настройках USART пишу так
    [code]
    #define F_CPU 1000000L
    #define baudrate 9600L
    #define bauddivider (F_CPU/(16*baudrate)-1)
    #define HI(x) ((x)>>8)
    #define LO(x) ((x)& 0xFF)
    //Init UART
    UBRRL = LO(bauddivider);
    UBRRH = HI(bauddivider);
    UCSRA = 0;
    UCSRB = 1<<RXEN|1<<TXEN|0<<RXCIE|0<<TXCIE;
    UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
    [/code]
    на скорости 9600 выдает кракозябры
    меняю F_CPU на 8000000
    [code]
    #define F_CPU 8000000L
    [/code]
    больше ничего не меняю, начинает работать правильно, но на скорости 1200, в чем может быть проблемма?

  13. kirill934 говорит:

    Здравствуйте. Помогите кто может! Делаю стыковку по UART двух Attiny2313. Отладку произвожу по отдельности на макетке. При отладке всё пашет идеально, но как втыкаю МК в плату, UART начинает ловить кучу мусора, причём безостановочно. Как это остановить?
    Вот такая плата:
    http://fotoifolder.ru/view_foto/i9jcbejhb9fx/
    Хотя это вопрос скорее по теме разводки платы или схемотехнике, но мне кажется что здесь этот вопрос тоже уместен. Помогите, пожалуйста.

  14. GeoDX говорит:

    DiHALT, слушай, я написал простейшую программу в СodeVision — по нажатии на кнопку int0 выходит в прерывание и диодиком один раз мигает.

    Диод вместо одного раза мигает два (и вообще функция прерывания 2 раза выполняется). Это программный глюк или просто дребезг контактов (происходят два прерывания и в очередь выстраиваются) ?

    • DI HALT говорит:

      Это дребезг. Т.е. у тебя прерывание отрабатывает дважды.

      • GeoDX говорит:

        Огромное спасибо, успокоил! Твой сайт — прямо как спасательный круг :)))

        Получается, что прерывание срабатывает и его флаг устанавливается/сбрасывается быстрее, чем дребезг второй раз прерывание запускает и флаг заново устанавливает (флаг кажется сразу после запуска прерывания сбрасывается)?

        • DI HALT говорит:

          Флаг сбрасывается при переходе на вектор. По крайней мере для INT так. Для других прерываний по своему. Соответственно тебе, чтобы это пофиксить надо в прерывании отключать прерывание по INT, запускать какую либо фоновую задержку (например через службу таймера или аппаратным таймером) которая бы через 10-20мс вернула все взад. Тогда и событие поймаешь и дребезг подавишь.

        • DI HALT говорит:

          А вообще ловить кнопки через INT моветон. Гемора больше чем пользы. Кнопка штука не сильно срочная и ее можно в обычном циклическом опросе раз в 10 в секунду проверять просто.

  15. pripoy08 говорит:

    подскажите пожалуйста!
    уже совсем сил нету! запускаю на меге 16-й ЮСАРТ, в протеусе все норм работает! а в реале не конает! через терминал принимаю данные, а там много ошибок! немного подстраиваю скорость, чтот лучше, но всетаки 100% на 9600 передачи не наблюдаю! в чем может быть причина???
    передаю число от 0 до 255, инкремент и выполнение передачи по прерыванию!
    заранее спасибо!

    • DI HALT говорит:

      Убедись что тактовая частота именна та на которой ты думаешь работает проц.

      • pripoy08 говорит:

        да! частота та! смотрел осциллом! но она немного плавает!(это сильно может повлиять?)
        стоит внешний кварц, на 14, 7… МГц, коэфф деления для скорости выбран с таблицы, с нулевой погрешностью! питание достаточно чистое! dsPIC, в таком режиме передачи, работает на ура!

        • DI HALT говорит:

          Тогда хз. Там еще есть бит U2X вот проверь его состояние. Он удваивает частоту уарта. Еще есть всякие предделители частоты. Частоту то на чем смотрел? На кварце? Ядро может работать совсем на другой частоте если есть преддилетль или настроено не на кварц.

  16. gari говорит:

    Я же могу изменив, если надо регистры, испольовать эту же программу для tiny2313a?

  17. dolkons говорит:

    Здравствуйте. Я новичок. Поэтому у меня такой вопрос. Как я понял, у нас основной цикл программы — это мигание диодом. А по прерыванию происходит отправка данных по UART. То есть получается во время прерывания одновременно идет мигание светодиодом и отправка данных. Я не совсем понял как именно наступают прерывания. В статье написано «Прерывание по опустошению буффера УАПП» — то есть получается, что у нас одновременно со светодиодами происходит опустошение буфера, а вслед за ним происходит прерывание? Разъясните пожалуйста, а то я запутался :)

  18. tooth_fairy говорит:

    Подскажите пожалуйста. при вызове функции из обработчика прерываний что происходит с флагом i ? Т.е. нужно ли мне запрещать глобально прерывания перед её вызовом или нет?
    ISR(TWI_vect)
    {
    switch(flags.twi_state)
    {
    case RTC:
    cli(); //————>>НУЖНО ЛИ ЭТО??
    twi_read(RTC_ADDRESS,RTC_REGISTER,RTC_LENGTH);
    bcd_to_ascii();
    sei(); //————И ЭТО??
    break;

  19. mitasamodel говорит:
    char buffer[buffer_MAX] = "0123456789ABCDEF";
    
    //Прерывание по опустошению буффера УАПП
    ISR (USART_UDRE_vect)
    {
    	if(buffer_index == buffer_MAX) // Вывели весь буффер?
    	{
    		UCSRB &=~(1<<UDRIE); // Запрещаем прерывание по опустошению - передача закончена
    	}
    	else
    	{
    		UDR = buffer[buffer_index]; // Берем данные из буффера.
    	}
    	buffer_index++; // Увеличиваем индекс
    }
    
    main
    {
    	buffer_index=0; // Сбрасываем индекс
    	UCSRB|=(1<<UDRIE); // Разрешаем прерывание UDRE
    	while(1)
    	{
    		;
    	}
    }
    
    • DI HALT говорит:

      А это мысль. Можно и так. Подготовил данные, включил прерывание. Правда при этом данные пойдут не мгновенно, а только когда прерывание его обработает. Но зачастую это не критично.

  20. Jet говорит:

    В этой программе я пытаюсь вывести данные (звук) из wavetable на вывод в ШИМ. Но проблема в том, что в переменную i почему-то записывается какой-то мусор. Что я делаю не так?
    Код для AVR Toolchain:

  21. Jet говорит:

    Забыл сказать, я сделал OCR0B = i; , чтоб посмотреть состояние i (ну тупой я, не знаю, как в отладчике посмотреть).

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