2
\$\begingroup\$

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.

  1. Compile above I/O toggling code in Keil with device AT89C2051 and clock 11.0592MHz selected.

  2. Generated hex file is loaded into superpro610p. During load buffer I tried both binary and intel format option in Superpro.

  3. Programmed MCU with loaded buffer.

  4. No toggle appears on any pin of MCU.

  5. Programmer Link

  6. HEX output file generated by Keil.

    :0300000002083DB6
    :0C083D00787FE4F6D8FD75810702082FD3
    :10080000900000AF82AE83E4FCFDFBFA7901F8D3DF
    :090810001208194003A380EB2239
    :0E082F00E4F5901208007590FF12080080F2A8
    :10081900EB9FF5F0EA9E42F0E99D42F0EC6480C856
    :0608290064809845F022F6
    :00000001FF
    
  7. 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)
    
  8. 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)
  1. 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)
asked Jun 10 at 14:38
\$\endgroup\$
38
  • \$\begingroup\$ you don't "burn hex files"; you load ("flash") a binary. "hex" is just short for "hexabinary" and that just is a method of printing numbers. \$\endgroup\$ Commented Jun 10 at 15:09
  • \$\begingroup\$ what is your actual code where you just say // Delay loop? \$\endgroup\$ Commented Jun 10 at 15:10
  • \$\begingroup\$ Thanks for response. Question is modified with description of procedure. \$\endgroup\$ Commented Jun 10 at 15:57
  • 1
    \$\begingroup\$ @MarcusMüller In the era of 8051s, the terminology really was to burn binaries into ROMs or Flash even. And hex file means a binary in Intel HEX format. To me, the terminology or what it means is valid, and understandable, even if I might not use the word burn, but I may use the words hex file daily to refer to the compiled binary. \$\endgroup\$ Commented Jun 10 at 16:56
  • 1
    \$\begingroup\$ As a next step, your programmer should be able to verify that the MCU's flash contains the binary you flashed before. Please check that. \$\endgroup\$ Commented Jun 11 at 5:44

2 Answers 2

2
\$\begingroup\$

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.

answered Jun 22 at 16:58
\$\endgroup\$
-2
\$\begingroup\$

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:

  1. 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", ...)
  2. 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:
    1. "given the rules of the C language, what is result calculated by each function?" (there's no result here)
    2. "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.)
  3. 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.

answered Jun 11 at 8:45
\$\endgroup\$
17
  • \$\begingroup\$ I added _nop_(); in the delay loop and still no toggling. \$\endgroup\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Jun 11 at 10:40

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.