Форум по микроконтроллерам: Программирование c нуля в AVRStudio 5 - Форум по микроконтроллерам

Перейти к содержимому

  • 14 Страниц +
  • 1
  • 2
  • 3
  • 4
  • Последняя »
  • Вы не можете создать новую тему
  • Вы не можете ответить в тему

Программирование c нуля в AVRStudio 5 основы и основные понятия

#21 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 242
  • Регистрация: 26 Февраль 11

Отправлено 28 Февраль 2012 - 20:43

Если, одновременно придет запрос на прерывания, например "Reset" и "WDT_ OVERFLOW", то программа перейдет на вектор "Reset", т.к. этот вектор прерывания ближе всех к нулевой строке программы, поэтому в плане приоритета они не равноцены и это надо учитывать. Если же программа уже перешла в подпрограмму прерывния, а в это время возникло несколько запросов на новые прерывания, то флаги этих прерываний останутся поднятыми, и первым будет выполено то прерывание, вектор которого ближе к нулевой строке... потом второй, третий и так далее.

Сообщение отредактировал galrad: 28 Февраль 2012 - 20:45

0

#22 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 29 Февраль 2012 - 13:05

Просмотр сообщенияgalrad (28 Февраль 2012 - 20:43) писал:

флаги этих прерываний останутся поднятыми
Ну, в моём понимании, "более высокий приоритет", это как-раз что, при возникновении такого прерывания (с более высоким приоритетом), начнётся его выполнение, а уже выполняющееся прерывание (с более низким приоритетом) будет отложено.
Я не прав?
0

#23 Пользователь офлайн   Alex 

  • Убиватель МК
  • PipPipPipPip
  • Группа: Пользователи
  • Сообщений: 1 895
  • Регистрация: 15 Февраль 11

Отправлено 29 Февраль 2012 - 19:21

Дмитрий, имеется в виду программная реализация приоритетности. При заходе в обработчик и при условии 2-х или более ожидаемых событий, обработается событие, находящееся ниже по адресу.
Не говорите что мне делать, и я не скажу куда Вам идти !
0

#24 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 29 Февраль 2012 - 22:24

Что подразумевается, и что будет происходить, я прекрасно понимаю.
Вопрос в том, что на техническом языке называется "приоритетом"?
0

#25 Пользователь офлайн   Alex 

  • Убиватель МК
  • PipPipPipPip
  • Группа: Пользователи
  • Сообщений: 1 895
  • Регистрация: 15 Февраль 11

Отправлено 29 Февраль 2012 - 23:17

Вам не понятно что такое приоритет ?
Не говорите что мне делать, и я не скажу куда Вам идти !
0

#26 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 05 Март 2012 - 19:36

Т.е., по Вашему, для ПИКов среднего семейства, можно изменять приоритет прерывания?
0

#27 Пользователь офлайн   MAZ 

  • Консерватор - до мозга костей и суставов
  • Группа: Администраторы
  • Сообщений: 1 422
  • Регистрация: 15 Февраль 11
  • ГородРоссия

Отправлено 05 Март 2012 - 21:04

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

#28 Пользователь офлайн   Alex 

  • Убиватель МК
  • PipPipPipPip
  • Группа: Пользователи
  • Сообщений: 1 895
  • Регистрация: 15 Февраль 11

Отправлено 05 Март 2012 - 21:10

Дмитрий, Вы, походу, так и не поняли о чём идёт речь.
Речь идёт о приоритетности 2-ух или более ожидаемых событий при переходе программы на вектор прерываний. А не о прерывании выполнения более низкого по приоритетности обработчика , более высоким по приоритетности событием.
Не говорите что мне делать, и я не скажу куда Вам идти !
0

#29 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 06 Март 2012 - 16:00

Я прекрасно понял о чём речь.
Только я не считаю, что это - более высокий приоритет.
Но коли это - так чудно, тогда умолкаю.
0

#30 Пользователь офлайн   Alex 

  • Убиватель МК
  • PipPipPipPip
  • Группа: Пользователи
  • Сообщений: 1 895
  • Регистрация: 15 Февраль 11

Отправлено 06 Март 2012 - 16:56

Цитата

Только я не считаю, что это - более высокий приоритет.
А что же тогда это по Вашему ?
Не говорите что мне делать, и я не скажу куда Вам идти !
0

#31 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 242
  • Регистрация: 26 Февраль 11

Отправлено 06 Март 2012 - 19:29

Просмотр сообщенияDmitry Dubrovenko (29 Февраль 2012 - 13:05) писал:

Ну, в моём понимании, "более высокий приоритет", это как-раз что, при возникновении такого прерывания (с более высоким приоритетом), начнётся его выполнение, а уже выполняющееся прерывание (с более низким приоритетом) будет отложено.
Я не прав?

Для AVR - это называется вложенным прерыванием. Вложенное прерывание не является приоритетным, оно задается программно. Если вложенные прерывания не заданы , то подпрограмма прерывания работающая сейчас - выполняется полностью. Если в процессе выполнения возникают события вызывающие другие прерывания, то они отмечаются поднятием флагов. А вот какой из них будет выполнен первым и определяется приоритетом.
В Пиках действительно, приоритетным считается то прерывание, которое останавливает действующее сейчас прерывание. Поэтому технически - приоритет в Пиках отличается от приоритета в AVR.

Сообщение отредактировал galrad: 06 Март 2012 - 19:30

0

#32 Гость_immoren_*

  • Группа: Гости

Отправлено 17 Май 2012 - 01:43

В продолжение примера программы с "бегущими огоньками"... если подключить к PD1 ещё одну кнопку, и чтобы при нажатии первой огоньки бежали в одну сторону, при нажатии второй -в другую, и причем чтобы для смены направления не нужно было кнопку "зажимать" (тоесть достаточно однократного нажатия), как такое можно реализовать?

#33 Пользователь офлайн   MAZ 

  • Консерватор - до мозга костей и суставов
  • Группа: Администраторы
  • Сообщений: 1 422
  • Регистрация: 15 Февраль 11
  • ГородРоссия

Отправлено 18 Май 2012 - 08:16

galrad на некоторое время выбыл из строя. Болезнь серьезная, сейчас пошел на поправку. Ждем его возвращения и желаем крепкого здоровья. Потерпите не много.
Если бы Бог не был консерватором, вместо десяти заповедей мы имели бы десять предложений
0

#34 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 242
  • Регистрация: 26 Февраль 11

Отправлено 20 Май 2012 - 13:01

Просмотр сообщенияimmoren (17 Май 2012 - 01:43) писал:

В продолжение примера программы с "бегущими огоньками"... если подключить к PD1 ещё одну кнопку, и чтобы при нажатии первой огоньки бежали в одну сторону, при нажатии второй -в другую, и причем чтобы для смены направления не нужно было кнопку "зажимать" (тоесть достаточно однократного нажатия), как такое можно реализовать?

Возможен такой, достаточно простой, вариант:
; ****************************************
; Автор:
; Дата:
; Версия:
; Название файла:
; Для AVR: ATtiny2313
; Тактовая частота: 8мГц
; ****************************************

; Выполняемые функции:;реверсивные бегущие огни с управлением от 2-х кнопок

; ==================================================
; .device;
; .nolist;
; .include;
; .list;
; ===================================================
.def temp=r16   ; директива .def назначает регистру r16 имя temp
.def temp1=r17    ; директива .def назначает регистру r17 имя temp1
.def temp2=r18    ; директива .def назначает регистру r18 имя temp2
.def temp3=r19    ; директива .def назначает регистру r19 имя temp3
.def temp4=r20    ; директива .def назначает регистру r20 имя temp4
;====================================================
; Начало программы
.cseg   ; директива .cseg определяет начало сегмента кодов
.org 0    ; начало первой строки программы

; =================================================== Инициализация стека

init: ldi temp,low(RAMEND) 	 ; Устанавливаем младший бит из значения RAMEND прописанного по
  out SPL,temp   		 ; умолчанию в файле tn2313def.inc
  .if (RAMEND)>=0x0100 	 ; Если значение опреративной памяти превышает число 256 то,
  ldi temp,high(RAMEND) 	 ; Устанавливаем старший бит из значения RAMEND прописанного по
  out SPH,temp   		 ; умолчанию в файле tn2313def.inc
  .endif 				 ; Конец условия

; =================================================== Инициализация Оперативной памяти

  ldi  ZL,low(SRAM_START)     ; Устанавливаем адрес начала ОЗУ прописанного по
  ldi  ZH,high(SRAM_START)     ; умолчанию в файле tn2313def.inc
  clr  temp   			 ; Очищаем r16
