Hjälp med interrupt i mikroc.
Hjälp med interrupt i mikroc.
Hej
Jag har börjat experimentera i mikroc. Jag vet att många förespråkar asm för noviser som mig. Men jag var otålig. Jag har blinkat lite ledar och lekt med lcd-display och det har gått fint.
Nu har jag dock stött på problem. Jag skulle vilja göra en enkel klocka. För att åstadkomma det vill jag använda timern och interrupt.
Min uppkoppling verkar ok tycker jag. Processorn kör och jag får ut text på displayen. Dock får jag inga interrupts på timern.
Koden är som följer:
/*
* Project name:
LCDClock
* Copyright:
(c) Daniel Carlsson, 2006.
* Description:
Simple program to learn how to write to 16x2 character display and how to
implement a hardware timer with interrupt.
* Test configuration:
MCU: PIC16F628A
Oscillator: XT, 04.0000 MHz
Ext. Modules: -
SW: mikroC v5.0
* Wiring :
D7 - port.7
D6 - port.6
D5 - port.5
D4 - port.4
E - port.3
RS - port.2
*/
short unsigned hh = 0;
short unsigned mi = 0;
short unsigned ss = 0;
/*
Writes the time in the HH:MM:SS format to an initiated LCD. Row and col
values dertermins the placement on the display.
*/
void WriteTimeToLCD(short unsigned row, short unsigned col,short unsigned hh,
short unsigned mi, short unsigned ss)
{
char tmp[4];
// Write hours to display
ShortToStr(hh, tmp);
if (hh < 10)
tmp[2] = '0';
Lcd_Chr(row, col, tmp[2]);
Lcd_Chr(row, col + 1, tmp[3]);
Lcd_Chr(row, col + 2, ':');
//Write minutes to display
ShortToStr(mi, tmp);
if (mi < 10)
tmp[2] = '0';
Lcd_Chr(row, col + 3, tmp[2]);
Lcd_Chr(row, col + 4, tmp[3]);
Lcd_Chr(row, col + 5, ':');
//Write seconds to display
ShortToStr(ss, tmp);
if (ss < 10)
tmp[2] = '0';
Lcd_Chr(row, col + 6, tmp[2]);
Lcd_Chr(row, col + 7, tmp[3]);
}
//Handels interrupt from TMR0
void interrupt(void) {
TMR0 = 96;
ss ++;
if (ss = 60)
ss = 0;
}
void main() {
OPTION_REG = 0x84; // Assign prescaler to TMR0
TMR0 = 96;
INTCON.T0IE = 1; // Enable TMRO interrupt
TRISB = 0; // PORTB is output
Delay_ms(1000);
Lcd_Init(&PORTB); // Initialize LCD connected to PORTB
Lcd_Cmd(Lcd_CURSOR_OFF); // Turn cursor off
Lcd_Cmd(Lcd_CLEAR); // Clear display
while (1) {
WriteTimeToLCD(1, 5, hh, mi, ss);
}
}
Det som händer då man kör koden är att man får en utskrift 00:00:00 på displayen vilket är korrekt. Dock hade jag förväntat mig att siffrorna för sekund skulle ändras. Intervallet för interrupt är inte en sekund det är jag medveten om men problemet är att jag får ingen interrupt alls.
Tar tacksamt emot en hjälpande hand.
/Daniel
Jag har börjat experimentera i mikroc. Jag vet att många förespråkar asm för noviser som mig. Men jag var otålig. Jag har blinkat lite ledar och lekt med lcd-display och det har gått fint.
Nu har jag dock stött på problem. Jag skulle vilja göra en enkel klocka. För att åstadkomma det vill jag använda timern och interrupt.
Min uppkoppling verkar ok tycker jag. Processorn kör och jag får ut text på displayen. Dock får jag inga interrupts på timern.
Koden är som följer:
/*
* Project name:
LCDClock
* Copyright:
(c) Daniel Carlsson, 2006.
* Description:
Simple program to learn how to write to 16x2 character display and how to
implement a hardware timer with interrupt.
* Test configuration:
MCU: PIC16F628A
Oscillator: XT, 04.0000 MHz
Ext. Modules: -
SW: mikroC v5.0
* Wiring :
D7 - port.7
D6 - port.6
D5 - port.5
D4 - port.4
E - port.3
RS - port.2
*/
short unsigned hh = 0;
short unsigned mi = 0;
short unsigned ss = 0;
/*
Writes the time in the HH:MM:SS format to an initiated LCD. Row and col
values dertermins the placement on the display.
*/
void WriteTimeToLCD(short unsigned row, short unsigned col,short unsigned hh,
short unsigned mi, short unsigned ss)
{
char tmp[4];
// Write hours to display
ShortToStr(hh, tmp);
if (hh < 10)
tmp[2] = '0';
Lcd_Chr(row, col, tmp[2]);
Lcd_Chr(row, col + 1, tmp[3]);
Lcd_Chr(row, col + 2, ':');
//Write minutes to display
ShortToStr(mi, tmp);
if (mi < 10)
tmp[2] = '0';
Lcd_Chr(row, col + 3, tmp[2]);
Lcd_Chr(row, col + 4, tmp[3]);
Lcd_Chr(row, col + 5, ':');
//Write seconds to display
ShortToStr(ss, tmp);
if (ss < 10)
tmp[2] = '0';
Lcd_Chr(row, col + 6, tmp[2]);
Lcd_Chr(row, col + 7, tmp[3]);
}
//Handels interrupt from TMR0
void interrupt(void) {
TMR0 = 96;
ss ++;
if (ss = 60)
ss = 0;
}
void main() {
OPTION_REG = 0x84; // Assign prescaler to TMR0
TMR0 = 96;
INTCON.T0IE = 1; // Enable TMRO interrupt
TRISB = 0; // PORTB is output
Delay_ms(1000);
Lcd_Init(&PORTB); // Initialize LCD connected to PORTB
Lcd_Cmd(Lcd_CURSOR_OFF); // Turn cursor off
Lcd_Cmd(Lcd_CLEAR); // Clear display
while (1) {
WriteTimeToLCD(1, 5, hh, mi, ss);
}
}
Det som händer då man kör koden är att man får en utskrift 00:00:00 på displayen vilket är korrekt. Dock hade jag förväntat mig att siffrorna för sekund skulle ändras. Intervallet för interrupt är inte en sekund det är jag medveten om men problemet är att jag får ingen interrupt alls.
Tar tacksamt emot en hjälpande hand.
/Daniel
Det låter som om du inte alls har läst kap 14.5 sid 107 ("Interrupts") i databladet !?
Gör du det så är det nästan omöjligt att inte se felet.
Johan_46's är *DELVIS* rätt. Vad som är fel framgår av databladet fig 14-14.
Det är inget som inte får det att fungera, men det är onödigt o detta fall.
Personligen tycker jag det är bättre att tala om var svaret står, än att ge
svaret självt, det kan bli en björntjänst om man har otur. Det finns en liten
risk att man skiver av fel och det kan förvirra mer än det hjälper
Gör du det så är det nästan omöjligt att inte se felet.
Johan_46's är *DELVIS* rätt. Vad som är fel framgår av databladet fig 14-14.
Det är inget som inte får det att fungera, men det är onödigt o detta fall.
Personligen tycker jag det är bättre att tala om var svaret står, än att ge
svaret självt, det kan bli en björntjänst om man har otur. Det finns en liten
risk att man skiver av fel och det kan förvirra mer än det hjälper

