Страницы курса:

Уроки

Задания


index.htm
01.htm
02.htm
03.htm
04.htm
05.htm
06.htm
07.htm
08.htm
09.htm


z1.htm
z2.htm
z3.htm
z4.htm
z5.htm
z6.htm
z7.htm
z8.htm
z9.htm
z10.htm
z11.htm

New! - ФОРУМ!

Совет - умейте правильно находить информацию!


ХОЧЕШЬ РАЗМЕСТИТЬ ТУТ СВОЮ РЕКЛАМУ ИЛИ ЛИНК?
Предлагаю меняться линками или баннерами.
Пиши на  info
sobaka 123avr.com

 

Задача - упражнение  1

Цель задачи:

  • 1) Познакомиться с компилятором Си для AVR CodeVisionAVR и получить основные навыки разработки ПО для МК (наз. firmware). 

  • 2) Закрепить знания по языку Си для МК.

  • 3) Получить файл  .hex - это "прошивка" готовая для загрузки в МК или для проверки работы программы на симуляторе-эмуляторе. Например в VMLAB.   

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

Для работы над задачей необходимы :

  • - Установленный в директорию "по умолчанию" (C:\CVAVR) компилятор CodeVisionAVR

  • - Data Sheet (ДШ) на МК AVR ATmega16

  • -Сам МК ATmega16 НЕ НУЖЕН !  Все можно сделать на ПК.

  Запускаем компилятор :

1) В папке C:\CVAVR (где у вас должен находится компилятор
    CodeVisionAVR) создайте папку z1 для файлов этого проекта.   

   

 

 

 

 

В компиляторах и симуляторах вы работаете с проектами.

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

Советы:

1) Давайте папкам проектов осмысленные названия. например:  z1 - это понятно - задача 1.

2) Удобно (мне например) располагать файлы и компилятора и симулятора в одной общей папке проекта - тогда компилятор выдает выходные файлы 
.hex .cof __.c    а симулятор имеет к ним доступ для использования в симуляции.  

При этом вы одновременно запускаете на ПК и компилятор и симулятор и подправив код в симуляторе, "компилите" проект и переключившись в симулятор "ре-билдите все" и запускаете симуляцию уже нового варианта пошивки. 

Работается быстро и удобно!

3) Создайте в папке проекта еще одну папку, например, Files и помещайте в нее все документы ДШ и АпНоуты относящиеся к вашему проекту но не являющимися файлами компилятора или симулятора.

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

 

 

 

 


2) Запустите компилятор.

  • Для создания файла проекта нажимайте:  Файл -> новый -> проект -> ОК -> No 

  • - перейдите в созданную для проекта папку z1 и введите в поле "имя файла": z1

  • - нажмите "сохранить" - откроется окно конфигурации проекта.

  • - перейдите на закладку "С compiler" 

  • - выберите MK (Chip) ATmega16 

  • - установите частоту тактирования МК (Clock) 1,0 МГц

  • - убедитесь что в окне "File Output Format(s)" есть COF и HEX      

  • - теперь все должно выглядеть так: 

 

Кликните для просмотра полной картинки

щелкните по рисунку, чтоб увидеть всю картинку !

- Нажмите ОК.

 

 

 

 

 

.COF файл содержит привязку к тексту программы на Си (в компиляторе CodeVisionAVR к тексту файла с расширением __.c  - т.е. к копии а не к  самому "исходнику" с текстом программы на Си - файлу  .c ) к содержимому файла прошивки МК - .hex 

Эта информация позволяет при симуляции в VMLAB (или другом симуляторе) наблюдать движение программы прямо по коду на языке Си, расставлять точки останова и иметь прочие радости цивильной отладки программы !

 

 

 

 


Перед вами появится открытый текстовый файл  Project Notes - z1.prj   в котором вы можете записывать свои замечания и мысли по проекту. 

Теперь нужно создать главный для нас текстовый файл для набора исходного текста на Си - его расширение  .с 

- нажимайте: Файл -> New -> Source -> ОК    

появился файл    untitled.c

