How to Program Interrupts in PIC16F877A

There are 15 interrupts available in PIC16F877A controller. I will try to demonstrate individual interrupt so that you can easily understand. The list is the following.
1. External Interrupt
2. RB Port Change Interrupt
3. Timer 0 Interrupt
4. Timer 1 Interrupt
5. Parallel Slave Port Read/Write Interrupt
6. A/D Converter Interrupt
7. USART Receive Interrupt
8. USART Transmit Interrupt
9. SPI (Slave) Interrupt
9. Synchronous Serial Port Interrupt
10. CCP1 (Capture, Compare, PWM) Interrupt
11. CCP2  (Capture, Compare, PWM) Interrupt
12. TMR2 to PR2 Match Interrupt
13. Comparator Interrupt
14. EEPROM Write Operation Interrupt
15. Bus Collision Interrupt


1. External Interrupt (RB0)

The following code demonstrates how to use external interrupt (RB0). A push button is connected to RB0/INT, when push button is pressed a high to low signal is generated. As in option register external interrupt is configured on falling edge so interrupt is generated and interrupt service routine is called where an LED connected on RD0 changed its state. The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.


1.1 Code in mikroC

sbit INT_STATUS_Dir at TRISD.B0;
sbit INT_STATUS at PORTD.B0;
void InterruptInit(void);
void main(void)
{
unsigned short count = 0x00;
// make portc as output
TRISC = 0x00;
// portc init to zero
PORTC = 0x00;
// make portd bit-0 as output
INT_STATUS_Dir = 0;
// portd bit-0 init to zero
INT_STATUS = 0;
// interrupt enable and init
InterruptInit();
while(1)
{
PORTC = count++;
Delay_ms(100);
}
}
// reserve for interrupt service routine
void interrupt(void)
{
if(INTCON.INTF == 1)
{
// RB0/INT External Interrupt Disable
INTCON.INTE = 0;
// invert when interrupt is called
INT_STATUS = ~INT_STATUS;
// must be cleared int flag
INTCON.INTF = 0;
// RB0/INT External Interrupt Enable
INTCON.INTE = 1;
}
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// Peripheral Interrupt Enable
INTCON.PEIE = 0;
// RB0/INT External Interrupt Enable
INTCON.INTE = 1;
// RB0/INT External Interrupt Flag clear
INTCON.INTF = 0;
// Interrupt Edge Select bit
// 1 = rising edge
// 0 = falling edge
OPTION_REG.INTEDG = 0;
}

1.2 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

2. RB Change Interrupt

The following code demonstrate how to use PORTB change interrupt. The change interrupt is available on PORTB bit-4 to bit-7. The interrupt is generated when high to low or low to high change on any of the PORTB bit-4 to bit-7.
It is the best practice of coding to stay for a minimum time in interrupt service routine (ISR). To achieve this by using flags they are updated in ISR and further more used in continuous loop. The interrupt status is shown on PORTD bit-4 to bit-7.
The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.


2.1 Code in mikroC

