AVR123:Глава 5

Материал из roboforum.ru Wiki
Перейти к: навигация, поиск

Вернуться на главную

Язык Си для AVR.

Как раз то, что необходимо и достаточно для микроконтроллеров.

По умолчанию компилятор CVAVR

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

Минимальная программа на Си может быть такой: <source lang="c">main(){}</source> Эта программа не делает ни чего полезного — но это уже программа и она показывает что в программе на языке Си — должна быть главная функция main — обязательно !

Скачайте и Распечатайте.[Памятка Си для МК на ОДНОЙ странице.

Рассказывая про МК я говорил вам, что: Задача программы МК:

  • читать числа из регистров и памяти МК,
  • делать что-то с числами, данными и
  • записывать числа в регистры и память.

Только так программа может общаться с МК.

Как это делать на языке Си

Регистры МК
( регистры - это ячейки-байты в памяти МК AVR ) в программе на Си имеют названия как и в ДШ и так как числа в большинстве из них можно менять - для программы регистры являются по сути переменными.
Переменная
- это набор ячеек в памяти в которых можно хранить число или числа и менять их. Переменная имеет адрес и имя.
Константа
- это как переменная но менять содержимое нельзя.

Подробней о переменных и константах написано ниже.

1) Чтобы поместить число в переменную (в регистр) в языке Си есть оператор присваивания

это знак "=" ( называемый в математике "равно" ) "=" в Си означает вычислить результат того что справа от оператора присваивания и поместить этот результат в переменную находящуюся левее оператора присваивания.

Информация
Запомните!

В Си знак "=" НЕ означает равенство!



<source lang="c"> PORTB = PINB + 34;/* Эта строчка на Си означает взять (прочитать, считать) значение переменной (регистра) PINB, затем прибавить к нему число 34 и поместить результат в переменную PORTB */

ПЕРЕМЕННАЯ = PINC; /* Эта строчка на Си означает взять (прочитать, считать) значение переменной (регистра) PINC и поместить результат в переменную с именем ПЕРЕМЕННАЯ */ </source> Чтобы в Си взять (прочитать) число из регистра или значение переменной нужно написать его название НЕ непосредственно с лева от оператора присваивания !

Примеры :
a) Строка где переменная стоит слева от = но через знак &

<source lang="c">PORTB & = 0x23;</source>

на Си означает - прочитать содержимое переменной PORTB, затем выполнить "поразрядное (побитное) логическое И" между прочитанным значением и числом 0x23 и поместить (записать, присвоить) результат в переменную PORTB

b) Строка где переменная стоит непосредственно слева от =

<source lang="c">PORTB = 0x23;</source>

на Си означает - не читая содержимое переменной PORTB присвоить ей значение 0x23 уничтожив то что было там раньше.

:Вместо & "И" (AND - только 1 и 1 дают 1) могут быть и другие побитные логические операции:
• | "ИЛИ" (OR только 0 и 0 дают 0)
• ^ "Исключающее ИЛИ" (XOR изменить бит напротив "1")
• ~ "инвертирование битов" (INV изменить биты регистра)
• арифметические операции: + - * / %

С оператором присваивания используются вот такие сокращения:

ДЛИННАЯ ЗАПИСЬ СМЫСЛ СОКРАЩАЕТСЯ ДО
x = x + 1; добавить 1 x++; или ++x;
x = x - 1; вычесть 1 x--; или --x;
x = x + y; прибавить y x += y;
x = x - y; вычесть y x -= y;
x = x * y; умножить на y x *= y;
x = x / y; поделить на y x /= y;
x = x % y; остаток от деления x %= y;
x--; вычесть 1 x -= 1;
x++; добавить 1 x += 1;
Примеры :

<source lang="c"> 00010010 | 01001111 // "ИЛИ" - только 0 и 0 дают 0 // англ. название OR

01011111 // это результат

// только биты_5 в обоих числах были нули

00010010 & 01001111 // "И" - только 1 и 1 дают 1 // англ. название AND

00000010 // это результат

// только биты_2 в обоих числах были единицы

00010010 ^ 01001111/* "исключающее ИЛИ" - результат любое из пары чисел в котором инвертированы (изменены) биты напротив битов равных "1" в другом числе. англ. название XOR */ 01011101 // это результат /* изменились биты во втором числе напротив установленных битов 4 и 1 первого числа. */

~ 01001111 /* инвертировать биты те что были "1" станут "0" и наоборот */ 10110000 // это результат</source>

Запомните !
Результатом поразрядных (побитных)

логических операций : & | ^ ~ является число ! Которое может быть интерпретировано компилятором как "истина" если оно не ноль и "ложно" если число ноль.

Числа

В компиляторе можно записывать в виде указанном в его Help ! Раздел - константы - Constants.

например - Целые числа могут быть записаны : <source lang="c"> - в десятичной форме - 1234

- в двоичной форме с префиксом 0b так: 0b101001

- в шестнадцатеричной форме с префиксом 0x так: 0x5А

- в восьмеричной форме с префиксом 0 так: 0775</source>

Числа с плавающей точкой имеют в записи эту точку и какое либо число после этой точки, так: <source lang="c">61.234 или так: 73.0 и так: .786

и могут иметь в конце F вот так: 61.234F</source> ТОЧКА ОБЯЗАТЕЛЬНА !

Различные представления числа D3h равно 0xD3 равно 0b1101 0011 равно 211
шестнадцатеричное число 0xD3
0 x D 3
двоичное представление - число 0b1101 0011
0 b 1 1 0 1 0 0 1 1
номера бита 7 6 5 4 3 2 1 0
два в степени равной номеру бита
128 64 32 16 8 4 2 1
число 211 в десятичном виде это сумма степеней двойки где биты равны "1"
Сложите +128 +64 +16 +2 +1

Четыре бита это 1 "нибл" (полубайт) или 1 символ в 16-ричной системе или десятичное число от 0 до 15.

"В уме" удобно оперировать ниблами:двоичный десятичный 16-ричный

двоичный десятичный 16-ричный
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 10 A
1011 11 B
1100 12 C
1101 13 D
1110 14 E
1111 15 F

Для перевода чисел из одного вида в другой можно использовать калькулятор Windows в инженерном виде.

Есть в Си операции которые изменяют значение переменной и без оператора присваивания : <source lang="c"> PORTA++; /* Эта строчка на Си означает взять значение переменной PORTA добавить к ней 1 и записать результат обратно в PORTA говорят: Инкрементировать регистр PORTA */

PORTC--; /* Эта строчка на Си означает обратное действие! Декрементировать - вычесть 1 из значения регистра PORTC */ </source>

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

Важно помнить что они имеют очень низкий приоритет - поэтому чтобы быть уверенными в порядке выполнения желательно писать их отдельной строчкой программы !

Информация
Обратите внимание !

В конце выражения или конструкции в программе на Си ставят точку с запятой.



Длинные выражения можно писать в несколько строк. <source lang="c"> /* ЗЕЛЕНЫМ я пишу комментарий к программе в Си он может быть написан в несколько строк

и пустых строк тоже */

// или в одну после двух черточек </source> Компилятор игнорирует все что написано в комментариях !

Вы не компилятор !

Не игнорируйте, пишите комментарии и читайте !

Когда инкремент или декремент используется в выражении то важно где стоят два знака + или - перед переменной или после переменной : <source lang="c"> a=4; b=7;

a = b++; /* Эта строчка на Си означает Взять значение переменной b присвоить его переменной a затем добавить 1 к переменной b и сохранить результат в b

Теперь a будет содержать число 7 b будет содержать число 8 */

a=4; b=7;

a = ++b; /* Эта строчка на Си означает Взять значение переменной b затем добавить к нему 1 и сохранить результат в b и этот же результат присвоить переменной a

Теперь a будет содержать число 8 и b будет содержать число 8 */ </source>

2) Арифметические операции в Си

<source lang="c"> x + y // сложение x - y // вычитание x * y // умножение

x / y /* деление.

Если числа целые результат - целое число с отброшенной дробной частью - не округленное !

т.е. если в результате деления на калькуляторе получается 6.23411 или 6.94 то результат будет просто целое число 6 - запомните !

Если числа с плавающей точкой, то есть float или double и записываются с точкой и числом после точки, то и результат будет число с плавающей точкой без отбрасывания дробной части 131.9739 / 6.18 даст 21.355 */

