Introduction to 8051 Microcontroller Programming
Overview
The 8051 microcontroller series represents a foundational architecture in embedded systems, featuring an 8-bit CPU, 128 bytes of internal RAM, and up to 64KB of program memory. It operates using a Harvard architecture that separates instruction and data buses.
Core Components
- CPU: Central processing unit capable of executing 8-bit operations
- Memory: Internal ROM/Flash for code storage and RAM for data handling
- Timers/Counters: Two 16-bit timers/counters for time measurement
- Serial Communication: UART interface for serial data transfer
- Interrupt System: Support for hardware and software interrupts
- I/O Ports: Multiple ports configurable for input/output operations
Development Environment Setup
Tools Required
- Keil uVision5: Integrated development environment for C programming
- STC_ISP: Programming software for flashing code into microcontrollers
- USB-to-Serial Converter: For communication between computer and microcontroller
Hardware Configuration
Ensure proper driver installation for USB-to-Serial converters. Create project directories within Keil and configure target settings for specific chip models.
Peripheral Module Implementation
LED Control
Basic Lighting
#include <reg52.h>
void main() {
P2 = 0xFE; // Turn on first LED
while(1); // Infinite loop
}
Blinking Effect
#include <reg52.h>
void delay_ms(unsigned int ms) {
unsigned char i, j;
while(ms--) {
for(i=2; i>0; i--)
for(j=239; j>0; j--);
}
}
void main() {
while(1) {
P2 = 0xFE;
delay_ms(1000);
P2 = 0xFF;
delay_ms(1000);
}
}
Sequential Lighting
#include <reg52.h>
void delay_ms(unsigned int ms) {
unsigned char i, j;
while(ms--) {
for(i=2; i>0; i--)
for(j=239; j>0; j--);
}
}
void main() {
unsigned char shift_reg = 0xFE;
P2 = shift_reg;
while(1) {
shift_reg = (shift_reg << 1) | (shift_reg >> 7);
P2 = shift_reg;
delay_ms(1000);
}
}
Button Interface
Basic Button Detection
#include <reg52.h>
void main() {
while(1) {
if(P3_5 == 0) {
P2_0 = 0;
} else {
P2_0 = 1;
}
}
}
Debounced Button Handling
#include <reg52.h>
void delay_ms(unsigned int ms) {
unsigned char i, j;
while(ms--) {
for(i=2; i>0; i--)
for(j=239; j>0; j--);
}
}
void main() {
while(1) {
if(P3_5 == 0) {
delay_ms(20);
while(P3_5 == 0);
delay_ms(20);
P2_0 = ~P2_0;
}
}
}
Digital Display
Single Digit Display
#include <reg52.h>
#include "delay.h"
unsigned char digit_codes[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
void display_digit(unsigned char position, unsigned char digit) {
if(digit > 9) return;
// Configure control signals
P2_2 = 0; P2_3 = 0; P2_4 = 0;
switch(position) {
case 0: P2_2 = 1; break;
case 1: P2_3 = 1; break;
case 2: P2_4 = 1; break;
default: return;
}
P0 = digit_codes[digit];
delay_ms(1);
}
void main() {
display_digit(0, 5);
while(1);
}
LCD1602 Interface
Basic Functionality
#include <reg52.h>
sbit RS = P2^6;
sbit RW = P2^5;
sbit EN = P2^7;
void lcd_delay() {
unsigned char i, j;
for(i=2; i>0; i--)
for(j=239; j>0; j--);
}
void write_command(unsigned char cmd) {
RS = 0; RW = 0;
P0 = cmd;
EN = 1; lcd_delay(); EN = 0; lcd_delay();
}\n
void write_data(unsigned char dat) {
RS = 1; RW = 0;
P0 = dat;
EN = 1; lcd_delay(); EN = 0; lcd_delay();
}
void lcd_init() {
write_command(0x38); // 8-bit, 2-line, 5x7
write_command(0x0c); // Display on, cursor off
write_command(0x06); // Increment cursor
write_command(0x01); // Clear screen
}
void main() {
lcd_init();
write_data('H');
write_data('e');
write_data('l');
write_data('l');
write_data('o');
while(1);
}
Matrix Keypad Input
Key Detection
#include <reg52.h>
#include "delay.h"
unsigned char scan_matrix() {
// Row scanning
P1 = 0xF0;
if(P1_4 == 0) {
delay_ms(20);
return 1;
}
if(P1_5 == 0) {
delay_ms(20);
return 2;
}
if(P1_6 == 0) {
delay_ms(20);
return 3;
}
if(P1_7 == 0) {
delay_ms(20);
return 4;
}
return 0;
}
void main() {
while(1) {
unsigned char key = scan_matrix();
if(key) {
// Process key press
}
}
}
Timer Applications
Time-based LED Blinking
#include <reg52.h>
void timer0_init() {
TMOD |= 0x01; // Timer0 mode 1
TH0 = 0x3C; // Load high byte
TL0 = 0xB0; // Load low byte
ET0 = 1; // Enable timer0 interrupt
EA = 1; // Enable global interrupt
TR0 = 1; // Start timer
}
unsigned int count = 0;
void timer0_isr() interrupt 1 {
TH0 = 0x3C;
TL0 = 0xB0;
count++;
if(count >= 1000) {
count = 0;
P2_0 = ~P2_0;
}
}
void main() {
timer0_init();
while(1);
}
Serial Communication
UART Transmission
#include <reg52.h>
void uart_init() {
PCON |= 0x80; // Baud rate doubler
SCON = 0x50; // Mode 1, 8-bit
TMOD |= 0x20; // Timer1 mode 2
TH1 = 0xF3; // Baud rate setting
TL1 = 0xF3;
TR1 = 1; // Start timer
ES = 1; // Enable UART interrupt
EA = 1; // Enable global interrupt
}
void uart_send_byte(unsigned char byte) {
SBUF = byte;
while(!TI);
TI = 0;
}
void main() {
uart_init();
uart_send_byte(0x55);
while(1);
}
Real-Time Clock DS1302
Time Reading
#include <reg52.h>
sbit SCLK = P3^6;
sbit IO = P3^4;
sbit CE = P3^5;
void ds1302_write(unsigned char addr, unsigned char dat) {
CE = 1;
for(unsigned char i=0; i<8; i++) {
IO = addr & (1<<i);
SCLK = 1; SCLK = 0;
}
for(unsigned char i=0; i<8; i++) {
IO = dat & (1<<i);
SCLK = 1; SCLK = 0;
}
CE = 0;
}
unsigned char ds1302_read(unsigned char addr) {
CE = 1;
for(unsigned char i=0; i<8; i++) {
IO = addr & (1<<i);
SCLK = 0; SCLK = 1;
}
IO = 1;
unsigned char dat = 0;
for(unsigned char i=0; i<8; i++) {
SCLK = 1; SCLK = 0;
if(IO) dat |= (1<<i);
}
CE = 0;
return dat;
}
void main() {
ds1302_write(0x8E, 0x00); // Disable write protection
// Read time values
while(1);
}
Temperature Sensor DS18B20
Temperature Measurement
#include <reg52.h>
sbit DQ = P3^7;
unsigned char ds18b20_init() {
DQ = 1;
DQ = 0;
for(unsigned char i=0; i<247; i++);
DQ = 1;
for(unsigned char i=0; i<32; i++);
unsigned char ack = DQ;
for(unsigned char i=0; i<247; i++);
return ack;
}
void ds18b20_start() {
ds18b20_init();
// Skip ROM command
// Convert T command
}
float read_temperature() {
// Read temperature from scratchpad
return 0.0f;
}
void main() {
float temp = read_temperature();
while(1);
}
Motor Control with PWM
Speed Regulation
#include <reg52.h>
sbit motor = P1^0;
void timer0_init() {
TMOD |= 0x01;
TH0 = 0xFB;
TL0 = 0xAE;
ET0 = 1;
EA = 1;
TR0 = 1;
}
unsigned int counter = 0;
unsigned int duty_cycle = 0;
void timer0_isr() interrupt 1 {
TH0 = 0xFB;
TL0 = 0xAE;
counter++;
if(counter < duty_cycle) {
motor = 1;
} else {
motor = 0;
}
if(counter >= 100) counter = 0;
}
void main() {
timer0_init();
while(1) {
if(P3_1 == 0) {
while(P3_1 == 0);
duty_cycle += 25;
if(duty_cycle > 75) duty_cycle = 0;
}
}
}
ADC and DAC Conversion
Analog-to-Digital Conversion
#include <reg52.h>
sbit CS = P3^5;
sbit CLK = P3^6;
sbit DIN = P3^4;
sbit DOUT = P3^7;
unsigned int read_adc(unsigned char command) {
CLK = 0;
CS = 0;
for(unsigned char i=0; i<8; i++) {
DIN = command & (0x80>>i);
CLK = 1; CLK = 0;
}
unsigned int result = 0;
for(unsigned char i=0; i<16; i++) {
CLK = 1; CLK = 0;
if(DOUT) result |= (0x8000>>i);
}
CS = 1;
return result;
}
void main() {
while(1) {
unsigned int adc_value = read_adc(0x94); // Read XP channel
// Process ADC value
}
}
Infrared Remote Control
Signal Reception
#include <reg52.h>
sbit INT0_PIN = P3^2;
void init_interrupt() {
IT0 = 1; // Falling edge trigger
EX0 = 1; // Enable external interrupt 0
EA = 1; // Enable global interrupts
}
void int0_isr() interrupt 0 {
static unsigned int pulse_width = 0;
static unsigned char state = 0;
if(state == 0) {
// Start pulse detection
pulse_width = 0;
state = 1;
} else {
// Process received data
state = 0;
}
}
void main() {
init_interrupt();
while(1);
}
Additional Concepts
Memory Management
- Code Space: Program instructions stored in Flash
- Data Space: RAM for variables and stack
- Special Function Registers: Control peripherals through dedicated addresses
I/O Port Configuration
- P0: Open-drain output
- P1/P2/P3: Quasi-bidirectional with pull-up resistors
Timing Considerations
- Clock Frequency: Typically 11.0592 MHz for standard applications
- Timer Prescaler: Used to adjust timing precision
- Interrupt Priorities: Configurable for critical tasks
Common Protocols
- I2C: Two-wire serial communication
- SPI: Four-wire synchronous protocol
- UART: Asynchronous serial communication
Programming Best Practices
- Use appropriate delay functions for timing
- Implement debouncing for mechanical switches
- Handle peripheral initialization properly
- Manage interrupt service routines efficiently
- Utilize memory qualifiers like
codefor constant data
These implementations demonstrate fundamental concepts in 8051 microcontroller programming, covering essential peripherals and communication protocols.