// demonstrate status on portd b7:4
sbit INT_RB4_Dir at TRISD.B4;
sbit INT_RB5_Dir at TRISD.B5;
sbit INT_RB6_Dir at TRISD.B6;
sbit INT_RB7_Dir at TRISD.B7;
sbit INT_RB4 at PORTD.B4;
sbit INT_RB5 at PORTD.B5;
sbit INT_RB6 at PORTD.B6;
sbit INT_RB7 at PORTD.B7;
void InterruptInit(void);
// global variables
unsigned short flag_rb4 = 0;
unsigned short flag_rb5 = 0;
unsigned short flag_rb6 = 0;
unsigned short flag_rb7 = 0;
void main(void)
{
unsigned short count = 0x00;
// make portc as output
TRISC = 0x00;
// portc init to zero
PORTC = 0x00;
// make portd bit-7:4 as output
INT_RB4_Dir = 0;
INT_RB5_Dir = 0;
INT_RB6_Dir = 0;
INT_RB7_Dir = 0;
// portd bit-7:4 init to zero
INT_RB4 = 0;
INT_RB5 = 0;
INT_RB6 = 0;
INT_RB7 = 0;
// interrupt enable and init
InterruptInit();
while(1)
{
// best approach to use interrupt flag in
// continous loop, to avoid ISR overloading
// because its optimized to stay minimum time
// in interrupt service routine
INT_RB4 = flag_rb4;
INT_RB5 = flag_rb5;
INT_RB6 = flag_rb6;
INT_RB7 = flag_rb7;
// other task
PORTC = count++;
Delay_ms(100);
}
}
// reserve for interrupt service routine
void interrupt(void)
{
if(INTCON.RBIF == 1)
{
// RB Port Change Interrupt Disable
INTCON.RBIE = 0;
// read RB7:4 status and update according flags
flag_rb4 = PORTB.B4;
flag_rb5 = PORTB.B5;
flag_rb6 = PORTB.B6;
flag_rb7 = PORTB.B7;
// must be clear flag
INTCON.RBIF = 0;
// RB Port Change Interrupt Enable
INTCON.RBIE = 1;
}
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// RB Port Change Interrupt Enable
INTCON.PEIE = 0;
// RB Port Change Interrupt Enable
INTCON.RBIE = 1;
// RB Port Change Interrupt Flag
INTCON.RBIF = 0;
}

2.2 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

3. Timer-0 Interrupt

The following code demonstrate how to use timer-0 interrupt. There are two major purpose of timer interrupt, first one is to generate precise clock for any external device and second one is where multi threading based embedded software is required. In the following code four threads are implemented and each thread is executed on its own time.
It is the best practice of coding to stay for a minimum time in interrupt service routine (ISR). To achieve this by using flags they are updated in ISR and further more used in continuous loop.
The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.


3.1 Code in mikroC

/*
Fout(Hz) = [Fosc(Hz)]/[4 x Prescalar]
Tick(Sec) = 1/Fout(Hz)
// Fosc(Hz) = 8000000(8Mhz) crystal frequency
// Prescalar = 2,  the other options are:
// 1:2
// 1:4
// 1:8
// 1:16
// 1:32
// 1:64
// 1:128
// 1:256
Fout(Hz) = 8000,000/[4x2]
Fout(Hz) = 1000,000
Tick(Sec) = 1/1000,000
Tick(uSec) = 1 uSec (timer-0 per increment time)
if reload TMR0 = 55, now interrupt will generate
every after 200 uSec
*/
sbit LED1_Dir at TRISB.B0;
sbit LED2_Dir at TRISB.B1;
sbit LED3_Dir at TRISB.B2;
sbit LED4_Dir at TRISB.B3;
sbit LED1 at PORTB.B0;
sbit LED2 at PORTB.B1;
sbit LED3 at PORTB.B2;
sbit LED4 at PORTB.B3;
void T0Config(void);
void InterruptInit(void);
// global variables
unsigned int thread1_time = 0x00;
unsigned int thread2_time = 0x00;
unsigned int thread3_time = 0x00;
unsigned int thread4_time = 0x00;
void main(void)
{
// make as output
LED1_Dir = 0;
LED2_Dir = 0;
LED3_Dir = 0;
LED4_Dir = 0;
// init to zero
LED1 = 0;
LED2 = 0;
LED3 = 0;
LED4 = 0;
// init timer-0
T0Config();
// interrupt enable and init
InterruptInit();
while(1)
{
// 1 mSec thread 200 uSec x 5 = 1 mSec
if(thread1_time >= 5)
{
LED1 = ~LED1;
thread1_time = 0;
}
// 100 mSec thread 200 uSec x 500 = 100 mSec
if(thread2_time >= 500)
{
LED2 = ~LED2;
thread2_time = 0;
}
// 200 mSec thread 200 uSec x 1000 = 200 mSec
if(thread3_time >= 1000)
{
LED3 = ~LED3;
thread3_time = 0;
}
// 500 mSec thread 200 uSec x 2500 = 500 mSec
if(thread4_time >= 2500)
{
LED4 = ~LED4;
thread4_time = 0;
}
}
}
// reserve for interrupt service routine
void interrupt(void)
{
if(INTCON.TMR0IF == 1)
{
// increment after 200 uSec
thread1_time++;
thread2_time++;
thread3_time++;
thread4_time++;
// must be cleared int flag
INTCON.TMR0IF = 0;
// reload value 55 and +10 uSec for other instruction
// time consuption
TMR0 = 55+10;
}
}
void T0Config(void)
{
TMR0 = 55+10; // timer0 register init
OPTION_REG.T0CS = 0; // internal clock select
OPTION_REG.T0SE = 0; // Low to High Edge
OPTION_REG.PSA = 0; // prescaler is assigned to the Timer0 module
OPTION_REG.PS0 = 0; //----------------------
OPTION_REG.PS1 = 0; // prescalar 2 select
OPTION_REG.PS2 = 0; //----------------------
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// Peripheral Interrupt Enable
INTCON.PEIE = 0;
// Timer 0 Overflow Interrupt Enable
INTCON.TMR0IE = 1;
// Timer 0 Overflow Interrupt Flag
INTCON.TMR0IF = 0;
}

