Uživatel:Vlachja5

Z MAM wiki

Přejít na: navigace, hledání

Semestrální práce, společně s Břetislavem Bakalou, bakalbr1:

Řízení krokového (variantně komutátorového) motoru okenní žaluzie

S důrazem na minimální odběr energie navrhněte obvody pro řízení komutátorového motoru na základě signálu detektoru denního světla, rozsah pohybu je mezi dvěma optickými závorami. Po rozednění se motor bude otáčet jedním směrem až do zastínění jedné optické závory, po setmění se motor bude otáčet opačně až do zastínění druhé optické závory. Zvažte sleep módy procesoru a buzení světelné závory, jen když je potřeba.


Práce rozdělena na dvě části:

1. Jan Vlach: vyhodnocení kdy zatáhnout a kdy roztáhnout

2: Břetislav Bakala: obsluha motorku.

Dve části projektu budou komunikovat prostřednictvím "komunikačního registru", jehož bity budou reprezentovat požadovaný směr pohybu nebo zastavení motorku. Tento registr bude čten obsluhou motorku a ta provede požadovanou operaci.


Blokový diagram vyhodnocení světla/tmy: Soubor:blokovy_diagram.png

Základní funkce kódu

;------------HLAVNÍ SMYČKA---------------------
;----------------------------------------------

hlavni_smycka:
	ldi r26, ADCSRA	;zapnutí převodu
	clr r27
	ldi r16,0b11001000
	st 	X, r16

	sleep
	rjmp hlavni_smycka


;-----------OBSLUHY PŘERUŠENÍ---------------

prevod:
	lds R17,ADCH
	rol R17
	rol R17	; reference bude 5 V, úplná tma je 2,5 V, světlo 0,5 V. na  změna na 6. bitu odpovídá rozhraní 1,25 V
	
	brcs zatahnout
	brcc roztahnout	
	clc
	reti

zavora:
	reti

Připojení optických závor a světlocitlivého prvku k mikrokontroléru: Soubor:schema_vlachja5.png


Celý kód části 1.


;--------------------INICIALIZACE--------------------
;----------------------------------------------------

.INCLUDE "m88def.inc"

.ORG 0x000 
	rjmp main

.ORG 0x004 
	rjmp zavora	;přerušení vyvolané světelnými závorami na pinech PCINT8-14

.ORG 0x015
	rjmp prevod	;přerušené při hotovém převodu z potenciometru

main: 
	ldi r16,high(RAMEND)  ;nastavení zásobníku
	out SPH,r16
	ldi r16,low(RAMEND)
	out SPL,r16

;nastavení pinů napájení a snímání optické závory
;PC4 (vstup horní závora) a PC2 (vstup dolní závora)
;PC5 (napájení horná závora) a PC3 (napájení dolní závora)
	sbi DDRC,5
	sbi DDRC,3
	cbi DDRC,4
	cbi DDRC,2

	cbi PORTC,5 ;napájení světelné závory z důvodu šetření energie budu zapínat jen když to bude třeba
	cbi PORTC,3
	sbi PORTC,4
	sbi PORTC,2

;nastavení pinů na fotorezistor: PC1 napájení děliče, 
;PC0 (ADC0) vstup 2,5 V tma, 0,5V světlo

	sbi DDRC,1
	sbi PORTC,1
 
;-----------NASTAVENÍ PŘERUŠENÍ------------

;nastavim přerušení PCINT1, piny PCINT8-14, vektor 003
	ldi r26, PCICR 		
	clr r27 			
	ldi r16,0b00000010 	
	st 	X, r16 	

;aktivuju piny PC4 (horní závora) a PC2 (dolní závora)
;jako aktivní pro přerušení
	ldi r26, PCMSK1
	clr r27
	ldi r16,0b00010100  
	st 	X, r16
	sei

;-----------NASTAVENÍ ADC----------

;nastavení ADC
	ldi r26, ADMUX
	clr r27
	ldi r16,0b00100000   ;vstup na pinu PC0, referenční hodnota na vstupu AREF; v ADCH 8 nejvyšších bitů
	st 	X, r16

	ldi r26, ADCSRA
	clr r27
	ldi r16,0b10001101	;zapnutí ADC; autotrigger vypnut; povolit přerušení; předdělička 32
	st 	X, r16

	ldi r26, DIDR0
	clr r27
	ldi r16,0b00000001	;vypne na PC0 digitální prenosy
	st 	X, r16


;------------VÝCHOZÍ POZICE PO RESETU----------
;----------------------------------------------

	sbic PINC,4	;pokud na horní bráně není žaluzie, je žaluzie otevřená a nepotřebuju vyjet nahoru
	rcall reset_roztahnout
	


;------------HLAVNÍ SMYČKA---------------------
;----------------------------------------------

hlavni_smycka:
	ldi r26, ADCSRA	;zapnutí převodu
	clr r27
	ldi r16,0b11001000
	st 	X, r16

	sleep
	rjmp hlavni_smycka


;-----------OBSLUHY PŘERUŠENÍ---------------

prevod:
	lds R17,ADCH
	rol R17
	rol R17	; reference bude 5 V, úplná tma je 2,5 V, světlo 0,5 V. na  změna na 6. bitu odpovídá rozhraní 1,25 V
	
	brcs zatahnout
	brcc roztahnout	
	clc
	reti

zavora:
	reti
	

;-----------PODPROGRAMY---------------

reset_roztahnout:
	sbi PORTC,3
	sbi PORTC,5
	ldi R16,0b00000001	;kód pro roztahování
	rcall exekuce		;volám provedení operace s motorem dle R16
	sleep				;čekám na přerušení ze světelných závor
	sbis PINC,4 		;pokud je horní závora zakrytá, byla závora při resetu žaluzie zcela dole a přerušení vyvolala dolní závora. Musím tedy ještě vytahovat, přeskočím tedy následující instrukci
	rjmp konec3			;pokud je horní závora nezakrytá, mám vytaženo a skáču na vypnutí motoru
	sleep				;čekám na přerušení druhé závory
konec3:
	ldi R16,0b00000100	;kód pro zastavení motoru
	rcall exekuce		;volám provedení operace s motorem dle R16	
	cbi PORTC,3
	cbi PORTC,5
	ret


zatahnout:
	sbi PORTC,3
	sbi PORTC,5
	sbic PINC,2 		;pokud v dolní bráně není žaluzie, není tedy zataženo, přeskočim násl. instr a budu zatahovat
	rjmp konec1			;pokud bylo zataženo, nebudu zatahovat a skáču rovnou na konec podprogramu
	ldi R16,0b00000010	;kód pro zatahování
	rcall exekuce		;volám provedení operace s motorem dle R16
	sleep				;čekám na přerušení první závory
	sleep				;protože první přerušení způsobila horní závora, čekám dále na druhé přerušení od horní
	ldi R16,0b00000100	;i druhá závora způsobila přerušení, proto zastavuji posun
	rcall exekuce
konec1:
	cbi PORTC,3
	cbi PORTC,5
	ret

roztahnout:
	sbi PORTC,3
	sbi PORTC,5
	sbis PINC,4 		;pokud v horní bráně je žaluzie, přeskočim násl. instr. a budu vytahovat
	rjmp konec2			;analogicky jako v podprogramu "zatahnout:"
	ldi R16,0b00000001
	rcall exekuce
	sleep
	sleep
	ldi R16,0b00000100
	rcall exekuce
konec2:
	cbi PORTC,3
	cbi PORTC,5
	ret 
	
	

Ovládání motoru, část 2.

Soubor:Mustek.png