- нажимайте: Файл - Сохранить как 

- введите в поле "имя файла":   z1.c  и нажмите Сохранить

Нужно добавить созданный файл z1.c   в список файлов проекта.

- откройте меню конфигурирования проекта: Прожект -> Конфига:



В открывшимся диалоге, нужно выбрать ярлык "Files" и нажать кнопку "Add"  

 

 

В новом диалоге, выберите файл "z1.c" и нажмите "Открыть"

Теперь файл включен в проект:


- нажимайте:  ОК 

- максимизируйте (разверните) окно файла  -  z1.c

  Теперь все готово к собственно программированию - т.е. к созданию текста программы на языке Си.
 

Первая программа на Си:

Я уже говорил на стр. 5 курса, посвященной языку Си для МК - что Минимальная программа на языке Си выглядит так:

main() {  }         

Вся программа состоит из одной строчки и с одержит обязательную для программы на Си функции:  main  (с англ. == главная). 

Эта программа бесполезна, она ни чего не делает, но ее можно скомпилировать нажав кнопку - "мэйк зе проджект" выполнить полную компиляцию проекта:

 



 

Появится информационное окно и сообщит что компиляция прошла успешно: 
Ни ошибок, ни "вонингов" (предупреждений) нет. 

 

 

 

 

Вы наверно догадались, что набираемый текст появляется в файле z1.c и он же компилируется по кнопке  "мэйк зе проджект Если  это вам не понятно то начните чтение задачи сначала и делайте это более внимательно!

 

 

 

 

Но что удивительно, программа уже имеет размер 92 слова или 184 байта - это составляет 1,1% объема памяти программ выбранного нами МК ATmega16.

Загляните в папку нашего проекта - z1 - в результате компиляции, там появилось много новых файлов.

 

 

 

 

 

Главные для нас конечно : 

z1.hex      - файл-прошивка для "загрузки" в МК
z1__.с      - копия файла z1.c для симуляторов 
z1.cof       - информация связывающая содержимое файлов z1__.с и z1.hex.

Эти три файла удобно использовать в симуляторе VMLAB или PROTEUS и в других. Необходимым для реального МК является лишь  файл прошивки -  z1.hex

 

 

 

 

z1.asm 
z1.lst  
z1.vec 
z1.inc

эти 4 файла содержат нашу программу написанную на стандартном ассемблере для AVR с привязкой к тексту на Си. Остальные файлы практически не интересны.

Вы уже ДОЛЖНЫ знать: 

  • - как создать новый проект в CVAVR, 

  • - как запустить компиляцию программы, 

  • - какие файлы мы получим в результате.

Если  это вам не понятно то ЛУЧШЕ начните чтение задачи сначала и делайте это более внимательно !

 

 

 

 

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

 

 

 

 


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

