Dzisiaj króciutko o przerwaniach w układach z rodziny PIC18.
Układy PIC18XXXX posiadają dwa podstawowe tryby pracy. Ten domyślnie włączony po resecie mikrokontrolera pozbawiony jest priorytetów (zwany jest on przez firmę Microchip trybem kompatybilności z układami rodziny PIC16F). Drugi tryb, jeśli go włączymy, oferuje nam dwa priorytety przerwania: wysoki i niski. W przypadku domyślnego trybu mamy dostępny jeden wspólny wektor przerwania, a gdy włączymy priorytety, ilość dostępnych wektorów wzrasta do dwóch. Każde źródło przerwania kontrolowane jest przez 3 bity:
[*]bit włączający dane źródło przerwania
[*]bit ustalający priorytet przerwania dla danego źródła
[*]bit sygnalizujący wystąpienie przerwania z danego źródła
W przypadku, gdy priorytety są wyłączone, zmiana bitu priorytetu jest ignorowana i przerwanie generowane jest z priorytetem wysokim.
Wektor przerwania o wysokim priorytecie znajduje się pod adresem 0x0008, natomiast wektor o niskim priorytecie pod adresem 0x0018.
Jeśli zaś mamy włączone priorytety i podczas obsługi przerwania o niskim priorytecie wystąpi przerwanie o priorytecie wysokim, przerywana jest obsługa przerwania o niższym priorytecie, by niezwłocznie obsłużyć ważniejsze przerwanie. Po zakończeniu jego obsługi kontrola przekazywana jest z powrotem do przerwania o niższym priorytecie. Ok, a teraz czas na krótkie przykłady. Umieściłem w nich komentarze, więc wszystko powinno być czytelne i zrozumiałe.
Miganie diodą z wykorzystaniem timer0 i przerwania w trybie domyślnym, czyli bez priorytetów:
Kod: Zaznacz cały
/*
* File: main.c
* Author: Anty
*
* Created on 24 październik 2015, 14:16
*/
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>
//ustawienia bitów konfiguracyjnych dla PIC18F4550 @ 48MHz (kwarc 4MHz)
#include "mpu_config.h"
#define TMR0_RELOAD (65535 - (_XTAL_FREQ/4)/256/100)
#define TMR0_RELOAD_L (TMR0_RELOAD & 0xFF) //0x2B
#define TMR0_RELOAD_H (TMR0_RELOAD >> 8) //0xFE
/*
priorytety są wyłączone, a wszelkie włączone przerwania będą wywoływały skok
pod domyślny adres 0x0008. Nie jest wymagane określanie priorytetu w kodzie
programu
*/
void interrupt myISR(void);
//zmienna realizująca funkcję timera programowego
volatile uint8_t softTimer;
void main(void)
{
/*
inicjalizacja sprzętu - pin sterujący diodą led:
Należy zwrócić uwagę na ustawienie PBADEN w bitach konfiguracyjnych
PBADEN decyduje o trybie pracy PORTB po resecie mikrokontrolera. Jeśli
PBADEN = ON, port pracuje w trybie analogowym, a jeśli OFF, pracuje w trybie cyfrowym
Tryb pracy mozna również zmieniać w czasie wykonywania programu. Dokładny opis
znajdziesz w datasheet układu, a konkretnie w rozdziale opisującym
przetwornik ADC
*/
TRISBbits.TRISB0 = 0;//pin RB0 jako wyjście
LATBbits.LATB0 = 0;//dioda led w stanie "zgaszonym"
//inicjalizacja sprzętowego timera 0
T0CONbits.TMR0ON = 1; //włącz timer 0 (domyślnie jest włączony)
T0CONbits.T08BIT = 0; //włącz 16 bitowy tryb pracy
T0CONbits.T0CS = 0; //taktowanie z wewnętrznego źródła (f_osc/4)
T0CONbits.PSA = 0; //timer bedzie taktowany poprzez preskaler
T0CONbits.T0PS2 = 1; //preskaler 256
T0CONbits.T0PS1 = 1;
T0CONbits.T0PS0 = 1;
//włączenie przerwań
INTCONbits.TMR0IE = 1;//umożliwienie generowania przerwań przez timer0
INTCONbits.PEIE_GIEL = 1;//włączenie przerwań dla ukłądów peryferyjnych
INTCONbits.GIE_GIEH = 1;//globalne włączenie przerwań. Można uzyć makra ei();
while(true)
{
if(!softTimer)
{
//zmień stan diody na przeciwny
LATBbits.LATB0 ^= 1;
//nakręć timerek programowy, by stan diody zmieniał się około
//1 raz na sekundę
softTimer = 99;
}
}
}
void interrupt myISR(void)
{
//jeśli włączone jest przerwanie oraz ustawiona jest flaga dla timer0
if(INTCONbits.T0IE && INTCONbits.T0IF)
{
//skasuj flagę przerwania
INTCONbits.T0IF = 0;
//załaduj licznik timera, by generował przerwanie co ok. 10ms
TMR0H = TMR0_RELOAD_H;
TMR0L = TMR0_RELOAD_L;
//obsłuż naszą zmienną softTimer
if(softTimer)
{
softTimer--;
}
}
}
A teraz lekko zmodyfikowana wersja, która włącza priorytety i wykorzystuje przerwanie o niskim priorytecie. Wysoki priorytet pozostaje "pusty":
Kod: Zaznacz cały
/*
* File: main.c
* Author: Anty
*
* Created on 24 październik 2015, 14:16
*/
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>
//ustawienia bitów konfiguracyjnych dla PIC18F4550 @ 48MHz (kwarc 4MHz)
#include "mpu_config.h"
#define TMR0_RELOAD (65535 - (_XTAL_FREQ/4)/256/100)
#define TMR0_RELOAD_L (TMR0_RELOAD & 0xFF) //0x2B
#define TMR0_RELOAD_H (TMR0_RELOAD >> 8) //0xFE
/*
Priorytety będą włączone, a wszelkie włączone przerwania będą wywoływały skok
pod adres zgodny z ustawionym priorytetem dla danego źródła. Dla niskiego priorytetu
jest to 0x0018, a dla wysokiego 0x0008
*/
void low_priority interrupt my_low_ISR(void);
void high_priority interrupt my_high_ISR(void);
//zmienna realizująca funkcję timera programowego
volatile uint8_t softTimer;
void main(void)
{
/*
inicjalizacja sprzętu - pin sterujący diodą led:
Należy zwrócić uwagę na ustawienie PBADEN w bitach konfiguracyjnych
PBADEN decyduje o trybie pracy PORTB po resecie mikrokontrolera. Jeśli
PBADEN = ON, port pracuje w trybie analogowym, a jeśli OFF, pracuje w trybie cyfrowym
Tryb pracy mozna również zmieniać w czasie wykonywania programu. Dokładny opis
znajdziesz w datasheet układu, a konkretnie w rozdziale opisującym
przetwornik ADC
*/
TRISBbits.TRISB0 = 0;//pin RB0 jako wyjście
LATBbits.LATB0 = 0;//dioda led w stanie "zgaszonym"
//inicjalizacja sprzętowego timera 0
T0CONbits.TMR0ON = 1; //włącz timer 0 (domyślnie jest włączony)
T0CONbits.T08BIT = 0; //włącz 16 bitowy tryb pracy
T0CONbits.T0CS = 0; //taktowanie z wewnętrznego źródła (f_osc/4)
T0CONbits.PSA = 0; //timer bedzie taktowany poprzez preskaler
T0CONbits.T0PS2 = 1; //preskaler 256
T0CONbits.T0PS1 = 1;
T0CONbits.T0PS0 = 1;
//włączenie priorytetów
RCONbits.IPEN = 1;
//przypisanie niskiego priorytetu dla timer0
INTCON2bits.TMR0IP = 0; //timer0 wygeneruje przerwanie o niskim priorytecie
//włączenie przerwań
INTCONbits.TMR0IE = 1;//umożliwienie generowania przerwań przez timer0
INTCONbits.PEIE_GIEL = 1;//włączenie wszystkich przerwań o niskim priorytecie
//dla układów peryferyjnych
INTCONbits.GIE_GIEH = 1;//włączenie wszystkich przerwań o wysokim priorytecie
while(true)
{
if(!softTimer)
{
//zmień stan diody na przeciwny
LATBbits.LATB0 ^= 1;
//nakręć timerek programowy, by stan diody zmieniał się około
//1 raz na sekundę
softTimer = 99;
}
}
}
void low_priority interrupt my_low_ISR(void)
{
//Obsługa przerwań o niskim priorytecie. Nie zapomnij o kasowaniu flag przerwań
//jeśli włączone jest przerwanie oraz ustawiona jest flaga dla timer0
if(INTCONbits.T0IE && INTCONbits.T0IF)
{
//skasuj flagę przerwania
INTCONbits.T0IF = 0;
//załaduj licznik timera, by generował przerwanie co ok. 10ms
TMR0H = TMR0_RELOAD_H;
TMR0L = TMR0_RELOAD_L;
//obsłuż naszą zmienną softTimer
if(softTimer)
{
softTimer--;
}
}
}
void high_priority interrupt my_high_ISR(void)
{
//tutaj umieść obsługę przerwań o wysokim priorytecie
//Nie zapomnij o kasowaniu flag przerwań
}
Oba programy zostały przeze mnie skompilowane i sprawdzone w fizycznym układzie PIC18F4550. Do kompletu załączam jeszcze zawartość pliku mpu_config.h, który został wykorzystany w obu programach:
Kod: Zaznacz cały
/*
* File: mpu_config.h
* Author: Anty
*
* Created on 24 październik 2015, 14:20
*/
#ifndef MPU_CONFIG_H
#define MPU_CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif
// CONFIG1L
#pragma config PLLDIV = 1 // PLL Prescaler Selection bits (No prescale (4 MHz oscillator input drives PLL directly))
#pragma config CPUDIV = OSC1_PLL2// System Clock Postscaler Selection bits ([Primary Oscillator Src: /1][96 MHz PLL Src: /2])
#pragma config USBDIV = 1 // USB Clock Selection bit (used in Full-Speed USB mode only; UCFG:FSEN = 1) (USB clock source comes directly from the primary oscillator block with no postscale)
// CONFIG1H
#pragma config FOSC = HSPLL_HS // Oscillator Selection bits (HS oscillator, PLL enabled (HSPLL))
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)
// CONFIG2L
#pragma config PWRT = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOR = ON // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
#pragma config BORV = 3 // Brown-out Reset Voltage bits (Minimum setting)
#pragma config VREGEN = OFF // USB Voltage Regulator Enable bit (USB voltage regulator disabled)
// CONFIG2H
#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config WDTPS = 32768 // Watchdog Timer Postscale Select bits (1:32768)
// CONFIG3H
#pragma config CCP2MX = ON // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = OFF // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)
#pragma config LPT1OSC = OFF // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = ON // MCLR Pin Enable bit (MCLR pin enabled; RE3 input pin disabled)
// CONFIG4L
#pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = OFF // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config ICPRT = OFF // Dedicated In-Circuit Debug/Programming Port (ICPORT) Enable bit (ICPORT disabled)
#pragma config XINST = OFF // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled)
// CONFIG5L
#pragma config CP0 = OFF // Code Protection bit (Block 0 (000800-001FFFh) is not code-protected)
#pragma config CP1 = OFF // Code Protection bit (Block 1 (002000-003FFFh) is not code-protected)
#pragma config CP2 = OFF // Code Protection bit (Block 2 (004000-005FFFh) is not code-protected)
#pragma config CP3 = OFF // Code Protection bit (Block 3 (006000-007FFFh) is not code-protected)
// CONFIG5H
#pragma config CPB = OFF // Boot Block Code Protection bit (Boot block (000000-0007FFh) is not code-protected)
#pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM is not code-protected)
// CONFIG6L
#pragma config WRT0 = OFF // Write Protection bit (Block 0 (000800-001FFFh) is not write-protected)
#pragma config WRT1 = OFF // Write Protection bit (Block 1 (002000-003FFFh) is not write-protected)
#pragma config WRT2 = OFF // Write Protection bit (Block 2 (004000-005FFFh) is not write-protected)
#pragma config WRT3 = OFF // Write Protection bit (Block 3 (006000-007FFFh) is not write-protected)
// CONFIG6H
#pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) are not write-protected)
#pragma config WRTB = OFF // Boot Block Write Protection bit (Boot block (000000-0007FFh) is not write-protected)
#pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM is not write-protected)
// CONFIG7L
#pragma config EBTR0 = OFF // Table Read Protection bit (Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF // Table Read Protection bit (Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF // Table Read Protection bit (Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF // Table Read Protection bit (Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks)
// CONFIG7H
#pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) is not protected from table reads executed in other blocks)
#ifdef __cplusplus
}
#endif
#endif /* MPU_CONFIG_H */
I to chyba tyle... Życzę miłego mrygania