Язык Си для микроконтроллеров AVR Си для МК AVR PIC страница 5  Краткий курс AVR начинающим с нуля - первые шаги.

Краткий курс - Самоучитель - AVR - быстрый старт с нуля


Книга   Язык программирования Си.
                     Брайан В. Керниган,    Деннис М. Ритчи

Внизу этой страницы !   Советую прочитать на досуге ...

 

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/

 

 

 

поиск   GOOGLE   по ключевым
словам в Кратком Курсе AVR

Поиск  на  этой  странице  -  Ctrl+F

 

 


Я расскажу об устройстве и структуре
программы на языке Си и опишу часто используемые в МК конструкции языка. 

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

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

Я покажу  вам, что Си это довольно ПРОСТО !

Если у вас реальные для начинающего цели. 
 

Минимальная программа на Си может быть такой:

     
  main(){}  
     

Эта программа не делает ни чего полезного - но это уже программа и она показывает что в программе на языке  Си - должна быть главная функция 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 так:  0x

- в восьмеричной форме с префиксом так: 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
% // даст 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 

  Рейтинг@Mail.ru


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


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   Читайте его и ищите в нем интересующее вас по ключевым словам.

 

 



 

          

 

 

 

 

Очень советую вот это:  

ЛАКОНИЧНО,  отлично написано! 

Для студентов! 

Ю.Ю.Громов, С.И.Татаренко

ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ СИ

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

Учебное пособие предназначено для студентов всех специальностей, аспирантов и
инженерно-технических работников использующих вычислительную технику. 

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

 

ОГЛАВЛЕНИЕ

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. РЕКУРСИЯ

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

 

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

 

 

 



Книга

Язык программирования Си

Брайан В. Керниган, Деннис М. Ритчи

 

 

 


Оглавление


Предисловие

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

Введение

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


Глава 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 Конструкция else-if
3.4 Переключатель switch
3.5 Циклы while и for
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. Объявление typedef
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.h>
B1.1. Операции над файлами
B1.2. Форматный вывод
B1.3. Форматный ввод
B1.4. Функции ввода-вывода символов
B1.5. Функции прямого ввода-вывода
B1.6. Функции позиционирования файла
B1.7. Функции обработки ошибок
B2. Проверки класса символа: <ctype.h>
B3. Функции, оперирующие со строками: <string.h>
B4. Математические функции: <math.h>
B5. Функции общего назначения: <stdlib.h>
B6. Диагностика: <assert.h>
B7. Списки аргументов переменной длины: <stdarg.h>
B8. Дальние переходы: <setjmp.h>
B9. Сигналы: <signal.h>
B10. Функции даты и времени: <time.h>
B11. Зависящие от реализации пределы: <limits.h> и <float.h>

 

Приложение С. Перечень изменений


Предметный указатель - язык Си





ключевые слова: программирование микроконтроллеров, как написать программу для микроконтроллера, обучение программированию микроконтроллеров, микроконтроллеры atmega128, как запрограммировать микроконтроллер, как прошить микроконтроллер, отладка программы для AVR, моделирование работы электронных схем, электронные проекты, хобби, язык си для микроконтроллеров, язык программирования си


AT76C712 , AT76C713 , AT90CAN128 , AT90CAN128 Automotive , AT90CAN32 , AT90CAN64 , AT90PWM2 , AT90PWM3 , AT90S1200 , AT90S2313 , AT90S2323 , AT90S2343 , AT90S4433 , AT90S8515 , AT90S8535 , ATmega128 , ATmega1280 , ATmega1281 , ATmega16 , ATmega161 , ATmega162 , ATmega163 , ATmega164 , ATmega165 , ATmega168 , ATmega168 Automotive , ATmega169 , ATmega2560 , ATmega2561 , ATmega32 , ATmega323 , ATmega324 , ATmega325 , ATmega3250 , ATmega329 , ATmega3290 , ATmega406 , ATmega48 , ATmega48 Automotive , ATmega64 , ATmega640 , ATmega644 , ATmega645 , ATmega6450 , ATmega649 , ATmega6490 , ATmega8 , ATmega8515 , ATmega8535 , ATmega88 , ATmega88 Automotive , ATtiny11 , ATtiny12 , ATtiny13 , ATtiny15L , ATtiny2313 , ATtiny25 , ATtiny26 , ATtiny28L , ATtiny45 , ATtiny85

 

 

Сайт управляется системой uCoz