flush:
  stZ+,temp   			 ; Обнуляем ячейку памяти
  cpi  ZH,high(RAMEND+1)     ; Если не достигли конца старшего байта оперативной памяти
  brneflush   			 ; то продолжаем обнулять

  cpi  ZL,low(RAMEND+1)     ; Если не достигли конца младшего байта оперативной памяти
  brneflush   			 ; то продолжаем обнулять

  clr  ZL				 ; Очищаем индексы младшего
  clr  ZH				 ; и старшего байтов памяти

; =================================================== Инициализация регистров общего назначения

  ldi  ZL, 30   		 ; Адрес самого старшего регистра
  clr  ZH				 ; Обнуляем
  dec  ZL				 ; Уменьшаем адрес на единицу
  stZ, ZH   			 ; Записываем в регистр 0
  brnePC-2   			 ; Зацикливаемся до полного обнуления

; =================================================== Инициализация портов

ser temp   		 ; устанавливает все биты регистра tempв 1
clr temp1  			 ; обнуляет регистр temp1
ldi temp2,0b00000011   	 ; записываем в temp2 число 3
out DDRB,temp  		 ; переводит все биты порта B на вывод
out DDRD,temp1   		 ; переводит все биты порта D на ввод
out PortB,temp1 		 ; отключает подтягивающие резисторы портов B
out PortD,temp2 		 ; отключает подтягивающие резисторы портов D кроме 0 и 1 бита

; =================================================== Рисунок бегущего огонька

ldi temp,0b00000001    	 ; определяет рисунок бегущего огонька

; =================================================== Программа бегущего огонька

Loop:   			 ; блок опроса кнопок
SBIS PinD,0  			 ; опрашивает0 бит регистра ввода
ldi temp4,0b00000001   	 ; и записывает в temp4 значение 1, если 0 бит равен 0 (при нажатии)
SBIS PinD,1  			 ; опрашивает1 бит регистра ввода
ldi temp4,0b00000010   	 ; и записывает в в temp4 значение 2, если 1 бит равен 0 (при нажатии)
SBRC temp4,0 			 ; если 0 бит регистра temp4 равен 1, 
rjmp left  			 ; то переход на метку left
SBRC temp4,1 			 ; если 1 бит регистра temp4 равен 1, 
rjmp right  			 ; то переход на метку right 


left:  			 ; блок перемещения огонька влево
rol temp   		 ; циклический сдвиг влево через флаг C
brcc led   		 ; если перенос очищен, то перейти на метку led
ldi temp1,0b00000001   	 ; если C=1, то записываем 
add temp, temp1 		 ; в 0 бит единицу
rjmp led   		 ; и переходим на метку led

right:   			 ; блок перемещения огонька вправо
ror temp   		 ; циклический сдвиг вправо через флаг C
brcc Led   		 ; если перенос очищен, то перейти на метку led
ldi temp1,0b10000000   	 ; если C=1, то записываем
add temp, temp1 		 ; в 7 бит единицу

led:   			 ; блок вывода огонька в порт
out PortB,temp   		 ; вывод в порт D значения регистра temp
rcall delay  			 ; вызов подпрограммы задержки
rjmp Loop   		 ; Возвращаемся к метке Loop, зацикливаемся

; ==================================================== Подпрограмма задержки

delay:
clr temp1  			 ; обнулить регистр temp1
clr temp2  			 ; обнулить регистр temp2
ldi temp3,2  			 ; записать в регистр temp3 число 2

delay1:
dec temp1  			 ; вычесть из значениярегистра temp1 единицу
brne delay1  			 ; если значение temp1 не равно 0 перейти к метке delay1

dec temp2  			 ; вычесть из значениярегистра temp2 единицу
brne delay1  			 ; если значение temp2 не равно 0 перейти к метке delay1

dec temp3  			 ; вычесть из значениярегистра temp3 единицу
brne delay1  			 ; если значение temp1 не равно 0 перейти к метке delay1
ret  				 ; выйти из подпрограммы


Программа написанная на СИ
#define F_CPU 8000000UL 	 // установка частоты микроконтроллера
#include <avr/io.h> 	 // стандартная библиотека ввода-вывода
#include <util/delay.h>    // библиотека задержки

 unsigned char a;    	 // назначение глобальной переменной 'a'

int main(void)
{ 
 DDRB = 0xFF; 			 // Порт PB на вывод
 DDRD = 0;    		 // Порт PD на вход
 PORTD=(1<<PD0)|(1<<PD1);  // Подтягивающий резистор на PD0 и PD1

 while(1)      			 // Бесконечный цикл
 {  
   if((PIND & (1<<PD0)) == 0) 	// Если бит PD0 порта PD обнулен, то
   a=0;     			 // переменная а обнулится
   
   if(a==0)    			 // Если переменная а обнулена, то
	{
	PORTB=(PORTB<<1); 	// сдвиг влево
	if(PINB==0)    		 // если порт PB обнуляется то
	PORTB=1;    			 // пропсываем в порт PB значение 1   	 
	}
   
   if((PIND & (1<<PD1)) == 0) 	// Если бит PD1 порта PD обнулен, то
   a=1;     			 // переменная а установится
   
   if(a==1)    			 // Если переменная а установлена, то
	{
	PORTB=(PORTB>>1); 	// сдвиг вправо
	if(PINB==0)    		 // если порт PB обнуляется, то
	PORTB=128;    			 // пропсываем в порт PB значение 128
	}

   _delay_ms(300);    		 // устанавливаем задержку на 0,3 секунд
   	 
 }
}

Для запоминания нажатия на кнопку используются дополнительные регистры. В зависимости от их значения и определяется в какую сторону будет "бежать" огонек. В СИ используется глобальная переменная 'а', которая работает с любой функцией в программе. С другой стороны, в СИ не приветствуется повсеместное использование глобальных переменных. Кроме глобальных переменных в СИ есть локальные переменные, которые определяются в конкретной фукции, например основная функция main, или любая другая функция заданная пользовотелем (тело функции ограничивается фигурными скобками { } ). Обязательно определяется вид переменной, например unsigned char - в нашем случае беззнаковое число в пределах от 0 до 255. (восьмибитный регистр).

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

Сообщение отредактировал galrad: 30 Декабрь 2015 - 02:07

0

#35 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 242
  • Регистрация: 26 Февраль 11

Отправлено 22 Май 2012 - 01:09

Вариант с прерываниями:
; ****************************************
; Автор:                         		*
; Дата:                                  *
; Версия:                                *
; Название файла:                        *
; Для AVR:  ATtiny2313           		*
; Тактовая частота:  8мГц                *
; ****************************************

; Выполняемые функции:  реверсивные бегущие огни с управлением от 2-х кнопок по  внешним прерываниям INT1 и INT0

; ==================================================
; .device     		;
; .nolist     		; 
; .include            ; 
; .list           	; 
; ===================================================
.def temp=r16     	; директива .def назначает регистру r16 имя temp
.def temp1=r17        ; директива .def назначает регистру r17 имя temp1
.def temp2=r18        ; директива .def назначает регистру r18 имя temp2
.def temp3=r19        ; директива .def назначает регистру r19 имя temp3
.def temp4=r20        ; директива .def назначает регистру r20 имя temp4
;====================================================
; Начало программы
.cseg             	; директива .cseg определяет начало сегмента кодов
.org 0                ; начало первой строки программы

; =================================================== Векторы прерывания
rjmp init              ; Reset - Переход на начало программы
rjmp INT_0
rjmp INT_1
reti                  ; Timer1_capt1
reti                  ; Timer1_comp1
reti                  ; Timer1_OVF1
reti                  ; Timer0_OVF0
reti                  ; UART_RX
reti                  ; UART_UDRE
reti                  ; UART_TX
reti                  ; ANA_COMP
reti                  ; PCINT
reti                  ; Timer1_compB
reti                  ; Timer0_compA
reti                  ; Timer0_compB
reti                  ; USI_START
reti                  ; USI_OVERFLOW
reti                  ; EE_READY
reti                  ; WDT_OVERFLOW

; =================================================== Инициализация стека
        
init:    ldi temp,low(RAMEND)        ; Устанавливаем младший бит из значения RAMEND прописанного по
        out SPL,temp                ; умолчанию в файле tn2313def.inc
        .if (RAMEND)>=0x0100        ; Если значение опреративной памяти превышает число 256 то,
        ldi temp,high(RAMEND)        ; Устанавливаем старший бит из значения RAMEND прописанного по
        out SPH,temp                ; умолчанию в файле tn2313def.inc
        .endif                        ; Конец условия

