Tag Archives: Алгоритм

Простой программный таймер для конечных автоматов

Достался мне тут на доделку один проект. Точнее два, но от одного автора. Управление промышленным оборудованием.

Сам проект ничего особого, простая логика на конечных автоматах. Но мне понравился там как реализован таймер. Я обычно предпочитаю динамический таймер, а идею глобального времени только высказывал, но так и не применил, т.к. в основном все делал на диспетчере или RTOS и там этот подход не особо удобен. Но если логика построена на простом суперцикле с набором функций-автоматов в main цикле, то такая реализация таймера фактически стала классикой. Вот, пользуясь случаем, заполняю этот пробел. Архитектура тут не важна. Главное чтоб был таймер способный давать прерывание раз в тик. Тик обычно 1мс.

▌Принцип работы и использование
У нас есть глобальная переменная TimeMs которая инкрементируется по прерыванию таймера раз в 1мс. Когда мы хотим поставить выдержку, то просто берем текущее значение TimeMs прибавляем к нему нашу выдержку и запоминаем все это в статичной переменной, пусть будет Delay, которая определена непосредственно в той функции автомата которая эту задержку использует. И при каждом следующем входе в автомат она будет проверять нет ли у нас условия Delay >= TimeMs.

То есть автомат мигалка будет выглядеть так:

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
void Blink (void)
{
 
// Переменные объявленные как static не исчезают после выхода из функции. А сохраняют свое состояние.
static uint8_t blink_state = 0;     // Переменная состояния конечного автомата
static uint32_t Delay;              // Переменная программного таймера мигалки, время моргания
 
switch (blink_state)
  {
  case 0:                           // Первый вход, инициализация и первый поджиг. 
               {
               Delay = MainTimerSet(1000);   // Ставим задержку на 1000мс
               LED_ON();                     // Зажигаем диодик
               blink_state = 1;              //  Переходим в следующее состояние автомата
               break;                        //  Выход из состояния
               }
 
  case 1:                                    // Первая стадия рабочего цикла (Не горим) 
             {            
             if ( !MainTimerIsExpired(Delay) ) break;      // Если 1с не прошла - сразу выходим
                                                           // Функция MainTimerIsExpired проверяет по
                                                           // таймерной переменной TimeMs не
                                                           // стал ли Delay меньше чем TimeMs
 
             LED_OFF();                                    // Если секунда прошла, то гасим диодик
             Delay = MainTimerSet(500);                    // Запоминаем время выключенной фазы 0.5с
             blink_state = 2;                              // Переключаем автомат во вторую стадию
             break;                                        // Выход из состояния
             }
 
case 2:                                    // Вторая стадия рабочего цикла (Горим) 
             {            
             if ( !MainTimerIsExpired(Delay) ) break;      // Если 0,5с не прошла - сразу выходим
 
             LED_ON();                                     // Если секунда прошла, то зажигаем диодик
             Delay = MainTimerSet(1000);                   // Запоминаем время включенной фазы 1с
             blink_state = 1;                              // Переключаем автомат в первую стадию
             break;                                        // Выход из состояния
             }
 
default: break
  }
}

Ну, а в самом Main вызов автоматов разных задач выглядит как то так:

1
2
3
4
5
6
7
8
9
10
void main (void)
{
while (1)
  {
  KeyScan();       //  Автомат отвечающий за сканирование клавы бла бла бла
  LCD_process();   // Автомат работающий с дисплеем бла бла бла
 
  Blink();         // Наша мигалка
 
  }

Теперь немного о реализации самой библиотечки таймера.
(далее…)

Read More »

Управление большим количеством светодиодов через Binary Angle Modulation

Вот приспичило вам сделать себе могучую светодиодную хреновину, чтобы моргала и переливалась. Да еще в RGB и плавненько так. Собрали вы это дело, поглядели на количество каналов которыми нужно рулить и призадумались…
 

▌А что не так с ШИМ?
Да все с ним хорошо, только аппаратных каналов обычно всего несколько штук. А программный ШИМ имеет ряд недостатков. Да, можно взять и на базе алгоритма управления кучей сервомашинок, используя всего один таймер собрать многоканальный ШИМ, но сколько у нас будет вызовов прерываний?
 


 

Каждый отдельный фронт потребует своего прерывания на смену уровня. А представьте, что у нас этих каналов будет не 4, а 40? Или 400? Да контроллер из прерываний вылезать не будет. Прерывания будут налезать друг на друга, порождая джиттер. Не говоря уже о том, что все эти каналы надо будет при любом изменении скважности заново сортировать по длительности. В общем, тупилово будет еще то.
 

▌Нас спасет BAM
Но решение есть. Зовется этот метод BAM. Суть его в том, что мы включаем нагрузку импульсами, поразрядно, с длительностью равной весу разряда.

 

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

 

Интегрируется все аналогично обычному ШИМу. Но есть ряд нюансов:

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

 
(далее…)

Read More »

Управление множеством сервомашинок

При построении разных роботов порой приходится использовать несколько сервоприводов. А если это какой-нибудь шестиногий паук, то приводов там этих просто тьма. Как ими управлять? На форуме кое кто даже сокрушался, что ему бы для этих целей плисину применить. Хотя на кой черт там ПЛИСка, когда с рулением даже трех десятков сервоприводов справится самый рядовой микроконтроллер, затребовав под это дело всего один таймер.

Итак, кто не помнит как управляются сервы может прогуляться в старую статью и освежить знания.

Возьмем, для начала, 8 сервомашинок. На каждую серву идет вот такой сигнал:

На каждую серву со своей ноги контроллера должна идти такая вот последовательность. Итого подобие ШИМ’a на 8 каналов. Как сгенерировать эту бодягу? Да проще простого. Принцип тут простой. Импульсы медленные — всего то 50Гц, меняются тоже нечасто — серва штука инерционная, поэтому даже сто раз в секунду ей не подергаешь. Так что времени на обработку у нас вагон и маленькая тележка.

Сами импульсы будут генерироваться одним таймером, в фоновом режиме. Принцип генерации прост: Все импульсы стартуют одновременно, выставляя свои уровни в 1.
Затем в таймер, в регистр сравнения, заносится время длительности первого импульса. По прерыванию сравнения происходит:

  • Сброс бита на порту первого канала
  • Загрузка в регистр сравнения таймера значения длительности второго импульса

(далее…)

Read More »

Организация древовидного меню

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

Попутно, из проекта в проект, развивалась своя псевдоОС — таймеры, события, диспетчеры.
Я ее полностью писал на си, оптимизацией и вылизыванием еще не занимался.

Перебирая разные системы, наткнулся на MicroMenu:

Попробуем разобрать ее на части и прикрутить к системе.
(далее…)

Read More »

Обработка множества инкрементальных энкодеров одновременно

Про инкрементальный энкодер и про обработку его сигналов с помощью МК уже была статья. Вроде-бы ничего сложного — два бита текущего состояния, два бита предыдущего — автомат с 16 состояниями. Рассмотрим эту задачу ещё раз с позиции максимально эффективной (по скорости и размеру кода) обработки сигналов множества энкодеров одновременно.

Обозначим текущее состояние энкодера как «y1» и «y2», а предыдущее, как «x1» и «x2». Всего 4 бита — 16 состояний. Условимся, что направление «Вперёд» у нас будет от первого датчика энкодера ко второму. Запишем все возможные состояния в таблицу.

Таблица 1.
№	y2	y1	x2	x1	Вперёд	Назад	Состояние
------------------------------------------------------------------
0	0	0	0	0	0	0	Стоп	
1	0	0	0	1	0	1	Назад
2	0	0	1	0	1	0	Вперёд
3	0	0	1	1	0	0	Не определено
4	0	1	0	0	1	0	Вперед
5	0	1	0	1	0	0	Стоп
6	0	1	1	0	0	1/0	Назад*	
7	0	1	1	1	0	1	Назад	
8	1	0	0	0	0	1	Назад	
9	1	0	0	1	1/0	0	Вперёд*	
A	1	0	1	0	0	0	Стоп	
B	1	0	1	1	1	0	Вперёд	
C	1	1	0	0	0	0	Не определено	
D	1	1	0	1	1	0	Вперёд	
E	1	1	1	0	0	1	Назад	
F	1	1	1	1	0	0	Стоп

* — строчки 6 и 9 в таблице в принципе означают перемещение назад и вперёд соответственно, в случае если оба датчика энкодера никогда не срабатывают одновременно. Такая ситуация теоретически может иметь место если энкодер это две оптопары и колесо с отверстиями, причем размер отверстия меньше расстояния между оптопарами. На практике это встречается редко, по этому будем иметь этот случай ввиду, но учитывать не будем.
(далее…)

Read More »

AVR. Учебный Курс. Инкрементальный энкодер.

Энкодер это всего лишь цифровой датчик угла поворота, не более того.

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

Если с абсолютным энкодером все просто, то с инкрементальным бывают сложности. Как его обрабатывать?

С Энкодера выходят два сигнала А и В, сдвинутых на 90 градусов по фазе, выглядит это так:

А дальше пляшем от типа энкодера. А они бывают разные. (далее…)

Read More »

AVR. Учебный Курс. Кусочно-линейная аппроксимация

Часто бывает так, что приходится обрабатывать жутко нелинейные величины, задаваемые каким-нибудь извращенным законом. Простейший пример — датчики расстояния SHARP GP2D12. Только поглядите на его характеристику:

Сам черт ногу сломит, а ведь нам бы неплохо иметь выход в человеческих величинах, ну или, хотя бы, линейно зависящие от расстояния. Что делать?

Вариантов тут, на самом деле, всего два. Первый очень быстрый, но жадный до памяти ПЗУ — табличный.
То есть мы просто берем и эту кривулину расписываем в памяти. Например, у нас с 8ми разрядного АЦП идет значение напряжения от 0 до 256, а мы на каждое значение создаем в памяти значение расстояния. Тогда с АЦП сразу гоним в индекс массива, где эти значения хранятся и получаем расстояние:

L=Curve[ADCH];

Недостаток один — прожорливость до памяти, растущая в геометрической прогрессии с ростом разрядности АЦП.

Вариант второй — написать функцию, переводящую одну величину в другую. (далее…)

Read More »

Виртуальная машина и байт код

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

Тут на помощь придет виртуальная машина. Суть в чем — в памяти контроллера, в основную программу, занесены основные процедуры управления устройством. Если это робот, то это могут быть такие простые команды как «вперед», «назад», «повернуть» и так далее.

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

Потом нам нужен обработчик скриптов, который бы брал откуда нибудь, нашу последовательность действий — скрипт и преобразовывал это в вызовы реальных кусков кода -микрооперации.
Обработчик скриптов может быть той же самой задачей диспетчера, запущенной фоном. А откуда он будет брать данные неважно. Их вполне можно засасывать по usart или тащить из EEPROM памяти. А можно загнать на IIC память и получится сменный картридж :)
(далее…)

