AVR. Учебный Курс. Программирование на Си. Часть 1

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

К сожалению все хотят результат немедленно. Поэтому я решил пойти с другой стороны — сделать обучалку по Си, но с показом его нижнего белья. Хороший программист-эмбеддер всегда крепко держит свою железку за шкварник, не давая ей ни шагу ступить без разрешения. Так что будет вначале Си код, потом то что родил компилятор и как все это работает на самом деле :)

С другой стороны у Си сильная сторона это переносимость кода. Если, конечно, писать все правильно. Разделяя алгоритмы работы и их железные реализации в разные части проекта. Тогда для переноса алгоритма в другой МК достаточно будет переписать только интерфейсный слой, где прописано все обращение к железу, а весь рабочий код оставить как есть. И, конечно же, читаемость. Сишный исходник проще понять с первого взгляда (хотя.. мне, например, уже пофигу на что фтыкать — хоть си, хоть асм :) ), но, опять же, если правильно все написать. Этим моментам я тоже буду уделять внимание.

В качестве подопытной железки на которой будет ставиться львинная доля всех примеров будет моя отладочная плата PinBoard.

Дальше все будет разжевано буквально по шагам для старта с полного нуля.

Первая программа на Си для AVR

Выбор компилятора и установка среды
Для AVR существует множество разных компиляторов Си:
В первую очередь это IAR AVR C — почти однозначно признается лучшим компилятором для AVR, т.к. сам контроллер создавался тесном сотрудничистве Atmel и спецов из IAR. Но за все приходится платить. И этот компилятор мало того, что является дорогущим коммерческим софтом, так еще обладает такой прорвой настроек, что просто взять и скомпилить в нем это надо постраться. У меня с ним правда не срослось дружбы, проект загнивал на странных ошибках на этапе линковки (позже выяснил, что это был кривой кряк).

Вторым идет WinAVR GCC — мощный оптимизирующий компилятор. Полный опенсорц, кроссплатформенный, в общем, все радости жизни. Еще он отлично интегрируется в AVR Studio позволяя вести отладку прямо там, что адски удобно. В общем, я выбрал его.

Также есть CodeVision AVR C — очень популярный компилятор. Стал популярен в связи со своей простотой. Рабочую программу в нем получить можно уже через несколько минут — мастер стартового кода этом сильно способствует, штампуя стандартыне инициализации всяких уартов. Честно говоря, я как то с подозрением к нему отношусь — как то раз приходилось дизасмить прогу написаную этим компилером, каша какая то а не код получалась. Жуткое количество ненужных телодвижений и операций, что выливалось в неслабый обьем кода и медленное быстродействие. Впрочем, возможно тут была ошибка в ДНК писавшего исходную прошивку. Плюс он хочет денег. Не так много как IAR, но ощутимо. А в деморежиме дает писать не более чем 2кб кода.
Кряк конечно есть, но если уж воровать, так миллион, в смысле IAR :)

Еще есть Image Craft AVR C и MicroC от микроэлектроники. Ни тем ни другим пользоваться не приходилось, но вот SWG очень уж нахваливает MicroPascal, мол жутко удобная среда программирования и библиотеки. Думаю MicroC не хуже будет, но тоже платный.

Как я уже сказал, я выбра WinAVR по трем причинам: халявный, интегрируется в AVR Studio и под него написана просто прорва готового кода на все случаи жизни.

Так что качай себе инсталяху WinAVR с официального источника и AVR Studio. Далее вначале ставится студия, потом, сверху, накатывается WinAVR и цепляется к студии в виде плагина. Настоятельно рекомендую ставить WinAVR по короткому пути, что то вроде C:\WinAVR тем самым ты избежишь кучи проблем с путями.

Cоздание проекта
Итак, студия поставлена, Си прикручен, пора бы и попробовать что нибудь запрограммировать. Начнем с простого, самого простого. Запускай студию, выбирай там новый проект, в качестве компилятора AVR GCC и вписывай название проекта.

Также не забудь поставить галочу Create Folder, чтобы у тебя все сложилось в одной директории. Ну и укажи место Location, где будет лежать проект. Указывай по короткому пути, что то вроде C:\AVR\ Как показывает практика, чем короче путь тем лучше — меньше проблем при компиляции и линковке проектов.

Проц у меня в Pinboard по дефолту ATmega16, поэтому выбираю его. Те же у кого в PinBoard стоит Mega32 (по спец заказу ставил некторым :) ) выбирают, соответственно ее.

Открывается рабочее поле с пустым *.c файлом.

Теперь не помешает настроить отображение путей в закладках студии. Для этого слазь по адресу:
Меню Tools — Options — General — FileTabs и выбираем в выпадающем списке «Filename Only». Иначе работать будет невозможно — на вкладке будет полный путь файла и на экране будет не более двух трех вкладок.

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

Тыкай в кнопку с шестеренкой.

Это настройки твоего проекта, а точнее настройки автоматической генерации make файла. На первой странице надо всего лишь вписать частоту на которой будет работать твой МК. Это зависит от фьюз битов, так что считаем что частота у нас 8000000Гц.
Также обрати внимание на строку оптимизации. Сейчас там стоит -Os это оптимизация по размеру. Пока оставь как есть, потом можешь попробовать поиграться с этим параметром. -O0 это отстутсвие оптимизации вообще.

Следующим шагом будет настройка путей. Первым делом добавь туда директорию твоего проекта — будешь туда подкладывать сторонние библиотеки. В списке появится путь «.\»

Make файл сгенерирован, его ты можешь поглядеть в папке default в своем проекте, просто пробегись глазами, посмотри что там есть.

На этом пока все. Жми везде ОК и переходи в исходник.

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

Работать будет так:
При приходе по COM порту единички (код 0х31) будем зажигать диодик, а при приходе нуля (код 0х30) гасить. Причем сделано будет все на прерываниях, а фоновой задачей будет мигание другого диода. Простенько и со смыслом.

Собираем схему
Нам надо соединить модуль USB-USART конвертера с выводами USART микроконтроллера. Для этого берем перемычку из двух проводков и накидывам на штырьки крест накрест. То есть Rx контроллера соединяем с Tx конвертера, а Tx конвертера с Rx контроллера.

Кроме того, через USART мы теперь сможем достучаться до загрузчика (Pinboard идет с уже прошитым загрузчиком) и прошить наш контроллер не используя программатор.

Также накинем джамперы, соединяющие LED1 и LED2. Тем самым мы подключим светодиоды LED1 и LED2 к выводам PD4 и PD5 соотверственно.

Получится, в итоге вот такая схема:

Подключение остальных выводов, питания, сброса не рассматриваю, оно стандартное

Пишем код

Сразу оговорюсь, что я не буду углубляться конкретно в описание самого языка Си. Для этого существует просто колоссальное количество материала, начиная от классики «Язык программирования Си» от K&R и заканчивая разными методичками.

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

Посмотреть оглавление.

Там правда еще не все главы перенесены, но, думаю, это ненадолго.

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

Добавляем библиотеки.
Первым делом мы добавляем нужные библиотеки и заголовки с определениями. Ведь Си это универсальный язык и ему надо обьяснить что мы работаем именно с AVR, так что вписывай в исходник строку:

1
#include <avr/io.h>

Этот файл находится в папке WinAVR и в нем содержится описание всех регистров и портов контроллера. Причем там все хитро, с привязкой к конкретному контроллеру, который передается компилятором через make файл в параметре MCU и на основании этой переменной в твой проект подключается заголовочный файл с описанием адресов всех портов и регистров именно на этот контроллер. Во как! Без него тоже можно, но тогда ты не сможешь использовать символические имена регистров вроде SREG или UDR и придется помнить адрес каждого вроде «0xC1″, а это голову сломать.

Сама же команда #include <имя файла> позволяет добавить в твой проект содержимое любого текстового файла, например, файл с описанием функций или кусок другого кода. А чтобы директива могла этот файл найти мы и указывали пути к нашему проекту (директория WinAVR там уже по дефолту прописана).

Главная функция.
Программа на языке Си вся состоит из функций. Они могут быть вложенными и вызываться друг из друга в любом порядке и разными способами. Каждая функция имеет три обязательных параметра:

  • Возвращаемое значение, например, sin(x) возвращает значение синуса икс. Как в математике, короче.
  • Передаваемые параметры, тот самый икс.
  • Тело функции.

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

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

1
2
3
4
5
int main(void)
{
 
return 0;
}

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

Разберем что же мы сделали.
int это тип данных которая функция main возвращает. Узнать подробней о типах данных

Конечно, в микроконтроллере main ничего вернуть в принципе не может и по идее должна быть void main(void), но GCC изначально заточен на PC и там программа может вернуть значение операционной системе по завершении. Поэтому GCC на void main(void) ругается Warning’ом.

Это не ошибка, работать будет, но я не люблю варнинги.

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

Вот такие вот { } фигурные скобочки это программный блок, в данном случае тело функции main, там будет распологаться код.

return — это возвращаемое значение, которое функция main отдаст при завершении, поскольку у нас int, то есть число то вернуть мы должны число. Хотя это все равно не имеет смысла, т.к. на микроконтроллере из main нам выходить разве что в никуда. Я возвращаю нуль. Ибо нефиг. А компилятор обычно умный и на этот случай код не генерит.
Хотя, если извратиться, то из main на МК выйти можно — например вывалиться в секцию бутлоадера и исполнить ее, но тут уже потребуется низкоуровневое ковыряние прошивки, чтобы подправить адреса перехода. Ниже ты сам увидишь и поймешь как это сделать. Зачем? Вот это уже другой вопрос, в 99.999% случаев это нафиг не надо :)

Сделали, поехали дальше. Добавим переменную, она нам не особо нужна и без нужны вводить переменные не стоит, но мы же учимся. Если переменные добавляются внутри тела функции — то они локальные и существуют только в этой функции. Когда из функции выходишь эти переменные удаляются, а память ОЗУ отдается под более важные нужды. Подробней о локальных и глобальных переменных, а также времени жизни оных.

1
2
3
4
5
6
int main(void)
{
unsigned char i; 
 
return 0;
}

unsigned значит беззнаковый. Дело в том, что в двоичном представлении у нас старший бит отводится под знак, а значит в один байт (char) влазит число +127/-128, но если знак отбросить то влезет уже от 0 до 255. Обычно знак не нужен. Так что unsigned.
i — это всего лишь имя переменной. Не более того.

Теперь надо проинициализировать порты и UART. Конечно, можно взять и подключить библиотеку и вызвать какой нибудь UartInit(9600); но тогда ты не узнаешь что же произошло на самом деле.

Делаем так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(void)
{
unsigned char i;
 
#define XTAL 8000000L
#define baudrate 9600L
#define bauddivider (XTAL/(16*baudrate)-1)
#define HI(x) ((x)>>8)
#define LO(x) ((x)& 0xFF)
 
UBRRL = LO(bauddivider);
UBRRH = HI(bauddivider);
UCSRA = 0;
UCSRB = 1<<RXEN|1<<TXEN|1<<RXCIE|0<<TXCIE;
UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
}

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

Они облегчат твои рутинные операции по вычислении нужных коэффициентов. В первой строке мы говорим что вместо XTAL можно смело подставлять 8000000, а L- указание типа, мол long — это тактовая частота процессора. То же самое baudrate — частота передачи данных по UART.

bauddivider уже сложней, вместо него будет подставлятся выражение вычисленное по формуле из двух предыдущих.
Ну, а LO и HI из этого результата возьмут младший и старший байты, т.к. в один байт оно явно может не влезть. В HI делается сдвиг икса (входной параметр макроса) восемь раз в вправо, в результате от него останется только старший байт. А в LO мы делаем побитовое И с числом 00FF, в результате останется только младший байт.

Так что все что сделано как #define можно смело выкинуть, а нужные числа подсчитать на калькуляторе и сразу же вписать их в строки UBBRL = …. и UBBRH = …..

Можно. Но! Делать этого КАТЕГОРИЧЕСКИ НЕЛЬЗЯ!

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

Дальше все просто:
Все эти «UBRRL и Со» это регистры конфигурации UART передатчика с помощью которого мы будем общаться с миром. И сейчас мы присвоили им нужные значения, настроив на нужную скорость и нужный режим.

Запись вида 1<<RXEN Означает следующее: взять 1 и поставить ее на место RXEN в байте. RXEN это 4й бит регистра UCSRB, так что 1<<RXEN образует двоичное число 00010000, TXEN — это 3й бит, а 1<<TXEN даст 00001000. Одиночная «|» это побитовое ИЛИ, так что 00010000 | 00001000 = 00011000. Таким же образом выставляются и добавляются в общуюу кучу остальные необходимые биты конфигурации. В итоге, собраное число записывается в UCSRB. Подробней расписано в даташите на МК в разделе USART. Так что не отвлекаемся на технические детали.

Готово, пора бы посмотреть что получилось. Жми на компиляцию и запуск эмуляции (Ctrl+F7).

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

Пора сделать ШАГ! Нажми F11!
Видишь стрелочка сразу же скакнула на вторую строку, на UBRRH = HI(bauddivider);

Дело в том, что изначально, на самом деле, она стояла на строке UBRRL = LO(bauddivider); Ведь то что у нас в define это не код, а просто предварительные вычисления, вот симулятор немного и затупил. Но теперь он осознал, первая инструкция выполнена и если ты залезешь в дерево I/O View, в раздел USART и поглядишь там на байт UBBRL то увидишь, что там значение то уже есть! 0х33.

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

Вскрытие
Теперь сбрось симуляцию в ноль. Нажми там Reset (Shift+F5). Открывай дизассемблированный листинг, сейчас ты увидишь что происходит в контроллере в самом деле. View -> Disassembler. И не ЫЫАААА!!! Ассемблер!!! УЖОС!!! А НАДО. Чтобы потом, когда что то пойдет не так, не тупил в код и не задавал ламерских вопросах на форумах, а сразу же лез в потроха и смотрел где у тебя затык. Ничего там страшного нет.