; =================================================== Инициализация Оперативной памяти

        ldi 	ZL,low(SRAM_START)    ; Устанавливаем адрес начала ОЗУ прописанного по
        ldi 	ZH,high(SRAM_START)    ; умолчанию в файле tn2313def.inc
        clr 	temp                ; Очищаем r16
flush:          
        st      Z+,temp                ; Обнуляем ячейку памяти
        cpi 	ZH,high(RAMEND+1)    ; Если не достигли конца старшего байта оперативной памяти
        brne    flush                ; то продолжаем обнулять

        cpi 	ZL,low(RAMEND+1)    ; Если не достигли конца младшего байта оперативной памяти
        brne    flush                ; то продолжаем обнулять

        clr 	ZL                    ; Очищаем индексы младшего
        clr 	ZH                    ; и старшего байтов памяти

; =================================================== Инициализация регистров общего назначения

        ldi 	ZL, 30                ; Адрес самого старшего регистра        
        clr 	ZH                    ; Обнуляем
        dec 	ZL                    ; Уменьшаем адрес на единицу
        st      Z, ZH                ; Записываем в регистр 0
        brne    PC-2                ; Зацикливаемся до полного обнуления

; =================================================== Инициализация портов

ser temp              ; устанавливает все биты регистра temp  в 1
clr temp1         	; обнуляет регистр temp1
ldi temp2,0b00001100  ; записываем в temp2 число 0b00001100
out DDRB,temp     	; переводит все биты порта B на вывод
out DDRD,temp1        ; переводит все биты порта D на ввод   	
out PortB,temp1   	; отключает подтягивающие резисторы портов B
out PortD,temp2   	; отключает подтягивающие резисторы портов D кроме 2 и 3 бита

; =================================================== Инициализация прерываний

ldi temp,(1<<INT1)|(1<<INT0) ; запись единиц в биты соответствующие INT1 и INT0, регистра temp (0b11000000)
out GIMSK,temp          ; инициализация внешних входов INT1 и INT0
ldi temp,(1<<ISC11)|(1<<ISC01) ; запись единиц в биты соответствующие ISC11 и ISC01, регистра temp (0b00001010)
out MCUCR,temp          ; инициализация прерываний по спадающему фронту входного сигнала
SEI                      ; разрешение прерываний

; =================================================== Рисунок бегущего огонька

ldi temp,0b00000001   ; определяет рисунок бегущего огонька

; =================================================== Программа бегущего огонька

Loop:                  ; блок опроса кнопок
SBRC temp4,0          ; если 0 бит регистра temp4 равен 1, 
rjmp left              ; то переход на метку left
SBRC temp4,1          ; если 1 бит регистра temp4 равен 1, 
rjmp right              ; то переход на метку right 


left:             	; блок перемещения огонька влево
rol temp              ; циклический сдвиг влево через флаг C
brcc led              ; если перенос очищен, то перейти на метку led
ldi temp1,0b00000001  ; если C=1, то записываем 
add temp, temp1   	; в 0 бит единицу
rjmp led              ; и переходим на метку led

right:                ; блок перемещения огонька вправо
ror temp              ; циклический сдвиг вправо через флаг C
brcc Led              ; если перенос очищен, то перейти на метку led
ldi temp1,0b10000000  ; если C=1, то записываем    
add temp, temp1   	; в 7 бит единицу

led:                  ; блок вывода огонька в порт
out PortB,temp        ; вывод в порт D значения регистра temp
rcall delay       	; вызов подпрограммы задержки
rjmp Loop            ; Возвращаемся к метке Loop, зацикливаемся

; ==================================================== Подпрограмма задержки

delay:    
clr temp1         	; обнулить регистр temp1
clr temp2         	; обнулить регистр temp2
ldi temp3,2       	; записать в регистр temp3 число 2

delay1:    
dec temp1         	; вычесть из значения  регистра temp1 единицу
brne delay1       	; если значение temp1 не равно 0 перейти к метке delay1

dec temp2     		; вычесть из значения  регистра temp2 единицу
 brne delay1       	; если значение temp2 не равно 0 перейти к метке delay1

dec temp3     		; вычесть из значения  регистра temp3 единицу
 brne delay1       	; если значение temp1 не равно 0 перейти к метке delay1
ret           		; выйти из подпрограммы

; ==================================================== Подпрограммы прерываний

INT_0:
ldi temp4,1
reti

INT_1:
ldi temp4,2
reti


Прерывания работают по входам INT0 и INT1 по такой схеме:
Прикрепленное изображение: схема.jpg

Программа на СИ
#define F_CPU 8000000UL                             	// установка частоты микроконтроллера
#include <avr/io.h>                                 	// стандартная библиотека ввода-вывода
#include <util/delay.h>                             	// библиотека задержки
#include <avr/interrupt.h>                          	// библиотека прерываний

unsigned char a;

ISR (INT0_vect)                                             	// определение вектора прерываний INT0
{
	a = 1;
}

ISR (INT1_vect)                                                     	// определение вектора прерываний INT1
{
	a = 0;
}



int main(void)
{
	    	DDRB = 0xFF;                                                	// Порт PB на вывод
	    	DDRD = 0;                                                           	// Порт PD на вход
	    	PORTD=(1<<PD2)|(1<<PD3);                            	// Подтягивающий резистор на PD2 и PD3
	    	GIMSK=(1<<INT0)|(1<<INT1);                          	// Установка внешних прерываний INT0 и INT1
	    	MCUSR=(1<<ISC11)|(1<<ISC01);                	// Установка прерываний по спадающему фронту входного сигнала
	    	asm ("sei");

	
	while (1) 
	{
			if(a == 0)												// Если нулевой бит порта PD обнулен
			{
				_delay_ms(300);										// устанавливаем задержку на 0,3 секунд				
				PORTB=(PORTB<<1);									// сдвиг влево
				if(PINB==0)											// если порт PB обнуляется то
				PORTB=1;											// пропсываем в порт PB значение 1
			}
			// Если нулевой бит порта PD установлен
			else
			{
				_delay_ms(300);										// устанавливаем задержку на 0,3 секунд					
				PORTB=(PORTB>>1);									// сдвиг вправо			
				if(PINB==0)											// если порт PB обнуляется то
				PORTB=128;											// пропсываем в порт PB значение 128
			}

	}
}


Здесь появилась новая библиотека прерываний #include <avr/interrupt.h> . Она стандартная встроенная в программу, остальные функции уже встречались ранее. Программа выглядит более описательной, и в понимании не представляет трудностей.

Сообщение отредактировал galrad: 29 Декабрь 2015 - 19:05

0

#36 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 242
  • Регистрация: 26 Февраль 11

Отправлено 23 Май 2012 - 20:59

В предыдущих программах бегущих огней появились новые строчки и новые команды. Рассмотрим подробнее. Условия уже были написаны, поэтому повторяться не будем.

Первый вариант:
команда SBIS PinD,0 опрашивает нулевой бит регистра ввода. Если опрашиваемый бит имеет логическую единицу, то следующая командная строка игнорируется, если бит равен 0, то происходит переход на следующую командную строку. В нашем случае если нажать на кнопку то в регистр temp4 запишется число один (ldi temp4,0b00000001), если кнопка не нажата, программа переходит на команду SBIS PinD,1 аналогично опрашивая первый бит регистра ввода. Если кнопка будет нажата, то в регистр temp4 запишется число 2 (ldi temp4,0b00000010) заменив предыдущее значение.
Команда SBRC temp4,0 позволят побито опрашивать регистры общего назначения, в нашем случае нулевой бит. Если указанный бит имеет значение нуля, то команда игнорирует следующую командную стоку и переходит на опрос первого бита, если логическую единицу, то переходит на метку left (rjmp left). Аналогично опрашивается первый бит командой SBRC temp4,1 с переходом на метку right.

Второй вариант:
Во втором варианте происходит инициализация прерываний
строка ldi temp,(1<<INT1)|(1<<INT0) соответствует записи в регистр temp числа 0b11000000 . (ldi temp, 0b11000000)
Посмотрите расположение битов INT1и INT0 в регистре
GIMSK, как видите им соответствуют 7 и 6 биты. Так нагляднее и не нужно гадать, где же эти биты раположены. Инициализируем входы INT1и INT0 командой out GIMSK,temp.
Далее идет строка ldi temp,(1<<ISC11)|(1<<ISC01) и out MCUCR,temp что означает инициализацию прерываний по спадающему фронту входного сигнала. И разрешение глобальных прерываний SEI.
В момент нажатия на кнопку INT0 происходит прерывание с переходом на вектор прерывания INT_0 и выполнением подпрограммы где происходит запись в регистр temp4 значения 1 (ldi temp4,0b00000001), если же нажать на кнопку INT1 то в регистр temp4 будет записано число 2 (ldi temp4,0b00000010) и выход из прерывания с выполнением основой программы. А дальше опрос битов регистра temp4, как в предыдущем примере.


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