x % y // вычислить остаток от деления нацело

// примеры:

5 / 2 // даст 2

5 % 2 // даст 1

75 / 29 // даст 2

75 % 29 // даст 17 </source>

3) Операторы сравнения (или отношения):

<source lang="c"> используются для сравнения переменных, чисел (констант) и выражений. x < y // X меньше Y x > y // больше x <= y // меньше или равно x >= y // больше или равно x == y // равно x != y /* не равно

Результат выполнения этих операторов:

"истина" это "1" (точнее "не ноль")

"ложно" это "0"

Значения хранимые в переменных (в регистрах) х и у НЕ изменяются!

Берутся (считываются) значения хранящиеся (или содержащиеся) в переменных и сравниваются */

! /* "НЕ" - логическое отрицание */ </source>

4) Логические операции :

<source lang="c"> | | // "ИЛИ" - только "ложь" и "ложь" // дают "ложь"

&& // "И" - только "истина" и "истина" // дают "истина"

! // "НЕ" - логическое отрицание

/* Правило - в Си считается:

"Ложь" (False) только ноль.

"Истина"(True)- не ноль. или так: (!0)

  • /

!(истина) // дает "ложь"

!(ложь) // дает "истина" </source> В результате логической операции вы получаете НЕ ЧИСЛО, а логическое значение "истина" или "ложь"

Для логических операций && и || берутся результаты выражений слева и справа от знака операции преобразованные в "истину" или "ложь" и определяется логический результат операции.

Компилятор, для определенности наверно, результат "истина" превращает в 1 а не в любое отличное от 0 число.

Логические операции могут объединять несколько проверяемых условий.

Пример:

<source lang="c"> if((выражение1)&&((выражение2) || (выражение3))) {/* Код программы здесь будет выполняться если: Выражение1 "Истина" (значит не ноль) и хотя бы одно из выражений 2 и 3 тоже "Истина" (значит не ноль).

  • /};</source>

Подробнее о логических операциях обязательно прочитайте по в низу этой страницы !

Приоритет операций в языке Си перечислены в порядке убывания приоритета. Операции, приведённые на одной строчке, имеют одинаковый приоритет. Операции, помеченные как R->L, исполняются справа налево.

  1. () [] -> .
  2. Унарные (R->L): ! ~ - * & sizeof (type) ++ --
  3. Бинарные арифметические: * / %
  4. Бинарные арифметические + -
  5. Сдвиг: << >>
  6. Сравнение: < <= > >=
  7. Сравнение: == !=
  8. Битовая: &
  9. Битовая: ^
  10. Битовая: |
  11. Логическая: &&
  12. Логическая: ||
  1. Тернарная (R->L): ?:
  2. Операции с присваиванием (R->L):
= += -= = /= &= |= ^= <<= >>=

{

{InfoBlock|Чтобы точно знать порядок выполнения операций программой используйте скобки ( )

( () + ( () * () ) ) Ведь скобки ( ) имеют наивысший приоритет.|Совет:}}

Самое интересное !

Ходовые конструкции на Си В компиляторе [CVAVR заготовки этих конструкций находятся под ярлыком "Code Templates" слева вверху. Вы можете выбирать нужные заготовки и вставлять их в свою программу.

05 temp.png

5) if(){ }else{ }; идеальная конструкция если вам нужно выполнить какую то часть программы при наличии каких либо

условий или при их отсутствии :

<source lang="c"> if (выражение) { код на Си /* делать этот код если выражение "истина" - т.е. результат его вычисления не ноль */ } else { код на Си /* делать этот код если выражение "ложь" - т.е. результат его вычисления равен нулю */ };

// } else { это не обязательный элемент конструкции, без него так :

if (выражение) { код на Си /* делать этот код если выражение "истина" - т.е. результат его вычисления не ноль */ }; </source>

6)while(){ }; условный цикл ( цикл с условием ) - используйте если вам

нужно выполнять какой то код программы пока выполняется (существует,

справедливо, "истино" - значит "не ноль") некоторое условие, результат вычисления выражения :

<source lang="c"> while (выражение) { код на Си /* делать этот код если выражение "истина" - т.е. результат его вычисления не ноль. Пока выполняется этот код выражение не проверяется на истинность ! После выполнения кода происходит переход к строке while снова проверять истинность выражения */ }; </source> Цикл while имеет вариант

do - while при котором код в { } выполняется по меньшей мере один раз не зависимо от истинности условия в скобках : <source lang="c"> do { код на Си /* сделать этот код один раз

затем, если выражение есть "истина" - т.е. результат его вычисления не ноль - опять делать код с начала, и так до тех пор пока выражение истина */ } while (выражение); </source> 7) for(;;){ }; - этот цикл позволяет выполнить часть программы нужное число раз: <source lang="c"> char i; /* объявление переменной для for это обычная переменная Си и значит может иметь любое допустимое имя по вашему желанию и тип */ for (i=5; i i = 5 это начальное выражение, то что в начале будет в переменной i

Число 5 просто для примера, может быть таким, как позволяет объявление типа переменной i, в нашем случае это char в большинстве компиляторов по-умолчанию это без знаковый символьный тип - он может хранить числа от 0 до 255 i < 20 - контрольное выражение Может быть с разными операторами отношения, важно лишь чтобы по ходу цикла оно становилось когда-то "ложью" - иначе цикл "зациклится" т.е. ни когда не кончится.

i += 4 - это счетчик или изменение переменной цикла. Обычно это i++ т.е.к переменной добавляется 1 каждый "прогон" цикла. Но опять же может быть таким какое вам требуется.

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

i =(7 + i*4) или i = (функция других переменных)

<source lang="c"> Циклы for(;;) и while()

часто используют вот так:

while(1);

for (;;);

/* Так написанные эти циклы означают :

МК выполнять эту строчку пока есть питание, нет сброса и нет прерывания. Когда возникает прерывание, программа переходит на обработчик прерывания и (если в обработчике нет перехода в другое место программы) по завершении кода обработчика опять возвращается в такой цикл. */

while(1){ код программы };

for (;;){ код программы };

/* Так написанные эти циклы означают :

МК выполнять код программы пока есть питание, нет сброса и нет прерывания. Когда возникает прерывание, программа переходит на обработчик прерывания и (если в обработчике нет перехода в другое место программы) по завершении кода обработчика опять возвращается сюда и продолжает выполнять код программы */ </source>

8)switch(){ }; - оператор множественного выбора,

позволяет вам сделать выбор из нескольких вариантов. <source lang="c"> switch (выражение) { case 5: код на Си /* этот код будет выполняться если результат вычисления выражения равен числу 5

на этом работа оператора switch закончится */ break;

case -32: код на Си /* этот код будет выполняться если результат вычисления выражения равен отрицательномц числу -32

на этом работа оператора switch закончится */ break;

case 'G': код на Си /* этот код будет выполняться если результат вычисления выражения равен числу соответствующему символу G в таблице ASCII

на этом работа оператора switch закончится */ break;

default: код на Си /* этот код будет выполняться если результат вычисления выражения не равен ни 5 ни -32 ни 'G'

А так же после выполнения кода не имеющего в конце break;

на этом работа оператора switch закончится */ };

/* switch закончен - выполняется дальнейший код программы */ </source>

case 
- может быть столько сколько вам нужно, чтобы программа работала быстрее старайтесь наиболее вероятные варианты располагать выше!
default 
- не обязателен. Его можно расположить и не в конце.
break; 
- если его не использовать то найдя нужный вариант программа будет выполнять и следующие ниже условия case