Read More »

AVR. Учебный курс. Конечный автомат

Каждый кто пытался разбираться с конечными автоматами наверняка натыкался на всякие замудреные графы, какие то графики. Многие посчитав это слишком сложным плюнули и забили. А Зря!

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

Так и в нашем случае — конечный автомат это функция которая запоминает свое состояние и при следующем вызове делает свое черное дело исходя из прошлого опыта. Простой пример — мигалка (псевдокод):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; Глобальные переменные
u08 Blink_State;
 
void Blink(void)
{
if (Blink_State == 1)
	{
	Led_On();
	Blink_State = 0;
	Return;
	}
if (Blink_State == 0)
	{
	Led_Off();
	Blink_State = 1;
	Return;
	}
}

Вызывая эту функцию подряд мы заставим диодик менять свое состояние при каждом вызове.
(далее…)

Read More »

AVR. Учебный курс. Архитектура Программ. Часть 3

Приоритетный диспетчер.
Одной из проблем простого диспетчера является то, что все задачи имеют равный приоритет. С одной стороны, это просто и удобно. С другой — какое-либо важное событие можно прошляпить, пока там конвейер перещёлкает все задачи…

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

Если нужна высокоуровневая система приоритетов, то можно очередь задач превратить в двумерный массив, где вторым этажом будет идти приоритет задачи. Правда при этом увеличиться время обработки конвейера — ведь надо будет сперва прочесать всю очередь в поисках наибольшего элемента. Но тут можно напридумывать кучу оптимизаций. Например, сортировать очередь при постановке задачи на конвейер, либо завести TOP list приоритетов, занося туда значения приоритетов. Тогда диспетчер, обрабатывая очередь, сразу будет искать нужный элемент, ориентируясь по TOP листу. Но вот так, на вскидку, я не берусь сказать какой из приемов будет эффективней/компактней/быстрей.
(далее…)