; tento blok pridat na zacatek kodu
  ;=====================================================================================================================================
 .equ	rychlost=0x80 ;nastaveni rychlosti otaceni motoru, definuje stridu v PWM regulaci - nutno doladit podle typu motoru a napajeciho napeti motoru, vychozi 50% strida
 .DEF	WORK=R17
 .DEF	MOTORSTAT=R16
  ;=====================================================================================================================================



  ;tento blok pridat k inicializaci
 ;=====================================================================================================================================

 ;inicializace - nastaveni portu PD5 a PD6 jako vystupu a jeho nastaveni na nulu
 SBI	DDRD,5
 SBI	DDRD,6
 CBI	PORTD,5
 CBI	PORTD,6


  ;PWM smer1 OC0A, smer 2 OC0B, no prescaler, fast PWM
 LDI	WORK,0b00000001
 STS	TCCR0B,WORK

 LDI	WORK,0b00000011 ; nastaveni fast PWM, standardni I/O porty
 STS	TCCR0A,WORK

 LDI	WORK,rychlost
 OUT	OCR0A,WORK	;nastaveni stridy pro 1. smer
 OUT	OCR0B,WORK	;nastaveni stridy pro 2. smer
 ;=====================================================================================================================================



 ;podporgram exekuce
 ;=====================================================================================================================================
 exekuce:
		SBRC	MOTORSTAT,2
		RJMP	moveSTOP
		SBRC	MOTORSTAT,0
		RJMP	moveUP
		SBRC	MOTORSTAT,1	;pokud neni ani jedna moznost, tak motor zastavi
		RJMP	moveDOWN

moveSTOP:
		LDI		WORK,0b00000011 ; nastaveni fast PWM, standardni I/O porty
		STS		TCCR0A,WORK
		CBI		PORTD,5
		CBI		PORTD,6	
		RET


moveUP:
		LDI		WORK,0b10000011 ; nastaveni fast PWM, PD6 on, PD5 off
		STS		TCCR0A,WORK
		RET

moveDOWN:
		LDI		WORK,0b00100011 ; nastaveni fast PWM, PD6 off, PD5 on
		STS		TCCR0A,WORK
		RET
;=====================================================================================================================================





Céčko ze cvičení / upravené znaky


#include <avr/interrupt.h>	//definice ISR makra, pokud nedám, tak by neznal přeruščrní
#include <avr/sleep.h>

#define SER		PD5
#define SRCLK	PD6
#define RCLK	PD7
#define SRCLR	PB0

// global constants and variables
// static code table used for 7-segment led display decoding (common anode)
/*unsigned char code [] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x9B, 0x92, 0x93, 0xF8, 0x80, 0x98,
				 		  0x88, 0x80, 0xC6, 0xC0, 0x86, 0x8E };*/
unsigned char code [] = { 0b10000010, 0b10011111, 0b11010000, 0b10010100, 0b10001101, 0b10100100, 0b10100000, 0b10011110, 0b10000000, 0b10001100,
				 		  0b10001000, 0b10100001, 0b11100010, 0b10010001, 0b11100000, 0b11101000 };  //tabulka jaký segmenty mají svítit pro dané číslo (podle pozice v tabulce)

// character to show on display
static volatile unsigned char anumber = 0;  //static - hodnota se nebude menit, volatile: optimalizér ho nebude ignorovat
// Interrupt vector table is created by compiler
// the ISR macro defines interrupt service handlers

// Pin change interrupt 1
// keyboard handler
ISR(PCINT1_vect)		//ISR a jako adresu zada symbolické jméno přerušení, za to. Tim enabluju použití makra, to _vect tam je dycky
{
	unsigned char keys[10];//pole kam lze ukládat kódy tlačátek,10 bajtů
	unsigned char nkeys = 0; //počet stisknutých kláves
	register unsigned char column;	//register znamená že pokud bude volný registr, nebude proměnnou cpát do zásobníku

//    DDRD 	&= 0b00000001;//protože mám na D sériovou komunikaci, nesmim se dotýkat ostatních bitů
	DDRD	&= 0b11110001;//testuju první řádek, ostattní do 3. stavu
	PORTD	&= 0b11110000;		// set keyboard rows at port D to zero, odstranim pull up
	if (!(PINC & 4))  //tohle je první sloupeček, odpovídá klávesa 1
		keys[nkeys++] = 1;//zapíšu jedničku do keys[0] a pak nkeys inkrementuju, ++ udělá inc, jinak to jde přes ldi...
	if (!(PINC & 8))
		keys[nkeys++] = 2;
	if (!(PINC & 16))
		keys[nkeys++] = 3;

	DDRD	&= 0b11110000;
	DDRD	|= (1 << PD1);
	PORTD	&= 0b11110000;		// set keyboard rows at port D to zero
	if (!(PINC & 4))
		keys[nkeys++] = 4;
	if (!(PINC & 8))
		keys[nkeys++] = 5;
	if (!(PINC & 16))
		keys[nkeys++] = 6;

	DDRD	&= 0b11110000;
	DDRD	|= (1 << PD2);
	PORTD	&= 0b11110000;		// set keyboard rows at port D to zero
	if (!(PINC & 4))
		keys[nkeys++] = 7;
	if (!(PINC & 8))
		keys[nkeys++] = 8;
	if (!(PINC & 16))
		keys[nkeys++] = 9;

	DDRD	&= 0b11110000;
	DDRD	|= (1 << PD3);
	PORTD	&= 0b11110000;		// set keyboard rows at port D to zero
	if (!(PINC & 8))
		keys[nkeys++] = 0;

	DDRD 	|= 0b00001111;		// configure rows as outputs
	PORTD 	&= 0b11110000;		// set keyboard rows at port D to zero

	if (nkeys > 0) {
		OCR1A = keys[0] * 100;  //vynásobim zpoždění 100 a pošlu do porovnávacího registru
		TCNT1 = 0;
	}
}

// Timer/Counter1 Compare Match A
// the value shown on the display will be updated here
ISR(TIMER1_COMPA_vect)		//časovač 1 - budu porovnávat hodnotu čítače s komparační úrovní v A
{
	unsigned char tmp = code[anumber];
	volatile char loopcounter = 8;

	do {
		PORTD &= ~(1 << SRCLK);			// assert 0 on serial clock line
		if (tmp & 0x80)					// test LSB
			PORTD |= (1 << SER);		// if 1, assert 1 on serial data line
		else
			PORTD &= ~(1 << SER);		// assert 0 otherwise
		PORTD |= (1 << SRCLK);			// assert 1 on serial clock line - rising edge writes bit into the serial shift register
		tmp <<=  1;					// shift right to proceed next bit
		loopcounter--;
	} while (loopcounter > 0); 
	PORTD &= ~(1 << RCLK);				// vlnovka - bitová negace (všechn bity obrátí) toggle parallel register clock 0-1 to make a rising edge
	PORTD |= (1 << RCLK);
	
	// update value to display for next time
	if (anumber < 15)
	    anumber++;
	else
		anumber = 0;
}

void main(void)		// hlavní vstupní bod programu, tam udělám definici
{
	// keyboard (and serial shift register connection) ports init
	DDRD 	|= 0b11101111;		// configure rows as outputs (PD0 ... PD3), serial shift register pins PD5 .. PD7
	PORTD 	= 0;				// set port D to zero
	DDRC	= 0;				// configure port C as input port
	PORTC	|= 0b00011100;		// activate pul-up resistors on three columns

	DDRB	|= (1 << SRCLR);	// configure PB0 as output - serial shoft register clear
	PORTB	|= (1 << SRCLR);	// toggle PB0 1-0-1 to clear the serial shift register
	PORTB	&= ~(1 << SRCLR);
	PORTB	|= (1 << SRCLR);

	// an interrupt masking must be here
	// keyboard interrupt
	PCICR 	= (1 << PCIE1);		// maskuju jen přerušení PCIE1 (to je název (pořadí) bitu), takže tam d¨ám jedničku rotovanou doleva o to pořadí bitu. Ale je to v podstatě jedno. Pin change interrupt control register, Pin change interrupt enable 1
	PCMSK1 	= 0b00011100;		// allow pin change interrupt on portC bits 2,3,4

	// timer configuration - 16 bitů, CTC mód - porovnává hodnoty komp. OCR1A, Když shoda - přerušení a vynulování
	TCCR1A	= 0;				// OC1A/OC1B disconnected, CTC bits WGM11, WGM10
	TCCR1B 	= (1 << CS12) | (1 << CS10) | (1 << WGM12); //| je log součet
								// CTC mode, prescaler division factor 1024
	OCR1A 	=  976;				// timer compare value, odpovídá zhruba 1 sec; má dvě části OCR1AH, OCR1AL; nejdřív zapsat H a pak L, v C to je jedno

	TIMSK1 = (1 << OCIE1A);		// enable timer interrupt

	set_sleep_mode(SLEEP_MODE_IDLE);
	sei();						// set Global Interrupt Enable!!!

	while (1) {
		sleep_cpu();
	}
}


