If MikaTech was a bad company, you could find tons of bad reputations about its service on the internet over the 28 years history
So, the answer is YES! We are good people.Why choose Mikatech, please click here to find out
About MikaTech
Time went fast, from the day we did our first 8051 MCU reverse engineering project in 1998, to the day we set up our million dollar reverse engineering lab in 2012, 14 years went by. Now we start our new business of embedded visual system development, hope we can serve another 10 years.
Peter Lee
Co-Founder & CEO
Hardware-focused engineers had to make compromises if they wanted to stay competitive in embedded development. Unlike ordinary electronic circuits that only require wiring and power supply to function, microcontrollers additionally require custom programming to carry out defined tasks. Fortunately, microcontroller architectures have not advanced to the point of requiring diverse high-level languages; nearly all microcontroller families only natively understand machine language. This standardization brings major benefits. The downside is that machine language, consisting purely of binary 0s and 1s, is unintelligible to human developers and only decipherable by specialized hardware design experts. To bridge the communication gap between microcontroller hardware and human programmers, assembly language — the first human-readable low-level programming language — was created.
Assembly solved the challenge of memorizing raw binary opcodes for hardware instructions, yet it introduced a new barrier that complicated workflow for both developers and microcontroller hardware. This new obstacle was resolved using two key tools: a PC-based assembler software program and a dedicated hardware programming device known as a chip programmer.
The assembler software accepts human-readable assembly mnemonics and reliably translates them into a binary executable output file. The compilation stage that converts assembly source code into machine code is critical, as the resulting HEX file is a stream of binary values that only microcontroller hardware can interpret. Raw assembly source code cannot run directly on a microcontroller unless this compiled HEX file is flashed into the device’s program memory. This is where the final component of the development workflow, the chip programmer, comes into play. It is a compact hardware device that connects to a desktop PC via a standard communication port and features a socket to mount the target microcontroller chip for firmware flashing.
Assembly language follows structural rules similar to natural human languages, featuring reserved words, strict formatting rules, and standardized syntax. The four core fundamental building blocks of assembly source code are listed below:
Strict formatting rules (collectively called syntax) must be followed when writing assembly source code to ensure error-free compilation into executable HEX machine code. The mandatory syntax rules are limited to the following requirements:
Excluding the outdated octal number system, assembly language supports numeric values written in three distinct base formats:
If no base suffix is specified, the assembler interprets all numeric literals as decimal values. Decimal notation uses digits 0 through 9. Since microcontroller storage allocates a maximum of two bytes for general numeric storage, the largest representable decimal value within assembly code is 65535. The suffix letter "D" may be appended to explicitly mark a value as decimal, for example: 1234D.
Hexadecimal notation is the most commonly used format for embedded programming. The hexadecimal base system uses sixteen unique symbols: digits 0–9 and letters A–F. The maximum two-byte hexadecimal value is FFFF, which is equivalent to the decimal value 65535. To differentiate hexadecimal literals from decimal values, the suffix letter "h" (uppercase or lowercase) is appended to the number, such as 54h.
Binary literals are frequently used when individual bit states of CPU registers need to be explicitly defined, as each binary digit directly corresponds to one hardware bit position. Binary notation only uses the digits 0 and 1. The maximum two-byte binary literal is 1111111111111111. The suffix letter "b" (uppercase or lowercase) is added to distinguish binary values from other numeric formats, for example: 01100101B.
Many assembly conditional directives and instruction operands accept mathematical and logical expressions instead of fixed static values. A sample conditional block is shown below:
IF (VERSION>1) LCALL Table_2 USING VERSION+1 ENDIF ...
The assembler can evaluate constant mathematical and logical expressions during compilation and embed the resulting computed values into the final machine code. The full set of supported operators is listed in the table below:
| Operator Symbol | Operation Description | Example Expression | Evaluated Result |
|---|---|---|---|
| + | Addition | 10+5 | 15 |
| - | Subtraction | 25-17 | 8 |
| * | Multiplication | 7*4 | 28 |
| / | Integer Division (truncates remainder) | 7/4 | 1 |
| MOD | Modulo (Division Remainder) | 7 MOD 4 | 3 |
| SHR | Bitwise Right Shift | 1000B SHR 2 | 0010B |
| SHL | Bitwise Left Shift | 1010B SHL 2 | 101000B |
| NOT | Bitwise Logical NOT (1’s Complement) | NOT 1 | 1111111111111110B |
| AND | Bitwise Logical AND | 1101B AND 0101B | 0101B |
| OR | Bitwise Logical OR | 1101B OR 0101B | 1101B |
| XOR | Bitwise Exclusive OR | 1101B XOR 0101B | 1000B |
| LOW | Extract Lower 8 Bits of 16-bit Value | LOW(0AADDH) | 0DDH |
| HIGH | Extract Upper 8 Bits of 16-bit Value | HIGH(0AADDH) | 0AAH |
| EQ, = | Equality Comparison | 7 EQ 4 or 7=4 | 0 (False) |
| NE,<> | Inequality Comparison | 7 NE 4 or 7<>4 | 0FFFFH (True) |
| GT, > | Greater Than Comparison | 7 GT 4 or 7>4 | 0FFFFH (True) |
| GE, >= | Greater Than or Equal To | 7 GE 4 or 7>=4 | 0FFFFH (True) |
| LT, < | Less Than Comparison | 7 LT 4 or 7<4 | 0 (False) |
| LE,<= | Less Than or Equal To | 7 LE 4 or 7<=4 | 0 (False) |
Custom symbolic names can be assigned to CPU registers, constant values, memory addresses, and subroutine entry points within assembly code, drastically simplifying source code readability and maintenance. For example, if GPIO pin P0.3 connects to a physical pushbutton that triggers a full process halt (STOP button), code logic becomes far more intuitive if the bit address P0.3 is aliased to the descriptive symbol "pushbutton_STOP". Similar to other programming languages, strict naming rules govern valid custom symbols:
The two symbols shown below are treated as identical by the assembler:
Serial_Port_Buffer SERIAL_PORT_BUFFER
START_ADDRESS_OF_TABLE_AND_CONSTANTS_1 START_ADDRESS_OF_TABLE_AND_CONSTANTS_2 TABLE_OF_CONSTANTS_1_START_ADDRESS TABLE_OF_CONSTANTC_2_START_ADDRESS
The complete list of reserved keywords that are prohibited from use as custom symbols is provided in the table below:
| A | AB | ACALL | ADD |
| ADDC | AJMP | AND | ANL |
| AR0 | AR1 | AR2 | AR3 |
| AR4 | AR5 | AR6 | AR7 |
| BIT | BSEG | C | CALL |
| CJNE | CLR | CODE | CPL |
| CSEG | DA | DATA | DB |
| DBIT | DEC | DIV | DJNZ |
| DPTR | DS | DSEG | DW |
| END | EQ | EQU | GE |
| GT | HIGH | IDATA | INC |
| ISEG | JB | JBC | JC |
| JMP | JNB | JNC | JNZ |
| JZ | LCALL | LE | LJMP |
| LOW | LT | MOD | MOV |
| MOVC | MOVX | MUL | NE |
| NOP | NOT | OR | ORG |
| ORL | PC | POP | PUSH |
| R0 | R1 | R2 | R3 |
| R4 | R5 | R6 | R7 |
| RET | RETI | RL | RLC |
| RR | RRC | SET | SETB |
| SHL | SHR | SJMP | SUBB |
| SWAP | USING | XCH | XCHD |
| XDATA | XOR | XRL | XSEG |
A label is a specialized type of symbol that represents a human-readable alias for a physical memory address in program ROM or data RAM. Labels are always placed at the very start of a source code line. Jump, branch, and subroutine call instructions would be extremely cumbersome to write and maintain without labels. The standard usage workflow for labels is straightforward:
During compilation, the assembler automatically resolves each label to its corresponding absolute memory address and substitutes the numeric value into the compiled machine code.
Unlike CPU instruction mnemonics that are compiled into executable machine code stored within the microcontroller’s program memory, assembler directives are native commands interpreted solely by the assembler software and have no impact on the runtime behavior of the microcontroller hardware. Some directives are mandatory for every valid assembly project, while others exist purely to simplify development workflows or speed up compilation processes.
Directives occupy the same vertical column reserved for instruction mnemonics in source code, and a single source line may only contain one assembler directive.
The EQU directive binds a fixed constant numeric value to a custom symbol name for reuse throughout the source file. Example usage:
MAXIMUM EQU 99
After this definition, every occurrence of the symbol "MAXIMUM" within the source code will be replaced with the literal value 99 during compilation. Each symbol may only be defined once using EQU, so EQU declarations are almost always placed at the top of the assembly file for global constant configuration.
The SET directive also maps a numeric value to a symbolic alias. Its key distinction from EQU is that SET allows repeated redefinition of the same symbol multiple times throughout the source file:
SPEED SET 45 SPEED SET 46 SPEED SET 57
The BIT directive assigns a custom symbol name to a specific bit-addressable memory location, where the target bit address must fall within the range 0–255. Sample definitions:
TRANSMIT BIT PSW.7 ;Assign alias "TRANSMIT" to bit 7 of the PSW status register OUTPUT BIT 6 ;Assign alias "OUTPUT" to bit address 06h RELAY BIT 81 ;Assign alias "RELAY" to bit address 81h (corresponds to Port 0 pin 1)
The CODE directive links a custom symbol to a fixed program memory address. Since the maximum address space of standard 8051 program memory is 64KB, valid addresses range from 0 to 65535. Example:
RESET CODE 0 ;Assign alias "RESET" to program memory location 00h TABLE CODE 1024 ;Assign alias "TABLE" to program memory address 1024 decimal (0400h)
The DATA directive attaches a symbolic alias to an address within the microcontroller’s directly accessible internal RAM, with valid address values spanning 0–255. It can reassign readable names to any standard SFR or general-purpose RAM register:
TEMP12 DATA 32 ;Name RAM address 32 as "TEMP12" STATUS_R DATA 0D0h ;Rename the PSW special function register to "STATUS_R"
The IDATA directive creates symbolic aliases for RAM locations accessed via indirect addressing through R0 or R1 pointer registers:
TEMP22 IDATA 32 ;Alias the RAM location pointed to by register at address 32 as "TEMP22" TEMP33 IDATA T_ADR ;Alias the RAM location pointed to by register T_ADR as "TEMP33"
The XDATA directive assigns symbolic names to memory locations within external data RAM, which supports full 16-bit addressing up to 65535. Example:
TABLE_1 XDATA 2048 ;Alias external RAM address 2048 decimal as "TABLE_1"
The ORG directive sets the starting memory offset for all subsequent compiled code or data definitions within the active segment. Example:
BEGINNING ORG 100
...
...
ORG 1000h
TABLE ...
...
This code snippet places the main program starting at memory offset 100 decimal, while the lookup table data block is anchored at address 1000 hexadecimal.
The USING directive selects which of the four CPU general-purpose register banks (R0–R7) will be utilized for subsequent code logic:
USING 0 ;Select Register Bank 0 (R0-R7 mapped to RAM addresses 0–7) USING 1 ;Select Register Bank 1 (R0-R7 mapped to RAM addresses 8–15) USING 2 ;Select Register Bank 2 (R0-R7 mapped to RAM addresses 16–23) USING 3 ;Select Register Bank 3 (R0-R7 mapped to RAM addresses 24–31)
The END directive marks the absolute termination of the assembly source file. The assembler halts all compilation processing immediately upon encountering this token. Example:
... END ;Marks the end of the entire assembly source program
Five dedicated directives control selection between the five distinct memory segment types available on standard 8051 microcontrollers:
CSEG ;Activates the program code memory segment for executable instructions BSEG ;Activates the bit-addressable internal RAM segment DSEG ;Activates the directly addressable general-purpose internal RAM segment ISEG ;Activates the indirectly addressable internal RAM segment (accessed via R0/R1 pointers) XSEG ;Activates the external data RAM segment
The CSEG code segment is enabled by default when the assembler initializes and remains active until an alternative segment directive is declared. Every memory segment maintains its own independent address counter, which resets to zero each time the assembler restarts compilation. The starting offset of a segment’s address counter can be manually set by appending an AT specifier followed by a numeric value, arithmetic expression, or pre-defined symbol. Example:
DSEG ;Switch to directly addressed internal RAM segment BSEG AT 32 ;Select bit-addressable RAM with segment counter offset starting at bit 32
The dollar symbol "$" represents the current value of the address counter for the active segment. Two practical usage examples are provided below:
Example 1:
JNB FLAG,$ ;Infinite loop that repeats this jump instruction until FLAG bit is cleared
Example 2:
MESSAGE DB 'ALARM turn off engine' LENGTH EQU $-MESSAGE-1
These two lines calculate the exact character count of the ASCII string "ALARM turn off engine" stored at the address labeled MESSAGE.
The DS directive reserves a contiguous block of byte-sized memory space within the currently active DSEG, ISEG, or XSEG data segment. Two usage examples are shown below:
Example 1:
DSEG ;Select direct-access internal RAM segment DS 32 ;Advance segment address counter by 32 bytes SP_BUFF DS 16 ;Reserve 16-byte contiguous block for serial port receive buffer IO_BUFF DS 8 ;Reserve 8-byte contiguous block for general I/O data buffer
Example 2:
ORG 100 ;Set starting address offset to 100 decimal DS 8 ;Reserve 8 empty consecutive bytes LAB ......... ;Program logic begins at memory address 108
The DBIT directive allocates a block of contiguous bit storage within the active BSEG bit-addressable RAM segment, with the allocation size specified as a raw bit count:
BSEG ;Activate bit-addressable RAM segment IO_MAP DBIT 32 ;Reserve the first 32 bit positions for I/O status flags
The DB directive stores static byte values or ASCII character strings directly into the active CSEG program memory segment. Multiple numeric literals are separated by commas, and ASCII text strings must be enclosed within single quotation marks. This directive may only be used while the CSEG code segment is active:
CSEG DB 22,33,'Alarm',44
If a label precedes a DB statement, the label’s address will point to the first data element defined (the value 22 in this sample).
The DW directive functions similarly to DB, but it stores full 16-bit two-byte words into program memory. The high-order byte of each word value is written to memory first, followed by the low-order byte.
These three directives create conditional compilation blocks within assembly source code. Each conditional block opens with an IF statement and terminates with ENDIF, with optional ELSE logic separating mutually exclusive code paths. The mathematical/logical expression enclosed in parentheses after IF determines which sections of source code are compiled into the final binary:
Example 1:
IF (VERSION>3) LCALL Table_2 LCALL Addition ENDIF ...
If the VERSION constant value is greater than 3 (condition true), the two subroutine calls to Table_2 and Addition are compiled into the final firmware image. If VERSION is less than or equal to 3 (condition false), these two instructions are discarded entirely during compilation.
Example 2:
If the symbol Model equals 1, the two instructions immediately following IF are compiled, and all code between ELSE and ENDIF is ignored. If Model equals 0, the IF-block instructions are skipped, and only the code under ELSE is assembled into machine code.
IF (Model) MOV R0,#BUFFER MOV A,@R0 ELSE MOV R0,#EXT_BUFFER MOVX A,@R0 ENDIF ...
Control directives begin with a dollar sign "$". They configure assembler behavior during compilation, including external source file inclusion, output HEX file destination paths, and formatting rules for the human-readable compilation listing file. Many control directives exist, but only the most commonly used ones are covered below:
This directive instructs the assembler to load and parse an external assembly source file during the compilation process:
$INCLUDE(TABLE.ASM)
The $MOD8253 directive references a pre-defined header file containing all special function register (SFR) names and their corresponding memory addresses for the 8253 microcontroller variant. With this directive enabled, developers may reference registers by their human-readable names instead of raw numeric addresses. If this directive is omitted, every SFR utilized in the project must be manually declared with its exact memory address at the start of the source file.
This chapter delivers core foundational knowledge of microcontroller hardware required for hands-on practical deployment. Readers will not find overly complex program logic or advanced circuit schematics with cutting-edge optimized designs within this section. Instead, the following practical examples demonstrate that embedded programming is neither an exclusive skill nor a talent-dependent craft — it is simply the process of assembling modular functional logic via standardized instruction syntax. Rest assured, hardware design and iterative development revolve around a consistent core workflow: “test, adjust, iterate”. Naturally, project complexity scales with experience; the same puzzle-like component logic applies to hobbyist prototype builds as well as industrial-grade professional architecture development...
While these introductory demos focus purely on fundamental functional operation, every embedded hardware platform carries an often-underestimated underlying security risk layer rarely covered in beginner tutorials. The core microcontroller (MCU) running these simple control routines may later be integrated into commercial end products, where safeguarding executable source code against unauthorized access becomes a vital design priority. Many novice engineers mistakenly believe the built-in lockbit fuse mechanism can fully block external read-out access to internal flash memory, yet this assumption is not universally reliable. In real-world threat scenarios, malicious actors can deploy chip decapsulation workflows to expose bare silicon die layers and perform direct micro-probing of security-controlling hardware fuses. Once the packaging enclosure is removed, adversaries can extract a complete memory dump of all program storage regions, completing full firmware extraction without valid authentication credentials. The on-board EEPROM chipset, which commonly stores factory calibration parameters and cryptographic security keys, faces identical vulnerability to such physical hardware intrusion attacks. Certain threat actors utilize focused ion beam (FIB) lab equipment to selectively modify individual fuse circuits and force a permanent unlock of memory zones marked as protected. Other attackers leverage side-channel analysis methodologies to gradually read out full program binaries without triggering any hardware tamper detection circuitry. In the worst-case exploitation scenario, a complete dump of both flash and EEPROM memory partitions enables attackers to manufacture identical hardware replicas, producing fully functional device clones that completely erode proprietary intellectual property value. For this reason, even while learning basic digital input/output logic and timing control subroutines, developers must recognize that identical chip pins and core registers can be exploited during malicious reverse engineering campaigns. Recovering a non-functional bricked embedded device occasionally requires bypassing security lock protections via voltage glitch injection — a legitimate hardware repair technique that simultaneously creates a viable attack entry point. Mastery of lockbit underlying operating logic is a mandatory competency for both embedded firmware developers and dedicated hardware security analysts. A large volume of commercial embedded devices have suffered complete intellectual property breaches due to improperly configured security fuses, leaving executable code fully exposed to external retrieval. A straightforward read-out of chip configuration memory bytes can expose all security policy settings, guiding threat actors directly to the weakest exploit vector. Even the simple watchdog timer demonstration covered later can be repurposed to implement hardware tamper detection logic. In real engineering practice, designers must strike a balance between hardware functionality and anti-intrusion safeguards, ensuring unauthorized firmware extraction remains technically and financially impractical. The subsequent practical demos illustrate elementary hardware operation workflows, yet they implicitly showcase how core MCU peripherals including timers, interrupt controllers, and serial communication interfaces can be repurposed to implement robust security hardening countermeasures. As an illustration, utilizing timer modules to generate randomized timing offsets can mitigate timing-based side-channel attacks targeting memory dump of confidential secret data. Additionally, UART serial transmission logic can be wrapped with lightweight encryption protocols to block raw data sniffing during unauthorized read-out attempts. LED multiplexing display circuits, though seemingly trivial, build critical awareness of precise timing sensitivity — a foundational skill required to implement secure authenticated boot sequences. The EEPROM data storage example directly covers persistent data recovery workflows, a technical concept that extends to secure long-term cryptographic key retention. Ultimately, every line of embedded firmware code should be authored under the assumption that hostile actors may eventually gain full physical access to the target hardware. The security lock fuse mechanism merely serves as a primary defensive barrier; comprehensive hardware protection relies on multi-layered safeguards that render chip decapsulation and silicon probing economically unfeasible for threat actors. This chapter establishes baseline security awareness despite not diving deep into specialized reverse engineering exploitation tactics. With this dual focus on functional operation and embedded security, we move on to review fundamental microcontroller wiring schematics and supporting peripheral hardware components.
As visualized in the above schematic, three mandatory signal domains must be wired correctly to guarantee stable microcontroller operation:
These constitute extremely minimal circuit topologies, yet hardware complexity rises drastically for equipment tasked with regulating high-cost industrial machinery or sustaining safety-critical operational workflows. Nevertheless, this simplified circuit layout suffices for introductory learning purposes...
While this microcontroller variant supports multiple input supply voltage levels, there is no practical reason to introduce unnecessary fault risks by testing marginal voltage operating conditions. A standard 5V DC supply remains the most widely adopted industry standard. The schematic diagram shown implements the low-cost LM7805 three-terminal positive linear voltage regulator, delivering consistent stable output voltage and sufficient 1A peak current capacity to power the microcontroller core plus all connected auxiliary peripheral hardware.
For reliable microcontroller initialization, a logic low (0V) signal must be asserted on the RS reset pin. The momentary pushbutton linking the RS reset pin to the VCC supply rail is not strictly mandatory hardware, yet it is included on nearly all prototype boards. This pushbutton enables controlled forced reinitialization if firmware execution enters an unstable fault state. Pulling the RS pin high to 5V triggers a full chip reset, restarting program execution from the firmware entry vector address.
Although the microcontroller integrates an internal RC oscillator, stable deterministic operation requires an external quartz crystal paired with two matching load capacitors to fix the core operating clock frequency, which directly defines the chip’s instruction execution throughput.
Naturally, this crystal oscillator topology is not universally applicable, and multiple alternative clock generation solutions exist. One alternative configuration sources a clock reference signal from a dedicated external oscillator module routed through an inverting logic buffer, as shown in the left-hand diagram.
Despite representing cutting-edge compact computing hardware, a standalone microcontroller serves no practical function without interfaced auxiliary peripheral hardware. Simply stated, voltage level changes on the chip’s input/output pins carry no functional meaning unless utilized to drive defined operations: switching loads on/off, data shifting, visual status display rendering, and more.
No peripheral component is simpler than mechanical switches and momentary pushbuttons; they deliver the most straightforward method of detecting voltage state transitions on microcontroller input pins.
Real-world implementation introduces unforeseen complexity stemming from mechanical contact bounce, a pervasive flaw inherent to all mechanical switching hardware. When metallic contact surfaces collide, physical momentum and material elasticity generate rapid repeated electrical pulses instead of a clean single voltage transition between 0V and full supply rail. This oscillating behavior originates from minor surface imperfections, accumulated dust contamination, and mechanical vibration. The bounce duration — measured in microseconds or milliseconds — passes unnoticed by human perception yet registers reliably as multiple discrete input events by the microcontroller’s high-speed digital logic. If a raw pushbutton signal feeds directly into a pulse-counting firmware routine, near 100% counting error rates will occur without signal conditioning.
The most cost-effective mitigation strategy for contact bounce is a passive RC low-pass filter circuit that suppresses rapid voltage transients. Since bounce timing characteristics vary across switch models, resistor and capacitor values lack rigid standardized specifications, yet the component values illustrated in the diagram deliver consistent performance for most common use cases.
Applications requiring absolute input signal stability demand more robust hardware countermeasures. An RS flip-flop circuit modifies the signal output logic state only once upon the initial contact transition, fully ignoring subsequent bounce pulses. While this hardware solution requires a higher-cost SPDT switch component, it eliminates bounce artifacts entirely and supports detection of extremely short input pulses that passive RC filters would mask, as no charge storage capacitors are utilized in the signal path.
Beyond hardware filtering topologies, a lightweight software debounce implementation exists. When firmware detects an input pin state transition, it re-verifies the pin logic level following a fixed delay period. A consistent state reading confirms an intentional switch actuation rather than transient bounce noise. This software approach carries zero additional component cost, suppresses electromagnetic interference artifacts, and operates reliably with low-quality worn mechanical contacts. Its primary limitation matches RC filter circuits: input pulses shorter than the programmed delay interval cannot be captured by the firmware logic.
Optocouplers are industry-standard isolation components used to galvanically isolate microcontroller circuitry from hazardous high-current or high-voltage external electrical environments. Typical optocoupler packages integrate one, two, or four LED light-emitting sources on the input side, paired with an equal quantity of light-sensitive semiconductor devices on the output stage: phototransistors, photothyristors, or phototriacs. Core functionality relies on a non-conductive optical transmission channel to transfer digital signals between isolated circuit domains while maintaining complete electrical separation. This isolation performance is only effective when the input LED side and output photodetector side receive independent power supply rails. This separation fully shields the microcontroller and sensitive precision peripheral hardware from destructive high-voltage transients and electromagnetic noise, two leading root causes of permanent hardware damage and unstable operational behavior in embedded systems. Phototransistor-output optocouplers represent the most widely deployed variant. For optocoupler packages with an exposed base pin (pin 6), the base terminal may be left unconnected. An optional resistor-capacitor network shown via dashed wiring in the schematic attenuates high-frequency noise by filtering ultra-short transient pulse signals.
A relay constitutes an electrically actuated mechanical switch controlled by a secondary low-power logic circuit. Relays interface with microcontroller output pins to switch high-power load hardware including DC motors, AC transformers, heating elements, incandescent lighting, and antenna arrays. These high-power loads are nearly always physically separated from the main PCB’s sensitive low-voltage logic components. Multiple relay form factors exist, yet all operate via identical electromagnetic actuation principles: energizing an internal copper coil generates a magnetic field that mechanically toggles one or multiple sets of conductive contact pairs. Identical to optocouplers, relays deliver full galvanic isolation with no direct electrical conduction path between input control logic and output load circuits. Most standard relay variants demand elevated voltage and current to energize the coil winding, though miniature signal relays can be directly driven by the low current sourcing capacity of microcontroller output pins.
The illustrated schematic targets the 8051 microcontroller architecture. A Darlington pair transistor amplifies drive current to energize the relay coil, a mandatory workaround for logic-high output states, where the microcontroller pin operates as a high-impedance input with minimal sourcing current capacity, violating standard drive circuit design guidelines yet required for this specific architecture’s electrical characteristics.
A reverse-biased freewheeling diode is wired in parallel with the relay coil winding to suppress destructive high-voltage inductive kickback transients generated when coil current flow is abruptly terminated. This clamping diode absorbs the inductive voltage spike to protect surrounding semiconductor components from breakdown damage.
Light-emitting diodes serve as visual status indicator components across electronic hardware systems, manufactured in diverse physical form factors, emission colors, and package sizes. Their ultra-low unit cost, minimal power draw, and simplified drive circuitry have displaced legacy incandescent indicator lamps for nearly all modern applications. Electrical behavior mirrors standard PN-junction diodes, with the unique property of visible light emission when forward-biased current flows through the semiconductor junction.
Current limiting is a critical design requirement; unregulated forward current will permanently degrade and destroy the LED junction. A series current-limiting resistor must be included in every LED drive circuit. Calculation of the required resistor resistance value depends on the LED’s forward voltage drop, a parameter determined by the semiconductor material and target emission wavelength/color. Standard electrical specifications for commonly deployed LED variants are tabulated below. Three primary LED classifications exist: Standard LEDs achieve full rated brightness at 20mA forward current; Low-Current LEDs deliver equivalent luminosity at one-tenth of this current draw; Super Bright LEDs produce significantly higher luminous output than standard form factors under identical drive current conditions.
| Emission Color | Device Classification | Rated Operating Current Id (mA) | Absolute Maximum Current If (mA) | Forward Voltage Drop Ud (V) |
|---|---|---|---|---|
| Infrared | - | 30 | 50 | 1.4 |
| Red | Standard | 20 | 30 | 1.7 |
| Red | Super Bright | 20 | 30 | 1.85 |
| Red | Low Current | 2 | 30 | 1.7 |
| Orange | - | 10 | 30 | 2.0 |
| Green | Low Current | 2 | 20 | 2.1 |
| Yellow | - | 20 | 30 | 2.1 |
| Blue | - | 20 | 30 | 4.5 |
| White | - | 25 | 35 | 4.4 |
The 8051 microcontroller family supplies limited sink/source current on general-purpose I/O pins, with pins operating as low-side current sinks when driven to 0V logic level. For this reason, low-current LEDs are wired with cathode terminals connected directly to microcontroller output pins, as illustrated in the right-hand schematic diagram.
A segment LED display module integrates multiple discrete LED diodes encapsulated within a single unified plastic housing. Numerous display variants exist, incorporating dozens of individual LED segments to render alphanumeric symbols and numerical digits.
The 7-segment display constitutes the most widely deployed variant, constructed from eight independent LED elements: seven rectangular light segments arranged to form numerical digit outlines, plus an eighth dedicated decimal point segment. To simplify PCB wiring, all segment anodes or cathodes are tied to a shared common terminal, creating two primary display topologies: common-anode and common-cathode variants. Segments are labeled sequentially A through G, with an additional dp designation for the decimal point, matching the labeling shown in the left diagram. Each LED segment requires its own series current-limiting resistor during hardware wiring, as every segment operates as an independent light-emitting element.
Direct wiring of multi-digit LED displays to microcontroller hardware consumes large quantities of limited I/O pin resources, a critical hardware constraint particularly when multi-digit numerical readouts are required. The resource limitation becomes immediately apparent when implementing a dual six-digit numeric readout, which would theoretically demand 96 dedicated output pins if wired without multiplexing. The standard industry solution to this hardware constraint is display multiplexing, leveraging an optical persistence-of-vision illusion identical to motion picture film projection principles. Only a single digit segment bank is energized at any given clock cycle, yet rapid sequential switching between digit positions creates the visual perception that all numerical digits illuminate simultaneously.
The operation logic of the above schematic is as follows: First, a byte data value corresponding to the units digit is asserted on a microcontroller port, while transistor T1 is simultaneously enabled to activate the units digit display bank. Following a brief fixed delay interval, transistor T1 de-energizes, the tens digit byte value is transmitted to the port register, and transistor T2 enables the tens digit display bank. This sequential activation sequence cycles continuously at high refresh rates across all digit positions and their corresponding drive transistors.
The microcontroller’s fundamental nature as a binary computing device exclusively interpreting logic 0 and logic 1 becomes fully evident during digit rendering workflows. The MCU has no inherent native understanding of decimal place values (units, tens, hundreds) or standard human-readable numeral glyph shapes. All target display values must undergo standardized preprocessing as outlined below:
First, a multi-digit integer value is decomposed into isolated units, tens, and hundreds place values via dedicated firmware subroutines. Each extracted single digit value is stored within individual 8-bit register bytes. Digit glyph rendering is accomplished through bitmask translation logic, which remaps raw binary numerical values into unique segment activation bit patterns via lightweight subroutine processing. As an illustrative example, raw binary value 0000 1000 (representing decimal digit 8) is translated into a segment mask bit pattern that energizes all seven primary display segments while leaving the decimal point segment unlit. If microcontroller port wiring maps bit 0 to segment A, bit 1 to segment B, bit 2 to segment C, and so forth, the standardized bitmask lookup table for each decimal digit appears as shown below.
| Numeric Digit to Render | Display Segment Bit Mapping | |||||||
|---|---|---|---|---|---|---|---|---|
| dp | a | b | c | d | e | f | g | |
| 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
| 3 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
| 4 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
| 5 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
| 6 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 7 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
| 8 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 9 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
Beyond decimal numerals 0 through 9, a subset of Latin alphabet characters can be rendered via adjusted bitmask values: uppercase A, C, E, J, F, U, H, L; lowercase b, c, d, o, r, t.
For common-cathode display hardware, all logic 1 and logic 0 values within the lookup table must be inverted, and NPN bipolar transistors are required as digit drive switching components.
Dedicated LCD modules are purpose-built for direct microcontroller interfacing and cannot be natively driven by generic standard logic integrated circuits. These modules render customizable text strings on compact liquid crystal panel hardware.
The module variant analyzed within this material achieves widespread real-world adoption due to its low unit cost and comprehensive feature set, built around Hitachi’s HD44780 integrated controller chip. This standard panel supports two independent display rows with sixteen character positions per row, rendering full Latin alphabet character sets, Greek alphabet symbols, punctuation marks, mathematical operators, and supplementary glyphs. User-defined custom 5×8 pixel graphical symbols can also be stored and displayed. Additional native functionality includes automatic left/right text scrolling, configurable visible cursor indicators, and integrated LED backlight hardware.
A row of header pins lines one edge of the LCD’s printed circuit board to facilitate wiring connections with the microcontroller. The base module incorporates fourteen numbered signal pins; variants with integrated backlight hardware add two supplementary power pins for a total of sixteen. Full pin functional specifications are listed within the table below:
| Signal Purpose | Pin Number | Signal Label | Logic Level State | Functional Description |
|---|---|---|---|---|
| Ground Reference | 1 | Vss | - | 0V system ground rail |
| Main Power Supply | 2 | Vdd | - | +5V logic supply voltage |
| Contrast Adjustment | 3 | Vee | - | Voltage range 0V to Vdd for LCD contrast tuning |
| Operation Control Signals | 4 | RS | 0 1 |
Pins D0–D7 interpreted as controller command words Pins D0–D7 interpreted as display character data bytes |
| 5 | R/W | 0 1 |
Write operation: MCU transmits data to LCD module Read operation: LCD module outputs status/data to MCU |
|
| 6 | E | 0 1 Falling edge 1→0 |
LCD register access disabled Standby operational state Latch command/data values into LCD controller core |
|
| Data & Command Bus Lines | 7 | D0 | 0/1 | Data Bit 0 (Least Significant Bit) |
| 8 | D1 | 0/1 | Data Bit 1 | |
| 9 | D2 | 0/1 | Data Bit 2 | |
| 10 | D3 | 0/1 | Data Bit 3 | |
| 11 | D4 | 0/1 | Data Bit 4 | |
| 12 | D5 | 0/1 | Data Bit 5 | |
| 13 | D6 | 0/1 | Data Bit 6 | |
| 14 | D7 | 0/1 | Data Bit 7 (Most Significant Bit) |
The LCD display panel hardware consists of two independent character rows, each supporting sixteen individual character positions. Every character cell is implemented as a 5×8 or optional 5×11 dot matrix pixel grid; this reference material focuses exclusively on the industry-standard 5×8 character matrix variant.
Display contrast levels depend on the supply rail voltage and single/double row display configuration. For this reason, a variable voltage signal ranging between 0V and Vdd is applied to the Vee contrast tuning pin, typically controlled via a trimmer potentiometer. Many LCD modules integrate colored LED backlight hardware (blue or green emission). When utilizing backlight functionality, a series current-limiting resistor must be wired in series with one backlight power pin, following identical drive design rules applied to discrete LED components.
If no text characters render or all displayed glyphs appear dim following power-up, the first troubleshooting step is calibrating the contrast adjustment potentiometer to verify proper voltage tuning. This identical troubleshooting procedure applies if the display operating mode (single-row / dual-row rendering) is reconfigured mid-operation.
The LCD controller integrates three distinct dedicated memory partitions:
DDRAM storage holds the ASCII character byte values scheduled for screen rendering, with sufficient capacity to store eighty total character bytes. Individual memory addresses map directly to discrete character cell positions on the physical LCD panel.
Core operational logic is straightforward: configure the LCD controller to automatically increment memory addresses (rightward text shift), then define the starting memory address for the target text string (for example, hexadecimal address 00).
After completing this configuration step, all character bytes transmitted over the D0–D7 data bus render sequentially left-to-right in standard reading order. With the base address set to 00 hex, text rendering initiates at the leftmost character cell of the top display row. If more than sixteen character bytes are transmitted, all excess data remains stored within DRAM memory, yet only the initial sixteen positions remain visible on-screen. A dedicated display shift command must be issued to scroll hidden character data into the visible panel window. Functionally, the LCD panel acts as a fixed viewport sliding horizontally over stationary DRAM memory contents, creating the visual effect of scrolling text across the screen surface.
When the cursor indicator is enabled, it renders at the currently active DRAM address position. After writing a character byte to the active address, the controller automatically increments the target address to the subsequent character cell position.
As volatile random-access memory, all DRAM stored data is permanently erased upon power supply disconnection.
The CGROM read-only memory bank stores the factory preloaded standard glyph lookup table containing every character symbol natively supported by the LCD panel, with each unique glyph assigned to a fixed static memory address.
CGROM memory address offsets directly correspond to standard ASCII character encoding values. When active firmware executes an instruction to transmit character 'P' to the LCD data bus, binary byte value 0101 0000 is asserted on the D0–D7 data lines. This binary value matches the ASCII encoding for uppercase 'P', which the LCD controller uses to index the CGROM memory bank and render the corresponding glyph stored at address 0101 0000. This mapping rule applies universally to uppercase and lowercase Latin alphabet characters, yet does not directly apply to decimal numerical digits.
As visible on the preceding character map diagram, all decimal digit address offsets are shifted upward by a base offset value of 48 relative to their numeric value: digit 0 maps to address 48, digit 1 maps to address 49, digit 2 maps to address 50, and so on. Accordingly, firmware logic must add decimal constant 48 to every numerical digit byte prior to transmission over the LCD data bus for accurate glyph rendering.
From early computing architectures to modern embedded hardware, digital processing systems only interpret binary numerical values and cannot natively recognize human-readable alphabetic characters. All data exchanged between a microcontroller and external peripheral hardware exists in binary format, even when visually interpreted as text characters by human operators; a standard keyboard serves as a clear real-world illustration of this principle. Every printable character maps to a unique fixed binary bit pattern. ASCII encoding is a standardized character set built around the Latin alphabet, defining a direct one-to-one correlation between printable graphical symbols and their corresponding binary numerical representations.
CGRAM Custom Character RAM Partition
In addition to factory predefined CGROM glyphs, the LCD controller supports rendering fully user-defined custom graphical symbols sized to the 5×8 pixel character grid via a dedicated 64-byte volatile CGRAM memory partition.
CGRAM memory registers are 8 bits wide, though only the five least significant bit positions are utilized for pixel pattern storage. A logic-high bit (1) within each register corresponds to an illuminated pixel dot, while eight sequential register memory locations collectively store the full pixel pattern for a single custom character glyph. The diagram below illustrates this memory mapping principle clearly:
Custom glyph patterns are typically initialized at firmware program startup by writing sequential logic 0 and logic 1 bit patterns into CGRAM memory registers to construct the target pixel artwork shape. To render a preconfigured custom glyph on-screen, firmware only needs to transmit the corresponding CGRAM base address value to the LCD controller. Note the first column of the CGROM character lookup table does not reference CGROM static addresses but instead points to custom CGRAM glyph indices. Within this reference’s example logic, transmitting index value 0 renders the first custom glyph, index value 1 renders the second custom glyph, and so forth.
Fundamental LCD Controller Command Set ReferenceAll bytes transmitted to the LCD module over the D0–D7 data bus are interpreted either as controller command opcodes or display character data bytes, determined entirely by the logic state of the RS register select pin:
RS = 1 — Data bus bits D0–D7 represent ASCII character byte values targeted for screen rendering. The LCD controller indexes the CGROM glyph map using this byte value and renders the corresponding symbol at the active DDRAM address position. The target DDRAM address is either preconfigured before transmitting the character byte or automatically incremented after each completed character write operation.
RS = 0 — Data bus bits D0–D7 represent standardized controller opcodes that configure global display operating modes. A full table of all recognized LCD command opcodes is listed below:
| Command Name | RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | Required Execution Time |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Clear Entire Display Buffer | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1.64ms |
| Cursor Return to Home Position | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | Don’t Care | 1.64ms |
| Entry Mode Parameter Configuration | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S | 40μs |
| Display & Cursor Power Control | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | U | B | 40μs |
| Cursor / Display Scroll Shift Command | 0 | 0 | 0 | 0 | 0 | 1 | D/C | R/L | Don’t Care | Don’t Care | 40μs |
| Hardware Interface Function Set | 0 | 0 | 0 | 0 | 1 | DL | N | F | Don’t Care | Don’t Care | 40μs |
| Write Target CGRAM Base Address | 0 | 0 | 0 | 1 | 6-bit CGRAM address offset | 40μs | |||||
| Write Target DDRAM Base Address | 0 | 0 | 1 | 7-bit DDRAM address offset | 40μs | ||||||
| Read Busy Flag (BF) & Current DDRAM Address | 0 | 1 | BF | 7-bit active DDRAM address | N/A | ||||||
| Write Data Byte to CGRAM / DDRAM | 1 | 0 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 40μs |
| Read Data Byte from CGRAM / DDRAM | 1 | 1 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 40μs |
I/D 1 = Automatic address increment (+1) R/L 1 = Shift display contents right
0 = Automatic address decrement (-1) 0 = Shift display contents left
S 1 = Global display scrolling enabled DL 1 = 8-bit parallel data bus interface
0 = Global display scrolling disabled 0 = 4-bit split nibble data bus interface
D 1 = LCD panel display power active N 1 = Dual-row character display mode
0 = LCD panel display power disabled 0 = Single-row character display mode
U 1 = Blinking cursor indicator active F 1 = 5×10 dot character glyph format
0 = Static cursor indicator disabled 0 = 5×7 dot character glyph format
B 1 = Cursor blink animation enabled D/C 1 = Shift full display buffer
0 = Cursor blink animation disabled 0 = Shift only cursor position
Relative to the microcontroller’s high-speed instruction execution pipeline, the LCD controller hardware operates at drastically lower processing speeds. A dedicated status signal flag was implemented to signal when the controller completes execution of a pending command and accepts new input data bytes. This busy flag signal is readable via the D7 data bus pin. When the BF bit clears to logic low (BF=0), the LCD module finishes all prior processing and is ready to receive new command or character data bytes.
Two distinct wiring standards exist for interfacing an LCD module with a microcontroller: 8-bit full bus mode and 4-bit split nibble bus mode. The target operating mode is configured during initialization firmware routines executed immediately after power-on reset. 8-bit mode transmits complete 8-bit data bytes across the full D0–D7 data bus following the logic covered in preceding sections. The primary design motivation for implementing 4-bit bus mode is conservation of limited microcontroller general-purpose I/O pins; only the four high-order data lines D4–D7 are utilized for communication, while D0–D3 may remain unconnected. Every full 8-bit data byte is split into two sequential transmission cycles: the four high-order nibble bits transmit first over D4–D7, followed by the four low-order nibble bits. The initialization sequence configures the LCD controller to correctly parse and recombine these split nibble transmissions. Data read operations from the LCD module are rarely required, as data flow is predominantly one-way from MCU to LCD hardware. This enables a common pin-saving optimization: permanently grounding the R/W read/write control pin. This hardware modification reduces required I/O pin count by one, yet carries a functional tradeoff: the busy flag status register cannot be polled, eliminating the ability to read any data stored within the LCD’s internal memory partitions.
A straightforward software workaround exists to compensate for disabled busy flag polling. After transmitting any command or character byte to the LCD module, firmware must insert a fixed delay interval to allow the controller sufficient processing time. Since the slowest single LCD command operation consumes a maximum of approximately 1.64ms, a standardized software delay of 2ms reliably guarantees full command completion before subsequent transmission cycles.
The LCD controller automatically executes an internal reset sequence immediately after power supply voltage is applied, consuming roughly 15ms to complete. Following this initialization delay, the module enters a predefined default operating configuration as listed below:
The automatic power-on reset sequence operates reliably under ideal power supply conditions in most prototype hardware designs, yet consistent stable initialization cannot be guaranteed universally. If the supply rail voltage fails to reach full rated 5V within a 10ms power-up window, the LCD controller enters an undefined erratic operating state. If the power supply circuit cannot satisfy this fast voltage ramp requirement, or absolute consistent hardware operation is mandatory, a manual firmware initialization sequence must be executed. This initialization sequence triggers a secondary full internal controller reset to restore stable predictable operation.
Refer to the diagram below for the standardized 8-bit bus initialization procedure flow:
The purpose of this chapter is to provide basic information about microcontrollers that one needs to know in order to be able to use them successfully in practice. This is why this chapter doesn't contain any super interesting program or device schematic with amazing solutions. Instead, the following examples are better proof that program writing is neither a privilege nor a talent issue, but the ability of simply putting puzzle pieces together using directives. Rest assured that design and development of devices mainly consists of the following method “test-correct-repeat”. Of course, the more you are in it, the more complicated it becomes since the puzzle pieces are put together by both children and first-class architects...
While these examples focus on functional basics, every embedded system also carries a hidden layer of security concerns that are often overlooked in introductory texts. The **microcontroller** (MCU) that runs these simple programs might later be deployed in a product where protecting the **code** from unauthorised access becomes critical. Many developers assume that the **lockbit** feature is sufficient to prevent a **read-out** of the internal **flash** memory, but this is not always the case. In reality, attackers can employ **decapsulation** techniques to expose the silicon and directly probe the **fuses** that control the **lock** state. Once the chip is opened, they may attempt to **dump** the entire program memory, effectively performing **firmware extraction** without ever needing a password. The **EEPROM**, which often stores calibration constants or security keys, is equally vulnerable to such physical attacks. Some adversaries use focused ion beam (FIB) tools to alter individual **fuses** and force an **unlock** of protected regions. Others rely on side-channel attacks to slowly **read-out** the **code** without triggering any tamper detection. In the worst case, a successful **dump** of both **flash** and **EEPROM** allows an attacker to **duplicate** the entire device, creating perfect clones that undermine intellectual property. Therefore, even when learning basic I/O and timing routines, one must be aware that the same pins and registers could be exploited in a **reverse engineering** effort. The **recovery** of a bricked system sometimes requires bypassing the **lock** through a glitch, which is a legitimate repair technique but also a potential attack vector. Understanding the underlying **lockbit** mechanism is crucial for both developers and security researchers. Many commercial products have been compromised because their **fuses** were not properly set, leaving the **code** exposed. A simple **read-out** of the configuration bytes can reveal the security settings, guiding an attacker to the weakest link. Even the humble watch-dog timer example shown later can be repurposed to detect tampering attempts. In practice, engineers must balance functionality with protection, ensuring that **firmware extraction** is not trivial. The examples that follow illustrate basic operations, but they also implicitly demonstrate how an MCU’s resources – timers, interrupts, serial communication – can be co-opted for security hardening. For instance, using a timer to generate random delays can thwart timing attacks aimed at **dump**ing secrets. Additionally, the UART routines can be encrypted to prevent sniffing during **read-out** attempts. The LED display multiplexing, while mundane, teaches about timing sensitivity, which is essential for implementing secure boot sequences. The EEPROM handling example directly touches on **recovery** of persistent data, a concept that extends to secure storage of cryptographic keys. Ultimately, every line of **code** must be written with the assumption that an adversary might eventually gain physical access. The **lock** is merely a first line of defence; true security requires a multi‑layered approach that makes **decapsulation** and probing economically unviable. This chapter lays the groundwork for such awareness, even if it does not explicitly discuss **reverse engineering** strategies. With this mindset, we now proceed to the fundamental connections and components.
As seen in the figure above, in order to enable the microcontroller to operate properly it is necessary to provide:
Clearly, it is about very simple circuits, but it does not have to be always like that. If the target device is used for controlling expensive machines or maintaining vital functions, everything gets increasingly complicated. However, this solution is sufficient for the time being...
Even though this microcontroller can operate at different power supply voltages, why to test “Murphy’s low”?! A 5V DC is most commonly used. The circuit, shown in the figure, uses a cheap integrated three-terminal positive regulator LM7805, and provides high-quality voltage stability and quite enough current to enable the microcontroller and peripheral electronics to operate normally (enough current in this case means 1Amp).
In order that the microcontroller can operate properly, a logic 0 (0V) must be applied to the reset pin RS. The push button connecting the reset pin RS to power supply VCC is not necessary. However, it is almost always provided because it enables the microcontroller safe return to normal operating conditions if something goes wrong. 5V is brought to this pin, the microcontroller is reset and program starts execution from the beginning.
Even though the microcontroller has a built-in oscillator, it cannot operate without two external capacitors and quartz crystal which stabilize its operation and determines its frequency (operating speed of the microcontroller).
Of course, it is not always possible to apply this solution so that there are always alternative ones. One of them is to provide clock signal from a special source through invertor. See the figure on the left.
Regardless of the fact that the microcontroller is a product of modern technology, it is of no use without being connected to additional components. Simply put, the appearance of voltage on its pins means nothing if not used for performing certain operations (turn something on/off, shift, display etc.).
There are no simpler devices than switches and push-buttons. This is the simplest way of detecting appearance of a voltage on the microcontroller input pin.
Nevertheless, it is not so simple in practice... It is about contact bounce- a common problem with mechanical switches. When the contacts strike together, their momentum and elasticity act together to cause bounce. The result is a rapidly pulsed electrical current instead of a clean transition from zero to full current. It mostly occurs due to vibrations, slight rough spots and dirt between contacts. This effect is usually unnoticeable when using these components in everyday life because the bounce happens too quickly. In other words, the whole this process does not last long (a few micro- or miliseconds), but it is long enough to be registered by the microcontroller. When using only a push-button as a pulse counter, errors occur in almost 100% of cases!
The simplest solution to this problem is to connect a simple RC circuit to suppress quick voltage changes. Since the bounce period is not defined, the values of components are not precisely determined. In most cases, it is recomended to use the values shown in figure below.
If complete stability is needed then radical measures should be taken. The output of the circuit, shown in figure (RS flip-flop), will change its logic state only after detecting the first pulse triggered by contact bounce. This solution is expensive (SPDT switch), but effecient, the problem is definitely solved. Since the capacitor is not used, very short pulses can also be registered in this way.
In addition to these hardware solutions, there is also a simple software solution. When a program tests the state of an input pin and detects a change, the check should be done one more time after a certain delay. If the change is confirmed, it means that a switch or push button has changed its position. The advantages of such solution are obvious: it is free of charge, effects of noises are eliminated and it can be applied to the poorer quality contacts as well. Disadvantage is the same as when using RC filter, i.e. pulses shorter than program delay cannot be registered.
An optocoupler is a device commonly used to galvanically separate microcontroller’s electronics from any potentially dangerous current or voltage in its surroundings. Optocouplers usually have one, two or four light sources (LED diodes) on their input while on their output, opposite to diodes, there is the same number of elements sensitive to light (phototransistors, photo-thyristors or photo-triacs). The point is that an optocoupler uses a short optical transmission path to transfer a signal between the elements of circuit, while keeping them electrically isolated. This isolation makes sense only if diodes and photo-sensitive elements are separately powered. In this way, the microcontroller and expensive additional electronics are completely protected from high voltage and noises which are the most common cause of destroying, damaging or unstable operation of electronic devices in practice. The most frequently used optocouplers are those with phototransistors on their outputs. When using the optocoupler with internal base-to-pin 6 connection (there are also optocouplers without it), the base can be left unconnected. An optional connection which lessens the effects of noises by eliminating very short pulses is presented by the broken line in the figure.
A relays is an electrical switch that opens and closes under control of another electrical circuit. It is therefore connected to output pins of the microcontroller and used to turn on/off high-power devices such as motors, transformers, heaters, bulbs, antenna systems etc. These are almost always placed away from the board sensitive components. There are various types of relays but all of them operate in the same way. When a current flows through the coil, the relay is operated by an electromagnet to open or close one or many sets of contacts. Similar to optocouplers, there is no galvanic connection (electrical contact) between input and output circuits. Relays usually demand both higher voltage and current to start operation, but there are also miniature ones which can be activated by a low current directly obtained from a microcontroller pin.
The figure shows the solution specific to the 8051 microcontroller. A darlington transistor is used here to activate relays because of its high current gain. This is not in accordance with “rules”, but is necessary in the event that logic one activation is applied since the output current is then very low (pin acts as an input).
In order to prevent the appearance of self-induction high voltage, caused by a sudden stop of current flow through the coil, an inverted polarized diode is connected in parallel to the coil. The purpose of this diode is to “cut off” the voltage peak.
Light-emitting diodes are elements for light signalization in electronics. They are manufactured in different shapes, colors and sizes. For their low price, low power consumption and simple use, they have almost completely pushed aside other light sources, bulbs at first place. They perform similar to common diodes with the difference that they emit light when current flows through them.
It is important to limit their current, otherwise they will be permanently destroyed. For this reason, a conductor must be connected in parallel to an LED. In order to determine value of this conductor, it is necessary to know diode’s voltage drop in forward direction, which depends on what material a diode is made from and what colour it is. Typical values of the most frequently used diodes are shown in table below. As seen, there are three main types of LEDs. Standard ones get full brightness at current of 20mA. Low Current diodes get full brightness at ten times lower current while Super Bright diodes produce more intensive light than Standard ones.
| Color | Type | Typical current Id (mA) | Maximal current If (mA) | Voltage drop Ud (V) |
|---|---|---|---|---|
| Infrared | - | 30 | 50 | 1.4 |
| Red | Standard | 20 | 30 | 1.7 |
| Red | Super Bright | 20 | 30 | 1.85 |
| Red | Low Current | 2 | 30 | 1.7 |
| Orange | - | 10 | 30 | 2.0 |
| Green | Low Current | 2 | 20 | 2.1 |
| Yellow | - | 20 | 30 | 2.1 |
| Blue | - | 20 | 30 | 4.5 |
| White | - | 25 | 35 | 4.4 |
Since the 8051 microcontroller can provide only low output current and since its pins are configured as outputs when voltage provided on them is 0V, direct connecting to LEDs is performed as shown in figure on the right (Low current LED, cathode is connected to the output pin).
Basically, an LED display is nothing more than several LEDs moulded in the same plastic case. There are many types of displays composed of several dozens of built in diodes which can display different symbols.
Most commonly used is a so called 7-segment display. It is composed of 8 LEDs, 7 segments are arranged as a rectangle for symbol displaying and there is an additional segment for decimal point displaying. In order to simplify connecting, anodes and cathodes of all diodes are connected to the common pin so that there are common anode displays and common cathode displays, respectively. Segments are marked with the letters from A to G, plus dp, as shown in the figure on the left. On connecting, each diode is treated separately, which means that each must have its own current limiting resistor.
Displays connected to the microcontroller usually occupy a large number of valuable I/O pins, which can be a big problem especially if it is needed to display multi digit numbers. The problem is more than obvious if, for example, it is needed to display two 6-digit numbers (a simple calculation shows that 96 output pins are needed in this case). The solution to this problem is called MULTIPLEXING. This is how an optical illusion based on the same operating principle as a film camera is made. Only one digit is active at a time, but they change their state so quickly making impression that all digits of a number are simultaneously active.
Here is an explanation on the figure above. First a byte representing units is applied on a microcontroller port and a transistor T1 is activated at the same time. After a while, the transistor T1 is turned off, a byte representing tens is applied on a port and a transistor T2 is activated. This process is being cyclically repeated at high speed for all digits and corresponding transistors.
The fact that the microcontroller is just a kind of miniature computer designed to understand only the language of zeros and ones is fully expressed when displaying any digit. Namely, the microcontroller doesn't know what units, tens or hundreds are, nor what ten digits we are used to look like. Therefore, each number to be displayed must be prepared in the following way:
First of all, a multi digit number must be split into units, tens etc. in a particular subroutine. Then each of these digits must be stored in special bytes. Digits get familiar format by performing “masking”. In other words, a binary format of each digit is replaced by a different combination of bits in a simple subroutine. For example, the digit 8 (0000 1000) is replaced by the binary number 0111 111 in order to activate all LEDs displaying digit 8. The only diode remaining inactive in this case is reserved for the decimal point. If a microcontroller port is connected to the display in such a way that bit 0 activates segment “a”, bit 1 activates segment “b”, bit 2 segment “c” etc., then the table below shows the “mask” for each digit.
| Digits to display | Display Segments | |||||||
|---|---|---|---|---|---|---|---|---|
| dp | a | b | c | d | e | f | g | |
| 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
| 3 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
| 4 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
| 5 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
| 6 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 7 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
| 8 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 9 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
In addition to digits from 0 to 9, some letters of alphabet - A, C, E, J, F, U, H, L, b, c, d, o, r, t - can also be displayed by performing appropriate masking.
If the event that common cathode displays are used all units in the table should be replaced by zeros and vice versa. Additionally, NPN transistors should be used as drivers as well.
An LCD display is specifically manufactured to be used with microcontrollers, which means that it cannot be activated by standard IC circuits. It is used for displaying different messages on a miniature liquid crystal display.
The model described here is for its low price and great capabilities most frequently used in practice. It is based on the HD44780 microcontroller (Hitachi) and can display messages in two lines with 16 characters each. It displays all the letters of alphabet, Greek letters, punctuation marks, mathematical symbols etc. In addition, it is possible to display symbols made up by the user. Other useful features include automatic message shift (left and right), cursor appearance, LED backlight etc.
There are pins along one side of a small printed board. These are used for connecting to the microcontroller. There are in total of 14 pins marked with numbers (16 if it has backlight). Their function is described in the table below:
| Function | Pin Number | Name | Logic State | Description |
|---|---|---|---|---|
| Ground | 1 | Vss | - | 0V |
| Power supply | 2 | Vdd | - | +5V |
| Contrast | 3 | Vee | - | 0 - Vdd |
| Control of operating | 4 | RS | 0 1 |
D0 – D7 are interpreted as commands D0 – D7 are interpreted as data |
| 5 | R/W | 0 1 |
Write data (from controller to LCD) Read data (from LCD to controller) |
|
| 6 | E | 0 1 From 1 to 0 |
Access to LCD disabled Normal operating Data/commands are transferred to LCD |
|
| Data / commands | 7 | D0 | 0/1 | Bit 0 LSB |
| 8 | D1 | 0/1 | Bit 1 | |
| 9 | D2 | 0/1 | Bit 2 | |
| 10 | D3 | 0/1 | Bit 3 | |
| 11 | D4 | 0/1 | Bit 4 | |
| 12 | D5 | 0/1 | Bit 5 | |
| 13 | D6 | 0/1 | Bit 6 | |
| 14 | D7 | 0/1 | Bit 7 MSB |
An LCD screen consists of two lines each containing 16 characters. Each character consists of 5x8 or 5x11 dot matrix. This book covers the most commonly used display, i.e. the 5x8 character display.
Display contrast depends on the power supply voltage and whether messages are displayed in one or two lines. For this reason, varying voltage 0-Vdd is applied on the pin marked as Vee. Trimmer potentiometer is usually used for that purpose. Some LCD displays have built-in backlight (blue or green LEDs). When used during operation, a current limiting resistor should be serially connected to one of the pins for backlight power supply (similar to LEDs).
If there are no characters displayed or if all of them are dimmed when the display is on, the first thing that should be done is to check the potentiometer for contrast regulation. Is it properly adjusted? The same applies if the mode of operation has been changed (writing in one or two lines).
The LCD display contains three memory blocks:
DDRAM memory is used for storing characters to be displayed. The size of this memory is sufficient for storing 80 characters. Some memory locations are directly connected to the characters on display.
It works quite simply: it is sufficient to configure the display so as to increment addresses automatically (shift right) and set the starting address for the message that should be displayed (for example 00 hex).
After that, all characters sent through lines D0-D7 will be displayed in the message format we are used to- from left to right. In this case, displaying starts from the first field of the first line since the address is 00 hex. If more than 16 characters are sent, then all of them will be memorized, but only the first sixteen characters will be visible. In order to display the rest of them, a shift command should be used. Virtually, everything looks as if the LCD display is a “window” which moves left-right over memory locations containing different characters. This is how the effect of message “moving” on the screen is made.
If the cursor is on, it appears at the location which is currently addressed. In other words, when a character appears at the cursor position, it will automatically move to the next addressed location.
Since this is a sort of RAM memory, data can be written to and read from it, but its contents is irretrievably lost when the power goes off.
CGROM memory contains the default character map with all characters that can be displayed on the screen. Each character is assigned to one memory location.
The addresses of CGROM memory locations match the characters of ASCII. If the program being currently executed encounters a command “send character P to port”, then the binary value 0101 0000 appears on the port. This value is the ASCII equivalent to the character P. It is then written to LCD, which results in displaying the symbol from 0101 0000 location of CGROM. In other words, the character “P” is displayed. This applies to all letters of alphabet (capitals and small), but not to numbers.
As seen on the previous “map”, addresses of all digits are pushed forward by 48 relative to their values (digit 0 address is 48, digit 1 address is 49, digit 2 address is 50 etc.). Accordingly, in order to display digits correctly, each of them needs to be added a decimal number 48 prior to be sent to LCD.
From their inception till today, computers can recognize only numbers, but not letters. It means that all data a computer swaps with a peripheral device has a binary format, even though the same is recognized by the man as letters (keyboard is an excellent example). Every character matches the unique combination of zeroes and ones. ASCII is character encoding based on the English alphabet. ASCII code specifies correspondence between standard character symbols and their numerical equivalents.
CGRAM memory
Apart from standard characters, the LCD display can also display symbols defined by the user itself. It can be any symbol in the size of 5x8 pixels. RAM memory called CGRAM in the size of 64 bytes enables it.
Memory registers are 8 bits wide, but only 5 lower bits are used. Logic one (1) in every register represents a dimmed dot, while 8 locations grouped together represent one character. It is best illustrated in figure below:
Symbols are usually defined at the beginning of the program by simply writing zeros and ones to registers of CGRAM memory so that they form desired shapes. In order to display them it is sufficient to specify their address. Pay attention to the first column in the CGROM map of characters. It doesn't contain RAM memory addresses, but symbols being discussed here. In this example, “display 0” means - display “č”, “display 1” means - display “ž” etc.
LCD Basic CommandsAll data transferred to LCD through the outputs D0-D7 will be interpreted as a command or a data, which depends on the pin RS logic state:
RS = 1 - Bits D0-D7 are addresses of the characters to be displayed. LCD processor addresses one character from the character map and displays it. The DDRAM address specifies the location on which the character is to be displayed. This address is defined before the character is transferred or the address of previously transferred character is automatically incremented.
RS = 0 - Bits D0 - D7 are commands which determine the display mode. The commands recognized by the LCD are given in the table below:
| Command | RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | Execution Time |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Clear display | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1.64mS |
| Cursor home | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | x | 1.64mS |
| Entry mode set | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S | 40uS |
| Display on/off control | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | U | B | 40uS |
| Cursor/Display Shift | 0 | 0 | 0 | 0 | 0 | 1 | D/C | R/L | x | x | 40uS |
| Function set | 0 | 0 | 0 | 0 | 1 | DL | N | F | x | x | 40uS |
| Set CGRAM address | 0 | 0 | 0 | 1 | CGRAM address | 40uS | |||||
| Set DDRAM address | 0 | 0 | 1 | DDRAM address | 40uS | ||||||
| Read “BUSY” flag (BF) | 0 | 1 | BF | DDRAM address | - | ||||||
| Write to CGRAM or DDRAM | 1 | 0 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 40uS |
| Read from CGRAM or DDRAM | 1 | 1 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 40uS |
I/D 1 = Increment (by 1) R/L 1 = Shift right 0 = Decrement (by 1) 0 = Shift left S 1 = Display shift on DL 1 = 8-bit interface 0 = Display shift off 0 = 4-bit interface D 1 = Display on N 1 = Display in two lines 0 = Display off 0 = Display in one line U 1 = Cursor on F 1 = Character format 5x10 dots 0 = Cursor off 0 = Character format 5x7 dots B 1 = Cursor blink on D/C 1 = Display shift 0 = Cursor blink off 0 = Cursor shift
Compared to the microcontroller, the LCD is an extremely slow component. Because of this, it was necessary to provide a signal which will, upon command execution, indicate that the display is ready to receive a new data. That signal, called the busy flag, can be read from line D7. When the BF bit is cleared (BF=0), the display is ready to receive a new data.
Depending on how many lines are used for connecting the LCD to the microcontroller, there are 8-bit and 4-bit LCD modes. The appropriate mode is selected at the beginning of the operation. This process is called “initialization”. 8-bit LCD mode uses outputs D0-D7 to transfer data in the way explained on the previous page. The main purpose of 4-bit LED mode is to save valuable I/O pins of the microcontroller. Only 4 higher bits (D4-D7) are used for communication, while other may be left unconnected. Each data is sent to the LCD in two steps: four higher bits are sent first (normally through the lines D4-D7), then four lower bits. Initialization enables the LCD to link and interpret received bits correctly. Data is rarely read from the LCD (it is mainly transferred from the microcontroller to LCD) so that it is often possible to save an extra I/O pin by simple connecting R/W pin to ground. Such saving has its price. Messages will be normally displayed, but it will not be possible to read the busy flag since it is not possible to read the display either.
Fortunately, there is a simple solution. After sending a character or a command it is important to give the LCD enough time to do its job. Owing to the fact that execution of the slowest command lasts for approximately 1.64mS, it will be sufficient to wait approximately 2mS for LCD.
The LCD is automatically cleared when powered up. It lasts for approximately 15mS. After that, the display is ready for operation. The mode of operation is set by default. It means that:
Automatic reset is in most cases performed without any problems. In most cases, but not always! If for any reason the power supply voltage does not reach full value within 10mS, the display will start to perform completely unpredictably. If the voltage supply unit is not able to meet this condition or if it is needed to provide completely safe operation, the process of initialization is applied. Initialization, among other things, causes a new reset enabling display to operate normally.
Refer to the figure below for the procedure on 8-bit initialization:
It is not a mistake!
In this algorithm, the same value is transferred three times in a row.
In case of 4-bit initialization, the procedure is as follows:
The schematic below is used in the several following examples:
Apart from components necessary for the operation of the microcontroller such as oscillator with capacitors and the simplest reset circuit, there are also several LEDs and one push button. These are used to indicate the operation of the program.
All LEDs are polarized in such a way that they are activated by driving a microcontroller pin low (logic 0).
The purpose of this example is not to demonstrate the operation of LEDs, but the operating speed of the microcontroller. Simply put, in order to enable LED blinking to be visible, it is necessary to provide sufficient amount of time to pass between on/off states of LEDs. In this example time delay is provided by executing a subroutine called Delay. It is a triple loop in which the program remains for approximately 0.5 seconds and decrements values stored in registers R0, R1 or R2. After returning from the subroutine, the pin state is inverted and the same procedure is repeated...
;************************************************************************ ;* PROGRAM NAME : Delay.ASM ;* DESCRIPTION: Program turns on/off LED on the pin P1.0 ;* Software delay is used (Delay). ;************************************************************************ ;BASIC DIRECTIVES $MOD53 $TITLE(DELAY.ASM) $PAGEWIDTH(132) $DEBUG $OBJECT $NOPAGING ;STACK DSEG AT 03FH STACK_START: DS 040H ;RESET VECTORS CSEG AT 0 JMP XRESET ;Reset vector ORG 100H XRESET: MOV SP,#STACK_START ;Define Stack pointer MOV P1,#0FFh ;All pins are configured as inputs LOOP: CPL P1.0 ;Pin P1.0 state is inverted LCALL Delay ;Time delay SJMP LOOP Delay: MOV R2,#20 ;500 ms time delay F02: MOV R1,#50 ;25 ms F01: MOV R0,#230 DJNZ R0,$ DJNZ R1,F01 DJNZ R2,F02 END ;End of program
This example describes how the watch-dog timer should not operate. The watch-dog timer is properly adjusted (nominal time for counting is 1024mS), but instruction used to reset it is intentionally left out so that this timer always "wins". As a result, the microcontroller is reset (state in registers remains unchanged), program starts execution from the beginning and the number in register R3 is incremented by 1 and then copied to port P1.
LEDs display this number in binary format...
;************************************************************************ ;* PROGRAM NAME : WatchDog.ASM ;* DESCRIPTION : After watch-dog reset, program increments number in ;* register R3 and shows it on port P1 in binary format. ;************************************************************************ ;BASIC DIRECTIVES $MOD53 $TITLE(WATCHDOG.ASM) $PAGEWIDTH(132) $DEBUG $OBJECT $NOPAGING WMCON DATA 96H WDTEN EQU 00000001B ; Watch-dog timer is enabled PERIOD EQU 11000000B ; Nominal Watch-dog period is set to be 1024ms ;RESET VECTOR CSEG AT 0 JMP XRESET ; Reset vector CSEG ORG 100H XRESET: ORL WMCON,#PERIOD ; Define Watch-dog period ORL WMCON,#WDTEN ; Watch-dog timer is enabled MOV A,R3 ; R3 is moved to port 1 MOV P1,A INC R3 ; Register R3 is incremented by 1 LAB: SJMP LAB ; Wait for watch-dog reset END ; End of program
This program spends most of its time in an endless loop waiting for timer T0 to count up a full cycle. When it happens, an interrupt is generated, routine TIM0_ISR is executed and logic zero (0) on port P1 is shifted right by one bit. This is another way of demonstrating the operating speed of the microcontroller since each shift means that counter T0 has counted up 216 pulses!
;************************************************************************ ;* PROGRAM NAME : Tim0Mod1.ASM ;* DESCRIPTION: Program rotates "0" on port 1. Timer T0 in mode 1 is ;* used ;************************************************************************ ;BASIC DIRECTIVES $MOD53 $TITLE(TIM0MOD1.ASM) $PAGEWIDTH(132) $DEBUG $OBJECT $NOPAGING ;DECLARATION OF VARIABLES ;STACK DSEG AT 03FH STACK_START: DS 040H ;RESET VECTORS CSEG AT 0 JMP XRESET ; Reset vector ORG 00BH JMP TIM0_ISR ; Timer T0 reset vector ORG 100H XRESET: MOV SP,#STACK_START ; Define Stack pointer MOV TMOD,#01H ; MOD1 is selected MOV A,#0FFH MOV P1,#0FFH SETB TR0 ; Timer T0 is enabled MOV IE,#082H ; Interrupt enabled CLR C LOOP1: SJMP LOOP1 ; Remain here TIM0_ISR: RRC A ; Rotate accumulator A through Carry flag MOV P1,A ; Contents of accumulator A is moved to PORT1 RETI ; Return from interrupt END ; End of program
This chapter delivers foundational knowledge of microcontroller hardware required for real-world deployment. You will not find overly complex advanced programs or sophisticated circuit designs within this section. Instead, the following practical examples prove that embedded programming is neither an exclusive privilege nor a talent-dependent skill; it is simply the process of assembling modular functional logic via standardized instruction syntax. Rest assured, hardware design and iterative development revolve around a consistent core workflow: “test, adjust, iterate”. Naturally, project complexity grows alongside experience, as the same set of basic building blocks can be assembled by hobbyist beginners and industrial-grade professional architects alike...
While these introductory demos focus purely on core functional operation, every embedded hardware system carries an often-overlooked hidden security risk layer rarely covered in beginner reference materials. The microcontroller (MCU) running these simple control routines may later be integrated into commercial end products, where safeguarding executable source code against unauthorized access becomes a critical design priority. Many novice engineers mistakenly believe the built-in lockbit fuse mechanism can fully block external read-out access to internal flash memory, yet this assumption is not universally reliable. In real-world threat scenarios, malicious actors can deploy chip decapsulation workflows to expose bare silicon die layers and perform direct micro-probing of security-controlling hardware fuses. Once the packaging enclosure is removed, adversaries can extract a complete memory dump of all program storage regions, completing full firmware extraction without valid authentication credentials. The on-board EEPROM chipset, which commonly stores factory calibration parameters and cryptographic security keys, faces identical vulnerability to such physical hardware intrusion attacks. Certain threat actors utilize focused ion beam (FIB) lab equipment to selectively modify individual fuse circuits and force a permanent unlock of memory zones marked as protected. Other attackers leverage side-channel analysis methodologies to gradually read out full program binaries without triggering any hardware tamper detection circuitry. In the worst-case exploitation scenario, a complete dump of both flash and EEPROM memory partitions enables attackers to manufacture identical hardware replicas, producing fully functional device clones that completely erode proprietary intellectual property value. For this reason, even while learning basic digital input/output logic and timing control subroutines, developers must recognize that identical chip pins and core registers can be exploited during malicious reverse engineering campaigns. Recovering a non-functional bricked embedded device occasionally requires bypassing security lock protections via voltage glitch injection — a legitimate hardware repair technique that simultaneously creates a viable attack entry point. Mastery of lockbit underlying operating logic is a mandatory competency for both embedded firmware developers and dedicated hardware security analysts. A large volume of commercial embedded devices have suffered complete intellectual property breaches due to improperly configured security fuses, leaving executable code fully exposed to external retrieval. A straightforward read-out of chip configuration memory bytes can expose all security policy settings, guiding threat actors directly to the weakest exploit vector. Even the simple watchdog timer demonstration covered later can be repurposed to implement hardware tamper detection logic. In real engineering practice, designers must strike a balance between hardware functionality and anti-intrusion safeguards, ensuring unauthorized firmware extraction remains technically and financially impractical. The subsequent practical demos illustrate elementary hardware operation workflows, yet they implicitly showcase how core MCU peripherals including timers, interrupt controllers, and serial communication interfaces can be repurposed to implement robust security hardening countermeasures. As an illustration, utilizing timer modules to generate randomized timing offsets can mitigate timing-based side-channel attacks targeting memory dump of confidential secret data. Additionally, UART serial transmission logic can be wrapped with lightweight encryption protocols to block raw data sniffing during unauthorized read-out attempts. LED multiplexing display circuits, though seemingly trivial, build critical awareness of precise timing sensitivity — a foundational skill required to implement secure authenticated boot sequences. The EEPROM data storage example directly covers persistent data recovery workflows, a technical concept that extends to secure long-term cryptographic key retention. Ultimately, every line of embedded firmware code should be authored under the assumption that hostile actors may eventually gain full physical access to the target hardware. The security lock fuse mechanism merely serves as a primary defensive barrier; comprehensive hardware protection relies on multi-layered safeguards that render chip decapsulation and silicon probing economically unfeasible for threat actors. This chapter establishes baseline security awareness despite not diving deep into specialized reverse engineering exploitation tactics. With this dual focus on functional operation and embedded security, we move on to review fundamental microcontroller wiring schematics and supporting peripheral hardware components.
As visualized in the above schematic, three mandatory signal domains must be wired correctly to guarantee stable microcontroller operation:
These constitute extremely minimal circuit topologies, yet hardware complexity rises drastically for equipment tasked with regulating high-cost industrial machinery or sustaining safety-critical operational workflows. Nevertheless, this simplified circuit layout suffices for introductory learning purposes...
While this microcontroller variant supports multiple input supply voltage levels, there is no practical reason to introduce unnecessary fault risks by testing marginal voltage operating conditions. A standard 5V DC supply remains the most widely adopted industry standard. The schematic diagram shown implements the low-cost LM7805 three-terminal positive linear voltage regulator, delivering consistent stable output voltage and sufficient 1A peak current capacity to power the microcontroller core plus all connected auxiliary peripheral hardware.
For reliable microcontroller initialization, a logic low (0V) signal must be asserted on the RS reset pin. The momentary pushbutton linking the RS reset pin to the VCC supply rail is not strictly mandatory hardware, yet it is included on nearly all prototype boards. This pushbutton enables controlled forced reinitialization if firmware execution enters an unstable fault state. Pulling the RS pin high to 5V triggers a full chip reset, restarting program execution from the firmware entry vector address.
Although the microcontroller integrates an internal RC oscillator, stable deterministic operation requires an external quartz crystal paired with two matching load capacitors to fix the core operating clock frequency, which directly defines the chip’s instruction execution throughput.
Naturally, this crystal oscillator topology is not universally applicable, and multiple alternative clock generation solutions exist. One alternative configuration sources a clock reference signal from a dedicated external oscillator module routed through an inverting logic buffer, as shown in the left-hand diagram.
Despite representing cutting-edge compact computing hardware, a standalone microcontroller serves no practical function without interfaced auxiliary peripheral hardware. Simply stated, voltage level changes on the chip’s input/output pins carry no functional meaning unless utilized to drive defined operations: switching loads on/off, data shifting, visual status display rendering, and more.
No peripheral component is simpler than mechanical switches and momentary pushbuttons; they deliver the most straightforward method of detecting voltage state transitions on microcontroller input pins.
Real-world implementation introduces unforeseen complexity stemming from mechanical contact bounce, a pervasive flaw inherent to all mechanical switching hardware. When metallic contact surfaces collide, physical momentum and material elasticity generate rapid repeated electrical pulses instead of a clean single voltage transition between 0V and full supply rail. This oscillating behavior originates from minor surface imperfections, accumulated dust contamination, and mechanical vibration. The bounce duration — measured in microseconds or milliseconds — passes unnoticed by human perception yet registers reliably as multiple discrete input events by the microcontroller’s high-speed digital logic. If a raw pushbutton signal feeds directly into a pulse-counting firmware routine, near 100% counting error rates will occur without signal conditioning.
The most cost-effective mitigation strategy for contact bounce is a passive RC low-pass filter circuit that suppresses rapid voltage transients. Since bounce timing characteristics vary across switch models, resistor and capacitor values lack rigid standardized specifications, yet the component values illustrated in the diagram deliver consistent performance for most common use cases.
Applications requiring absolute input signal stability demand more robust hardware countermeasures. An RS flip-flop circuit modifies the signal output logic state only once upon the initial contact transition, fully ignoring subsequent bounce pulses. While this hardware solution requires a higher-cost SPDT switch component, it eliminates bounce artifacts entirely and supports detection of extremely short input pulses that passive RC filters would mask, as no charge storage capacitors are utilized in the signal path.
Beyond hardware filtering topologies, a lightweight software debounce implementation exists. When firmware detects an input pin state transition, it re-verifies the pin logic level following a fixed delay period. A consistent state reading confirms an intentional switch actuation rather than transient bounce noise. This software approach carries zero additional component cost, suppresses electromagnetic interference artifacts, and operates reliably with low-quality worn mechanical contacts. Its primary limitation matches RC filter circuits: input pulses shorter than the programmed delay interval cannot be captured by the firmware logic.
Optocouplers are industry-standard isolation components used to galvanically isolate microcontroller circuitry from hazardous high-current or high-voltage external electrical environments. Typical optocoupler packages integrate one, two, or four LED light-emitting sources on the input side, paired with an equal quantity of light-sensitive semiconductor devices on the output stage: phototransistors, photothyristors, or phototriacs. Core functionality relies on a non-conductive optical transmission channel to transfer digital signals between isolated circuit domains while maintaining complete electrical separation. This isolation performance is only effective when the input LED side and output photodetector side receive independent power supply rails. This separation fully shields the microcontroller and sensitive precision peripheral hardware from destructive high-voltage transients and electromagnetic noise, two leading root causes of permanent hardware damage and unstable operational behavior in embedded systems. Phototransistor-output optocouplers represent the most widely deployed variant. For optocoupler packages with an exposed base pin (pin 6), the base terminal may be left unconnected. An optional resistor-capacitor network shown via dashed wiring in the schematic attenuates high-frequency noise by filtering ultra-short transient pulse signals.
A relay constitutes an electrically actuated mechanical switch controlled by a secondary low-power logic circuit. Relays interface with microcontroller output pins to switch high-power load hardware including DC motors, AC transformers, heating elements, incandescent lighting, and antenna arrays. These high-power loads are nearly always physically separated from the main PCB’s sensitive low-voltage logic components. Multiple relay form factors exist, yet all operate via identical electromagnetic actuation principles: energizing an internal copper coil generates a magnetic field that mechanically toggles one or multiple sets of conductive contact pairs. Identical to optocouplers, relays deliver full galvanic isolation with no direct electrical conduction path between input control logic and output load circuits. Most standard relay variants demand elevated voltage and current to energize the coil winding, though miniature signal relays can be directly driven by the low current sourcing capacity of microcontroller output pins.
The illustrated schematic targets the 8051 microcontroller architecture. A Darlington pair transistor amplifies drive current to energize the relay coil, a mandatory workaround for logic-high output states, where the microcontroller pin operates as a high-impedance input with minimal sourcing current capacity, violating standard drive circuit design guidelines yet required for this specific architecture’s electrical characteristics.
A reverse-biased freewheeling diode is wired in parallel with the relay coil winding to suppress destructive high-voltage inductive kickback transients generated when coil current flow is abruptly terminated. This clamping diode absorbs the inductive voltage spike to protect surrounding semiconductor components from breakdown damage.
Light-emitting diodes serve as visual status indicator components across electronic hardware systems, manufactured in diverse physical form factors, emission colors, and package sizes. Their ultra-low unit cost, minimal power draw, and simplified drive circuitry have displaced legacy incandescent indicator lamps for nearly all modern applications. Electrical behavior mirrors standard PN-junction diodes, with the unique property of visible light emission when forward-biased current flows through the semiconductor junction.
Current limiting is a critical design requirement; unregulated forward current will permanently degrade and destroy the LED junction. A series current-limiting resistor must be included in every LED drive circuit. Calculation of the required resistor resistance value depends on the LED’s forward voltage drop, a parameter determined by the semiconductor material and target emission wavelength/color. Standard electrical specifications for commonly deployed LED variants are tabulated below. Three primary LED classifications exist: Standard LEDs achieve full rated brightness at 20mA forward current; Low-Current LEDs deliver equivalent luminosity at one-tenth of this current draw; Super Bright LEDs produce significantly higher luminous output than standard form factors under identical drive current conditions.
| Emission Color | Device Classification | Rated Operating Current Id (mA) | Absolute Maximum Current If (mA) | Forward Voltage Drop Ud (V) |
|---|---|---|---|---|
| Infrared | - | 30 | 50 | 1.4 |
| Red | Standard | 20 | 30 | 1.7 |
| Red | Super Bright | 20 | 30 | 1.85 |
| Red | Low Current | 2 | 30 | 1.7 |
| Orange | - | 10 | 30 | 2.0 |
| Green | Low Current | 2 | 20 | 2.1 |
| Yellow | - | 20 | 30 | 2.1 |
| Blue | - | 20 | 30 | 4.5 |
| White | - | 25 | 35 | 4.4 |
The 8051 microcontroller family supplies limited sink/source current on general-purpose I/O pins, with pins operating as low-side current sinks when driven to 0V logic level. For this reason, low-current LEDs are wired with cathode terminals connected directly to microcontroller output pins, as illustrated in the right-hand schematic diagram.
A segment LED display module integrates multiple discrete LED diodes encapsulated within a single unified plastic housing. Numerous display variants exist, incorporating dozens of individual LED segments to render alphanumeric symbols and numerical digits.
The 7-segment display constitutes the most widely deployed variant, constructed from eight independent LED elements: seven rectangular light segments arranged to form numerical digit outlines, plus an eighth dedicated decimal point segment. To simplify PCB wiring, all segment anodes or cathodes are tied to a shared common terminal, creating two primary display topologies: common-anode and common-cathode variants. Segments are labeled sequentially A through G, with an additional dp designation for the decimal point, hardware security research is to improve the overall security matching the labeling shown in the left diagram. Each LED segment requires its own series current-limiting resistor during hardware wiring, as every segment operates as an independent light-emitting element.
Direct wiring of multi-digit LED displays to microcontroller hardware consumes large quantities of limited I/O pin resources, a critical hardware constraint particularly when multi-digit numerical readouts are required. The resource limitation becomes immediately apparent when implementing a dual six-digit numeric readout, which would theoretically demand 96 dedicated output pins if wired without multiplexing. The standard industry solution to this hardware constraint is display multiplexing, leveraging an optical persistence-of-vision illusion identical to motion picture film projection principles. Only a single digit segment bank is energized at any given clock cycle, yet rapid sequential switching between digit positions creates the visual perception that all numerical digits illuminate simultaneously.
The operation logic of the above schematic is as follows: First, a byte data value corresponding to the units digit is asserted on a microcontroller port, while transistor T1 is simultaneously enabled to activate the units digit display bank. Following a brief fixed delay interval, transistor T1 de-energizes, the tens digit byte value is transmitted to the port register, and transistor T2 enables the tens digit display bank. This sequential activation sequence cycles continuously at high refresh rates across all digit positions and their corresponding drive transistors.
The microcontroller’s fundamental nature as a binary computing device exclusively interpreting logic 0 and logic 1 becomes fully evident during digit rendering workflows. The MCU has no inherent native understanding of decimal place values (units, tens, hundreds) or standard human-readable numeral glyph shapes. All target display values must undergo standardized preprocessing as outlined below:
First, a multi-digit integer value is decomposed into isolated units, tens, and hundreds place values via dedicated firmware subroutines. Each extracted single digit value is stored within individual 8-bit register bytes. Digit glyph rendering is accomplished through bitmask translation logic, which remaps raw binary numerical values into unique segment activation bit patterns via lightweight subroutine processing. As an illustrative example, raw binary value 0000 1000 (representing decimal digit 8) is translated into a segment mask bit pattern that energizes all seven primary display segments while leaving the decimal point segment unlit. If microcontroller port wiring maps bit 0 to segment A, bit 1 to segment B, bit 2 to segment C, and so forth, the standardized bitmask lookup table for each decimal digit appears as shown below.
| Numeric Digit to Render | Display Segment Bit Mapping | |||||||
|---|---|---|---|---|---|---|---|---|
| dp | a | b | c | d | e | f | g | |
| 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
| 3 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
| 4 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
| 5 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
| 6 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 7 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
| 8 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 9 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
Beyond decimal numerals 0 through 9, a subset of Latin alphabet characters can be rendered via adjusted bitmask values: uppercase A, C, E, J, F, U, H, L; lowercase b, c, d, o, r, t.
When using common-cathode display hardware, all logic 1 and logic 0 values within the lookup table must be inverted, and NPN bipolar transistors are required as digit drive switching components.
Dedicated LCD modules are purpose-built for direct microcontroller interfacing and cannot be natively driven by generic standard logic integrated circuits. These modules render customizable text strings on compact liquid crystal panel hardware.
The module variant analyzed within this material achieves widespread real-world adoption due to its low unit cost and comprehensive feature set, built around Hitachi’s HD44780 integrated controller chip. This standard panel supports two independent display rows with sixteen character positions per row, rendering full Latin alphabet character sets, Greek alphabet symbols, punctuation marks, mathematical operators, and supplementary glyphs. User-defined custom 5×8 pixel graphical symbols can also be stored and displayed. Additional native functionality includes automatic left/right text scrolling, configurable visible cursor indicators, and integrated LED backlight hardware.
A row of header pins lines one edge of the LCD’s printed circuit board to facilitate wiring connections with the microcontroller. The base module incorporates fourteen numbered signal pins; variants with integrated backlight hardware add two supplementary power pins for a total of sixteen. Full pin functional specifications are listed within the table below:
| Signal Purpose | Pin Number | Signal Label | Logic Level State | Functional Description |
|---|---|---|---|---|
| Ground Reference | 1 | Vss | - | 0V system ground rail |
| Main Power Supply | 2 | Vdd | - | +5V logic supply voltage |
| Contrast Adjustment | 3 | Vee | - | Voltage range 0V to Vdd for LCD contrast tuning |
| Operation Control Signals | 4 | RS | 0 1 |
Pins D0–D7 interpreted as controller command words Pins D0–D7 interpreted as display character data bytes |
| 5 | R/W | 0 1 |
Write operation: MCU transmits data to LCD module Read operation: LCD module outputs status/data to MCU |
|
| 6 | E | 0 1 Falling edge 1→0 |
LCD register access disabled Standby operational state Latch command/data values into LCD controller core |
|
| Data & Command Bus Lines | 7 | D0 | 0/1 | Data Bit 0 (Least Significant Bit) |
| 8 | D1 | 0/1 | Data Bit 1 | |
| 9 | D2 | 0/1 | Data Bit 2 | |
| 10 | D3 | 0/1 | Data Bit 3 | |
| 11 | D4 | 0/1 | Data Bit 4 | |
| 12 | D5 | 0/1 | Data Bit 5 | |
| 13 | D6 | 0/1 | Data Bit 6 | |
| 14 | D7 | 0/1 | Data Bit 7 (Most Significant Bit) |
The LCD display panel hardware consists of two independent character rows, each supporting sixteen individual character positions. Every character cell is implemented as a 5×8 or optional 5×11 dot matrix pixel grid; this reference material focuses exclusively on the industry-standard 5×8 character matrix variant.
Display contrast levels depend on the supply rail voltage and single/double row display configuration. For this reason, a variable voltage signal ranging between 0V and Vdd is applied to the Vee contrast tuning pin, typically controlled via a trimmer potentiometer. Many LCD modules integrate colored LED backlight hardware (blue or green emission). When utilizing backlight functionality, a series current-limiting resistor must be wired in series with one backlight power pin, following identical drive design rules applied to discrete LED components.
If no text characters render or all displayed glyphs appear dim following power-up, the first troubleshooting step is calibrating the contrast adjustment potentiometer to verify proper voltage tuning. This identical troubleshooting procedure applies if the display operating mode (single-row / dual-row rendering) is reconfigured mid-operation.
The LCD controller integrates three distinct dedicated memory partitions:
DDRAM storage holds the ASCII character byte values scheduled for screen rendering, with sufficient capacity to store eighty total character bytes. Individual memory addresses map directly to discrete character cell positions on the physical LCD panel.
Core operational logic is straightforward: configure the LCD controller to automatically increment memory addresses (rightward text shift), then define the starting memory address for the target text string (for example, hexadecimal address 00).
After completing this configuration step, all character bytes transmitted over the D0–D7 data bus render sequentially left-to-right in standard reading order. With the base address set to 00 hex, text rendering initiates at the leftmost character cell of the top display row. If more than sixteen character bytes are transmitted, all excess data remains stored within DRAM memory, yet only the initial sixteen positions remain visible on-screen. A dedicated display shift command must be issued to scroll hidden character data into the visible panel window. Functionally, the LCD panel acts as a fixed viewport sliding horizontally over stationary DRAM memory contents, creating the visual effect of scrolling text across the screen surface.
When the cursor indicator is enabled, it renders at the currently active DRAM address position. After writing a character byte to the active address, the controller automatically increments the target address to the subsequent character cell position.
As volatile random-access memory, all DRAM stored data is permanently erased upon power supply disconnection.
The CGROM read-only memory bank stores the factory preloaded standard glyph lookup table containing every character symbol natively supported by the LCD panel, with each unique glyph assigned to a fixed static memory address.
CGROM memory address offsets directly correspond to standard ASCII character encoding values. When active firmware executes an instruction to transmit character 'P' to the LCD data bus, binary byte value 0101 0000 is asserted on the D0–D7 data lines. This binary value matches the ASCII encoding for uppercase 'P', which the LCD controller uses to index the CGROM memory bank and render the corresponding glyph stored at address 0101 0000. This mapping rule applies universally to uppercase and lowercase Latin alphabet characters, yet does not directly apply to decimal numerical digits.
As visible on the preceding character map diagram, all decimal digit address offsets are shifted upward by a base offset value of 48 relative to their numeric value: digit 0 maps to address 48, digit 1 maps to address 49, digit 2 maps to address 50, and so on. Accordingly, firmware logic must add decimal constant 48 to every numerical digit byte prior to transmission over the LCD data bus for accurate glyph rendering.
From early computing architectures to modern embedded hardware, digital processing systems only interpret binary numerical values and cannot natively recognize human-readable alphabetic characters. All data exchanged between a microcontroller and external peripheral hardware exists in binary format, even when visually interpreted as text characters by human operators; a standard keyboard serves as a clear real-world illustration of this principle. Every printable character maps to a unique fixed binary bit pattern. ASCII encoding is a standardized character set built around the Latin alphabet, defining a direct one-to-one correlation between printable graphical symbols and their corresponding binary numerical representations.
CGRAM Custom Character RAM Partition
In addition to factory predefined CGROM glyphs, the LCD controller supports rendering fully user-defined custom graphical symbols sized to the 5×8 pixel character grid via a dedicated 64-byte volatile CGRAM memory partition.
CGRAM memory registers are 8 bits wide, though only the five least significant bit positions are utilized for pixel pattern storage. A logic-high bit (1) within each register corresponds to an illuminated pixel dot, while eight sequential register memory locations collectively store the full pixel pattern for a single custom character glyph. The diagram below illustrates this memory mapping principle clearly:
Custom glyph patterns are typically initialized at firmware program startup by writing sequential logic 0 and logic 1 bit patterns into CGRAM memory registers to construct the target pixel artwork shape. To render a preconfigured custom glyph on-screen, firmware only needs to transmit the corresponding CGRAM base address value to the LCD controller. Note the first column of the CGROM character lookup table does not reference CGROM static addresses but instead points to custom CGRAM glyph indices. Within this reference’s example logic, transmitting index value 0 renders the first custom glyph, index value 1 renders the second custom glyph, and so forth.
Fundamental LCD Controller Command Set ReferenceAll bytes transmitted to the LCD module over the D0–D7 data bus are interpreted either as controller command opcodes or display character data bytes, determined entirely by the logic state of the RS register select pin:
RS = 1 — Data bus bits D0–D7 represent ASCII character byte values targeted for screen rendering. The LCD controller indexes the CGROM glyph map using this byte value and renders the corresponding symbol at the active DDRAM address position. The target DDRAM address is either preconfigured before transmitting the character byte or automatically incremented after each completed character write operation.
RS = 0 — Data bus bits D0–D7 represent standardized controller opcodes that configure global display operating modes. A full table of all recognized LCD command opcodes is listed below:
| Command Name | RS | RW | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | Required Execution Time |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Clear Entire Display Buffer | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1.64ms |
| Cursor Return to Home Position | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | Don’t Care | 1.64ms |
| Entry Mode Parameter Configuration | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S | 40μs |
| Display & Cursor Power Control | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | U | B | 40μs |
| Cursor / Display Scroll Shift Command | 0 | 0 | 0 | 0 | 0 | 1 | D/C | R/L | Don’t Care | Don’t Care | 40μs |
| Hardware Interface Function Set | 0 | 0 | 0 | 0 | 1 | DL | N | F | Don’t Care | Don’t Care | 40μs |
| Write Target CGRAM Base Address | 0 | 0 | 0 | 1 | 6-bit CGRAM address offset | 40μs | |||||
| Write Target DDRAM Base Address | 0 | 0 | 1 | 7-bit DDRAM address offset | 40μs | ||||||
| Read Busy Flag (BF) & Current DDRAM Address | 0 | 1 | BF | 7-bit active DDRAM address | N/A | ||||||
| Write Data Byte to CGRAM / DDRAM | 1 | 0 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 40μs |
| Read Data Byte from CGRAM / DDRAM | 1 | 1 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 40μs |
I/D 1 = Automatic address increment (+1) R/L 1 = Shift display contents right
0 = Automatic address decrement (-1) 0 = Shift display contents left
S 1 = Global display scrolling enabled DL 1 = 8-bit parallel data bus interface
0 = Global display scrolling disabled 0 = 4-bit split nibble data bus interface
D 1 = LCD panel display power active N 1 = Dual-row character display mode
0 = LCD panel display power disabled 0 = Single-row character display mode
U 1 = Blinking cursor indicator active F 1 = 5×10 dot character glyph format
0 = Static cursor indicator disabled 0 = 5×7 dot character glyph format
B 1 = Cursor blink animation enabled D/C 1 = Shift full display buffer
0 = Cursor blink animation disabled 0 = Shift only cursor position
Relative to the microcontroller’s high-speed instruction execution pipeline, the LCD controller hardware operates at drastically lower processing speeds. A dedicated status signal flag was implemented to signal when the controller completes execution of a pending command and accepts new input data bytes. This busy flag signal is readable via the D7 data bus pin. When the BF bit clears to logic low (BF=0), the LCD module finishes all prior processing and is ready to receive new command or character data bytes.
Two distinct wiring standards exist for interfacing an LCD module with a microcontroller: 8-bit full bus mode and 4-bit split nibble bus mode. The target operating mode is configured during initialization firmware routines executed immediately after power-on reset. 8-bit mode transmits complete 8-bit data bytes across the full D0–D7 data bus following the logic covered in preceding sections. The primary design motivation for implementing 4-bit bus mode is conservation of limited microcontroller general-purpose I/O pins; only the four high-order data lines D4–D7 are utilized for communication, while D0–D3 may remain unconnected. Every full 8-bit data byte is split into two sequential transmission cycles: the four high-order nibble bits transmit first over D4–D7, followed by the four low-order nibble bits. The initialization sequence configures the LCD controller to correctly parse and recombine these split nibble transmissions. Data read operations from the LCD module are rarely required, as data flow is predominantly one-way from MCU to LCD hardware. This enables a common pin-saving optimization: permanently grounding the R/W read/write control pin. This hardware modification reduces required I/O pin count by one, yet carries a functional tradeoff: the busy flag status register cannot be polled, eliminating the ability to read any data stored within the LCD’s internal memory partitions.
A straightforward software workaround exists to compensate for disabled busy flag polling. After transmitting any command or character byte to the LCD module, firmware must insert a fixed delay interval to allow the controller sufficient processing time. Since the slowest single LCD command operation consumes a maximum of approximately 1.64ms, a standardized software delay of 2ms reliably guarantees full command completion before subsequent transmission cycles.
The LCD module automatically clears all screen content upon power-up, a process that takes roughly 15 milliseconds to complete. Once finished, the display enters an operational ready state with a set of preconfigured default operating parameters as listed below:
The built-in power-on automatic reset sequence works reliably under most standard power supply conditions, yet it is not universally fault-free. If the supply voltage fails to reach its full rated level within a 10-millisecond power-up window, the LCD controller will behave erratically with unpredictable rendering output. When the power supply circuit cannot satisfy this fast voltage ramp requirement, or consistent stable display operation is mandatory for the product, a manual software initialization sequence must be executed. This initialization routine triggers a full internal controller reset to restore predictable, stable display functionality.
Refer to the diagram below for the complete 8-bit bus initialization workflow:
This repeated three identical command transmissions in sequence is not a typo or error in the algorithm.
The initialization sequence intentionally sends the same function set command three times consecutively.
The standardized 4-bit bus initialization procedure is shown in the following diagram:
The circuit schematic shown below serves as the shared hardware platform for all subsequent demo programs:
In addition to core microcontroller supporting circuitry including crystal oscillator with load capacitors and minimal passive reset components, the board features multiple indicator LEDs and one tactile pushbutton. These peripherals provide visible runtime status feedback for all firmware examples.
All LEDs are wired as sink-type indicators: each LED illuminates when its corresponding microcontroller GPIO pin is driven to logic level 0 (low).
The core objective of this example is not simply to demonstrate basic LED operation, but to illustrate the fast native instruction execution speed of the microcontroller. To produce visible, human-perceivable LED on-off toggling, intentional time delays must be inserted between each pin state inversion. This demo implements a reusable software delay subroutine named Delay. The nested triple loop holds program execution for approximately 0.5 seconds by continuously decrementing values stored in general-purpose registers R0, R1 and R2. Once the delay subroutine returns, the target I/O pin logic level flips, and the full cycle repeats indefinitely...
;************************************************************************
;* PROGRAM NAME : Delay.ASM
;* DESCRIPTION: Toggles the LED connected to pin P1.0 using software delay loops
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(DELAY.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK SEGMENT DEFINITION
DSEG AT 03FH
STACK_START: DS 040H
;RESET INTERRUPT VECTOR
CSEG AT 0
JMP XRESET ; Jump to main reset entry point
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer address
MOV P1,#0FFh ; Configure all Port 1 pins as high-impedance inputs
LOOP:
CPL P1.0 ; Flip the logic state of pin P1.0
LCALL Delay ; Invoke software delay subroutine
SJMP LOOP ; Loop back to repeat LED toggle
Delay:
MOV R2,#20 ; Outer loop count for ~500ms total delay
F02: MOV R1,#50 ; Middle loop count for ~25ms per iteration
F01: MOV R0,#230 ; Innermost single-cycle decrement loop
DJNZ R0,$
DJNZ R1,F01
DJNZ R2,F02
END ; End of assembly source file
This example demonstrates improper watchdog timer operation to showcase forced chip resets. The watchdog timer is configured with a nominal overflow period of 1024 milliseconds, yet the critical watchdog refresh instruction is deliberately omitted from the code. The timer will always overflow and trigger a full microcontroller reset. Upon reset, all register values are preserved, program execution restarts from the reset vector address, the value stored in register R3 increments by one, and this binary value is output directly to Port 1.
The connected LEDs display the incrementing counter value in raw binary format...
;************************************************************************
;* PROGRAM NAME : WatchDog.ASM
;* DESCRIPTION : On watchdog timeout reset, increment counter R3 and output binary value to Port 1 LEDs
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(WATCHDOG.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
WMCON DATA 96H
WDTEN EQU 00000001B ; Bit mask to enable watchdog timer module
PERIOD EQU 11000000B ; Bit mask to set watchdog overflow period to 1024ms
;RESET VECTOR DEFINITION
CSEG AT 0
JMP XRESET ; Main program entry after hardware reset
CSEG
ORG 100H
XRESET: ORL WMCON,#PERIOD ; Configure watchdog timer overflow interval
ORL WMCON,#WDTEN ; Activate watchdog counter operation
MOV A,R3 ; Load counter value R3 into accumulator
MOV P1,A ; Output accumulator binary state to Port 1 LEDs
INC R3 ; Increment counter register R3 by 1
LAB: SJMP LAB ; Infinite idle loop with no watchdog refresh
END ; End of assembly source file
The main program spends nearly all execution time inside an infinite idle loop waiting for Timer T0 to complete a full 16-bit count cycle. When the timer overflows, a hardware interrupt signal fires, triggering the interrupt service routine TIM0_ISR. Inside the ISR, a logic low bit (0) stored in the accumulator is right-shifted one position across Port 1. This demo visually demonstrates the microcontroller’s high processing speed, as each single bit shift represents the completion of 216 external timer clock pulses!
;************************************************************************
;* PROGRAM NAME : Tim0Mod1.ASM
;* DESCRIPTION: Rotates a logic low bit across Port 1 LEDs using Timer T0 in 16-bit Mode 1
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(TIM0MOD1.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;VARIABLE STORAGE DECLARATIONS
;STACK MEMORY ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;INTERRUPT VECTOR TABLE
CSEG AT 0
JMP XRESET ; Primary hardware reset vector
ORG 00BH
JMP TIM0_ISR ; Timer T0 overflow interrupt vector
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer register
MOV TMOD,#01H ; Set Timer0 to 16-bit Mode 1 operation
MOV A,#0FFH
MOV P1,#0FFH
SETB TR0 ; Start Timer T0 counting
MOV IE,#082H ; Global interrupt enable + Timer0 interrupt enable
CLR C ; Clear carry flag for bit rotation
LOOP1: SJMP LOOP1 ; Idle infinite main loop
TIM0_ISR: RRC A ; Rotate accumulator right through carry flag
MOV P1,A ; Write updated accumulator value to Port 1
RETI ; Return from interrupt service routine
END ; End of assembly source file
Similar to the prior timer example, the main program resides in a continuous idle loop labeled LOOP1. When Timer T0 is configured in Mode 3, its single 16-bit counter is split into two independent 8-bit timers TL0 and TL1, each with its own dedicated interrupt trigger.
The first interrupt fires when TL0 overflows, executing the TIM0_ISR service routine to rotate a logic low bit across Port 1 LEDs, creating the visual effect of illuminated lights shifting across the port.
The second interrupt triggers on TL1 overflow, running TIM1_ISR to invert the DIRECTION status bit. This bit controls the left/right rotation direction of the LED bit pattern, so flipping it reverses the scrolling movement of the indicator lights.
If the tactile pushbutton wired to pin P3.2 is pressed, the logic low level on this pin halts the counting operation of Timer T1.
;************************************************************************
;* PROGRAM NAME : Split.ASM
;* DESCRIPTION: TL0 shifts LED bits on Port P1, TL1 controls rotation direction. Both timers run in Mode 3 split operation. Logic low on P3.2 disables Port 1 bit rotation.
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(SPLIT.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;BIT VARIABLE MEMORY AREA
BSEG AT 0
;BIT FLAG DEFINITIONS
SEMAPHORE: DBIT 8
DIRECTION BIT SEMAPHORE
;STACK SEGMENT DEFINITION
DSEG AT 03FH
STACK_START: DS 040H
;INTERRUPT VECTOR TABLE
CSEG AT 0
JMP XRESET ; Hardware reset entry point
ORG 00BH
JMP TIM0_ISR ; TL0 overflow interrupt vector
ORG 01BH
JMP TIM1_ISR ; TL1 overflow interrupt vector
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer
MOV TMOD,#00001011B ; Configure Timer0 to Mode 3 split dual 8-bit timers
MOV A,#0FFH
MOV P1,#0FFH
MOV R0,#30D
SETB TR0 ; Enable TL0 counter operation
SETB TR1 ; Enable TL1 counter operation
MOV IE,#08AH ; Enable global, Timer0 and Timer1 interrupts
CLR C
CLR DIRECTION ; Set initial bit rotation direction to right
LOOP1: SJMP LOOP1 ; Main idle loop
TIM0_ISR:
DJNZ R0,LAB3 ; Slow down LED rotation refresh rate
JB DIRECTION,LAB1
RRC A ; Rotate accumulator contents right via carry flag
SJMP LAB2
LAB1: RLC A ; Rotate accumulator contents left via carry flag
LAB2: MOV P1,A ; Output rotated bit pattern to Port 1 LEDs
LAB3: RETI ; Exit interrupt service routine
TIM1_ISR:
DJNZ R1,LAB4 ; Delay direction toggle timing
DJNZ R2,LAB4 ; Set hold time before reversing scroll direction
CPL DIRECTION ; Flip rotation direction flag
MOV R2,#30D
LAB4: RETI
END ; End of assembly source file
This program builds upon the logic of the split timer demo. The core concept remains identical, but this implementation uses two fully independent 16-bit hardware timers T0 and T1 instead of split 8-bit counters within one timer module. To demonstrate simultaneous parallel timer operation on a single I/O port, Timer T0 overflow events handle shifting the logic low bit pattern across Port 1 LEDs, while Timer T1 overflow events toggle the scrolling direction flag. The firmware spends nearly all runtime inside the idle loop LOOP1, waiting for hardware interrupt triggers. By reading the DIRECTION flag bit, the ISRs determine whether to shift the accumulator bit pattern left or right, changing the visual movement direction of the port LEDs.
;************************************************************************
;* PROGRAM NAME : Tim0Tim1.ASM
;* DESCRIPTION: Timer0 shifts bit pattern on Port P1, Timer1 toggles rotation direction. Both timers configured for 16-bit Mode 1.
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(TIM0TIM1.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;BIT VARIABLE MEMORY AREA
BSEG AT 0
;BIT FLAG DEFINITIONS
SEMAPHORE: DBIT 8
DIRECTION BIT SEMAPHORE
;STACK SEGMENT ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;INTERRUPT VECTOR TABLE
CSEG AT 0
JMP XRESET ; Hardware reset entry point
ORG 00BH ; Timer 0 overflow interrupt vector
JMP TIM0_ISR
ORG 01BH ; Timer 1 overflow interrupt vector
JMP TIM1_ISR
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer register
MOV TMOD,#11H ; Set both Timer0 and Timer1 to 16-bit Mode 1
MOV A,#0FFH
MOV P1,#0FFH
MOV R0,#30D ; Initialize timing delay counter
SETB TR0 ; Start Timer0 counting
SETB TR1 ; Start Timer1 counting
MOV IE,#08AH ; Enable global, Timer0 and Timer1 interrupts
CLR C
CLR DIRECTION ; Set initial bit rotation to right
LOOP1: SJMP LOOP1 ; Main idle infinite loop
TIM0_ISR:
JB DIRECTION,LAB1
RRC A ; Rotate accumulator right through carry flag
SJMP LAB2
LAB1: RLC A ; Rotate accumulator left through carry flag
LAB2: MOV P1,A ; Write updated bit pattern to Port 1 LEDs
RETI ; Return from interrupt
TIM1_ISR:
DJNZ R0,LAB3 ; Hold delay before reversing scroll direction
CPL DIRECTION ; Toggle rotation direction flag
MOV R0,#30D ; Reload delay counter value
LAB3:
RETI
END ; End of assembly source file
This example illustrates Timer T2 configured in hardware auto-reload counting mode. Indicator LEDs are wired to Port P3, while a manual trigger pushbutton for forced timer reset (T2EX) connects to pin P1.1.
The program execution flow mirrors the prior timer examples. When Timer T2 completes its full count cycle, an interrupt request is generated, executing the TIM2_ISR service routine. Inside the ISR, a logic low bit rotates within the accumulator register, and the updated accumulator value is output to Port 3 LED indicators. The interrupt status flags TF2 and EXF2 are cleared to acknowledge the interrupt, and the program returns to the idle LOOP1 state to wait for the next timer overflow interrupt request...
Pressing the T2EX pushbutton triggers an immediate hardware reset of the Timer T2 counter. This dedicated T2EX input only resets Timer T2, while the separate main RESET pushbutton performs a full global microcontroller chip reset.
;************************************************************************
;* PROGRAM NAME : Timer2.ASM
;* DESCRIPTION: Rotates a logic low bit across Port P3 LEDs. Timer T2 controls scroll speed and operates in auto-reload counting mode
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(TIMER2.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;REGISTER ADDRESS DEFINITION
T2MOD DATA 0C9H
;STACK MEMORY ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;INTERRUPT VECTOR TABLE
CSEG AT 0
JMP XRESET ; Hardware reset entry point
ORG 02BH ; Timer T2 overflow interrupt vector
JMP TIM2_ISR
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer register
MOV A,#0FFH
MOV P3,#0FFH
MOV RCAP2L,#0FH ; Set low byte of 16-bit auto-reload capture register
MOV RCAP2H,#01H ; Set high byte of 16-bit auto-reload capture register
CLR CAP2 ; Disable capture mode, enable auto-reload operation
SETB EXEN2 ; Enable T2EX pin external timer reset function
SETB TR2 ; Start Timer T2 counting sequence
MOV IE,#0A0H ; Enable Timer T2 interrupt requests
CLR C
LOOP1: SJMP LOOP1 ; Main idle wait loop
TIM2_ISR: RRC A ; Rotate accumulator contents right through carry flag
MOV P3,A ; Output accumulator bit pattern to Port 3 LEDs
CLR TF2 ; Clear Timer T2 overflow flag TF2
CLR EXF2 ; Clear T2EX external trigger flag EXF2
RETI ; Exit interrupt service routine
END ; End of assembly source file
This section covers another interrupt-driven firmware example. External hardware interrupt signals are triggered when a logic low level is applied to input pin P3.2 (INT0) or P3.3 (INT1). The active input pin determines which corresponding interrupt service routine executes:
A logic low signal on P3.2 activates the Isr_Int0 interrupt routine, incrementing the counter value stored in register R0 and copying this binary count value to Port 0 output LEDs. A logic low signal on P3.3 triggers the Isr_Int1 routine, incrementing register R1 and writing its value to Port 1 LEDs.
In summary, every physical press of the INT0 or INT1 pushbutton increments its respective counter, with the live count value displayed in binary format on the matching port LEDs (an illuminated LED represents logic level 0).
;************************************************************************
;* PROGRAM NAME : Int.ASM
;* DESCRIPTION : Counts INT0 interrupts triggered by falling edge signal on P3.2, output count to Port 0 LEDs. Simultaneously counts INT1 falling-edge interrupts on P3.3 with results displayed on Port 1 LEDs.
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(INT.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;INTERRUPT VECTOR TABLE
CSEG AT 0
JMP XRESET ; Primary hardware reset vector
ORG 003H ; INT0 external interrupt service vector address
JMP Isr_Int0
ORG 013H ; INT1 external interrupt service vector address
JMP Isr_Int1
ORG 100H
XRESET:
MOV TCON,#00000101B ; Configure INT0 and INT1 to trigger on falling edge signal transitions
MOV IE,#10000101B ; Enable global interrupt flag plus INT0 and INT1 interrupts
MOV R0,#00H ; Initialize INT0 counter starting value to zero
MOV R1,#00H
MOV P0,#00H ; Clear all Port 0 LED outputs
MOV P1,#00H ; Clear all Port 1 LED outputs
LOOP: SJMP LOOP ; Main idle waiting loop
Isr_Int0:
INC R0 ; Increment INT0 event counter register
MOV P0,R0 ; Write counter binary value to Port 0 LEDs
RETI
Isr_Int1:
INC R1 ; Increment INT1 event counter register
MOV P1,R1 ; Write counter binary value to Port 1 LEDs
RETI
END ; End of assembly source file
The following set of demos covers the operation of multi-digit 7-segment LED displays. All hardware uses common-cathode display modules, meaning the anode terminal of every segment LED connects directly to microcontroller GPIO pins. Standard intuitive logic defines logic level 1 as an active "on" state and logic level 0 as an inactive "off" state. For low-current low-power display variants, each segment LED is wired in series with a moderately high-value current-limiting resistor.
To minimize consumption of limited microcontroller I/O pins, four individual digit displays are wired for time-division multiplexed operation. All segments sharing identical labels are tied to a single shared output port, and only one digit display is enabled and illuminated at any single moment.
The digit enable transistors and display segments toggle at a rapid refresh rate, creating the visual persistence illusion that all numerical digits light up simultaneously.
This introductory firmware serves as a basic warm-up exercise for working with segment displays. The core goal is to render a static numerical digit on a single display unit; multiplexing logic is not implemented here. The digit value 3 is hardcoded to display exclusively on the rightmost digit position.
The microcontroller has no native built-in understanding of human-readable decimal digit shapes, which are represented in raw binary as simple 8-bit values (the number 3 is stored as binary 0000 0011). A dedicated lookup subroutine named Disp converts raw numerical values into the corresponding segment activation bitmask patterns required for visual digit rendering. The operating principle is straightforward: add the input digit value to the program counter address to perform a table jump, where each memory location stores a precomputed unique bit combination for digits 0 through 9. Outputting this bitmask value to the segment port illuminates the correct segments to form the target digit shape on the display.
;************************************************************************
;* PROGRAM NAME : 7Seg1.ASM
;* DESCRIPTION: Displays static digit "3" on a single 7-segment common cathode LED display
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(7SEG1.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK SEGMENT ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;RESET VECTOR DEFINITION
CSEG AT 0
JMP XRESET ; Hardware reset entry point
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer register
MOV P1,#0 ; Clear all segment output pins to turn off all LEDs
MOV P3,#20h ; Activate the rightmost digit display D4
LOOP:
MOV A,#03 ; Load target digit value 3 into accumulator
LCALL Disp ; Call segment mask lookup subroutine
MOV P1,A ; Output digit bitmask to segment port P1
SJMP LOOP ; Infinite static display loop
Disp: ; Digit-to-segment bitmask lookup subroutine
INC A
MOVC A,@A+PC
RET
DB 3FH ; Segment mask for digit 0
DB 06H ; Segment mask for digit 1
DB 5BH ; Segment mask for digit 2
DB 4FH ; Segment mask for digit 3
DB 66H ; Segment mask for digit 4
DB 6DH ; Segment mask for digit 5
DB 7DH ; Segment mask for digit 6
DB 07H ; Segment mask for digit 7
DB 7FH ; Segment mask for digit 8
DB 6FH ; Segment mask for digit 9
END ; End of assembly source file
This program extends the functionality of the prior single-digit demo, still utilizing only the rightmost display position without multiplexing logic. Unlike the previous example, this firmware sequentially cycles through all decimal digits from 0 to 9. A short software delay subroutine L2 is executed between each digit update to slow the counting speed to a human-readable pace. The full sequence runs continuously inside the main loop labeled LOOP following these defined steps:
;************************************************************************
;* PROGRAM NAME: 7Seg2.ASM
;* DESCRIPTION: Sequentially counts and displays all digits 0 through 9 on a single static 7-segment display
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(7SEG2.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK MEMORY ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;RESET INTERRUPT VECTOR
CSEG AT 0
JMP XRESET ; Hardware reset entry point
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer register
MOV R3,#0 ; Initialize digit counter starting at 0
MOV P1,#0 ; Clear all segment output pins
MOV P3,#20h ; Enable rightmost digit display D4
LOOP:
MOV A,R3
LCALL Disp ; Convert raw counter value to segment bitmask
MOV P1,A ; Output bitmask to segment port
INC R3 ; Advance counter value by one
CJNE R3,#10,L2 ; Skip counter reset if value not equal to 10
MOV R3,#0 ; Reset counter to zero after reaching digit 9
L2:
MOV R2,#20 ; Outer loop count for ~500ms display hold delay
F02: MOV R1,#50 ; Middle loop ~25ms iteration delay
F01: MOV R0,#230
DJNZ R0,$
DJNZ R1,F01
DJNZ R2,F02
SJMP LOOP ; Return to main counting loop
Disp: ; Digit segment bitmask lookup subroutine
INC A
MOVC A,@A+PC
RET
DB 3FH ; Digit 0 segment mask
DB 06H ; Digit 1 segment mask
DB 5BH ; Digit 2 segment mask
DB 4FH ; Digit 3 segment mask
DB 66H ; Digit 4 segment mask
DB 6DH ; Digit 5 segment mask
DB 7DH ; Digit 6 segment mask
DB 07H ; Digit 7 segment mask
DB 7FH ; Digit 8 segment mask
DB 6FH ; Digit 9 segment mask
END ; End of assembly source file
This example introduces time-division multiplexing for multi-digit displays, the most basic implementation rendering the fixed two-digit value 23 across separate tens and units digit positions. Precise timing synchronization is the critical core requirement for flicker-free multiplexed output; all other logic remains straightforward. Transistor T4 enables the units digit display D4 while the corresponding bitmask pattern for digit 3 is output to the segment port. The digit enable transistor is then disabled, and the process repeats with transistor T3 activating the tens digit display D3 to render digit 2. This full sequence loops continuously at high refresh speed to create the visual illusion that both digits are illuminated simultaneously.
;************************************************************************
;* PROGRAM NAME: 7Seg3.ASM
;* DESCRIPTION: Multiplexed two-digit static display showing fixed number "23" on dual 7-segment modules
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(7SEG3.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK SEGMENT ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;RESET VECTOR DEFINITION
CSEG AT 0
JMP XRESET ; Hardware reset entry point
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer register
LOOP: MOV P1,#0 ; Blank all segment outputs before digit switching
MOV P3,#20h ; Activate units digit display D4
MOV A,#03 ; Load digit value 3 for units position
LCALL Disp ; Retrieve matching segment bitmask
MOV P1,A ; Write bitmask to segment port P1
MOV P1,#0 ; Clear segments before switching digit
MOV P3,#10h ; Activate tens digit display D3
MOV A,#02 ; Load digit value 2 for tens position
LCALL Disp ; Retrieve matching segment bitmask
MOV P1,A ; Write bitmask to segment port P1
SJMP LOOP ; Restart multiplex refresh cycle
Disp: ; Digit segment bitmask lookup subroutine
INC A
MOVC A,@A+PC
RET
DB 3FH ; Digit 0 segment mask
DB 06H ; Digit 1 segment mask
DB 5BH ; Digit 2 segment mask
DB 4FH ; Digit 3 segment mask
DB 66H ; Digit 4 segment mask
DB 6DH ; Digit 5 segment mask
DB 7DH ; Digit 6 segment mask
DB 07H ; Digit 7 segment mask
DB 7FH ; Digit 8 segment mask
DB 6FH ; Digit 9 segment mask
END ; End of assembly source file
This demo expands multiplexing logic to utilize all four available digit positions, supporting numerical values ranging from 0000 up to 9999. The hardcoded static value 1234 is rendered across the four display modules. After initializing the stack pointer, the firmware enters the continuous LOOP refresh cycle to execute time-division multiplexing. The shared Disp lookup subroutine converts raw single-digit binary values into the corresponding segment activation bit patterns required to light the correct display segments.
;************************************************************************
;* PROGRAM NAME : 7Seg5.ASM
;* DESCRIPTION : Multiplexed four-digit static display rendering fixed number "1234" on four 7-segment LED modules
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(7SEG5.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK MEMORY ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;RESET VECTOR DEFINITION
CSEG AT 0
JMP XRESET ; Hardware reset entry point
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer register
LOOP: MOV P1,#0 ; Blank all segment outputs
MOV P3,#20h ; Activate thousands digit display D4
MOV A,#04 ; Load digit value 4 for thousands place
LCALL Disp ; Fetch segment bitmask for digit 4
MOV P1,A ; Output bitmask to segment port
MOV P1,#0 ; Clear segments before next digit switch
MOV P3,#10h ; Activate hundreds digit display D3
MOV A,#03 ; Load digit value 3 for hundreds place
LCALL Disp ; Fetch segment bitmask for digit 3
MOV P1,A ; Output bitmask to segment port
MOV P1,#0 ; Blank segment outputs
MOV P3,#08h ; Activate tens digit display D2
MOV A,#02 ; Load digit value 2 for tens place
LCALL Disp ; Fetch segment bitmask for digit 2
MOV P1,A ; Output bitmask to segment port
MOV P1,#0 ; Blank segment outputs
MOV P3,#04h ; Activate units digit display D1
MOV A,#01 ; Load digit value 1 for units place
LCALL Disp ; Fetch segment bitmask for digit 1
MOV P1,A ; Output bitmask to segment port
SJMP LOOP ; Restart full four-digit multiplex refresh cycle
Disp: ; Digit segment bitmask lookup subroutine
INC A
MOVC A,@A+PC
RET
DB 3FH ; Digit 0 segment mask
DB 06H ; Digit 1 segment mask
DB 5BH ; Digit 2 segment mask
DB 4FH ; Digit 3 segment mask
DB 66H ; Digit 4 segment mask
DB 6DH ; Digit 5 segment mask
DB 7DH ; Digit 6 segment mask
DB 07H ; Digit 7 segment mask
DB 7FH ; Digit 8 segment mask
DB 6FH ; Digit 9 segment mask
END ; End of assembly source file
This demo combines multi-digit multiplexed display logic with live incremental counting functionality for more complex embedded behavior. Registers R2 (units digit counter) and R3 (tens digit counter) increment continuously to generate a rolling two-digit count sequence (97, 98, 99, 00, 01, 02...).
The digit enable transistors activate each display for a fixed 25-millisecond duration controlled by the Delay subroutine. Even with this visible hold time per digit, rapid multiplex refresh creates the simultaneous illumination illusion. After completing 20 full two-digit refresh cycles, the numerical counter value increments by one, and the full multiplexing sequence repeats indefinitely.
;************************************************************************
;* PROGRAM NAME : 7Seg4.ASM
;* DESCRIPTION: Multiplexed dual 7-segment display running continuous two-digit counter from 00 to 99
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(7SEG4.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK SEGMENT ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;RESET VECTOR DEFINITION
CSEG AT 0
JMP XRESET ; Hardware reset entry point
ORG 100H
XRESET: MOV SP,#STACK_START ; Initialize stack pointer register
MOV R2,#0 ; Initialize units digit counter to 0
MOV R3,#0 ; Initialize tens digit counter to 0
MOV R4,#0 ; Multiplex refresh cycle counter
LOOP: INC R4 ; Count full two-digit refresh cycles
CJNE R4,#20d,LAB1 ; Hold counter increment until 20 full refreshes complete
MOV R4,#0
MOV P1,#0 ; Blank all segment outputs
INC R2 ; Advance units digit counter
CJNE R2,#10d,LAB1
MOV R2,#0 ; Reset units digit after reaching 9
INC R3 ; Increment tens digit register
CJNE R3,#10d,LAB1
MOV R3,#0 ; Reset tens digit after reaching 9
LAB1:
MOV P3,#20h ; Enable units digit display D4
MOV A,R2 ; Load units counter value into accumulator
LCALL Disp ; Convert digit to segment bitmask
MOV P1,A ; Render units digit on display D4
LCALL Delay ; 25ms hold delay for visual persistence
MOV P1,#0 ; Blank segment outputs before digit switch
MOV P3,#10h ; Enable tens digit display D3
MOV A,R3 ; Load tens counter value into accumulator
LCALL Disp ; Convert digit to segment bitmask
MOV P1,A ; Render tens digit on display D3
LCALL Delay ; 25ms hold delay
SJMP LOOP ; Restart multiplex refresh cycle
Delay:
MOV R1,#50 ; Outer loop for ~5ms base delay
F01: MOV R0,#250
DJNZ R0,$
DJNZ R1,F01
RET
Disp: ; Digit segment bitmask lookup subroutine
INC A
MOVC A,@A+PC
RET
DB 3FH ; Digit 0 segment mask
DB 06H ; Digit 1 segment mask
DB 5BH ; Digit 2 segment mask
DB 4FH ; Digit 3 segment mask
DB 66H ; Digit 4 segment mask
DB 6DH ; Digit 5 segment mask
DB 7DH ; Digit 6 segment mask
DB 07H ; Digit 7 segment mask
DB 7FH ; Digit 8 segment mask
DB 6FH ; Digit 9 segment mask
END ; End of assembly source file
This program writes data to the built-in on-chip EEPROM memory. In this demo, the target data value is hexadecimal 0x23, which will be stored to EEPROM memory address 0x0000.
Below is a complete list of all Atmel 8051 microcontroller part numbers:
### Standard Flash Series (AT89C/S)
- AT89C1051 - AT89C1051U - AT89C2051 - AT89C4051 - AT89C51 - AT89C51AC3 - AT89C51CC03 - AT89C51ED2 - AT89C51IC2 - AT89C51ID2 - AT89C51IE2 - AT89C51RB2 - AT89C51RC - AT89C51RC2 - AT89C51RD2 - AT89C51RE2 - AT89C5115 - AT89C5130 - AT89C5130A - AT89C5131 - AT89C5131A - AT89C5132 - AT89C52 - AT89C55 - AT89S51 - AT89S52 - AT89S53 - AT89S8252
### Low Voltage Series (AT89LV/LS)
- AT89LS51 - AT89LS52 - AT89LS53 - AT89LV51 - AT89LV52 - AT89LV55
### OTP (One-Time Programmable) Series (AT87F)
- AT87F51 - AT87F51RC - AT87F52 - AT87F55WD
### ROMless / Special Function Series (AT83/AT80)
- AT83/87C5103 - AT83/87C5111 - AT83/87C5112 - AT83C5134 - AT83C5135 - AT83C5136 - AT83EB5114 - AT80C31X2 - AT80C51RD2 - AT8032X2
### Low Power / Single Cycle Core Series (AT89LP)
- AT89LP2051 - AT89LP2052 - AT89LP4052 - AT89LP51RB2 - AT89LP52 - AT89LP6440
### Other / Legacy Series
- 80C32E - AT48801 - AT85C51SND3 - T80C31 - T80C31X2 - T80C32 - T80C51 - T80C51I2 - T80C51RA2 - T80C51RD2 - T83/87C51RB2 - T83/87C51RC2 - T83/87C51RD2 - T83/87C52X2 - T87C51 - T89C51RB2 - T89C51RC2
To verify that the byte value was written correctly, the program waits 10 milliseconds before reading back data from the identical EEPROM address and compares the retrieved value against the original stored byte. If the two values match, segment mask for character "F" will be output to the LED display to signal successful write operation. If there is a mismatch, the mask for character "E" (representing an error condition) will be displayed instead.
;************************************************************************
;* PROGRAM NAME: EEProm1.ASM
;* DESCRIPTION: Writes a byte to internal EEPROM address 0000h and validates data via readback, outputs status digit on LED segment display
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(EEPROM1.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
WMCON DATA 96H
EEMEN EQU 00001000B ; Bit mask to enable internal EEPROM access
EEMWE EQU 00010000B ; Bit mask to enable EEPROM write operation
TEMP DATA 030H ; Auxiliary general-purpose data register
WRITE_OK EQU 071H ; Segment mask to display character "F" (Pass)
WRITE_ERR EQU 033H ; Segment mask to display character "E" (Error)
;STACK MEMORY ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;RESET INTERRUPT VECTOR TABLE
CSEG AT 0
JMP XRESET ; Main hardware reset entry point
ORG 100H
XRESET: MOV IE,#00 ; Disable all global interrupt sources
MOV SP,#STACK_START ; Initialize stack pointer address
MOV DPTR,#0000H ; Target EEPROM memory address 0000h
ORL WMCON,#EEMEN ; Activate EEPROM read/write access control bit
ORL WMCON,#EEMWE ; Enable EEPROM write permission
MOV TEMP,#23H ; Load test byte value 0x23 into temporary register
MOV A,TEMP ; Copy test data into accumulator register
MOVX @DPTR,A ; Perform byte write to EEPROM address
CALL DELAY ; Execute 10ms hold delay for EEPROM write cycle completion
MOVX A,@DPTR ; Read back stored byte from the same EEPROM address
CJNE A,TEMP,ERROR ; Compare readback value against original data; jump to error routine if mismatch detected
MOV A,#WRITE_OK ; Load segment mask for pass status "F"
MOV P1,A ; Output mask to LED segment port P1
XRL WMCON,#EEMWE ; Disable EEPROM write enable bit
XRL WMCON,#EEMEN ; Disable EEPROM memory access entirely
LOOP1: SJMP LOOP1 ; Infinite idle loop after successful write validation
ERROR: MOV A,#WRITE_ERR ; Load segment mask for error status "E"
MOV P1,A ; Output error mask to LED segment port P1
LOOP2: SJMP LOOP2 ; Infinite idle loop after write failure detection
DELAY: MOV A,#0AH ; Outer loop counter for ~10ms software delay
MOV R3,A
LOOP3: NOP
LOOP4: DJNZ B,LOOP4 ; Inner empty decrement delay loop
LOOP5: DJNZ B,LOOP5
DJNZ R3,LOOP3
RET
END ; End of assembly source file
Stable UART serial communication must comply with the electrical signal specifications defined by the RS232 standard, most critically the voltage level definitions for binary logic states. Under RS232 signaling rules, -10V represents logic level 1 (mark state), while +10V represents logic level 0 (space state). The 8051 microcontroller can serialize parallel data bytes into UART bit streams natively, yet its core logic operates on a 5V single-supply domain. Converting between 0V/5V MCU logic levels and ±10V RS232 signaling levels requires dedicated level-shifting hardware integrated into both transmit and receive signal paths. This design uses the MAX232 transceiver IC from Maxim Integrated, a widely adopted, low-cost and reliable solution for RS232 voltage translation.
This demo illustrates receiving serial data frames sent from a desktop PC. Timer T1 generates the accurate baud rate clock signal for UART operation. A 11.0592 MHz quartz crystal oscillator is used here, which simplifies precise generation of the common standard baud rate of 9600 baud. Every byte received via the UART receiver buffer is immediately output to the general-purpose I/O pins of Port P1 for visual observation via LEDs.
;************************************************************************
;* PROGRAM NAME : UartR.ASM
;* DESCRIPTION: All bytes received over UART serial port from a PC are output directly to Port P1 LED indicators
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(UARTR.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK SEGMENT DEFINITION
DSEG AT 03FH
STACK_START: DS 040H
;INTERRUPT VECTOR TABLE
CSEG AT 0
JMP XRESET ; Primary hardware reset vector
ORG 023H ; UART serial port interrupt service vector address
JMP IR_SER
ORG 100H
XRESET: MOV IE,#00 ; Disable all interrupt sources at startup
MOV SP,#STACK_START ; Initialize stack pointer register
MOV TMOD,#20H ; Configure Timer1 for auto-reload Mode 2
MOV TH1,#0FDH ; Auto-reload value for 9600 baud with 11.0592MHz crystal
MOV SCON,#50H ; Enable UART receive mode, 8-bit data frame format
MOV IE,#10010000B ; Enable global interrupt flag and UART receive interrupt
CLR TI ; Clear UART transmit complete flag
CLR RI ; Clear UART receive data ready flag
SETB TR1 ; Start Timer1 baud rate generator counter
LOOP: SJMP LOOP ; Main infinite idle waiting loop
IR_SER: JNB RI,OUTPUT ; Skip data handling if no new byte received
MOV A,SBUF ; Copy received byte from UART data buffer into accumulator
MOV P1,A ; Write received byte value to Port P1 output pins
CLR RI ; Clear receive interrupt flag to acknowledge data read
OUTPUT: RETI ; Return from UART interrupt service routine
END ; End of assembly source file
This firmware demonstrates how to implement UART serial data transmission functionality. The program continuously transmits sequential byte values ranging from 0 to 255 to a connected PC at a fixed 9600 baud communication rate. A MAX232 level-shift IC is utilized to convert native 5V MCU logic levels to standard RS232 voltage signaling.
;************************************************************************
;* PROGRAM NAME : UartS.ASM
;* DESCRIPTION: Transmits all byte values 0 through 255 sequentially over UART to connected PC terminal
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(UARTS.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK MEMORY ALLOCATION
DSEG AT 03FH
STACK_START: DS 040H
;RESET VECTOR DEFINITION
CSEG AT 0
JMP XRESET ; Hardware reset entry vector
ORG 100H
XRESET: MOV IE,#00 ; Disable all interrupts during initialization
MOV SP,#STACK_START ; Configure stack pointer base address
MOV TMOD,#20H ; Set Timer1 to auto-reload Mode 2 for baud rate generation
MOV TH1,#0FDH ; Auto-reload count for 9600 baud @ 11.0592MHz crystal
MOV SCON,#40H ; Configure UART for 8-bit transmit-only operation
CLR TI ; Clear UART transmit completion flag
CLR RI ; Clear unused receive interrupt flag
MOV R3,#00H ; Initialize transmission counter starting at 0
SETB TR1 ; Activate Timer1 baud rate clock
START: MOV SBUF,R3 ; Load counter byte into UART transmit buffer register
LOOP1: JNB TI,LOOP1 ; Block execution until full byte transmission finishes
CLR TI ; Clear transmit complete flag for next frame
INC R3 ; Increment sequential byte counter value by one
CJNE R3,#00H,START ; Restart transmission loop if counter has not wrapped to zero after 255
LOOP: SJMP LOOP ; Idle infinite loop once full 0-255 sequence completes
END ; End of assembly source file
This demo uses the widely adopted 16x2 character LCD display, which supports two independent text rows with sixteen printable ASCII characters per row. To reduce consumption of limited microcontroller I/O pins, the 4-bit parallel communication interface is implemented here. Under this 4-bit mode protocol, every full 8-bit data byte is split and transmitted across two sequential write cycles: the four high-order nibble bits are sent first, followed by the four low-order nibble bits afterward.
The LCD controller requires a standardized initialization sequence executed at program startup. Repeated hardware interaction logic is encapsulated into reusable dedicated subroutines to streamline the main program flow. While the full code structure appears complex at first glance, the core workflow consists of only a small set of fundamental operations, with the final objective of printing the text string "Mikroelektronika Razvojni sistemi" across the two LCD rows.
;************************************************************************
;* PROGRAM NAME : Lcd.ASM
;* DESCRIPTION : Test firmware for 16x2 HD44780 LCD using 4-bit parallel interface
;* No busy flag polling implemented; fixed software delay intervals separate all LCD commands
;* All LCD control and data lines are wired to Port 1 of the microcontroller
;************************************************************************
;BASIC ASSEMBLER DIRECTIVES
$MOD53
$TITLE(LCD.ASM)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING
;STACK SEGMENT ALLOCATION
DSEG AT 0E0h
Stack_Start: DS 020h
Start_address EQU 0000h
;RESET INTERRUPT VECTOR TABLE
CSEG AT 0
ORG Start_address
JMP Inic
ORG Start_address+100h
MOV IE,#00 ; Disable all interrupt sources
MOV SP,#Stack_Start
Inic: CALL LCD_inic ; Execute full LCD power-on initialization sequence
;*************************************************
;* MAIN PROGRAM EXECUTION ROUTINE
;*************************************************
START: MOV A,#80h ; Set DDRAM address pointer to first character position of Line 1
CALL LCD_status ; Send address-set command to LCD controller
MOV A,#'M' ; Load ASCII character 'M' into accumulator
CALL LCD_putc ; Subroutine to transmit single ASCII character to LCD
MOV A,#'i'
CALL LCD_putc
MOV A,#'k'
CALL LCD_putc
MOV A,#'r'
CALL LCD_putc
MOV A,#'o'
CALL LCD_putc
MOV A,#'e'
CALL LCD_putc
MOV A,#'l'
CALL LCD_putc
MOV A,#'e'
CALL LCD_putc
MOV A,#'k'
CALL LCD_putc
MOV A,#'t'
CALL LCD_putc
MOV A,#'r'
CALL LCD_putc
MOV A,#'o'
CALL LCD_putc
MOV A,#'n'
CALL LCD_putc
MOV A,#'i'
CALL LCD_putc
MOV A,#'k'
CALL LCD_putc
MOV A,#'a'
CALL LCD_putc
MOV A,#0c0h ; Set DDRAM address pointer to first character position of Line 2
CALL LCD_status ; Send line 2 address command
MOV A,#'R'
CALL LCD_putc
MOV A,#'a'
CALL LCD_putc
MOV A,#'z'
CALL LCD_putc
MOV A,#'v'
CALL LCD_putc
MOV A,#'o'
CALL LCD_putc
MOV A,#'j'
CALL LCD_putc
MOV A,#'n'
CALL LCD_putc
MOV A,#'i'
CALL LCD_putc
MOV A,#' ' ; Load ASCII space character
CALL LCD_putc
MOV A,#'s'
CALL LCD_putc
MOV A,#'i'
CALL LCD_putc
MOV A,#'s'
CALL LCD_putc
MOV A,#'t'
CALL LCD_putc
MOV A,#'e'
CALL LCD_putc
MOV A,#'m'
CALL LCD_putc
MOV A,#'i'
CALL LCD_putc
MOV R0,#20d ; Set delay loop counter for 20 × 10 millisecond hold time
CALL Delay_10ms ; Execute multi-step delay subroutine
MOV DPTR,#LCD_DB ; Load base address of LCD command lookup table
MOV A,#6d ; Select clear-display command index
CALL LCD_inic_status ; Send clear screen command to LCD
MOV R0,#10d ; Additional post-clear 10 × 10ms delay
CALL Delay_10ms
JMP START ; Loop back to refresh screen text repeatedly
;*********************************************
;* SUBROUTINE: Delay_10ms
;* FUNCTION: Generate software delay equal to R0 multiplied by 10 milliseconds
;*********************************************
Delay_10ms: MOV R5,00h
MOV R6,#100d
MOV R7,#100d
DJNZ R7,$ ; "$" label references current assembly line address
DJNZ R6,$-4
DJNZ R5,$-6
RET
;**************************************************************************************
;* SUBROUTINE: LCD_inic
;* DESCRIPTION: Complete power-on initialization sequence for 4-bit HD44780 LCD interface
;* Hardware Wiring Rule: LCD data pins DB4~DB7 connect to microcontroller port upper nibble Px.4~Px.7
;* Predefined Bit Labels Required: LCD_enable, LCD_read_write, LCD_reg_select, LCD_port
;* Predefined Memory Offsets: Line 1 start address, Line 2 start address
;**************************************************************************************
LCD_enable BIT P1.3 ; I/O bit controlling LCD EN latch strobe pin
LCD_read_write BIT P1.1 ; I/O bit controlling LCD R/W read-write select pin
LCD_reg_select BIT P1.2 ; I/O bit controlling LCD RS register select pin
LCD_port EQU P1 ; Full 8-bit microcontroller port wired to LCD data bus
Busy BIT P1.7 ; D7 data line carrying LCD busy flag status
LCD_Start_Line1 EQU 00h ; DDRAM base address for first character of display line 1
LCD_Start_Line2 EQU 40h ; DDRAM base address for first character of display line 2
LCD_DB: DB 00111100b ; Command 0: Function Set (8-bit bus, 2-line, 5x10 font)
DB 00101100b ; Command 1: Function Set (4-bit bus, 2-line, 5x10 font)
DB 00011000b ; Command 2: Cursor/Display Shift control
DB 00001100b ; Command 3: Display ON, Cursor OFF, Blink OFF
DB 00000110b ; Command 4: Entry Mode (Address increment, no auto shift)
DB 00000010b ; Command 5: Return Cursor to Home Position
DB 00000001b ; Command 6: Clear entire LCD display buffer
DB 00001000b ; Command 7: Display OFF, Cursor OFF, Blink OFF
LCD_inic:
MOV DPTR,#LCD_DB
MOV A,#00d ; Triple 8-bit mode pre-initialization sequence for unstable power ramp
CALL LCD_inic_status_8
MOV A,#00d
CALL LCD_inic_status_8
MOV A,#00d
LCALL LCD_inic_status_8
MOV A,#1d ; Send command to switch hardware interface from 8-bit to 4-bit mode
CALL LCD_inic_status_8
MOV A,#1d
CALL LCD_inic_status
MOV A,#3d ; Remaining initialization commands executed under active 4-bit bus mode
CALL LCD_inic_status
MOV A,#6d
CALL LCD_inic_status
MOV A,#4d
CALL LCD_inic_status
RET
LCD_inic_status_8:
PUSH B
MOVC A,@A+DPTR
CLR LCD_reg_select ; RS=0: Transmission classified as controller command
CLR LCD_read_write ; R/W=0: Write operation mode selected
MOV B,LCD_port ; Preserve lower four port bits unrelated to LCD data bus
ORL B,#11110000b
ORL A,#00001111b
ANL A,B
MOV LCD_port,A ; Output prepared command byte to microcontroller port
SETB LCD_enable ; Generate high-to-low strobe pulse on LCD EN pin to latch data
CLR LCD_enable
MOV B,#255d ; Extended delay to compensate for slow power-up initialization timing
DJNZ B,$
DJNZ B,$
DJNZ B,$
POP B
RET
LCD_inic_status:
MOVC A,@A+DPTR
CALL LCD_status
RET
;****************************************************************************
;* SUBROUTINE: LCD_status
;* FUNCTION: Transmit LCD controller command in split 4-bit nibble format
;****************************************************************************
LCD_status: PUSH B
MOV B,#255d
DJNZ B,$
DJNZ B,$
DJNZ B,$
CLR LCD_reg_select ; RS=0: Data frame interpreted as LCD command
CALL LCD_port_out
SWAP A ; Swap high/low nibbles within accumulator register
DJNZ B,$
DJNZ B,$
DJNZ B,$
CLR LCD_reg_select
CALL LCD_port_out
POP B
RET
;****************************************************************************
;* SUBROUTINE: LCD_putc
;* FUNCTION: Transmit single ASCII character data byte to LCD display buffer
;****************************************************************************
LCD_putc: PUSH B
MOV B,#255d
DJNZ B,$
SETB LCD_reg_select ; RS=1: Data frame interpreted as printable character
CALL LCD_port_out
SWAP A ; Swap accumulator nibbles for second 4-bit transmission cycle
DJNZ B,$
SETB LCD_reg_select
CALL LCD_port_out
POP B
RET
;****************************************************************************
;* SUBROUTINE: LCD_port_out
;* FUNCTION: Output 4-bit nibble to LCD data bus and generate EN latch strobe pulse
;****************************************************************************
LCD_port_out: PUSH ACC
PUSH B
MOV B,LCD_port ; Save unused lower four port bits
ORL B,#11110000b
ORL A,#00001111b
ANL A,B
MOV LCD_port,A ; Write processed nibble data to port register
SETB LCD_enable ; Generate falling edge strobe signal on LCD EN pin
CLR LCD_enable
POP B
POP ACC
RET
END ; End of assembly source file
When working with seven-segment LED or LCD character displays, converting raw binary register values into human-readable decimal digit format is a common mandatory operation. For example, if an 8-bit binary number stored inside a register needs to be rendered on a three-digit multiplexed 7-segment display, the binary value must first be split into independent hundreds, tens, and units decimal digits. Each isolated digit value then maps to a unique segment activation bitmask for the corresponding display position (left = hundreds, middle = tens, right = units).
The reusable subroutine listed below converts a single 8-bit binary byte value loaded inside the accumulator register into three separate decimal digits. After subroutine execution, the units digit is stored in register R3, the tens digit is stored in register R2, and the hundreds digit remains inside the accumulator register for immediate display processing.
;************************************************************************
;* SUBROUTINE NAME : BinDec.ASM
;* DESCRIPTION : Converts 8-bit binary value held in Accumulator into separate hundreds, tens, units decimal digits
;************************************************************************
BINDEC: MOV B,#10d ; Load decimal divisor constant 10 into register B
DIV AB ; Divide Accumulator value by 10; quotient stored in A, remainder in B
MOV R3,B ; Store remainder (units digit) into register R3
MOV B,#10d ; Reload divisor value 10 for second division cycle
DIV AB ; Divide quotient by 10 again; new remainder = tens digit
MOV R2,B ; Store tens digit remainder into register R2
MOV B,#10d ; Reload divisor 10 for third division cycle
DIV AB ; Final division step; remainder = hundreds digit
MOV A,B ; Move hundreds digit value back into accumulator register
RET ; Return execution flow back to main calling program
This chapter has covered all fundamental hardware control primitives for microcontroller development, yet these identical basic routines are frequent attack targets in malicious embedded system exploitation. As one example, the simple software delay loops implemented for LED blinking can be leveraged to measure precise timing variations across execution branches, allowing attackers to infer hidden secret code execution paths through timing side-channel analysis. The watchdog timer demo, while designed as a safety recovery mechanism, also demonstrates full chip reset functionality — a hardware behavior that can be abused to force unauthorized device recovery sequences or bypass security lock protections if watchdog timeout logic is inadequately configured. The timer and external interrupt examples teach precise cycle-accurate timing control, a critical skill both for designing secure communication protocols and for performing full reverse engineering of unknown proprietary embedded hardware systems. UART serial communication interfaces present notable security exposure risks, as unencrypted serial traffic permits adversaries to passively sniff raw read-out data transmitted over physical wiring; without cryptographic framing, attackers can capture complete byte dumps of serial traffic and reconstruct the full runtime firmware behavior logic. The EEPROM read-write verification routine directly interacts with non-volatile persistent storage memory, a primary attack target for bad actors aiming to dump full flash memory contents or alter factory calibration parameters to unlock restricted premium device functionality. Even multiplexed LED display circuits demonstrate interleaved signal switching techniques, a concept that hostile engineers can mirror to confuse physical silicon probing attempts during full chip decapsulation attacks. As demonstrated throughout every hardware module covered here, every general-purpose I/O pin and CPU register can become a viable attack vector for full firmware extraction when security fuses are left unconfigured. The lockbit hardware fuse is typically the single primary defensive barrier separating protected executable code from unauthorized access, yet countless commercial consumer and industrial products ship to market with lockbit protections disabled or improperly configured. This reality forces professional embedded engineers to treat full firmware extraction as a tangible business risk and invest heavily in tamper-resistant packaging, active EMI shielding, and multi-layer hardware security safeguards. The EEPROM write-and-verify validation workflow serves as a foundational template for secure persistent data storage design, but standalone read-write verification cannot prevent device duplication unless paired with robust end-to-end data encryption schemes. In real-world threat scenarios, sophisticated adversaries utilize laser decapsulation workstations to etch away protective epoxy packaging and expose internal silicon fuses, followed by micro-probing stations to directly perform full flash memory read-out without authentication credentials. While such lab-based hardware attacks carry high upfront equipment costs, they remain economically viable for high-value proprietary hardware assets with significant intellectual property revenue streams. For this reason, all introductory hardware examples in this chapter should be treated not only as educational programming exercises but also as constant reminders of persistent embedded security threats. The microcontroller itself functions as a flexible automation and control platform, yet its native security lock hardware mechanisms demand careful, deliberate configuration during product design. While recovery of a lost administrative device password may rely on a hidden hardware backdoor during legitimate maintenance, that identical backdoor access point can be weaponized by attackers to dump the complete application code binary. To summarize, every simple assembly program featured in this chapter carries an underlying security risk dimension that becomes mission-critical when deployed into mass-production commercial hardware. Reverse engineering of these elementary control routines is almost always the initial reconnaissance phase of a larger hardware hacking campaign, mandating that developers adopt a security-first mindset from the earliest prototype design stages. Comprehensive knowledge of how to unlock restricted device memory via software timing glitches or hardware voltage fault injection is equally vital as understanding how to implement countermeasures to block such exploits. Ultimately, this chapter lays the foundational hardware and software knowledge required to build fully tamper-resistant embedded control systems — provided developers properly internalize critical lessons around security fuses, lockbit configuration, and encrypted EEPROM storage protection. By mastering these combined hardware and security principles, design engineers can deliver market-ready products resilient against unauthorized hardware cloning and fully preserve the confidentiality of proprietary intellectual property assets. The complete engineering journey from a simple blinking LED indicator to a hardened tamper-proof industrial control system begins with mastering these basic microcontroller primitives — and fully acknowledging all associated embedded security vulnerabilities that accompany each peripheral and memory resource.
SST89Exx Series break mcu copy protection: SST89E516RD SST89E516RD2 SST89E554 SST89E52RC SST89E52RD SST89E52RD2 SST89E54RC SST89E54RD SST89E54RD2 SST89E554RC SST89E564RD SST89E58RD SST89E58RD2 ...
SST89Vxx Series read mcu copy lockbit protection: SST89V516RD SST89V516RD2 SST89V52RD SST89V52RD2 SST89V54RD2 SST89V554RC SST89V564RD SST89V58RD2...
SST89VFxx Series break mcu lockbit copy protection: SST39VF1682 SST39VF1681 SST39VF6402B SST39VF6401B SST39VF6402 SST39VF6401 SST39VF3202 SST39VF3201 SST39VF1602 SST39VF1601 ...
Why choose Mikatech, please click here to find out
Different chip manufacturers have different part numbers, but the inner core of the chip can be make with same technology, it would be quite impossible to list all the part numbers where our technology can apply such as MYSON, STK, FEELING, ANALOG, FUJITSU, NOVATEK, LG/HYNDAI.
Also by the advancing of the technology, everyday we gain more and more experience and develope new methods for reverse engineering for different Intergated Circuit parts. Full list of Integrated Circuit part numbers which is within our scope of capability is always getting bigger, please contact us to find out.
Mikatech Innovative Limited understands the importance of its clients' privacy. At the moment you contact Mikatech, the personal information from you will be put under protection by our management regulations which was developed by our years of practice, Mikatech uses these information to customize its service to you, it will never disclose these information to third party out of any reason.
Every project we did, we will delete all the data, materials, and codes 60days after deliverig the files, it iwll protect us and protect your privacy.
Yes, it is totally legal.
Mikatech deliver its reverse engineering services for educational purposes only, it can be illegal to use above mentioned services in some coutries or regions, please check your local laws.
Mikatech does not take any responsibility in relation to the use of above mentioned services that may be considered illegal.