Вначале будет ботва из серии:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+00000000:   940C002A    JMP       0x0000002A     Jump
+00000002:   940C0034    JMP       0x00000034     Jump
+00000004:   940C0034    JMP       0x00000034     Jump
+00000006:   940C0034    JMP       0x00000034     Jump
+00000008:   940C0034    JMP       0x00000034     Jump
+0000000A:   940C0034    JMP       0x00000034     Jump
+0000000C:   940C0034    JMP       0x00000034     Jump
+0000000E:   940C0034    JMP       0x00000034     Jump
+00000010:   940C0034    JMP       0x00000034     Jump
+00000012:   940C0034    JMP       0x00000034     Jump
+00000014:   940C0034    JMP       0x00000034     Jump
+00000016:   940C0034    JMP       0x00000034     Jump
+00000018:   940C0034    JMP       0x00000034     Jump
+0000001A:   940C0034    JMP       0x00000034     Jump
+0000001C:   940C0034    JMP       0x00000034     Jump
+0000001E:   940C0034    JMP       0x00000034     Jump
+00000020:   940C0034    JMP       0x00000034     Jump
+00000022:   940C0034    JMP       0x00000034     Jump
+00000024:   940C0034    JMP       0x00000034     Jump
+00000026:   940C0034    JMP       0x00000034     Jump
+00000028:   940C0034    JMP       0x00000034     Jump

Это таблица векторов прерываний. К ней мы еще вернемся, пока же просто посмотри и запомни, что она есть. Первая колонка — адрес ячейки флеша в которой лежит команда, вторая код команды третья мнемоника команды, та самая ассемблерная инструкция, третья операнды команды. Ну и автоматический коммент.
Так вот, если ты посмотришь, то тут сплошные переходы. А код команды JMP четырех байтный, в нем содержится адрес перехода, записанный задом наперед — младший байт по младшему адресу и код команды перехода 940C

Дальше идет предварительные приготовления, Си без них не может.

1
+0000002A:   2411        CLR       R1             Clear Register

Обнуление регистра R1

1
+0000002B:   BE1F        OUT       0x3F,R1        Out to I/O location

Запись этого нуля по адресу 0x3F, Если ты поглядишь в колонку I/O view, то ты увидишь что адрес 0x3F это адрес регистра SREG — флагового регистра контроллера. Т.е. мы обнуляем SREG, чтобы запустить программу на нулевых условиях.

1
2
3
4
+0000002C:   E5CF        LDI       R28,0x5F       Load immediate
+0000002D:   E0D4        LDI       R29,0x04       Load immediate
+0000002E:   BFDE        OUT       0x3E,R29       Out to I/O location
+0000002F:   BFCD        OUT       0x3D,R28       Out to I/O location

Это загрузка указателя стека. Напрямую грузить в I/O регистры нельзя, только через промежуточный регистр. Поэтому сначала LDI в промежуточный, а потом оттуда OUT в I/O. О стеке я тоже еще расскажу подробней. Пока же знай, что это такая динамическая область памяти, висит в конце ОЗУ и хранит в себе адреса и промежуточные переменные. Вот сейчас мы указали на то, откуда у нас будет начинаться стек.

1
+00000030:   940E0036    CALL      0x00000036     Call subroutine

Вот тут, собственно, идет переход к функции main. А через три команды как раз начинается Main. И переход идет через CALL, с сохранением адреса в стеке. При этом бездарно просерается два байта ОЗУ, а их всего 1024. =) Низачот! Впрочем, что то мне подсказывает, что обьявление main как inline int main(void) решит эту проблему, но не пробовал. Можешь сам проверить.

1
+00000032:   940C0041    JMP       0x00000041     Jump

Прыжок в сааааамый конец программы, а там у нас запрет прерываний и зацикливание наглухо само на себя:

1
2
+00000041:   94F8        CLI                      Global Interrupt Disable
+00000042:   CFFF        RJMP      PC-0x0000      Relative jump

Это на случай непредвиденых обстоятельств, например выхода из функции main. Из такого зацикливания контроллер можно вывести либо аппаратным сбросом, либо, что вероятней, сбросом от сторожевой собаки — watchdog. Ну или, как я говорил выше, подправить это мест в хекс редакторе и ускакать куда нам душе угодно. Также обрати внимание на то, что бывает два типа переходов JMP и RJMP первый это прямой переход по адресу. Он занимает четыре байта и может сделать прямой переход по всей области памяти. Второй тип перехода — RJMP — относительный. Его команда занимает два байта, но переход он делает от текущего положения (адреса) на 1024 шага вперед или назад. И в его параметрах указывается смещение от текущей точки. Используется чаще, т.к. занимает в два раза меньше места во флеше, а длинные прееходы нужны редко.

1
+00000034:   940C0000    JMP       0x00000000     Jump

А это прыжок в самое начало кода. Перезагрузка своего рода. Можешь проверить, все вектора прыгают сюда. Из этого вывод — если ты сейчас разрешишь прерывания (они по дефолту запрещены) и у тебя прерывание пройзойдет, а обработчика нет, то будет программный сброс — программу кинет в самое начало.

Функция main. Все аналогично, даже можно и не описывать. Посмотри только что в регистры заносится уже вычисленное число. Препроцессор компилятора рулит!!! Так что никаких «магических» чисел!

1
2
3
4
5
6
7
8
9
10
11
12
+00000036:   E383        LDI       R24,0x33       Load immediate
+00000037:   B989        OUT       0x09,R24       Out to I/O location
15:       UBRRH = HI(bauddivider);
+00000038:   BC10        OUT       0x20,R1        Out to I/O location
16:       UCSRA = 0;
+00000039:   B81B        OUT       0x0B,R1        Out to I/O location
17:       UCSRB = 1<<RXEN|1<<TXEN|1<<RXCIE|0<<TXCIE;
+0000003A:   E988        LDI       R24,0x98       Load immediate
+0000003B:   B98A        OUT       0x0A,R24       Out to I/O location
18:       UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
+0000003C:   E886        LDI       R24,0x86       Load immediate
+0000003D:   BD80        OUT       0x20,R24       Out to I/O location

А вот тут косяк:

1
2
3
+0000003E:   E080        LDI       R24,0x00       Load immediate
+0000003F:   E090        LDI       R25,0x00       Load immediate
+00000040:   9508        RET                      Subroutine return

Спрашивается, для чего это компилятор добавляет такую ботву? А это не что иное, как Return 0, функцию то мы определили как int main(void) вот и просрали еще целых четыре байта не пойми на что :) А если сделать void main(void) то останется только RET, но появится варнинг, что мол у нас функция main ничего не возвращает. В общем, поступай как хошь :)

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

Продолжение следует через пару дней …

А пока вот тебе готовый проект с тем, что есть сейчас

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