Céčko ze cvičení


#include <avr/interrupt.h>	//definice ISR makra, pokud nedám, tak by neznal přeruščrní
#include <avr/sleep.h>

#define SER		PD5
#define SRCLK	PD6
#define RCLK	PD7
#define SRCLR	PB0

// global constants and variables
// static code table used for 7-segment led display decoding (common anode)
/*unsigned char code [] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x9B, 0x92, 0x93, 0xF8, 0x80, 0x98,
				 		  0x88, 0x80, 0xC6, 0xC0, 0x86, 0x8E };*/
unsigned char code [] = { 0x3F, 0x06, 0x5B, 0x4F, 0x64, 0x6D, 0xFC, 0x07, 0x7F, 0x67,
				 		  0xF7, 0x7F, 0x39, 0x3F, 0x79, 0x71 };  //tabulka jaký segmenty mají svítit pro dané číslo (podle pozice v tabulce)

// character to show on display
static volatile unsigned char anumber = 0;  //static - hodnota se nebude menit, volatile: optimalizér ho nebude ignorovat
// Interrupt vector table is created by compiler
// the ISR macro defines interrupt service handlers

// Pin change interrupt 1
// keyboard handler
ISR(PCINT1_vect)		//ISR a jako adresu zada symbolické jméno přerušení, za to. Tim enabluju použití makra, to _vect tam je dycky
{
	unsigned char keys[10];//pole kam lze ukládat kódy tlačátek,10 bajtů
	unsigned char nkeys = 0; //počet stisknutých kláves
	register unsigned char column;	//register znamená že pokud bude volný registr, nebude proměnnou cpát do zásobníku

//    DDRD 	&= 0b00000001;//protože mám na D sériovou komunikaci, nesmim se dotýkat ostatních bitů
	DDRD	&= 0b11110001;//testuju první řádek, ostattní do 3. stavu
	PORTD	&= 0b11110000;		// set keyboard rows at port D to zero, odstranim pull up
	if (!(PINC & 4))  //tohle je první sloupeček, odpovídá klávesa 1
		keys[nkeys++] = 1;//zapíšu jedničku do keys[0] a pak nkeys inkrementuju, ++ udělá inc, jinak to jde přes ldi...
	if (!(PINC & 8))
		keys[nkeys++] = 2;
	if (!(PINC & 16))
		keys[nkeys++] = 3;

	DDRD	&= 0b11110000;
	DDRD	|= (1 << PD1);
	PORTD	&= 0b11110000;		// set keyboard rows at port D to zero
	if (!(PINC & 4))
		keys[nkeys++] = 4;
	if (!(PINC & 8))
		keys[nkeys++] = 5;
	if (!(PINC & 16))
		keys[nkeys++] = 6;

	DDRD	&= 0b11110000;
	DDRD	|= (1 << PD2);
	PORTD	&= 0b11110000;		// set keyboard rows at port D to zero
	if (!(PINC & 4))
		keys[nkeys++] = 7;
	if (!(PINC & 8))
		keys[nkeys++] = 8;
	if (!(PINC & 16))
		keys[nkeys++] = 9;

	DDRD	&= 0b11110000;
	DDRD	|= (1 << PD3);
	PORTD	&= 0b11110000;		// set keyboard rows at port D to zero
	if (!(PINC & 8))
		keys[nkeys++] = 0;

	DDRD 	|= 0b00001111;		// configure rows as outputs
	PORTD 	&= 0b11110000;		// set keyboard rows at port D to zero

	if (nkeys > 0) {
		OCR1A = keys[0] * 100;  //vynásobim zpoždění 100 a pošlu do porovnávacího registru
		TCNT1 = 0;
	}
}

// Timer/Counter1 Compare Match A
// the value shown on the display will be updated here
ISR(TIMER1_COMPA_vect)		//časovač 1 - budu porovnávat hodnotu čítače s komparační úrovní v A
{
	unsigned char tmp = code[anumber];
	volatile char loopcounter = 8;

	do {
		PORTD &= ~(1 << SRCLK);			// assert 0 on serial clock line
		if (tmp & 0x80)					// test LSB
			PORTD |= (1 << SER);		// if 1, assert 1 on serial data line
		else
			PORTD &= ~(1 << SER);		// assert 0 otherwise
		PORTD |= (1 << SRCLK);			// assert 1 on serial clock line - rising edge writes bit into the serial shift register
		tmp <<=  1;					// shift right to proceed next bit
		loopcounter--;
	} while (loopcounter > 0); 
	PORTD &= ~(1 << RCLK);				// vlnovka - bitová negace (všechn bity obrátí) toggle parallel register clock 0-1 to make a rising edge
	PORTD |= (1 << RCLK);
	
	// update value to display for next time
	if (anumber < 15)
	    anumber++;
	else
		anumber = 0;
}

void main(void)		// hlavní vstupní bod programu, tam udělám definici
{
	// keyboard (and serial shift register connection) ports init
	DDRD 	|= 0b11101111;		// configure rows as outputs (PD0 ... PD3), serial shift register pins PD5 .. PD7
	PORTD 	= 0;				// set port D to zero
	DDRC	= 0;				// configure port C as input port
	PORTC	|= 0b00011100;		// activate pul-up resistors on three columns

	DDRB	|= (1 << SRCLR);	// configure PB0 as output - serial shoft register clear
	PORTB	|= (1 << SRCLR);	// toggle PB0 1-0-1 to clear the serial shift register
	PORTB	&= ~(1 << SRCLR);
	PORTB	|= (1 << SRCLR);

	// an interrupt masking must be here
	// keyboard interrupt
	PCICR 	= (1 << PCIE1);		// maskuju jen přerušení PCIE1 (to je název (pořadí) bitu), takže tam d¨ám jedničku rotovanou doleva o to pořadí bitu. Ale je to v podstatě jedno. Pin change interrupt control register, Pin change interrupt enable 1
	PCMSK1 	= 0b00011100;		// allow pin change interrupt on portC bits 2,3,4

	// timer configuration - 16 bitů, CTC mód - porovnává hodnoty komp. OCR1A, Když shoda - přerušení a vynulování
	TCCR1A	= 0;				// OC1A/OC1B disconnected, CTC bits WGM11, WGM10
	TCCR1B 	= (1 << CS12) | (1 << CS10) | (1 << WGM12); //| je log součet
								// CTC mode, prescaler division factor 1024
	OCR1A 	=  976;				// timer compare value, odpovídá zhruba 1 sec; má dvě části OCR1AH, OCR1AL; nejdřív zapsat H a pak L, v C to je jedno

	TIMSK1 = (1 << OCIE1A);		// enable timer interrupt

	set_sleep_mode(SLEEP_MODE_IDLE);
	sei();						// set Global Interrupt Enable!!!

	while (1) {
		sleep_cpu();
	}
}



Domácí úkol 1:


;--------------------INICIALIZACE--------------------
;----------------------------------------------------

.INCLUDE "m88def.inc"

.ORG 0x000 
	rjmp main ; 

.ORG 0x003 
	rjmp dvere ; přerušení vyvolané otevřením nebo zavřením dveří

.ORG 0x015
	rjmp prevod	;přerušené při hotovém převodu z potenciometru


main: 
	ldi r16,high(RAMEND)  ;nastavení zásobníku
	out SPH,r16
	ldi r16,low(RAMEND)
	out SPL,r16

	ldi r16,0b00000001     ;aktivace sleep modu
	out SMCR,r16


; na pinu B1 čtení, na pinu B0 buzení nulou
	cbi DDRB,1
	sbi PORTB,1
	sbi DDRB,0
	cbi PORTB,0