[Прочитайтеподробней о switch с примерами.

[Скачайте и распечатайте таблицу символов ASCII на ОДНОЙ странице!

9) goto - оператор безусловного (немедленного) перехода.

... какой-то код нашей программы на Си ...

mesto_5: /* сюда мы попадем после выполнения строки программы goto mesto_5 */

   код будет выполнятся после goto mesto_5;

... какой-то код нашей программы на Си ...

mesto_1: /* сюда мы попадем после выполнения строки программы goto mesto_1 */

   код будет выполнятся после goto mesto_1;

... какой-то код нашей программы на Си ...

goto mesto_1; /* перейти в то место программы где в начале строки написано mesto_1: */

... какой-то код нашей программы на Си ...

goto mesto_5; /* перейти в то место программы где в начале строки написано mesto_5: */

... какой-то код нашей программы на Си ...




Используйте goto с осторожностью! Думайте к чему может привести выполнение функций или конструкций вашей программы не до конца.

CVAVR помогает вам в контроле не позволяя где угодно втыкать goto


	Оператор Си   ?   работает почти как  if - вот так: 

Пример функция в которую передается значение переменной val_d

Вызов функции и передач в нее числа хранящегося в переменной с именем peremennaya

resultat = funkziya(peremennaya);


В функции число из peremennaya будет помещено в val_d и обработано.

int funkziya(int val_d) {

 return ((val_d>511)?(-1024+val_d):(val_d));

}

( (выражение) ? ( если выражение истина ) : ( если выражение ложно ) )

если val_d>511 то функция возвратит val_d уменьшенное на 1024

если val_d=<511 то функция возвратит val_d не меняя его.

Возвращаемое функцией значение val_d будет помещено в переменную в строке вызова функции - resultat

Подробнее о функциях будет написано ниже.




Ну вот - ПОЧТИ всё что нужно нам из Си !


Как использовать описанное выше вы можете посмотреть в примерах к компилятору !

Примеры в папке :

C:\CVAVR\EXAMPLES

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

Это великолепный способ само-обучения программированию !




Новичку понадобятся для понимания программ написанных профи :

Указатели, Структуры и Союзы.

Прочитайте о них в он-лайн книгах по Си которые расположены ниже на страничке. Примеры применения указателей, структур и союзов в разных компиляторах вы найдете в FAQ к курсу по AVR - скачайте и читайте.




Структура программы на языке Си.

Программа на языке Си это текстовый файл, обычно с расширением .c

Текст программы называют исходным или "исходником" или "сурцом" от анг. source code - это вам ключевые слова для поиска

Весь исполняемый код программы на Си находится в функциях - т.е. в фигурных скобках { исполняемый код программы }


Программа на Си имеет определенную структуру :

1) заголовок

2) включение необходимых внешних файлов - #include

3) ваши определения для удобства работы - #define

4) объявление глобальных переменных и констант

   Глобальные переменные и константы   

- объявляются вне какой либо функции.

   т.е. не после фигурной скобки   {

- доступны в любом месте программы - значит можно читать их значения и присваивать переменным значения там где вам требуется - в любом месте программы после их объявления.

5) описание функций - обработчиков прерываний

6) описание других функций используемых в программе

7) функция main <- это единственный обязательный пункт !


Это не жесткий порядок а ориентировочный !

Иногда п.6 это прототипы функций, а сами функции описываются полностью после п.7

Прототип функции - показывает образец того как применять функцию в программе, какие значения в нее передаются и если она возвращает какое-то значение то прототип указывает тип возвращаемых данных. Прототип не имеет скобок { } а после скобок ( ) ставится  ;

Функция - имеет { "тело" } в фигурных скобках. Тело это код на Си определяющий то что делает функция.

после вызова функции не ставится !





Программа на Си начинает работу с функции main()

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

main(){

... какой то код программы ...

вызов функции_1; //программа перейдет в функцию_1

строка программы; // будет выполнятся после

                 // возврата из функции_1 

... какой то код программы ...

}

На странице 3. "Прерывания в AVR" - вы уже читали, что описанный выше ход программы может нарушаться прерываниями.




Пример программы на Си

с описанной выше структурой я буду писать на голубом фоне.

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

Ниже будет написан пример ОДНОЙ программы на Си.


	/* пункт 1   заголовок программы 

Он оформляется как комментарий, и обычно содержит информацию

- о названии, назначении, версии и авторе программы - краткое описание алгоритма программы - пояснения о назначении выводов МК и режиме его работы, фьюзы - компилятор, инструменты и их версии - другие сведения которые вы считает полезным указать

  • /

// комментарий после двух косых черт пишут в одну строку!


// пункт 2 включение внешних файлов

  1. include <mega16.h> /* перед компиляцией, препроцессор компилятора вставит вместо этой строчки содержимое (текст) заголовочного файла "хидера" mega16.h - этот файл содержит перечень регистров имеющихся в МК ATmega16 и соответствие их названий их физическим адресам в МК.

Посмотрите его содержание !!!

CVAVR\inc\mega16.h

Не забывайте указать какой МК вы используете в свойствах проекта в компиляторе */


//delay functions

  1. include <delay.h>

/* перед компиляцией, препроцессор компилятора вставит вместо этой строчки текст "хидера" delay.h - этот файл содержит функции для создания пауз в программе.

Теперь чтобы сделать паузу вам нужно лишь написать : */

delay_us(N); /* сделать паузу N (число) микроСек это должна быть константа - НЕ переменная !!! например delay_us(12 + 7*3); например delay_us(117); */

delay_ms(x); /* сделать паузу x милиСек x - может быть переменная, выражение или число от 0 до 65535 (тип unsigned int) например delay_ms(3280); например delay_ms(переменная); например delay_ms(переменная*4 + 760); */


// пункт 3 определения пользователя

// AD7896 control signals PORTB bit allocation

  1. define ADC_BUSY PINB.0
  2. define NCONVST PORTB.1

/* после этих двух строк, перед компиляцией, препроцессор компилятора заменит в тексте программы ADC_BUSY на PINB.0 и NCONVST на PORTB.1

Таким образом вместо того что бы помнить что вывод занятости AD7896 подключен у вас к ножке PB0 вы можете проверять значение осмысленного понятия ADC_BUSY - "АЦП занят"

а вместо управления абстрактной ножкой PB1 (через PORTB.1) вы можете управлять "НьюКонвешнСтат" - NCONVST - "стартовать новое АЦ преобразование"

  1. define - Это удобно ! Но ВОВСЕ не обязательно.
  • /
  1. define INIT_TIMER0 TCNT0=0x100L-F_XTAL/64L/500L

/* этот пример показывает что определения могут быть и сложней ! */



  1. define - может содержать и некоторые переменные, вместо которых в тексте программы могут быть подставлены и числа и слова. Может определять даже сложные, полноценные функции.

Например:

  1. define invbit(p,n) (p=p^bit(n))

Здесь переменные величины это 'p' и 'n'. Теперь для инвертирования бита 5 в регистре PORTB вам достаточно написать в программе:

invbit(PORTB,5);

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

Примеры #define есть в FAQ к курсу.



Определения БИТ-ов AVR (соответствие номера бита в регистре его названию по ДШ) есть в "хидерах" .h в компиляторах ICC, IAR, WinAVR и других компиляторах,

Но их нет в хидерах CodeVisionAVR - это не позволяет напрямую вставлять в текст программы примеры кода из даташит МК

Поэтому я сделал для вас файл - заголовок m8_128.h содержащий определения битов некоторых AVR.

Скачайте его и добавьте в программу вот так:

(Если вы читаете курс с начала и делаете то, что предлагается то этот файл у вас уже есть).

  1. include <mega16.h>

/* сперва обычный "хидер"-заголовок для МК

  используемого в вашей программе */
  1. include <m8_128.h>

/* затем мой "хидер"-заголовок с определениями

  названий и номеров битов для используемого МК */

Теперь вы можете использовать примеры на Си из ДШ на ваш МК !

Мой файл m8 128.h содержит определения битов для микроконтроллеров ATmega8 ATmega16 ATmega32 ATmega64 ATmega128


Для всех AVR определения битов есть в .h заголовках WinAVR вот архив (127 Кб) скачайте и используйте если понадобится.


Мастер начального кода программы в компиляторе ICC умеет по вашему желанию автоматически делать

  1. define для ножек для ножек МК !

Подробней про это и с картинкой смотри в соответствующей задаче курса.





Пункт 4.

Объявление переменных

Перед использованием переменной в программе на Си её необходимо объявить - т.е. указать компилятору какой тип данных она может хранить и как она называется.

Наиболее подробно об этом по ссылке:

1.2. ТИПЫ ДАННЫХ И ИХ ОБ ЯВЛЕНИЕ

Ниже сжато - самое главное:

Формат объявления переменной таков:

[<storage modifier>] <type definition> <identifier>;


	[<storage modifier>]- необязательный элемент, 

он нужен толон нужен только в некоторых случаях и может быть:

extern - если переменная может использоваться в других файлах исходного кода программы, например объявляется во внешнем файле - хидере delay.h приведенном выше, а используется в основном файле программы.