Добавим в тело функции  (это пространство между фигурными скобками { }  т.е. все что написано от скобки { до скобки }main операцию с двумя не объявленными переменными и не поставим в конце точку с запятой : 


main(){
temp = papa + 2
}

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

 

Внизу экрана в окне сообщений - Messages - появится сообщение только об одной ошибке.  Сообщение содержит название файла и номер строки в которой обнаружена ошибка, а так же краткое описание ошибки. Аналогичное сообщение появилось и в окне навигатора по проекту :

 

 

Если щелкнуть по линку будет подсвечена строка в тексте программы содержащей ошибку.

Компилятор заругался на использование в тексте программы неопределенного символа:      temp 

Разберем эту ситуёвину:

Раз мы написали  temp  с лева от знака = 

значит мы хотели присвоить temp какое то значение 

и значит temp - это переменная.

 

 

 

 

 

Очень важно!   Напоминаю:  

в Си знак      = 

это не знак равно!  

Это оператор присваивания значения справа от знака  =  
тому что стоит слева от знака
=

Поэтому запись :    

х = х + 3

не лишена смысла как это было бы в математике, а означает буквально:  

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

 

 

 

 

 

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

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

Подробно о переменных и тапах данных вы можете прочитать 
в help'е компилятора - раздел Variables и
Data Types.

 

Наиболее часто используемый в МК тип: 

unsigned char - без знаковый символьный - может хранить числа от 0 до 255 - это один байт или 8 бит или один регистр 8-ми битного МК AVR.

 

 

 

 

 

Внимание ! 

Если переменная х была, например, объявлена так 

 unsigned char  х;

и   х имела значение 254 то после строки

х = х + 3;

ее значение станет 1 (десятичная единица), 

так как если прибавлять 3 раза по единице то значение х будет меняться так:

вначале  255,       затем   0,        и     наконец   1

Обязательно поймите это !

Так меняются значения в регистре 8-ми битного таймера МК когда он "тикает" - т.е. считает.

 

Правило: при "переполнении" диапазона допустимых для переменной значений  происходит переход от максимального значения к минимальному и затем значение увеличивается далее. 

т.е. 255 затем 0 затем 1 затем 2 и так далее, по кругу.

В обратную сторону - аналогично!

 

 

 

 

 


Если в свойствах проекта вы не убирали галочку:

 

то можете писать просто:  char 

Так  "по умолчанию" в большинстве компиляторов !

char temp;

main(){
temp = papa + 2
}

 

 

 

 

 

Обратите внимание мы объявили переменную вне тела какой либо функции, такие переменные являются глобальными - т.е. "видны" == доступны и могут быть использованы и изменены в любом месте программы, в любой функции. 

 

 

 

 

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

Так и есть !

Объявим переменную   papa  но не новой строкой, а в той же где объявили   temp

Кроме того инициализируем её - т.е. присвоим ей значение при объявлении - числом 0xFE - это 16-ричное представление десятичного 
числа  254

char papa = 0xFE, temp;

 

 

 

 

 

В одну строку можно записать объявление нескольких переменных одного типа через запятую. После последней не забудьте поставить точку с запятой. Если при объявлении глобальной переменной не присвоить ей значение то она будет содержать 0.      

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

 

 

 

 

  Теперь обе наши переменные без знаковые символьные, при этом 

temp  содержит число 0

papa содержит (хранит) значение 254.

Нажмите кнопку    проверить текст программы  

Компилятор сообщает нам о пропущенном символе     ;    

 

 

 

 

 

Обратите внимание !  ВАЖНО !  компилятор указывает на строку ниже чем та  где мы забыли поставить точку с запятой. 

 

 

 

 

Знак  ; нужно поставить после числа 2  - вот так:

char papa = 0xFE, temp;

main(){
temp = papa + 2
;
}

 Теперь проверка программы не выдает сообщений об ошибках и можно выполнить полную компиляцию программы нажав кнопку :

 

Наконец то в информационном окне мы видим, что ошибок нет и размер программы стал 99 слов или 1.2% памяти программ ATmega16. 

 

 

 

 

Вы можете приказать компилятору разместить переменную по определенному адресу, в указанном вами регистре:

register int beta @10;

Старший байт двухбайтовой переменной beta будет размещен в регистре общего назначения 10 а младший в регистре 11. 

При симуляции программы вы можете вывести панель с регистрами МК и увидеть какие значения находятся в этих регистрах и как они меняются по ходу выполнения программы.

 

 

 

 

Итак ... 

Вы получили начальные навыки работы в  компиляторе  CodeVisionAVR  и  мы можем приступить к написанию реальной программы для МК. Программу будем писать на основе АпНоута Application Note AVR031 "Getting Started with ImageCraft C for AVR"  компании Atmel - в котором рассказывается как начать работать с компилятором от компании ImageCraft - тоже очень достойный компилятор ! 

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

Разработку электронного устройства нужно начинать  с постановки задачи.  Это делают в виде Т.З.  -  Технического задания.

 

 

 

 

Т.З. - это важнейший документ -- этап в создании устройства !    Можно сказать фундамент.  Не пытайтесь "прикинуть" его в уме.  Используйте бумагу - пишите подробно.

 

 

 

 

Итак ...

Простейшее ТЗ: 

  • Разработать устройство на микроконтроллере ATmega16 которое будет отображать, в двоичном виде, НЕ горящими светодиодами 8-ми битное число начиная с 0 и с постоянным увеличением на 1. 

  • Устройство питается постоянным стабилизированным напряжением 
    от 4 до 5.5 вольт. 

  • Тактирование МК осуществляется от кварцевого резонатора с частотой 4 МГц. 

  • Всего подключено 8 светодиодов от ножек порта A через токо-ограничительные резисторы к питанию МК. 

  • Переключение светодиодов должно производится с паузами в 65 мС.

Делаем ... 

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

  • а) Кварц на 4 МГц подключаем к ножкам МК XTAL1 и XTAL2 и эти ножки заземляем конденсаторами по 22 пФ на ножку GND номер 11.

  • б) Делаем цепь сброса МК - RESET. Для этого ножку 9 заземляем конденсатором 0.1 мкФ на ножку GND номер 11. От ножки 9 ставим диод 4148 (или любой маломощный маленький кремниевый, например кд522) черточкой (на графическом изображении диода) к питанию МК - ножка 10 VCC. Параллельно диоду ставим резистор 10 кОм.  

  • в) Ножки 30 и 32 соединяем с ножкой 10. 

  • г) Ножку 31 с ножкой 11.

  • д) Питание МК подается так:  0 на ножку 11 и +5 вольт на ножку 10.

  • е)  Поставьте блокировочный конденсатор 0.1 мкФ между ножками 10 и 11.

  • ж) 8 светодиодов подключаются черточками к ножкам МК с 33 до 40. К другим выводам светодиодов подсоединяются резисторы от 470 до 750 Ом - их тоже будет 8 - вторые выводы резисторов подсоединяются к питанию МК - ножка 10.

  • з) Если вы захотите "залить" полученную прогу в реальный МК вам понадобится добавить несколько соединений и разъем для программирования - как написано на странице курса о прошивании МКИ запрограммировать фьюзы под кварц на 4 МГц по ДШ.

