19 Февраль 2010
Одним из серьезных достоинств контроллеров AVR является дикое количество прерываний. Фактически, каждое периферийное устройство имеет по вектору, а то и не по одному. Так что на прерываних можно замутить кучу параллельных процессов. Работа на прерываниях является одним из способов сделать псевдо многозадачную среду.
Идеально для передачи данных и обработки длительных процессов.
Для примера покажу буфферизированный вывод данных по USART на прерываниях.
В прошлых примерах был такой код:
Идеально для передачи данных и обработки длительных процессов.
Для примера покажу буфферизированный вывод данных по USART на прерываниях.
В прошлых примерах был такой код:
Данный метод, очевидно, совершенно неэффективен. Дело в том, что у нас тут есть тупейшее ожидание события — поднятие флага готовности USART. А это зависит, в первую очередь, от скорости передачи данных. Например, на скорости 600 бод передача каких то 600 знаков будет длиться 9 секунд, блокируя работу всей программы, что ни в какие ворота не лезет.
Как быть?
Ну раз у нас отправка идет по аппаратному устройству, то большую часть времени мы впустую крутим цикл ожидания флага, хотя от процессора, собственно, тут ничего и не требуется и можно было бы заняться другими делами. Напрашивается мысль выбросить весь цикл ожидания в тело ядра/автомата/суперцикла и обрабатывать флаг на каждой итерации главного цикла/диспетчера. Но при этом у нас будет плавать время между байтами — ацтой!
Поэтому прерывания, пожалуй, будет единственным адекватным вариантов.
Итак, если брать в пример USART то у него есть три прерывания:
- RXT — прием байта. С этим понятно, мы его уже использовали
- TXC — завершение отправки
- UDRE — опустошение приемного буфера
Байты TXC и UDRE обычно вызывают путаницу. Поясню разницу.
Дело в том, что регистр передачи данных UDR в AVR на самом деле куда хитрей чем кажется, он двухэтажный. На первом ярусе, собственно UDR, а ниже находится конвейер сдвигового регистра. Первый байт, попавший в пустой регистр UDR тут же проваливается на конвейер, а UDR снова опустошается. После чего конвейер неторопливо, в соответствии с битрейтом, выплевывает данные в линию, а потом снова зажевывает байт из UDR. Поэтому, фактически, в UDR за короткое время влезает сразу два байта — первый тут же проваливается, а второй ждет.
Так вот,
- Флаг пустого регистра UDRE выставляется тогда, когда мы можем загнать байт в UDR,
- Флаг окончания передачи TXC появляется только тогда, когда у нас конвейер опустел, а новых данных в UDR нет.
Да, можно слать данные и по флагу TXC, но тогда у нас будет лишняя пауза между двумя разными байтами — время на опустошение буфера. Некошерно.
Вот как это можно сделать корректней.
Вначале выводим данные в массив, либо берем его из флеша — не важно. Для простоты запихну массив в ОЗУ. Код возьму из прошлой статьи:
Инициализация интерфейса выглядит стандартно:
Обратите внимание, что прерывания UDRE мы не разрешаем. Это делается потом. Иначе сразу же, на старте, контроллер ускачет на это прерывание, т.к. при пуске UDR пуст и мы получим черти что.
Отправка выглядит элементарно. Вначале пихаем первый байт нашего сообщения, не забыв выставить правильно индекс. А дальше разрешаем прерывание по UDRE оно само улетит куда надо, по прерываниям:
Отправка выглядит элементарно. Вначале пихаем первый байт нашего сообщения, не забыв выставить правильно индекс. А дальше разрешаем прерывание по UDRE оно само улетит куда надо, по прерываниям:
Дальше можно затупить или делать вообще что угодно:
В течении нескольких тактов выскочит прерывание UDRE
Да, кстати, я один раз словил гадский баг который отловил только трассировкой ассемблерного листнига. У меня была такая последовательность:
И вот тут почему то первым байтом шел мусор. А дальше все нормально. Причем если менять уровень оптимизации, то баг то вылезал то нет. Причиной такого поведения являлось то, что я то надеялся на то, что прерывание UDRE выскочит гораздо поздней чем я присвою индексу буфера нужное значение (buffer_index = 1;) Но индюк тоже думал, а по факту я пихаю байт в UDR, он в тот момент естественно пуст и уже следующим тактом, на выполнении команды UCSRB|=(1<<UDRIE) данные проваливались в сдвиговый регистр, а UDR тотчас пустел и выставлял бит прерывания.
А дальше, в зависимости от оптимизации, этот бит успевал выставиться к моменту когда я выставлял верный номер индекса либо не успевал.
Проблема решилась перестановкой строк:
Отсюда правило:
Готовь все необходимые данные ПЕРЕД разрешением прерываний.
Все, автоматика! Каждый раз когда UDRE пустеет прерывание срабатывает и бросает туда новых дров. Когда же буфер пустеет и индекс достигает максимума, то мы просто запрещаем прерывание UDRE и успокаиваемся.
Осталось только дать понять головной программе, что мы отработали. Для этого и есть флаг TXC можно разрешить его прерывание и тогда он сбросится при обработке прерывания USART_TXC_vect, а в самом обработчике сделать заброс задачи на диспетчер или еще что нибудь умное. Либо периодически проверять главным циклом наличие флага TXC и вручную его стереть (записью единицы).
Вот полный код:
Если грузануть его в Pinboard, предварительно подключив USART к FT232 и законнектиться терминалкой, то будет мигать наш LED4, а в терминалку от стрелятся байты ASCII кодов нашей строки. В это же время будет неторопливо тикать наш цикл с мигалкой.
Online Casino | Play and Win with Our Bonus - Freebies
ОтветитьУдалитьBaccarat, as the name implies, is a game that can be played at casinos for 1xbet beginners and those with an interest 제왕카지노 in a particular 바카라 game that has many ways