volatile - ставьте если нужно предотвратить возможность повреждения содержимого переменной в прерывании, и не позволить компилятору попытаться выкинуть её при оптимизации кода.

Ставьте всегда если не знаете точно - нужно или нет !

пример: volatile unsigned char x;


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


register - разместить переменную в регистрах AVR - это может ускорить доступ к ней. CVAVR по-умолчанию размещает переменные в регистрах до их заполнения. Но размещение переменных в регистрах делает их не видимыми при отладке в PROTEUS.


eeprom - разместить переменную в EEPROM. Это энергонезависимая память - значение таких переменных сохраняется при выключении питания и при перезагрузке МК.

пример: eeprom unsigned int x;

Если это первая переменная в EEPROM то её младший байт будет помещен в ячейку 1 EEPROM а старший в ячейку 2. Ячейка 0 не используется так как рекомендует производитель. CVAVR похоже не использует и 0 и 1 ячейки EEPROM. Необходимо помнить что запись в EEPROM длительный процесс - по таблице "Table 1. EEPROM Programming Time" это 8500 тактов процессора.

Количество записей в ячейки EEPROM ограничено !

Подробней в "Accessing the AVR internal EEPROM".




Книги и учебники по радиоэлектронике и микроконтроллерам там

Скачать весь курс по AVR одним архивом


Глобальные переменные объявляются до появления их в тексте какой либо функции. После объявления, глобальные переменные доступны в любой функции программы.

Локальные переменные объявляются в самом начале функций - т.е. сразу после фигурной скобки {

Локальные переменные доступны только в той функции где они объявлены! В разных функциях могут быть объявлены локальные переменные с одинаковыми именами - я не советую вам так делать.

Советую не использовать ЛОКАЛЬНЫЕ переменные в главной функции main.



<type definition> - тип данных которые может хранить переменная.

наиболе часто используемые типы данных :

unsigned char - хранит числа от 0 до 255 (байт)

unsigned int - хранит числа от 0 до 65535 (слово == 2 байта)

unsigned long int - хранит от 0 до 4294967295

                                                                (двойное слово == 4 байта)


Подробнее все типы данных посмотрите в Help

CVAVR\bin\CVAVR.HLP

Раздел "Data Types"

Уточняйте ТИПЫ данных в руководстве к вашему компилятору !


Вместо unsigned char - можно писать писать просто char, так как компилятор по-умолчанию считает char без знаковым байтом.

А если вам нужен знаковый байт то объявляйте его так :

signed char imya_peremennoi;




<identifier> - имя переменной - некоторый набор символов по вашему желанию, но не образующий зарезервированные слова языка Си.

Выше был уже пример идентификатора - имени переменной: imya_peremennoi


Давайте осмысленные имена переменным и функциям - напоминающие, "говорящие" вам об их назначении.

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

Например так :

 moya_peremennaya        _vasha_funkziya 	 
	 	 



Внимание!

Глобальные переменные, а также локальные с модификатором static - при старте и рестарте программы равны 0 если вы не присвоили им (например оператором =) иное значение при их объявлении или по ходу программы.


Подробные примеры объявления переменных посмотрите пожалуйста в разделе Variables в "Хелп" компилятора CVAVR.


Вот несколько примеров объявления переменных :

unsigned char my_peremen = 34; unsigned int big_peremen = 34034;

Это объявлены две переменные и им присвоены значения.

Первая my_peremen - символьного типа - это 1 байт, она может хранить число от 0 до 255. В данном случае в ней хранится число 34.

Вторая big_peremen - целого типа, два байта, в ней может хранится число от 0 до 65535 , а в примере в неё поместили десятичное число 34034.


Пример массива содержащего 3 числа или элемента массива.

char mas[3]={11,22,33};

Нумерация элементов начинается с 0. Т.е. элементы данного массива называются

mas[0], mas[1], mas[2]

и в них хранятся десятичные числа 11, 22 и 33.

Где то в программе вы можете написать:

mas[1] = 210;

Теперь в mas[1] будет хранится число 210

- массивы могут быть многомерными, - можно не присваивать значений элементам

  массива при объявлении.

НО только при объявлении вы можете присвоить значения всем элементам массива сразу ! Потом это можно будет сделать только индивидуально для каждого элемента.


Строковая переменная или массив содержащий строку символов.

char stroka[6]="Hello";

Символов (букв) между кавычками 5 , а я указал размер строки 6  !

Дело в том, что строки символов должны заканчиваться десятичным числом 0.

Не путайте его с символом '0' которому соответствует десятичное число 48 по таблице ASCII - которая устанавливает каждому числу определенный символ.

Например :

Элемент строки stroka[1] содержит число 101 которому по таблице ASCII соответствует символ 'e'

Элемент stroka[4] содержит число 111 которому соответствует символ 'o'

Элемент stroka[5] содержит число 0 которому соответствует символ 'NUL' его еще обозначают вот так '\0'


Строковая переменная может быть "распечатана" или выведена в USART MK вот так:

printf("%s\n", stroka);


	Вы можете преобразовать 

строковую переменную в число  !

Если исходная строка символов такая  :

char stroka[]="3654694";

то вот так:

perem_1 = atoi(stroka);

мы поместим в переменную perem_1 (которую должны были ранее в программе объявить как "беззнаковую целую") число 36546.

Это число влезет в переменную perem_1 которая может хранить числа от 0 до 65535.

А вот 9 и 4 уже не поместятся.

Для бОльших чисел есть функция - atol()

Чтобы использовать эти функции необходимо включить в начале программы заголовочный файл :

  1. include <stdlib.h>


Для преобразования числа в строку

есть itoa() и ltoa()

и аналогичные функции для чисел с плавающей точкой.

Подробнее об этих и других полезных функциях смотрите раздел "Standard Library Functions" справки компилятора CodeVisionAVR.





Советую вам скачать заголовочный файл

m8 128.h

Он содержит названия битов МК ATmega8 -16 -32 -64 -128 и сокращенные названия типов данных как в компиляторе IAR.

вот отрывок из него:

  1. define u8 unsigned char // 0 to 255
  2. define s8 signed char // -128 to 127
  1. define u16 unsigned int // 0 to 65535
  2. define s16 signed int // -32768 to 32767
  1. define u32 unsigned long int

// 0 to 4294967295

  1. define s32 signed long int

// -2147483648 to 2147483647

  1. define f32 float // ±1.175e-38 to ±3.402e38
  2. define d32 double // ±1.175e-38 to ±3.402e38


После включения моего "хидера" в текст вашей программы вы сможете писать вместо длинного

unsigned long int <имя 32 битной переменной>

коротко :

u32 <имя 32 битной переменной>

u - без знаковая - значит не отрицательная
s - значит переменная со знаком 

32 - количество бит в переменной


	КОНСТАНТЫ.

flash и const ставятся перед объявлением констант - неизменяемых данных хранящихся во флэш памяти программ. Они позволяют вам использовать не занятую программой память МК. Обычно для хранения строковых данных - различные информационные сообщения, либо чисел и массивов чисел.

КОНСТАНТЫ ПРИМЕРЫ из CVAVR help

flash int integer_constant=1234+5; flash char char_constant=’a’; flash long long_int_constant1=99L; flash long long_int_constant2=0x10000000; flash int integer_array1[ ]={1,2,3}; flash int integer_array2[10]={1,2}; flash int multidim_array[2][3]={{1,2,3},{4,5,6}}; flash char string_constant1[ ]=”This is a string constant”;

const char string_constant2[ ]=”This is also a string constant”;

В других компиляторах могут быть отличия !





Следующий пункт в структуре программы на Си для МК

пункт 5

Описание функций-обработчиков прерываний.


Механизм прерываний подробно описан на странице 3 - Прерывания в AVR

	/* 

Конкретно в ЭТОЙ программе - есть только одно прерывание и значит одна функция обработчик прерывания.

Программа будет переходить на неё при возникновении прерывания :

ADC_INT - по событию "окончание АЦ преобразования"

  • /

interrupt [ADC_INT] void adc_isr(void) { PORTB=(unsigned char) (~(ADCW>>2)); /* отобразить горящими светодиодами подключенными от + питания МК через резисторы 560 Ом к ножкам порта_B старшие 8 бит результата аналого-цифрового преобразования

Сделаем паузу 127 мСек - просто как пример пауз */ delay_ms(127);


/*

В реальных программах старайтесь не делать пауз в прерываниях !

Обработчик прерывания должен быть как можно короче и быстрее.

Например - в обработчике прерывания вы только устанавливаете флаги (биты или переменная) означающие состояние кнопок, значения переменных или регистров, а обрабатываете это уже в основном цикле программы, через конструкции if - else или switch (описаны выше!)

  • /

// начать новое АЦПреобразование ADCSRA|=0x40;

} // закрывающая скобка обработчика прерывания



Функция обработчик прерывания может быть названа вами произвольно - как и любая функция кроме main.

Здесь она названа : adc_isr


При каком прерывании ее вызывать - компилятор узнает из строчки :

interrupt[ADC_INT]

по первому зарезервированному слову - interrupt - он узнаёт, что речь идет об обработчике прерывания,

а номер вектора прерывания (адрес куда физически, внутри МК перескочит программа при возникновении прерывания) будет подставлен вместо ADC_INT препроцессором компилятора перед компиляцией - этот номер указан в подключенном нами ранее заголовочном файле ("хидере") описания "железа" МК - mega16.h - это число сопоставленное слову ADC_INT. Не ленитесь, посмотрите в файле !



Очень информативна и тем ценна для обучающегося следующая строка программы:

PORTB = (unsigned char) (~(ADCW >> 2));


Давайте проанализируем как она работает.

= оператор присваивания. Он означает присвоить значение вычисления выражения справа от оператора присваивания той переменной что указана слева от него.

Значит нужно вычислить выражение справа и поместить его в переменную PORTB.

Вычислим что справа от оператора присваивания.

ADCW - это переменная слово (двухбайтовая величина - так она объявлена в файле mega16.h) в котором CodeVisionAVR сохраняет 10-битный результат АЦП - а именно в битах9_0 (биты с 9-го по 0-й) т.е. результат выровнен обычно - вправо.


VMLAB имеет только 8 светодиодов - значит нужно отобразить 8 старших бит результата - т.е. биты_9_2 - для этого мы сдвигаем все биты слова ADCW вправо на 2 позиции

ADCW >> 2 /* биты 1 и 0 вылетают вправо из числа в небытие, бит_9 перемещается в позицию бит_7, бит_8 в позицию бит_6 и так далее до бит_2 становится бит_0 */

Теперь старшие 8 бит результата АЦП встали в биты7_0 младшего байта (LowByte - LB) слова ADCW

	>> n 	означает сдвинуть все биты числа вправо на n  позиций 

это равносильно делению на 2 в сепени n

	<< n 	

означает сдвинуть все биты числа влево на n позиций это равносильно умножению на 2 в сепени n


Сдвиг используется очень часто !




Светодиоды подключены так как написано выше - т.е. подключены правильно !

Загораются (показывая "1") при "0" на соответствующем выводе МК - значит нам нужно выводить в PORTB число в котором "1" заменены "0" и наоборот - это делает как я рассказал выше:

~ операция побитного инвертирования - меняет значения битов.

Значит результатом этого выражения

~(ADCW >> 2)

будут инвертированные 8 старших бит результата АЦП находящиеся в младшем (правом - LB) байте двух байтового слова ADCW


Выше я уже говорил что :

в Си в переменную можно помещать только тот тип данных который она может хранить !


Так как PORTB это байт, а ADCW - это два байта, то прежде чем выполнить оператор присваивания (это знак = ) нужно преобразовать слово (слово - word - значит два байта) ADCW в без знаковый байт.

	Преобразование типов данных - делают так : 

перед тем что надо преобразовать записывают в скобках ( ) тип данных к которому нужно преобразовать.



Пишем ...

(unsigned char) (~(ADCW>>2))

Результат этой строки - один байт и мы можем поместить его в PORTB


Если в регистре DDRB все биты равны "1" - т.е. все ножки порта_B выходы, мы безусловно увидим старшие 8 бит результата АЦП горящими светодиодами.



Вам должна быть абсолютно понятна разобранная строка:

PORTB = (unsigned char) (~(ADCW>>2));


Если это не так то

повторите разбор, и перечитайте материал по Си по использованным операторам Си.


Разберем еще одну строчку

ADCSRA|=0x40;

Обратите внимание на необходимость ставить в конце выражений точку с запятой - не забывайте !

Эта строка означает следующее:

Двигаемся слева на право :

- берем значение переменной ADCSRA (это регистр МК - значит программа прочитает его, возьмет число из него)

- выполняем с этим числом операцию обозначаемую вертикальной черточкой | ( это поразрядная операция ИЛИ - только "0" и "0" дают "0" ) с числом 0x40

- присвоим или поместим результат поразрядного ИЛИ обратно в переменную ADCSRA - т.е. запишем результат в регистр ADCSRA

0x40 это в двоичном виде: 0100 0000

так как в результате поразрядного ИЛИ только два "0" дают "0" биты в ADCSRA напротив нулей не изменятся, а вот бит_6 в ADCSRA оказывается напротив "1" и теперь он станет "1" не зависимо от того каким он был до этого !

т.е. смысл рассматриваемой строки программы

ADCSRA|=0x40;

"установить" (т.е. сделать "1") бит_6 в регистре ADCSRA


	Число справа от составных операторов    |=   &=   ^=

обычно называют маской,

и говорят "наложить маску" - так как в результате меняются лишь те биты которые нужно изменить.




Управление отдельными битами в переменной или регистре.

Как изменить только некоторые биты не изменяя остальные.

Для обнуления нужных бит используют обозначаемое знаком & поразрядное логическое И - только "1" и "1" дает "1"

PEREM &=(~0x04); // обнулить бит_2 в переменной PEREM

Скобки здесь я добавил для улучшения читаемости кода.


Самоконтроль - важно:

а) обязательно разберитесь почему обнуляется бит_2