;nastavim přerušení PCINT0, piny PCINT0-7, vektor 003
	ldi r26, PCICR 		
	clr r27 			
	ldi r16,0b00000001 	
	st 	X, r16 			

;aktivuju pin PB1 jako aktivní pro přerušení klávasnicí
	ldi r26, PCMSK0
	clr r27
	ldi r16,0b00000010  
	st 	X, r16
	sei 


; nastavení dvou nezávislých PWM na čítači 0
	
	ldi r16,0b10100011	; Čítač 2 nezáv. Fast PWM non inverting bez předděličky  
	out	TCCR0A, r16
	
	ldi r16,0b00000001	;čítat bude až nahoru
	out TCCR0B, r16

	ldi r16,0b00000000	;přednastavim si nulové střední hodnoty obou pwm: A řídí světla, B přístrojovku
	out	OCR0A, r16
	
	ldi r16,0b00000000	;přednastavim si nulové střední hodnoty obou pwm: A řídí světla, B přístrojovku
	out	OCR0B, r16	

	sbi DDRD, 6	;	OC0A je na 6. pinu podtu D
	sbi DDRD, 5	;   OC0B na 5. pinu na portu D, musím tedy nastavit jako výstup


;nastavení ADC
	ldi r26, ADMUX
	clr r27
	ldi r16,0b00100000   ;vstup na pinu PC0, referenční hodnota na vstupu AREF; v ADCH 8 nejvyšších bitů
	st 	X, r16

	ldi r26, ADCSRA
	clr r27
	ldi r16,0b10001000	;zapnutí ADC; autotrigger vypnut; povolit přerušení; dělič 32 101 na konci
	st 	X, r16

	ldi r26, DIDR0
	clr r27
	ldi r16,0b00000001	;vypne na PC0 digitální prenosy
	st 	X, r16

	ldi r26, ADCSRA
	clr r27
	ldi r16,0b11001000	;zapnu převod
	st 	X, r16	


;----------- ...TADY JÁ BUDU SKORO FURT... ---------------


spat:
	sleep 
	nop 
	rjmp spat





;----------------OBSLUHA PŘERUŠENÍ-----------

; přerušení při dokončení převodu z potenciometru
prevod:
	lds r20,ADCL	;čtení převedené hodnoty
	lds r21,ADCH

	out OCR0B, R21	;do komparačního registru uložím horních 8 bitů z přenosu 

	ldi r26, ADCSRA
	clr r27
	ldi r16,0b11001000	;zapnu další převod
	st 	X, r16

	reti



; přerušení při změně dvěřního spínače
dvere:
	sbic	PINB,1	; pokud je spínač sepnut (pin B1 stažen do nuly) dvěře se otevřely, tak přeskočim na rozsvitit. Jinak na zhasnout.
	rjmp	zhasnout
	rjmp	rozsvitit
zpet:
	reti

rozsvitit:
	ldi 	R17, 0b10100011	; Zapnu PWM A - byla vypnuta při posledním stmívání
	out 	TCCR0A, R17
	ldi 	R17, 0b11111111	; Hned rozsvítim na plný jas - komparační registru 0xFF
	out 	OCR0A, R17   

	rjmp 	zpet	; skok na zpět: a hned konec přerušení

zhasnout:
	;rcall 	wait5s	; čeká ještě 5 sekund
	ldi 	R17, 0b11111111	  ;budu postupně dekrementovat s plného osvětlení po 12. Když proběhne 20x, z 255 zbyde 15
	ldi		R18, 0b00010100   ;hodnota 20 pro dekrementaci -> bude 20 kroků stmívání (plus jeden poslední)
