Problem med portinställningar - PIC18F452
Problem med portinställningar - PIC18F452
Hej!
Jag håller på med ett program som bl.a. skall göra följande:
Skapa en tidbas (för att visa klocka/tid)
Läsa av temperatur från en temp-sensor
Visa bl.a. temperatur och tid på en display.
För att skapa en tidbas använder jag nåt som kallas #int_ccp1 (Capture or Compare interrupt) som i princip räknar ner två register från FF till 00. (RTC-n får jag ej använda enligt instuktioner, så det är uteslutet)
Till detta har jag följande inställningar för bl.a. kommunikation med RS232 och I2C bussen:
//RS232-kommunikation
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
//Seriekommunikation på I2C-bussen
#use I2C (MASTER, SDA=PIN_C4, SCL=PIN_C3, RESTART_WDT, FAST)
//Portinställningar
/*****************************/
SET_TRIS_A(0x7F);
SET_TRIS_B(0x00);
PORTB=0xFF;
SET_TRIS_C(0xA8); // 1010 1000
/*****************************/
So far so good.. dessa inställningar kan jag ha om jag vill kommunicera med t.ex. hyper-terminalen.
Problemmet är att om jag vill skicka text (tid,temp..) till en display (ILM-216) så måste jag ha andra inställningar:
/* RS232-kommunikation (OBS! MÅSTE vara så för komm. med LCD) */
#use rs232(baud=2400, xmit=PIN_E0, rcv=PIN_E1, invert)
/*****************************/
SET_TRIS_A(0x7F);
SET_TRIS_B(0x0C);
PORTB=0xFF;
SET_TRIS_C(0xFF);// 1111 1111
SET_TRIS_E(0xF6);// 1111 1110
/*****************************/
Min fråga är då:
Finns det en lösning där jag kan ha kvar #int_ccp1 som tidbas och samtidigt kunna skicka text till lcd-n, och hur gör jag för att "ändra" port-inställningar "online". Jag vill inte (kan inte?) göra det i main (while(1) loopen) eft.som jag då skulle "tappa" tid.
Tack för alla tips!
/usNinja.
p.s. Jag hoppas ni förstår vad jag menar, i annat fall kan jag skicka lite av koden och försöka förklara mer tydligt och utförligt.
Jag håller på med ett program som bl.a. skall göra följande:
Skapa en tidbas (för att visa klocka/tid)
Läsa av temperatur från en temp-sensor
Visa bl.a. temperatur och tid på en display.
För att skapa en tidbas använder jag nåt som kallas #int_ccp1 (Capture or Compare interrupt) som i princip räknar ner två register från FF till 00. (RTC-n får jag ej använda enligt instuktioner, så det är uteslutet)
Till detta har jag följande inställningar för bl.a. kommunikation med RS232 och I2C bussen:
//RS232-kommunikation
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
//Seriekommunikation på I2C-bussen
#use I2C (MASTER, SDA=PIN_C4, SCL=PIN_C3, RESTART_WDT, FAST)
//Portinställningar
/*****************************/
SET_TRIS_A(0x7F);
SET_TRIS_B(0x00);
PORTB=0xFF;
SET_TRIS_C(0xA8); // 1010 1000
/*****************************/
So far so good.. dessa inställningar kan jag ha om jag vill kommunicera med t.ex. hyper-terminalen.
Problemmet är att om jag vill skicka text (tid,temp..) till en display (ILM-216) så måste jag ha andra inställningar:
/* RS232-kommunikation (OBS! MÅSTE vara så för komm. med LCD) */
#use rs232(baud=2400, xmit=PIN_E0, rcv=PIN_E1, invert)
/*****************************/
SET_TRIS_A(0x7F);
SET_TRIS_B(0x0C);
PORTB=0xFF;
SET_TRIS_C(0xFF);// 1111 1111
SET_TRIS_E(0xF6);// 1111 1110
/*****************************/
Min fråga är då:
Finns det en lösning där jag kan ha kvar #int_ccp1 som tidbas och samtidigt kunna skicka text till lcd-n, och hur gör jag för att "ändra" port-inställningar "online". Jag vill inte (kan inte?) göra det i main (while(1) loopen) eft.som jag då skulle "tappa" tid.
Tack för alla tips!
/usNinja.
p.s. Jag hoppas ni förstår vad jag menar, i annat fall kan jag skicka lite av koden och försöka förklara mer tydligt och utförligt.
[Någan kanske kan flytta detta till "Mikroprocessorer"... ]
> (RTC-n får jag ej använda...)
*Vilken* "RTC" ???
> #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
Är det en programvaru USART ? (Varför skulle man annars välja pinnar?)
Samma fråga för I2C, för övrigt...
> Jag vill inte (kan inte?) göra det i main (while(1) loopen) eft.som jag då skulle "tappa" tid.
Vad betyder "tappa tid"? Och varför gör du det ?
> (RTC-n får jag ej använda...)
*Vilken* "RTC" ???
> #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
Är det en programvaru USART ? (Varför skulle man annars välja pinnar?)
Samma fråga för I2C, för övrigt...
> Jag vill inte (kan inte?) göra det i main (while(1) loopen) eft.som jag då skulle "tappa" tid.
Vad betyder "tappa tid"? Och varför gör du det ?
Hej!
> (RTC-n får jag ej använda...)
>> *Vilken* "RTC" ???
Man kan använda RTC som exakt tidbas, men det får man inte göra, glöm det där. Man ska skapa en "software klock" och jag valde capture or compare.
> #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
>> Är det en programvaru USART ? (Varför skulle man annars välja pinnar?)
Samma fråga för I2C, för övrigt...
Jag tror det!
"Tappa tid" vill jag inte göra genom att ändra inställningar för ofta i huvudloppen t.ex. eftersom tiden/klockan skall gå så exakt som möjligt!
Jag vill, om det är möjligt, göra port-inställningarna en gång och sen köra med dessa.
> (RTC-n får jag ej använda...)
>> *Vilken* "RTC" ???
Man kan använda RTC som exakt tidbas, men det får man inte göra, glöm det där. Man ska skapa en "software klock" och jag valde capture or compare.
> #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
>> Är det en programvaru USART ? (Varför skulle man annars välja pinnar?)
Samma fråga för I2C, för övrigt...
Jag tror det!
"Tappa tid" vill jag inte göra genom att ändra inställningar för ofta i huvudloppen t.ex. eftersom tiden/klockan skall gå så exakt som möjligt!
Jag vill, om det är möjligt, göra port-inställningarna en gång och sen köra med dessa.
Om "tappar tid" betyder att din tidsreferens beror på main-loop genomloppstid är du redan där fel ute!
Om det är så att du måste skapa en klocka som leds ur kretsen och sedan tillbaka kan du använda en timer som sedan, efter den är ställd rätt kommer pulserna att flöda helt automatisk.
Om du inte har pinnar nog är det bara att byta processor till en med fler pinnar.
Om du använder mjukvaru-UART är du riktigt illa ute om det finns en riktig UART i kretsen (som i övrigt inte verkar definierat).
EDIT: Såg att typen framgick av överskriften (som inte syns vid svar)
Om det är så att du måste skapa en klocka som leds ur kretsen och sedan tillbaka kan du använda en timer som sedan, efter den är ställd rätt kommer pulserna att flöda helt automatisk.
Om du inte har pinnar nog är det bara att byta processor till en med fler pinnar.
Om du använder mjukvaru-UART är du riktigt illa ute om det finns en riktig UART i kretsen (som i övrigt inte verkar definierat).
EDIT: Såg att typen framgick av överskriften (som inte syns vid svar)
Senast redigerad av Icecap 11 juni 2007, 12:10:04, redigerad totalt 1 gång.
> Man kan använda RTC som exakt tidbas,
Du menar en *extern* RTC !?
Jo, i så fall ja...
Right, då kör du alltså "som vanligt" med en timer som räknar tiden.
Men den påverkas ju inte alls av vad du gör i main-loopen,
så det där med att "tappa tid" förstår jag inte...
> klockan skall gå så exakt som möjligt!
Hm, och exakt *hur* exakt är "så exakt som möjligt" ????
Vad har du för krav ?
Och vad är igentligen "#int_ccp1" ? Något special i det verktyg som
du utvecklar i (och vad är *det* för verktyg ???) ?
Du menar en *extern* RTC !?
Jo, i så fall ja...
Right, då kör du alltså "som vanligt" med en timer som räknar tiden.
Men den påverkas ju inte alls av vad du gör i main-loopen,
så det där med att "tappa tid" förstår jag inte...
> klockan skall gå så exakt som möjligt!
Hm, och exakt *hur* exakt är "så exakt som möjligt" ????
Vad har du för krav ?
Och vad är igentligen "#int_ccp1" ? Något special i det verktyg som
du utvecklar i (och vad är *det* för verktyg ???) ?
OK, ett lite snabbkurs:
* Man kan göra en tidsräknare vid att använda en timer. Den ställer man till att ge interrupt med ett känd mellanrum, t.ex. 10Hz eller vad som nu fungerar bra.
* För varje timer-interrupt räknar man sedan upp en variabel (vi kallar den 'X' här). När X >= 10 ska X nollas och 'Seconds' räknas upp ett steg. När 'Seconds' >= 60 ska 'Seconds' nollas och 'Minutes' räknas upp ett steg osv osv. Nu har du en klocka som går så exakt som kristallen är.
* Ska du räkna datum också blir det lite mer jobb men principen är detsamma; räkna upp och kolla om det ska räknas till nästa steg.
*Om det finns hårdvaru-UART _ska_ den användas om möjligt!!!! (om man behöver UART självklart), mjukvaru-UART är en nödlösning!!!
* Man kan göra en tidsräknare vid att använda en timer. Den ställer man till att ge interrupt med ett känd mellanrum, t.ex. 10Hz eller vad som nu fungerar bra.
* För varje timer-interrupt räknar man sedan upp en variabel (vi kallar den 'X' här). När X >= 10 ska X nollas och 'Seconds' räknas upp ett steg. När 'Seconds' >= 60 ska 'Seconds' nollas och 'Minutes' räknas upp ett steg osv osv. Nu har du en klocka som går så exakt som kristallen är.
* Ska du räkna datum också blir det lite mer jobb men principen är detsamma; räkna upp och kolla om det ska räknas till nästa steg.
*Om det finns hårdvaru-UART _ska_ den användas om möjligt!!!! (om man behöver UART självklart), mjukvaru-UART är en nödlösning!!!
Hej igen.
Här kommer en utförligare förklaring.
Icecap: Jag tror jag har gjort ganska exakt så som du beskriver. Och det funkar utmärkt, det är inte det som är problemmet. Mer om problemet längre ner.
I main har jag bl.a. följande [1]:
#include <18F452.h>
#device *=16 ADC=10
#ID CHECKSUM
#ZERO_RAM
#use DELAY(CLOCK=19660800)
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)
//Här kopplas portarna till filregistret
#BYTE PORTA = 0xF80
#BYTE PORTB = 0xF81
#BYTE PORTC = 0xF82
#BYTE PORTD = 0xF83
#BYTE PORTE = 0xF84
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
//Seriekommunikation på I2C-bussen
#use I2C (MASTER, SDA=PIN_C4, SCL=PIN_C3, RESTART_WDT, FAST)
//Makron
#define SENSOR_LM75_ADDR 0x9e // I2C adress för temperatursensor
#define EEROM_ADDR 0xa0 // I2C adress för .
/*... osv ...*/
// Adressen för registret där CCP1IF finns PIR1 bit 2 (tillhör interruptrutinen nedan)
#byte PIR1=0xF9E // Adress F9Eh
// Interruprutin
#int_ccp1 // Deklarera att det är Capture or Compare on unit1 som genererar interrupt
compare1_avbrott()
{
bit_clear(PIR1,CCP1IF); // Nolla interruptflaggan CCP1IF
liGlobalCounter=liGlobalCounter-1;
output_bit(PIN_C2,~input(PIN_C2)); // Blinka Status Led vid avbrott
}
void main(void)
{
// Initieringa ccp-räknare
liGlobalCounter=NO_OF_CCP1;
// Tilldela registerparet komparatorvärdet
// (CCP_1 finns deklarerad i includefilen 18F452.h)
CCP_1=COUNTERVALUE;
//************************************************************************
SET_TRIS_A(0x7F);
SET_TRIS_B(0x00);
PORTB=0xFF;
SET_TRIS_C(0xA8); // 1010 1000
/* Timer1 som tidbas för interrupt*/
//************************************************************************
setup_timer_1(T1_DISABLED); // Se includefilen 18F452.h
set_timer1(RESET_TIMER_REG); // TMR1H och TMR1L nollställs
enable_interrupts(INT_CCP1); // Interrupten enable för CCP1
enable_interrupts(GLOBAL); // Möjliggör interrupt
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); // Starta Timer1 med prescale 1:8
//************************************************************************
// Konfigurera CCP1 som compare
setup_ccp1(CCP_COMPARE_RESET_TIMER);
do
{
if(liGlobalCounter==NOLL) //En sek.
{
get_temp();
update_clock();
printf(" \033[2J");//Rensa textskärmen
printf("\rTemp.: %.2f \tMedeltemp.: %.2f \tKlockan är: %02d:%02d:%02d", fTemperature, fMedTempNew, iHours, iMinutes, iSeconds);
printf(" \033[2;59f"); //Flytta cursorn långt bort och skriv ut på samma ställe
}
} while (TRUE);
//funktioner:
//Beräkna tid
void update_clock(void)
{
iSeconds+=1;
if (iSeconds==60)
{
iMinutes+=1;
iSeconds=RESET_SEC;
if (iMinutes==60)
{
iHours+=1;
iMinutes=RESET_MIN;
if (iHours==24)
{
//Days+=1;
iHours=RESET_HOUR;
}
}
}
}
//hämta temperatur från lm75 sensor
void get_temp(void)
{
i2c_start(); // start by master
i2c_write(SENSOR_LM75_ADDR); // skriv
i2c_write(REG_0); // välj register
i2c_start(); // start igen
i2c_write(SENSOR_LM75_ADDR | READ_BIT); // bitvis OR ==> MSb=1
iTempSensorhi = i2c_read(ACK); // läs MSB, ACK=fortsätt med nästa byte
bTempSensorlo = i2c_read(NO_ACK); // läs LSB, NO_ACK=mastern nöjd
i2c_stop(); // stop cond. by master
fTemperature=iTempSensorhi;
if (bTempSensorlo & TEMP_MASK)
fTemperature+=0.5;
}
Detta funkar jättebra när jag ska kommunicera med hyperterminalen i windows.
MEN.. om jag ska skicka text till LCD-n, t.ex. tid och temperatur, så måste jag ha andra inställningar/options på #use rs232(...), nämligen följande [2]:
/***************************************************/
#use rs232(baud=2400, xmit=PIN_E0, rcv=PIN_E1, invert)
SET_TRIS_A(0x7F); // 0111 1111 (1 är IN)
SET_TRIS_B(0x0C); // 0000 1100 (0 är out)
PORTB=0xFF;
SET_TRIS_C(0xFF);
SET_TRIS_E(0xF6);// 1111 1110
/***************************************************/
Alltså: de första inställningarna [1] fungerar jättebra, interruptrutingen, beräkning av klockan (o temperaturen), OM man skickar dessa värden till hyperterminalen, och det andra [2] funkar jättebra om man vill skicka vanlig text till LCD-n, Men båda funkar inte samtidigt, eftersom det är olika inställningar på SET_TRIS_X... och #use 232(...)
Så, är det möjligt att använda denna interruptrutin som den är, samtidigt som att det skall vara möjligt att skicka text till lcd-n.
För kommunikation med lcd-n är jag absolut låst till följande:
#use rs232(baud=2400, xmit=PIN_E0, rcv=PIN_E1, invert)
Och jag får inte använda någon RTC för att beräkna tid. Det skall vara en mjukvaroklocka.
Jag vet inte om detta är tillräckligt med info för att kunna lösa detta problem.. kanske jag inte förstått hur det här med pinnar funkar.. hur vet man vilka som får/ska användas till vad, vilka är "lediga", osv.
Tack så länge.
Här kommer en utförligare förklaring.
Icecap: Jag tror jag har gjort ganska exakt så som du beskriver. Och det funkar utmärkt, det är inte det som är problemmet. Mer om problemet längre ner.
I main har jag bl.a. följande [1]:
#include <18F452.h>
#device *=16 ADC=10
#ID CHECKSUM
#ZERO_RAM
#use DELAY(CLOCK=19660800)
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)
//Här kopplas portarna till filregistret
#BYTE PORTA = 0xF80
#BYTE PORTB = 0xF81
#BYTE PORTC = 0xF82
#BYTE PORTD = 0xF83
#BYTE PORTE = 0xF84
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
//Seriekommunikation på I2C-bussen
#use I2C (MASTER, SDA=PIN_C4, SCL=PIN_C3, RESTART_WDT, FAST)
//Makron
#define SENSOR_LM75_ADDR 0x9e // I2C adress för temperatursensor
#define EEROM_ADDR 0xa0 // I2C adress för .
/*... osv ...*/
// Adressen för registret där CCP1IF finns PIR1 bit 2 (tillhör interruptrutinen nedan)
#byte PIR1=0xF9E // Adress F9Eh
// Interruprutin
#int_ccp1 // Deklarera att det är Capture or Compare on unit1 som genererar interrupt
compare1_avbrott()
{
bit_clear(PIR1,CCP1IF); // Nolla interruptflaggan CCP1IF
liGlobalCounter=liGlobalCounter-1;
output_bit(PIN_C2,~input(PIN_C2)); // Blinka Status Led vid avbrott
}
void main(void)
{
// Initieringa ccp-räknare
liGlobalCounter=NO_OF_CCP1;
// Tilldela registerparet komparatorvärdet
// (CCP_1 finns deklarerad i includefilen 18F452.h)
CCP_1=COUNTERVALUE;
//************************************************************************
SET_TRIS_A(0x7F);
SET_TRIS_B(0x00);
PORTB=0xFF;
SET_TRIS_C(0xA8); // 1010 1000
/* Timer1 som tidbas för interrupt*/
//************************************************************************
setup_timer_1(T1_DISABLED); // Se includefilen 18F452.h
set_timer1(RESET_TIMER_REG); // TMR1H och TMR1L nollställs
enable_interrupts(INT_CCP1); // Interrupten enable för CCP1
enable_interrupts(GLOBAL); // Möjliggör interrupt
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); // Starta Timer1 med prescale 1:8
//************************************************************************
// Konfigurera CCP1 som compare
setup_ccp1(CCP_COMPARE_RESET_TIMER);
do
{
if(liGlobalCounter==NOLL) //En sek.
{
get_temp();
update_clock();
printf(" \033[2J");//Rensa textskärmen
printf("\rTemp.: %.2f \tMedeltemp.: %.2f \tKlockan är: %02d:%02d:%02d", fTemperature, fMedTempNew, iHours, iMinutes, iSeconds);
printf(" \033[2;59f"); //Flytta cursorn långt bort och skriv ut på samma ställe
}
} while (TRUE);
//funktioner:
//Beräkna tid
void update_clock(void)
{
iSeconds+=1;
if (iSeconds==60)
{
iMinutes+=1;
iSeconds=RESET_SEC;
if (iMinutes==60)
{
iHours+=1;
iMinutes=RESET_MIN;
if (iHours==24)
{
//Days+=1;
iHours=RESET_HOUR;
}
}
}
}
//hämta temperatur från lm75 sensor
void get_temp(void)
{
i2c_start(); // start by master
i2c_write(SENSOR_LM75_ADDR); // skriv
i2c_write(REG_0); // välj register
i2c_start(); // start igen
i2c_write(SENSOR_LM75_ADDR | READ_BIT); // bitvis OR ==> MSb=1
iTempSensorhi = i2c_read(ACK); // läs MSB, ACK=fortsätt med nästa byte
bTempSensorlo = i2c_read(NO_ACK); // läs LSB, NO_ACK=mastern nöjd
i2c_stop(); // stop cond. by master
fTemperature=iTempSensorhi;
if (bTempSensorlo & TEMP_MASK)
fTemperature+=0.5;
}
Detta funkar jättebra när jag ska kommunicera med hyperterminalen i windows.
MEN.. om jag ska skicka text till LCD-n, t.ex. tid och temperatur, så måste jag ha andra inställningar/options på #use rs232(...), nämligen följande [2]:
/***************************************************/
#use rs232(baud=2400, xmit=PIN_E0, rcv=PIN_E1, invert)
SET_TRIS_A(0x7F); // 0111 1111 (1 är IN)
SET_TRIS_B(0x0C); // 0000 1100 (0 är out)
PORTB=0xFF;
SET_TRIS_C(0xFF);
SET_TRIS_E(0xF6);// 1111 1110
/***************************************************/
Alltså: de första inställningarna [1] fungerar jättebra, interruptrutingen, beräkning av klockan (o temperaturen), OM man skickar dessa värden till hyperterminalen, och det andra [2] funkar jättebra om man vill skicka vanlig text till LCD-n, Men båda funkar inte samtidigt, eftersom det är olika inställningar på SET_TRIS_X... och #use 232(...)
Så, är det möjligt att använda denna interruptrutin som den är, samtidigt som att det skall vara möjligt att skicka text till lcd-n.
För kommunikation med lcd-n är jag absolut låst till följande:
#use rs232(baud=2400, xmit=PIN_E0, rcv=PIN_E1, invert)
Och jag får inte använda någon RTC för att beräkna tid. Det skall vara en mjukvaroklocka.
Jag vet inte om detta är tillräckligt med info för att kunna lösa detta problem.. kanske jag inte förstått hur det här med pinnar funkar.. hur vet man vilka som får/ska användas till vad, vilka är "lediga", osv.
Tack så länge.
Testa att använda 'Code'-taggen, det blir mycket enklare att läsa.
Klockan uppdateras TOTALT i ISR'n och sedan kan du trigga en flagga om ATT sekunderna är uppdaterat, då kommer din klocka att gå rätt även om din main-rutin kan ta lång tid.
Ett alternativ till att uppdatera allt i ISR'n är att ha en byte som du räknar upp för varje sekund:
Nu kommer 'Update' att ökas varje sekund.
EDIT: Ett problem är att mjukvaru-UART stoppar interrupten då den måste hålla en mycket hård timing, alltså kan det vara en idé att skicka ett teckan åt fången istället för en sträng, det ger interrupten möjlighet att "komma emellan".
Klockan uppdateras TOTALT i ISR'n och sedan kan du trigga en flagga om ATT sekunderna är uppdaterat, då kommer din klocka att gå rätt även om din main-rutin kan ta lång tid.
Ett alternativ till att uppdatera allt i ISR'n är att ha en byte som du räknar upp för varje sekund:
Kod: Markera allt
unsigned char Update; // Nollas under uppstart
// Interruprutin
#int_ccp1 // Deklarera att det är Capture or Compare on unit1 som genererar interrupt
compare1_avbrott()
{
bit_clear(PIR1,CCP1IF); // Nolla interruptflaggan CCP1IF
liGlobalCounter--; // Räkna ner den ett steg
if(!liGlobalCounter) Update++; // Räkna upp sekunder att lägga till
output_bit(PIN_C2,~input(PIN_C2)); // Blinka Status Led vid avbrott
}
Kod: Markera allt
//Beräkna tid
void update_clock(void)
{
while(Update--) // Upprepa till alla sekunderna är räknade
{
if(++iSeconds>=60)
{
iSeconds=RESET_SEC;
if(++iMinutes>=60)
{
iMinutes=RESET_MIN;
if(++iHours>=24)
{
//Days+=1;
iHours=RESET_HOUR;
}
}
}
}
}