во вторых, нужно придумать алгоритм который заставит работать созданную схему выполняя ТЗ.

 

 

 

 

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

 

 

 

 

Мы напишем алгоритм в виде псевдокода. 

  • а) старт программы 

  • б) сделать ножки МК к которым подключены светодиоды выходами. 

  • в) запустить таймер МК со скоростью счета обеспечивающей его переполнение каждые 65 мС. 

  • г) добавить 1 к числу в регистре PORTA 

  • д) ждать переполнения таймера 

  • е) очистить признак переполнения таймера. 

  • ж) перейти к пункту г)

Все - алгоритм готов и записан в псевдокоде.

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

Листинг (== текст) программы на языке Си для 
МК  ATmega16 - я буду набирать на голубом фоне.

Будем двигаться по нашему алгоритму записанному в виде псевдокода:

а) старт программы.

В МК программа стартует автоматически после подачи питания и наличии "1" на ножке RESET.  Т.е. на Си ни чего специального писать не надо. Но мы напишем то с чего обычно начинается программа на Си.  А программа на Си начинается с директив препроцессора (это тот кто готовит текст программы к компиляции). Например вот так : 

 

 

 

 

#include <mega16.h>

#define PA_OUT DDRA = 0xFF

 

 

 

 

#include<>  -  означает, что вместо #....>  препроцессор должен подставить текст файла - mega16.h - в нем описаны регистры МК и именно ATmega16, чтобы компилятор знал их физические адреса в МК и не ругался когда увидит их в тексте вашей программы !

#define AAA BBB - означает что препроцессор компилятора перед компиляцией программы заменит в ее тексте все найденные AAA на то, что стоит правее пробела после AAA. В этом примере на BBB, но это могут быть и более сложные выражения ! 

Вы поняли, что в нашей программе перед компиляцией 
PA_OUT
          будет заменено на   DDRA = 0xFF     

и при этом текст в окне   z1.c  конечно не изменится !

Вы наверно заметили (если внимательно читаете !) что мы в свойствах проекта установили частоту тактирования 1 МГц - значит нужно изменить ее на 4 МГц.