б) Как в двоичном виде выглядит результат (~0x04)


А вот так более понятно:

 PEREM &=(~(1 << 2)); // обнулить бит_2 в переменной PEREM


Обнулить биты 5, 3 и 0 в переменной PEREM

 PEREM &=(~ (  (1 << 5)|(1 << 3)|(1 << 0)  ) );

конечно здесь вместо (1 << 0) можно написать просто (1)


"Установить" - сделать "1" - биты 7, 5 и 3 в переменной PEREM

 PEREM |=(1 << 7)|(1 << 5)|(1 << 3);


Обязательно разберитесь как это работает - вы должны это понимать и ГЛАВНОЕ использовать в своих программах.

Например (1 << 4) означает: взять число 1 и все его биты сдвинуть в лево на 4 позиции - в итоге мы получим двоичное 10000. Эти вычисления компилятор сделает сам и в программе заменит всё, что правее = на число-результат.

Вместо номеров битов вы можете использовать их названия из даташит. Но в CVAVR для этого надо включить мой заголовок о котором написано выше или самому сделать определения битов из архива выложеного выше. А в IAR надо отметить галочку "Элау бит дефинишнс" в свойствах проекта.

 WDTCR |= (1 << WDTOE) | (1 << WDE);

эта строка программы "Установит" - сделает "1" биты WDTOE и WDE в регистре WDTCR




  ПОЧТОЙ по России !  Программаторы USB AVR PIC ARM 8051, прошивание микроконтроллеров AVR PIC LPC ARM 8051 на заказ, изготовление "любительских" печатных плат по ЛУТ технологии, сборка электронных наборов - Мастер Кит и других, закупка для вас радио деталей электронных компонентов в "Тэрраэлектроника" + добавлю резисторы и конденсаторы + отправка почтой, изготовление электронных устройств для вас на заказ не дорого по вашим схемам или разработаю сам, другие услуги для радио электронщиков любителей, для мастеровых людей и хоббийщиков RC моделистов и для строящих Роботов, компоненты для самодельных станков с ЧПУ.                                       
     На сайте  -  "МК ПОЧТОЙ народ РУ"


Программатор для AVR на USB



Собран и проверен - стоит всего 500 рублей в Москве, а по России пересылка почтой всего 100 рублей.

В подарок к программатору вы бесплатно получаете дополнительный микроконтроллер ATmega8 -16PU и уникальный DVD с материалами по электронике и библиотекой КНИГ !

Микроконтроллер ATmega8 прошитый для этого программатора можно приобрести отдельно за 120 рублей + схема и и рисунок платы.

