片机课件之MCS-51单片机汇编语言程序设计.ppt
第4章 MCS-51单片机汇编语言程序设计,汇编语言具有高效、快捷等特点,在中小规模应用软件中广泛应用。了解常用伪指令的格式、功能及使用方法,掌握顺序程序、分支程序、循环程序、子程序的特点及编写方法是汇编语言程序设计的基础。顺序程序的执行次序就是按语句的先后次序执行,应避免用顺序程序实现大量重复操作;分支程序中有比较判断环节,应注意逻辑正确性;循环程序中有一部分要反复执行多次,应注意避免死循环;子程序是能完成某一特定功能的程序模块,应具有通用性。通常都将常用功能模块编写成子程序形式,以提高程序的可读性和执行效率。,4.1 概述,程序是完成某一特定任务的若干指令的有序集合。编写程序,就要熟悉计算机语言。目前计算机语言种类繁多,性能各异,大致可分为三类:机器语言、汇编语言和高级语言。汇编语言也是一种面向机器的程序设计语言,它用英文字符来代替对应的机器语言。例如用ADD代替机器语言中的加法运算,这些英文字符被称为助记符。计算机高级语言是一种面向算法、过程和对象的程序设计语言,它采用更接近人们习惯的自然语言和数学语言描述算法、过程和对象,如BASIC,C,JAVA等都是常用的高级语言。,4.1.2 汇编语言语句种类及格式,汇编语言语句有三种基本类型:指令语句、伪指令语句和宏指令语句。指令语句:每一个指令语句都在汇编时产生一个目标代码,对应着机器的一种操作。【例】:MOV A,05H;其目标代码为74H 05H。伪指令语句:伪指令语句是一种说明语句,主要是为汇编程序服务的,在汇编时没有目标代码与之对应,没有对应的机器操作。【例】:ONE EQU 1;其功能为定义ONE的值为1宏指令语句:用以代替汇编语言源程序中重复使用的程序段而设置的一种语句,由汇编程序在汇编时产生相应的目标代码。,4.1.3 常用伪指令,为了便于编程和对汇编语言程序进行汇编,各种汇编语言都提供一些特殊的伪指令,由伪指令确定的操作称为伪操作。1起始地址说明 格式:ORG nn功能:用于汇编语言源程序或数据块开头,指出程序段或数据块存储的起始地址。nn表示16位地址。【例】:ORG 1000HMAIN:MOV DPTR,#2000H ORG伪指令规定了程序段MAIN的起始地址为1000H。,2汇编结束说明格式:END 功能:用于汇编语言源程序末尾,指示源程序到此全部结束。在汇编时,对END后面的指令不予汇编。因此,END语句必须放在整个程序的末尾,有且只能有一个。,3赋值格式:字符名 EQU 数据或汇编符号 功能:将一个数(8位或16位)或汇编符号赋值给所定义的字符名。EQU伪指令中的字符名必须先赋值后使用,故该语句通常放在源程序的开头。【例】:ORG 0000H CH1 EQU 50H CH2 EQU R4 MOV A,CHl;A(50H),相当于MOV A,50H MOV A,CH2;AR4,相当于MOV A,R4,4定义字节格式:标号:DB nl,n2,nn功能:用于定义8位数据的存放地址。表示把指令右边的单字节数据依次存放到以左边标号为起始地址的连续存储单元中,通常用于定义数据表,程序中使用查表指令将数据取出。【例】:ORG 1000HMAIN:MOV DPTR,TAB;取表头地址 MOV A,R2;R2中存放查表指针 MOVC A,ADPTR;从表中取出数据TAB:DB 7FH,6FH,77H,7CH,39H,5EH,79H,61H END,5定义字格式为:标号:DW nnl,nn2,nnN功能:用于定义16位数据的存放地址。DW指令与DB指令相似,都是在内存的某个区域内定义数据,不同的是DW指令定义的是字,而DB指令定义的是字节。DW指令表示把指令右边的双字节数据依次存入指定的连续存储单元中。常用于定义一个地址表。,6位地址赋值格式:字符名称 BIT 位地址功能:该指令把BIT右边的位地址赋给左边的字符名称。被定义的位地址在源程序中可用符号名称来表示。也可以用EQU指令来定义位地址变量。【例】:ORG 1000HL0BIT P1.1 Ll BIT 20H,7定义存储区格式:标号:DS X功能:用于定义从标号开始预留一定数量的内存单元,以备源程序执行过程中使用。预留单元的数量由X决定。【例】:ORG 1000H CDS:DS 08HMAIN:MOV DPTR,1000H END程序汇编到DS语句时,从1000H地址开始预留8个连续地址单元,后面内容从1008H地址开始依次存放。,4.1.4 汇编语言程序设计方法,汇编语言程序设计同高级语言程序设计一样,是有章可循的,只要按照一定的方法步骤去做,程序设计就会变成一件轻松愉快的事情,设计的程序也会规范、清晰、易读、易懂。使用汇编语言设计程序大致上可分为以下几个步骤。1.分析题意,明确要求。2.确定算法。3.画程序流程图。4.分配内存工作单元。5.编写源程序。6.程序优化。7.上机调试。,4.2 顺序程序设计,【例】程序初始化。初始化就是为变量、寄存器、存储单元赋一初值,是最简单、最常用的操作。如将R0-R3,P1,30H,40H单元初始化为00H,把R4,R5初始化为0FFH。参考程序如下:ORG 0000H;PC起始地址 LJMP START;转主程序 ORG 0100H;主程序起始地址START:MOV R0,#00H;初始化 MOV R1,#00H MOV R2,#00H MOV R3,#00H MOV P1,#00H,MOV R4,#0FFH MOV R5,#0FFH MOV 30H,#00H MOV 40H,#00HHERE:SJMP HERE;反复执行该指令,相当于等待 END用立即数比较直观,但用MOV A,#00H,MOV R0,A 指令赋值,效果更好。,【例】逻辑运算。逻辑操作是控制过程中经常使用的,掌握逻辑运算的特点是提高程序效率的重要途径。在逻辑运算中,进位标标志CY的地位很特殊,它是逻辑累加器,大多数逻辑操作要通过CY来完成。用程序实现图4-2所示的逻辑电路功能。,图4-2 逻辑电路,参考程序如下:ORG 0000H LJMP START ORG 0100H MOV P1,#0FFH;P1口初始化LOOP:MOV C,P1.1 ORL C,P1.2;P1.1与P1.2逻辑或运算 CPL C;取反 ANL C,P1.0;C与P1.0逻辑与运算 CPL C MOV 07H,C;暂存于07H单元中 MOV C,P1.3 ANL C,/P1.4;P1.3与P1.4的反逻辑与运算 CPL C ORL C,07H MOV P1.5,C;把结果在P1.5口输出 SJMP$END,4.3 分支程序设计,分支程序的主要特点是程序包含有判断环节,不同的条件对应不同的执行路径。编程的关键任务是合理选用具有逻辑判断功能的指令。由于选择结构程序的走向不再是单一的,因此,在程序设计时,应该借助程序框图(判断框)来明确程序的走向,避免犯逻辑错误。一般情况下,每个选择分支均需单独一段程序,并有特定的名字,以便当条件满足时实现转移。1单分支选择结构当程序的判断是二选一时,称为单分支选择结构。通常用条件转移指令实现判断及转移。单分支选择结构有三种典型表现形式。,图4-3 单分支选择结构,(a)当条件满足时执行分支程序1,否则执行分支程序2。(b)当条件满足时跳过程序段1,从程序段2顺序执行;否则,顺序执行程序段1和程序段2。(c)当条件满足时程序顺序执行程序段2;否则,重复执行程序段1,直到条件满足为止。由于条件转移指令均属相对寻址方式,其相对偏移量rel是个带符号的8位二进制数,可正可负。因此,它可向高地址方向转移,也可向低地址方向转移。对于第三种形式,可用程序段1重复执行的次数作为判断条件,当重复次数达到某一数值时,停止重复,程序顺序往下执行。这是分支结构的一种特殊情况,这实际是循环结构程序。用这种方式可方便实现状态检测。【例】:LOOP:JB P1.1,LOOP单分支程序一般要使用状态标志,应注意标志位的建立。,【例】设a存放在累加器A中,b存放在寄存器B中,若a0,Y=ab;若a0,则Y=ab。这里的关键是判a是正数,还是负数;可通过判断ACC.7确定。ORG 0000HJMP BRORG 0100HBR:JB ACC.7,MINUS;负数,转到MINUSCLR C;清进位位SUBB A,B;A-BSJMP DONEMINUS:ADD A,B;A+BDONE:SJMP$;等待 END,2多分支选择结构当程序的判别输出有两个以上的出口流向时,称为多分支选择结构。8051的多分支结构程序还允许嵌套,即分支程序中又有另一个分支程序。汇编语言本身并不限制这种嵌套的层次数,但过多的嵌套层次将使程序的结构变得十分复杂和臃肿,以致造成逻辑上的混乱。多分支选择结构通常有两种形式,如图4-4所示。,图4-4 多分支选择结构,8051的散转指令和比较指令均可以实现多分支转移。散转指令 JMP A+DPTR 比较指令 CJNE A,direct,rel(共有4条)使用散转指令前,先将各分支程序编写好,存放在程序存储器中,并将各分支程序的入口地址组成一个表格放在一起,把表首地址送入DPTR,把子程序的序号放入A中。在8051指令中,还有4条功能极强的比较转移指令:CJNE A,direct,rel;A内容与直接寻址单元内容比较,不等转移CJNE A,#data,rel;A内容与立即数比较,不等转移CJNE Rn,#data,rel;寄存器内容与立即数比较,不等转移CJNE Ri,#data,rel;间址单元内容与立即数比较,不等转移这4条指令对指定单元内容进行比较,当不相等时程序作相对转移,并指出其大小,以备作第二次判断;若两者相等,则程序顺序执行。,【例】散转程序。编写程序,根据20H单元中的内容转入相应的分支,执行指定的操作,将结果存入指定存储器单元。程序流程框图如图4-5所示,图4-5 散转程序流程,参考程序 ORG 0000H LJMP MEMS RESULT EQU 0050H ORG 0100HMEMS:MOV A,20H MOV DPTR,#KKKK;散转程序入口地址表首 址 RL A;分支号乘2,每个入口地址均为2字节 JMP A+DPTR;转移END1:SJMP$KKKK:AJMP MEMSP0;A=0加法 AJMP MEMSP1;A=1减法 AJMP MEMSP2;A=2乘法 SJMP MEMSP3;A=3除法 SJMP MEMSP4;A=4逻辑与 SJMP MEMSP5;A=5逻辑或,MEMSP0:MOV A,R0;相加分支 CLR C ADD A,R1 MOV RESULT,A LJMP END1MEMSP1:MOV A,R0;相减分支 CLR C SUBB A,R1 MOV RESULT,A LJMP END1MEMSP2:MOV A,R0;乘法分支 MOV B,R1 CLR C MUL AB MOV RESULT,A MOV RESULT+1,B LJMP END1,MEMSP3:MOV A,R0;除法分支 MOV B,R1 CLR C DIV AB MOV RESULT,A MOV RESULT+1,B LJMP END1MEMSP4:MOV A,R0;逻辑与分支 ANL A,R1 MOV RESULT,A LJMP END1MEMSP5:MOV A,R0;逻辑或分支 ORL A,R1 MOV RESULT,A LJMP END1 END,【例】两个无符号数比较大小。设外部RAM单元ST1和ST2中存放两个无符号二进制数,要找出其中的大数存入ST3单元中。程序流程框图如图4-6所示。,图4-6 求大数程序流程,参考程序如下:ORG 0000H LJMP START ORG 0100HSTART:MOV A,addr1;将addr1中内容送A CJNE A,addr2,LOOP1;两数比较,不相等则 转LOOP1LOOP3:AJMP$;结束LOOP1:JC LOOP2;当CY1,转LOOP2 MOV addr3,A;CY0,(A)(addr2)SJMP LOOP3;转结束 LOOP2:MOV addr3,addr2;CY1,(addr2)(A)SJMP LOOP3 END,4.4 循环程序设计,在实际应用中经常会遇到功能相同,需要多次重复执行某段程序的情况,这时可把这段程序设计成循环结构,这有助于节省程序的存储空间,提高程序的质量。循环程序一般由4部分组成。1.初始化。即设置循环过程中有关工作单元的初始值,如置循环次数、地址指针及工作单元清零等。2.循环体。即循环处理部分,完成主要的计算或操作任务,是重复执行的程序段。3.循环控制。每循环一次,就要修改循环次数、数据及地址指针等循环变量。并根据循环结束条件,判断是否结束循环。4.循环结束处理。对结果进行分析、处理、保存。,循环程序结构有两种,如图4-7所示。,图4-7 循环程序结构,图(a)是“先执行后判断”结构,适用于循环次数已知的情况。其特点是进入循环后,先执行循环处理部分,然后根据循环次数判断是否结束循环。图(b)是“先判断后执行”结构,适用于循环次数未知的情况。其特点是将循环控制部分放在循环的入口处,先根据循环控制条件判断是否结束循环,若不结束,则执行循环操作;若结束,则退出循环。,【例】50 ms软件延时程序。软件延时程序一般都是由DJNZ Rn,rel指令构成。执行一条DJNZ指令需要两个机器周期。软件延时程序的延时时间主要与机器周期和延时程序中的循环次数有关,在使用12 MHz晶振时,一个机器周期为1s,执行一条DJNZ指令需要两个机器周期,即2s。适当设置循环次数,即可实现延时功能。参考程序如下:ORG 0000HLJMP MEMSORG 0100HMEMS:DEL:MOV R7,#125;外循环次数,该指令为一个机器周期DEL1:MOV R6,#200;内循环次数DEL2:DJNZ R6,DEL2;2002400s(内循环时间)DJNZ R7,DEL1;0.4 ms12550 ms(外循环时间)SJMP$END,【例】排序程序。设在内部RAM中存一无符号数的数组,其长度为n,起始地址是30H,要求将它们按从大到小排序,排序后仍存放在原区域中。按“冒泡法”对n个数排序时,可能用不到n-1次循环,排序就结束了。为了提高排序速度,程序中可设一交换标志位,如10H位,每次循环中,若有交换则执行SETB 10H,表明排序未完成;若无交换,则执行CLR 10H,表明排序已经完成。每次循环结束时,测10H位,判断排序是否结束。参考程序如下:ORG 0000H LJMP BUBBLE ORG 0100HBUBBLE:MOV R0,#30H MOV B,#64H CLR 10H DEC B;长度计数,LOOP:MOV A,R0;内循环的入口 MOV 20H,A;暂存,为交换作准备 INC R0 MOV 21H,R0 CJNE A,21H,BUEU;若(20H)(21H)转移 BUEU:JNC BUNEXT;(20H)(21H)转移 MOV A,R0;若(20H)(21H)则交换 MOV R0,20H DEC R0;使R0退格指向小地址 MOV R0,A INC R0;恢复R0指向大地址 SETB 10H;置交换标志BUNEXT:DJNZ B,LOOP;内循环是否结束的判断 JB 10H,BUBBLE;判断标志位为1否?外循环结束的判断 END,4.5 子程序设计,在实际应用中,一些特定的运算或操作经常使用,例如多字节的加、减、乘、除处理,代码转换、字符处理等。如果每次遇到这些运算或操作,都重复编写程序,不仅会使程序烦琐冗长,而且也会浪费编程者大量时间。因此经常把这些功能模块按一定结构编写成固定的程序段,存放在内存中,当需要时,调用这些程序段。通常将这种能够完成一定功能、可以被其它程序调用的程序段称为子程序。调用子程序的程序称为主程序或调用程序。调用子程序的过程,称为子程序调用,用ACALL addr11和LCALL addr16两条指令完成。子程序执行完后返回主程序的过程称为子程序返回,用RET指令完成。,1.在编写子程序时要注意以下几点:要给每个子程序赋一个名字。它是子程序入口地址的符号,便于调用。明确入口参数、出口参数。所谓入口参数,即调用该子程序时应给哪些变量传递数值,放在哪个寄存器或哪个内存单元,通常称为参数传递。出口参数则表明了子程序执行的结果存在何处。例如,调用开平方子程序,计算。在调用子程序之前,必须先将x值送到主程序与子程序的某一交接处N(如累加器A),调用子程序后,子程序从该交接处取得被开方数,并进行开方计算,求出的值。在返回主程序之前,子程序还必须把计算结果送到另一交接处M。这样在返回主程序之后,主程序才可能从交接处M得到的值。注意保护现场和恢复现场。在执行子程序时,可能要使用累加器、PSW或某些工作寄存器,而在调用子程序之前,这些寄存器中可能存放有主程序的中间结果,这些中间结果在主程序中仍然有用,这就要求在子程序使用这些资源之前,要将其中的内容保护起来,即保护现场。当子程序执行完毕,即将返回主程序之前,再将这些内容取出,恢复到原来的寄存器,这一过程称为恢复现场。,保护现场通常用堆栈来完成。并在子程序的开始部分使用压栈指令PUSH,把需要保护的寄存器内容压入堆栈。当子程序执行结束,在返回指令RET前边使用弹栈指令POP,把堆栈中保护的内容弹出到原来的寄存器。要注意,由于堆栈操作是“先入后出”。因此,先压入堆栈的参数应该后弹出,才能保证恢复原来的数据。为了做到子程序有一定的通用性,子程序中的操作对象,尽量用地址或寄存器形式,而不用立即数、绝对地址形式。另外,子程序中如含有转移指令,应尽量用相对转移指令。2.子程序的调用与返回 主程序调用子程序是通过子程序调用指令LCALL add16和ACALL add11来实现的。前者称为长调用指令,指令的操作数部分给出了子程序的16位入口地址;后者为绝对调用指令,它的操作数提供了子程序的11位入口地位,此地址与程序计数器PC的高5位并在一起,构成16位的调用地址(即子程序入口地址)。它们的功能,首先是将PC中的当前值(调用指令下一条指令地址,称断点地址)压入堆栈(即保护断点),然后将子程序入口地址送入PC,使程序转入子程序运行。,子程序的返回是通过返回指令RET实现的。这条指令的功能是将堆栈中返回地址(即断点)弹出堆栈,送回到PC,使程序返回到主程序断点处继续往下执行。子程序调用过程如图4-8所示,图4-8 子程序调用过程,主程序在调用子程序时要注意以下问题。在主程序中,要安排相应指令来传递子程序的入口参数,即提供子程序的入口数据。在主程序中,要安排相应的指令,处理子程序提供的出口数据,即操作结果。在主程序中,不希望被子程序更改内容的寄存器,也可以在调用前由主程序安排压栈指令来保护现场,子程序返回后再安排弹栈指令恢复现场。在主程序中,要正确地设置堆栈指针。,3.子程序嵌套子程序嵌套是指在子程序执行过程中,还可以调用另一个子程序。子程序嵌套过程如图4-9所示。,图4-9 子程序嵌套过程,4.子程序的特性编写子程序应注意以下问题:通用性。为使子程序能适应各种不同程序、不同条件的调用,子程序应具有较好的通用性。可浮动性。可浮动性是指子程序段可设置在存储器的任何地址区域。假如子程序只能设置在固定的存储器地址段,这在编制主程序时要特别注意存储器地址空间的分配,防止两者重叠。为了能使子程序段浮动,必须在子程序中避免选用绝对转移地址,而应选用相对转移类指令,子程序首地址亦应采用符号地址。可递归和可重入性。子程序能自己调用自己的性质,称为子程序的可递归性,而子程序能同时被多个任务(或多个用户程序)调用的性质,称为子程序的可重入性。这在比较复杂的程序中经常用到。子程序说明文件。对于通用子程序,为便于各种用户选用,要求在子程序编制完成后提供一个说明文件,使用户不必详读源程序,只需阅读说明文件就能了解子程序的功能及应用。,【例】求平方。用程序实现 Ca2b2。设a、b均小于10,a存在31H单元,b存在32H单元,把C存入33H单元。因本题两次用到平方值,所以在程序中采用把求平方编为子程序的方法。子程序名称:SQR。功能:求X2,通过查平方表来获得。入口参数:某数在A中。出口参数:某数的平方在A中。参考主程序和子程序如下:主程序:ORG 0000HLJMP MAINORG 0100H MAIN:MOV SP,#3FH;设堆栈指针(调用和返回指令要用到堆栈)MOV A,31H;取a值LCALL SQR;第一次调用,求a2MOV R1,A;a2 值暂存R1中 MOV A,32H;取b 值LCALL SQR;第二次调用,求b2 ADD A,R1;完成 a2b2MOV 33H,A;存结果到33HSJMP$;暂停,子程序:ORG 0200HSQR:ADD A,#01H;查表位置调整,RET为一字节指令MOVC A,A+PC;查表取平方值RET;子程序返回TAB:DB 0,1,4,9,16,25DB 36,49,64,81 END求平方的子程序在此采用的是查表法,用伪指令DB将09的平方值以表格的形式定义到ROM中。子程序中A之所以要加1,是因为RET指令占了一个字节。,4.6 常用程序举例,代码转换【例】十六进制数转换为ASCII码。设十六进制数存在A中。数字09的ASCII码分别是30H39H;英文大写字母AF的ASCII码分别是41H46H。可见数字09的ASCII码值与数字值相差30H,字母AF的ASCII码值与其数值相差37H。转换过程中,先判断十六进制数是09还是AF,再将相应差值补上即可。参考程序如下:ORG 0000HLJMP STARTORG 2000HSTART:MOV R2,A;将待转换的十六进制数暂存于R2ADD A,#0F6H;将待转换的十六进制数加246,检查有无进位来判别它是否大于等于10MOV A,R2;原十六进制数送到AJNC AD30;如无进位,加30HADD A,#37H;有进位,加37HAD30:ADD A,#30HSJMP$END,2查表程序实际应用中,线性表是一种常用的数据结构。查表就是根据变量X,在表格中查找对应的Y值,使Y=F(X)。在8051指令集中,设有两条查表指令:MOVC A,ADPTRMOVC A,APC【例】BCD码转换为ASCII码。BCD码只有00001001十个代码,对应的ASCII码为30H39H。它们之间有固定的对应关系,可用计算法,也可用查表法进行转换。设BCD码存放在A中,ASCII码表格存于首地址为TAB的存储器中。转换程序如下:ORG 0100HTRANS1:MOV DPTR,#TAB;将ASCII码表首地址置入DPTR MOVC A,A+DPTR;查表得对应的ASCII码 SJMP$TAB:DB 30H,31H,32H,33H,34H,35H,36H,37H,38H,39H,3.数据运算【例】多字节无符号数加法。设被加数与加数分别在以ADR1与ADR2为首址的片内数据存储器区域中,自低字节起,由低到高依次存放;它们的字节数为L,要求加得的和放回被加数的单元。流程框图如图4-10所示。,图4-10 多字节无符号数加法流程图,参考程序如下:ORG 0000HLJMP STARTORG 0100HSTART:MOV R0,#ADR1 MOV R1,#ADR2 MOV R2,#L CLR CLOOP:MOV A,R0;通过R0间址,取得被加数的一个字节 ADDC A,R1;加数与被加数相加 MOV R0,A;保存结果INC R0;修改指针,指向下一个相加的数INC R1DJNZ R2,LOOP;循环实现多字节数相加SJMP$END,4.I/O口操作【例】统计自P1口输入的字串中正数、负数、零的个数。设R0、R1、R2三个工作寄存器分别为统计正数、负数、零的个数的计数器。程序流程框图如图4-11所示。图4-11 统计程序流程图,图4-11 统计程序流程图,参考程序如下:ORG 0000HLJMP STARTORG 0100HSTART:CLR A MOV R0,A MOV R1,A MOV R2,A MOVX P1,#0FFH;P1口置输入状态ENTER:MOV A,P1;自P1口取一个数 JZ ZERO;该数为0,转ZERO JB P1.7,NEG;该数为负,转NEG INC R0;该数不为0、不为负,则必为正数,R0内容加1 SJMP ENTER;循环自P1口取数ZERO:INC R2;零计数器加1 SJMP ENTERNEG:INC R1;负数计数器加1 SJMP ENTER END,【例】编制一个循环闪烁灯程序。设8051单片机的P1口作为输出口,经驱动电路74LS240(8反相三态缓冲/驱动器)接8只发光二极管,如图4-12所示。当输出位为“1”时,发光二极管点亮,输出位为“0”时为暗。试编程实现:每个灯闪烁点亮10次,再转移到下一个灯闪烁点亮10次,循环不止。,图4-12 循环闪烁灯电路,参考程序如下:ORG 0000HLJMP FLASHORG 0100HCLR CFLASH:MOV A,#01H;置灯亮初值FSH0:MOV R2,#0AH;置闪烁次数FLOP:MOV P1,A;点亮LCALL DY1s;延时1sMOV P1,#00H;熄灭LCALL DY1s;延时1s DJNZ R2,FLOP;闪烁10次 RLC A;左移一位 SJMP FSH0;循环END,实 践题目:汽车方向灯控制系统内容:汽车在行驶时,有左转弯、右转弯、刹车、警示、停靠等操作。左转弯时,应使左头灯、左尾灯、仪表板左转指示灯闪烁。右转弯时,应使右头灯、右尾灯、仪表板右转指示灯闪烁。刹车时,左右两个尾灯亮。警示时,头灯、尾灯、仪表板指示灯等6个灯全闪烁。汽车停靠时,左右头灯、左右尾灯高频闪烁。要求:用按键开关代表汽车的相关操作,以LED发光二极管代替汽车灯。高频闪烁频率为30Hz,其它闪烁频率为1Hz。硬件:画出硬件电路原理图。软件:画出程序流程图,编写源程序。调试:记录调试过程,分析调试结果。,