Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:lab:lab2-2022 [2023/03/18 21:10]
alexandru.predescu
pm:lab:lab2-2022 [2024/03/17 09:54] (current)
florin.stancu
Line 5: Line 5:
 ~~SHOWSOLUTION~~ ~~SHOWSOLUTION~~
  
-====== Laboratorul 2: Întreruperi. Întreruperi externe======+====== Laboratorul 2: Întreruperi ​hardware. Întreruperi externe ======
  
-Acest laborator are ca scop familiarizarea voastră cu lucrul cu întreruperile hardware și cu timer-ele prezente în microcontroller-ul Atmega328p. Vom folosi ​timer-ele doar pentru a număranu și pentru a genera semnal PWM. Această funcționalitate va fi studiată și utilizată în laboratoarele următoare.+Acest laborator are ca scop familiarizarea voastră cu lucrul cu întreruperile hardware și în particular ​cu întreruperile externe. Vom folosi ​întreruperi externe ​pentru a detecta imediat (în timp real) apăsarea unui butonindependent de programul principal.
  
 ===== 1. Întreruperi ===== ===== 1. Întreruperi =====
Line 54: Line 54:
  
  
-==== 1.1. Utilizarea întreruperilor ====+==== Utilizarea întreruperilor ====
  
 În general, pașii parcurși pentru a activa o întrerupere sunt următorii: În general, pașii parcurși pentru a activa o întrerupere sunt următorii:
Line 68: Line 68:
 cli(); cli();
 </​file>​ </​file>​
- 
- 
-==== 1.2. Lucrul cu întreruperi ==== 
- 
-Reguli de programare în context întrerupere:​ 
- 
-  * Nu există o valoare de return. La închierea execuției handler-ului,​ procesorul execută din nou instrucțiuni de unde rămăsese înainte de declanșarea întreruperii 
-  * Datorită faptului că handler-ul întârzie orice altă activitate din main și inhibă execuția altor întreruperi,​ se dorește ca timpul de execuție să fie cât mai mic. 
-  * La folosirea unor variabile comune în mai multe handler-e de întrerupere și/sau main trebuie avut în vedere accesul concurent la acestea (în special la variabilele de 16/32 biți a căror modificare necesită mai mulți cicli de procesor). 
-  * Variabilele comune trebuie marcate ca ''​volatile''​ pentru ca accesele la acestea să nu fie optimizate de către compilator 
- 
- 
-Alte wrapper-e (în jurul unor instrucțiuni ale core-ului AVR) oferite de interfață sunt: 
- 
-  * **sei()** - setează pe 1 bitul I din SREG, activând întreruperile globale. Apelează instrucțiunea assembler //sei// 
-  * **cli()** - setează pe 0 bitul I din SREG, dezactivând întreruperile globale. Apelează instrucțiunea assembler //cli// 
-  * **reti()** - întoarcerea dintr-o rutină de tratare a intreruperii,​ activează întreruperile globale. Ar trebui sa fie ultima instrucțiune apelată într-o rutină care folosește flag-ul ISR_NAKED. 
- 
-<note important>​Întreruperile întâlnite care nu au o rutină de tratare vor cauza o întrerupere de reset!</​note>​ 
- 
- 
-===== 1. Întreruperi externe. INT vs. PCINT ===== 
-Întreruperile se pot împărți în două categorii: 
-  * întreruperile componentelor //​interne//:​ timere, interfețe seriale (USART), convertor analog-digital 
-  * întreruperile componentelor //​externe//:​ activate de pinii externi ai uC-ului 
-În [[pm:​lab:​lab2-2022|Laboratorul 2]] ați studiat mecanismul de întreruperi de pe Atmega328p și ați configurat întreruperi în contextul timer-elor. 
- 
-Pe lângă întreruperile componentelor interne uC-ului, există și câteva linii pentru întreruperi de la periferice externe: INT0-INT1 și PCINT0-PCINT2. Diferența dintre aceste două tipuri de întreruperi externe este dată de capabilitățile suportate și de granularitatea lor. 
- 
-Semnalele pentru întreruperile **INTn** pot genera o întrerupere la tranziție (crescătoare,​ descrescătoare sau ambele) sau pe nivel 0, în funcție de configurarea întreruperii. Întreruperile **PCINTn** (Pin Change INTerrupt) se declanșează la ambele tranziții (mai exact, atunci când se face toggle la valoare) și câte 8 pini sunt multiplexați pe o singură întrerupere. Semnalele de întrerupere PCINT se pot activa individual, însă pentru a afla exact ce semnal a declanșat o anumită întrerupere trebuie verificat registrul PINn corespunzător. Dacă mai multe semnale se declanșează în același timp, ele nu vor putea fi deosebite. 
- 
-<note important>​În cazul întreruperilor de tip pin change interrupt, a nu se confunda vectorul de tratare a întreruperilor ex. PCINT2 cu întreruperea efectivă ex. PCINT20 (PD4). Maparea dintre vector și întreruperi se poate găsi în secțiunea 30 pag. 278 din {{:​pm:​atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}}</​note>​ 
- 
- 
-==== Configurare ==== 
  
 În general, pașii parcurși pentru a activa o întrerupere externă INT sau PCINT sunt următorii: În general, pașii parcurși pentru a activa o întrerupere externă INT sau PCINT sunt următorii:
Line 108: Line 73:
 === Configurăm perifericul care va trimite întreruperile === === Configurăm perifericul care va trimite întreruperile ===
  
-În exemplu, pentru ''​INT0''​ și ''​INT1''​ (pinii ''​PD2''​ și ''​PD3''​),​ configurarea se face din registrul ''​EICRA''​ (External Interrupt Control Register A, secțiunea 12.2.1, pagina 54 din {{:​pm:​atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}},​ în timp ce pentru întreruperi de tip pin change se folosește registrul ''​PCICR''​. ​+De exemplu, pentru ''​INT0''​ și ''​INT1''​ (pinii ''​PD2''​ și ''​PD3''​),​ configurarea se face din registrul ''​EICRA''​ (External Interrupt Control Register A, secțiunea 12.2.1, pagina 54 din {{:​pm:​atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}},​ în timp ce pentru întreruperi de tip pin change se folosește registrul ''​PCICR''​. ​
  
 <file c> <file c>
Line 119: Line 84:
   ​   ​
 <note tip> <note tip>
-O întrerupere externă poate fi configurată astfel încât **să detecteze tranziția semnalului logic** pe front crescător, descrescător sau oricare (vezi tabelul 13-1 din {{:pm:doc8272.pdf | datasheet}}). În comparație,​ o întrerupere de tip pin change este generată la modificarea **nivelului logic** al semnalului, iar valoarea semnalului **trebuie verificată explicit în codul întreruperii**.+O întrerupere externă poate fi configurată astfel încât **să detecteze tranziția semnalului logic** pe front crescător, descrescător sau oricare (vezi tabelul 13-1 din {{:pm:atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}}). În comparație,​ o întrerupere de tip pin change este generată la modificarea **nivelului logic** al semnalului, iar valoarea semnalului **trebuie verificată explicit în codul întreruperii**.
 </​note>​ </​note>​
           ​           ​
 === Scriem rutina de tratare a întreruperii === === Scriem rutina de tratare a întreruperii ===
  
-De exemplu, pentru întreruperile de tip ''​INT0'' ​și respectiv ''​PCINT2'',​ codul va arăta în felul următor:+De exemplu, pentru întreruperile de tip ''​INT0''​respectiv ''​PCINT2'',​ codul va arăta în felul următor:
  
 <file c> <file c>
Line 145: Line 110:
  
 <note important>​ <note important>​
-Dacă avem mai multe întreruperi pe același vector ​și de ex. mai multe butoane, nu se poate determina cu exactitate care dintre butoane a generat întreruperea de tip PCINT. În plus, este posibil să apară mai multe întreruperi în timp ce starea unui pin verificat să rămână neschimbată,​ caz în care va fi detectată o nouă apăsare în mod eronat.+Dacă avem mai multe întreruperi pe același vector ​(de ex. mai multe butoane), nu se poate determina cu exactitate care dintre butoane a generat întreruperea de tip PCINT. În plus, este posibil să apară mai multe întreruperi în timp ce starea unui pin verificat să rămână neschimbată,​ caz în care va fi detectată o nouă apăsare în mod eronat.
 </​note>​ </​note>​
  
Line 165: Line 130:
  
  
 +==== Lucrul cu întreruperi ====
  
-===== 3. Afișaje cu 7 segmente=====+Reguli de programare în context întrerupere:​
  
-Afișajele LED cu 7 segmente sunt utilizate în multe aplicații ca indicatoare numerice pe panoul frontalCele mai comune aplicații sunt calculatoareleceasurile digitale, cuptoarele cu microunde, echipamentele electronice ​de laborator, cum ar fi generatoarele ​de funcții ​și contoarele de frecvență. De asemeneaeste obișnuit să existe afișaje cu 7 segmente care au un punct ce poate fi activat după cifră.+  * Nu există o valoare de returnLa închierea execuției handler-uluiprocesorul execută din nou instrucțiuni ​de unde rămăsese înainte ​de declanșarea întreruperii 
 +  * Datorită faptului că handler-ul întârzie orice altă activitate din main și inhibă execuția altor întreruperise dorește ca **timpul de execuție ​să fie cât mai mic**. 
 +  * La folosirea unor variabile comune în mai multe handler-e de întrerupere ​și/sau main trebuie avut în vedere **accesul concurent** la acestea (în special la variabilele de 16/32 biți a căror modificare necesită mai mulți cicli de procesor). 
 +  * Variabilele comune trebuie marcate ca ''​volatile''​ pentru ca accesele la acestea să nu fie optimizate de către compilator
  
-{{ :​pm:​lab:​lab2:​7segment_digit.png?​100 |}} 
  
-Un afișaj LED cu 7 segmente este un aranjament de bare LED (a, b, c, d, e, f, g, dp) care pot fi alimentate individual pentru a afișa cifre (și unele caractere). Configurația poate fi fie anod comun, fie catod comun: +Alte wrapper-e (în jurul unor instrucțiuni ale core-ului AVRoferite de interfață sunt:
-   * **Anod comun**: anozii (pozitiv) tuturor LED-urilor ​sunt conectați electric la un pin și fiecare catod (negativ) al LED-urilor are propriul pin +
-   * **Catod comun**catozii (negativ) tuturor LED-urilor sunt conectați electric la un pin și fiecare anod (pozitiv) al LED-urilor are propriul pin+
  
-{{ :​pm:​lab:​lab2:​common_cathode.png?400 |}}+  * **sei()** - setează pe 1 bitul I din SREG, activând întreruperile globale. Apelează instrucțiunea assembler //sei// 
 +  * **cli()** - setează pe 0 bitul I din SREG, dezactivând întreruperile globale. Apelează instrucțiunea assembler //cli// 
 +  * **reti()** - întoarcerea dintr-o rutină de tratare a intreruperii,​ activează întreruperile globale. Ar trebui sa fie ultima instrucțiune apelată într-o rutină care folosește flag-ul ISR_NAKED.
  
-{{ :​pm:​lab:​lab2:​common_anode.png?​400 |}}+<note important>​Întreruperile active care nu au specificată o rutină de tratare **vor cauza o întrerupere de reset**!</​note>​
  
-<note tip> Există și variante cu mai mult de o cifră integrată în componenta de afișare. Afișajele cu mai multe cifre sunt motivul pentru care catozii (sau anozii) sunt conectați, astfel încât cifrele să poată fi multiplexate și să nu folosească o grămadă de pini pentru a controla afișajul.</​note>​ 
  
-Următorul tabel de căutare poate fi util pentru programarea unui driver de afișare cu 7 segmente:+===== 2. Întreruperi externe. INT vs. PCINT ===== 
 +Întreruperile se pot împărți în două categorii: 
 +  * întreruperile componentelor //​interne//:​ timere, interfețe seriale (USART), convertor analog-digital 
 +  * întreruperile componentelor //​externe//:​ activate de pinii externi ai uC-ului
  
-^ Simbol ^ hex ^ ^ b ^ c ^ d ^ e ^ f ^ g ^| +Pe lângă întreruperile componentelor interne uC-ului, există și câteva linii pentru întreruperi de la periferice externe: **INT0-INT1** și **PCINT0-PCINT2**. Diferențdintre aceste două tipuri de întreruperi externe este dată de capabilitățile suportate și de granularitatea lor.
-| 0 | 0x7e | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +
-| 1 | 0x30 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | +
-| 2 | 0x6d | 1 | 1 | 0 | 1 | 1 | 0 | 1 | +
-| 3 | 0x79 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +
-| 4 | 0x33 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | +
-| 5 | 0x5b | 1 | 0 | 1 | 1 | 0 | 1 | 1 | +
-| 6 | 0x5f | 1 | 0 | 1 | 1 | 1 | 1 | 1 | +
-| 7 | 0x70 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | +
-| 8 | 0x7f | 1 | 1 | 1 | 1 | 1 | 1 | 1 | +
-| 9 | 0x7b | 1 | 1 | 1 | 1 | 0 | 1 | 1 |+
  
 +Semnalele pentru întreruperile **INTn** pot genera o întrerupere la tranziție (crescătoare,​ descrescătoare sau ambele) sau pe nivel 0, în funcție de configurarea întreruperii. Întreruperile **PCINTn** (Pin Change INTerrupt) se declanșează la ambele tranziții (mai exact, atunci când se face toggle la valoare) și câte 8 pini sunt multiplexați pe o singură întrerupere. Semnalele de întrerupere PCINT se pot activa individual, însă pentru a afla exact ce semnal a declanșat o anumită întrerupere trebuie verificat registrul PINn corespunzător. Dacă mai multe semnale se declanșează în același timp, ele nu vor putea fi deosebite.
  
 +<note important>​În cazul întreruperilor de tip pin change interrupt, a nu se confunda vectorul de tratare a întreruperilor ex. PCINT2 cu întreruperea efectivă ex. PCINT20 (PD4). Maparea dintre vector și întreruperi se poate găsi în secțiunea 30 pag. 278 din {{:​pm:​atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}}</​note>​
 +
 +===== 3. Întreruperi externe în Arduino =====
 +
 +Deși noi vom lucra în principal cu registre, în Arduino, întreruperile se mai pot configura folosind funcția **attachInterrupt()**.
 +Funcția primește ca parametru numărul întreruperii externe (pe Arduino UNO avem INT0, INT1), funcția care va fi apelată (echivalent ISR), și modul prin care se face detecția tranziției:​
 +
 +  * LOW: nivel logic 0
 +  * CHANGE: tranziție de nivel logic
 +  * RISING: front crescător
 +  * FALLING: front descrescător
 +
 +Dezactivarea întreruperilor externe se poate face cu funcția **detachInterrupt()**
 +Activarea sau dezactivarea temporară a tuturor întreruperilor se poate face cu funcțiile **noInterrupts()** și **interrupts()**
  
-===== 4. Exerciții ===== 
-Alegerea parametrilor pentru timer pentru setările registrelor:​ 
 <file c> <file c>
-  OCR1A = 31249; //counter +int counter = 0;
-  TCCR1B |= (1 << WGM12); ​  // CTC mode +
-  TCCR1B |= (1 << CS12); ​   // 256 prescaler  +
-  TIMSK1 |(1 << OCIE1A) // enable timer compare interrupt +
-</​file>​+
  
-Dacă frecvența de bază a procesorului este de 16 MHz și prescalerul 256 atunci timerul are frecvența de 62500 Hz deci se va incrementa cu 1 la fiecare 1/62500 Hz = 16 μs. O întrerupere va fi generată când valoarea din timer va fi egală cu cea din registrul OCR1A (31249). 16μs×31250 ​0.5s+void myCallback() 
 +  counter +1; 
 +}
  
-{{:​pm:​lab:​lab2_2022:​ex1.png?​800|}}+void setup() ​{ 
 +  pinMode(2, INPUT_PULLUP);​ 
 +  attachInterrupt(0,​ myCallback, FALLING); 
 +}
  
-**Task 1** (1pRulați exemplul din {{:pm:​lab:​lab2_2022:​task1.zip|scheletul de laborator}}. Ce mod de funcționare folosește timer-ul? Dar prescaler-ul?​+void loop() { 
 +  Serial.print("​numar apasari"); 
 +  Serial.println(counter);​ 
 +  delay(1000);​ 
 +} 
 +</​file>​
  
-**Task 2** (2p) Dorim să vizualizăm efectele prescaler-ului asupra frecvenței cu care se incrementează numărul din display-ul cu 7 segmente. Vom folosi butonul legat la PD3 pentru a cicla prin valorile posibile ale prescaler-ului. 
  
 <​hidden>​ <​hidden>​
-Schema task 1&2 +AABadd more info about debouncing, some photos from oscilloscope maybe..
-https://www.tinkercad.com/things/​ltXoLQCYr7X-ex2-schelet-lab-timer/​editel?​sharecode=wsn61JV_WbbCX-QdCVPOoFJwAm2nZ8nGxsxb_hdkpLY +mention some simple debouncing methods, like using delays 
-*nu modificati schema+</hidden>
  
-[[https://github.com/cs-pub-ro/laborator-pm/tree/master/laborator/lab2/task1_sol ​Solutie task 1&2]]+===== 4. Exerciții ===== 
 + 
 +=== Task 1 (întreruperi / butoane) - 6p ===  
 +Folosiți întreruperi externe (INT și/sau PCINT) pentru a detecta apăsarea unui buton conectat la ''​PD2''​ (pin 2) și a unuia conectat la ''​PD4''​ (pin 4). Modificați starea unui LED conectat la ''​PD7''​ (pin 7) în ISR. 
 + 
 +  * Configurați întreruperea externă (INT) pe front descrescător (falling edge) sau PCINT-ul corespunzător pinului folosit 
 +  * Modificați starea LED-ului la apăsarea oricărui buton (PD2, PD4) 
 + 
 +**Tinkercad** - Testați programul folosind următorul montaj: 
 + 
 +{{:​pm:​lab:​lab3_2021:​button_led_interrupts.png?​600|}} 
 + 
 + 
 +**Arduino** - Încărcați codul pe placă și observați comportamentul. La apăsarea butonului, LED-ul își schimbă starea o singură dată? Adăugați o metodă de [[https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce|debouncing]] pentru a detecta o singură apăsare pe buton: 
 +  * NU este recomandat să folosiți delay în ISR 
 +  * Se poate folosi funcția ​//millis()// pentru a verifica intervalul de timp de la ultima apăsare (100 ms ar trebui să fie suficient pentru un pushbutton/​microswitch,​ dar ajustați după caz) 
 + 
 + 
 +<file c> 
 +ISR(INT0_vect) 
 +
 +  // cod întrerupere externă 
 +  PORTD ^= (1 << PD7); 
 +
 + 
 +ISR(PCINT2_vect) { 
 +  // cod întrerupere de tip pin change 
 +  // TODO 
 +
 + 
 +void setup_interrupts() { 
 +  // buton 1: PD2 / INT0 
 +  // buton 2: PD4 / PCINT20 
 +  cli(); 
 + 
 +  // input 
 +  DDRD &= ~(1 << PD2) & ~(1 << PD4); 
 +  // input pullup 
 +  PORTD |= (<< PD2) | (1 << PD4); 
 + 
 +  // configurare intreruperi 
 +  // intreruperi externe 
 +  // TODO 
 + 
 +  // întreruperi de tip pin change (activare vector de întreruperi) 
 +  // TODO 
 + 
 +  // activare intreruperi 
 +  // intrerupere externa 
 +  // TODO 
 + 
 +  // întrerupere de tip pin change 
 +  // TODO 
 + 
 +  sei(); 
 +
 + 
 +void setup() { 
 +  setup_interrupts();​ 
 +  DDRD |= (1 << PD7); 
 +  PORTD &= ~(1 << PD7); 
 +
 + 
 +void loop() {   
 +   
 +
 +</​file>​ 
 + 
 +<​hidden>​ 
 +**Soluția** se găsește pe [[https://​www.tinkercad.com/​things/​cVPb9x8NUbX|Tinkercad Button Led Interrupts]]
 </​hidden>​ </​hidden>​
  
-**Task 3** (3pRealizațun cronometru de tip “countdown timer”. La finalul numărătoriiLED-ul va trebui să rămână aprins iar la apăsarea ​butonului se va reseta cronometrul ​la valoarea maximă. Valoarea maximă a cronometrului va fi de 10 secunde ​și se va afișpe display valoarea corespunzătoare ​(9..0).+=== Task (blink control- 4p ===  
 +Pe baza aceluiașmontajfolosiți întreruperi externe (INT și/sau PCINT) pentru a detecta ​apăsarea ​unui buton conectat ​la ''​PD2''​ (pin 2) și a unuia conectat la ''​PD4'' ​(pin 4). Modificați intervalul de semnalizare (blink) al unui LED conectat la ''​PD7''​ (pin 7) în ISR astfel:
  
-Bonus (1p): Aprindeți LED-ul pentru 200 ms la fiecare secundă cât timp cronometrul numără descrescător. +  * butonul conectat la ''​PD2''​ crește intervalul cu 100 ms 
-{{:​pm:​lab:​lab2_2022:​ex4.png?​1000|}}+  * butonul conectat ​la ''​PD4''​ scade intervalul cu 100 ms 
 + 
 +<file c> 
 + 
 +volatile int dt = 500; 
 + 
 +ISR(INT0_vect) 
 +
 +  // cod întrerupere externă 
 +  // TODO 
 +
 + 
 +ISR(PCINT2_vect) ​{ 
 +  // cod întrerupere de tip pin change 
 +  // TODO 
 +
 + 
 +// setup_interrupts() la fel ca la Task 1 
 + 
 +void setup() ​{ 
 +  setup_interrupts();​ 
 +  DDRD |= (1 << PD7); 
 +  PORTD &= ~(1 << PD7); 
 +} 
 + 
 +void loop() {   
 +  PORTD |= (1 << PD7); 
 +  delay(dt);​ 
 +  PORTD &= ~(1 << PD7); 
 +  delay(dt);​ 
 +} 
 + 
 +</​file>​ 
 + 
 +<​hidden>​ 
 +**Soluția** se găsește pe [[https://​www.tinkercad.com/​things/​knmCTWcDM6Q|Tinkercad Button Led Blink Interrupts]] 
 +</​hidden>​
  
-**Task 4** (4p) Conectați un buzzer la pinul PD2. Folosind notele muzicale definite în repo-ul ​ [[https://​github.com/​robsoncouto/​arduino-songs|arduino-songs]] sau biblioteca Melody, implementați un cântec la alegere. Atunci când butonul va fi apăsat, notele vor fi ridicate cu o octavă (vor reveni la normal atunci când butonul nu mai este apăsat). 
  
-Bonus (1p) Pe măsură ce cronometrul se apropie de zero, viteza de redare va fi încetinită liniar astfel încât atunci când cronometrul ajunge la 0 viteza va fi 0.1 din valoarea normală. 
  
 ===== 5. Resurse ===== ===== 5. Resurse =====
Line 237: Line 319:
   * Arduino UNO pinout   * Arduino UNO pinout
 {{:​pm:​lab:​uno.jpg?​600|pinout Arduino UNO}} {{:​pm:​lab:​uno.jpg?​600|pinout Arduino UNO}}
-  * Responsabili: ​[[St3fandascalu@gmail.com ​ | Stefan Dascalu]], ​[[alexandru.predescu@upb.ro | Alexandru Predescu]]+  * Responsabili:​ [[alexandru.predescu@upb.ro | Alexandru Predescu]]
  
-<​solution>​ 
-<​hidden>​Arhiva cu soluțiile o puteți descărca de aici: {{:​pm:​lab:​lab2_2022:​lab2-solved.zip}}</​hidden>​ 
-</​solution>​ 
  
 ===== 5. Linkuri utile ===== ===== 5. Linkuri utile =====
pm/lab/lab2-2022.1679166608.txt.gz · Last modified: 2023/03/18 21:10 by alexandru.predescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0