Курс AVR123.nm.ru


Electronic Banner Exchange (ElBE)
 


 



 

 

 

 

 

 

 


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

 

 

Как и чем прошить МК AVR 
читайте на
странице 7 курса !

 

 

поиск   GOOGLE   по 
Краткому Курсу AVR

Подставьте слово
и получите результат

 


Книги и учебники по электронике и микроконтроллерам AVR PIC ARM  скачайте там

 

Цель задачи:  

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

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

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

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

Вы учитесь и это естественно !

 

 

 

 

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

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

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

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

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

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

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

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

Если все же не найдете ответа - задавайте вопрос в конференцию 

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

 

 



 

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

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

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

Сам МК ATmega16 НЕ НУЖЕН пока ! 

Все можно сделать на ПК на модели ATmega16.

 

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

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

 

 

 

 

 

 

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

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

Советы:

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

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

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

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

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

4) Почаще архивируйте содержимое папки проекта, создавая каждый раз новый архив, тогда в случае необходимости вам легко будет вернуться  на несколько шагов назад.      Читатель курса - Владимир - сообщил о специальной программе для контроля версий проекта - CS-RCS  (Revision control system от Component Software)  бесплатная лицензия Personal.

 

 

 

 



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 
и нажмите Сохранить

 

 

Помните 
о Help'е !

 



 и Используйте!


 

 

 

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

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



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

 

 

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

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


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

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

 

Реклама недорогих радиодеталей почтой:

 

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

Первая программа для AVR

Я уже говорил на стр. 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. 

Слева, в окне навигатора CVAVR вы можете посмотреть адреса по которым разместил ваши переменные компилятор.

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

 

 

 

 

 

 

 

Итак ... 

Вы получили начальные навыки работы 
в  компиляторе  CodeVisionAVR
  

Мы можем приступить к написанию реальной программы для МК.


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

 

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

 

 

Разработку электронного устройства нужно начинать 
с постановки задачи. 

Это делают в виде Т.З.  -  Технического задания.

 

 

 

 

Т.З. - это важнейший документ -- этап в создании устройства!    Можно сказать фундамент. 

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

Используйте бумагу и пишите подробно.

 

 

 

 

 

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

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

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

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

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

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

 

 

Делаем ... 

 

во первых !

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


ТЗ у нас простое и схема будет не сложной.



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

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

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

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

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

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

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

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

 

 

Во-вторых !

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

 

 

 

 

 

Алгоритм - это описание последовательности и логики шагов (действий) приводящих к нужному результату. 

Он в общем-то не зависит от языка программирования и может быть записан :

- графически с помощью специальной программы либо от руки. 

- псевдокодом, т.е. обычным текстом.

 

 

 

 

 

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

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

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

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

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

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

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

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


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

 

В-третьих !

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

Мы будем использовать Си для МК

 

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

 

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

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

В МК программа стартует автоматически после подачи питания и наличии "1" на ножке RESET.  Т.е. на Си ни чего специально для пуска программы писать не надо.

Но мы напишем то с чего обычно начинается программа на Си.  

А программа на Си начинается с директив препроцессора (это тот кто готовит текст программы к компиляции). Например вот так : 

 

 

 

 

#include <mega16.h>

#define PA_OUT DDRA = 0xFF

 

 

 

 

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

 

 

 

 

Удивляюсь ! Почему то большинство компиляторов не могут сами понять что нужно использовать файл mega16.h - хотя мы уже указали какой МК применяем в файле конфигурации проекта ! 

 

 

 

 

 

#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", то считывать, что там на них есть.  И обычно не всеми сразу, а то одной то другой, то несколькими !

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

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

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

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) в CodeVisionAVR   CVAVR

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;

5) в WinAVR 

#ifndef _BV    // If !WinAVR
#define _BV(n)    (1 << (n))
#endif

#define set_bit(p, n)    p |= _BV(n)
#define clear_bit(p, n)    p &= ~_BV(n)
#define invert_bit(p, n)    p ^= _BV(n)



Это очень нужные ВАМ операции !
Распечатайте на листочке.

Как читать значения битов смотрите в примерах на страничке  "Си для МК"

 

 

 

 

 

 

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

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


 

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

 

 



Очень подробно и с картинками я описал работу с мастером начального кода программы на Си для AVR компилятора ICC от ImageCraft и создание нового проекта в Задаче 6 

По-моему получилось понятно ! 

 

 

 

 

 

 

Реклама недорогих радиодеталей почтой:

 

 

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