stmivat:
	subi	R17, 0x0C		;odečtu 12
	out 	OCR0A, R17  	;zapíšu stemělejší hodnotu do komp. registru -> do pwm
	;rcall 	wait100ms		;počkám 100 ms (celé stmívání bude trvat cca  100 ms * 20 = 2 s
	dec 	R18
	brne	stmivat
	
	ldi R17, 0b00000000	;pro jistotu vynuluji pwmku
	out OCR0A, R17

	ldi R17, 0b00100011	; deaktivace PWMky A - neskáčou tam impulsy při startu čítání
	out TCCR0A, R17
	cbi PORTD, 5
	
	rjmp zpet


wait5s:
	LDI		R16, 4			; LDI - Load Immediate
wait5s_1:	
	INC		R1
	BRNE	wait5s_1
	INC		R2
	BRNE	wait5s_1
	DEC		R16
	BRNE	wait5s_1
	RET

wait100ms:
	LDI		R16, 4			; LDI - Load Immediate
wait100ms_1:	
	INC		R1
	BRNE	wait100ms_1
	INC		R2
	BRNE	wait100ms_1
	DEC		R16
	BRNE	wait100ms_1
	RET



;-----------INICIALIZACE----------

.INCLUDE "m88def.inc"

.EQU RES = 0
.EQU OUT_ENABLE = 1
.EQU INPUT = 2
.EQU SHIFT_CLK = 3
.EQU STORE_CLK = 4

.ORG 0x000 
	rjmp main ; 

.ORG 0x003 
	rjmp stisk ; přerušení vyvolané klávesnicí


main: 
	ldi r16,high(RAMEND)  ;nastavení zásobníku??
	out SPH,r16
	ldi r16,low(RAMEND)
	out SPL,r16

;aktivace sleep modu
	ldi r16,0b00000001
	out SMCR,r16
	
	clr r16  ;vynulvání používaných registrů - pro jistotu
	ldi r17,0b00001000 ;osmicka pro dekrementaci při sériovém plnění posuvného reg.
	clr r18  ;pro porovnávání předchozího registru



;---------NASTAVENÍ PŘERUŠENÍ--------------


;nastavim přerušení PCINT0, piny PCINT0-7, vektor 003
	ldi r26, PCICR 		
	clr r27 			
	ldi r16,0b00000001 	
	st X, r16 			

;aktivuju pin PB1 jako aktivní pro přerušení klávasnicí
	ldi r26, PCMSK0
	clr r27
	ldi r16,0b00000010  
	st X, r16
	sei 

; na pinu B1 čtení, na pinu B0 buzení nulou
	cbi DDRB,1
	sbi PORTB,1
	sbi DDRB,0
	cbi PORTB,0



;---------NASTAVENÍ KOMUNIKACE S SHIFT REGISTREM---------------


; nastavení bitů komunikujících s registrem
	sbi DDRC,0
	sbi DDRC,1
	sbi DDRC,2
	sbi DDRC,3
	sbi DDRC,4

	CBI PORTC,RES
	SBI PORTC,RES
	CBI PORTC,OUT_ENABLE
	CBI PORTC,INPUT
	CBI PORTC,SHIFT_CLK
	CBI PORTC,STORE_CLK



;---------STAV DISPLEJE PO RESETU----------------

	ldi r16,0b11000000 ;nula na sedmisegmentovce (spol. anoda = inverzní)
	rcall zapis_znak

;----------- ...TADY JÁ BUDU SKORO FURT... ---------------

spat:
	sleep 
	nop 
	rjmp spat


;-------------PROCEDURY---------------

;prohození nuly a jedničky na 7segmentovce v registru r16
urci_znak:
	ldi r19,0b11000000
	cpse r16,r19
	rjmp zmen_znak1
	rjmp zmen_znak2
zmen_znak2:
	ldi r16,0b11111001
	ret
zmen_znak1:
	ldi r16,0b11000000
	ret

	
; zapsání znaku 7segmentovky do posuvného registru a zobrazení
zapis_znak:
	cbi PORTC,OUT_ENABLE
	ror r16
	brcs jedna ;podprogram pro zápis 1 do shift
	brcc nula  ;podprogram pro zápis 0 do shift
zpet:
	dec r17		;Dekrementuje registr kde je 9
	cpse r17,r18  ;až proběhne 8krát, bude R17 = 0 (=r18) a přestane se opakovat zápis
	rjmp zapis_znak
	sbi PORTC,STORE_CLK
	cbi PORTC,STORE_CLK	
	sbi PORTC,OUT_ENABLE
	ror r16
	ldi r17,0b00001000
	ret
jedna:
	SBI PORTC,INPUT
	SBI PORTC,SHIFT_CLK
	CBI PORTC,SHIFT_CLK
	rjmp zpet
nula:
	CBI PORTC,INPUT
	SBI PORTC,SHIFT_CLK
	CBI PORTC,SHIFT_CLK
	rjmp zpet


;----------------OBSLUHA PŘERUŠENÍ-----------

stisk:
	rcall urci_znak
	rcall zapis_znak
	reti


Příprava na cvičení s 7segmentovkou


;-----------INICIALIZACE----------

.INCLUDE "m88def.inc"

.EQU RES = 0
.EQU OUT_ENABLE = 1
.EQU INPUT = 2
.EQU SHIFT_CLK = 3
.EQU STORE_CLK = 4

.ORG 0x000 
	rjmp main ; 

.ORG 0x003 
	rjmp stisk ; přerušení vyvolané klávesnicí


main: 
	ldi r16,high(RAMEND)  ;nastavení zásobníku??
	out SPH,r16
	ldi r16,low(RAMEND)
	out SPL,r16

;aktivace sleep modu
	ldi r16,0b00000001
	out SMCR,r16
	
	clr r16  ;vynulvání používaných registrů - pro jistotu
	ldi r17,0b00001000 ;osmicka pro dekrementaci při sériovém plnění posuvného reg.
	clr r18  ;pro porovnávání předchozího registru



;---------NASTAVENÍ PŘERUŠENÍ--------------


;nastavim přerušení PCINT0, piny PCINT0-7, vektor 003
	ldi r26, PCICR 		
	clr r27 			
	ldi r16,0b00000001 	
	st X, r16 			

;aktivuju pin PB1 jako aktivní pro přerušení klávasnicí
	ldi r26, PCMSK0
	clr r27
	ldi r16,0b00000010  
	st X, r16
	sei 

; na pinu B1 čtení, na pinu B2 buzení nulou
	cbi DDRB,1
	sbi PORTB,1
	sbi DDRB,2
	cbi PORTB,2



;---------NASTAVENÍ KOMUNIKACE S SHIFT REGISTREM---------------


; nastavení bitů komunikujících s registrem
	sbi DDRC,0
	sbi DDRC,1
	sbi DDRC,2
	sbi DDRC,3
	sbi DDRC,4

	CBI PORTC,RES
	SBI PORTC,RES
	CBI PORTC,OUT_ENABLE
	CBI PORTC,INPUT
	CBI PORTC,SHIFT_CLK
	CBI PORTC,STORE_CLK



;---------STAV DISPLEJE PO RESETU----------------

	ldi r16,0b11000000 ;nula na sedmisegmentovce (spol. anoda = inverzní)
	rcall zapis_znak

;----------- ...TADY JÁ BUDU SKORO FURT... ---------------

spat:
	sleep 
	nop 
	rjmp spat


;-------------PROCEDURY---------------

;prohození nuly a jedničky na 7segmentovce v registru r16
urci_znak:
	ldi r19,0b11000000
	cpse r16,r19
	rjmp zmen_znak1
	rjmp zmen_znak2
zmen_znak2:
	ldi r16,0b11111001
	ret
zmen_znak1:
	ldi r16,0b11000000
	ret

	
; zapsání znaku 7segmentovky do posuvného registru a zobrazení
zapis_znak:
	cbi PORTC,OUT_ENABLE
	ror r16
	brcs jedna ;podprogram pro zápis 1 do shift
	brcc nula  ;podprogram pro zápis 0 do shift
zpet:
	dec r17		;Dekrementuje registr kde je 9
	cpse r17,r18  ;až proběhne 8krát, bude R17 = 0 (=r18) a přestane se opakovat zápis
	rjmp zapis_znak
	sbi PORTC,STORE_CLK
	cbi PORTC,STORE_CLK	
	sbi PORTC,OUT_ENABLE
	ror r16
	ldi r17,0b00001000
	ret
jedna:
	SBI PORTC,INPUT
	SBI PORTC,SHIFT_CLK
	CBI PORTC,SHIFT_CLK
	rjmp zpet
nula:
	CBI PORTC,INPUT
	SBI PORTC,SHIFT_CLK
	CBI PORTC,SHIFT_CLK
	rjmp zpet


;----------------OBSLUHA PŘERUŠENÍ-----------

stisk:
	rcall urci_znak
	rcall zapis_znak
	reti

Finální vezre ovládání ventilátoru

; Soubor ze cvičení k regilaci otáček s detailnějšími komentáři a upraven
; s klávesnicí ve třetím stavu pro eliminaci problému se stisknutím více tlačítek najednou
; odstranění problému s nenulovou střední hodnotou napětí při stisku nuly
; řešení nenulové stř. hod. převzato od kolegy Matyáše Del Campa (delcamat)

.INCLUDE "m88def.inc"

.EQU PWM_PULSES = 36	; value for 27.8 kHz PWM
;.EQU PWM_PULSES = 45	; value for 22.2 kHz PWM


.ORG 0x000 
	rjmp Main ; Při resetu (odpovídá vekt. 0000) skoč na Main

.ORG 0x004 
	rjmp isr1 ; Při změně pinu (vektor 0008 - u každého AVR jinak!, definice konkrétního pinu níže) skož na isr1

Main: 

	ldi r16,high(RAMEND); Main program start
	out SPH,r16 ; Set Stack Pointer to top of RAM
	ldi r16,low(RAMEND)
	out SPL,r16


; Hardware initialization

; Keyboard init:
	cbi DDRC, 2 ; Sloupce klávesnice jako vstupy (jen tři - písmena netřeba)
	cbi DDRC, 3
	cbi DDRC, 4

	sbi PORTC, 2 ; nastavení měkkých jedniček na vstupy sloupců klávesnice - pull up odpory
	sbi PORTC, 3
	sbi PORTC, 4

	sbi DDRD, 0 
	sbi DDRD, 1  
	sbi DDRD, 2
	sbi DDRD, 3

	cbi PORTD, 0	
	cbi PORTD, 1
	cbi PORTD, 2
	cbi PORTD, 3


; Timer controlled PWM init:
;
; There are registers TCCR0A and TCCR0B for config, DDRD for output bit
; enabling, OCR0A for counter lenght and OCR0A for pulse "high" lenght.
; Register TIMSK0 controlls interrupts - not used, 0x00 by default 

PWM_INIT:
	ldi R17, 0b00100011	; Nastavení 1. řídicího registru čítače, poslední dva bity nastavují mód čítače (v tomto případě Fast PWM kdy vrchní hodnota čítání bude v registru OCR0A - pozor! O módu rozhoduje také bit 3 v registru TCCR0B!), první čtyři bity nastavují, jak se čítač chová při dosažení porovnávací úrovně (v tomto případě nastaví na pinu OC0B jedničku když má čítač menší hodnotu než OCR0B a naopak)   
	out TCCR0A, R17			; 
	ldi R17, 0b00001001	; nastavení módu viz přechozí komentář, čítání bez předděličky
	out TCCR0B, R17
	ldi R17, PWM_PULSES	; Čítač přeteče při dosažení hodnoty v prom. PWM_PULSES, tzn PWM na frekvenci  frekv. oscil/PWM_PULSES. Změnou OCR0B pak budu měnit střídu PWM
	out OCR0A, R17   		

	sbi DDRD, 5	; Čítač sdílí svůj výstup OC0B s 5. pinem na portu D, musím tedy nastavit jako výstup


; Keyboard interrupt setup
;
; Activation of pin change interrupt - PROBLEM!!! PCICR and PCIMSK are 
; extended I/O registers (0x68 and 0x6C), and must be handled as 
; a memory location


;PCICR je registr nastavující na posledních třech bitech jeden jaký ze tří možných vektorů přerušení pro změnu na pinu budou aktivní
	ldi r26, PCICR ; R26 a 27 tvoří 16bit registr X. Do spodních 8 bitů dám adresu PCICR
	clr r27 ; Hořejšek registru X vymažu
	ldi r16,0b00000010 ; do R 16 si připravim požadované nastavení - v tomto případě PCINT1 - vektor 0008 viz začátek programu
	st X, r16 ; Uložim do X nastavení přerušení - tím nastavim registr PCICR

;PCMSK1 mi určuje piny, které budou způsobovat přerušení PCINT1, stejným způsobem nastavím jako předchozí
	ldi r26, PCMSK1
	clr r27
	ldi r16,0b00011100 ; Přerušení budou způsobovat piny PCINT10, PCINT11 a PCINT12, které mají stejné vývody jako piny 2,3,4 na portu C - to jsou naše slouce klávesnice s pullup odpory
	st X, r16

	sei ; zapnutí přerušení: mělo by být ekvivalentní sbi SREG,7


; Set sleep mode of the CPU
; SMCR: 0,0,0,0,SM2,SM1,SM0,SE   SM2..0: Sleep Mode Select Bits, SE: Sleep enabled)

	ldi r16,0b00000001 ; Aktivace Sleep módu - při provedení instukce sleep skutečně proc usne
	out SMCR,r16 ; Zápis do příslušného registru