Read More »

AVR. Учебный Курс. Архитектура Программ Часть 2

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

Во первых тут очередь выполнения задач не жестко заданная, а динамическая, конвеерного типа. То есть у нас есть в памяти массив из указателей на задачи-функции. Диспетчер берет указатель и, если он не указывает на Idle, осуществлет переход по этому адресу. Предварительно удалив его из очереди и подкинув очередь.

Заброс указателей-задач в очередь осуществляется другими задачами и прерываниями, а также программными таймерами. Собственно, принцип передачи управления от задачи к задаче похож на флаговый автомат — работаем через посредника. Только у нас тут отстутствуют проверки флагов, а переход делается диспетчером. За счет этого увеличение числа задач не сказывается на увеличении размера управляющей структуры.

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

А теперь подробно распишу тот диспетчер который стоит в 90% моих проектов на Си.
(далее…)

Read More »

AVR. Учебный Курс. Архитектура Программ

Все учебные курсы по микроконтроллерам которые я встречал (в том числе, к сожалению, и мой ассемблерный, но я надеюсь это постепенно поправить) страдают одной и той же проблемой.
В курсе бросаются строить дом не заложив фундамент. Только показав на примере как мигнуть светодиодом, сразу же кидаются в периферию. Начинают осваивать ШИМ-ы, таймеры, подключать дисплеи и всякие термодатчики.
С одной стороны это понятно — хочется действа и результата мгновенно. С другой — рано или поздно такой подход упрется в тот факт, что программа, надстраиваемая без четкой идеологии, просто обрушится под своей сложностью. Сделав невозможным дальнейшее развитие.
Итогом становится либо рождение жутких программных уродцев, либо миллионы вопросов на форуме вида «а как бы мне все сделать одновременно, а то частоты контроллера уже на все не хватает».
 

