Hi bill_lancaster,
If you really want to understand how serial communication and the USART peripheral work at the absolute hardware level, take a look at this.
Unfortunately, this code is written for the Arduino Uno / Nano (ATmega328P) and not for your ESP8266. However, it has a huge advantage: you can test and compile it directly in your browser using https://costycnc.github.io/avr-compiler-js/
video youtube
If you just want a quick fix for your ESP project, feel free to ignore this message. But if you love understanding what happens inside the chip registers under the hood, here is a complete layout. It shows exactly how the hardware handles the letter 'A' you are sending in your loop.
; ==============================================================================
; WHAT THIS ASSEMBLY (ASM) CODE DOES UNDER THE HOOD:
; ==============================================================================
; 1. It sets the built-in LED pin (D13) as an output.
; 2. It touches the hardware boxes (registers) to set the serial speed to 9600 baud.
; 3. It configures the frame size and turns on the data channels (RX and TX).
; 4. It turns on a hardware "alarm" (interrupt) that triggers automatically the exact millisecond a byte arrives.
; 5. While the board does nothing in the main infinite loop, it just waits.
; 6. As soon as you send a character from the PC (or another chip), the hardware alarm triggers:
; * If the character is the letter "A" (ASCII number 65), the LED turns ON.
; * If you send any other letter, the LED turns OFF.
; ==============================================================================
.org 0x0000
jmp RESET
.org 0x0024
jmp receive
.org 0x0060
RESET:
sbi 0x04, 5
ldi r16, 103
ldi r17, 0
sts 0xC4, r16
sts 0xC5, r17
ldi r16, 0b00000110
sts 0xC2, r16
ldi r16, 0b10011000
sts 0xC1, r16
sei
loop:
rjmp loop
receive:
sbi 0x05, 5
lds r16, 0xC6
cpi r16, 65
breq next
cbi 0x05, 5
next:
reti
; ==============================================================================
; PART 2: COMPLETELY COMMENTED CODE (FOR ABSOLUTE BEGINNERS) - Hidden from compiler
; ==============================================================================
; Think of a "register" as a box with 8 light switches.
; Each switch is a "bit" numbered from 7 to 0 (from left to right).
; Switch UP (1) = electricity flows or a function turns ON.
; Switch DOWN (0) = no electricity or a function turns OFF.
;
; Box 0x04: controls whether wires D8 to D13 are INPUTS (0) or OUTPUTS (1).
; Box 0x05: gives POWER (1) or turns OFF (0) wires D8 to D13.
; Boxes 0xC4 / 0xC5: set the serial port speed (to talk to the PC).
; Boxes 0xC1 / 0xC2: turn ON the listening (RX) and sending (TX) channels.
; Box 0xC6: the mailbox where the message from the PC arrives.
; ----------------------------------------------
;
; .org 0x0000 Set code position to address 0x0000 in memory
; jmp RESET Jump to the RESET section when the chip turns on
;
; .org 0x0024 Set code position to address 0x0024 (Serial message arrived address)
; jmp receive Jump to the receive section as soon as a message arrives
;
; .org 0x0060 Set code position to address 0x0060 to start main program
; RESET: Label for the start of the setup configuration
; sbi 0x04, 5 Flip switch #5 in box 0x04 to ON to make wire D13 an OUTPUT
;
; ldi r16, 103 Load the number 103 into temporary register drawer r16
; ldi r17, 0 Load the number 0 into temporary register drawer r17
; sts 0xC4, r16 Copy number 103 from drawer r16 into speed box 0xC4
; sts 0xC5, r17 Copy number 0 from drawer r17 into speed box 0xC5
;
; We set the text size: 8 bits per character
; Switches: 7 6 5 4 3 2 1 0
; ldi r16, 0b00000110
; β β
; β βββ Switch #1 UP (UCSZ01 - Text size helper bit)
; βββββ Switch #2 UP (UCSZ00 - Text size main bit)
; (When both #1 and #2 are UP, text size is 8-bit)
;
; sts 0xC2, r16 Copy these switches from drawer r16 into serial control box 0xC2
;
; We turn on communication and the alarm for incoming messages
; Switches: 7 6 5 4 3 2 1 0
; ldi r16, 0b10011000
; β β β
; β β βββ Switch #3 UP (TXEN0 - Turns ON data sending)
; β βββββ Switch #4 UP (RXEN0 - Turns ON data receiving)
; βββββββββ Switch #7 UP (RXCIE0 - Turns ON alarm when message arrives)
;
; sts 0xC1, r16 Copy these switches from drawer r16 into serial control box 0xC1
;
; sei Global Interrupt Enable: turn ON the main alarm switch
;
; loop: Label for the main infinite circle
; rjmp loop Relative Jump back to loop: spin here and do nothing forever
;
; receive: Label for the alarm code that runs when a message arrives
; sbi 0x05, 5 Flip switch #5 in box 0x05 to ON to give power to wire D13 (LED ON)
;
; lds r16, 0xC6 Load the message byte from mailbox box 0xC6 into drawer r16
;
; cpi r16, 65 Compare the byte in drawer r16 with number 65 (ASCII for 'A')
;
; breq next Branch if Equal: if it is 'A', skip the next line and go to next
;
; cbi 0x05, 5 Clear Bit: flip switch #5 in box 0x05 to OFF to turn off wire D13 (LED OFF)
;
; next: Label used to skip the LED turning off
; reti Return from Interrupt: turn the main alarm off and go back to the loop
If you compile this code and then disassemble the final binary file, you will see the EXACT SAME clean code you wrote.
There are no hidden instructions, no automatic libraries, and no magic shortcuts.
What you see is exactly what the CPU executes, giving you a 100% understanding!
But remember, this is only a teaching tool; it is not meant for production code.
Good luck with your ESP8266 structure transfer project!