3.2 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

4. Timer-1 Interrupt

The following code demonstrate how to use timer-1 interrupt. There are two major purpose of timer interrupt, first one is to generate precise clock for any external device and second one is where multi threading based embedded software is required. In the following code four threads are implemented and each thread is executed on its own time.
It is the best practice of coding to stay for a minimum time in interrupt service routine (ISR). To achieve this by using flags they are updated in ISR and further more used in continuous loop.
The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.


4.1 Code in mikroC

/*
Fout(Hz) = [Fosc(Hz)]/[4 x Prescalar]
Tick(Sec) = 1/Fout(Hz)
// Fosc(Hz) = 8000000(8Mhz) crystal frequency
// Prescalar = 2,  the all options are:
// 1:1
// 1:2
// 1:4
// 1:8
Fout(Hz) = 8000,000/[4x2]
Fout(Hz) = 1000,000
Tick(Sec) = 1/1000,000
Tick(uSec) = 1 uSec (timer-1 per increment time)
if reload TMR1 = 65,435, now interrupt will generate
every after 100 uSec
*/
sbit LED1_Dir at TRISB.B0;
sbit LED2_Dir at TRISB.B1;
sbit LED3_Dir at TRISB.B2;
sbit LED4_Dir at TRISB.B3;
sbit LED1 at PORTB.B0;
sbit LED2 at PORTB.B1;
sbit LED3 at PORTB.B2;
sbit LED4 at PORTB.B3;
// functions proto-types
void T1Config(void);
void InterruptInit(void);
// global variables
unsigned int thread1_time = 0x00;
unsigned int thread2_time = 0x00;
unsigned int thread3_time = 0x00;
unsigned int thread4_time = 0x00;
void main(void)
{
// make as output
LED1_Dir = 0;
LED2_Dir = 0;
LED3_Dir = 0;
LED4_Dir = 0;
// init to zero
LED1 = 0;
LED2 = 0;
LED3 = 0;
LED4 = 0;
// init timer-1
T1Config();
// interrupt enable and init
InterruptInit();
while(1)
{
// 1 mSec thread 100 uSec x 10 = 1 mSec
if(thread1_time >= 10)
{
LED1 = ~LED1;
thread1_time = 0;
}
// 2 mSec thread 100 uSec x 20 = 2 mSec
if(thread2_time >= 20)
{
LED2 = ~LED2;
thread2_time = 0;
}
// 5 mSec thread 100 uSec x 50 = 5 mSec
if(thread3_time >= 50)
{
LED3 = ~LED3;
thread3_time = 0;
}
// 10 mSec thread 100 uSec x 100 = 10 mSec
if(thread4_time >= 100)
{
LED4 = ~LED4;
thread4_time = 0;
}
}
}
// reserve for interrupt service routine
void interrupt(void)
{
if(PIR1.TMR1IF == 1)
{
// increment after 100 uSec
thread1_time++;
thread2_time++;
thread3_time++;
thread4_time++;
// must be cleared int flag
PIR1.TMR1IF = 0;
// reload value 65,435 (65535-65435 = 100)
// Add +15 with 65,435 for other instruction
// time consuption, new value is 0xFFAA (65,450)
TMR1H = 0xFF; // load higher byte
TMR1L = 0xAA; // load lower byte
}
}
void T1Config(void)
{
TMR1H = 0xFF; // load higher byte
TMR1L = 0x9B; // load lower byte
T1CON.T1CKPS1 = 0; // 1:2 prescale value
T1CON.T1CKPS0 = 1; // 1:2 prescale value
T1CON.T1OSCEN = 1; // Oscillator is enabled
T1CON.T1SYNC  = 0; // This bit is ignored
T1CON.TMR1CS  = 0; // Internal clock (FOSC/4)
T1CON.TMR1ON  = 1; // Enables Timer1
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// Peripheral Interrupt Enable
INTCON.PEIE = 1;
// Timer 1 Overflow Interrupt Enable
PIE1.TMR1IE = 1;
// Timer 1 Overflow Interrupt Flag
PIR1.TMR1IF = 0;
}