Самое интересное, что правильной организации программы учат программистов в ВУЗах, но вот только к микроконтроллеру народ обычно идет не от программирования, а от железа. А, как показала практика обучения в ВУЗе, электронщиков толковому программингу практически не обучают :( Приходится все додумывать самостоятельно.

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

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

 

Итак, я для себя выделяю следующие структуры, по порядку возростания сложности конструкции и количеству управляющего кода:
 

  • Суперцикл
  • Суперцикл+прерывания
  • Флаговый автомат
  • Диспетчер
  • Приоритетный диспетчер
  • Кооперативная RTOS
  • Вытесняющая RTOS

 

А теперь подробно по каждому пункту: (далее…)

Read More »

М. Дамке «Операционные системы микроЭВМ»

Автор:		М. Дамке
Название: 	Операционные системы микроЭВМ
Издательство: 	Финансы и статистика

Старожил сайта и один из самых активных и толковых комментаторов, камрад SWG сделал замечательную вещь— отсканировал и пожал в DejaVu книгу по написанию операционных систем под микро-ЭВМ.

Я пока основательно не врубался, по диагонали пролистал — рулез! Особого грузилова нет, все в виде алгоритмов и концепций. Написано все простым и понятным языком. В частности все разобрано на примере Z80 и i8080 В общем, замечательная книга. Как говорил SWG где то в комментах: «Почитай эту книгу и изобретение велосипедов пойдет куда веселей» :)

Read More »

Ревич «Практическое программирование Микроконтроллеров AVR на языке ассемблера»

Автор:		Юрий Ревич
Название: 	Практическое программирование 
		Микроконтроллеров AVR на
 		языке ассемблера
Издательство: 	Bhv

Отличная книга. Из серии настольных. Автор очень доступно, подробно, без занудства и заумностей рассказывает как использовать все то богатство, что разработчики из Atmel напихали в свой контроллер, причем книжка свежая, а значит контроллеры там уже из современных. Разобраны все периферийные устройства, а также на конкретных практических примерах наглядно показано, как воспроизводить звуки через ШИМ, как писать и читать на MMC флешки, как пользоваться компараторами, АЦП, SPI, TWI. Есть целый раздел посвященный математике на микроконтроллерах. Сложение, деление, умножение целых и дробных чисел. Как со знаком, так и без. В сочетании с даташитом или книгой Евстифеева дает практически полную информацию для работы с AVR. Также есть глава посвященная связи МК с компом. Есть пример проги на Delphi для работы с COM портом. В общем, настоятельно рекомендую заиметь, не помешает.

Read More »

AVR. Учебный курс. Подключение к AVR LCD дисплея HD44780

Сегодня разменял четверть века
Сегодня разменял четверть века!
Так случилось, что прикупил я тут себе поприколу LCD дисплейчик две строки по восемь символов. Валялся он в ящике валялся, да чегото поперло меня и решил я его заюзать, попутно вкурив в его работу. О том как подключить к AVR LCD дисплей я вам сейчас и поведаю.

Для начала оговорюсь сразу, что речь тут пойдет о LCD индикаторах на контроллере HD44780, который стал промышленным стандартом де-факто на рынке цифро-буквенных дисплеев. Продается везде где только можно, стоит недорого (8х2 мне обошелся порядка 150 рублей), а также под него написана куча кода. Я же, как обычно, решил изобрести велосипед и сварганить свою собственную тру-библиотеку для работы с этим типом индикаторов. Разумеется на ассемблере, а на чем же еще? ;)

(далее…)

Read More »

AVR. Учебный курс. Процедура сканирования клавиатуры

Итак, клавиатуру я сделал и написал процедуру сканирующую клавиатурную матрицу 4х4 кнопки. Пора бы рассказать как организовать опрос такой клавы. Напомню, что клава представляет из себя строки, висящие на портах и столбцы, которые сканируются другим портом. Код написан для контроллера ATMega8535, но благодаря тому, что все там указано в виде макросов его можно быстро портировать под любой другой контроллер класса Mega, а также под большую часть современных Tiny. Хотя в случае с Tiny может быть некоторый затык ввиду неполного набора команд у них. Придется чуток дорабатывать напильником.

(далее…)

Read More »

Работа с АЦП. Программные средства повышения точности

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

Суть усреднения в том, что у нас есть статичный (считаем, что за время измерения сигнал не меняется) сигнал к которому подмешан шум. Шум возникает изза работы транзисторов, из-за колебания опорного напряжения, помех, наведенных на сигнальные линии. Да от чего только он не возникает. Особенность шума в том что он, как правило, хаотичен. Так что во время нашего замера может меняться как в меньшую так и в большую сторону. Тут то мы его и прижучим.