; Procesor bude spát dokud nezmáčknu klávesu a nezpůsobim přerušení

loop:
	sleep 
	nop 
	rjmp loop


; PCINT0 Service Routine:
;
; To do: *** rpm reading, regulation, etc.

isr1:
	rcall KEYPRESS
        ldi R17, 0b00100011	;aktivuje nastavení stejného modu čítače jako při inicializaci
	out TCCR0A, R17	
	cpi R16,0	;když je zmáčknuta nula, tak se skočí na off:
	breq off
        mov R17, R16
	lsl R17	; posun vlevo - v bináru násobení dvěmi
	lsl R17	; ještě jednou - takže dohromady 4, maximální hodnota při stisku 9 je tedy 36
	out OCR0B, R17    ; uložim číslo 0 ... 36 v (10 kroků, tlačítek) do porovnávacího registru k nastavení střídy
	reti ; návrat z přerušení na poslední adresu před přerušením +1 (při reti se narozdíl od ret neukládá stavový registr, je nutno to udělat růčo)

off:
	ldi R17, 0b00000011	; deaktivace PWMky - neskáčou tam impulsy při startu čítání
	out TCCR0A, R17
	cbi PORTD, 5
	reti

; Keyboard decoding:

KEYPRESS:
KEY0:		;kontrola stistku nuly - po úpravě 
	cbi		DDRD,0
	cbi 	PORTD, 0
	cbi		DDRD,1
	cbi 	PORTD, 1
	cbi		DDRD,2
	cbi 	PORTD, 2	; řádky které nekontroluji tak nastavím do třetího stavu, tj jako vstupy bez pullup
	
	sbi		DDRD,3		; řádek odpovádající kontrolované klávese jako výstup do nuly
	cbi 	PORTD, 3	
	
	sbic	PINC, 3		; v případě že je kontrolovaná klávesa, a tedy je na PINC sražená měkká 1 na 0, přeskočim následující instrukci
	rjmp	KEY1		; skok na test další klávesy
	ldi		R16, 0		; do uživ. registru šoupnu zmáčknutou hodnotu (v tomto případě nulu) kterou pak vhodně navhájuju, vložim do komparační
	rjmp	KEYRET
KEY1:
	sbi		DDRD,0		; nulu na první řádek, testuju jedničku
	cbi 	PORTD, 0	
	
	cbi		DDRD,1		; třetí stav na ost. řádcích
	cbi 	PORTD, 1
	cbi		DDRD,2	
	cbi 	PORTD, 2
	cbi		DDRD,3
	cbi 	PORTD, 3
		
	sbic	PINC, 2
	rjmp	KEY2
	ldi		R16, 1		; pressed key 1
	rjmp	KEYRET
KEY2:
	sbic	PINC, 3
	rjmp	KEY3
	ldi		R16, 2
	rjmp	KEYRET
KEY3:
	sbic	PINC, 4
	rjmp	KEY4
	ldi		R16, 3
	rjmp	KEYRET

KEY4:
	cbi		DDRD,0		
	cbi 	PORTD, 0
	
	sbi		DDRD,1
	cbi 	PORTD, 1	; log. 0 to the row with 4, 5, 6, B
	
	cbi		DDRD,2
	cbi 	PORTD, 2
	cbi		DDRD,3
	cbi 	PORTD, 3
		
	sbic	PINC, 2
	rjmp	KEY5
	ldi		R16, 4		; pressed key 4
	rjmp	KEYRET
KEY5:
	sbic	PINC, 3
	rjmp	KEY6
	ldi		R16, 5
	rjmp	KEYRET
KEY6:
	sbic	PINC, 4
	rjmp	KEY7
	ldi		R16, 6
	rjmp	KEYRET

KEY7:
	cbi		DDRD,0		
	cbi 	PORTD, 0
	cbi		DDRD,1		
	cbi 	PORTD, 1

	sbi		DDRD,2
	cbi 	PORTD, 2	
	
	cbi		DDRD,3
	cbi 	PORTD, 3

	sbic	PINC, 2
	rjmp	KEY8
	ldi		R16, 7		;pressed key 7
	rjmp	KEYRET
KEY8:
	sbic	PINC, 3
	rjmp	KEY9
	ldi		R16, 8
	rjmp	KEYRET
KEY9:
	sbic	PINC, 4
	rjmp	KEYRET
	ldi		R16, 9

KEYRET:
	sbi DDRD, 0 
	sbi DDRD, 1  
	sbi DDRD, 2
	sbi DDRD, 3

	cbi PORTD, 0	
	cbi PORTD, 1
	cbi PORTD, 2
	cbi PORTD, 3
	ret				; návrat na adresu uloženou na vršku zásobníku - adresa posledního volání podprogramu +1


Cvičení 6, update: větrák s přerušením, časovačem v modu Fast PWM a klávesnicí se třetím stavem.

; Soubor ze cvičení k regilaci otáček s detailnějšími komentáři a upraven
; s klávesnicí ve třetím stavu pro eliminaci problému se stisknutím více tlačítek najednou


.INCLUDE "m88def.inc"

.EQU PWM_PULSES = 36	; value for 27.8 kHz PWM
;.EQU PWM_PULSES = 45	; value for 22.2 kHz PWM


.ORG 0x000 
	rjmp Main ; Při resetu (odpovídá vekt. 0000) skoč na Main

.ORG 0x004 
	rjmp isr1 ; Při změně pinu (vektor 0008 - u každého AVR jinak!, definice konkrétního pinu níže) skož na isr1

Main: 

	ldi r16,high(RAMEND); Main program start
	out SPH,r16 ; Set Stack Pointer to top of RAM
	ldi r16,low(RAMEND)
	out SPL,r16


; Hardware initialization

; Keyboard init:
	cbi DDRC, 2 ; Sloupce klávesnice jako vstupy (jen tři - písmena netřeba)
	cbi DDRC, 3
	cbi DDRC, 4

	sbi PORTC, 2 ; nastavení měkkých jedniček na vstupy sloupců klávesnice - pull up odpory
	sbi PORTC, 3
	sbi PORTC, 4

	sbi DDRD, 0 
	sbi DDRD, 1  
	sbi DDRD, 2
	sbi DDRD, 3

	cbi PORTD, 0	
	cbi PORTD, 1
	cbi PORTD, 2
	cbi PORTD, 3


; Timer controlled PWM init:
;
; There are registers TCCR0A and TCCR0B for config, DDRD for output bit
; enabling, OCR0A for counter lenght and OCR0A for pulse "high" lenght.
; Register TIMSK0 controlls interrupts - not used, 0x00 by default 