4.2 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

5. PSP Read and Write Interrupt

The following code demonstrates how to use parallel slave port on interrupt request.
The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.
Data Read Request
 Data Write Request

5.1 Code in mikroC (UUT)

// function proto-type
void interrupt(void);
void PSP_Setting(void);
void InterruptInit(void);
void main(void)
{
// portb as input port
TRISB = 0xFF;
// portc as output port
TRISC = 0x00;
// portc init to 0x00
PORTC = 0x00;
PSP_Setting();
InterruptInit();
while(1)
{
// polling code here
}
}
void interrupt(void)
{
// write request to PSP
if(TRISE.IBF == 1)
{
PORTC = PORTD;
// auto clear TRISE.IBF
}
// read request from PSP
// TRISE.IBF == 0
else
{
PORTD = PORTB;
}
}
void PSP_Setting(void)
{
// select porte as digital i/o
ADCON1.PCFG3 = 0;
ADCON1.PCFG2 = 0;
ADCON1.PCFG1 = 1;
ADCON1.PCFG0 = 0;
// enable PSP mode
TRISE.PSPMODE = 1;
// clear input buffer overflow error
TRISE.IBOV = 0;
// clear output buffer full bit
TRISE.OBF = 0;
// clear input buffer full bit
TRISE.IBF = 0;
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// Peripheral Interrupt Enable
INTCON.PEIE = 1;
// PSP interrupt enable
PIE1.PSPIE = 1;
// Clear PSP interrupt
PIR1.PSPIF = 0;
}

5.2 Code in mikroC (STIM)