Для изменения частоты тактирования МК 

  • - откройте меню конфигурирования проекта: Прожект -> Конфига

  • - щелкните закладку  C Compiler   и измените значение Clock на 4 MHz 

  • - нажмите - OK

б) сделать ножки МК к которым подключены светодиоды выходами. 

 

 

 

На стр. 2 курса я уже рассказывал вам, что 

Каждому порту в МК AVR соответствуют минимум три регистра:

DDRx - регистр направления работы - вход или выход

х - означает букву A, B, C, D, E... порта, по числу портов в конкретном МК.

PINх - регистр содержит значения физических (т.е. реальных, тех которые мы получим измерив вольтметром и преобразовав в 1 или 0) уровней сигнала на соответствующих ножках МК. 


PORTх - регистр в который МЫ записываем значения "1" или "0" -  которые хотим получить на соответствующих ножках МК при назначении их выходом.  Т.е. если соответствующий бит в регистре DDRx установлен (значит равен "1"). 

ВАЖНО! - если бит в регистре DDRx равен "0", а в такой же бит PORTх записана "1" то "ножка" МК будет "входом с подтяжкой" т.е. к ней как бы подключен резистор примерно 40 кОм от питания МК.

PORTA - подробно описан КОНЕЧНО в ДатаШите   как и все устройство МК.

 

 

 

 

в)      запустить таймер МК со скоростью счета обеспечивающей 
          его переполнение каждые 65 мС. 

В МК ATmega16 есть несколько таймеров. Самый простой из них  8-ми битный таймер-счетчик Timer_0 . см. ДатаШит - раздел "8-bit Timer/Counter0

 

 

 

 

 

8-ми битный означает что он в простейшем режиме работы Normal Mode считает так:  

n, n+1, n+2, ... 253, 254, 255, 0, 1, 2, ...

Так меняются числа в регистре таймера TCNT0

Причем при появлении в нем  0 после 255 возникает "переполнение" таймера_0

И в регистре флагов прерываний от таймеров - TIFR  устанавливается (т.е. становится "1") бит_0  - он называется "флаг возникновения события - переполнение таймера 0". 

 

 

 

 

Опять же из ДШ узнаем, что: 

Для запуска Таймера_0  - т.е. для того чтобы происходило периодическое увеличение значения в регистре Таймера_0  TCNT0 - нужно лишь установить коэффициент деления. 

На этот коэф. будет делится  частота тактирования МК (clock  -  частота кварца например или частота работы внутреннего RC генератора МК)  перед тем как подать на таймер_1 как источник его счета - "тиканья".

Для этого нужно изменить в соответствии с таблицей  
Table 42. Clock Select Bit Description
 
 
биты2_0 – CS02 CS01 CS00 "Clock Select" в регистре TCCR0 управления Таймером_0 - он называется в ДШ "Timer/Counter Control Register

 

 

 

 

 

биты2_0  -  значит бит2, бит1 и бит0.

 

 

 

 


Если все биты  CS02 CS01 CS00 - нули - то таймер остановлен ! 

Давайте посчитаем ... 

Кварц дает 4000000 колебаний в секунду, поделим их на 256 (столько насчитывает таймер_0 между переполнениями) и на максимально возможный коэф. деления  1024 по таблице 42.

Получаем  15,2587890625 переполнений (т.е. переходов значения в регистре TCNT0  из 255 в 0) таймера_0 в секунду.  Период переполнений - это обратная величина и составляет 65,536 мС.

Это достаточно точно соответствует паузе 65 мС заданной в ТЗ.

 Теперь мы теоретически подкованы достаточно 
чтобы написать код на Си выполняющий пункты
б) и в)

Эти два пункта нашего алгоритма настраивают задействованную в устройстве периферию МК (это PORTA и Timer0) так как нам нужно.

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

 

 

 

 

// ++++ функция инициализации МК ++++
void initialization(void){

PA_OUT;//сделать PORTA выходом

TCCR0 = 0x05;/* таймер включить считать с частотой 1024 раз меньше чем тактовая.*/
}

 

 

 

 