Программатор позволяет питать ваше устройство от USB.

Скачать документацию на программатор.

Обращайтесь к Семенову Михаилу.




Электронные компоненты ПОЧТОЙ из "Тэрраэлектроника" - мои услуги - это 350 рублей для заказов до 1500 руб. оплаты в Тэрру, 450 руб. для заказов на сумму 1500-3000 рублей и 650 рублей для заказов стоимостью более 3000 рублей. После получения денег я выкупаю ваш заказ в "Тэрраэлектроника" или получаю если его оплатили вы сами и отправляю его вам почтой. Укомплектую резисторами и конденсаторами.



Программатор отладчик для всех микроконтроллеров PIC на USB - PICkit2

Полный аналог собран мной и проверен в работе - только корпус прямоугольный без закруглений а разъемы такие же, кнопка КРАСНАЯ, все функции фирменного - всего 899 руб.




Микроконтроллер PIC18F2550 прошитый для самостоятельного изготовления программатора PICkit2 можно приобрести отдельно за 239 рублей + схема и и рисунок платы.

Прочитайте подробно о PICkit2 и о его возможностях.

Средняя цена фирменного - 1550 рублей !



Программаторы изготовлены качественно, надежно, высылаются полностью собранные, протестированные и готовые к работе. К программаторам получаемым по почте приложен бесплатно DVD диск с драйверами для программаторов, ПО для разработки программ для AVR PIC ARM 8051 и огромным количеством документации от производителей МК и множеством книг по микроконтроллерам и электронике на русском языке, учебными материалами для начинающих.



Вы можете приобрести уникальный DVD всего за 199 рублей с пересылкой по России.

Скачайте содержание этого DVD и ознакомьтесь.



Прошивка, программирование микроконтроллеров AVR PIC ARM 8051 LPC на заказ вашими прошивками. Вы присылаете прошивку - я проверяю, что файл читается программатором и сообщаю вам номер счета, вы переводите деньги, я покупаю нужный МК, прошиваю и отправляю вам.

Цена услуги со стоимостью услуги по покупке МК

25 рублей при прошивке 21 микроконтроллера и более

35 рублей за 11-20 МК 45 рублей за 5-10 МК 65 рублей за 2-4 МК 80 рублей за 1 МК

Для популярных AVR ATmega8 ATmega16 ATmega169 ATtiny2313 AT90s2313 ATtiny13 и популярных PIC16F84 PIC18F452 PIC18F2550 PIC18c508 PIC16F628 PIC24FJ64GA002 dsPIC30F2010 dsPIC30F2020 dsPIC30F2012 стоимость прошивки одного МК - 60 рублей.




Цены могут уменьшаться при комбинации ЛЮБЫХ услуг например получение различных компонентов в "Тэрраэлектроника" + прошивка AVR PIC из этой закупки естественно будет стоить дешевле.


Есть прошивки ко многим конструкторам наборам МастерКит и других производителей - стоимость таких прошивок подлежит отдельной оплате. Спрашивайте какие прошивки вам нужны.

6) Я могу купить вам наборы электронные МастерКит и других производителей - собрать и настроить и отправить вам, цена услуги от 500 рублей.

7) Изготовление печатных плат по лазерно утюжной технологии - ЛУТ - по вашим рисункам для печати на лазерном принтере.

8) Изготовить, спаять нужную вам схему, электронное устройство.

9) Разработать нужное вам устройство по КОНКРЕТНОМУ техническому заданию.

10) Разработать новую прошивку для любых электронных устройств или наборов МастерКит и аналогичных.





	 /* Пункт 6      Функции используемые в программе   */

// их может быть столько сколько вам нужно.

// у нас будет одна, кроме main и // обработчика прерывания.

/* =================================

Это будет функция в которой описано начальное конфигурирование МК в соответствии с поставленной задачей

Удобно над функцией сделать заголовок подробно поясняющий назначение функции !

===================================== */

(void)_init_mk(void) { /* Вначале любой функции объявляются ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ - если конечно они вам нужны */

/* void - означает пусто.

Перед названием функции - void - означает что функция не возвращает ни какого значения. А в скобках после названия означает что при вызове в функцию не передаются ни какие значения. */

// инициализация Port_B DDRB=0xFF; // все ножки сделать выходами PORTB=0xFF; // вывести на все ножки "1"


/* настройка АЦП - производится записью определенного числа в регистр "ADC Control and Status Register A" – ADCSRA

посмотрите его описание в ДШ МК мега16.

Нам нужно:

- Включить модуль АЦП

- Установить допустимую частоту тактирования АЦП при частоте кварца 3.69 МГц - мы выберем коэф. деления 64 - это даст частоту такта для процессов в АЦП 57.656 КГц

- Включить прерывание по завершению АЦ преобразования.

По ДШ для этого нужно записать в регистр ADCSRA число: 1000 1110 или 0х8E */

// ADC initialization w Oscillator=3.69MHz // ADC Clock frequency: 57.656 kHz // ADC Interrupts: On ADCSRA=0x8E;


/* Теперь выбираем вход АЦП ADC0 (ножка PA0) и внешнее опорное напряжение (это напряжение код АЦП которого будет 1023) с ножки AREF

Смотрим что нужно записать для этого в регистр мультиплексора (выбора входа) АЦП ADMUX

см. ДШ */

// Нужно записать 0 (он там по-умолчанию) ADMUX=0;


/* Разрешаем ГЛОБАЛЬНО все прерывания

     разрешенные индивидуально ! 

Вы наверно поняли что индивидуально мы разрешили лишь прерывание по завершении АЦП - вот оно то и сможет возникать у нас. */

  1. asm("sei")



Внимание !

Так делаются Ассемблерные вставки в CVAVR :

  1. asm ("инструкция на ассемблере")

Обратите внимание - точки с запятой в конце НЕТ ! Такие вставки принято иногда делать. НО они не являются необходимыми.

На языке Си можно управлять ВСЕМИ программно изменяемыми битами в регистрах МК !


	Напоминаю ...    

Все регистры МК перечислены в таблице в конце ДШ с указанием номеров страниц с подробным описанием регистра и его битов.




Часто используются такие ассемблерные вставки :

  1. asm("sei") // Разрешить ГЛОБАЛЬНО все прерывания
  1. asm("cli") // Запретить ГЛОБАЛЬНО все прерывания
  1. asm("nop") // Пауза в 1 такт процессора
  1. asm("wdr") // Сбросить сторожевой таймер


	// все функция закончена

} // скобка закрывающая для функции _init_mk()




Далее...

	/* 
  Пункт 7       Главная функция  main()  -  обязательная ! 
  • /

/* ================================= Главная функция -

Си программа начинает выполнятся с нее!

===================================== */

void main(void){ /* Вначале любой функции объявляются

  (если нужны)  ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ  */

_init_mk();/*Вызываем функцию инициализации, настроийки аппаратуры МК. Выполнив ее программа вернется сюда и будет выполнять следующую строку */

// запускаем первое АЦП ADCSRA|=0x40;

// бесконечный цикл в ожидании прерываний while(1); /* Программа будет крутится на этой строчке постоянно проверяя истинно ли условие в скобках после while а так как там константа 1 - то условие будет истинно всегда!

 // функция main закончена

} // скобка закрывающая для функции main()



Эта программа на Си будет работать так :

По завершении АЦП будет возникать прерывание и программа будет перескакивать в функцию обработчик прерывания adc_isr()

При этом будут автоматически запрещены все прерывания ГЛОБАЛЬНО !

В конце adc_isr() запускается новое АЦ преобразование и при выходе из обработчика прерывания снова разрешаются глобально прерывания, и программа возвращается опять в бесконечный цикл while(1)

Такая чехарда будет продолжаться пока есть питание МК и не будет сброса.

Светодиоды будут высвечивать 8-ми битный код АЦП напряжения на ножке PA0


Всё - программа на Си написана и разобрана.

Вам должно быть все ясно и абсолютно понятно!

Если это не так то перечитайте, подумайте, повторите разбор, почитайте рекомендованное ниже по Си.




Еще щепотка Си :


Пример: делать что-то пока на ножке PBn есть "1"

Как AVR переводит напряжения на выводах МК в логические уровни - рассказано с картинками, графиками на странице 2 курса - Устройство микроконтроллера AVR.

