|
Книга Язык программирования Си.
Брайан В. Керниган, Деннис М. Ритчи
Внизу этой
страницы ! Советую прочитать
на досуге ...
Cтраница 5 -
учебный курс AVR
Краткий
учебный курс - Самоучитель - AVR - быстрый старт с
нуля.
Язык Си для
микроконтроллеров AVR.
Язык Си для
микроконтроллеров.
Язык Си для
AVR.
Как раз то, что необходимо и
достаточно
для микроконтроллеров.
Книги
и
учебники по электронике и микроконтроллерам
AVR PIC ARM 8051
Страницы курса :
заглавная 1
2
3
4
5
6
7
8
9
Задачи-упражнения курса по
AVR -
там
Скачать весь курс по
AVR одним архивом
на заглавной странице курса.
Переводчик он-лайн
http://www.translate.ru/
Я расскажу об устройстве и структуре
программы на языке Си
и опишу
часто используемые в МК
конструкции языка.
По
умолчанию компилятор CVAVR
В других
компиляторах могут быть незначительные
отклонения, нюансы не связанные с языком
Си, а обусловленные стараниями и предпочтениями
разработчиков этих компиляторов.
Я
покажу вам, что Си это довольно ПРОСТО !
Если у вас
реальные для
начинающего цели.
Минимальная
программа на Си
может быть такой:
Эта
программа не делает ни чего полезного -
но это уже программа и она показывает
что в программе на языке Си - должна
быть главная функция main
- обязательно !
Реальные
программы на Си конечно больше.
Скачайте
и Распечатайте
Памятка Си для МК на ОДНОЙ странице.
Рассказывая
про МК я говорил вам, что:
Задача
программы МК:
- читать числа из регистров
и памяти МК,
- делать что-то с
числами, данными
и
- записывать числа в регистры
и память.
Только
так программа может общаться с МК.
Как это делать на языке Си
Регистры
МК ( регистры - это ячейки-байты в памяти МК
AVR ) в программе на Си имеют названия как и
в ДШ и так как числа в большинстве из них
можно менять - для программы регистры являются по сути переменными.
Переменная
- это набор ячеек в памяти в которых можно
хранить число или числа и менять их.
Переменная имеет адрес и имя.
Константа
- это как переменная но менять содержимое нельзя.
Подробней о
переменных и константах написано ниже.
1) Чтобы
поместить число в переменную (в
регистр) в языке Си есть
оператор присваивания
это знак
=
( называемый в математике "равно" )
Запомните!
В Си
этот знак НЕ означает равенство !
=
в Си означает вычислить результат того что справа от оператора
присваивания и поместить этот результат в переменную находящуюся
левее оператора
присваивания.
| |
|
|
| |
PORTB = PINB
+
34;/*
Эта строчка на Си означает
Взять
(прочитать, считать) значение переменной (регистра) PINB,
затем прибавить к нему число 34 и
поместить результат в переменную PORTB */ |
|
| |
|
|
| |
|
|
| |
ПЕРЕМЕННАЯ = PINC;
/*
Эта строчка на Си означает
Взять
(прочитать, считать) значение переменной (регистра) PINC и
поместить результат в переменную с
именем ПЕРЕМЕННАЯ */ |
|
| |
|
|
Чтобы в
Си
взять (прочитать) число из регистра или значение
переменной нужно написать его/её название НЕ
непосредственно с лева
от оператора присваивания !
Во
загогулина понимашшшь...
(говорил ЕБН )
примеры
:
a) Строка
где переменная стоит слева от
= но через
знак &
PORTB &
=
0x23;
на Си
означает - прочитать содержимое
переменной PORTB,
затем выполнить "поразрядное (побитное)
логическое И" между прочитанным
значением и числом 0x23
и поместить (записать, присвоить)
результат в переменную PORTB
b) Строка
где переменная стоит непосредственно
слева от
=
PORTB =
0x23;
на Си
означает - не читая содержимое
переменной 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; |
Реклама недорогих радиодеталей почтой:
примеры
:
| |
|
|
| |
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 //
это результат
|
|
| |
|
|
Запомните
!
Результатом поразрядных (побитных)
логических операций :
&
| ^
~
является
число !
Которое может
быть интерпретировано компилятором как
"истина"
если оно не ноль и "ложно"
если число ноль.
| |
|
|
| |
Числа в компиляторе можно записывать в виде указанном
в
его Help, обязательно
посмотрите раздел - константы - Constants.
Там же указаны диапазоны чисел для данного компилятора.
например
-
Целые числа
могут быть записаны :
- в десятичной
форме: 12 234
-5493
- в
двоичной форме с префиксом 0b
так: 0b101001
- в шестнадцатеричной
форме с префиксом 0x
так: 0x5А
- в восьмеричной
форме с префиксом 0
так: 0775
Числа с плавающей точкой обычно имеют в записи эту точку
- например: 61.234
или так:
-73.0 и так:
.786
- могут
иметь в конце F
вот так: 61.234F
- или с указанием степени
вот так: 12.7234E-13
Цвета я
применил УСЛОВНО для лучшей читаемости.
|
|
| |
|
|
Различные представления числа
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-ричный |
| 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 в инженерном виде.
Есть в Си
операции которые изменяют значение
переменной и
без оператора присваивания :
| |
|
|
| |
PORTA++;
/*
Эта строчка на Си означает
Взять
значение переменной PORTA добавить к
ней 1 и записать результат
обратно в PORTA
говорят:
Инкрементировать регистр PORTA */
PORTC--;
/* Эта
строчка на Си означает
обратное действие!
Декрементировать
- вычесть 1 из значения регистра
PORTC */
|
|
| |
|
|
Инкремент
и декремент удобно использовать для
изменения
значения различных
переменных счетчиков.
Важно помнить
что они имеют очень низкий приоритет -
поэтому
чтобы быть уверенными в порядке
выполнения желательно писать
их
отдельной строчкой программы !
| |
|
|
| |
Обратите
внимание !
В конце
выражения или конструкции в программе на Си
ставят
точку с запятой.
Длинные
выражения можно писать в несколько
строк.
/* ЗЕЛЕНЫМ я
пишу комментарий к программе
в Си он
может быть написан в несколько
строк
и пустых строк тоже */
//
или в
одну после двух черточек
Компилятор
игнорирует все что написано в
комментариях !
Вы
не компилятор !
Не игнорируйте,
пишите
комментарии и читайте !
|
|
| |
|
|
Когда
инкремент или декремент используется в
выражении то важно где стоят два знака
+ или
- перед переменной или после переменной :
| |
|
|
| |
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
*/
|
|
| |
|
|
2)
Арифметические
операции в Си
| |
|
|
| |
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
|
|
| |
|
|
3)
Операторы
сравнения (или отношения):
используются
для сравнения переменных, чисел (констант)
и выражений.
| |
|
|
| |
x < y
// X меньше Y
x > y // больше
x <= y // меньше или равно
x >= y // больше или равно
x == y // равно
x != y /* не равно
Результат выполнения этих
операторов:
"истина"
это "1" (точнее "не ноль")
"ложно"
это "0"
Значения
хранимые в переменных (в регистрах)
х
и у НЕ изменяются!
Берутся
(считываются) значения хранящиеся (или
содержащиеся) в переменных и
сравниваются */
!
/*
"НЕ"
- логическое отрицание */
|
|
| |
|
|
4)
Логические
операции :
| |
|
|
| |
|| //
"ИЛИ" - только "ложь" и "ложь"
//
дают "ложь"
&&
// "И" - только "истина"
и "истина"
//
дают "истина"
!
// "НЕ" -
логическое отрицание
/*
Правило
- в Си считается:
"Ложь"
(False)
только ноль.
"Истина"(True)- не ноль.
или так: (!0)
*/
!(истина)
// дает "ложь"
!(ложь)
// дает "истина"
|
|
| |
|
|
В
результате логической операции вы
получаете
НЕ ЧИСЛО, а логическое значение "истина"
или "ложь"
Для
логических операций &&
и ||
берутся результаты
выражений слева и справа от знака
операции преобразованные в "истину"
или "ложь" и определяется
логический результат операции.
Компилятор,
для определенности наверно, результат
"истина" превращает в 1 а не в любое
отличное от 0 число.
Логические
операции могут объединять
несколько проверяемых условий.
Например:
if((выражение1)&&((выражение2)||(выражение3)))
{/*
Код
программы здесь будет выполняться если:
Выражение1
"Истина" (значит не ноль) и
хотя бы одно из выражений 2 и 3 тоже "Истина"
(значит не ноль).
};
Подробнее
о логических операциях обязательно
прочитайте по в низу этой страницы
!
Приоритет операций в языке Си
перечислены в порядке убывания приоритета.
Операции, приведённые на одной
строчке, имеют одинаковый приоритет. Операции, помеченные как R->L,
исполняются справа налево.
() [] ->
.
Унарные (R->L): ! ~ - *
& sizeof (type) ++ --
Бинарные арифметические: * / %
Бинарные арифметические + -
Сдвиг: << >>
Сравнение: < <= > >=
Сравнение: == !=
Битовая:
&
Битовая: ^
Битовая: |
Логическая:
&&
Логическая: ||
Тернарная (R->L): ?:
Операции с присваиванием (R->L):
= +=
-= *= /=
&= |= ^=
<<= >>=
| |
|
|
| |
Совет:
Чтобы
точно знать порядок выполнения операций программой
используйте
скобки ( )
( () + ( () * ()
) )
Ведь скобки ( ) имеют
наивысший приоритет.
|
|
| |
|
|
Самое интересное !
Ходовые
конструкции на Си
В компиляторе
CVAVR
заготовки этих конструкций находятся под
ярлыком "Code Templates" слева вверху.
Вы можете выбирать
нужные заготовки и вставлять их в свою программу.

5) if(){}else{};
идеальная
конструкция если вам нужно выполнить
какую то часть программы при наличии
каких либо условий или при их отсутствии :
| |
|
|
| |
if (выражение)
{
код на Си
/* делать этот
код если выражение "истина" - т.е.
результат его вычисления
не ноль */
}
else { код на Си
/* делать этот код
если выражение "ложь" - т.е.
результат его вычисления равен нулю
*/
};
|
|
| |
|
|
} else {
это не
обязательный элемент конструкции, без него так :
| |
|
|
| |
if (выражение) {
код на Си
/* делать этот
код если выражение "истина" - т.е.
результат его вычисления не ноль */
};
|
|
| |
|
|
6)
while(){}; условный цикл
( цикл с условием ) - используйте если вам
нужно выполнять
какой то код программы пока выполняется
(существует,
справедливо, "истино" - значит "не ноль") некоторое условие, результат
вычисления выражения :
| |
|
|
| |
while (выражение)
{ код на Си
/*
делать этот код если выражение "истина"
- т.е. результат его вычисления не
ноль.
Пока
выполняется этот код выражение не
проверяется на истинность !
После
выполнения кода происходит
переход к строке while снова проверять
истинность выражения */
};
|
|
| |
|
|
Цикл
while имеет вариант
do - while
при котором код
в { } выполняется по меньшей мере
один раз не
зависимо от истинности условия в
скобках :
| |
|
|
| |
do {
код на Си
/*
сделать этот код один раз
затем, если выражение есть "истина" - т.е.
результат его вычисления не ноль -
опять делать код с начала, и так до
тех пор пока выражение истина */
}
while (выражение);
|
|
| |
|
|
7)
for(;;){};
- этот цикл позволяет выполнить часть
программы нужное число раз:
| |
|
|
| |
char i; /*
объявление переменной для for это
обычная переменная Си и значит может
иметь любое допустимое имя по
вашему желанию и тип */
for (i=5; i<20; i+=4) {
код на Си
/* Вначале
будет определено "истинно" или "ложно" контрольное
выражение
i<20
Так как переменной i
присвоено значение 5 то контрольное выражение "истинно" и
код
цикла for будет
первый раз выполнен для i=5,
затем по
выражению i+=4, i станет 9 ,
теперь
снова будет проверена истинность (справедливость, выполнение) контрольного
выражение i<20
и так
как 9<20 код цикла for
будет
выполнен еще раз но для i=9.
Так
будет происходить до тех пор пока
результат вычисления контрольного выражения даёт "истинно".
Когда результат станет "ложно" - программа выйдет из цикла for
не исполняя код. */
};
|
|
| |
|
|
i = 5
это начальное выражение, то что в начале будет в
переменной
i
Число
5 просто для примера, может быть
таким, как позволяет объявление типа переменной
i, в нашем случае
это
char
в большинстве компиляторов
по-умолчанию это без знаковый символьный тип - он может хранить числа от 0 до 255
i < 20 -
контрольное выражение
Может
быть с разными операторами
отношения, важно
лишь
чтобы по ходу цикла оно становилось
когда-то "ложью" - иначе цикл
"зациклится" т.е. ни когда не
кончится.
i += 4 -
это счетчик или изменение переменной цикла.
Обычно
это i++
т.е.к
переменной добавляется 1
каждый "прогон" цикла. Но опять
же может быть таким какое вам
требуется.
Начальным
условием - может быть любое допустимое
в Си выражение результатом которого
является целое число.
Контрольное
выражение - определяет до каких пор
будет выполнятся цикл.
Счетчик -
показывает как изменяется начальное
выражение перед каждом новом выполнении
цикла .
Выражение
- значит это может быть не просто переменная, а что-то посложней,
например:
i
=(7 + i*4)
или
i = (функция других переменных)
Циклы
for(;;)
и while()
часто
используют вот так:
| |
|
|
| |
while(1);
for (;;); /*
Так написанные эти циклы означают : МК
выполнять эту строчку пока есть
питание, нет сброса и нет прерывания. Когда
возникает прерывание, программа
переходит на обработчик прерывания и (если
в обработчике нет перехода в другое
место программы) по завершении кода
обработчика опять возвращается в такой цикл.
*/ while(1){
код программы
};
for (;;){
код программы
}; /*
Так написанные эти циклы означают : МК
выполнять код программы пока есть
питание, нет сброса и нет прерывания. Когда
возникает прерывание, программа
переходит на обработчик прерывания и (если
в обработчике нет перехода в другое
место программы) по завершении кода
обработчика опять возвращается сюда и продолжает выполнять
код программы
*/
|
|
| |
|
|
8)
switch(){};
- оператор множественного выбора,
позволяет вам сделать выбор из
нескольких вариантов.
| |
|
|
| |
switch
(выражение) {
case 5: код на Си
/* этот код
будет выполняться если результат
вычисления выражения равен числу 5
на этом
работа оператора switch закончится
*/
break;
case -32:
код на Си
/* этот код
будет выполняться если результат
вычисления выражения равен
отрицательномц числу -32
на этом
работа оператора switch закончится */
break;
case 'G':
код на Си
/* этот код
будет выполняться если результат
вычисления выражения равен числу
соответствующему символу G в
таблице ASCII
на этом
работа оператора switch закончится */
break;
default: код на Си
/* этот код
будет выполняться если результат
вычисления выражения не равен ни
5
ни
-32
ни
'G'
А так же
после выполнения кода не
имеющего в конце break;
на этом
работа оператора switch закончится */
};
/* switch
закончен - выполняется дальнейший
код программы */
|
|
| |
|
|
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
включение внешних файлов
#include <mega16.h>
/*
перед
компиляцией, препроцессор
компилятора вставит вместо этой
строчки содержимое (текст)
заголовочного файла "хидера"
mega16.h
- этот файл содержит перечень
регистров имеющихся в МК ATmega16 и
соответствие их названий их
физическим адресам в МК.
Посмотрите
его содержание !!!
CVAVR\inc\mega16.h
Не забывайте
указать какой МК вы используете в свойствах проекта в компиляторе */
//delay functions
#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
#define ADC_BUSY PINB.0
#define NCONVST PORTB.1
/* после
этих двух строк, перед компиляцией,
препроцессор компилятора заменит в
тексте программы ADC_BUSY на PINB.0 и NCONVST
на PORTB.1
Таким
образом вместо того что бы помнить
что вывод занятости AD7896
подключен у вас к ножке PB0 вы можете
проверять значение осмысленного
понятия ADC_BUSY
- "АЦП занят"
а
вместо управления абстрактной
ножкой PB1 (через PORTB.1) вы можете
управлять "НьюКонвешнСтат" - NCONVST -
"стартовать новое АЦ
преобразование"
#define
- Это удобно ! Но ВОВСЕ не обязательно.
*/
#define INIT_TIMER0 TCNT0=0x100L-F_XTAL/64L/500L
/* этот
пример показывает что
определения могут быть и сложней !
*/
|
|
| |
|
|
#define - может
содержать и некоторые переменные,
вместо которых в тексте программы могут
быть подставлены и числа и слова. Может определять даже сложные,
полноценные функции.
Например:
#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.
Скачайте его
и добавьте в программу вот так:
(Если вы читаете курс с начала и
делаете то, что предлагается то этот файл у вас уже есть).
#include <mega16.h>
/* сперва обычный "хидер"-заголовок
для МК
используемого в вашей программе */
#include <m8_128.h>
/* затем мой "хидер"-заголовок
с определениями
названий и номеров битов для
используемого МК */
Теперь
вы можете использовать примеры
на Си из ДШ на ваш МК !
Мой файл m8
128.h
содержит определения битов для
микроконтроллеров
ATmega8
ATmega16 ATmega32 ATmega64
ATmega128
Для всех AVR определения битов
есть в .h заголовках
WinAVR
вот архив (127 Кб)
скачайте и используйте если понадобится.
| |
|
|
| |
Мастер
начального кода программы в
компиляторе
ICC умеет по вашему желанию
автоматически делать
#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()
Чтобы использовать эти
функции необходимо
включить в начале программы заголовочный файл :
#include <stdlib.h>
Для
преобразования числа в строку
есть
itoa()
и
ltoa()
и аналогичные функции для чисел с плавающей
точкой.
Подробнее об этих и других полезных функциях смотрите раздел
"Standard Library Functions" справки компилятора
CodeVisionAVR. |
|
| |
|
|
Советую вам
скачать заголовочный файл
m8 128.h
Он содержит
названия битов МК ATmega8 -16 -32 -64 -128
и
сокращенные названия типов данных как в
компиляторе IAR.
вот
отрывок из него:
#define u8 unsigned char // 0 to 255
#define s8 signed char // -128 to 127
#define u16 unsigned int // 0 to 65535
#define s16 signed int // -32768 to 32767
#define u32 unsigned long int
// 0 to 4294967295
#define s32 signed long int
// -2147483648 to 2147483647
#define f32 float // ±1.175e-38 to ±3.402e38
#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-й) т.е. результат выровнен обычно
- вправо.
| |
|
|
| |
Кстати
БЭЙСИК тоже не плох !
Скачать BASCOM-AVR
можно там
Там же большой список интересных примеров со схемами и исходниками
выполненных пользователями BASCOM и
русскоязычная литература по Баском и BASIC.
BASCOM имеет свой
симулятор и программатор.
|
|
| |
|
|
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
| |
|
|
| |
/*
Пункт 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;
/*
Разрешаем ГЛОБАЛЬНО все
прерывания
разрешенные
индивидуально !
Вы
наверно поняли что индивидуально мы
разрешили
лишь прерывание по завершении АЦП -
вот оно то и
сможет возникать у нас. */
#asm("sei")
|
|
| |
|
|
Внимание
!
Так делаются
Ассемблерные вставки в
CVAVR
:
#asm ("инструкция на ассемблере")
Обратите
внимание - точки с запятой в конце НЕТ !
Такие
вставки принято иногда делать. НО они не являются
необходимыми.
На языке Си можно управлять ВСЕМИ программно
изменяемыми битами в регистрах МК !
| |
|
|
| |
Напоминаю
...
Все
регистры МК перечислены в таблице в конце ДШ с указанием номеров страниц с
подробным описанием регистра и его битов. |
|
| |
|
|
Часто используются такие
ассемблерные вставки :
#asm("sei") //
Разрешить ГЛОБАЛЬНО все
прерывания
#asm("cli") //
Запретить ГЛОБАЛЬНО все
прерывания
#asm("nop")
//
Пауза в 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
Вот её
первый абзац - цитата:
" Использование
алгоритмических языков высокого уровня
(ЯВУ) и, в частности, Си для
программирования микроконтроллеров,
несомненно, дает ряд преимуществ по
сравнению с языком ассемблера.
Основными из них являются:
· высокая скорость разработки программ;
· легкость отладки разрабатываемых
программ;
· независимость программного кода от
типа контроллера и, соответственно,
более или менее простой перенос
программ на разные платформы;
· простота сопровождения программ;
Исходные тексты на языке Си имеют
сравнительно небольшие размеры, сами
программы, как правило, хорошо
структурированы и понятны. "
Не
надо учить книжки наизусть!
Скачайте
чтобы иметь их "под рукой".
Повторю
Отличное
руководство по Си для AVR это HELP в
компиляторе CodeVisionAVR
Читайте
его и ищите в нем интересующее вас по ключевым словам.
|