Разберем что мы написали ...

void initialization(void){

 

Это

 

 

тело

 

 

функции

 

}

Это функция, которая названа нами осмыслено: initialization т.е. очевидно "инициализация".

 

 

 

 

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

 

 

 

 

Строчка программы :

PA_OUT;//сделать PORTA выходом

будет заменена препроцессором перед компиляцией в соответствии с написанной выше директивой  #define на:

DDRA = 0xFF;

Вы уже должны точно знать что эта строка означает - поместить в переменную DDRA (в данном случае это регистр допускающий запись в него число 0xFF.  

Это десятичное число 255  а в двоичном виде 0b1111 1111 

Значит после выполнения этой строчки программы все биты регистра DDRA станут "1" (т.е. будут установлены)

Ранее я информировал вас и чуть выше напоминал !   что при этом ножки порта A станут выходами. Значит пункт б) нашего  алгоритма выполнен.

Строчка программы :

TCCR0 = 0x05;

поместит в переменную TCCR0 (регистр МК  TCCR0) число 0b00000101   (это и есть 0x05 или в десятичном виде число 5) - т.е. биты 2 и 0  "установятся" - значит станут единицами, а бит 1  останется "0".  

 

 

 

 

Конечно в программе можно написать так:

TCCR0 = 0b00000101;

или даже так: 

TCCR0 = 5;

 

 

 

 


А выше мы уже нашли в ДШ что так (бит2_0     1  0  1) установится коэф. деления частоты кварца на 1024 и таймер начнет считать - в "нормальном режиме" - т.е. добавлять единицу к числу в регистре TCNT0  каждые 1024 периода колебаний сигнала (говорят еще - тактов, клоков) на ножке XTAL1.

Теперь и пункт в) нашего  алгоритма выполнен.

... с функцией initialization  разобрались !  

Идем дальше.

Напомню оставшуюся часть алгоритма:

  • г) добавить 1 к числу в регистре PORTA 

  • д) ждать переполнения таймера 

  • е) очистить признак переполнения таймера. 

  • ж) перейти к пункту г)

Мы видим, что оставшаяся часть алгоритма будет выполняться  циклически.

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

main(){

 

Это

 

 

тело

 

 

функции

 

}

в ней вначале вызвать функцию инициализации МК и затем 

написать безусловный цикл :

while(1){

 

Это

 

 

тело

 

 

цикла

 

};

Пишем ...

 

 

 

 

// ++++  Главная функция  ++++
void main (void){

initialization(); /* Вызвать (== выполнить) функцию инициализации МК */

//Бесконечный цикл
while (1){ //Делать всегда

PORTA++; //добавить 1 к значению в PORTA

while (!(TIFR&0x01));
// ждем установки флага переполнения timer0

TIFR = 0x01
// очистить флаг переполнения timer0

         };
//цикл while (1)закончен

 

 

 

 


Разберем что написали ...

До начала бесконечного цикла вам должно уже быть все понятно.

Цикл :

while (1){}; 

"бесконечный" потому, что в скобках где стоит условие его выполнения написана константа - число 1 - это "не ноль" значит "истина" в Си - поэтому цикл будет выполняться пока есть питание МК и нет "ресета".

 

 

 

 

Можно написать любое число отличное от  0

не ноль это "истина" ! 

например вот так:

while (121){};

или так: 

while (-17){};

или даже так: 

while (!0){};

 

 

 

 

Строчка программы :

PORTA++; //добавить 1 к значению в PORTA

вы, уверен! знаете уже - берет число из переменной с именем PORTA, добавляет к ней 1 и результат помещает обратно в переменную PORTA. 

Строчка программы :

while (!(TIFR&0x01));
// ждем установки флага переполнения timer0

Поинтересней будет ... 

это условный цикл :

while (условие){}; 

НО !  без тела - поэтому скобки {} опущены.  

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

Давайте разбирать  -    условие   в скобках. Вот оно :

!(TIFR&0x01)

напомню - знак   !  означает логическое отрицание - меняет  "ложь" на "истину" и наоборот.

Значит в цикле мы будем "сидеть", а МК будет в нем "молотить" пока результат вычисления выражения 

TIFR&0x01

будет равен 0 что значит в Си "ложно".

& - это побитное (поразрядное) логическое "И"  - означает что только 1  и  1 дают  1.

В числе 0x01  все биты равны 0 кроме бит_0  - значит когда бит_0 в регистре TIFR станет "1" то результат & станет 1 или "истина" и программа покинет цикл.

Выше я писал, что бит_0 регистра TIFR - это "флаг"  ("1" - как бы поднят флажок - есть событие которому соответствует этот флаг) события: "переполнение таймера_0".

 

 

 

 

Почитайте описание регистра TIFR в ДШ

Вот про этот   бит_0

Bit 0 – TOV0

Timer/Counter0 Overflow Flag

The bit TOV0 is set (one) when an overflow 
occurs in Timer/Counter0. 

 

 

 

 

 

Значит когда таймер переполнится, программа покинет цикл:

while (!(TIFR&0x01));

Т.е. программой выполнен пункт алгоритма:

д) ждать переполнения таймера 

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

