Hi Buddies!
In our last class, we built a digital clock without using interrupts — and yes, it worked well!
But you might have noticed a small flicker when you pressed a button. Today, let’s solve that using interrupts. But before that, let’s understand what interrupts are.
Blocking Code Method
In earlier classes, especially when blinking LEDs, we used a delay inside the main loop. That delay blocks the system — during this time, the microcontroller doesn’t do anything else.
This method is called the blocking method. The microcontroller is “stuck” in the delay and can’t respond to anything else until it finishes.
This is why our display flickered when buttons were pressed in the last version. Let’s fix it now!
What is an Interrupt?
Instead of waiting and blocking, we want the microcontroller to do its usual work. But when something important (like a timer tick or button press) happens, the system should pause its normal job, handle the event, and then resume.
That’s exactly what an interrupt does.
Each interrupt has a special vector address in memory where its code is stored. When an interrupt occurs, the microcontroller jumps to that address, runs the code, and returns to the main program.
Interrupts in 8051
The 8051 has five types of interrupts:
- External Interrupt 0 (INT0)
- Timer 0 Interrupt (TIMER0)
- External Interrupt 1 (INT1)
- Timer 1 Interrupt (TIMER1)
- Serial Interrupt (UART)
These interrupts have default priority:
- INT0 (Highest)
- TIMER0
- INT1
- TIMER1
- SERIAL (Lowest)
If two interrupts occur at the same time, the microcontroller handles the higher-priority one first.
What is a Vector Address?
Each interrupt has a fixed vector address:
Interrupt | Vector Address | ISR Number |
---|---|---|
INT0 | 0003H | 0 |
TIMER0 | 000BH | 1 |
INT1 | 0013H | 2 |
TIMER1 | 001BH | 3 |
SERIAL | 0023H | 4 |
In SDCC, you use the __interrupt(n)
keyword to define an interrupt function.
1 2 3 | void my_interrupt_handler() __interrupt(2) { // Your interrupt code here } |
You don’t need to handle jump/return manually; the compiler takes care of it.
Enabling Interrupts
To use interrupts, you must:
- Enable the specific interrupt (like
ET0
for Timer 0). - Enable the global interrupt flag (
EA = 1;
).
Timer 0 for 4ms Interrupt
We want to refresh our display every 4ms using Timer 0 in Mode 1 (16-bit).
Here’s the math:
- 1 tick ≈ 1.085 μs
- 4 ms = 4000 μs → 4000 / 1.085 ≈ 3686 ticks
- 65536 – 3686 = 61850 = 0xF1DA
So we preload TH0 = 0xF1 and TL0 = 0xDA.
Final Clock Code (Using Timer Interrupt)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | #include <8051.h> #include "../library/Delay/Delay.h" #include "../library/DS1307/DS1307.h" code unsigned char lookUpTable[10] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 }; u8 segmentBuff[4]; u8 disPos = 0; u16 count = 0; // Timer 0 Interrupt Routine void Timer0_ISR(void) __interrupt(1) { TH0 = 0xF1; TL0 = 0xDA; P2 |= 0x0F; // Turn off all digits P0 = segmentBuff[disPos]; P2_0 = (disPos != 0); P2_1 = (disPos != 1); P2_2 = (disPos != 2); P2_3 = (disPos != 3); disPos++; if (disPos > 3) disPos = 0; if (count) count--; } // Timer 0 Initialization void Timer0_Init() { TMOD &= 0xF0; TMOD |= 0x01; // Timer 0 Mode 1 TH0 = 0xF1; TL0 = 0xDA; ET0 = 1; TR0 = 1; EA = 1; } void main() { RTC_Init(); Timer0_Init(); while (1) { if (!count) { RTC_Read(); segmentBuff[0] = lookUpTable[hours / 10]; segmentBuff[1] = lookUpTable[hours % 10]; segmentBuff[2] = lookUpTable[minutes / 10]; segmentBuff[3] = lookUpTable[minutes % 10]; count = 250; // 250 × 4ms = 1 sec } P1_0 = (count > 125); // Toggle LED every 0.5 sec if (!P3_0) { DelayXms(1); while (!P3_0) DelayXms(1); hours++; if (hours > 23) hours = 0; RTC_Write(); count = 0; } if (!P3_1) { DelayXms(1); while (!P3_1) DelayXms(1); minutes++; if (minutes > 59) minutes = 0; RTC_Write(); count = 0; } } } |
Summary
- We moved the display scanning to a timer interrupt.
- This removes flicker and allows other logic (RTC read, key press) to run smoothly.
- This is the final version of our 7-segment clock project using interrupts.
If you want the previous non-interrupt version, check out Class 10.
Next class, we’ll go deeper into optimizing or customizing interrupt priorities!
Happy Building, Buddies!
Let’s keep learning and creating!
Leave Your Reply
You must be logged in to post a comment.