sbit nRD_Dir at TRISB.B0;
sbit nWR_Dir at TRISB.B1;
sbit nCS_Dir at TRISB.B2;
sbit WR_RD_SEL_Dir at TRISB.B7;
sbit nRD at PORTB.B0;
sbit nWR at PORTB.B1;
sbit nCS at PORTB.B2;
sbit WR_RD_SEL at PORTB.B7;
//void PSP_Setting(void);
void WriteActive(void);
void ReadActive(void);
void IdleBus(void);
void main(void)
{
unsigned char count = 0x00;
unsigned char read_buf = 0x00;
// direction as output
nRD_Dir = 0;
nWR_Dir = 0;
nCS_Dir = 0;
// direction as input
WR_RD_SEL_Dir = 1;
// init to 1 because all signals are
// active low
nRD = 1;
nWR = 1;
nCS = 1;
// portc as output port
TRISC = 0x00;
// portc init to 0x00
PORTC = 0x00;
// make as output
TRISD = 0x00;
Delay_ms(1);
// pulse to init PSP
WriteActive();
while(1)
{
if(WR_RD_SEL == 1)
{
// make as output
TRISD = 0x00;
Delay_ms(1);
// data write to PSP
WriteActive();
PORTD = count++;
Delay_ms(5);
// idle bus
IdleBus();
Delay_ms(10);
}
if(WR_RD_SEL == 0)
{
// make as input
TRISD = 0xFF;
Delay_ms(1);
// data read from PSP
ReadActive();
read_buf = PORTD;
PORTC = read_buf;
Delay_ms(5);
// idle bus
IdleBus();
Delay_ms(10);
}
}
}
void PSP_Setting(void)
{
// select porte as digital i/o
ADCON1.PCFG3 = 0;
ADCON1.PCFG2 = 0;
ADCON1.PCFG1 = 1;
ADCON1.PCFG0 = 0;
// enable PSP mode
TRISE.PSPMODE = 1;
// clear input buffer overflow error
TRISE.IBOV = 0;
// clear output buffer full bit
TRISE.OBF = 0;
// clear input buffer full bit
TRISE.IBF = 0;
}
void WriteActive(void)
{
nRD = 1;
nWR = 0;
nCS = 0;
Delay_us(100);
}
void ReadActive(void)
{
nRD = 0;
nWR = 1;
nCS = 0;
Delay_us(100);
}
void IdleBus(void)
{
nRD = 1;
nWR = 1;
nCS = 1;
Delay_us(100);
}

5.3 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

6. ADC Interrupt

The following code demonstrates how to use ADC interrupt. The benefit of ADC interrupt is that when conversion is complete, ADC generate an interrupt and in the ISR just read the converted value.
The following code read the channel-0 of ADC and digital value is send to the PORTB and PORTC, also convert digital value to analog and send to serial port. 
The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.
To run the simulation, you have to set the following configuration of debug in Proteus ISIS.



6.1 Code in mikroC

// functions proto-types
// vref = vdd (+5v)
#define VREF_P 5.0
// VREF/1024
#define STEP_V 0.0048828125
void ADCInit(void);
void InterruptInit(void);
// global variables
unsigned int adc_value = 0;
unsigned adc_get_data = 0;
char adc_data_str[14];
void main(void)
{
float adc_vin = 0.0;
// set direction as output
TRISB = 0x00;
TRISC = 0xF0;
// as input
TRISA = 0xFF;
// init to zero
PORTB = 0x00;
PORTC = 0xF0;
// initialize hardware UART1
// establish communication at 9600 bps
UART1_Init(9600);
// Initialize ADC module with default settings
ADCInit();
// must be call after ADC_Init()
InterruptInit();
while(1)
{
if(adc_get_data == 1)
{
PORTB = adc_value;
PORTC = (adc_value>>8) & 0x03;
adc_get_data = 0;
// sample value to analog i/p voltage conversion
adc_vin = adc_value * STEP_V;
// double to string conversion
FloatToStr(adc_vin,adc_data_str);
// if the previous data has been shifted out, send next data:
if (UART1_Tx_Idle() == 1)
{
  UART1_Write_Text("Voltage on CH0: ");
  UART1_Write_Text(adc_data_str);
  UART1_Write('\r');
  UART1_Write('\n');
}
}
}
}
// reserve for interrupt service routine
void interrupt(void)
{
if(PIR1.ADIF == 1)
{
// ADC Interrupt Disable
PIE1.ADIE = 0;
// read analog value from ADC module channel 0
adc_value = (ADRESH << 8) | ADRESL;
adc_get_data = 1;

// clear adc flag
PIR1.ADIF = 0;
// ADC Interrupt Enable
PIE1.ADIE = 1;
// Enable convertion GO/DONE bit
ADCON0 |= 0b00000100;
}
}
void ADCInit(void)
{
// Right justified. Six (6) Most Significant bits
// of ADRESH are read as ‘0’.
ADCON1.ADFM = 1;
// AN7-AN0 analog,
// VREF+ = VDD, and VREF- = VSS
ADCON1.PCFG3 = 0;
ADCON1.PCFG2 = 0;
ADCON1.PCFG1 = 0;
ADCON1.PCFG0 = 0;
// FRC
// (clock derived from the internal A/D RC oscillator)
ADCON1.ADCS2 = 0;
ADCON0.ADCS1 = 1;
ADCON0.ADCS0 = 1;
// Analog Channel Select bits
ADCON0.CHS2 = 0;
ADCON0.CHS1 = 0;
ADCON0.CHS0 = 0;
// A/D converter module is powered up
ADCON0.ADON = 1;
Delay_us(1);
// Enable convertion GO/DONE bit
ADCON0 |= 0b00000100;
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// Peripheral Interrupt Enable
INTCON.PEIE = 1;
// ADC Interrupt Enable
PIE1.ADIE = 1;
// ADC Converter Interrupt Flag bit
PIR1.ADIF = 0;
}