224 thoughts on “AVR. Учебный Курс. Программирование на Си. Часть 1”

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

      1. «Си — хороший язык для плохих программ.»

        Глупости пишете,молодой человек. Не так уж на свете много плохих ЯП, как много плохих реализаций. Плюс, не надо забывать, что в любом проекте есть такие вещи, как бюджет и сроки. Читайте Йордона «Путь камикадзе».

        1. По поводу хорошего языка — это не мое личное мнение. Думаю вы не вникли в суть самой фразы.
          «Си — инструмент, острый, как бритва: с его помощью можно создать и элегантную программу, и кровавое месиво» (Керниган). Я просто немного перефразировал.

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

        1. Вот потому то я ценю комментарии. Например такие как, у DI Halt-а. Уникальные, запоминающиеся, понятные. А три килобайта кода просмотренные через год после написания больше напоминают мне китайскую граммоту и разбираться в них приходится практически заново.
          Кроме того использование макровозможностей языка тоже очень помогает читабельности.

        2. Грамотно написаная прога даже на асме не нуждается в комментариях! А кашу можно устроить и на си и никакие коменты тут не помогут.

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

          1. > Грамотно написаная прога даже на асме не нуждается в комментариях!

            Достаточно опасное заблуждение. Вероятно, оно простительно при программировании микроконтроллера с 1024 байтами памяти, но при создании большого проекта, включающего в себя множество аппаратно-зависимых частей (под совершенно разные платформы), да ещё и такого, над которым работает команда из более, чем 2 человек, уже в обязательном порядке требует четкого документирования кода.

            А подход некоторых программистов, изложенный тобой, часто ведёт к катастрофическим последствиям в виде срыва сроков сдачи проекта.

  1. Не удалось мне в IAR сделать main встраиваемой. Это какой-то вопрос философии С.
    С другой стороны, какой смысл использовать С в проектах, где на счету каждый байт.

  2. > Если переменные добавляются внутри тела функции — то они локальные и существуют только в этой функции

    тут более обще: если переменные объявлены внутри блока ограниченного {}, они существуют внутри этого блока:
    int main()
    {
    int TEST = 1;
    {
    int TEST = 2;
    /* point_1 */
    }
    /* point_2 */
    return 0;
    }
    будет ли ругаться компилер — проверьте :)
    это наводит на такие извраты (хотя как посмотреть), типа локальных функций:
    int main()
    {
    int foo(int v)
    {
    return v*v;
    }

    int r = foo(2);

    return 0;
    }

    И ещё, я тут писал уже: http://hatred.homelinux.net/wiki/zhurnal:2009-10-02_13.44_0b00100100,
    у gcc, и у gcc-avr в частности (и как полагается в WinAVR gcc тоже), есть удобная форма
    представления бинарных констант: 0b11000110, по аналогии 0xFF для шестнадцатиричных систем.

      1. А в виде ассемблерного кода они никаких отличий не несут :)
        вот исходный вариант, фунции отличаются только именами и областью видимости:
        — cut —
        int bar(int f)
        {
        return f*f;
        }

        int main()
        {
        int foo(int v)
        {
        return v*v;
        }

        int r = foo(2);
        r = bar(r);

        return 0;
        }
        — cut —

        все стороннее не инклужу, пока — за ненадобностью.
        вот это код после обработки avr-gcc -S -mmcu=attiny2313 test.c:
        — cut —
        .file «test.c»
        __SREG__ = 0x3f
        __SP_H__ = 0x3e
        __SP_L__ = 0x3d
        __tmp_reg__ = 0
        __zero_reg__ = 1
        .global __do_copy_data
        .global __do_clear_bss
        .text
        .global bar
        .type bar, @function
        bar:
        push r29
        push r28
        rcall .
        in r28,__SP_L__
        in r29,__SP_H__
        /* prologue: function */
        /* frame size = 2 */
        std Y+2,r25
        std Y+1,r24
        ldd r24,Y+1
        ldd r25,Y+2
        ldd r18,Y+1
        ldd r19,Y+2
        movw r22,r18
        rcall __mulhi3
        /* epilogue start */
        pop __tmp_reg__
        pop __tmp_reg__
        pop r28
        pop r29
        ret
        .size bar, .-bar
        .type foo.1195, @function
        foo.1195:
        push r29
        push r28
        rcall .
        in r28,__SP_L__
        in r29,__SP_H__
        /* prologue: function */
        /* frame size = 2 */
        std Y+2,r25
        std Y+1,r24
        ldd r24,Y+1
        ldd r25,Y+2
        ldd r18,Y+1
        ldd r19,Y+2
        movw r22,r18
        rcall __mulhi3
        /* epilogue start */
        pop __tmp_reg__
        pop __tmp_reg__
        pop r28
        pop r29
        ret
        .size foo.1195, .-foo.1195
        .global main
        .type main, @function
        main:
        push r29
        push r28
        rcall .
        in r28,__SP_L__
        in r29,__SP_H__
        /* prologue: function */
        /* frame size = 2 */
        ldi r24,lo8(2)
        ldi r25,hi8(2)
        rcall foo.1195
        std Y+2,r25
        std Y+1,r24
        ldd r24,Y+1
        ldd r25,Y+2
        rcall bar
        std Y+2,r25
        std Y+1,r24
        ldi r24,lo8(0)
        ldi r25,hi8(0)
        /* epilogue start */
        pop __tmp_reg__
        pop __tmp_reg__
        pop r28
        pop r29
        ret
        .size main, .-main
        — cut —

        Оптимизация не включена, т.е. -O0, иначе он вообще там кода почти не оставляет, но это и правильно, и логично. Кстати, gcc -S — бывает полезно посмотреть, как там компилятор думает, и как он может наоптимиздить.

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

  3. у автора талант объяснять сложные вещи простым языком. Таких бы как он, да в школу и институты, и на выходе мы бы получили огромное количество талантливых, увлеченных и обученных детей, вместо менеджеров с высшим образованием. :(

    1. +миллион… а кроме всего прочего, взрослых людей, как я, всю жизнь живших в IT «высокого уровня», на твердую «4» знающих физику, умеющих паять, хлебнувших современного регионального высшего образования и подавившихся… вдруг ставших эмбеддерами:-))) Полгода назад я еще только с огромным интересом читал статьи Ди, ставил WinAVR, спотыкался о 16-ричные числа, тыркался в Протеус, пытался собирать что-то на макетке(УЖОС!)… Сейчас прохожу этап тестирования первого условно-коммерческого девайса(полный цикл разработки, от пустого экрана WINAVR до прототипа(ЛУТ, SSOP28+SOIC8+1206)), разорился на минидрель от СТ, станцию Lukey702, сделал сам PinBoard по доке от Ди(первая поделка ЛУТ-ом). Это я к чему? Да к тому, что мне уже почти 30, а КАК БЫ ЭТО УВЛЕКЛО смышленого 10-12 летнего пацана? А у нас по телеку Дом-2 и прочая гомосятина, дети растут — смотреть страшно… А я в 6-8 классах приходил со школы, а по телеку показывали телеуроки по физике, химии, ин. языкам… Сказать по правде, наверное именно этому я обязан своей тягой к знаниям, к новому… И если бы такие люди, как Ди, смогли бы объединиться против разрушения мозгов будущего поколения, было бы реально здорово…

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

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

        1. Воистину так… Только вот для того, чтобы организовать кружок радиоэлектроники, надо пройти не одну бюрократическую инстанцию(наверное, не стоит спорить, что кружок проще для восприятия 10-12-летнего товарисча, чем инет(если, конечно, дома у папы малолетнего товарисча не стоИт прокся, которая пускает дитятко только к «хорошим» ресурсам, и то, желательно с фильтром матерного:-)))))) Что касаемо ак. часов и т.п. — тут мое мнение таково: существующая система образования изжила себя на 100%(учился 2 курса без троек, в итоге понял, что новых знаний получаю минимум, зато плачУ — оочень некисло… и в основном эти деньги идут на благосостояние ректоров и Ко)… Одна из причин, почему я поднял эту тему здесь и сейчас — через полгода мне предпожительно нужно будет начать заниматься воспитанием маленького человека:-) И Я реально боюсь нынешней школы и ВУЗов… А воспитать и обучить самому — тоже крайне тяжко…

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

  4. Есть пара замечаний.

    «Любая программа на Си должна содержать функцию main, иначе это нифига не Си :)»

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

    «#include »

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

        1. Вообще, когда я пишу на си, то все подобные макроопределения у меня всегда вынесенеы в HAL.h , просто тут я до этого момента еще не дошел :)

          Поэтому да, пишу в ассемблерном стиле.

  5. Спасибо автору! Как начинающему, интересно почитать изложенное, если на простом, доступном языке. Вот бы документацию потом к IAR-у 5.2 где нить достать, желательно на русском.

          1. Естественно, что вырождается. Но поскольку вся библиотека, идущая с WinAVR’ом написана с использованием _BV (как, впрочем, и сторонний код — достаточно погуглить), не вижу смысла не использовать ее. Общий стиль он зачастую многого стоит.

  6. > #define XTAL 8000000L
    выбросить строку, а вместо
    > #define bauddivider (XTAL/(16*baudrate)-1)
    написать
    #define bauddivider (F_CPU/(16*baudrate)-1)

    дело в том что частоту камня Вы уже указали в make файле, непонятно зачем плодить макросы с одним значением под разными именами

    а если по теме, то писать на С — это удобно, понятно, современно и т.д. Поэтому всех призываю: Пишите на С ! http://www.youtube.com/watch?v=XHosLhPEN3k
    :)

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

      1. avr-g++ :-D

        кстати посмотри, в WinAVR (плять скачаю уже, хоть какие там исполнимые файлы есть посмотрю), должен быть.

        код получается несколько больше, нннооооо на порядки 128мб не выходит никак.

        В топку паравоза по мотивам соседней темы, про ардуино — там именно C++ и многие средства стандартной библиотеки сделаны именно классами (типа Serial и LCD).

        PS сам на C++ пишу только гуй когда нужно
        PPS существовала шутка: что будет если ядро линукс переписать на C++ — Windows!

    1. Хотелось бы, чтобы никогда. :) Иначе и туда понабежит куча малообразованых индо-китайцев-недоучек, которые ничерта не понимают в программировании, но хорошо умеют всё подряд «инкапсулировать» и «наследовать». :)

  7. Отличная статья!!!
    Первый раз взглянул что компилятор компилит! -)))

    Вопрос, почему похожие варанты копилятся по разному:

    ==========установка бита — всего одна команда
    36: ACSR |= (1<<ACBG);
    +00000029: 9A46 SBI 0x08,6 Set bit in I/O register
    ==========то же самое — аж 3 команды
    47: GIMSK |= (1<<PCIE); //разреш. прер. от PCI для Tiny13
    +0000002A: B78B IN R24,0x3B In from I/O location
    +0000002B: 6280 ORI R24,0x20 Logical OR with immediate
    +0000002C: BF8B OUT 0x3B,R24 Out to I/O location
    ==========аналогично — 1 команда
    48: PCMSK |= 0b00010000; //прер. возн. при изм. вывода PCINT4
    +0000002D: 9AAC SBI 0x15,4 Set bit in I/O register
    ==========теперь — 2!!!!!
    49: TCCR0A = 0b00000010; //установка режима — сброс по соапад.
    +0000002E: E082 LDI R24,0x02 Load immediate
    +0000002F: BD8F OUT 0x2F,R24 Out to I/O location

    Почему так по разному получается?
    Заранее спасибо!!!

    1. в первом случае адрес регистра ACSR=0x08 поэтому до него может дотянутся команда SBI (она работает в поле от 0 до 0х1F)

      Во втором случае у нас адрес уже 0x3B, что уже значительно дальше. ПОэтому доступ только через IN OUT, а поскольку нам надо изменить всего один бит из байта, то делаем чтение, по маске накладываем 1 и пихаем байт на место — три команды.

      Во втором случае нам надо записать два бита — поэтому грузим непосредственно число и загружаем его по адресу.

      Компилятор тут молодец, не сделал ни одного лишнего движения.

      1. Спасибо! Просто и доходчиво!
        Вот только не понял где в последнем случае меняем ДВА бита?

        49: TCCR0A = 0b00000010; //установка режима — сброс по соапад.
        +0000002E: E082 LDI R24,0×02 Load immediate
        +0000002F: BD8F OUT 0×2F,R24 Out to I/O location

        Вроде только одну единичку ставим?
        Хотя…. ведь целый байт пишем!!

        1. А, я просто посмотрел на позицию. Да, тут мы явно пишем целый байт.

          Попробуй указать как TCCR0A |=1<<WGM11;
          и посмотри какой будет код. Будет три команды.

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

          1. Да действительно, получается 3 команды!!!!

            50: TCCR0A |=1<<WGM01;
            +0000014A: B58F IN R24,0x2F In from I/O location
            +0000014B: 6082 ORI R24,0x02 Logical OR with immediate
            +0000014C: BD8F OUT 0x2F,R24 Out to I/O location

            Но ведь предыдущие 2 команды и эти 3 команды выполняется разное количество тактов? И не факт, что эти три, выполняются дольше чем «те» две?

              1. Спасибо!!!
                Было бы интересно в следующих выпусках этой серии увидеть какие-нибудь базовые (общие) рекомендации по оптимизации кода. Ведь одну и ту же операцию можно реализовать разными методами!

      1. Спасибо за ответ. Всё догрузилось. Просто у меня почти всегда выключен javascript (это вырубает всю рекламу и большинство ненужных свистелок-перделок) и автоматическая загрузка картинок. Не люблю во время чтения отвлекаться на всякие летающие и порхающие хреновины. Жаль что при этом меню не видно.

  8. DI HALT очень большое спасибо за статью. Понравилась. Есть к вам большая просьба написать статью в продолжение этой темы про работу с программой WinAVR и её библиотеки. Я работаю с CodeVision AVR но все рекомендуют переходить на WinAVR так как он делает более компактный и оптимальный код. А толкового самоучителя по этой программе нету.

    1. Еще будет, хочу сделать полный курс по WinAVR.

      План примерно такой:

      Вводная, общие сведения (три четыре части)
      Принципы построения алгоритмов (суперцикл, флаговый автомат, RTOS)
      Основы отладки, отладочные приемы. Поиск багов.
      Постепенный разбор всех либ под WinAVR начиная от простейших, вроде LCD и до USB или Эзернета.

  9. Да, по поводу WinAVR. Вот в хелпе к нему, например, есть разъяснение как взять элемент массива. Так вот из-за того самого портирования gcc на АВР и введения кучи всяких макроопределений делается это не совсем так, как кажется на первый взгляд :) И наверное в том же хелпе ещё много подобных разъяснений. Так вот по поводу реализации таких казалось бы простых вещей хотелось бы узнать по-больше

  10. Автору огромное спасибо за труд!

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

    ИМХО — быстро незначит лучше!

    1. На асме -весь прошлый материал сайта. Впрочем, новый тоже будет. Хотя мне сейчас бы надо материалу по Си надавать, чтобы была база под построение новой системы.

  11. Какая-то фигня… Установил студию, прикрутил к ней Си, в оболочке проделал все процедуры (давил на шестеренку, выбирал каталоги, нажимал на «Ок») — Make-файл не создается. В папке проекта (D:\ПРОЕКТЫ\Микроконтроллеры\3) появились два файла: 3.aps и 3.c, и пустая папка default. Make-файла нет. Соответственно, компиляция не идет.
    Make-файл создается автоматически? Есть возможность создать его какой-то кнопкой/пунктом_меню в оболочке? Что не сделано или сделано не так?
    Версия студии:
    AVR Studio 4.17.666
    GUI Version 4, 17, 0, 655
    AVR Simulator 1, 0, 2, 1
    ATMEGA16 247

    Operating System
    Major 5
    Minor 1
    PlatformID 2
    Build 2600
    Service Pack 2

    Plugins:

    AvrPluginAvrAsmObject 1, 0, 0, 47
    AvrPluginavrgccplugin 1, 0, 0, 11
    Stk500Dll 1, 0, 1, 13

              1. Еще раз вышел и заново зашел, указал директорию проекта \4
                В ней после нажатия Ок в шестеренке появились 4.aps и 4.c
                Директория default не создалась…
                В сишном файле:
                #include
                int main(void)
                {
                unsigned char i;
                #define XTAL 8000000L
                #define baudrate 9600L
                #define bauddivider (XTAL/(16*baudrate)-1)
                #define HI(x) ((x)>>8)
                #define LO(x) ((x)& 0xFF)

                UBRRL = LO(bauddivider);
                UBRRH = HI(bauddivider);
                UCSRA = 0;
                UCSRB = 1<<RXEN|1<<TXEN|1<<RXCIE|0<<TXCIE;
                UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;

                return 0;
                }

            1. Столкнулся с такой же проблемой. Она (AVR-Studio) не находит makefile если в пути к нему присутствуют русские символы.
              решение просто: в makefile не использовать кириллицу

              1. Студия не создает Makefile. т.е. при создании проекта makefile должен создаваться (если выключена опция использования внешнего makefile-а), а он не создается… создал проект в корне диска D: — не помогло…

                  1. В итоге имею такую картину в окне сообщений:

                    avr-gcc -I»D:\4\5\.» -mmcu=atmega16 -Wall -gdwarf-2 -std=gnu99 -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT 5.o -MF dep/5.o.d -c ../5.c

                    ../5.c: In function ‘main':
                    ../5.c:4: warning: unused variable ‘i’
                    Build succeeded with 1 Warnings…

                    Победа будет за нами…

                    1. В итоге имею такую картину в окне сообщений:

                      Да, нормалек, все фурычит. Сейчас взад верну студию 4.17 и WinAVR последний и еще раз проверю. Спасибо за присутствие :-)

                    2. Все вернул, проверил, работает. НО. Первый прогон компилятора все равно сопровождается руганью. Повторно компилирую — все ОК
                      make: `7.o’ is up to date.
                      Build succeeded with 0 Warnings…

                    3. А тебе надо грохнуть все обьектники в папке дефолт.

                      И еще, сноси нахер эту 17ю версию. Глючное говно. Мне тут с фронта сообщили прикольную вещь — она на меге88 криво инициализирует стек. Т.е. тупо в регистр SP (в эмуляции) ничего записать нельзя, а это сам понимаешь пиздец высшей категории. Не зря билд 666

                    4. ШО, ОПЯТЬ???!!! :-)
                      да, про 666 я тоже отметил…
                      ладно, пока пусть работает, до первого косяка.
                      насчет объектных файлов — заново все определяю, создаю новый проект, текст программы копирую через буфер обмена, первый раз компилирую (жму alt+F7) — ругается на отсутствие makefile-а, директории default и еще на что-то. повторно, тут же, компилирую (жму alt+F7) — все ок. вот что характерно…

        1. Мой компьютер-Свойства-Дополнительно-Переменные среды.

          Там в переменной Path «C:\WinAVR\bin;C:\WinAVR\utils\bin;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;D:\3dsmax5\backburner2\;C:\Program Files\Common Files\Autodesk Shared\;C:\Program Files\Pico Technology\PicoScope6\»

          Тут для всех прог, в том числе и для винавр.

          Еще должны быть переменные
          AVRLIB = C:\Program Files\AVRlib (у тебя скорей всего будет другой путь)

              1. Стоп, аврлиб это проксиконовская либа. Ее в стандартной поставке WinAVR нет. Поэтому добавив ее ничего не изменится. Тут что то другое… может криво встало? В каком порядке ставил все?

                1. Сначала студию, потом WinAVR.
                  Тоже подумал, что где-то кривизна, переустановил студию, заменил 4.17 на 4.16, предположив, что может быть какая-нибудь бага в новой версии — не помогло.

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

      Все у меня делалось автоматом и никаких телодвижений делать не приходилось.

        1. Нет даже это я не делал.

          Видеть winavr означает что студия сама каким то образом определяет что на компе стоит winavr и узнает пути к нему (либо через PATH либо через реестр, хз) и сама ее правильно везде прописывает.

          1. Я тоже руками не прописывал. После установки AVR Studio и WinAVR пути сами прописались. Меня ввело в заблуждение слово «plugin». Полез в Plug-in Manager студии, а там WinAVR нет, там AVR-ский компилятор, потому и задал вопрос.
            Спасибо.

  12. Посоветуйте пожалуйста какую книгу можно почитать по программированию на Си для atmega.

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

  13. Cпасибо за „Учебный Курс. Программирование на Си,“ но нельза бы от слов перейти к делу и продолжить,и то чего нет на других строницах(везде «учёба» заключаеться в описании моргании свет.диода и таймера переписанниго друг у друга )да побольше коментариев, а то мы думаем что если мы это знаем то и все должны это уже знать-лучше повториться-повторения мать учения.

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

      #define a 8UL

      1. То есть, что бы у меня переменная «с» была Float, надо написать:

        #define a 8.0F
        #define b 10.0F
        #define c a/b

        Или я думаю не в том направлении? =(

        PS Макроопределение тоже должно иметь тип )

        1. Макроопределение на то и макроопределение, чтобы не иметь типа :)
          Исторически, это очень древний костыль, который работает за счёт ТЕКСТОВЫХ подстановок. То есть тупо сначала подставляются в текст все дефайны, а потом уже полученный текст компилируется.
          в итоге тип выражения (и не только) зависит от контекста. К примеру:
          #define a 8
          #define b 10
          #define c a/b
          char i = c * 3;
          Развернётся в
          char i = 8/10 * 3;
          Что в итоге даст 0 (целочисленное деление 8 / 10 — результат целый, меньше 1, значит 0).
          В то время как
          i = 3 * c;
          Даст
          i = 3 * 8/10;
          И в итоге 2 (3 * 8 = 24, 24 / 10 = 2).

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

          PS Для самостоятельного изучения предлагаю постичь разницу между
          #define c a+b
          и
          #define c (a+b)
          в контексте, скажем
          d = 3 * c;

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

        2. Достаточно так:
          #define a 8.
          #define b 10.
          #define c a/b
          Точка является указанием, что число не целое и нужны вычисления с плавающей запятой. Если точек нет, то число — целое и «мерин разрубленный пополам — это не пол мерина, а ноль меринов».

  14. Очень понравился курс. Но обязательно нужно продолжение.
    Как писать прерывание в AVRSTUDIO4
    Я сделал простейший эксперимент: Написал прогу моргания светодиодом в ИАРе в АВРстудио4 и в Ошоне для меги16 код на Ошоне
    в 2 раза меньше. Поэтому делаю вывод ИАР и АВРстудио зело избыточны!

  15. Ошон — это OshonSoft там есть Picsimulator, Pic18simulator Pic12simulator
    AVRsimulator http://www.oshonsoft.com
    я на нём много прог написал.Очень простая и понятная штука хотя на английском.Один общий язык что пики, что авры и главное без глюков.
    Симулятор прямо в пакете. Написал-проверил.
    Недостатки конечно есть нет знаковых представлений чисел,таймеры авр
    не симулируются.

      1. Ладно, попробую переустановить.

        Попутно есть ещё вопрос. Я устанавливал AVRStudio на разных компьютерах, на одном возникла такая фигня: создаём проект, выбираем AVR Simulator, в левой колонке ищем тип AVR — нужен ATMega8 и оказывается, что его выбрать нельзя, он прописан бледно серым цветом. Если перенести на этот комп готовый проект для мега8 то отладчик запускаться не будет. На других машинах с отладкой для мега8 всё в порядке. Это имеет какое-то разумное объяснение или тоже мистика?

  16. Посоветуйте пожалуйста книжечку по программированию на С для АВР от А до Я там где более детально описана теория, но не слишком много лишнего. (ну так ориентировочно страниц до 300)

      1. Неправда, есть :)

        1. Empbedded C programming and the Atmel AVR, second edition.
        2. Pardue WinAVR C programming for AVR microcontrollers.

        Если интересует просто язык безотносительно контроллеров, то рекомендую Побельского и Фомина — «Язык программирования C».

  17. делаю все пошагово как описано в статье (создание проекта)
    не генерируется make файл. пути настраиваю — списке появится путь “.\” , но в папке default ничего не появляется.
    загадка) кто-нить сталкивался с такой проблемой?

  18. все, вопрос снят.
    глюк такого характера:
    не генерируется make файл. пути настраиваю — списке появится путь “.\” , но в папке default ничего не появляется. даже если нажать «обновить». соответственно и при компиляции ругань
    Но!
    при закрытии Студии с сохранением проекта — туда прописывается все что надо. и при следующем открытии проекта и компиляции — усе проходит как надо без воплей Студии «Где файлы!?!»
    видимо WinAVR как то криво к Студии прикрутилась
    хотя странно все это, странно

  19. Хочу прочитать два байта из ОЗУ термодатчика DS18B20, делаю это в цикле. Почему-то переменная temp1 не меняется, всегда остаётся нулём. Не могу найти ошибку в цикле.

    short temp1;
    short tmp;
    int i;

    for (i=0;i<16;i++)
    {
    DDR_DQ |= 0x08; //пин на выход
    PORT_DQ &= 0xF7;
    _delay_us(5);
    DDR_DQ &= 0xF7; //пин на вход
    _delay_us(10);
    tmp = PIN_DQ;
    if ((tmp&(1<<3)) == 0) // если не установлен 3 бит переменной tmp
    {
    temp1 <<= 1;
    }
    else {
    temp1 +=1;
    temp1 <<= 1;
    }
    _delay_us(45);
    }

  20. К автору статей.
    Я являюсь новичком в вопросах работы с микроконтроллерами. Главным образом потому что потребность в них возникает довольно редко. Когда впервые возникла потребность что-то напрограммировать и залить в камень (с год-полтора назад), узнал от товарища об этом сайте и буквально за пару дней наваял простенькую программку, всё было предельно просто и понятно, всё заработало: и порты, и таймеры, и ШИМы…. От прочитанного материала оставалось ощущение просто экстаза. Всё легко, последовательно, просто. Писал на АСМе.
    Теперь снова возникла необходимость — сейчас ваяю цифровой одометр. Увы, курс сильно изменился… И так, двояко… Уже пятый день сидел жевал АСМ-овую часть курса. Курс явно углубился, но если бы не кое-какие знания, со многим точно не разобрался бы! Часто сильно не хватает примеров кода. Не хватает подобия статьи «Первая программа» или как-то так, где просто были бы описаны основные команды, операторы, как забрасывать большие числа в регистры, как понимать и пользоваться установкой, смещением битов через угловые скобки, как курить даташит в конце концов и много другого, базового….
    Прошу понять меня правильно, этот коммент родил с точки зрения обывателя, чтобы курс по части АСМа, возможно, стал лучше, а никак не для выражения какого-то недовольства. Раньше, действительно, он читался легче. Сподвигло, кстати, прочтение этой статьи, где всё разжёвано и в рот положено — глотать легко и приятно.

    1. Единственно, я довольно сильно перелопатил хронологию статей. Т.е. сильно поменялся их порядок. Основную теорию я стараюсь вынести вперед, а практику и примеры потом.

  21. Приветствую.
    DI, в каком порядке всетаки корректнее устанавливать ВинАВР и Студию? В статье «AVR Studio ликбез» ты говорил:
    «рекомендую скачать GCC aka WinAVR и установить ее ДО студии, туда же, поближе к корню. Тогда студия подхватит ее в качестве своего плагина. Если поставить после, то тоже, может быть подхватит, но возможны проблемы.»
    А в этой статье — наоборот:
    «Так что качай себе инсталяху WinAVR с официального источника и AVR Studio. Далее вначале ставится студия, потом, сверху, накатывается WinAVR и цепляется к студии в виде плагина.»

    И еще вопрос — планируешь ли ты возобновить (закончить :)) работу над своим «Язык программирования С. Справочник»? А то там, начиная с раздела 1.5 Функции — идет возврат к содежимому справочника.

    1. Ставил и так и эдак. Ставилось всегда ОК. так что думаю похрену.

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

  22. Не знаю почему кому то нравится писать на С… Для меня дык самые лучшие языки это асм и Algorithm Builder…. Буилдер хотя бы из за того что в данный момент занят переводом важных мне процедур с С и асма на буилдер и из асма это делается за пару сек, а с С надо прыгать с бубном…. На буилдере кстати нормальную прогу без знания асма написать проблематично, только поделки типа мигания светодиодом…
    PS: Видимо я программер старой закалки — я программировал раньше в асме под 8086 и Z80…

    1. На АБ дальше AVR не упрыгаешь. А на Си смена МК без взрыва мозга занимает ну день два. Мне чтобы пересесть на STM32 потребовалось пару часов.

      АБ есть только под авр. Так что какой бы он удобный ни был — он ограничен со всех сторон. Плюс его разрабатывает один человек. Представь, что завтра с Громовым что то случится. Где окажется АБ? Будете изучать дружно СИ?

      1. Нет я при переходе на другой кристалл все равно буду стараться учить асм, СИ это крайний случай… А если даже что и случится с Громовым то АБ некто у AVRщиков не заберет :)

  23. Кстати, попробовал спасти те самые два байта на вызов main’а.
    Ни фига не вышло =(
    Ни inline, ни уровни оптимизации, ни другие попавшиеся под руку опции не спасают. Вставляет RCALL, хоть ты тресни :(

  24. Привет)) Помогите плз раскурить проблему в Си, а она такова: студия не хочет комплить ничего, что хоть как-то связано с задержками(паузами). На либу ругается, хз на что, что-то там expretion ‘=’ token. Читал про это в одной из частей по курсу авр, и так и не понял, как это устраняется. Я так понял это какая-то бага с присвоением. А прога-то детская, проще только… пустой маин и ретёрн 0;

    Code:

    #define F_CPU 8000000UL
    #include
    #include
    #include

    int main(void)
    {
    DDRD = 0xFF;

    While(1=1)
    {
    PORTD = 0b11111111;
    _delay_ms(200);

    PORTD = 0b00000000;
    _delay_ms(200);
    }
    }

    Типа аццкая прога мигания чем-либо)) Пытаюсь подучить ассемблер по учебному курсу AVR от DI, все суточные трапезы теперь, вместо сопровождения фильмами, сопровождаются втыканием в ассем. Подтверждаются все цитаты DI: можно и башкой апстену, и вынести моск напрочь, а в моем случае, похоже, после 7 или 8 части все извилины сплелись в дичайшую косищу, и как это теперь расплетать я хз_) Ну а поскольку год назад преподавали С в универе, а после этого С#, то все же решил сначала хоть чего-то добиться на сях, а потом уже плотно курить смысл происходящего. Вобщем скажите, как это сцукО_о заставить работать)))

      1. У меня другие тоже комплилятся, где сцукодилаев нету(( Лень писать новую функцию тормоза, к тому же она тут 100% зачОтнее написана, чем тот велосипед из грабель, который я изобрету

  25. Доброго дня. Подскажите, что делать с такой ошибкой…
    «avr-objcopy: ‘Test2.elf': No such file»
    его действительно нет пробовал локально искать, и соответсвенно HEX не создается
    Делал все вродь по описанию.

  26. Всем привет. Не могли бы подсказать как настроить AVR Studio, либо как-то изменить код(хотя он полностью идентичен коду из этой статьи), чтобы компилятор не маялся вот такой вот ерундой:
    UBRRL = LOW(UBRR);
    LDI R30,0x29 Load immediate
    LDI R31,0x00 Load immediate
    LDI R24,0x33 Load immediate
    STD Z+0,R24 Store indirect with displacement
    И вот так вот с каждой записью константы в регистр I/O. Не понимаю зачем использовать в данном случае косвенную загрузку, когда можно просто ограничится командой OUT?

  27. уваэаемы DI Halt, скачайте пожалуйста до конца методу по Си, или хотя бы название дайте чтоб в инете найти :)

    P.S. » Там правда еще не все главы перенесены, но, думаю, это ненадолго.»

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

    1. Не помню кто, но на форуме была сказана одна умная вещь.

      Си с микроконтроллерами надо изучать отлично зная либо микроконтроллер либо си. Не зная ни того ни другого будет лажа.

      А интуитивности в нем нет, это довольно низкоуровневый язык. В этом его прелесть.

  28. Здравствуйте все, я только начал заниматься программаторами и микроконтроллерами (буквально вчера собрал программатор), так что если сморожу какую чушь…
    Вообщем сварганил вот такой код:
    #define F_CPU 16000000UL
    #include
    #include

    int main(void)
    {
    DDRB = 0xFF;
    while(1)
    {
    for(int i = 1; i <= 128; i *= 2)
    {
    PORTB = ~i;
    _delay_loop_2(300000);
    }
    }
    return 0;
    }

    Пытаюсь его скомпилировать и мне вылазит два варнинга и две ошибки:
    c:\program files\atmel\avr tools\avr toolchain\bin\../lib/gcc/avr/4.5.1/../../../../avr/include/avr/io.h:480:6: warning: #warning "device type not defined"
    ../anmul.c:7:5: error: 'DDRB' undeclared (first use in this function)
    ../anmul.c:12:13: error: 'PORTB' undeclared (first use in this function)
    ../anmul.c:13:13: warning: large integer implicitly truncated to unsigned type
    Помогите плиз кто может

  29. DI привет. Слушай нужна твоя консультация. Работаю в avrstudio5.1. Нарыл библиотеку для подключения LCD дисплея. Состоит из двух файлов lib_lcd.h и lib_lcd.c. Сказано на сайте что нужно подключить хидер #include «lcd_lib.h» и усе. Так и сделал. При компиляции файл lib_lcd.c естественно не цепляется к проекту. Так вот вопрос что нужно сделать что его подцепить к проекту? Положить в директорию проекта и указать где то в студии путь к этому проекту? Как указано выше в статье? http://shurikss123.xe0.ru/index.php/home/electro/mikro/10-id10

      1. Извини DI не совсем понял. Эти разделы sources и hiders где находятся? В самой программе студии или это нужно искать папки в C:\Program Files\Atmel? В AVRstudio_5.1 эти разделы пока не нашел.

        1. В свойствах проекта. В студии. Не знаю как в пятой, а в 4й студии (а также в CoIDE, Keil и тыды это стандарт) есть этакое дерево проекта, где указаны все файлы и зависимости. Вот туда их надо добавить. Иначе по одному только инклюднику студия их не найдет. Там же нет путей в которых сишник искать ничего.

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

  30. Господа, подскажите пожалуйста…
    Пишу програмку на Си под 32Мегу… Программа получается объемная, ну очень объемная. Прокрутил в начало листинга посмотрел как называется функция, пока прокрутил обратно — снова забыл… Простыня еще та получается…
    Подскажите как разбить программу на несколько файлов. Если можно, хотелось бы вынести отдельно инициализацию, отдельно обработку прерываний, отдельно некоторые функции. Причем эти файлы должны быть прикручены только к данному проекту…
    Заранее спасибо…

  31. Спасибо большое DI HALT , ты нормально объясняешь в статьях , доступно . Как хочется порой не ковырять в интернетах и не грузить мозг гигабайтами пусто-порожней писанины и рассуждений о регистрах,байтах ,АЛУ и прочей ерунды. Пиши пожалуйста так-же доступно.И по возможности ещё вариант книг для тех кто не хочет учить-а хочет писать проги! Я уверен ты напишешь всё на доступном русском языке по английским правилам ( со 100% повторяемостью примеров!).
    Успехов тебе !

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