Slimmade koden lite.
//Handels interrupt from TMR0
void interrupt(void) {
INTCON.T0IE = 0; // Disable TMRO interrupt
INTCON.T0IF = 0; //Timer0 Interrupt flag
PORTB.F1 = 1;
Delay_ms(1000);
PORTB.F1 = 0;
Delay_ms(1000);
INTCON.T0IE = 1; // Enable TMRO interrupt
}
void main() {
TRISB = 0; // PORTB is output
OPTION_REG = 0x84; // Assign prescaler to TMR0
TMR0 = 1;
INTCON.GIE = 1;
INTCON.T0IE = 1; // Enable TMRO interrupt
INTCON.T0IF = 0; //Timer0 Interrupt flag
}
Läste sid 107 i databladet som sodjan ordinerade och fick igång timern. Dock fick jag något annat att fundera på.... Så som jag tolkade databladet så förstod jag det som att INTCON.GIE = 1 sätter på samtliga interrupts. Sen får man definiera vilka interrupt man vill "lyssna på" i detta fall timer0 dvs jag sätter INTCON.T0IE = 1. Är jag helt ute och cyklar eller är detta korrekt uppfattat?
Tack för de snabba informativa svaren. Gillar sodjans pedagogik att visa var man skall leta för att få hjälp istället för att levrera svaret direkt.
/daniel
//Handels interrupt from TMR0
void interrupt(void) {
INTCON.T0IE = 0; // Disable TMRO interrupt
INTCON.T0IF = 0; //Timer0 Interrupt flag
PORTB.F1 = 1;
Delay_ms(1000);
PORTB.F1 = 0;
Delay_ms(1000);
INTCON.T0IE = 1; // Enable TMRO interrupt
}
void main() {
TRISB = 0; // PORTB is output
OPTION_REG = 0x84; // Assign prescaler to TMR0
TMR0 = 1;
INTCON.GIE = 1;
INTCON.T0IE = 1; // Enable TMRO interrupt
INTCON.T0IF = 0; //Timer0 Interrupt flag
}
Läste sid 107 i databladet som sodjan ordinerade och fick igång timern. Dock fick jag något annat att fundera på.... Så som jag tolkade databladet så förstod jag det som att INTCON.GIE = 1 sätter på samtliga interrupts. Sen får man definiera vilka interrupt man vill "lyssna på" i detta fall timer0 dvs jag sätter INTCON.T0IE = 1. Är jag helt ute och cyklar eller är detta korrekt uppfattat?
Tack för de snabba informativa svaren. Gillar sodjans pedagogik att visa var man skall leta för att få hjälp istället för att levrera svaret direkt.
/daniel
Vid de allra flesta normala avbott kan det som skall "göras" delas
upp i två delar, en del som måste göras "nu" och som inte får/bör
avbrytas av annat. Detta görs lämpligen i själva ISR'en. T.ex läsa
av ett mottgaget tecken från USART'en eller läsa av ett värde från
ADCn och spara undan dessa. Även vissa beräkningar där man vill
undvika "race conditions" melan olika ruiner kan göras i ISR'en.
Den andra delen som inte är så tidskritisk (t.ex tolkninen av det
mottagana tecknet eller utvärderingen av ADC värdet) kan sedan
göras från main-koden.
D.v,s att ISR'en gör det den *måste* göra (men inte mer!) och sätter
sedan en flagga (en bit i ett register). Main-rutinen ser sedan ut
ungefär så gär :
main-loop
bit-test usart_receive_flagga
om "1", call usart_handler
bit-test adc_value_ready_flagga
om "1", call adc_handler
goto main-loop
D.v.s att rutinen för tolkning av USART tecknet kan avbrytas
av att ADC går klart, och tvärtom. Inga låsningar.
En annan tumregel är att ingen ISR rutin skall vara längre
än den längsta acceptabla fördröjningen för något av de *andra*
avbrotten.
Om man t.ex kör USART i 57 Kbaud baud, så kan det komma tecken med
(ca) 150 us intervall. Alltså får ingen *annan* ISR vara längre än
ca 150 us, eftersom det då finns risk för "over-run" i USART'en.
Lägg på lämpliga säkerhetsmariginaler o.s.v...
Nu är 150 us än ganska lång ISR i alla fall, men du förstår principen.
Det är ovanligt att en ISR *måste* vara längre än ett par 10-tal us.
upp i två delar, en del som måste göras "nu" och som inte får/bör
avbrytas av annat. Detta görs lämpligen i själva ISR'en. T.ex läsa
av ett mottgaget tecken från USART'en eller läsa av ett värde från
ADCn och spara undan dessa. Även vissa beräkningar där man vill
undvika "race conditions" melan olika ruiner kan göras i ISR'en.
Den andra delen som inte är så tidskritisk (t.ex tolkninen av det
mottagana tecknet eller utvärderingen av ADC värdet) kan sedan
göras från main-koden.
D.v,s att ISR'en gör det den *måste* göra (men inte mer!) och sätter
sedan en flagga (en bit i ett register). Main-rutinen ser sedan ut
ungefär så gär :
main-loop
bit-test usart_receive_flagga
om "1", call usart_handler
bit-test adc_value_ready_flagga
om "1", call adc_handler
goto main-loop
D.v.s att rutinen för tolkning av USART tecknet kan avbrytas
av att ADC går klart, och tvärtom. Inga låsningar.
En annan tumregel är att ingen ISR rutin skall vara längre
än den längsta acceptabla fördröjningen för något av de *andra*
avbrotten.
Om man t.ex kör USART i 57 Kbaud baud, så kan det komma tecken med
(ca) 150 us intervall. Alltså får ingen *annan* ISR vara längre än
ca 150 us, eftersom det då finns risk för "over-run" i USART'en.
Lägg på lämpliga säkerhetsmariginaler o.s.v...
Nu är 150 us än ganska lång ISR i alla fall, men du förstår principen.
Det är ovanligt att en ISR *måste* vara längre än ett par 10-tal us.