В микроконтроллерах AVR могут быть от одного до 4-х таймеров, восьмиразрядные или шестнадцатиразрядные.
Упрощенно таймеры обозначаются буквой T, и номером от нуля до трех. Обычно четные Т0 и Т2 являются восьмиразрядными, а нечетные Т1 и Т3 шестнадцатиразрядными. При программировании упрощенный вариант используется только в комментариях, а в программах прописывается полное название таймера – регистр TCNT. Ниже показаны обозначения таймеров:

Таймеры.

T0, T2 – (TCNT0, TCNT2) восьмиразрядные счетчики (четные)
TCNTn - счетный регистр 8 разрядного счетчика
где; n-номер счетчика

T1, T3 – (TCNT1Н и TCNT1L, TCNT3H и TCNT3L) шестнадцатиразрядные счетчики (нечетные)
TCNTny - счетный регистр 16 разрядного счетчика
где; n-номер счетчика
y-старший (H) или младший (L) разряд

Т1 состоит из двух восьмиразрядных регистров TCNT1Н и TCNT1L, а
Т3 из двух регистров TCNT3H и TCNT3L. Буквой H обозначается старший разряд, а L младший.

Так как таймеры являются регистрами, то к ним можно обращаться в любой момент времени, считывать, записывать, обнулять и менять значение.
Приняты определенные правила записи и чтения в таймеры TCNT1Н и TCNT1L.

1.Программы записи и чтения данных таймера должны быть атомарными, т.е. перед чтением или записью мы запрещаем прерывания, а по окончании процесса вновь разрешаем.
2.При записи сначала записывается старший байт H а затем младший L.
3.При чтении сначала считывается младший байт L затем старший H.
Например:
Запись данных в счетный регистр.
	CLI 				; Запрещаем прерывания
	OUT	TCNT1H,R16	; Запись старшего байта
	OUT	TCNT1L,R17	; Запись младшего байта
	SEI 				; Разрешаем прерывания
Чтение данных из счетного регистра
	CLI 				; Запрещаем прерывания
	IN	TCNT1L,R16	; Считывание младшего байта
	IN	TCNT1H,R17	; Считывание старшего байта
	SEI 				; Разрешаем прерывания

Почему такие правила? А все для того, чтобы не исказились данные за то время, которое уйдет на процесс считывания из каждого регистра.
Если использовать прямое чтение 8-битных регистров TCNT1H и TCNT1L, то нельзя быть уверенным, что эти регистры прочитались одновременно. Может произойти следующая ситуация: Счетчик содержал значение $01FF, Вы считали TCNT1H (содержащий значение 01 в какую-то переменную). За это время пришел счетный импульс, и содержимое TCNT1L стало равно $00, а в TCNT1H записалось значение $02. Теперь Вы читаете значение TCNT1L в другую переменную, получаете в этой переменной значение $00 (ведь таймер/счетчик уже произвел счет). 16-битное значение этих переменных получилось $0100, но на момент считывания старшего байта содержимое счетчика было $01FF, и младший байт у Вас должен был прочитаться как FF. Для предотвращения такой ситуации служит временный регистр, содержащийся в блоке таймера/счетчика. Этот регистр прозрачный, т.е. действует автоматически. При считывании значения регистра TCNT1L в переменную, содержимое TCNT1H попадает в этот регистр. Затем при чтении старшего байта в переменную, считывается значение временного регистра. Временный регистр абсолютно прозрачен для пользователя, но для его корректной работы необходимо соблюдать указанную выше последовательность действий. Обращение к регистрам через дополнительный (буферный) регистр называется двойной буферизацией

Таймеры связаны с счетными импульсами, которые могут быть внешними и поступать на специальный вход микросхемы или формироваться собственным генератором. В свою очередь частота собственного генератора может синхронизироваться внешним кварцевым резонатором, а может определяться внутренней RC – схемой. После этого, внешняя частота или частота собственного генератора, проходят предделитель, управляемый регистром CLKPR.Частоту генератора после предделителя (прескалера) CLKPR нередко называют тактовым сигналом (тактовой частотой) процессора (CPU).
Частота, подаваемая на вход таймера обозначается как CLKTn Эта частота соответствует тактовому сигналу процессора.
Один счетный импульс увеличивает значение таймера на единицу, поэтому регистры TCNT являются счетными, и называются таймером/счетчиком (ТС).
Для правильной работы таймера/счетчика по внешнему тактовому сигналу минимальное время между двумя переключениями внешнего тактового сигнала должно быть не менее одного периода тактового сигнала CPU. Синхронизируется внешний тактовый сигнал нарастающим фронтом внутреннего тактового сигнала CPU. (Это нужно помнить при построении частотомеров).
Управляющим регистром для таймера/счетчика TCNT является регистр TCCR.
Маской прерывания для таймера/счетчика TCNT служит регистр TIMSK (регистр управления прерываниями таймера).
Регистром флагов маски прерывания TIMSK - является регистр TIFR. Запомните, что эти 3 регистра (TCCR, TIMSK, TIFR) при работе таймера/счетчика TCNT используются почти всегда.

Прерывания могут вызываться по переполнению регистра TCNT, сравнению значения регистра TCNT со значением специальных регистров сравнения OCR, захвату – по значениям специальных регистров захвата ICR и определяются режимом работы таймера/счетчика. Кроме этого запрос прерывания может происходить по срабатыванию сторожевого таймера (Watchdog Timer) WDT.

Таймеры/счетчики могут работать в разных режимах и соответственно выполнять разные функции.
Режим работы, то есть, поведение таймера/счетчика и выхода сигнала совпадения, определяется как режимом работы генератора сигналов (WGM02:0) – (сокращенная запись регистров WGM02; WGM01; WGM00), так и режимом вывода сигнала совпадения (СОМ0х1:0) - (сокращенная запись регистров СОМ0х1; СОМ0х0).Состояние битов, определяющих режим вывода сигнала совпадения, не влияет на последовательность подсчета, которая определяется только состоянием битов конфигурации генератора сигналов.

Биты СОМ0х1:0 определяют, должен ли выходной сигнал ШИМ быть инвертирован или нет (инвертированный или не инвертированный ШИМ).
(ШИМ) Широтно-импульсная модуляция или Pulse-width modulation (PWM).
Для не-ШИМ-режимов содержимое битов СОМ0х1:0 определяет, должен ли сигнал на выходе быть установлен в единицу, сброшен в ноль либо переключен в противоположное состояние в момент совпадения


Выделяют следующие режимы работы таймеров:
Normal – подсчет и прерывание по переполнению. В этом режиме, при выставленном бите TOIE регистра TIMSK возникает запрос на прерывание. Условием для прерывания будет обнуление регистра таймера TCNT, которое происходит после заполнения всех ячеек (после числа 0xFF) и фиксируется аппаратным поднятием флага переполнения TOV в регистре TIFR.
Флаг TOV0 в этом случае ведет себя как девятый бит, за тем исключением, что он только устанавливается, но не сбрасывается.
Используя прерывание по переполнению таймера, которое автоматически очищает флаг TOV0, можно увеличить коэффициент пересчета программным путем. Режим Normal не имеет других функций и считается наиболее простым.

Capture – (захват) запоминание значения счетчика в специальном регистре захвата ICRx по управляющему сигналу (от специального входа микроконтроллера (ICP) или от выхода встроенного компаратора), где x- соответствует номеру счетчика. Одновременно с захватом происходит запрос на прерывание и переход на векторы прерывания - CAPT. Устанавливается прерывание в этом режиме битом ICIE или TICIE в регистре TIMSK, и фиксируется поднятием флага ICF в регистре TIFR.

CTC – (Clear Timer on Compare) сброс при совпадении – сравнение со значением специальных регистров совпадения OCR, которые используется для того, чтобы управлять коэффициентом пересчета счетчика.
Происходит запрос на прерывание и переход на векторы прерывания - COMP. Регистры совпадения обозначаются как OCRnxy
где; n - номер счетчика,
x –регистр А или B (если регистров совпадения 2, то обозначают первый как как регистр A, а второй как регистр B, каждый из них может быть 16 разрядным)
y - старший (H) или младший (L) разряд регистров совпадения.