PWM_INIT:
	ldi R17, 0b00100011	; Nastavení 1. řídicího registru čítače, poslední dva bity nastavují mód čítače (v tomto případě Fast PWM kdy vrchní hodnota čítání bude v registru OCR0A - pozor! O módu rozhoduje také bit 3 v registru TCCR0B!), první čtyři bity nastavují, jak se čítač chová při dosažení porovnávací úrovně (v tomto případě nastaví na pinu OC0B jedničku když má čítač menší hodnotu než OCR0B a naopak)   
	out TCCR0A, R17			; 
	ldi R17, 0b00001001	; nastavení módu viz přechozí komentář, čítání bez předděličky
	out TCCR0B, R17
	ldi R17, PWM_PULSES	; Čítač přeteče při dosažení hodnoty v prom. PWM_PULSES, tzn PWM na frekvenci  frekv. oscil/PWM_PULSES. Změnou OCR0B pak budu měnit střídu PWM
	out OCR0A, R17   		

	sbi DDRD, 5	; Čítač sdílí svůj výstup OC0B s 5. pinem na portu D, musím tedy nastavit jako výstup


; Keyboard interrupt setup
;
; Activation of pin change interrupt - PROBLEM!!! PCICR and PCIMSK are 
; extended I/O registers (0x68 and 0x6C), and must be handled as 
; a memory location


;PCICR je registr nastavující na posledních třech bitech jeden jaký ze tří možných vektorů přerušení pro změnu na pinu budou aktivní
	ldi r26, PCICR ; R26 a 27 tvoří 16bit registr X. Do spodních 8 bitů dám adresu PCICR
	clr r27 ; Hořejšek registru X vymažu
	ldi r16,0b00000010 ; do R 16 si připravim požadované nastavení - v tomto případě PCINT1 - vektor 0008 viz začátek programu
	st X, r16 ; Uložim do X nastavení přerušení - tím nastavim registr PCICR

;PCMSK1 mi určuje piny, které budou způsobovat přerušení PCINT1, stejným způsobem nastavím jako předchozí
	ldi r26, PCMSK1
	clr r27
	ldi r16,0b00011100 ; Přerušení budou způsobovat piny PCINT10, PCINT11 a PCINT12, které mají stejné vývody jako piny 2,3,4 na portu C - to jsou naše slouce klávesnice s pullup odpory
	st X, r16

	sei ; zapnutí přerušení: mělo by být ekvivalentní sbi SREG,7


; Set sleep mode of the CPU
; SMCR: 0,0,0,0,SM2,SM1,SM0,SE   SM2..0: Sleep Mode Select Bits, SE: Sleep enabled)

	ldi r16,0b00000001 ; Aktivace Sleep módu - při provedení instukce sleep skutečně proc usne
	out SMCR,r16 ; Zápis do příslušného registru


; Procesor bude spát dokud nezmáčknu klávesu a nezpůsobim přerušení

loop:
	sleep 
	nop 
	rjmp loop


; PCINT0 Service Routine:
;
; To do: *** rpm reading, regulation, etc.


isr1:	;obsluha přerušení při zmáčknutí klávesy
	rcall KEYPRESS

	mov R17, R16
	lsl R17		; posun vlevo - v bináru násobení dvěmi
	lsl R17		; ještě jednou - takže dohromady 4, maximální hodnota při stisku 9 je tedy 36
	out OCR0B, R17  ; uložim číslo 0 ... 36 v (10 kroků, tlačítek) do porovnávacího registru k nastavení střídy

	reti ; návrat z přerušení na poslední adresu před přerušením +1 (při reti se narozdíl od ret neukládá stavový registr, je nutno to udělat růčo)


; Keyboard decoding:

KEYPRESS:
KEY0:		;kontrola stistku nuly - po úpravě 
	cbi		DDRD,0
	cbi 	PORTD, 0
	cbi		DDRD,1
	cbi 	PORTD, 1
	cbi		DDRD,2
	cbi 	PORTD, 2	; řádky které nekontroluji tak nastavím do třetího stavu, tj jako vstupy bez pullup
	
	sbi		DDRD,3		; řádek odpovádající kontrolované klávese jako výstup do nuly
	cbi 	PORTD, 3	
	
	sbic	PINC, 3		; v případě že je kontrolovaná klávesa, a tedy je na PINC sražená měkká 1 na 0, přeskočim následující instrukci
	rjmp	KEY1		; skok na test další klávesy
	ldi		R16, 0		; do uživ. registru šoupnu zmáčknutou hodnotu (v tomto případě nulu) kterou pak vhodně navhájuju, vložim do komparační
	rjmp	KEYRET
KEY1:
	sbi		DDRD,0		; nulu na první řádek, testuju jedničku
	cbi 	PORTD, 0	
	
	cbi		DDRD,1		; třetí stav na ost. řádcích
	cbi 	PORTD, 1
	cbi		DDRD,2	
	cbi 	PORTD, 2
	cbi		DDRD,3
	cbi 	PORTD, 3
		
	sbic	PINC, 2
	rjmp	KEY2
	ldi		R16, 1		; pressed key 1
	rjmp	KEYRET
KEY2:
	sbic	PINC, 3
	rjmp	KEY3
	ldi		R16, 2
	rjmp	KEYRET
KEY3:
	sbic	PINC, 4
	rjmp	KEY4
	ldi		R16, 3
	rjmp	KEYRET

KEY4:
	cbi		DDRD,0		
	cbi 	PORTD, 0
	
	sbi		DDRD,1
	cbi 	PORTD, 1	; log. 0 to the row with 4, 5, 6, B
	
	cbi		DDRD,2
	cbi 	PORTD, 2
	cbi		DDRD,3
	cbi 	PORTD, 3
		
	sbic	PINC, 2
	rjmp	KEY5
	ldi		R16, 4		; pressed key 4
	rjmp	KEYRET
KEY5:
	sbic	PINC, 3
	rjmp	KEY6
	ldi		R16, 5
	rjmp	KEYRET
KEY6:
	sbic	PINC, 4
	rjmp	KEY7
	ldi		R16, 6
	rjmp	KEYRET

KEY7:
	cbi		DDRD,0		
	cbi 	PORTD, 0
	cbi		DDRD,1		
	cbi 	PORTD, 1

	sbi		DDRD,2
	cbi 	PORTD, 2	
	
	cbi		DDRD,3
	cbi 	PORTD, 3

	sbic	PINC, 2
	rjmp	KEY8
	ldi		R16, 7		;pressed key 7
	rjmp	KEYRET
KEY8:
	sbic	PINC, 3
	rjmp	KEY9
	ldi		R16, 8
	rjmp	KEYRET
KEY9:
	sbic	PINC, 4
	rjmp	KEYRET
	ldi		R16, 9

KEYRET:
	sbi DDRD, 0 
	sbi DDRD, 1  
	sbi DDRD, 2
	sbi DDRD, 3

	cbi PORTD, 0	
	cbi PORTD, 1
	cbi PORTD, 2
	cbi PORTD, 3
	ret				; návrat na adresu uloženou na vršku zásobníku - adresa posledního volání podprogramu +1

Cvičení 6: větrák s přerušením, časovačem v modu Fast PWM a klávesnicí se třetím stavem.


; Soubor ze cvičení k regilaci otáček s detailnějšími komentáři a upraven
; s klávesnicí ve třetím stavu pro eliminaci problému se stisknutím více tlačítek najednou


.INCLUDE "m168def.inc"

.EQU PWM_PULSES = 36	; value for 27.8 kHz PWM
;.EQU PWM_PULSES = 45	; value for 22.2 kHz PWM


.ORG 0x0000 
	jmp Main ; Při resetu (odpovídá vekt. 0000) skoč na Main

.ORG 0x0008 
	jmp isr1 ; Při změně pinu (vektor 0008 - u každého AVR jinak!, definice konkrétního pinu níže) skož na isr1

Main: 

	ldi r16,high(RAMEND); Main program start
	out SPH,r16 ; Set Stack Pointer to top of RAM
	ldi r16,low(RAMEND)
	out SPL,r16


; Hardware initialization