е) очистить признак переполнения таймера. 

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

В описании бит_0 регистра TIFR сказано так же про "очистку" 
или "сброс" этого бита - т.е. как сделать его опять нулем:

 

 

 

 

TOV0  is cleared by hardware when executing 
the corresponding interrupt handling vector. 

 

 

 

 

Здесь сказано, что 

бит_0 (TOV0) сам очищается (становится "0") если происходит вызов обработчика прерывания связанного 
с событием устанавливающим данный флаг - т.е. 

Это относится и к флагам других прерываний !

 

 

 

When the SREG I-bit, TOIE0(Timer/Counter0 
Overflow Interrupt Enable), and TOV0 are set 
(one), the Timer/Counter0 Overflow interrupt 
is executed. 

 

 

 

 


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

  • - разрешили прерывания глобально ("установив" бит I в регистре SREG ) и 

  • - разрешили данное прерывание "установив" бит TOIE0.

Прерывания в AVR - это важно ! почитайте подробней

 Мы не планировали использовать прерывание от таймера_0 и не разрешали его и не разрешили прерывания вообще. 

Поэтому мы используем другой метод очистки флага :

 

 

 

Внимание ЧУДО !  
Что бы опустить флажок - т.е. обнулить бит-флаг в него нужно вписать ОДИН.
Alternatively, TOV0 is cleared by writing  a logic one to the flag. (из ДШ)

Только у AVR я видел такую странную штуку, у других МК чтоб "обнулить" флаг - нужно вписать ЕСТЕСТВЕННО НОЛЬ !  С этим можно жить - просто надо запомнить...

 

 

 

 

строка программы:

TIFR = 0x01
// очистить флаг переполнения timer0

как вы должны УЖЕ понимать помещает число 1 в регистр  т.е. именно вписывает логическую  "1" в бит_0  - тем самым очищает флаг и выполняет пункт алгоритма   е) очистить признак переполнения таймера. 

 

 

 

Внимание !  строка:

TIFR = 0x01

вписывает в другие биты регистра (кроме бит_0)  нули ! 

В нашем случае это допустимо. 

Если вам нужно записать "1" только в один бит_n регистра не трогая другие биты используйте 

манипулирование отдельными битами 

Вот такой код:

REGISTER |= 1<<n

Если вам нужно записать "0" только в один бит_n регистра не трогая другие биты используйте такой код:

REGISTER &= ~(1<<n);

В бит n появится нужное вам число не зависимо от того  что там было!

Подробнее об операциях с отдельными битами, в разных компиляторах рассказано ниже! 

 

 

 

 

Последний пункт нашего алгоритма 

ж) перейти к пункту г)

не требует написания специального кода - он выполняется автоматически, 
так как программа выполнила все "тело" цикла и сама перейдет опять в начало цикла, т.е. к пункту алгоритма - 
г)

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

А будет ли она это делать мы проверим после компиляции, путем запуска ее в симуляторе в задаче-упражнении 2.