Берем и снимаем не одно измерение, а сразу кучу. А потом берем по ним среднее арифметическое. Так как полезный сигнал у нас константен, то его составляющая такой и останется, как ее не усредняй, а вот шум изрядно приглушит. И чем больше выборок мы сделаем, тем сильней задавит шумовую составляющую. Западло этого метода очевидно — резко снижается скорость обработки. Так как вместо одной выборки нам приходится делать серию и объявлять ее как одну, но это неизбежное зло.

В качестве демонстрации метода я приведу пример усреднения. Программка простая, хватает 64 выборки, усредняет их и отправляет по UART. Сразу отмечу тот факт, что для эффективного подавления шума нужно чтобы частота выборок была ниже частоты всяких паразитных колебаний (вроде 50Гц наводок от сети) раза в два три, иначе у нас эти колебания благополучно пролезут как полезный сигнал. А еще число выборок нужно брать кратным двойке, чтобы можно было делить простым сдвигом. Впрочем, смотрите на код, там будет более понятно. Весь код я выкладывать не буду, только главный файл. Все инициализации АЦП и UART, а также ряд служебных процедурок я оставлю за кадром. Если интересно, то вы всегда можете скачать проект и посмотреть сами. Сбор числа у меня идет в прерывании от АЦП, а деление в прерывании по передаче. Так минимизируется число действий выполняемых процом. Хотя растягивание прерываний это не есть гуд. Но городить флаговую операционную систему мне тут впадлу, впрочем, дойдет и до нее время. (далее…)

Read More »

Матричная клавиатура

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

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

Считывающий порт включается в режиме Pull-up входа, то есть вход с подтягивающими резисторами. Если контроллер это не поддерживает, то эти резисторы надо повесить снаружи.

Сканирующий порт работает в режиме выхода, он подключен к столбцам. Столбцы должны быть подтянуты резисторами к питанию. Впрочем, если используется полноценный Push-Pull то подтяжка не нужна — выход сам поднимет ногу на нужный уровень.

(далее…)

Read More »

AVR. Учебный курс. Работа с портами ввода-вывода. Практика

Вот ты читаешь сейчас это и думаешь — память, регистры, стек и прочее это хорошо. Но ведь это не пощупать, не увидеть. Разве что в симуляторе, но я и на дельфи с тем же условием могу накодить. Где мясо!!!
 
В других курсах там, чуть ли не с первых строк, делают что то существенное — диодиком мигают и говорят, что это наш Hello World. А тут? Гыде???
 

Да-да-да, я тебя понимаю. Более того, наверняка ты уже сбегал к конкурентам и помигал у них диодиком ;)))) Ничего, простительно.
 

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

О портах было рассказано, шаблон программы у вас уже есть, так что сразу и начнем.
 
(далее…)

Read More »

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

Бывает такая ситуация, когда надо на один периферийный девайс повесить много разных задач, а он всего один и что то надо с этим делать.

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

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

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

То есть в свитче вида:

1
2
3
4
5
6
7
switch(x)
	{
	1: Действие 1
	2: Действие 2
	3: Действие 3
	4: Действие 4
	}

Будет последовательное сравнение х вначале с 1, потом с 2, потом с 3 и так до перебора всех вариантов. А в таком случае реакция на Действие 1 будет быстрей чем реакция на Действие 4. Особо важно это при расчете точных временных интервалов на таймере.

Но есть простое решение этой проблемы — индексный переход. Достаточно перед тем как мы начнем ожидать прерывание предварительно загрузить в переменные (а можно и сразу в индексный регистр Z) направление куда нам надо перенаправить наш вектор и воткнуть в обработчик прерывания индексный переход. И вуаля! Переход будет туда куда нужно, без всякого сравнения вариантов.
(далее…)

Read More »