while(PINB & (1 << n)){ // для любого компилятора

            Какой-то код программы    

/* Какой-то код будет выполнятся снова и снова, пока проверка условия в скобках после while будет давать "истину" - значит пока на ножке PBn есть логическая единица

Проверка условия выполняется в начале и затем каждый раз по завершении выполнения какого-то кода

Пока выполняется какой-то код проверка того что на ножке PBn не производится. */

                                             };

примечание - в CVAVR можно написать проще while(PINB.n){ // но для регистров с адресом до 31


Пример: выполнить что-то если на ножке PCn есть "0"

if((~PINC)&(1 << n)){ // для любого компилятора

                    что-то
/* что-то начнет выполняться если на ножке PCn "0" */
                   };

примечание - в CVAVR можно написать проще if(!(PINC.n)){


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

Примечание - Условие :

if((~PINC)&(1 << n)) {

можно записать и вот так :

if(!(PINC & (1 << n))) {


Пример: выполнить что-то если на ножке PBn есть "1"

if((PINB)&(1 << n)){ // для любого компилятора

                    что-то
/* что-то начнет выполняться если на ножке PBn "1" */
                   };

примечание - в CVAVR можно написать проще if(PINB.n){




К битам регистров с адресами от 0 до 31 в компиляторе CodeVisionAVR можно обратится (и читать и записывать) проще.

Вот так: REGISTR.BIT




Пример: PINB.2 или PORTA.5 или DDRC.7

Пример: if(!PINB.2){

               этот код /* Если на ножке PB2 низкий 

логический уровень - то выполнить этот код */

          };   

Пример: PORTA.3 = 1; /* Сделать бит 3 в регистре PORTA единицей - говорят: "установить бит" по англ. "set bit" */

Пример: PORTB.6 = 0; /* Сделать бит 6 в регистре PORTB нулем - говорят: "очистить, сбросить бит" по англ. "clear bit" */



Битовые операции подробно описаны в задаче 1

и конечно в справке - help - компиляторов !





Теперь вы должны знать

- как записать число в регистр, в переменную

- как изменить бит в регистре

- как взять число из регистра

- как выполнить что-то в зависимости от

 значения бита в регистре или в переменной





- записывайте возникающие вопросы !

   и лучше на бумагу - моторная память !

- найдите в DataSheet (ДШ) регистры и устройства МК использованные в задаче, прочитайте о них подробней.

- если вопросы остались перечитайте задачу снова !

- если вопросы не разрешены, ищите ответ:

1) в help и документации компилятора, симулятора, других используемых программ!

2) поиском Windows в папках и help компилятора и симулятора.

3) поиском Windows в папке где сохранен у вас курс.

4) в моем не структурированном мини-AVRFAQ - это сборник ответов на часто задаваемые мне по курсу вопросы и советы по применению МК от знающих людей.


Дальше - страница 6

Задачи - Упражнения Курса Практическая работа с Компилятором с Симулятором с МК и внешними устройствами



Язык Си - дополнительная литература


Очень доступно о Си рассказано здесь:

Андрей Богатырев. Руководство полного идиота по программированию на языке Си

обязательно используйте его при работе! и заглядывайте в него.


Статья "Си без Си" уважаемого и очень опытного микроконтроллерщика - ник: Bill

h--p://www.caxapa.ru/story/bill_1.html

Вот её первый абзац - цитата:

" Использование алгоритмических языков высокого уровня (ЯВУ) и, в частности, Си для программирования микроконтроллеров, несомненно, дает ряд преимуществ по сравнению с языком ассемблера. Основными из них являются:

  • высокая скорость разработки программ;
  • легкость отладки разрабатываемых программ;
  • независимость программного кода от типа контроллера и, соответственно, более или менее простой перенос программ на разные платформы;
  • простота сопровождения программ;

Исходные тексты на языке Си имеют сравнительно небольшие размеры, сами программы, как правило, хорошо структурированы и понятны. "

Не надо учить книжки наизусть!

Скачайте чтобы иметь их "под рукой".

Повторю

Отличное руководство по Си для AVR это HELP в компиляторе CodeVisionAVR

Читайте его и ищите в нем интересующее вас по ключевым словам

ОГЛАВЛЕНИЕ

1.ОПИСАНИЕ ЯЗЫКА СИ

1.1. ЭЛЕМЕНТЫ ЯЗЫКА СИ

1.1.1. Используемые символы

1.1.2. Константы

1.1.3. Идентификатор

1.1.4. Ключевые слова

1.1.5. Использование комментариев в тексте программы

1.2. ТИПЫ ДАННЫХ И ИХ ОБ ЯВЛЕНИЕ

1.2.1 Категории типов данных

1.2.2. Целый тип данных

1.2.3. Данные плавающего типа

1.2.4. Указатели

1.2.5. Переменные перечислимого типа

1.2.6. Массивы

1.2.7. Структуры

1.2.8. Объединения (смеси)

1.2.9. Поля битов

1.2.10. Переменные с изменяемой структурой

1.2.11. Определение объектов и типов

1.2.12. Инициализация данных

1.3. ВЫРАЖЕНИЯ И ПРИСВАИВАНИЯ

1.3.1. Операнды и операции

1.3.2. Преобразования при вычислении выражений

1.3.3. Операции отрицания и дополнения

1.3.4. Операции разадресации и адреса

1.3.5. Операция sizeof

1.3.6. Мультипликативные операции

1.3.7. Аддитивные операции

1.3.8. Операции сдвига

1.3.9. Поразрядные операции

1.3.10. Логические операции

Внимание!
Я обнаружил ошибку в разделе 1.3.10

правильно вот так: Операция логического И вырабатывает значение 1 если оба операнда имеют НЕ нулевые значения.



1.3.11. Операция последовательного вычисления

1.3.12. Условная операция

1.3.13. Операции увеличения и уменьшения

1.3.14. Простое присваивание

1.3.15. Составное присваивание

1.3.16. Приоритеты операций и порядок вычислений

1.3.17. Побочные эффекты

1.3.18. Преобразование типов

1.4. ОПЕРАТОРЫ

1.4.1. Оператор выражение

1.4.2. Пустой оператор

1.4.3. Составной оператор

1.4.4. Оператор if

1.4.5. Оператор switch

1.4.6. Оператор break

1.4.7. Оператор for

1.4.8. Оператор while

1.4.9. Оператор do while

1.4.10. Оператор continue

1.4.11. Оператор return

1.4.12. Оператор goto

1.5. ФУНКЦИИ

1.5.1. Определение и вызов функций

1.5.2. Вызов функции с переменным числом параметров

1.5.3. Передача параметров функции main

1.6. СТРУКТУРА ПРОГРАММЫ И КЛАССЫ ПАМЯТИ

1.6.1. Исходные файлы и объявление переменных

1.6.2. Объявления функций

1.6.3. Время жизни и область видимости программных объектов

1.6.4. Инициализация глобальных и локальных переменных

1.7. УКАЗАТЕЛИ И АДРЕСНАЯ АРИФМЕТИКА

1.7.1. Методы доступа к элементам массивов

1.7.2. Указатели на многомерные массивы

1.7.3. Операции с указателями

1.7.4. Массивы указателей

1.7.5. Динамическое размещение массивов

1.8. ДИРЕКТИВЫ ПРЕПРОЦЕССОРА

1.8.1. Директива #include

1.8.2. Директива #define

1.8.3. Директива #undef

2. ОРГАНИЗАЦИЯ СПИСКОВ И ИХ ОБРАБОТКА

2.1. ЛИНЕЙНЫЕ СПИСКИ

2.1.1. Методы организации и хранения линейных списков

2.1.2. Операции со списками при последовательном хранении

2.1.3. Операции со списками при связном хранении

2.1.4. Организация двусвязных списков

2.1.5. Стеки и очереди

2.1.6. Сжатое и индексное хранение линейных списков

2.2. СОРТИРОВКА И СЛИЯНИЕ СПИСКОВ

2.2.1. Пузырьковая сортировка

2.2.2. Сортировка вставкой

2.2.3. Сортировка посредством выбора

2.2.4. Слияние списков

2.2.5. Сортировка списков путем слияния

2.2.6. Быстрая и распределяющая сортировки

2.3. ПОИСК И ВЫБОР В ЛИНЕЙНЫХ СПИСКАХ

2.3.1. Последовательный поиск

2.3.2. Бинарный поиск

2.3.3. М-блочный поиск

2.3.4. Методы вычисления адреса

2.3.5. Выбор в линейных списках

2.4. РЕКУРСИЯ

Чтение по порядку глав

Вообще сайт СитФорум по программированию ПК рулит!


Оглавление

Предисловие

Предисловие к первому изданию

Введение

Список ошибок, допущенных в книгеhr

Глава 1. Обзор языка

1.1 Начнем, пожалуй

1.2 Переменные и арифметические выражения

1.3 Инструкция for

1.4 Именованные константы

1.5 Ввод-вывод символов

1.5.1 Копирование файла

1.5.2 Подсчет символов

1.5.3 Подсчет строк

1.5.4 Подсчет слов

1.6 Массивы

1.7 Функции

1.8 Аргументы. Вызов по значению

1.9 Символьные массивы

1.10 Внешние переменные и область видимости

Глава 2. Типы, операторы и выражения

2.1 Имена переменных

2.2 Типы и размеры данных

2.3 Константы

2.4 Объявления

2.5 Арифметические операторы

2.6 Операторы отношения и логические операторы

2.7 Преобразования типов

2.8 Операторы инкремента и декремента

2.9 Побитовые операторы

2.10 Операторы и выражения присваивания

2.11 Условные выражения

2.12 Приоритет и очередность вычислений

Глава 3. Управление

3.1 Инструкции и блоки

3.2 Конструкция if-else

3.3 Конструкцияn else-if

3.4 Переключатель switch

3.5 Циклы while и

3.6 Цикл do-while

3.7 Инструкции break и continue

3.8 Инструкция goto и метки

Глава 4. Функции и структура программы

4.1 Основные сведения о функциях

4.2 Функции, возвращающие нецелые значения

4.3 Внешние переменные

4.4 Области видимости

4.5 Заголовочные файлы

4.6 Статические переменные

4.7 Регистровые переменные

4.8 Блочная структура

4.9 Инициализация

4.10 Рекурсия

4.11 Препроцессор языка Си

4.11.1 Включение файла

4.11.2 Макроподстановка

4.11.3 Условная компиляция

Глава 5. Указатели и массивы

5.1 Указатели и адреса

5.2 Указатели и аргументы функций

5.3 Указатели и массивы

5.4 Адресная арифметика

5.5 Символьные указатели функции

5.6 Массивы указателей, указатели на указатели

5.7 Многомерные массивы

5.8 Инициализация массивов указателей

5.9 Указатели против многомерных массивов

5.10 Аргументы командной строки

5.11 Указатели на функции

5.12 Сложные объявления

Глава 6. Структуры

6.1 Основные сведения о структурах

6.2 Структуры и функции

6.3 Массивы структур

6.4 Указатели на структуры

6.5 Структуры со ссылками на себя

6.6 Просмотр таблиц

6.7 Средство typedef

6.8 Объединения

6.9 Битовые поля

Глава 7. Ввод и вывод

7.1 Стандартный ввод-вывод

7.2 Форматный вывод (printf)

7.3 Списки аргументов переменной длины

7.4 Форматный ввод (scanf)

7.5 Доступ к файлам

7.6 Управление ошибками (stderr и exit)

7.7 Ввод-вывод строк

7.8 Другие библиотечные функции

7.8.1 Операции со строками

7.8.2 Анализ класса символов и преобразование символов

7.8.3 Функция ungetc

7.8.4 Исполнение команд операционной системы

7.8.5 Управление памятью

7.8.6 Математические функции

7.8.7 Генератор случайных чисел

Глава 8. Интерфейс с системой UNIX

8.1 Дескрипторы файлов

8.2 Нижний уровень ввода-вывода (read и write)

8.3 Системные вызовы open, creat, close, unlink

8.4 Произвольный доступ (lseek)

8.5 Пример. Реализация функций fopen и getc

8.6 Пример. Печать каталогов

8.7 Пример. Распределитель памяти

Приложение A. Справочное руководство

A1. Введение

A2. Соглашения о лексике

A2.1. Лексемы (tokens)

A2.2. Комментарий

A2.3. Идентификаторы

A2.4. Ключевые слова

A2.5. Константы

A2.5.1. Целые константы

A2.5.2. Символьные константы

А2.5.3. Константы с плавающей точкой

A2.5.4. Константы-перечисления

A2.6. Строковые литералы

A3. Нотация синтаксиса

A4. Что обозначают идентификаторы

A4.1. Класс памяти

A4.2. Базовые типы

A4.3. Производные типы

A4.4. Квалификаторы типов

A5. Объекты и Lvalues

A6. Преобразования

A6.1. Целочисленное повышение

A6.2. Целочисленные преобразования

A6.3. Целые и числа с плавающей точкой

A6.4. Типы с плавающей точкой

А6.5. Арифметические преобразования

A6.6. Указатели и целые

A6.7. Тип void

А6.8. Указатели на void

A7. Выражения

A7.1. Генерация указателя

A7.2. Первичные выражения

A7.3. Постфиксные выражения

A7.3.1. Обращение к элементам массива

A7.3.2. Вызов функции

A7.3.3. Обращение к структурам

A7.3.4. Постфиксные операторы инкремента и декремента

А7.4. Унарные операторы

А7.4.1. Префиксные операторы инкремента и

A7.4.2. Оператор получения адреса

A7.4.3. Оператор косвенного доступа

A7.4.4. Оператор унарный плюс

A7.4.5. Оператор унарный минус

A7.4.6. Оператор побитового отрицания

A7.4.7. Оператор логического отрицания

A7.4.8. Оператор определения размера sizeof

A7.5. Оператор приведения типа

A7.6. Мультипликативные операторы

A7.7. Аддитивные операторы

A7.8. Операторы сдвига

A7.9. Операторы отношения

A7.10. Операторы равенства

A7.11. Оператор побитового И

A7.12. Оператор побитового исключающего ИЛИ

A7.13. Оператор побитового ИЛИ

A7.14. Оператор логического И

A7.15. Оператор логического ИЛИ

А7.16. Условный оператор

A7.17. Выражения присваивания

A7.18. Оператор запятая

A7.19. Константные выражения

A8. Объявления

A8.1. Спецификаторы класса памяти

А8.2. Спецификаторы типа

A8.3. Объявления структур и объединений

A8.4. Перечисления

А8.5. Объявители

A8.6. Что означают объявители

A8.6.1. Объявители указателей

А8.6.2. Объявители массивов

А8.6.3. Объявители функций

A8.7. Инициализация

A8.8. Имена типов

А8.9. Объявление

A8.10. Эквивалентность типов

A9. Инструкции

A9.1. Помеченные инструкции

A9.2. Инструкция-выражение

A9.3. Составная инструкция

A9.4. Инструкции выбора

A9.5. Циклические инструкции

A9.6. Инструкции перехода

А10. Внешние объявления

A10.1. Определение функции

A10.2. Внешние объявления

A11. Область видимости и связи

A11.1. Лексическая область видимости

A11.2. Связи

A12. Препроцессирование

A12.1. Трехзнаковые последовательности

A12.2. Склеивание строк

А12.3. Макроопределение и макрорасширение

A12.4. Включение файла

A12.5. Условная компиляция

A12.6. Нумерация строк

A12.7. Генерация сообщения об ошибке

A12.8. Прагма

A12.9. Пустая директива

A12.10. Заранее определенные имена

A13. Грамматика

Приложение B. Стандартная библиотека

B1. Ввод-вывод: stdio.

B1.1. Операции над файлами

B1.2. Форматный вывод

B1.3. Форматный ввод

B1.4. Функции ввода-вывода символов

B1.5. Функции прямого ввода-вывода

B1.6. Функции позиционирования файла

B1.7. Функции обработки ошибок

B2. Проверки класса символа: ctype.

B3. Функции, оперирующие со строками: string.

B4. Математические функции: math.

B5. Функции общего назначения: stdlib.

B6. Диагностика: assert.

B7. Списки аргументов переменной длины: stdarg.

B8. Дальние переходы: setjmp.

B9. Сигналы: signal.

B10. Функции даты и времени: time.

B11. Зависящие от реализации пределы: limits.и float.

Приложение С. Перечень измененийhr

Предметный указатель - язык Си

--Nekolex 20:47, 27 сентября 2009 (UTC)