51单片机的编程问题

51单片机的编程问题,第1张

1:C51编译器如何区分位地址和字节地址

是靠预定义实现的,比如:sfr P0 = 0x80; sbit P0_0 = 0x80;前者声明了P0端口地址位于0x80,后者说明了P0端口的bit0,即P00位于位地址空间0x80处。这2个0x80具有完全不同的含义,靠关键字sfr和sbit来区别。这样当程序被编译时,编译器会依此编译成相应的汇编语言。例如:

C51语句: P0 = 1;

P0声明为sfr,因此编译成:mov 80h,01h,将把0x01数据送入0x80单元,由于0x80单元物理上对应P0端口,因此,P00脚将输出高电平(其实是呈现高阻态,P0口独有的),其他1-7脚输出低电平。

C51语句: P0_0 = 1;

P0_0声明为sbit,因此编译成:setb 80h,这将把位地址空间的0x80地址的bit的值置1。这个位正是P0口的bit0,执行后,P00将输出高阻态。而P01-7不会变化。

2:C51为什么要嵌套汇编

51单片机一个显著优点就是指令执行时间固定,因此可以适应时序要求严格的场合。例如符合ISO7816协议的cpu卡的读写,对时序要求比较严格。其实就是用io脚做出来的同步半双工串口。支持cpu卡的程序一般比较庞大,需要用c51来组织,但是由于c编译的不确定性,必须把底层程序封装成汇编语言模块嵌入到工程中。这就带来几个问题:如何声明函数、参数如何传递等。限于篇幅,不能说得很细。下面举例:

汇编程序单独保存一个文件,加入到工程中,函数如下:

_proc_a:

mov a, r7

inc a

mov r7, a

ret

用c语言在h文件中声明: extern unsigned char proc_a(unsigned char val);

调用时形如: retvalue = proc_a(0x11);

说明:

a:汇编程序如果带参数,则需要在汇编程序前多加一个下划线。而声明它的地方不用加(伟福编译器这么要求的)。

b:函数的形参中第一参数用R7传递,函数返回值用R7返回,这是C51的通用规范。其他参数都有相应规定。函数可以返回一个位,用psw的c位返回。c:上面的语句,执行顺序是把0x11给R7,然后跳转子程序,子程序将它加1后送回。

d:函数跳转到汇编程序时,本区的R0-R7,A,B,PSW,DPTR等寄存器可以供子程序使用,不必考虑调用后是否要恢复这些常规资源。上例中,A的值被函数使用了,编程者不必恢复调用前的值。

(1);P33小键盘按下P13亮再按05秒灭。

org 0000h

ajmp main0

org 0080h

MAIN0:

CLR 21H ;清标志

main:

mov c,p33 ;检测按键

jc main

MOV R6,#200 ;设定延时

MOV R7,#200

TT0:

JNB 21H,TT2 ;检测灯状态

tt:

mov c,p33 ;检测按键

jnc tt1

JNB 20H,MAIN ;检测时间标志

CLR 20H

AJMP TT3

TT2:

mov c,p33 ;检测按键

jnc tt2

TT3:

CPL 21H ;改变灯状态

cpl p13

ajmp main

TT1: NOP ;延时但不是子程序

NOP

NOP

NOP

NOP

NOP

DJNZ R6,TT

MOV R6,#200

DJNZ R7,TT

MOV R7,#200

SETB 20H

AJMP TT

end

(2)数码管数据 p0,数码管控制p2 独立按键p1口 ,;对独立键进行按键次数计数,三位数码管显示。

;K1按下后,进行加1计数

;K2按下后,进行减1计数。

;K3按下后,进行加5计数。

;K4按下后,清计数单元,数码管显示0。

;按住键可以快速计数。

K1 BIT P14

K2 BIT P15

K3 BIT P16

K4 BIT P17

K_OLD EQU 30H

K_NEW EQU 31H

K_COUNT EQU 32H

DISSTART EQU 40H ;显示单元首地址

LED_DATA EQU P0 ;数码管数据口定义

;---------------------------------------------------------

ORG 0000H

JMP MAIN

ORG 0080H

;---------------------------------------------------------

MAIN:

MOV SP,#60H

MOV P1,#0FFH

MOV P0,#0FFH

MOV K_OLD,#00H

MOV K_COUNT,#00H

MAIN1:

CALL CONVT

CALL PLAY

CALL KEY_PROG

JMP MAIN1

KEY_PROG:

CALL K_SCAN ;键扫描

MOV A,K_NEW

CJNE A,K_OLD,KEY_P1

JMP KEY_P_END

KEY_P1:

MOV R4,#20

KEY_P2:

CALL CONVT ;用显示程序来进行键延时

CALL PLAY

DJNZ R4,KEY_P2

CALL K_SCAN ;再判断键是否按下

MOV A,K_NEW

CJNE A,K_OLD,KEY_P3

JMP KEY_P_END

KEY_P3:

JB ACC0,LOOP1 ;K1按下

JB ACC1,LOOP2 ;K2按下

JB ACC2,LOOP3 ;K3按下

JB ACC3,LOOP4 ;K4按下

JMP KEY_P_END

LOOP1:

INC K_COUNT ;键计数加1

JMP KEY_P_END

LOOP2:

DEC K_COUNT ;键计数减1

JMP KEY_P_END

LOOP3:

INC K_COUNT ;键计数加5

INC K_COUNT

INC K_COUNT

INC K_COUNT

INC K_COUNT

JMP KEY_P_END

LOOP4:

MOV K_COUNT,#00H ;键计数单元清零

