ХОЧЕШЬ
РАЗМЕСТИТЬ ТУТ СВОЮ РЕКЛАМУ ИЛИ ЛИНК?
Предлагаю меняться линками или баннерами.
Пиши на info
sobaka 123avr.com
|
Задача - упражнение 1
Цель
задачи:
-
1) Познакомиться с компилятором
Си для AVR
CodeVisionAVR и получить основные навыки
разработки ПО для МК (наз. firmware).
-
2)
Закрепить знания по
языку
Си
для МК.
-
3)
Получить
файл .hex - это "прошивка"
готовая для загрузки в МК или для
проверки работы программы на симуляторе-эмуляторе.
Например в
VMLAB.
Не
расстраивайтесь если не все сразу будет понятно. Вы
учитесь и это естественно !
Для работы над задачей необходимы
:
Запускаем
компилятор :
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
МГц.
Для
изменения частоты тактирования МК
б)
сделать ножки МК к которым подключены
светодиоды выходами.
|
|
|
|
На
стр. 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.
|
|
|
|
|
Обработчик
прерывания вызывается если
ранее в программе мы
Прерывания
в 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 |