Учимся говорить с электроникой на одном языке
Не только люди друг с другом общаются на языке, но и люди с компьютерами (а микроконтроллеры можно тоже назвать микрокомпьютером) общаются на языке, называемом языком программирования. Как и у людей существует множество языков, русский, английский, немецкий, китайский и т.п., также существуют и различные языки программирования, такие как С, Pascal, Basic, Fortran, Java, PHP и прочие.
Каждый язык программирования имеет свои особенности и в связи с этим для использования в различных применениях предпочтение отдается разным языкам. Так, например, скриптовый язык программирования PHP имеющий богатые возможности по работе со строковыми данными, и функциями связанными с работой с текстом, совершено не подходит для написания программ на МК, поскольку, как и любой другой скриптовый язык, он требует для своей работы специального установленного ПО, исполняющего написанный на этом языке код. Таким образом для нас подходят только компилируемые языки, такие как например С, Pascal, Basic и т.п.. Наиболее простым в освоении считается Basic, но в связи с его упрощением он создает далеко не идеальный программный код, в связи с чем, и без того не особо богатые ресурсы микроконтроллера расходуются слишком расточительно. Pascal, среди названных трех языков, является средним языком. Самым лучшим по оптимальности получаемого кода считается язык программирования С. Но даже он не всегда обеспечивает нужного результата, поэтому также порой используется Assembler, данный язык представляет ни что иное как непосредственная работа с микроконтроллером на низком уровне. С точки зрения электроники, написание программ на нем можно сравнить с разработкой и сборкой усилителя на транзисторах, а написание программ на языках высокого уровня со сборкой усилителя на микросхемах. Написание программ на ассемблере является более трудоемким процессом, но при должном умении он дает возможность получить более лучшие результаты по быстродействию и занимаемым программой ресурсам. Также как и усилитель, собранный на транзисторах, может дать лучшие параметры звучания, но только в том случае если этот усилитель собирается по хорошо «вылизанной» схеме с правильной разводкой платы и при соблюдении всех необходимый требований. Именно по этим причинам, наиболее часто применяемым языком программирования, для написания программ для МК является язык программирования Си, с которым я и предлагаю начать наше знакомство. Те, кто знаком с данным языком, могут пропустить данную часть и приступить к изучению следующей части, поскольку в данном материале отображены только базовые знания, необходимые для написания программ, описанные максимально доступным для начинающих языком.
Комментарии
Поскольку наиболее понятное изучение нового материала происходит с использованием примеров с комментариями, первым делом необходимо запомнить, как же на языке Си создаются комментарии в исходном тексте программы.
// Комментарии в программе это заметки, которые игнорируются языком Си //и не считаются частью программного кода. // Они записываются в одной и более строках, и заключаются между символами /* и */ // или в одной строке, начинающейся с последовательности // // Так может быть записан только однострочный комментарий, //этот комментарий правильный А это уже не является комментарием /* Так записываются многострочные комментарии, /* многострочные комментарии могут вставляться в часть кода*/ Это какой-то код /* это комментарий */ а это снова код |
Структура программ
Любая программа, как и предложение в нашем языке, состоит из различных частей «речи», таких как константы, операторы и переменные. Например, в предложении «Если я прийду домой в 18 часов, то успею посмотреть фильм, иначе он закончится» имеются оператор условия «Если…, то успею…, иначе…», константы – «я» и переменная «18 часов». Примерно вот так эта запись будет выглядеть, если её записать на языке Си
// Здесь оператор условия «if», в скобках указывается само условие, //по выполнению, которого будет выполнено действие следующее далее, если действий несколько, то они объединяются фигурными скобками. if (я прийду домой в 18часов) успею посмотреть фильм; // Если условие не выполнилось, то программа выполнит условие идущее после «else», //else не является обязательным и может отсутствовать else фильм закончится; |
Условие состоит из трех частей, это константа, знак сравнения, и переменная. В данном случае «я» является константой, «прийду домой в» знаком сравнения, и «18 часов» переменная. Более подробнее об использовании условных операторов будет рассказано далее.
Переменные
Все константы, операторы и пр. располагаются во flash памяти (ПЗУ, ROM, память программ), содержимое этой памяти не может быть изменено программой. Для хранения данных, которые изменяются во время выполнения программы, используется оперативная память (ОЗУ, RAM, память данных). Все переменные располагаются в RAM памяти, для создания переменной используются ключевые слова:
char – создает переменную размером 1 байт (8 бит), данная переменная может содержать 28=256 значений;
int – размер создаваемой переменой зависит от используемой архитектуры, так например для МК AVR Atmega8 и ему подобных размер переменной будет 16 бит, для STM32 он составит 32 бита;
short - создает переменную размером 16 бит, данная переменная может содержать 65536 значений и занимает в памяти 2 байта;
long – 32 битная переменная, занимает в памяти 4 байта и содержит 232 значений;
float – переменная используемая для чисел с плавающей точкой (дробных чисел), занимает так же 4 байта, но на действия с данным типом переменных необходимо затратить большее число тактов процессора, т.е. больше времени.
Для числовых переменных существуют модификаторы, обозначающие знак переменной:
unsigned – указывает на то, что создаваемая переменная имеет беззнаковый тип, т.е. unsigned char создаст переменную с диапазоном от 0 до 255;
signed – указывает на то, что создаваемая переменная имеет знаковый тип, т.е. signed char создаст переменную с диапазоном от -128 до +127
Для хранения строковых данных, а также для хранения большого количества однотипных данных удобно использовать массивы данных. Ко всему массиву целиком можно обращаться по одному имени. Кроме того, можно выбирать любой элемент массива по индексу элемента. Для этого необходимо задать индекс, который указывает на его относительную позицию. Число элементов массива назначается при его определении и в дальнейшем не изменяется. Если массив объявлен, то к любому его элементу можно обратиться следующим образом: указать имя массива и индекс элемента в квадратных скобках.
unsigned char array_name[10]
Любые переменные, а также функции и константы должны выбираться с учетом следующих правил:
1. Они должны начинаться с буквы латинского алфавита (а,...,z, А,...,Z) или с символа подчеркивания «_».
2. В них могут использоваться буквы латинского алфавита, символ подчеркивания и цифры (0,...,9). Использование других символов запрещено.
3. В языке Си буквы нижнего регистра (а,...,z), применяемые в идентификаторах, отличаются от букв верхнего регистра (А,...,Z). Это означает, что следующие идентификаторы считаются разными: name, NaMe, NAME и т.д.
4. Они могут иметь длину в соответствии со стандартом ANSIC не превышающую 32 символов.
5. Идентификаторы для новых объектов не должны совпадать с ключевыми словами языка и именами стандартных функций из библиотеки.
По окончании объявления переменной необходимо написать точку с запятой «;».
Если перед значением числа добавлено выражение «0х», это означает что данное число записано в шестнадцатеричном виде, например 0х12.
Попробуем создать несколько переменных.
/* Например, некая переменная может изменяться в диапазоне от 0 до 100, unsigned char imja_peremennaya1; /*Если мы захотим создать переменную, изменяющуюся в диапазоне, например, от 50 до 1000 нам необходимо объявить ее как unsigned short, не смотря на то, что она также входит и в диапазон unsigned long. Её также возможно объявить типом unsigned long, но это не рационально, поскольку при этом будет зарезервирована бОльшая область памяти, чем требуется для данной переменной*/ unsigned short imja_peremennaya2; /* Для объявления переменной изменяющейся в диапазоне от -30 до +150 подходит тип signed int */ signed int imja_peremennaya3; /* Массивы определяются так же, как и переменные, но дополнительно в квадратных скобках указывается размер массива */ /*Данный массив представляет набор из 100 переменных типа unsigned int имеющих имя imja_massiva. При обращении к массиву необходимо в квадратных скобках указать номер элемента, к которому происходит обращение. */ unsigned int imja_massiva[100]; /* Инициализировать строку можно следующим образом: */ //В квадратных скобках указывается резервируемое в ОЗУ место unsigned char stroka1[7] = "Строка"; //Еще один вариант объявления массива для строк unsigned char stroka2[ ] = {'С', 'т', 'р', 'о', 'к', 'а', '\0'}; /* Следует помнить, что любая строка должна заканчиваться байтом равным 0, это является признаком конца строки */ |
Таблица 1. Диапазон возможных значений переменной для различных типов переменной
Тип |
Диапазон значений для unsigned |
Диапазон значений для signed |
char |
0 ... 255 |
-128 ... 127 |
short |
0 ... 65535 |
-32768 ... 32767 |
int (для Cortex-M3) |
0 ... 4294967295 |
-2147483648 ... 2147483647 |
long |
0 ... 4294967295 |
-2147483648 ... 2147483647 |
long long |
0 ... 18446744073709551615 |
-9223372036854775808 ... 9223372036854775807 |
Операторы
Для совершения операций с переменными существуют операторы действия
Оператор |
Пример |
Описание |
+ |
a+b |
Математическое сложение |
- |
a-b |
Математическое вычитание |
* |
a*b |
Умножение, перемножает две переменные a и b |
/ |
a/b |
Делит переменную a на b |
% |
a%b |
Определение остатка от деления a на b. Если, например, а=6, b=4 получим 6/4=1,5 следовательно остаток равняется 6-(1*4)=2. Если а=35, b=6, то остаток будет 5 |
= |
a=b |
Присваивание. Переменной а присваивается значение переменой b. |
< |
a<b |
Операция сравнения - меньше |
> |
a>b |
Операция сравнения - больше |
<= |
a<=b |
Операция сравнения - меньше, либо равно |
>= |
a>=b |
Операция сравнения - больше, либо равно |
== |
a==b |
Математическая операция сравнения - равенство |
!= |
a!=b |
Математическая операция сравнения - не равенство |
++ |
a++ |
Инкремент числа а. Операция производит увеличение на единицу значения числа а. Эквивалентно выражению а=а+1 |
-- |
а-- |
Декремент числа а. Операция производит уменьшение на единицу значения числа а. Эквивалентно выражению а=а-1 |
<< |
a<<b |
Побитовый сдвиг влево. Сдвигает переменную а, на количество бит b влево. Пример 23<<1, число 23 при представлении в двоичной системе (http://wiki.mvtom.ru/index.php/Двоичная_система_счисления, http://www.wikiznanie.ru/ru-wz/index.php/Двоичная_система_счисления) счисления выглядит как 0001 0111, сдвинем его на 1 в влево, получим 0 0010 1110, что соответствует числу 46 в десятичной системе. Данную операцию можно записать эквивалентным математическим выражением: а*2b. Числа, не вошедшие в диапазон переменной, будут отброшены. |
>> |
a>>b |
Побитовый сдвиг вправо. Сдвигает переменную а, на количество бит b вправо. Пример 23>>1, в результате выполнения операции получим 0000 1011, что соответствует числу 11 в десятичной системе. Данную операцию можно записать эквивалентным математическим выражением: а/2b. |
& |
a&b |
Поразрядное логическое "И". В результате выполнения данной операции получим число в котором имеются только разряды установленные в числах а Иb. Например, имеем выражение 109&180, представим эти числа в двоичном представлении: В результате выполнения данной операции получим число 36, поскольку только во втором и пятом разрядах в обоих исходных числах были единицы. |
| |
a|b |
Поразрядное логическое "ИЛИ". Устанавливает разряды, имеющиеся хотя бы в одном из чисел логическую единицу. В результате выполнения операции 109|180 получим: 0110 1101 | 1111 1101 Во всех разрядах, кроме первого, хоть в одном числе имеется логическая единица, в результате выполнения данной операции во всех разрядах, кроме первого, получим логические единицы. |
^ |
a^b |
Поразрядное исключающее "ИЛИ" Устанавливает в 1 каждый разряд, где соответствующие разряды каждого выражения, но не оба из них, равны 1. В двоичном виде 0110 1101 ^ |
~ |
~a |
Возвращает инвертированное значение а. Т.е. все логические единицы всех разрядов становятся равными нулю, а все нули единицами. а=0110 1101, |
Данные операторы для сокращения размера исходного кода могут комбинироваться. Пример составных операций приведен ниже:
Составная операция |
Эквивалентная запись |
a+=b |
a=a+b |
a-=b |
a=a-b |
a*=b |
a=a*b |
a/=b |
a=a/b |
a%=b |
a=a%b |
a<<=b |
a=a<<b |
a>>=b |
a=a>>b |
a&=b |
a=a&b |
a|=b |
a=a|b |
a^=b |
a=a^b |
Наиболее часто встречающиеся комбинации операторов для установки и сброса битов при написании программ для МК:
a |= (1<<b), возведение (установка) бита с номером b в байте а a |= ((1<<b)|(1<<с)) возведение битов с номером b и с в байте а a &=~ (1<<b) сброс бита с номером b. a &=~ ((1<<b)||(1<<с)) сброс битов с номером b и с. |
Познакомившись с тем, что такое переменная, как её создавать (объявлять), а также с основными действиями над переменными, можно рассмотреть небольшой пример работы с переменными.
/*Для начала нам надо объявить переменную, предположим, что переменные «perem1» и «x» будут изменяться в диапазоне от нуля до 100*/ //Объявим переменную «perem1» и сразу присвоим ей начальное значение 20 unsigned char perem1=20; //подобным образом поступим со второй переменной «х» unsigned char x=5; //Создадим еще одну переменную, в которую запишем результат сложения «perem1» и «х» unsigned char otvet; //После выполнения данного действия. переменная «otvet» l будет иметь значение равное 25 otvet = perem1+x; otvet *= 10; //Умножим содержимое переменной «otvet» на константу 10 //Теперь в переменной «otvet» содержится число 250 //Вычтем из переменной «otvet» константу 30 и разделим получившееся на 15 otvet = (otvet-30)/15; /* После выполнения данных действий мы должны получить число 14,66(6), но поскольку переменная «otvet» объявлена как unsigned char, т.е. целочисленная беззнаковая переменная, то дробная часть будет отброшена и в результате в переменной «otvet» будет записано только число 14*/ //Добавим к переменной «otvet» число 250 otvet = otvet +250; /*В результате совершенной операции мы должны получить число 264 (в двоичном виде 1 0000 1000), но по поскольку «otvet» имеет тип unsigned char, он может иметь только положительные значения находящиеся в диапазоне от 0 до 255 (в диапазоне одного байта), число 264 превышает размерность одного байта и поэтому значение переменной будет ограничено размерностью одного байта, таким образом, мы получим в переменной «otvet» значение 8 (в двоичном виде 0000 1000)*/ //Отнимем от переменой «otvet» переменную «perem1» и запишем результат в «otvet» otvet -= perem1; /*В результате совершенной операции мы должны получить число (8-20) число -12, но поскольку «otvet» имеет тип unsigned char, он не может иметь отрицательных значений, аналогично описанному примеру выше, в переменной «otvet» будет записано значение 244.*/ //Увеличим на 1 значение переменной «perem1» perem1++; //Теперь переменная содержит значение 21 //Посчитаем остаток от деления переменной «perem1» на переменную «х» otvet = perem1%х; /*В результате операции мы получим значение равное 1, поскольку при делении 21/5 ответ получается равным 4 + 1/5 */ |
Логические операторы
Но язык программирования не язык без использования предложений, в смысле операторов. В языке Си четыре основных оператора, это if, for, while, switch.
Оператор if уже упоминался ранее, данный оператор производит условный переход к выполнению различных частей программ, сейчас рассмотрим его подробнее.
Синтаксис записи данного оператора следующий:
if (условие)
{
Код, выполняемый при выполнении «условия»
}
else
{
Данный код выполняется если «условие» не верно.
}
В круглых скобках идущих сразу после if указывается условие, при выполнении которого, будет исполнен код, объединенный в первых фигурных скобках. Если условие не верно, то выполняется условие во вторых фигурных скобках идущих после else. else не является обязательным, т.е. если не требуется выполнения кода в случае если «условие» не выполнено, то else может отсутствовать.
В качестве условия могут использоваться выражения сравнения, например a==b, ab, a!=b и т.п.. Также в качестве условия может стоять переменная, если переменная не равна нулю, то считается что условие выполнилось.
Фигурные скобки обозначают начало и конец кода, выполняющегося по определенному условию.
Если требуется одновременное выполнение сразу нескольких условий, то такие условия объединяются символами логического условия && и ||. Символ && означает одновременное выполнение первого И второго условия, а символ || требует выполнение первого ИЛИ второго ИЛИ сразу двух условий.
Рассмотрим небольшой пример использования оператора if
unsigned char a, b, с; /* Во время объявления нескольких переменных с одним типом допускается перечислять объявляемые переменные через запятую*/ //Зададим значения переменным a=10; /*Создадим простое условие, в соответствии с данным условием, если переменная «а» меньше пяти, то переменной «а» будет присвоено значение 5*/ if (a<5) a=5; /* Если в коде выполняемом по условию содержится только одно действие, if (a<=b) //Если переменная «а» меньше или равна «b» if (a) a--; //Если переменная «а» не равна нулю, то выполнится её декремент // Запишем двойное условие. if ( (a {/* Данное условие выполнится только в том случае, если выполнится одновременно два условия, переменная «а» будет меньше переменой «b» И переменная «b» будет меньше переменной «с» */ |
Для создания циклов, дающих возможность многократно повторять один и тот же код, до выполнения определенного условия существуют операторы циклов for и while.
Синтаксис оператора while довольно прост, в круглых скобках, следующих сразу за ключевым словом while, стоит условие, пока данное условие выполняется, будет выполняться код, размещенный в фигурных скобках.
while (условие) {код}
unsigned char w=20; while (w>=10) |
Использовать оператор while следует с осторожностью, поскольку с его применением, возможно создать такую ситуацию, когда необходимое условие может никогда не выполниться, что приведет к зависанию программы. Более безопасным является использование цикла for. Данный цикл предназначен для выполнения заранее заданного количества повторений кода. Синтаксис написания цикла for следующий:
for (задание начального условия; условие; операция по изменению условия) {код}
unsigned char i, a; a=0; /* for (i=0; i<10, i++) /* Выход из любого цикла также может быть осуществлен раньше выполнения условия цикла, для этого используется «break». */ a=0; for (i=0; i<100; i++) //Данный цикл может выполняться до 100 раз. if (a>=50) break; /* Но переменная «а» станет равной 50 раньше, чем перестанет выполняться условие цикла, поэтому цикл for прервется после выполнения 50 циклов */ |
Функции
Зачастую программа вынуждена несколько раз выполнять ровно одну и ту же последовательность действий и при достаточно большом наборе таких действий и их повторов, с целью экономии программной памяти, и улучшения читаемости исходного кода, эти последовательности оформляют в виде отдельной группы. Когда снова необходимо выполнить определенный кусок кода, программу просто перенаправляют к данному куску кода. Такой обособленный кусок кода в Си как раз и называется функцией.
Предпосылками к созданию функций в программе, являются:
1. Наличие одинаковых, достаточно больших и многократно повторяющихся наборов действий.
2. Желание структурировать программу в виде отдельных блоков с общим функциональным назначением.
Но у использования функций есть и свои минусы, дело в том, при каждом переходе на функцию и возврате из неё микроконтроллер вынужден сохранять некоторые системные данные, например адрес программы, с которого произошёл переход в функцию, а это требует дополнительных временных ресурсов. Нужно учитывать этот факт и стараться не создавать в программе множество коротких функций.
Любая функция имеет свой уникальный заголовок, список передаваемых ей переменных, тип возвращаемой переменной и, конечно же, тело функции, содержащее исходный код. Имя функции выбирается по тем же правилам что и имя для переменных. Общий вид функции:
возвращаемый_тип_переменной Function_name (передаваемый тип переменной)
{
Тело функции
}
Если функция не должна принимать данные, и/или не должна возвращать данные, то в качестве типа данных указывается void. Любая функция может возвращать только одну переменную, но при этом функция может принимать одну или несколько различных переменных перечисляемых через запятую.
/*Создадим простейшую функцию, которая ничего не принимает, ничего не возвращает и вообще ничего не делает */ void Function_Name (void) /*А на этот раз напишем полезную функцию, которая делает задаваемую программную задержку */ void Delay (unsigned int n) //Передадим функции число «n» /*Наша функция готова принять две переменных «х1» и «х2» типа unsigned char unsigned int Summa (unsigned char x1, unsigned char x2) /* /*Создадим функцию которая принимает две переменные («x1» и «х2»), суммирует их и unsigned char z=2; //объявим глобальную переменную «z» и присвоим ей значение 2 unsigned int Summa2 (unsigned char x1, unsigned char x2) /* Выполнение любой программы всегда начинается с функции «main», данная функция обязательно должна присутствовать в любой программе написанной на Си */ void main (void) // Это самая главная функция программы r = Summa2 (10, 5); //Вызовем функцию, и передадим ей в качестве «х1» значение 10, «х2»=5 /* После выполнения данной функции, в переменной «r» будет содержаться результат вычисления равный 7 */ |
В данном примере созданные функции Function_Name, Delay, Summa и Summa2 могут быть вызваны из главной функции main, поскольку вызываемые функции размещены по тексту выше места их вызова. Для того, чтобы иметь возможность вызывать функции расположенные как выше, так и ниже места вызова, например для вызова функции Summa из функции Function_Name, необходимо объявить функцию в программе. Для этого в самом начале файла необходимо прописать имена функций (объявить функции) с указанием передаваемых и возвращаемых им типов данных:
void Function_Name (void);
void Delay (unsigned int n);
unsigned int Summa (unsigned char x1, unsigned char x2);
unsigned int Summa2 (unsigned char x1, unsigned char x2);
Поскольку функция main ниоткуда не вызывается, то её можно не объявлять.
Заголовочные файлы
В больших программах появляется множество различных функций, и для удобства объявление этих функций, их объявления выносят в отдельный файл, называемый заголовочным файлом. Этот файл имеет расширение .h и подключается к файлу исходного кода (файлу с расширением .c) с помощью директивы #include.
#include "main.h"
Для удобства читаемости исходного кода обычно создается несколько .c файлов исходного кода, и соответствующие им заголовочные .h файлы, куда выносятся все объявления функций, которые должны быть видны из других файлов с исходными кодами. Эти заголовочные фалы также подключаются через директиву #include к файлу с исходным кодом, в котором требуется вызов функций из других файлов. Помимо объявления функций, в заголовочные файлы также выносят указания на глобальные переменные, доступ к которым хотят сделать доступным из других файлов, для этого к объявлению переменной добавляют слово extern.
extern unsigned char z;
Пример заголовочного файла main.h:
void Function_Name (void); void Delay (unsigned int n); unsigned int Summa (unsigned char x1, unsigned char x2); unsigned int Summa2 (unsigned char x1, unsigned char x2); extern unsigned char z; |
Макросы (предпроцессорные директивы)
При использовании в различных частях исходного кода множества повторяющихся констант, любо других небольших фрагментов кода, таких как переопределения выводов микроконтроллера и т.п., для упрощения изменения этих мест, а также для улучшения читаемости исходного кода используют предпроцессорную директиву #define.
#define имя-макроса заменяемая-строка
Команда #define используется для организации замены строки по всему файлу, где она указана. Другими словами, #define приводит к тому, что компилятор (препроцессор) проходит по всему файлу и делает замену "имя-макроса" на "заменяемая-строка". Использование вместо настоящих функций макросов дает одно существенное преимущество: увеличивается скорость выполнения кода, потому что в таких случаях не надо тратить ресурсы на вызов функций.
Предпроцессорная директива #define может использовать логические условия, подобные условию if(…) else… Форма записи данных условий следующая:
#ifdef ASD //Если макрос ASD был определен ранее //Действия, выполняемые, если ASD был определен ранее. #else //Если ASD не был определен ранее //Действия если ASD не был определено ранее #endif //Конец условия |
Помимо #ifdef также используются и другие условия:
#ifndef – подобно условию #ifdef, но выполняющимся, если указанный макрос не был ранее определен.
#if – подобно условию if(…) else…, сравнивает выполнение логического условия для макросов.
Программисты, пишущие программы на языке С, в именах определяемых идентификаторов часто используют буквы верхнего регистра. Если разработчики программ следуют этому правилу, то тот, кто будет читать их программу, с первого взгляда поймет, что будет происходить макрозамена.
Пример программы
Пример заголовочного файла main.h нашей программы
#define PROCESSOR_CLOCK 24000000L //Значение тактовой частоты процессора void Delay(unsigned long n); //Объявляем функцию |
Пример файла исходного кода main.с нашей программы
#include "main.h" //Подключаем заголовочный файл main.h #include "LED.h" //Заголовочный файл с описанием функций управления светодиодом void Delay(unsigned long n) //Функция программной задержки //main – это главная функция, с нее всегда начинается выполнение программы, она обязательно должна быть в любой программе. void main(void) |
Пример заголовочного файла управления светодиодом LED.h
//Выставим бит 5 в порте А (к нему должен быть подключен светодиод) #define LED_ON PORTA|=(1<<5) //Сбрасываем бит 5 в порте А #define LED_OFF PORTA&=~(1<<5) //Считываем бит 5 из порта А #define LED_STATE PORTA&(1<<5) //Объявляем функцию управления светодиодом void LED(void); |
Пример файла исходного кода управления светодиодом LED.с
#include "LED.h" //Заголовочный файл с описанием функций управления светодиодом //Функция управления светодиодом void LED(void) |
Скачать пример приведенного исходного кода можно по ссылке.
Для закрепления пройденного материала предлагаю решить небольшую задачку, в приведенном ниже коде умышленно допущены ошибки, попробуйте найти их.
#define DELAY 40000 //Количество циклов для программной задержки //Объявим глобальную переменную «i», для подсчета количества пройденных циклов программной задержки unsigned char I //Объявляем нашу функцию MyFunction void MyFunction(void); //Собственно сама наша функция MyFunction void MyFunction(void) if (a<1000) a=+3; //Если переменная меньше 1000, увеличим значение переменной на 3 if (a>1000) //Если переменная «а» стала больше 1000, if (a>=40000) //Как только переменная «а» достигла 40000, //Наша главная функция void Main(void) if (i = DELAY) //Если счетчик стал равным значению задержки |
На этом примере изучение основ программирования на языке Си можно считать завершенным. Теперь у нас имеются базовые знания для написания простейших программ, но для создания настоящих программ, способных работать на микроконтроллерах, необходимо изучить основы работы со средой программирования, ее настройками и созданием проекта, данному вопросу будет посвящена следующая глава, в ней также мы напишем первую работоспособную программу и запустим её на микроконтроллере.
Список использованных источников:
1. Википедия;
2. ПИЭ.Wiki;
3. Ю.Ю.Громов, С.И.Татаренко ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ СИ Тамбов, 1994
ARM – это просто (часть 3). Hello World по микроконтроллеровски
Сторожев Денис (Дeн)
Комментарии (12) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
{
a=+10; //то будем увеличивать ее за каждый вход в эту функцию на 10
}
//Объявим глобальную переменную «i», для подсчета количества пройденных циклов программной задержки
unsigned int i;
//Объявляем нашу функцию MyFunction
void MyFunction(void);
//Собственно сама наша функция MyFunction
void MyFunction(void)
{
signed int a; //Объявляем переменную «а»
if (a1000) //Если переменная «а» стала больше 1000,
{
a=+10; //то будем увеличивать ее за каждый вход в эту функцию на 10
}
if (a>=DELAY) //Как только переменная «а» достигла 40000,
{
a=0; //сбросим ее в нуль
}
}
//Наша главная функция
void Main(void)
{
i=0; //Сбрасываем значение счетчика «i» на 0
//Основной цикл, будем находиться в нем бесконечно
while(1)
{
i++; //Увеличим счетчик циклов задержки
if (i==DELAY) //Если счетчик стал равным значению задержки
{
i=0; //сбросим счетчик
MyFunction();
}
}
}
В частности очень мешает пониманию процесса следующая вставка:
a |= ((1b и с в байте а.
a &=~ (1b в байте а.
a &=~ ((1b и с в байте а.
1. Может должно быть:
2.
3. И вместо