6.2 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

7. USART Receive Interrupt

The following code demonstrates how to use USART receiver interrupt.
The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.


7.1 Code in mikroC

// functions proto-types
void InterruptInit(void);
// global variables
unsigned usart_get_data = 0;
char usart_rcv_char = '\0';
unsigned short count = 0;
void main(void)
{
// set direction as output
TRISB = 0x00;
// init to zero
PORTB = 0x00;
// initialize hardware UART1
// establish communication at 9600 bps
UART1_Init(9600);
// must be call after UART1_Init();
InterruptInit();
// info message
if (UART1_Tx_Idle() == 1)
{
UART1_Write_Text("Enter I(i) for Increment or D(d) for Decrement Counter\r\n");
}
while(1)
{
if(usart_get_data == 1)
{
PORTB = usart_rcv_char;
// inc counter
if((usart_rcv_char == 'I') || (usart_rcv_char == 'i'))
{
PORTB = ++count;
}
// dec counter
else if((usart_rcv_char == 'D') || (usart_rcv_char == 'd'))
{
PORTB = --count;
}
else
{
if (UART1_Tx_Idle() == 1)
{
  UART1_Write_Text("Enter I(i) for Increment or D(d) for Decrement Counter\r\n");
}
}
// flag reset
usart_get_data = 0;
}
}
}
// reserve for interrupt service routine
void interrupt(void)
{
// usart receive interrupt
if(PIR1.RCIF == 1)
{
// USART Receiver Interrupt Disable
PIE1.RCIE = 0;
// received usart data
usart_rcv_char = UART1_Read();
// set data received flag
usart_get_data = 1;
// reset flag (optional)
PIR1.RCIF = 0;
// USART Receiver Interrupt Enable
PIE1.RCIE = 1;
}
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// Peripheral Interrupt Enable
INTCON.PEIE = 1;
// USART Receiver Interrupt Enable
PIE1.RCIE = 1;
// USART Receiver Interrupt Flag bit
PIR1.RCIF = 0;
}

7.2 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

8. USART Transmitter Interrupt

The following code demonstrates how to use USART transmitter interrupt.
The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.


8.1 Code in mikroC

