Цикл for в code vision
Решил написать небольшую вводную статейку для тех, кто впервые взялся за программирование микроконтроллеров и никогда раньше не был знаком с языком Си. В подробности влезать не будем, обо всем понемногу, чтобы получить общее представление по работе с CodeVisionAVR.
Более подробную информацию можно посмотреть на английском языке в CodeVision User Manual, а также рекомендую сайт https://somecode.ru с видеоуроками по Си для микроконтроллеров и книгу «Как программировать на С» автор Дейтель, это единственная годная книга, с которой я сам начинал.
Начнем с того, что какие бы действия мы не делали, в конечном счете, все сводится к прошивке микроконтроллера. Сам процесс прошивки происходит следующим образом: при помощи некой программы выбирается файл прошивки, выбираются параметры, нажимается кнопочка и происходит непосредственно прошивка, которая, по сути, является копированием. Точно также как с компьютера на флешку вы копируете музыку или документы, физика процесса одна и та же.
Сама прошивка имеет расширение .hex и представляет собой набор инструкций, в виде единиц и нулей, который понятен микроконтроллеру. Откуда взять прошивку? Ее можно скачать с сайтов по электронике, либо написать самому. Написать ее можно в специальных программах, которые называются средой разработки. Из наиболее известных мне AVR Studio, IAR, CodeVision, WinAVR… Нельзя сказать, что какая из этих сред лучше или хуже, каждому свое. Можно сказать, что различаются эти программы в основном удобством, языком программирования и ценой. В пределах данного сайта, рассматривается только CodeVision.
Со средой разобрались, теперь разберемся с процессом написания прошивки. В CodeVision изначально нужно создать проект. Его можно создать при помощи мастера кода или пустой. В любом случае, нужно выбрать тип используемого микроконтроллера и указать его частоту. При использовании мастера, вам будет предложено выбрать начальные настройки и сгенерировать исходный код с настройками. Далее появится окно, в котором можно редактировать этот код. Хотя вы можете написать свой исходный код, в блокноте и потом прицепить его к проекту в настройках.
Файл с исходным кодом является набором команд на языке программирования, задача CodeVision`а перевести эти команды в двоичный код, ваша задача написать этот исходный код. CodeVision понимает язык Си, файлы с исходным кодом имеют расширение «.c». Но у CodeVision есть некоторые конструкции, которые не используются в Си, за это его многие программисты не любят, а используемый язык называют Си подобным. Однако, это не мешает писать серьезные проекты. Множество примеров, генератор кода, большой набор библиотек дает большой плюс CodeVision`у. Единственный минус, то что он платный, хотя есть бесплатные версии с ограничением кода.
Исходный код должен содержать заголовок с используемым типом микроконтроллера и функцию main. Например, используется ATtiny13
void main(void)
{
};
До функции main можно подключить необходимые библиотеки, объявить глобальные переменные, константы, настройки. Библиотека это отдельный файл, обычно с расширением «.h», в котором уже есть заранее написанный код. В одних проектах этот код может быть нам нужен, а в других не нужен. Например, в одном проекте мы используем жк дислей, а в другом не используем. Подключить библиотеку для работы с жк дисплеем «alcd.h», можно так:
#include <alcd.h>
void main(void)
{
};
Переменные это участки памяти, в которые можно поместить некие значения. Например, сложили два числа, результат нужно, где то сохранить, чтобы использовать в дальнейшем. Сначала необходимо объявить переменную, т.е. выделить под нее память, например:
int i=0;
т.е. мы объявили переменную i и поместили в нее значение 0, int – тип переменной, или проще говоря, означает размер выделенной памяти. Каждый тип переменных может хранить только определенный диапазон значений. Например, int можно записать числа от -32768 до 32767. Если нужно использовать числа с дробной частью значит, переменную нужно объявлять как float, для символов используют тип char.
char от -128 до 127
unsigned char от 0 до 255
int от -32768 до 32767
unsigned int от 0 до 65535
long int от -2147483648 до 2147483647
unsigned long int от 0 до 4294967295
float от ±1.175e-38 до ±3.402e38
Внутри функции main уже выполняется основная программа. После выполнения функции программа остановится, поэтому делают бесконечный цикл while, который крутит одну и ту же программу постоянно.
{
while (1)
{
};
};
В любой части исходного кода можно написать комментарий, на работу программы он влиять никак не будет, но будет помогать сделать пометки к написанному коду. Закомментировать строку можно двумя слешами //после этого компилятор будет игнорировать всю строку, либо несколько строк /**/, например:
int i=0; //объявляем переменную i и присваиваем ей значение 0
//Сложение:
i = 2+2; //после выполнения данного выражения переменная i будет равна 4
//Вычитание:
i = 2-2; //после выполнения данного выражения переменная i будет равна 0
//Умножение:
i = 2*2; //после выполнения данного выражения переменная i будет равна 4
//Деление:
i = 2/2; //после выполнения данного выражения переменная i будет равна 1
Зачастую в программе требуется выполнить переход от одного куска кода к другому, в зависимости от условий, для этого существует условные операции if(), например:
{
i=0;
}
/*если i меньше 3, то перейти к коду следующему после тела условия, т.е. после скобок {}*/
Также if можно использовать совместно с else – иначе
{
i=0;
}
else
{
i=5; //иначе, т.е. если i больше 3, присвоить значение 5
}
Также имеется операция сравнения «==» ее нельзя путать с «=» присвоить. Обратная операция не равно «!=», допустим
{
i=0;
}
if(i!=5) //если i не равно 5, присвоить i значение 0
{
i=0;
}
Перейдем к более сложным вещам – функциям. Допустим, у вас есть некий кусок кода, который повторяется несколько раз. Причем этот код довольно большой в размерах. Писать его каждый раз неудобно. Например, в программе, каким то образом изменяется переменная i, при нажатии на кнопку 0 и 3 порта D выполняется одинаковый код, который в зависимости от величины переменной i включает ножки порта B.
{
if(PIND.0==0) //проверяем нажата ли кнопка на PD0
{
if(i==0) //если i==0 включить PB0
{
PORTB.0=1;
}
if(i==5) // если i==5 включить PB1
{
PORTB.1=1;
}
}
…
if(PIND.3==0)// выполняем тоже самое, при проверке кнопки PD3
{
if(i==0)
{
PORTB.0=1;
}
if(i==5)
{
PORTB.1=1;
}
}
}
В общем, то код не очень большой, но он мог бы быть еще и больше во много раз, поэтому гораздо удобнее было бы создать свою функцию.
Например:
{
if(i==0)
{
PORTB.0=1;
}
if(i==5)
{
PORTB.1=1;
}
}
void означает что функция ничего не возвращает, об этом чуть ниже i_check() – это название нашей функции, можете назвать как угодно, я назвал именно так – проверка i. Теперь мы можем переписать наш код:
{
if(i==0)
{
PORTB.0=1;
}
if(i==5)
{
PORTB.1=1;
}
}
void main(void)
{
if(PIND.0==0) //проверяем нажата ли кнопка на PD0
{
i_check();
}
…
if(PIND.3==0)
{
i_check();
}
}
Когда код будет доходить до строчки i_check(); то он перепрыгнет внутрь функции и выполнит код внутри. Согласитесь, код компактнее и нагляднее, т.е. функции помогают заменить одинаковый код, всего на одну строчку. Обратите внимание, что функция объявляется вне основного кода, т.е. до функции main. Можно сказать, да зачем мне это надо, но изучая уроки вам часто будут попадаться функции, например очистка жк экрана lcd_clear() — функция не принимает никаких параметров и ничего не возвращает, однако очищает экран. Иногда эта функция используется чуть ли не через строчку, так что экономия кода очевидна.
Намного интереснее выглядит применение функции, когда она принимает значения, например, есть переменная c и есть функция sum, которая принимает два значения типа int. Когда основная программа будет выполнять эту функцию, то в скобках уже указаны аргументы, таким образом «a» станет равной двум, а «b» станет равной 1. Функция выполнится и «с» станет равна 3.
void sum(int a, int b)
{
c=a+b;
}
void main(void)
{
sum(2,1);
}
Одна из часто встречаемых подобных функций это перевод курсора у жк дисплея lcd_gotoxy(0,0); которая, кстати, тоже принимает аргументы – координаты по х и у.
Еще один вариант использования функции, когда она возвращает значение, теперь она уже не будет void, усовершенствуем предыдущий пример функции сложения двух чисел:
int sum(int a, int b)
{
return a+b;
}
void main(void)
{
с=sum(2,1);
}
Результат будет такой же как и в прошлый раз c=3, однако обратите внимание, мы переменной «с» присваиваем значение функции, которая уже не void, а возвращает сумму двух чисел типа int. Таким образом мы не привязываемся к конкретной переменной «с», что добавляет гибкости в использовании функций. Простой пример подобной функции — чтение данных АЦП, функция возвращает измеренное значение result=read_adc();. На этом закончим с функциями.
Теперь перейдем к массивам. Массив это связанные переменные. Например, у вас есть таблица синуса с несколькими точками, не будете же вы создавать переменные int sinus1=0; int sinus2=1; и т.д. для этого используют массив. Например, создать массив из трех элементов можно так:
int sinus[3]={0,1,5};
в квадратных скобках указывается общее количество элементов массива. Присвоить переменной «с» значение третьего элемента можно таким образом:
с=sinus[2];
Обратите внимание, нумерация элементов массива начинается с нуля, т.е. «с» станет равна пяти. У данного массива элемента sinus[3] не существует!!!
Отдельному элементу можно присвоить значение так:
sinus[2]=10;
Возможно, вы уже успели заметить, что в CodeVision нет строковых переменных. Т.е. нельзя создать переменную string hello=”привет”; для этого придется создавать массив из отдельных символов.
Создали мы массив из букв, теперь их надо вывести на дисплей, функция вывода на дисплей одного символа, допустим нуля, выглядит так – lcd_putchar(0); т.е. чтобы вывести массив с приветствием придется по очереди выводить все буквы.
lcd_putchar(hello[1]);
lcd_putchar(hello[2]);
и т.д.
Получается довольно громоздко, тут на помощь приходят циклы.
Например цикл while
while(i<6)
{
lcd_putchar(hello[i]);
i++;
}
Выглядит намного компактнее, расшифровывается так: до тех пор пока i меньше шести, выводить каждый элемент массива по очереди. Т.е. код внутри будет выполняться, пока условие в скобках не станет неверным. Так же можно использовать «нестандартным» способом цикл while, например
Пока кнопка не нажата ничего не делать – гонять пустой цикл.
Еще один вариант цикл for
for(i=0;i<6;i++)
{
lcd_putchar(hello[i]);
}
Смысл точно такой же, как и у while только добавлено начальное условие i=0 и условие, выполняемое каждый цикл i++. Код внутри цикла максимально упрощен.
После того, как вы написали свою программу, исходный код компилируется, и если нет ошибок, то в папке с проектом вы получите заветную прошивку. Теперь можно прошивать микроконтроллер и наслаждаться работой устройства.
Не стоит сразу стараться использовать циклы, массивы и функции в своих прошивках. Ваша главная задача заставить прошивку работать, поэтому делайте так как вам проще не обращайте внимание на размер кода. Придет время когда захочется не просто писать рабочий код, а написать его красиво и компактно. Тогда можно будет углубиться в дебри языка Си. Желающим овладеть всем и сразу еще раз рекомендую книгу «Как программировать на Си», там много примеров и задач. Ставьте Visual Studio, создавайте консольное приложение win32 и там вволю тренируйтесь.
Источник
В настоящее время существуют множество различных программ для AVR микроконтроллеров. Самая узнаваемая программа — это, конечно, Atmel Studio, но в ней слишком много наворотов. Поэтому для новичков рекомендуется другая среда разработки: CodeVisionAVR. Именно о ней мы и поговорим в нашей статье. Скачать ее можно по прямой ссылке здес ь. Конечно, есть версии и посвежей, но кроме ненужных фишек в них ничего полезного не добавляют.
В чем же плюсы этой программы?
CodeVisionAVR — это среда разработки, которая поддерживает все операции с МК AVR. В ней мы можем написать заготовку кода, скомпилировать ее и с помощью программатора «залить» в нужный нам МК. И не надо отдельно качать программку-прошиватель, компилятор и другие ненужные программы. А зачем, если все это есть в КодВижне? Также КодВижн поддерживает почти все известные программаторы для AVR, будь это китайские клоны или оригинальные программаторы. Большой плюс КодВижена в том, что он сам создает готовый шаблон программы, из которого нам нужно удалить все лишнее, что является несомненным плюсом для новичков.
Вот так выглядит программа после ее установки (кликните по картинике, чтобы увеличить изображение)
Для того, чтобы создать новый проект, кликаем по значку «шестеренка»:
После вылезет вот такое окошко. Ставим маркер-точку на AT90, Atiny, FPSLIC и нажимаем «ОК».
Потом вылезет вот такое окошко. В красном прямоугольнике я показал, на какую часть окошка надо обратить внимание:
Выбираем наш МК. В данном случае я использую МК Atiny2313, поэтому из всего списка МК, которая предлагает нам программа, выбираю именно его.
Также можно поменять частоту «Clock». По умолчанию она устанавливается на 8 МегаГерц.
Далее нажимаем в этом же окошке на значок «шестеренки»:
Вышло вот такое окошко. Первым делом мы выбираем папку, в которую будем сохранять наш проект. Я создал папку на рабочем столе и назвал ее MK AVR. Потом написал название нашего проекта «lesson 1», ну типа «первый урок» ). Ну и потом нажимаем кнопку «Сохранить». Обратите внимание на расширение файла, которое я обвел фиолетовым цветом:
Потом выйдет еще одно такое же окошко, только расширение файла будет другим. Не теряемся, прописываем также название файла, в данном случае lesson 1, и нажимаем кнопку «Сохранить»
И потом выйдет точно такое же завершающее окошко ;-). Также не теряемся, прописываем название файла и снова нажимаем кнопку «Сохранить».
Ну вот мы и готовы к созданию нашего первого проекта ????
КодВижен для нас приготовил шаблон. Здесь явно много лишнего барахла. Первым делом сносим шапку
Выделяем, нажимаем Del.
Текст, который находится после двух косых черточек «// любой текст » или /* любой текст */ называется комментарием. Они нужны для удобного восприятия писанины и никак не сказываются на работу МК. Всех их мы выделяем и тоже сносим:
Итак, теперь думаем над проектом, который будем химичить. Моргание одного светодиодика — это уж слишком банально. Поэтому мы возьмем целых ТРИ светодиода! Наша программа будет поочередно зажигать каждый светодиод и потом в таком же порядке их тушить. У нас будет вот такой алгоритм, то есть порядок действий:
1) При подаче питания на МК зажечь первый светодиод.
2) Через 1 секунду зажечь второй светодиод, но первый светодиод при этом продолжает гореть.
3) Через 1 секунду зажечь третий светодиод, но два предыдущих также горят , то есть у нас горят сразу 3 светодиода.
4) Через 2 секунды тухнет третий светодиод.
5) Через секунду тухнет второй светодиод.
6) Еще через секунду тухнет первый светодиод.
7) Через 2 секунды весь этот цикл повторяется с пункта 1.
Алгоритм мы с вами согласовали. Осталось дело за малым: написать программку ????
Итак, первые две строчки кода — это подключение библиотек. То есть эти файлы у нас уже есть в программе КодВижн. Нам осталось их только подключить.
Include с англ. — включать, содержать, подключать. Include еще называется оператором присоединения внешних файлов.
То есть мы с вами присоединяем к нашей программе библиотеку «tiny 2313.h» для МК Atiny 2313, так как именно на нем будет работать наша схема. А также подключаем библиотеку «delay.h». Delay — c англ. откладывать, отсрочивать. То есть грубо говоря, подключаем еще и задержку.
Уф, две строчки кода вроде бы написали).
Сама программа пишется после void main (void) между фигурными скобками. Там мы будем писать нашу программу.
Давайте внимательно глянем на наш МК:
Здесь мы видим основное название ножек, а в скобках их двойное предназначение. Двойное предназначение нас пока не интересует. Для нас в данный момент интересны обозначения PA, PB, PD. Их еще называют портами Ввода-Вывода. То есть на них можно что-то вводить и с них можно что-то выводить). В данном случае я хочу использовать ножки РВ0, РВ1, РВ2 для того, чтобы управлять светодиодами. На каждую из этих ножек я повешу по одному светодиоду, итого получается три. Остальные ножки нас не интересуют, так как мы не будем их использовать.
Наша схема будет выглядеть вот таким образом:
Следовательно, я должен настроить эти ножки для управления светодиодами.
Итак, в фигурных скобках пишем:
PORTB=0b00000000
0b — это бинарная система счисления. Настраивать в бинарной системе удобно тем, что каждый бит — это одна ножка МК. Поэтому счет идет справа-налево. Присмотритесь к рисунку выше. Здесь мы видим PORTB и нули при двоичной ситеме исчисления. Что это означает? А означает это то, что эти выводы при неиспользовании их в программе будут притянуты к нулю, то есть на них будет низкий потенциал или ноль Вольт. В случае, если мы видим единички установленные на каком-либо из битов, то это значит, что на этом порту при неипользовании в программе у нас будет высокий потенциал или плюс 5 вольт, или, говоря другими словами, этот вывод у нас будет принят к плюсу питания.
DDRB=0b00000111
Это означает, что PB0, PB1, PB2 настроены на выход. Считаем справа-налево, если вы не забыли. А если ноль, то значит настроены на вход.
После каждой строчки не забывайте ставить знак » ; » , иначе при компиляции (превращения вашей программы в код, который понимает МК), будут ошибки.
Теперь рассмотрим while (1). Он означает, что кусочек программы, который будет написан после него, будет зацикливаться, то есть повторяться бесконечное количество раз.
Ну и собственно можно начать писать саму программу…
Итак, как она будет выглядеть?
Смотрите, программа находится между фигурными скобками. Эти скобки принадлежат while (1). То есть этот кусок программы будет повторяться бесконечное число раз.
Давайте разберем саму программу…
PORTB.0=1; Это означает, что при подачи питания на МК, у нас ножка PB0 будет иметь логическую единицу , то есть на выходе этой ножки будет 5 Вольт, которые зажгут светодиод.
delay_ms(1000); Ждем 1000 миллисекунд, то есть 1 секунду.
PORTB.1=1; Через одну секунду на ножке PB1 появляется логическая единица, то есть 5 вольт, которые и зажгут второй светодиод
delay_ms(1000); Ждем еще одну секунду.
PORTB.2=1; Как только прошла секунда, у нас на ножке PB2 появляется логическая единичка, и, следовательно, зажигается третий светодиод.
delay_ms(2000); Ждем 2 секунды…Итого у нас горят все разом три светодиода в течение двух секунд.
Но потом как пройдут эти две секунды…
PORTB.2=0; На ножке PB2 появляется логический ноль, то есть напряжение исчезает. Третий светодиод тухнет.
delay_ms(1000); Ждем секунду
PORTB.1=0; Исчезает напряжение на ножке PB1. Светодиод второй тоже перестает источать свет.
delay_ms(1000); Ждем секунду.
PORTB.0=0; И на ножке PB0 напряжение тоже стает равно нулю. Первый светодиод тухнет тоже.
delay_ms(2000); Ждем две секунды и возвращаемся на начало программы, то есть к
PORTB.0=1;
delay_ms(1000);
Вот так выглядит полностью сама программа. Ее можно даже тупо скопировать и скомпилировать.
#include <tiny2313.h>
#include <delay.h>
void main(void)
{
PORTB=0b00000000;
DDRB=0b00000111;
while (1)
{
PORTB.0=1;
delay_ms(1000);
PORTB.1=1;
delay_ms(1000);
PORTB.2=1;
delay_ms(2000);
PORTB.2=0;
delay_ms(1000);
PORTB.1=0;
delay_ms(1000);
PORTB.0=0;
delay_ms(2000);
}
}
Ну все, программа написана. Осталось дело за малым: преобразовать нашу писанину в машинный код, чтобы его понимал МК. Для этого мы нажимаем на кнопочку «создать все файлы проекта»
После нажатия на эту кнопку, у нас выскакивает окошко, в котором говорится о том, успешно ли прошла компиляция и тд. Если все нормально, то нажимаем ОК.
Если что-то не так, то отобразятся ошибки. Я специально убрал знак » ; » после одной строчки. Ругается примерно как-то так, и это не есть хорошо:
Если все ОК, то у вас в папке «Exe», там где у вас все проекты будет лежать готовый файлик, в данном случае «lesson 1. hex».
Теперь этот файлик с удовольствием скушает наш МК AVR и будет выполнять программку, которая находится в этом файле.
Продолжение
Источник
Источник