вот весь текст программы:

 

 

 

 

#include <mega16.h> /* Вставить вместо этой строки текст файла mega16.h содержащий описание регистров МК. Не забывайте указать какой МК применяете в свойствах проекта ! */

#define PA_OUT DDRA = 0xFF 
/*
Заменить везде в тексте программы 

PA_OUT    на       DDRA = 0xFF           
*/

 

// ++++ функция инициализации МК ++++
void initialization(void){

PA_OUT;//сделать весь PORTA выходом

TCCR0 = 0x05;/* таймер включить считать, делая один отсчет каждые 1024 колебания на ножке XTAL1 */
}


// ++++  Главная функция  ++++
void main (void){

initialization(); /* Вызвать (== выполнить) функцию инициализации МК - т.е. настройки нужных нам устройств МК в соответствии с поставленой задачей */

//Бесконечный цикл
while (1){ //Делать всегда

PORTA++; //добавить 1 к значению в PORTA

while (!(TIFR&0x01));
// ждем установки флага переполнения timer0

TIFR = 0x01
// очистить флаг переполнения timer0

         };
//цикл закончен


//скобка для main() 

 

 

 

 

 

Опять запускаем компилятор :

  • - Откройте проект  C:\CVAVR\z1\z1.prj (напомню: файл -> оупен) и разверните окно исходного текста программы - файл z1.с

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

  • - Сохраните изменения файл -> сейф ол 

  • - Для компиляции программы нажмите кнопочку "мэйк зе прожект" де 

Компиляция должна пройти без ошибок и "вонингов", результат выводится в информационное окно:

EEPROM usage: 0 byte(s) (0,0% of EEPROM)

Program size: 111 words (1,4% of FLASH)

Если у вас возникли ошибки - то постарайтесь сделать последние несколько шагов за ново и без ошибок - все должно получится.

 Вы можете скачать архив z1.rar содержащий проект, исходник программы на Си и результаты компиляции.

Эти файлы мы будем использовать в  задаче-упражнении 2 при проверке работы нашей программы.

 

 

 

 

Внимание !  Очень важно!  Битовые операции ! 

Манипуляции отдельными битами регистров и переменных.

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

И обычно не всеми сразу, а то одной то другой !

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

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

ПРОВЕРЕНО в компиляторах  IAR,  CodeVision,  ImageCraft

1) мой вариант для любого компилятора:

// объявление:

#define SET_B(x) |= (1<<x
#define CLR_B(x) &=~(1<<x
#define INV_B(x) ^=(1<<x

// x - номер бита в регистре

// использование:

PORTB SET_B(5);  // "установить"  бит5

PORTB CLR_B(2);  // "очистить"  бит2

PORTB INV_B(6);  // инвертировать бит6

/* 
"установить"  значит сделать "1"

"очистить"  значит сделать "0"

"инвертировать" - сделать "0" если был "1" 
                  и наоборот.              
*/



2) в CodeVision:

PORTB.7=0;   // сделать бит7  "0"

PORTB.5=1;   // сделать бит5  "1"

PORTB.1^=1;   // инвертировать бит1

Работает только с регистрами адрес которых меньше 31 !


3) в ImageCraft: (есть в Хелпе: "Bit Twiddling")

#include <macros.h>

PORTB &= ~BIT(7); // сделать бит7  "0"

PORTB |= BIT(5); // сделать бит5  "1"

PORTB ^= BIT(1); // инвертировать бит1


4) в IAR:

PORTB_Bit7 = 0;
PORTB_Bit5 = 1;
~PORTB_Bit1;

Это очень нужные ВАМ операции!

распечатайте на листочке.

 

 

 

 

 

Задача-упражнение 1 завершена. 

Теперь у вас есть навыки работы с компилятором, понятие о последовательности создания программы 
и есть файл "прошивки" для МК  
  z1.hex 

Дальше ->    Задача 2  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Rating All.BY Rambler's Top100 МЕТА - Украина. Рейтинг сайтов


2009-2015 123avr.com