// functions proto-types
void InterruptInit(void);
void UARTTX_IEnable(void);
void UARTTX_IDisable(void);
// global variables
char txd_buffer[32] = "\0";
unsigned short buffer_count = 0;
int txd_buffer_length = 0;
void main(void)
{
// set direction as output
TRISB = 0x00;
// init to zero
PORTB = 0x00;
// initialize hardware UART1
// establish communication at 9600 bps
UART1_Init(9600);
// must be call after UART1_Init();
InterruptInit();
strcpy(txd_buffer,"hellow world\r\n");
txd_buffer_length = strlen(txd_buffer);
while(1)
{
PORTB = 0x55;
Delay_ms(500);
PORTB = 0xAA;
Delay_ms(500);
buffer_count = 0;
// enable usart txd interrupt
UARTTX_IEnable();
}
}
// reserve for interrupt service routine
void interrupt(void)
{
// usart transmit interrupt
if(PIR1.TXIF == 1)
{
if(buffer_count < txd_buffer_length)
{
UART1_Write(txd_buffer[buffer_count]);
// inc counter for next character
buffer_count++;
}
else
{
// disable usart txd interrupt
UARTTX_IDisable();
}
// rteset interrupt flag (optional)
PIR1.TXIF = 0;
}
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// Peripheral Interrupt Enable
INTCON.PEIE = 1;
// USART Transmit Interrupt Disable
PIE1.TXIE = 0;
// USART Transmit Interrupt Flag bit
PIR1.TXIF = 0;
}
// enable usart tx interrupt
void UARTTX_IEnable(void)
{
// USART Transmit Interrupt Enable
PIE1.TXIE = 1;
}
// disable usart tx interrupt
void UARTTX_IDisable(void)
{
// USART Transmit Interrupt Disable
PIE1.TXIE = 0;
}

8.2 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

9. SPI (Slave) Interrupt

The following code demonstrates how to use SPI interrupt in slave mode.
The code is written in “mikroC PRO for PIC v.6.6.3” IDE and simulation is done with Proteus 8.0 SP0. At the end of code, you can find complete project files for download.

9.1 Code in mikroC

// set direction
sbit LED_Dir at TRISB.B0;
sbit SS_Dir at TRISA.B5;
sbit SCLK_Dir at TRISC.B3;
sbit SDO_Dir at TRISC.B5;
sbit SDI_Dir at TRISC.B4;
sbit LED at PORTB.B0;
// global variable
unsigned short receive_data = 0x00;
// function prototype
void InterruptInit(void);
void main(void)
{
// set as output
LED_Dir = 0;
SCLK_Dir = 0;
SDO_Dir = 0;
TRISD = 0x00;
PORTD = 0x00;
// set as input
SDI_Dir = 1;
SS_Dir = 1;
// Set SPI1 module to slave mode,
// Slave select enabled, 
// data sampled at the middle of interval, 
// clock idle state low,
// data transmitted at low to high edge
SPI1_Init_Advanced(
_SPI_SLAVE_SS_ENABLE,
_SPI_DATA_SAMPLE_MIDDLE,
_SPI_CLK_IDLE_LOW, 
_SPI_LOW_2_HIGH
);
InterruptInit();
Delay_ms(1);
while(1)
{
//do something here
// cpu health led
LED = ~LED;
Delay_ms(500);
}
}
// reserve for interrupt service routine
void interrupt(void)
{
// SSP interrupt
if(PIR1.SSPIF == 1)
{
// save data to a temp buffer
receive_data = SSPBUF;
// send received data to portd
PORTD = receive_data;
// reset interrupt flag
PIR1.SSPIF = 0;
}
}
void InterruptInit(void)
{
// Global Interrupt Enable
INTCON.GIE = 1;
// Peripheral Interrupt Enable
INTCON.PEIE = 1;
// Synchronous Serial Port Interrupt Enable bit
PIE1.SSPIE = 1;
// SSP Interrupt Flag bit
PIR1.SSPIF = 0;
}

9.2 Download Files

For download “mikroC PRO for PIC” project and “Proteus 8.0” simulation files, click here.

Comments

Popular posts from this blog

How to Program Parallel Slave Port (PSP) in PIC16F877A

How to Program SPI in PIC16F877A (Slave Mode)