В большинстве моделей микроконтроллеров одному восьмиразрядному таймеру соответствуют два восьмиразрядных регистра сравнения OCR, регистр А и регистр В. Например, таймеру TCNT0 соответствуют два регистра сравнения OCR0A и OCR0B.
Если таймер шестнадцатиразрядный, то ему соответствуют четыре восьмиразрядных регистра сравнения, например, таймеру TCNT1 соответствуют OCR1AH и OCR1AL, OCR1BH и OCR1BL.

В режиме СТС возникает запрос прерывания, сброс таймеров (при совпадении с OCR1 и OCR1A) или изменяется состояние выводов микроконтроллера, которые зависят от установленных настоек таймера.


Значения ячеек регистров TIMSK и TIFR в разных моделях микроконтроллеров имеют похожие функции и названия, но могут отличаться расположением, и при переходе с одного контроллера на другой трудностей не вызывают.
Tiny

Прикрепленное изображение: TIMSK_2313.jpg

Бит 0 - OCIE0A: прерывание по совпадению таймера-счетчика T0A
Бит 1 - TOIE0: прерывание по переполнению Т0
Бит 2 - OCIE0B: прерывание по совпадению таймера-счетчика T0B
Бит 3 - ICIE1: Разрешение прерывания по входу захвата (вход ICP)
Бит 4 - Зарезервировано
Бит 5 – OCIE1B: прерывание по совпадению таймера-счетчика T1B
Бит 6 – OCIE1A: прерывание по совпадению таймера-счетчика T1A
Бит 7 – TOIE1: прерывание по переполнению Т1

Прикрепленное изображение: TIFR_2313.jpg

Флаги соответствуют прерываниям в регистре TIMSK. Устанавливаются в "1" при выполнении условий соответствующего прерывания.

Mega8

Прикрепленное изображение: TIMSK_М8.jpg

Прикрепленное изображение: TIFR_M8.jpg

Mega16

Прикрепленное изображение: TIMSK_М16.jpg

Бит 7 - OCIE2: прерывание по совпадению Т2
Бит 6 - TOIE2: прерывание по переполнению Т2
Бит 5 - TICIE1: прерывание по захвату Т1
Бит 4 - OCIE1A: прерывание по совпадению A Т1
Бит 3 - OCIE1B: прерывание по совпадению В Т1
Бит 2 - TOIE1: прерывание по переполнению Т1
Бит 1 - OCIE0: прерывание по совпадению таймера-счетчика T0 (В Mega8 этот бит зарезервирован)
Бит 0 - TOIE0: прерывание по переполнению Т0
Если соответствующий бит установлен в "1" и бит I (7-й бит) регистра состояний SREG установлен в "1", тогда соответствующее прерывание будет срабатывать.

Прикрепленное изображение: TIFR_M16.jpg

Флаги соответствуют прерываниям в регистре TIMSK. Устанавливаются в "1" при выполнении условий соответствующего прерывания.

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

TCCRnx - восьмиразрядные регистры управления таймерами
где; n-номер счетчика
х-буквенное обозначение регистра управления


Например в микроконтроллере ATtiny2313 простому восьмиразрядному таймеру/счетчику T0 будет соответствовать регистры - TCCR0А и TCCR0B
a, шестнадцатиразрядному таймеру/счетчику T1 – восьмиразрядные регистры TCCR1A, TCCR1B, TCCR1C. В других моделях микроконтроллеров управляющих регистров может быть больше, которые обозначаются буквами латинского алфавита – A,B,C,D и т.д.

Для того, чтобы упорядочить типы таймеров/счетчиков по их функциональному назначению, их тоже принято обозначать в виде заглавных букв латинского алфавита:
A – Восьмиразрядный таймер-счетчик без дополнительных функций
B – Восьмиразрядный таймер-счетчик с функциями сравнения/ШИМ
C – Восьмиразрядный таймер-счетчик с функциями сравнения/ШИМ и функцией счета реального времени
D – Шестнадцатиразрядный таймер-счетчик с функциями захвата и сравнения/ШИМ
E – Шестнадцатиразрядный таймер-счетчик с функцией захвата и двумя каналами для выполнения функций сравнения/ШИМ.

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

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

BOTTOM – счетчик достигает значения BOTTOM (начало), когда его содержимое равно 0х0000
MAX - счетчик достигает значения MAX (максимум), когда его содержимое равно 0хFF для восьмибитных таймеров/счетчиков и 0xFFFF для шестнадцатибитных таймеров/счетчиков.
TOP - Счетчик достигает значения ТОР (вершина), когда его содержимое
достигает самого высокого значения в данном режиме работы. В
зависимости от режима значение ТОР может быть равно либо OxFF (МАХ) для восьмибитных таймеров/счетчиков или 0x00FF, 0x01FF, 0x03FF для шестнадцатибитных таймеров/счетчиков либо значению, записанному в регистре OCR0A (режим сброса по совпадению - CTC)

Напомню, что в ATtiny2313 один 8- разрядный таймер/счетчик с отдельным предделителем и один 16-разрядный таймер/счетчик с отдельным предделителем, схемой сравнения, схемой захвата и двумя каналами широтно-импульсной модуляции (ШИМ).
Или по другому - один таймер/счетчик типа A и один таймер/счетчик типа D
с регистрами управления TCCR0А, TCCR0B и TCCR1A, TCCR1B, TCCR1С.

ATtiny2313
T0, таймер/счетчик типа A, регистры управления TCCR0А, TCCR0B

Прикрепленное изображение: TCCR0A_2313.jpg

Прикрепленное изображение: TCCR0B_2313.jpg

T1, таймер/счетчик типа B, регистры управления TCCR1А, TCCR1B, TCCR1C

Прикрепленное изображение: TCCR1A_2313.jpg

Прикрепленное изображение: TCCR1B_2313.jpg

Прикрепленное изображение: TCCR1C_2313.jpg

Рассмотрим назначение битов регистров TCCR:

CS02:CS01:C00 - Разряды, определяющие источник тактового сигнала таймера/счетчика Т0.
CS12:CS11:C10 - Разряды, определяющие источник тактового сигнала таймера/счетчика Т1.

Прикрепленное изображение: выбор источника тактового сигнала.jpg

где n - номер счетчика
В зависимости от установленных бит CS, мы можем получить прямой, инверсный или поделенный на 8,64, 256, 1024 счетный импульс, а так же полностью остановить счет.
Для практического программирования нужно просто запомнить эту таблицу с назначением битов CS.

COM0A1:COM0A0:COM0B1:COM0B0 - Эти разряды определяют поведение вывода OC1A:OC1B при совпадении значения счетного регистра TCNT0 и регистра сравнения OCR0A:OCR0B


COM1A1:COM1A0:COM1B1:COM1B0 - Эти разряды определяют поведение вывода OC1A:OC1B при совпадении значения счетного регистра TCNT1 и регистра сравнения OCR1A:OCR1B


ICNC1 - Разряд управления схемой помех, если бит равен "0" захват будет по первому активному фронту, если "1" захват будет после четвертой одинаковой выборки сигнала захвата.

ICES1 - Разряд выбора активного фронта сигнала, если его значение равно "0", сохранение счетного регистра TCNT1 в регистре захвата OCR1 будет по спадающему фронту сигнала, если "1" по нарастающему.

WGM02: WGM01: WGM00 - Эти разряды определяют режим работы таймера/счетчика Т0

Прикрепленное изображение: выбор режимов таймера.jpg

WGM13:WGM12:WGM11:WGM10 - Эти разряды определяют режим работы таймера/счетчика Т1

Прикрепленное изображение: выбор режимов 16 битного таймера.jpg