KEY_P_END:

RET

;---------------------------------------------------------

;代码变换 (HEX TO BCD)

;---------------------------------------------------------

CONVT:

MOV A,K_COUNT

MOV B,#100

DIV AB

MOV DISSTART+2,A ;百位存放在DISSTART+2

MOV A,#10

XCH A,B

DIV AB

MOV DISSTART+1,A ;十位存放在DISSTART+1

MOV DISSTART,B ;个位存放在DISSTART

RET

;---------------------------------------------------------

; 键扫描子程序

;---------------------------------------------------------

K_SCAN:

MOV P1,#0FFH

MOV K_NEW,#00H

MOV A,P1

CPL A

ANL A,#0F0H

SWAP A

MOV K_NEW,A

RET

;---------------------------------------------------------

;延时子程序

;---------------------------------------------------------

DELAY:

MOV R6,#200

DEL:

MOV R7,#0FFH

DJNZ R7,$

DJNZ R6,DEL

RET

;---------------------------------------------------------

PLAY:

MOV R0,#DISSTART ;获得显示单元首地址

MOV R1,#07FH ;从第一个数码管开始

MOV R2,#03H ;共显示3位数码管

DISP1:

MOV A,@R0 ;获得当前位地址

MOV DPTR,#TAB_NU ;获得表头

MOVC A,@A+DPTR ;查表获得显示数据

MOV LED_DATA,A ;显示数据

MOV P2,R1 ;开始显示当前位

MOV A,R1 ;准备显示下一位

RR A

MOV R1,A ;下一位

INC R0 ;取下一个单元地址

LCALL DELAY2MS ;延时 2 MS

DJNZ R2,DISP1 ;重复显示下一个

MOV P2,#0FFH ;关闭显示

RET ;显示完成,返回

;---------------------------------------------------------

;延时子程序

;---------------------------------------------------------

DELAY2MS:

MOV R6,#10

DEL1:

MOV R7,#100

DJNZ R7,$

DJNZ R6,DEL1

RET

TAB_NU:

db 28h,7eh,0a2h,62h,74h,61h,21h,7ah,20h,60h ;字形代码表

;

END

两个例子我想够你明白的了。详细的解释,代表性的例子,可慢慢学习

相信很多初学者都有同样的疑问,也有同样的希望。就是用C++语言来写单片机程序的源代码。现在我来解释一下单片机的源代码程序几乎没有人用C++来写的。

1 假设一下你现在用C++写完了一个程序代码。那么你要让单片机认识必须要编译吧,对吧 。请问你有合适的编译软件来编译你的代码吗?

2 就算有编译器能编译,你能保证它的代码效率一定能给单片机用吗?

单片机的ROM是K级的水平,执行指令的速度也远远小于PC。绝对不能和PC机相比的。这个就决定了单片机编程时很多时候要考虑它的代码效率和代码大小问题。所以一般情况下对于单片机编程我们之用汇编语言或者C语言。就算是用C写程序也要注意代码的效率等问题。

还有就是只有很大型的项目及应用程序开发才有可能有C++来写程序。一般的系统文件,应用程序都是用C来写的。有的系统代码甚至要用汇编语言来写。

举个生活中的例子:如果你从家里去公司上班你一般的出行方式是什么?

1坐公车 2骑车 3自己开车 4坐地铁 5走过去。如果谁说他是做直升飞机去上班的。那要么这个人脑子不正常,要么就是太有钱了炫富。如果你要打苍蝇用苍蝇拍子就行了。如果要你用巡航导d来打苍蝇,你认为可能吗?

所以我们单片机写程序的话一般就是汇编语言和C语言。几乎没有人用C++语言的。

以下是使用51单片机(如STC89C52)和4个开关来控制数码管显示一位密码的简单示例代码。假设使用的是共阴数码管,并且数码管的引脚连接到了单片机的P0口。

#include <reg52h> // 引入单片机寄存器定义头文件// 数码管显示的密码模式,共16种,每种模式使用4位二进制表示unsigned char password[16] = { 0x3F, // 0b00111111, 模式0

0x06, // 0b00000110, 模式1

0x5B, // 0b01011011, 模式2

0x4F, // 0b01001111, 模式3

0x66, // 0b01100110, 模式4

0x6D, // 0b01101101, 模式5

0x7D, // 0b01111101, 模式6

0x07, // 0b00000111, 模式7

0x7F, // 0b01111111, 模式8

0x6F, // 0b01101111, 模式9

0x77, // 0b01110111, 模式10

0x7C, // 0b01111100, 模式11

0x39, // 0b00111001, 模式12

0x5E, // 0b01011110, 模式13

0x79, // 0b01111001, 模式14

0x71 // 0b01110001, 模式15};void main() { unsigned char i = 0; // 密码模式的索引

while (1) { // 读取四个开关的状态,每个开关对应一个二进制位

unsigned char switchValue = (P1 & 0x0F); // 根据开关状态选择密码模式

i = switchValue; // 设置数码管显示的密码模式

P0 = password[i]; // 简单延时

for (unsigned int j = 0; j < 1000; j++);

}

}

以上代码通过读取四个开关(连接到P1口的低4位)的状态来选择密码模式,并将选择的密码模式通过P0口设置到数码管上显示。

以上就是关于51单片机的编程问题全部的内容,包括:51单片机的编程问题、朋友们帮忙写个51单片机的按键小程序汇编的、用c++怎样编写51单片机程序等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zz/9304467.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-27
下一篇2023-04-27

发表评论

登录后才能评论

评论列表(0条)

    保存