I am trying to run following code on AT89C2051, using Keil for compiling and programming hex file with Superpro.
#include <reg51.h>
// Define a delay function
void delay_ms(unsigned int milliseconds) {
unsigned int i, j;
for (i = 0; i < milliseconds; i++) {
for (j = 0; j < 1275; j++) { // Approximate delay, adjust based on clock frequency
// Delay loop
}
}
}
// Main function
void main() {
while (1) {
// Turn the LED on (assuming LED is connected to P0.0)
P1 = 0x00; // Set P1 to 0 (low voltage) to turn the LED on
delay_ms(500); // Delay for 500 milliseconds
// Turn the LED off
P1 = 0xFF; // Set P1 to FF (high voltage) to turn the LED off
delay_ms(500); // Delay for 500 milliseconds
}
}
The code never works I suspect it is not getting programmed correctly. Please tell me what is the correct procedure to program this microcontroller.
Following is the procedure that I am following.
Compile above I/O toggling code in Keil with device AT89C2051 and clock 11.0592MHz selected.
Generated hex file is loaded into superpro610p. During load buffer I tried both binary and intel format option in Superpro.
Programmed MCU with loaded buffer.
No toggle appears on any pin of MCU.
HEX output file generated by Keil.
:0300000002083DB6 :0C083D00787FE4F6D8FD75810702082FD3 :10080000900000AF82AE83E4FCFDFBFA7901F8D3DF :090810001208194003A380EB2239 :0E082F00E4F5901208007590FF12080080F2A8 :10081900EB9FF5F0EA9E42F0E99D42F0EC6480C856 :0608290064809845F022F6 :00000001FF
Startup.lst file generated by Keil.
A51 MACRO ASSEMBLER STARTUP 06/11/2025 10:53:16 PAGE 1 MACRO ASSEMBLER A51 V8.2.7.0 OBJECT MODULE PLACED IN .\Objects\STARTUP.obj ASSEMBLER INVOKED BY: C:\Keil_v5\C51\BIN\A51.EXE STARTUP.A51 SET(SMALL) PRINT(.\Listings\STARTUP.lst) OBJECT(.\Objects\S TARTUP.obj) EP LOC OBJ LINE SOURCE 1 $nomod51 2 ;------------------------------------------------------------------------------ 3 ; This file is part of the C51 Compiler package 4 ; Copyright (c) 1988-2005 Keil Elektronik GmbH and Keil Software, Inc. 5 ; Version 8.01 6 ; 7 ; *** <<< Use Configuration Wizard in Context Menu >>> *** 8 ;------------------------------------------------------------------------------ 9 ; STARTUP.A51: This code is executed after processor reset. 10 ; 11 ; To translate this file use A51 with the following invocation: 12 ; 13 ; A51 STARTUP.A51 14 ; 15 ; To link the modified STARTUP.OBJ file to your application use the following 16 ; Lx51 invocation: 17 ; 18 ; Lx51 your object file list, STARTUP.OBJ controls 19 ; 20 ;------------------------------------------------------------------------------ 21 ; 22 ; User-defined <h> Power-On Initialization of Memory 23 ; 24 ; With the following EQU statements the initialization of memory 25 ; at processor reset can be defined: 26 ; 27 ; <o> IDATALEN: IDATA memory size <0x0-0x100> 28 ; <i> Note: The absolute start-address of IDATA memory is always 0 29 ; <i> The IDATA space overlaps physically the DATA and BIT areas. 0080 30 IDATALEN EQU 80H 31 ; 32 ; <o> XDATASTART: XDATA memory start address <0x0-0xFFFF> 33 ; <i> The absolute start address of XDATA memory 0000 34 XDATASTART EQU 0 35 ; 36 ; <o> XDATALEN: XDATA memory size <0x0-0xFFFF> 37 ; <i> The length of XDATA memory in bytes. 0000 38 XDATALEN EQU 0 39 ; 40 ; <o> PDATASTART: PDATA memory start address <0x0-0xFFFF> 41 ; <i> The absolute start address of PDATA memory 0000 42 PDATASTART EQU 0H 43 ; 44 ; <o> PDATALEN: PDATA memory size <0x0-0xFF> 45 ; <i> The length of PDATA memory in bytes. 0000 46 PDATALEN EQU 0H 47 ; 48 ;</h> 49 ;------------------------------------------------------------------------------ 50 ; 51 ;<h> Reentrant Stack Initialization 52 ; 53 ; The following EQU statements define the stack pointer for reentrant 54 ; functions and initialized it: 55 ; 56 ; <h> Stack Space for reentrant functions in the SMALL model. 57 ; <q> IBPSTACK: Enable SMALL model reentrant stack A51 MACRO ASSEMBLER STARTUP 06/11/2025 10:53:16 PAGE 2 58 ; <i> Stack space for reentrant functions in the SMALL model. 0000 59 IBPSTACK EQU 0 ; set to 1 if small reentrant is used. 60 ; <o> IBPSTACKTOP: End address of SMALL model stack <0x0-0xFF> 61 ; <i> Set the top of the stack to the highest location. 0100 62 IBPSTACKTOP EQU 0xFF +1 ; default 0FFH+1 63 ; </h> 64 ; 65 ; <h> Stack Space for reentrant functions in the LARGE model. 66 ; <q> XBPSTACK: Enable LARGE model reentrant stack 67 ; <i> Stack space for reentrant functions in the LARGE model. 0000 68 XBPSTACK EQU 0 ; set to 1 if large reentrant is used. 69 ; <o> XBPSTACKTOP: End address of LARGE model stack <0x0-0xFFFF> 70 ; <i> Set the top of the stack to the highest location. 0000 71 XBPSTACKTOP EQU 0xFFFF +1 ; default 0FFFFH+1 72 ; </h> 73 ; 74 ; <h> Stack Space for reentrant functions in the COMPACT model. 75 ; <q> PBPSTACK: Enable COMPACT model reentrant stack 76 ; <i> Stack space for reentrant functions in the COMPACT model. 0000 77 PBPSTACK EQU 0 ; set to 1 if compact reentrant is used. 78 ; 79 ; <o> PBPSTACKTOP: End address of COMPACT model stack <0x0-0xFFFF> 80 ; <i> Set the top of the stack to the highest location. 0100 81 PBPSTACKTOP EQU 0xFF +1 ; default 0FFH+1 82 ; </h> 83 ;</h> 84 ;------------------------------------------------------------------------------ 85 ; 86 ; Memory Page for Using the Compact Model with 64 KByte xdata RAM 87 ; <e>Compact Model Page Definition 88 ; 89 ; <i>Define the XDATA page used for PDATA variables. 90 ; <i>PPAGE must conform with the PPAGE set in the linker invocation. 91 ; 92 ; Enable pdata memory page initalization 0000 93 PPAGEENABLE EQU 0 ; set to 1 if pdata object are used. 94 ; 95 ; <o> PPAGE number <0x0-0xFF> 96 ; <i> uppermost 256-byte address of the page used for PDATA variables. 0000 97 PPAGE EQU 0 98 ; 99 ; <o> SFR address which supplies uppermost address byte <0x0-0xFF> 100 ; <i> most 8051 variants use P2 as uppermost address byte 00A0 101 PPAGE_SFR DATA 0A0H 102 ; 103 ; </e> 104 ;------------------------------------------------------------------------------ 105 106 ; Standard SFR Symbols 00E0 107 ACC DATA 0E0H 00F0 108 B DATA 0F0H 0081 109 SP DATA 81H 0082 110 DPL DATA 82H 0083 111 DPH DATA 83H 112 113 NAME ?C_STARTUP 114 115 116 ?C_C51STARTUP SEGMENT CODE 117 ?STACK SEGMENT IDATA 118 ---- 119 RSEG ?STACK 0000 120 DS 1 121 122 EXTRN CODE (?C_START) 123 PUBLIC ?C_STARTUP A51 MACRO ASSEMBLER STARTUP 06/11/2025 10:53:16 PAGE 3 124 ---- 125 CSEG AT 0 0000 020000 F 126 ?C_STARTUP: LJMP STARTUP1 127 ---- 128 RSEG ?C_C51STARTUP 129 0000 130 STARTUP1: 131 132 IF IDATALEN <> 0 0000 787F 133 MOV R0,#IDATALEN - 1 0002 E4 134 CLR A 0003 F6 135 IDATALOOP: MOV @R0,A 0004 D8FD 136 DJNZ R0,IDATALOOP 137 ENDIF 138 139 IF XDATALEN <> 0 MOV DPTR,#XDATASTART MOV R7,#LOW (XDATALEN) IF (LOW (XDATALEN)) <> 0 MOV R6,#(HIGH (XDATALEN)) +1 ELSE MOV R6,#HIGH (XDATALEN) ENDIF CLR A XDATALOOP: MOVX @DPTR,A INC DPTR DJNZ R7,XDATALOOP DJNZ R6,XDATALOOP ENDIF 153 154 IF PPAGEENABLE <> 0 MOV PPAGE_SFR,#PPAGE ENDIF 157 158 IF PDATALEN <> 0 MOV R0,#LOW (PDATASTART) MOV R7,#LOW (PDATALEN) CLR A PDATALOOP: MOVX @R0,A INC R0 DJNZ R7,PDATALOOP ENDIF 166 167 IF IBPSTACK <> 0 EXTRN DATA (?C_IBP) MOV ?C_IBP,#LOW IBPSTACKTOP ENDIF 172 173 IF XBPSTACK <> 0 EXTRN DATA (?C_XBP) MOV ?C_XBP,#HIGH XBPSTACKTOP MOV ?C_XBP+1,#LOW XBPSTACKTOP ENDIF 179 180 IF PBPSTACK <> 0 EXTRN DATA (?C_PBP) MOV ?C_PBP,#LOW PBPSTACKTOP ENDIF 184 0006 758100 F 185 MOV SP,#?STACK-1 186 187 ; This code is required if you use L51_BANK.A51 with Banking Mode 4 188 ;<h> Code Banking 189 ; <q> Select Bank 0 for L51_BANK.A51 Mode 4 A51 MACRO ASSEMBLER STARTUP 06/11/2025 10:53:16 PAGE 4 190 195 ;</h> 0009 020000 F 196 LJMP ?C_START 197 198 END A51 MACRO ASSEMBLER STARTUP 06/11/2025 10:53:16 PAGE 5 SYMBOL TABLE LISTING ------ ----- ------- N A M E T Y P E V A L U E ATTRIBUTES ?C_C51STARTUP. . . C SEG 000CH REL=UNIT ?C_START . . . . . C ADDR ----- EXT ?C_STARTUP . . . . C ADDR 0000H A ?STACK . . . . . . I SEG 0001H REL=UNIT ACC. . . . . . . . D ADDR 00E0H A B. . . . . . . . . D ADDR 00F0H A DPH. . . . . . . . D ADDR 0083H A DPL. . . . . . . . D ADDR 0082H A IBPSTACK . . . . . N NUMB 0000H A IBPSTACKTOP. . . . N NUMB 0100H A IDATALEN . . . . . N NUMB 0080H A IDATALOOP. . . . . C ADDR 0003H R SEG=?C_C51STARTUP PBPSTACK . . . . . N NUMB 0000H A PBPSTACKTOP. . . . N NUMB 0100H A PDATALEN . . . . . N NUMB 0000H A PDATASTART . . . . N NUMB 0000H A PPAGE. . . . . . . N NUMB 0000H A PPAGEENABLE. . . . N NUMB 0000H A PPAGE_SFR. . . . . D ADDR 00A0H A SP . . . . . . . . D ADDR 0081H A STARTUP1 . . . . . C ADDR 0000H R SEG=?C_C51STARTUP XBPSTACK . . . . . N NUMB 0000H A XBPSTACKTOP. . . . N NUMB 0000H A XDATALEN . . . . . N NUMB 0000H A XDATASTART . . . . N NUMB 0000H A REGISTER BANK(S) USED: 0 ASSEMBLY COMPLETE. 0 WARNING(S), 0 ERROR(S)
Another lst file in the project.
C51 COMPILER V9.60.7.0 CODE 06/11/2025 14:25:16 PAGE 1
C51 COMPILER V9.60.7.0, COMPILATION OF MODULE CODE
OBJECT MODULE PLACED IN .\Objects\CODE.obj
COMPILER INVOKED BY: C:\Keil_v5\C51\BIN\C51.EXE CODE.C OPTIMIZE(8,SPEED) PRINT(.\Listings\CODE.lst) TABS(2) OBJECT(.\Obj
-ects\CODE.obj)
line level source
1 #include <reg51.h>
2 #include <intrins.h>
3 int x=0;
4 // Define a delay function
5 void delay_ms(unsigned int milliseconds) {
6 1 unsigned int i, j;
7 1 unsigned int x=0;
8 1 for (i = 0; i < milliseconds; i++) {
9 2 for (j = 0; j < 1275; j++) { // Approximate delay, adjust based on clock frequency
10 3 _nop_();
11 3 }
12 2 }
13 1 }
14
15 // Main function
16 void main() {
17 1 while (1) {
18 2 // Turn the LED on (assuming LED is connected to P0.0)
19 2 P1 = 0x00; // Set P1 to 0 (low voltage) to turn the LED on
20 2 delay_ms(500); // Delay for 500 milliseconds
21 2
22 2 // Turn the LED off
23 2 P1 = 0xFF; // Set P1 to FF (high voltage) to turn the LED off
24 2 delay_ms(500); // Delay for 500 milliseconds
25 2 }
26 1 }
MODULE INFORMATION: STATIC OVERLAYABLE
CODE SIZE = 55 ----
CONSTANT SIZE = ---- ----
XDATA SIZE = ---- ----
PDATA SIZE = ---- ----
DATA SIZE = 2 2
IDATA SIZE = ---- ----
BIT SIZE = ---- ----
END OF MODULE INFORMATION.
C51 COMPILATION COMPLETE. 0 WARNING(S), 0 ERROR(S)
- m51 file in the project folder.
BL51 BANKED LINKER/LOCATER V6.22.4.0 06/11/2025 14:25:16 PAGE 1
BL51 BANKED LINKER/LOCATER V6.22.4.0, INVOKED BY:
C:\KEIL_V5\C51\BIN\BL51.EXE .\Objects\STARTUP.obj, .\Objects\CODE.obj TO .\Objects\AMMETER PRINT (.\Listings\AMMETER.m51
>> )
MEMORY MODEL: SMALL
INPUT MODULES INCLUDED:
.\Objects\STARTUP.obj (?C_STARTUP)
.\Objects\CODE.obj (CODE)
C:\KEIL_V5\C51\LIB\C51S.LIB (?C_INIT)
LINK MAP OF MODULE: .\Objects\AMMETER (?C_STARTUP)
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0002H UNIT ?DT?CODE
DATA 000AH 0002H UNIT _DATA_GROUP_
IDATA 000CH 0001H UNIT ?STACK
* * * * * * * C O D E M E M O R Y * * * * * * *
CODE 0000H 0003H ABSOLUTE
0003H 07FDH *** GAP ***
CODE 0800H 008CH UNIT ?C_C51STARTUP
CODE 088CH 0025H UNIT ?PR?_DELAY_MS?CODE
CODE 08B1H 0012H UNIT ?PR?MAIN?CODE
CODE 08C3H 0005H UNIT ?C_INITSEG
OVERLAY MAP OF MODULE: .\Objects\AMMETER (?C_STARTUP)
SEGMENT DATA_GROUP
+--> CALLED SEGMENT START LENGTH
----------------------------------------------
?C_C51STARTUP ----- -----
+--> ?PR?MAIN?CODE
+--> ?C_INITSEG
?PR?MAIN?CODE ----- -----
+--> ?PR?_DELAY_MS?CODE
?PR?_DELAY_MS?CODE 000AH 0002H
******************************************************************************
* RESTRICTED VERSION WITH 0800H BYTE CODE SIZE LIMIT; USED: 004AH BYTE ( 3%) *
******************************************************************************
Program Size: data=13.0 xdata=0 code=203
LINK/LOCATE RUN COMPLETE. 0 WARNING(S), 0 ERROR(S)
2 Answers 2
The problem is some adjustment you have in your IDE. I dare to say that Keil commonly does The Right ThingTM, so I assume you did something.
The data sheet documents that the MCU has only 2K of flash ROM, see chapter 7 on page 6. The address range is 0x0000 to 0x07FF.
The MCU starts execution at address 0. But the bytes of the provided binary at that address (02083D
) form an absolute jump to 0x083d. The data sheet says about such instructions:
Violating the physical space limits may cause unknown program behavior.
And
Again, violating the memory boundaries may cause erratic execution.
Please correct your project's parameters to match the address range of the MCU.
Your system probably just does what you told it to.
Your code does not contain a delay function:
// Define a delay function
void delay_ms(unsigned int milliseconds) {
unsigned int i, j;
for (i = 0; i < milliseconds; i++) {
for (j = 0; j < 1275; j++) { // Approximate delay, adjust based on clock frequency
// Delay loop
}
}
}
The job of your C compiler is the following:
- Read C code, make syntactical understanding (i.e., understand what a function is, convert statements like
i += 7
into "there's a variable i, and it gets a new value, and the new value is the result of the operation+
applied to the variable i and an immediate value, 7", ...) - model what the so-called (by the standard) "abstract C machine" would do based on that code, i.e., make a behavioural model of your code according to a well-defined theoretical computer. That asks the following questions:
- "given the rules of the C language, what is result calculated by each function?" (there's no result here)
- "given the memory model of the C language, and the memory locations outside of sole control of the function, what about the rest of the environment / memory does this function change, after it's run completely?" (there's no side effects / changes to memory here; and just counting up a memory value, for example, can be done by a single update, instead of 1275 steps, because the question is after it's run, not at every tiny step in the source code.)
- implement that behaviour by generating machine code for your target computer (here: the CPU of the AT89C2051).
Your loop doesn't actually calculate anything that becomes externally visible. Hence, the effect on the abstract C machine of calling delay_ms(1000)
would be the same as doing nothing. So, the compiler, correctly understanding your code says "oh this function calculates no value and has no side effects, let's just remove it". This is valid. This is how your compiler knows that unsigned int A = ...; A = A + A
is the same as A *= 2
is the same as A << 1
, and that it knows how to put into machine code.
So, your code really doesn't contain a delay, when compiled. (rich.g.william's delay loop code doesn't either, i = i
also has no effects and hence "compiles away", even without compiler optimizations; this is the typical "but I can trick the compiler into doing what I want" magical thinking that luckily has little to do with how C compilers or the C language work.)
The side-effect of "wasting cycles" is something you need to explicitly tell, in your C code, you want to preserve – there's no shortcut there. Any version of your delay loop that doesn't actually change anything every loop iteration is actually a null operation for the abstract C machine, and will get optimized away. And that is fine, because you want your C code in general to be correct and fast, and the rumor that C is "just high-level syntax for assembler" simply hasn't been true for maybe 30, 40 years.
You'll, in actual standard libraries for microcontrollers, find such wait loops often implemented in straight inline assembler. That actually gives you the freedom to "count cycles" (you don't know what, even if they do something, your for
loops get compiled to, it might change depending on e.g. how many registers the code surrounding them needs). In your case, try with an inline assembly NOP
where you just say // Delay Loop
.
-
\$\begingroup\$ I added _nop_(); in the delay loop and still no toggling. \$\endgroup\$Sidk– Sidk2025年06月11日 09:29:17 +00:00Commented Jun 11 at 9:29
-
\$\begingroup\$ I don't know what
_nop_()
is, but if it's not doing observable side effects, you've won nothing. I think the term "inline assembly" was clear enough. \$\endgroup\$Marcus Müller– Marcus Müller2025年06月11日 10:22:21 +00:00Commented Jun 11 at 10:22 -
\$\begingroup\$ Sidk, you must find assembler in the list file, there was nothing in there, not even a mention of P1 \$\endgroup\$user574606– user5746062025年06月11日 10:31:03 +00:00Commented Jun 11 at 10:31
-
\$\begingroup\$ Marcus my little bit of code wasn't meant to work just to give Sidk the idea of writing a tiny bit of code that's easy to find in the list file, and it compiles fine in VS 2022 \$\endgroup\$user574606– user5746062025年06月11日 10:33:58 +00:00Commented Jun 11 at 10:33
-
\$\begingroup\$ @Sidk Use your own C++ code it was fine, you are very close to finding out what's wrong now, just will be something very small wrong now \$\endgroup\$user574606– user5746062025年06月11日 10:40:00 +00:00Commented Jun 11 at 10:40
// Delay loop
? \$\endgroup\$