FOC1A:FOC1B - Эти разряды служат для принудительного изменения состояния вывода OC1A:OC1B
(Регистр TCCR1C и его биты FOC1A:FOC1B не прописаны в файле 2313def.inc поэтому при необходимости можно программно их назначить
.equ	TCCR1С	= 0x22  ; TCCR1C - Timer/Counter1 Control Register C
.equ	FOC1B	= 6	; изменение состояния вывода OC1B
.equ	FOC1A	= 7	; изменение состояния вывода OC1A

Посмотрите карту регистров ввода-вывода для ATtiny2313. Там есть регистр TCCR1С под адресом $0x22, но работает этот регистр или нет, я не проверял. (По технической документации должен работать.)

Рассмотрим еще два регистра:
GTCCR – главный регистр управления таймерами

Прикрепленное изображение: GTCCR_2313.jpg

В этом регистре всего один бит PSR10 – выполняет сброс предварительных делителей всех таймеров. После сброса предделителей таймеров бит PSR10 немедленно очищается (переводится в 0).


Есть еще один регистр, который управляет делителем тактовой частоты процессора CLKPR. Нередко начинающий программист путает биты регистра TCCR, определяющие источник тактового сигнала таймера/счетчика с битами управления предделителя CLKPR. Дело в том, что регистр CLKPR изменяет тактовую частоту процессора. Например, если в наличии имеется кварцевый резонатор на 64 МГц, а нам нужно 8 МГц то мы можем использовать этот резонатор, подключив через прескалер (предделитель), разделив исходную частоту на 8. Прескаллер (предделитель) регистра TCCR (биты CS), управляет частотой, которая попадает на входы счетчиков/таймеров и ни как не влияет на тактовую частоту процессора.

Регистр CLKPR (Clock Prescale Register)
Изменяя его биты можно менять значение предделителя тактового сигнала
Для управления предделителем тактового сигнала предназначен регистр CLKPR, расположенный по адресу (0х46) в пространстве дополнительных регистров ввода/вывода. Формат этого регистра:

Прикрепленное изображение: CLKPR.jpg

Старший бит (СLKРСЕ) служит для разрешения изменения частоты тактового сигнала, а биты CLKPS3...CLKPS0 задают коэффициент деления предделителя

Выбор коэффициента деления предделителя (прескалера) тактового сигнала

Прикрепленное изображение: предделитель тактового генератора.jpg

Для изменения содержимого битов CLKPS3...0 следует выполнить следующие действия:

1.Записать в бит СРСЕ лог. 1, а в биты CLKPS3...0 — лог. 0.

2.В течение следующих четырех тактов занести требуемое значение в биты CLKPS3...0, при этом бит СРСЕ будет сброшен в 0. В противном случае бит СРСЕ буден сброшен аппаратно по истечении четырех тактов, запрещая дальнейшее изменение битов CLKPS3...0.

Начальное состояние битов CLKPS3...0 определяется конфигурационной ячейкой CKDIV8. Если она не запрограммирована (1), то при запуске микроконтроллера в битах CLKPS3...0 будет находиться значение 0000. Если же ячейка CKDIV8 запрограммирована (0), стартовым значением битов CLKPS3...0 является 0011 (коэффициент деления — 8).

В микроконтроллере ATtiny2313 по умолчанию установлена частота встроенного тактового генератора 8 МГц, с коэффициентом деления прескалера 8, т.е. тактовая частота процессора равна 1 МГц. Биты регистра CLKPR доступны при прошивке кристалла программатором в виде фьюзов. О них мы еще поговорим отдельно.

Теперь попробуем переписать программу бегущих огней так, чтобы задержка формировалась в счетчике/таймере микроконтроллера:
; ****************************************
; Автор:                                 *
; Дата:                                  *
; Версия:                                *
; Название файла:                        *
; Для AVR:  ATtiny2313                   *
; Тактовая частота:  8мГц                *
; ****************************************

; Выполняемые функции:  Реверсивные бегущие огни с задержкой от встроенного таймера 
;						в режиме сравнения.

; ==================================================

      ; .device                             ;
      ; .nolist                             ; 
      ;  .include 	                    ; Присоединение файла описаний
      ;  .list				    ; Включение листинга

; =================================================== Псевдокоманды управления

       .def 	temp = r16		    ; Определение основного рабочего регистра temp
       .def 	temp1 = r17		    ; Определение дополнительного рабочего регистра temp1
       .def 	temp2 = r18		    ; Определение дополнительного рабочего регистра temp2
       .equ	    time = 3024			; Присвоение перменой time значения 3024

; =================================================== Начало программного кода

	    .cseg 			            ; Выбор сегмента программного кода
		.org	0		            ; Установка текущего адреса на ноль

; =================================================== Векторы прерываний
	
        rjmp	init	            ; Переход на начало программы
		reti			            ; Внешнее прерывание 0
		reti			            ; Внешнее прерывание 1
		reti			            ; Прерывание по захвату таймера T1
		rjmp 	pr_tim	            ; Прерывание по совпадению T1
		reti			            ; Прерывание по переполнению T1
		reti			            ; Прерывание по переполнению T0
		reti			            ; Прерывание UART прием завершен
		reti			            ; Прерывание UART регистр данных пуст
		reti		            	; Прерывание UART передача завершена
		reti			            ; Прерывание по компаратору
		reti			            ; Прерывание по изменению на любом контакте
		reti			            ; Таймер/счетчик 1. Совпадение B 
		reti		            	; Таймер/счетчик 0. Совпадение B 
		reti			            ; Таймер/счетчик 0. Совпадение A 
		reti			            ; USI Стартовая готовность
		reti			            ; USI Переполнение
		reti			            ; EEPROM Готовность
		reti			            ; Переполнение сторожевого таймера

; =================================================== Инициализация стека
        
init:	ldi temp,low(RAMEND)		; Устанавливаем младший бит из значения RAMEND прописанного по
		out SPL,temp				; умолчанию в файле tn2313def.inc
		.if (RAMEND)>=0x0100		; Если значение опреративной памяти превышает число 256 то,
		ldi temp,high(RAMEND)		; Устанавливаем старший бит из значения RAMEND прописанного по
		out SPH,temp				; умолчанию в файле tn2313def.inc
		.endif						; Конец условия

; =================================================== Инициализация Оперативной памяти

		ldi     ZL,low(SRAM_START)	; Устанавливаем адрес начала ОЗУ прописанного по
		ldi     ZH,high(SRAM_START)	; умолчанию в файле tn2313def.inc
		clr     temp				; Очищаем r16
flush:          
		st      Z+,temp				; Обнуляем ячейку памяти
		cpi     ZH,high(RAMEND+1)	; Если не достигли конца старшего байта оперативной памяти
		brne    flush				; то продолжаем обнулять
 
		cpi     ZL,low(RAMEND+1)	; Если не достигли конца младшего байта оперативной памяти
		brne    flush				; то продолжаем обнулять
 
		clr     ZL					; Очищаем индексы младшего
		clr     ZH					; и старшего байтов памяти

; =================================================== Инициализация регистров общего назначения

		ldi     ZL, 30				; Адрес самого старшего регистра        
		clr     ZH					; Обнуляем
		dec     ZL					; Уменьшаем адрес на единицу
		st      Z, ZH				; Записываем в регистр 0
		brne    PC-2				; Зацикливаемся до полного обнуления

; =================================================== Инициализация Главного предделителя

		ldi		temp,(1<<CLKPCE)    ; Активируем разрешение записи в 
		out		CLKPR, temp	        ; регистр CLKPR
		ldi		temp, 0		        ; Записываем 0 в регистр temp
		out		CLKPR, temp	        ; Записываем этот ноль в CLKPR

; =================================================== Инициализация портов ВВ
	
        ser temp                    ; устанавливает все биты регистра temp  в 1
        clr temp1                   ; обнуляет регистр temp1
        ldi temp2,0b00000001        ; записываем в temp2 число 1
        out DDRB,temp               ; переводит все биты порта B на вывод
        out DDRD,temp1              ; переводит все биты порта D на ввод      
        out PortB,temp1             ; отключает подтягивающие резисторы портов B
        out PortD,temp2             ; отключает подтягивающие резисторы портов D кроме 0 порта

; =================================================== Инициализация таймера T1

		ldi		temp,(1<<WGM12)|(1<<CS12)|(1<<CS10)	; Выбор режима таймера
		out		TCCR1B, temp
		ldi		temp, high(time)	; Старший полубайт кода совпадения
		out		OCR1AH, temp		; Запись в регистр совпадения старш.
		ldi		temp, low(time)		; Младший полубайт кода совпадения
		out		OCR1AL, temp		; Запись в регистр совпадения младш.

; =================================================== Запись в регистр маски прерываний
		
		ldi 	temp,(1<<OCIE1A)	; Разрешаем прерывание от таймера
		out		TIMSK, temp

; =================================================== Начало основной программы

		ldi 	temp, 0b10000000	; Запись начального значения
		sei						    ; Разрешение прерываний
Loop:	rjmp	Loop			    ; Пустой бесконечный цикл

; =================================================== Подпрограмма обработки прерываний

pr_tim:	sbic PinD,0                 ; опрашивает  0 бит регистра ввода,
        rjmp right					; если 0 то следующую строку пропускает

left:   rol temp                    ; циклический сдвиг влево через флаг C
        brcc led                    ; если перенос очищен, то перейти на метку led
        ldi temp1,0b00000001        ; если C=1, то записываем 
        add temp, temp1             ; в 0 бит единицу
        rjmp led                    ; и переходим на метку led

right:  ror temp                    ; циклический сдвиг вправо через флаг C
        brcc led                    ; если перенос очищен, то перейти на метку led
        ldi temp1,0b10000000        ; если C=1, то записываем    
        add temp, temp1             ; в 7 бит единицу

led:    out PortB,temp              ; вывод в порт D значения регистра temp
		reti

дальше выясним как она работает, разберем работу таймеров/счетчиков в режимах ШИМ.

Сообщение отредактировал galrad: 04 Август 2012 - 11:36

0

#37 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 242
  • Регистрация: 26 Февраль 11

Отправлено 25 Май 2012 - 10:23

А этот вариант программы как обобщение предыдущих статей:
; ****************************************
; Автор:                     			*
; Дата:                                  *
; Версия:                                *
; Название файла:                        *
; Для AVR:  ATtiny2313       			*
; Тактовая частота:  8мГц                *
; ****************************************

; Выполняемые функции:  Реверсивные бегущие огни с задержкой от встроенного таймера 
;						в режиме сравнения, с управлением от 2-х кнопок по  внешним прерываниям INT1 и INT0

; ==================================================

      ; .device                 			;
      ; .nolist                 			; 
   	.include "2313def.inc"       		; Присоединение файла описаний
      ;  .list	    			; Включение листинга

; =================================================== Псевдокоманды управления

   	.def 	temp = r16    		; Определение основного рабочего регистра temp
   	.def 	temp1 = r17    		; Определение дополнительного рабочего регистра temp1
   	.def 	temp2 = r18    		; Определение дополнительного рабочего регистра temp2
   	.def 	temp3 = r19    		; Определение дополнительного рабочего регистра temp4
   	.equ    	time = 3024			; Присвоение перменой time значения 3024 (время задержки)

; =================================================== Начало программного кода

    	.cseg             			; Выбор сегмента программного кода
		.org	0            		; Установка текущего адреса на ноль

; =================================================== Векторы прерываний
   		
    	rjmp	init								; Переход на начало программы
		.org	INT0addr
    	rjmp	INT_0
		.org	INT1addr
    	rjmp	INT_1
		.org	OC1addr
    	rjmp	pr_tim								; Прерывание по совпадению T1


; =================================================== Начало программы
        
init:									

; =================================================== Инициализация стека

		ldi temp,low(RAMEND)		; Устанавливаем младший бит из значения RAMEND прописанного по
		out SPL,temp				; умолчанию в файле tn2313def.inc
		.if (RAMEND)>=0x0100		; Если значение опреративной памяти превышает число 256 то,
		ldi temp,high(RAMEND)		; Устанавливаем старший бит из значения RAMEND прописанного по
		out SPH,temp				; умолчанию в файле tn2313def.inc
		.endif						; Конец условия

; =================================================== Инициализация Оперативной памяти

		ldi 	ZL,low(SRAM_START)	; Устанавливаем адрес начала ОЗУ прописанного по
		ldi 	ZH,high(SRAM_START)	; умолчанию в файле tn2313def.inc
		clr 	temp				; Очищаем r16
flush:          
		st      Z+,temp				; Обнуляем ячейку памяти
		cpi 	ZH,high(RAMEND+1)	; Если не достигли конца старшего байта оперативной памяти
		brne    flush				; то продолжаем обнулять
 
		cpi 	ZL,low(RAMEND+1)	; Если не достигли конца младшего байта оперативной памяти
		brne    flush				; то продолжаем обнулять
 
		clr 	ZL					; Очищаем индексы младшего
		clr 	ZH					; и старшего байтов памяти

; =================================================== Инициализация регистров общего назначения

		ldi 	ZL, 30				; Адрес самого старшего регистра        
		clr 	ZH					; Обнуляем
		dec 	ZL					; Уменьшаем адрес на единицу
		st      Z, ZH				; Записываем в регистр 0
		brne    PC-2				; Зацикливаемся до полного обнуления

; =================================================== Инициализация Главного предделителя

		ldi		temp,(1<<CLKPCE)    ; Активируем разрешение записи в 
		out		CLKPR, temp        	; регистр CLKPR
		ldi		temp, 0        		; Обнуляем регистр temp
		out		CLKPR, temp        	; Деление  тактовой частоты  в регистре CLKPR равно 1

; =================================================== Инициализация портов
	
		ser temp              ; устанавливает все биты регистра temp  в 1
		clr temp1 			; обнуляет регистр temp1 - все биты регистра temp1 в 0 
		ldi temp2,(1<<PD3)|(1<<PD2)  ; запись битов PD3 и PD2
		out DDRB,temp 		; переводит все биты порта B на вывод
		out DDRD,temp1        ; перевод битов PD3 и PD2 на ввод      
		out PortB,temp1   	; отключает подтягивающие резисторы портов B
		out PortD,temp2   	; подключение подтягивающих резисторов на PD3 и PD2

; =================================================== Инициализация таймера T1

		ldi		temp,(1<<WGM12)|(1<<CS12)|(1<<CS10)	; Выбор режима таймера
		out		TCCR1B, temp
		ldi		temp, high(time)	; Старший полубайт кода совпадения
		out		OCR1AH, temp		; Запись в регистр совпадения старш.
		ldi		temp, low(time)		; Младший полубайт кода совпадения
		out		OCR1AL, temp		; Запись в регистр совпадения младш.

; =================================================== Запись в регистр маски прерываний
		
		ldi 	temp,(1<<OCIE1A) 	; Разрешаем прерывание от таймера
		out		TIMSK, temp

; =================================================== Инициализация прерываний

		ldi temp,(1<<INT1)|(1<<INT0) ; запись битов INT1 и INT0
		out GIMSK,temp            ; инициализация внешних входов INT1 и INT0
		ldi temp,(1<<ISC11)|(1<<ISC01) ; запись битов ISC11 и ISC01
		out MCUCR,temp            ; инициализация прерываний по спадающему фронту входного сигнала
		SEI                           			; разрешение прерываний

; =================================================== Рисунок бегущего огонька

		ldi 	temp, 0b10000000	; Запись начального значения

; =================================================== Глобальное разрешение прерываний и зацикливание
		sei			    			; Разрешение прерываний
Loop:	rjmp	Loop    			; Пустой бесконечный цикл

; =================================================== Подпрограмма обработки прерываний таймера

pr_tim:	
		SBRC temp3,0              ; если 0 бит регистра temp4 равен 1, 
		rjmp left             			; то переход на метку left
		SBRC temp3,1              ; если 1 бит регистра temp4 равен 1, 
		rjmp right                        ; то переход на метку right 

left:   rol temp                    ; циклический сдвиг влево через флаг C
        brcc led                    ; если перенос очищен, то перейти на метку led
        ldi temp1,0b00000001        ; если C=1, то записываем 
        add temp, temp1 			; в 0 бит единицу
        rjmp led                    ; и переходим на метку led

right:  ror temp                    ; циклический сдвиг вправо через флаг C
        brcc led                    ; если перенос очищен, то перейти на метку led
        ldi temp1,0b10000000        ; если C=1, то записываем    
        add temp, temp1 			; в 7 бит единицу

led:    out PortB,temp              ; вывод в порт D значения регистра temp
		reti

; =================================================== Подпрограммы прерываний кнопок

INT_0:
		ldi temp3,1					; Запись состояния кнопки PD2
		reti

INT_1:
		ldi temp3,2					; Запись состояния конпки PD3
		reti


Один из возможных вариантов программы на СИ может выглядеть следующим образом:
#define F_CPU 8000000UL										// установка частоты микроконтроллера
#include <avr/io.h>											// стандартная библиотека ввода-вывода
#include <avr/interrupt.h>									// библиотека прерываний

unsigned char  a;											// Определение глобальной переменной
															// unsigned - беззнаковое число; char - число от 0 до 255;
int main(void)
{
	//---------------------------------------- Инициализация портов
	DDRB = 0xFF;											// Порт PB на вывод
	DDRD = 0;												// Порт PD на вход
	PORTD=(1<<PD2)|(1<<PD3);								// Подтягивающий резистор на PD2 и PD3
	//---------------------------------------- Инициализация таймера
	TCCR1B =(1<<WGM12)|(1<<CS12)|(1<<CS10);					// Режим работы таймера (WGM12=1, WGM11=0, WGM10=0 режим CTC сравнивается со значением регистра
															// OCR1A). Регистры CS10, CS11, CS12 определяют кратность деления прескаллера (1024).
	OCR1A=324;												// Регистру OCR1A присвоенно значение 324
	TIMSK=(1<<OCIE1A);										// Бит OCIE1A - регистра TIMSK определяет прерывание по совпадению таймера-счетчика T1A
	//---------------------------------------- Инициализация внешних прерываний
	GIMSK=(1<<INT0)|(1<<INT1);								// Установка внешних прерываний INT0 и INT1
	MCUSR=(1<<ISC11)|(1<<ISC01);							// Установка прерываний по спадающему фронту входного сигнала
	asm ("sei");											// разрешение глобальных прерываний
	//---------------------------------------- Бесконечный цикл
	while(1)
	{
	}
	return 0;      	

}

	//---------------------------------------- Векторы прерываний

ISR (INT0_vect)												// определение вектора прерываний INT0
{
	a = 0;													// обнулить значение переменной а
}

ISR (INT1_vect)												// определение вектора прерываний INT1
{
	a = 1;													// присвоить переменной а значение равное 1
}

ISR (TIMER1_COMPA_vect)										// определение вектора прерываний по совпадению
{
	//---------------------------------------- подпрограмма
	if(a == 0)												// Если нулевой бит порта PD обнулен
	{
		PORTB=(PORTB<<1);									// сдвиг влево
		if(PINB==0)											// если порт PB обнуляется то
		PORTB=1;											// пропсываем в порт PB значение 1
	}
	// Если нулевой бит порта PD установлен
	else
	{
		PORTB=(PORTB>>1);									// сдвиг вправо
		if(PINB==0)											// если порт PB обнуляется то
		PORTB=128;											// пропсываем в порт PB значение 128
	}
	
}
	//---------------------------------------- конец программы


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

Сообщение отредактировал galrad: 28 Декабрь 2015 - 16:43

0

#38 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 242
  • Регистрация: 26 Февраль 11

Отправлено 25 Май 2012 - 16:36

Запомнить сразу все регистры и биты управления нереально, поэтому почти каждый начинающий программист делает для себя шаблоны. Как они будут выглядеть – дело вкуса каждого. К чему я это пишу, а для того, чтобы предыдущими статьями вы пользовались как справочником, не пытаясь заучивать всё и вся. В процессе создания своих программ, они останутся в памяти.
Итак, разберем, как работает программа. Рассмотрим последнюю, так как в ней присутствуют все элементы, которые были в предыдущих.
Основное тело программы построено из строк инициализаций. Как правило, эти строки универсальны, т.е. если будет создаваться другая программа, то эти строки прописываются без изменений или изменения будут незначительными (обратите внимание на инициализацию портов ввода/вывода).
ldi temp2,(1<<PD3)|(1<<PD2)  
out DDRD,temp2        
out PortD,temp2       

В регистре temp2, биты соответствующие выводам PD3 и PD2 прописываются как логические 1, остальные биты равны 0
Прикрепленное изображение: порты.jpg
Команда ldi temp2,(1<<PD3)|(1<<PD2), равнозначна команде
ldi temp2, 0b0001100, с той разницей, что мы не задумываемся какое же там число записано. Мы пишем обозначение портов, а число само пропишется.
Значение 1<<PD3 использует операнд - сдвиг единицы влево указанное количество раз (PD3=3 в файле 2313def.inc), значение 1<<PD2 сдвигает единицу с нулевой на 2 позиции влево. (Кстати, используя этот операнд тоже можно написать программу бегущих огней…)
Операнд | означает побитовое ИЛИ (если оба соответствующих бита операндов равны 0, двоичный разряд результата равен 0; если же хотя бы один бит из пары равен 1, двоичный разряд результата равен 1.)
Мы как бы собираем байт из разных битов.
Нам нужно сравнивать меняющиеся значение таймера/счетчика T1A с выставленными значениями регистра OCR. Для этого нужно инициализировать маску прерывания в регистре TIMSK
Прикрепленное изображение: TIMSK_2313.jpg
ldi     temp,(1<<OCIE1A) 
out             TIMSK, temp

Я думаю, что здесь все очевидно. Идем дальше.
Командой .equ time = 3024, мы присвоили переменной time числовое десятичное значение 3024 (0b10111101000 или 0х0BD0), которое по сути является временем задержки. Именно это значение запишется в регистр сравнения OCR при инициализации таймера Т1.
ldi		temp,(1<<WGM12)|(1<<CS12)|(1<<CS10)	
out		TCCR1B, temp
ldi		temp, high(time)	
out		OCR1AH, temp
ldi		temp, low(time)
 out		OCR1AL, temp

Первую строку, я думаю, уже нет необходимости рассматривать, она тоже очевидна. Число, которое пропишется в регистр управления TCCR1B будет равно 0x0D (0b00001101)
Прикрепленное изображение: TCCR1B_2313.jpg
т.е. режим номер 4: Режим СТС, верхний предел определяется числом, записанным в OCR1A, а по достижении этого числа поднимается флаг прерывания TOV.
Прикрепленное изображение: выбор режимов 16 битного таймера.jpg
С делением частоты на 1024. т.е. время задержки будет составлять 3024*1024/8=3096576/8=387072 (около 0,39 секунд). Почему делим на 8? Мы инициализировали регистр CLKPR (прописали нули),т.е. процессор работает с частотой 8 МГц
Прикрепленное изображение: выбор источника тактового сигнала.jpg
Запись переменных в старший и младший биты регистров OCR, тоже не требует объяснений, выполняется по правилам записи данных в таймеры (да и вообще в 16 битные регистры), которые описывались ранее.
В AVRstudio, результат инициализации портов можно посмотреть в окне IO View:
Прикрепленное изображение: регистр OCR в st5.jpg

Сообщение отредактировал galrad: 25 Май 2012 - 17:41

0

#39 Гость_fog_*

  • Группа: Гости

Отправлено 27 Май 2012 - 23:38

В проекте есть сегмент .eseg, после запуска симуляции при просмотре памяти eeprom этих данных в ней нет, везде ff, как исправить?
После компиляции проекта получаются два файла: hex и eep, есть ли возможность все сделать в одном файле, или как эти файлы (два) подключить к PROTEUSu?

Прикрепленные изображения

  • Прикрепленное изображение: 222.JPG

Сообщение отредактировал fog: 27 Май 2012 - 23:49


#40 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 242
  • Регистрация: 26 Февраль 11

Отправлено 29 Май 2012 - 14:43

Просмотр сообщенияfog (27 Май 2012 - 23:38) писал:

В проекте есть сегмент .eseg, после запуска симуляции при просмотре памяти eeprom этих данных в ней нет, везде ff, как исправить?
После компиляции проекта получаются два файла: hex и eep, есть ли возможность все сделать в одном файле, или как эти файлы (два) подключить к PROTEUSu?


Программа расчитана так, что в этом окне будут появляться подсвеченные красным цветом новые значения в процессе выполнения отладки программы (режим debud). Можно конечно ручками ввести свое значение в ячейку памяти eeprom т.е. посмотреть как будет вести себя программа при конкретных значениях памяти (это так же как ячейки памяти общего назначения - r16-r31).
Прикрепленное изображение: редактирование_1.jpg
Прикрепленное изображение: редактирование.jpg
Поэтому нет необходимости выводить всю таблицу. Посмотреть данные EEPROM можно в файле с расширением .eep в отдельном окне, а поменять значение в самой программе перекомпелировав ее.
Прикрепленное изображение: данные eeprom в программе.jpg
Прикрепленное изображение: файл_EEP.jpg
Прикрепленное изображение: файл_EEP_1.jpg
Формат данных, записанных в файл .eep соответствует формату .hex
Прикрепленное изображение: блокнот.jpg
Если нужно присоединить этот файл в Протеус то сначало его нужно переименовать в .hex, а затем конвертировать в бинарный файл bin. Конверторы можно найти в сети.
Прикрепленное изображение: Proteus.jpg
Прикрепленное изображение: Proteus_1.jpg

Сообщение отредактировал galrad: 29 Май 2012 - 14:50

0

Поделиться темой:


  • 14 Страниц +
  • 1
  • 2
  • 3
  • 4
  • Последняя »
  • Вы не можете создать новую тему
  • Вы не можете ответить в тему

2 человек читают эту тему
0 пользователей, 2 гостей, 0 скрытых пользователей