So far (Parts 1~5), we have reinforced C syntax and the concepts of bits and pointers without using a board. From this part onward, we will get on an actual chip. The first protagonist is the classic in embedded education, the 8051 (here, AT89C52). The 8051 has a simple structure and few registers, making it the best chip to learn "how to manipulate hardware with C."
The key of this part is the SFR (Special Function Register). All of 8051's ports, timers, serial communication, and interrupts are controlled via SFRs. The <reg52.h> provided by Keil C51 pre-declares these SFRs with names, allowing us to handle hardware as if assigning values to variables like P1 = 0x0F;. This part will teach two ways to write values to port SFRs (entire port / individual bit sbit), leading to the LED blinking in Part 7.
Simulator reference: The 8051 examples in this track are verified using the μVision built-in simulator. An actual board is not necessary, and the port status can be checked in the Peripherals → I/O-Ports window.
Learning Objectives
- Outline the basic structure of the 8051 (CPU, ROM/RAM, 4 ports, timer, serial, interrupt).
- Explain what SFRs are and where they are located in memory (0x80~0xFF).
- Understand how
<reg52.h>exposes SFRs with names. - Use the distinction between writing to the entire port SFR (
P1 = ...) and writing to individual bits (sbit). - Understand the usage of C51-specific keywords
sfr,sbit,bit.
Target Environment
- Compiler: Keil C51 (for 8051 series).
- Device: AT89C52 (8051 compatible, μVision simulator supported).
- Verification: μVision simulator (Peripherals → I/O-Ports → Port 1). Pure logic checking on PC
gcc(pc_test.c).
Key Concepts
1) Overview of 8051
| Resource | Details |
|---|---|
| CPU | 8-bit, accumulator (ACC) centered |
| Program Memory | Code ROM/Flash (AT89C52: 8KB) |
| Data Memory | Internal RAM 256 bytes (lower 128 + upper 128), SFR area |
| Input/Output | 4 8-bit ports P0~P3 (total 32 pins) |
| Timer | Timer0/1 (AT89C52 adds Timer2) |
| Serial | 1 channel UART |
| Interrupt | External 2, timer, serial, etc. |
intis 16-bit in 8051 (see Part 2). So values that are sufficient with 8 bits useunsigned char(oruint8_t) to save RAM and code size.
2) SFR — Special Function Registers
SFRs are registers that control peripheral devices and are arranged in the internal RAM address range 0x80~0xFF. Ports (P0~P3), timer control (TCON/TMOD), serial (SCON/SBUF), interrupts (IE/IP), etc., are all SFRs.
| SFR | Address | Function |
|---|---|---|
P0 |
0x80 | Port 0 |
P1 |
0x90 | Port 1 |
P2 |
0xA0 | Port 2 |
P3 |
0xB0 | Port 3 |
TMOD |
0x89 | Timer Mode |
TCON |
0x88 | Timer Control |
SCON |
0x98 | Serial Control |
IE |
0xA8 | Interrupt Enable |
SFRs with addresses that are multiples of 8, like 0x80, 0x88, 0x90 … (0x_0/0x_8), can be bit-addressable. That is, individual bits can be accessed using sbit. This includes P0~P3, TCON, SCON, IE, and IP.
3) <reg52.h> — SFR by Name
<reg52.h> is a built-in header in Keil C51 that pre-declares the above SFRs. Internally, it looks roughly like this.
sfr P1 = 0x90; /*The name P1 refers to the SFR at address 0x90*/
sfr TMOD = 0x89;
sbit P1_0 = 0x90; /*(Example) Bit 0 of P1*/
So with one line of #include <reg52.h>, we can directly use names like P1, TMOD, IE. No external library is required.
4) Two Ways to Write to a Port
(1) Write the Entire Port — 8 bits at once.
P1 = 0x0F; /* P1.0~P1.3 = 1, P1.4~P1.7 = 0 */
(2) Write Individual Bits — Give names to bits with sbit and write only one.
sbit LED0 = P1 ^ 0; /*P1.0 is named LED0 (declared at file scope)*/
LED0 = 1; /*Only P1.0 is set to 1, the remaining bits remain the same*/
^ in P1 ^ 0 is C51-specific notation meaning 'bit 0 of P1', not XOR. sbit can only be used in bit-addressable SFRs.
5) C51-Specific Keywords — sfr, sbit, bit
| Keyword | Meaning | Example |
|---|---|---|
sfr |
Declare an 8-bit SFR | sfr P1 = 0x90; |
sbit |
A bit of an SFR | sbit LED = P1 ^ 0; |
bit |
Bit variable (internal bit memory) | bit flag; |
These are Keil C51 extensions not present in standard C. Therefore, this main.c will not compile on a PC's gcc (PC verification is done separately with pc_test.c).
Creating a μVision Project
- Specify
Project → New µVision Project…→ folder and name (e.g.,c51_sfr). - In the Device selection window, choose Atmel → AT89C52 (if not available, install the 8051 device pack via the Pack Installer).
- When asked 'Copy STARTUP.A51 to project?', select Yes — includes default startup code.
- Add
main.cto Source Group 1 (Right-click → Add New/Existing Item), then copy and paste the code below. - In the
Project → Options for Target → Debugtab, select Use Simulator. - Rebuild(F7) → Start/Stop Debug Session(Ctrl+F5).
- Open the
Peripherals → I/O-Ports → Port 1window to observe the bit status.
Examples
Example 1 — main.c: Two Ways to Access the SFR
Shows both writing to the entire port and writing to sbit bits.
#include <reg52.h>
/*
*Lesson 6 Example 1 — Two ways to access SFR (Special Function Register).
*Target: AT89C52 (8051 compatible), Compiler: Keil C51.
*
*(1) Write to the entire port at once: P1 = 0x0F;
*(2) Write to individual bits with assigned names: declare with sbit, then LED0 = 0;
*
*Inside reg52.h, P1 is pre-declared as 'sfr P1 = 0x90;',
*so we can directly access the port SFR at address 0x90 using the name P1.
*/
/*Assign meaningful names to individual bits of the P1 port (P1 is a bit-addressable SFR).*/
sbit LED0 = P1 ^ 0; /* P1.0 */
sbit LED7 = P1 ^ 7; /* P1.7 */
void main(void)
{
/*(1) Write to the entire port: P1.0~P1.3 = 1, others = 0*/
P1 = 0x0F;
/*(2) Write to individual bits: leave the remaining bits as they are and change only one bit*/
LED0 = 0; /* P1.0 = 0 → 0x0E */
LED7 = 1; /* P1.7 = 1 → 0x8E */
/*The embedded main does not terminate. It waits while maintaining the value of P1.*/
while (1) {
/*Confirm P1 = 0x8E in the simulator Port 1 window.*/
}
}
Example 2 — pc_test.c: Verify Final Port Value on PC
Simulate the SFR as a regular variable and verify the result of the above operation (0x8E) on the PC.
#include <stdio.h>
/*
*Lesson 6 Example 2 — Verify the final port value produced by the lecture example (main.c) on the PC.
*On an actual chip, P1 is an SFR (address 0x90), but here it is simulated with a general variable.
*Only check the result when "P1 = 0x0F and then P1.0 is set to 0, P1.7 is set to 1".
*/
int main(void)
{
unsigned char P1 = 0x0F; /*Full port write*/
P1 = (unsigned char)(P1 & ~(1u << 0)); /* LED0 = 0 → P1.0 clear */
P1 = (unsigned char)(P1 | (1u << 7)); /* LED7 = 1 → P1.7 set */
printf("P1 = 0x%02X\n", P1); /* 0x8E */
return 0;
}
Checking with Simulator
- Start a debug session with Ctrl+F5, then run with F5 (or step line by line with F11).
- Observe P1 bits in the
Peripherals → I/O-Ports → Port 1window. - Expected behavior:
P1 = 0x0F(0000 1111) →LED0 = 0(0000 1110) →LED7 = 1(1000 1110). Final P1 = 0x8E. In the Port 1 window, the pattern with bit 7 and bits 1~3 on (1000 1110) is visible.
Verification on a PC
Pure logic verification is only possible on the PC (device main.c is for C51 only).
gcc pc_test.c -o pc_test && ./pc_test
Expected output (stdout):
P1 = 0x8E
Common Mistakes
Q. When compiling main.c with gcc, it says reg52.h does not exist.
A. <reg52.h>, sfr, and sbit are exclusive to Keil C51. Device main.c can only be built in μVision. On a PC, just verify the logic using pc_test.c (standard C).
Q. I get an error when I use sbit LED = P1 ^ 0; inside a function.
A. The declaration of sfr/sbit must be file scope (global). It cannot be declared inside a function. Declare it above main.
Q. I used P1 = 0x0F but input is not read.
A. This section only deals with writing output. To read ports as input on the 8051, you must first write 1 to the corresponding pin to set it to input mode (quasi-bidirectional port). Input reading will be covered in Chapter 8.
Q. I used int on a large counter and it behaves strangely.
A. On the 8051, int is 16-bit. For an 8-bit value, use the port Value/Counter-is unsigned char. If a large number is needed, explicitly use unsigned int (16-bit) or unsigned long (32-bit).
Summary
- The 8051 is a simple 8-bit MCU and controls peripherals using SFRs (0x80~0xFF).
<reg52.h>provides SFRs such asP1,TMOD,IEby name.- Ports are handled with full write (
P1 = 0x0F) and bit write (sbit). sfr/sbit/bitare keywords exclusive to C51, so they do not work with PCgcc(logic is verified withpc_test.c).- For values small enough to fit in 8 bits, use
unsigned charto save resources.
Try it yourself
Solve the assignment below. The reference solution is shown below.