; Keyboard init:
	cbi DDRC, 2 ; Sloupce klávesnice jako vstupy (jen tři - písmena netřeba)
	cbi DDRC, 3
	cbi DDRC, 4

	sbi PORTC, 2 ; nastavení měkkých jedniček na vstupy sloupců klávesnice - pull up odpory
	sbi PORTC, 3
	sbi PORTC, 4

	cbi DDRD, 0 ; následujících 8 řádků nastaví řádky klávesnice do třetího stavu
	cbi DDRD, 1 ; 
	cbi DDRD, 2
	cbi DDRD, 3

	cbi PORTD, 0	
	cbi PORTD, 1
	cbi PORTD, 2
	cbi PORTD, 3


; Timer controlled PWM init:
;
; There are registers TCCR0A and TCCR0B for config, DDRD for output bit
; enabling, OCR0A for counter lenght and OCR0A for pulse "high" lenght.
; Register TIMSK0 controlls interrupts - not used, 0x00 by default 

PWM_INIT:
	ldi R17, 0b00100011	; Nastavení 1. řídicího registru čítače, poslední dva bity nastavují mód čítače (v tomto případě Fast PWM kdy vrchní hodnota čítání bude v registru OCR0A - pozor! O módu rozhoduje také bit 3 v registru TCCR0B!), první čtyři bity nastavují, jak se čítač chová při dosažení porovnávací úrovně (v tomto případě nastaví na pinu OC0B jedničku když má čítač menší hodnotu než OCR0B a naopak)   
	out TCCR0A, R17			; 
	ldi R17, 0b00001001	; nastavení módu viz přechozí komentář, čítání bez předděličky
	out TCCR0B, R17
	ldi R17, PWM_PULSES	; Čítač přeteče při dosažení hodnoty v prom. PWM_PULSES, tzn PWM na frekvenci  frekv. oscil/PWM_PULSES. Změnou OCR0B pak budu měnit střídu PWM
	out OCR0A, R17   		

	sbi DDRD, 5	; Čítač sdílí svůj výstup OC0B s 5. pinem na portu D, musím tedy nastavit jako výstup


; Keyboard interrupt setup
;
; Activation of pin change interrupt - PROBLEM!!! PCICR and PCIMSK are 
; extended I/O registers (0x68 and 0x6C), and must be handled as 
; a memory location


;PCICR je registr nastavující na posledních třech bitech jeden jaký ze tří možných vektorů přerušení pro změnu na pinu budou aktivní
	ldi r26, PCICR ; R26 a 27 tvoří 16bit registr X. Do spodních 8 bitů dám adresu PCICR
	clr r27 ; Hořejšek registru X vymažu
	ldi r16,0b00000010 ; do R 16 si připravim požadované nastavení - v tomto případě PCINT1 - vektor 0008 viz začátek programu
	st X, r16 ; Uložim do X nastavení přerušení - tím nastavim registr PCICR

;PCMSK1 mi určuje piny, které budou způsobovat přerušení PCINT1, stejným způsobem nastavím jako předchozí
	ldi r26, PCMSK1
	clr r27
	ldi r16,0b00011100 ; Přerušení budou způsobovat piny PCINT10, PCINT11 a PCINT12, které mají stejné vývody jako piny 2,3,4 na portu C - to jsou naše slouce klávesnice s pullup odpory
	st X, r16

	sei ; zapnutí přerušení: mělo by být ekvivalentní sbi SREG,7


; Set sleep mode of the CPU
; SMCR: 0,0,0,0,SM2,SM1,SM0,SE   SM2..0: Sleep Mode Select Bits, SE: Sleep enabled)

	ldi r16,0b00000001 ; Aktivace Sleep módu - při provedení instukce sleep skutečně proc usne
	out SMCR,r16 ; Zápis do příslušného registru


; Procesor bude spát dokud nezmáčknu klávesu a nezpůsobim přerušení

loop:
	sleep 
	nop 
	rjmp loop


; PCINT0 Service Routine:
;
; To do: *** rpm reading, regulation, etc.


isr1:	;obsluha přerušení při zmáčknutí klávesy
	rcall KEYPRESS

	mov R17, R16
	lsl R17		; posun vlevo - v bináru násobení dvěmi
	lsl R17		; ještě jednou - takže dohromady 4, maximální hodnota při stisku 9 je tedy 36
	out OCR0B, R17  ; uložim číslo 0 ... 36 v (10 kroků, tlačítek) do porovnávacího registru k nastavení střídy

	reti ; návrat z přerušení na poslední adresu před přerušením +1 (při reti se narozdíl od ret neukládá stavový registr, je nutno to udělat růčo)


; Keyboard decoding:

KEYPRESS:
KEY0:		;kontrola stistku nuly - po úpravě 
	cbi		DDRD,0
	cbi 	PORTD, 0
	cbi		DDRD,1
	cbi 	PORTD, 1
	cbi		DDRD,2
	cbi 	PORTD, 2	; řádky které nekontroluji tak nastavím do třetího stavu, tj jako vstupy bez pullup
	
	sbi		DDRD,3		; řádek odpovádající kontrolované klávese jako výstup do nuly
	cbi 	PORTD, 3	
	
	sbic	PINC, 3		; v případě že je kontrolovaná klávesa, a tedy je na PINC sražená měkká 1 na 0, přeskočim následující instrukci
	rjmp	KEY1		; skok na test další klávesy
	ldi		R16, 0		; do uživ. registru šoupnu zmáčknutou hodnotu (v tomto případě nulu) kterou pak vhodně navhájuju, vložim do komparační
	rjmp	KEYRET
KEY1:
	sbi		DDRD,0		; nulu na první řádek, testuju jedničku
	cbi 	PORTD, 0	
	
	cbi		DDRD,1		; třetí stav na ost. řádcích
	cbi 	PORTD, 1
	cbi		DDRD,2	
	cbi 	PORTD, 2
	cbi		DDRD,3
	cbi 	PORTD, 3
		
	sbic	PINC, 2
	rjmp	KEY2
	ldi		R16, 1		; pressed key 1
	rjmp	KEYRET
KEY2:
	sbic	PINC, 3
	rjmp	KEY3
	ldi		R16, 2
	rjmp	KEYRET
KEY3:
	sbic	PINC, 4
	rjmp	KEY4
	ldi		R16, 3
	rjmp	KEYRET

KEY4:
	cbi		DDRD,0		
	cbi 	PORTD, 0
	
	sbi		DDRD,1
	cbi 	PORTD, 1	; log. 0 to the row with 4, 5, 6, B
	
	cbi		DDRD,2
	cbi 	PORTD, 2
	cbi		DDRD,3
	cbi 	PORTD, 3
		
	sbic	PINC, 2
	rjmp	KEY5
	ldi		R16, 4		; pressed key 4
	rjmp	KEYRET
KEY5:
	sbic	PINC, 3
	rjmp	KEY6
	ldi		R16, 5
	rjmp	KEYRET
KEY6:
	sbic	PINC, 4
	rjmp	KEY7
	ldi		R16, 6
	rjmp	KEYRET

KEY7:
	cbi		DDRD,0		
	cbi 	PORTD, 0
	cbi		DDRD,1		
	cbi 	PORTD, 1

	sbi		DDRD,2
	cbi 	PORTD, 2	
	
	cbi		DDRD,3
	cbi 	PORTD, 3

	sbic	PINC, 2
	rjmp	KEY8
	ldi		R16, 7		;pressed key 7
	rjmp	KEYRET
KEY8:
	sbic	PINC, 3
	rjmp	KEY9
	ldi		R16, 8
	rjmp	KEYRET
KEY9:
	sbic	PINC, 4
	rjmp	KEYRET
	ldi		R16, 9

KEYRET:
	cbi DDRD, 0 ; následujících 8 řádků nastaví řádky klávesnice do třetího stavu
	cbi DDRD, 1 ; 
	cbi DDRD, 2
	cbi DDRD, 3

	cbi PORTD, 0	
	cbi PORTD, 1
	cbi PORTD, 2
	cbi PORTD, 3
	ret				; návrat na adresu uloženou na vršku zásobníku - adresa posledního volání podprogramu +1
Osobní nástroje