wlo 发表于 2024-1-17 15:19:55

第七课
文件包含命令INCLUDE---用于将寄存器定义文件包含于当前程序中,与C语言中的#include语
句的作用类似。使用格式为:
$INCLUDE (文件名)
开发STC8H8K64U单片机的汇编语言程序时,可以使用STC提供的STC8H.H文件。使用时在程序的开始处写上下而的命令:
$NOMOD51------$INCLUDE(STC8H.H)
其中,$NOMOD51告诉汇编器不使用预定义的寄存器名。因为汇编器内部定义了8051的寄存器名,如果没有使用$NOMOD51,后面包含了STC8H.H的头文件,汇编时,就会出现寄存器符号重复定义的错误。使用上述命令后,在用户程序中就可以使用STC8H8K64U单片机的特殊寄存器名称了。
1.助记符语言描述
机器语言 (Machine Language) 是用二进制数表示的指令,是CPU唯一能够直接识别和执行的程序形式
例如:248B
机器语言的缺点是不直观,不易识别、理解和记忆,因此编写、调试程序时都不采用这种形式的语言。为了解决这个问题,引入了助记符.
汇编语言----的指令通常由操作码和操作数组成。操作码指出的是要对操作数进行什么操作。操作数指出的是对什么数进行操作以及将操作的结果放到何处
为便于阅读和记忆,操作码用规定的缩写英文字母组成,称为助记符。例如   MOV是数据的传送,ADD是数据的相加运算,ANL是数据的逻辑与运算等。
操作码--------可以查看对应官网的手册结合起来看,比较通俗易懂

操作数
操作数是一条指令操作的对象。操作数可以是数据,也可以是地址。不同功能的指令,操作对象形式不同
不同指令的操作数特点如下:
1.传送类指令,必须指明操作对象从哪儿来 (源地址),传到何处去(称为目的地址)
2.数据操作类指令,一般靠运算器完成,数据操作类指令的对象一般是两个
3.程序控制类指令的操作对象是程序计数器PC和一个数。
4.逻辑操作类有单操作数和双操作数之分。
注意:
数据只能是整数,不能是小数
当汇编指令中的数据是十六进制且是以字母开头时该数据盛加一个前导0,以表示后面的字母不是变量而是数字。
在8051内核单片机中,一个数据的前面有前缀#号则表示后面的据是立即数,如果数据的前面没有#号,则说明该数据表示的是直接地址。
立即数可以写成十进制的格式,也可以写成十六进制数的格式,还可以写成二进制数的格式。
其区分是在数据的后面加上后缀以示区别: 十进制数据的后缀为D(可省略不写),十六进制数据的后缀为H,二进制数据的后缀为B。
汇编语言的概念及格式
用助记符来描述机器指令的语言称为符号语言或汇编语言,汇编语言一般格式如下:
[标号:]操作码助记符 [第一操作数][,第二操作数][,第三操作数][; 注释
其中,带有中括号的内容是可以省略的内容或者指令格式中不需要的内容.
标号后面有一个冒号“:”,它实质上是标号所在行的指令的地址,根据程序设计的需要设置。标号是由字母开头的有意义的字符串组成,例如:
LOOP LOOP1 TABLE
另外,在汇编语言程序中,标号也用作子程序的名字
汇编语言中的操作码助记符表示了指令的功能,操作数表示指令操作的对象
操作数:根据指令的语法要求,一条指令中,可能有0~3个操作数。例如:
RETI         ;中断返回,无操作数
CPL      A      ;累加器逐位取反,只有一个操作数
ADD    A,#56H    ;两个操作数的情况------寄存器A与立即数 相加
CINE       R2,#60H,LOOP      ;三个操作数的情况
注释
注释是为了阅读程序方便由编程人员加上的,并不影响程序的执行和功能,所以,注释部分不是必需的。
注释部分必须用分号“;"或“//”开头,也可以使用“/.../”的形式注释某一段内容。
一般都写在它所注释的指令的后面或者某段程序的开始,注释本身只用于对指令功能加以说明,使阅读程序时便于理解,
所以注释可以用中文或者英文甚至任何便于理解的字符表示
指令代码的存储格式
指令代码是汇编语言中指令的二进制表示方法,是指令在存储器中存放的形在机器中为了方便表示,将8位二进制码表示为一个字节
8051内核的单片机的指令长度有单字节、双字节、三字节三种,在存储器中分别占有1~3个单元。其格式如下:
单字节指令:       RET   ;机器代码: 22H
双字节指令:       MOV A,#0FH   ;机器代码: 74H OFH
三字节指令:       MOV 74H,#OBH    ;机器代码: 75H 74H OBH
例如,程序中有如下两条连续存放的指令:
MOV A,#68H   ;对应的机器码是: 74H和68H
MOV B,#73H   ;对应的机器码是75H、FOH和73H
假设这两条指令存放的起始地址是1000H,则存放格式为:


操作数是指令的重要组成部分,它指定了参与运算的数或数所在的单元地址,而如何得到这个地址就称为寻址方式。
8051内核的单片机共有7种寻址方式,描述如下:
1.立即寻址
指令中的源操作数是立即数,这种寻址方式叫做立即寻址。立即数的类型可以是数字也可以是字符,一般字长为8位或16位
例       MOV A,#28H   ;把十六进制的立即数28H送入到累加器A中,机器码: 74H 28H
MOV DPTR#0050H   ;把十六进制的16位立即数0050H送入到DPTR中

2.寄存器寻址
指定寄存器的内容为操作数,对寄存器ACC、B、DPTR和CY(进位标志,也是布尔处理机的累加器》寻址时,具体的寄存器已隐含在其操作码中。
而对选定的8个工作寄存器R7~R0,则用指今操作码的低3位指明所用的寄存器
例 INC R5         ;把寄存器R5的内容加1后再送回R5
3.直接寻址
直接寻址就是在指令中包含了操作数的地址,即在指令中直接包含了参加运算或传送的单元或
的地址。直接寻址可访问三种地址空间:
特殊功能寄存器SFR: 直接寻址是唯一的访问形式
内部数据RAM中的00H~7FH的128个字节单元。
位地址空间。
例    MOV A,45H   ;把45H单元的内容送入累加器A中      直接寄存器操作的是寄存器,就是寄存器的地址

-----------------间接寻找,方便批量操作处理---------------
4.寄存器间接寻址
指令指定某一寄存器的内容作为操作数地址。可用来间接寻址的寄存器有:
选定工作寄存器区的R0、R1、堆栈指针SP或者16位的数据指针DPTR,
使用时前面加@表示间接寻址。
例 MOV A@R0       ;将R0中的内容所表示的地址单元中的内容送给A
MOVX @DPTP.A   ;将A中的内容送到DPTR指向的外部RAM单元中

5.变址寻址       PC变地寻址不常用
由指令指定的偏移量寄存器和基址寄存器DPTR或PC相加所得结果作为操作数地址。
第一类用PC作基地址寄存器加上累加器A的内容形成操作数的地址A+PC。
例 MOVC A,@A+PC    ;读取A+PC指向的程序存储器单元的值送给A
其中A作为偏移量寄存器(称为变址寄存器),PC作为基址寄存器,A中内容为无符号数和PC
相加,从而得到其真正的操作数地址。例如:


6.相对寻址
该寻址方式主要用于相对跳转指令。把指令中给定的地址偏移量与本指令所在单元地址(即程
序计数器PC中的内容)相加,得到真正的程序转移地址。与变址方式不同,该偏移量有正、负号,在
该机器指令中必须以补码形式给出,所转移的范围为相对于当前PC值的-128~+127之间。

JC跳转范围有限,-128~+127    可以多次跳转
7.位寻址
支持位单元存取操作是8051内核单片机的一个主要特点。位操作指令可以对位地址空间的每
位进行运算和传送操作。
例如      Mov   C,P10   将P1.0的状态传送到进位标志CY
SETB      20H.6;将20H单元的第6位置为1
CLR 25H;将25H位的内容清零
比较
Mov   A,60H   寄存器A是表示8位的数据,结果是60H中的值
MoVC,60H    寄存器C是表示某个位,结果只会是0或者1

wlo 发表于 2024-1-19 13:53:03

第八课
数据传送类指令简介         相当于 C语言=
数据传送类指令是使用频率最高的一类指令。    主要用来给8051单片机系统的内部和外部资源赋值、进行堆栈的存取操作等。数据
传送类指令执行前后,对程序状态字PSW一般不产生影响。按其操作方式,又可把它们分为三种.
数据传送
数据交换
栈操作。
传送指令一-MOV
Mov指令的作用区间主要是内部数据存储器和特殊功能寄存器。格式为 Mov <目的字节>,源字节:
把第二操作数指定的字节变量传送到由第一操作数指定的单元中,源字节内容不变,一般不影响别的寄存器或标志。PSW
(1) 立即数送累加器A和Rn、内部RAM、SFR。共有4条指令

@符号表示间接寻址,Ri中i=0或i=1
    MOV 56H,#21H
    MOV A,#30H
         ;MOV A,#30H
      ;MOV R0,56H
      ;MOV @R0,#56H
    MOV R0,56H
         MOV R0,#60H
      MOV @R0,#56H
    MOV P0,#80H

间接寻址@Ri是以Ri的内容作为地址进行寻址,由于Ri为8位寄存器,所以其寻址范围可为
00H~FFH。例如,指令序列:
MOV R1,#82H
MOV A,@R1
上述指令组合对于STC8H8K64U单片机而言,其功能是将内部RAM的82H单元中的内容送
到A中。内部80H~FFH的RAM单元,只能使用这种间接寻址方式进行访问。
MOV A,82H和MOV A,DPL是等价的!
3.程序存储器向累加器A传送指令一-MOVC
对于程序存储器的访问,单片机提供了两条极其有用的查表指令:
指令格式:
MOVC A ,@A+PC;PC<- (PC) +1, (A)< - ( (A) + (PC) )
MOVC A,@A+DPTR ; (A) <- ( (A) + (DPTR) )
这两条指令采用变址寻址,以PC或DPTR为基址寄存器,以累加器A为变址寄存器,基址寄存
器与变址寄存器内容相加得到程序存储器某单元的地址值,MOVC指令把该存储单元的内容传送
到累加器A中。指今主要用于查表,即完成从程序存储器读取数据的功能。由于两条指令使用的基
址寄存器不同,因此使用范围也不同
第一条指令以PC作为基址寄存器,指令查表范围只能在以PC当前值开始后的256个字节范围
内,使表格地址空间分配受到限制。
第二条指令以数据指针DPTR为基址寄存器,由于DPTR可以通过指今来设置,表格常数可设
置在64K程序存储器的任何地址空间。若DPTR已有它用,在将表首地址赋给DPTR之前必须保护
现场,执行完查表后再予以恢复。


5.栈操作指令
堆栈区是将内部存储器的一部分区域划作专门用于堆栈的区域。堆栈区的操作规则是后进
先出(LIFO-Last In First Out),即最后存入的数据将被最先取出。
堆 区当前的栈顶地址用堆栈指针寄存器SP中的值表示,即SP始终指向栈顶。


上述两条指令完成两种基本堆栈操作: 压入堆栈(PUSH) ,弹出堆栈(POP)
堆栈中的数据以“后进先出”的方式处理,这种“后进先出”的特点由堆栈指针SP来控制,SP用来自动跟踪栈顶地址。
入栈和出栈操作过程如下:
(1) 入栈操作:先(SP) +1 - >(SP),指向栈顶的上一个空单元,然后把直接寻址单元的内容压入SP所指的单元中。
(2)出栈操作: 先弹出栈顶内容到直接寻址单元,然后 (SP)-1->(SP) ,形成新的堆栈指针。

数据传送类指令的注意事项
在数据传送类操作中应注意以下几点:
1)除了用POP或MOV指令将数据传送到PSW外,传送操作一般不影响标志位。当向累加
器中传送数据时,会影响PSW中的P标志。
2) 执行传送类指令时,把源地址单元的内容送到目的地址单元后,源地址单元中的内容不变。
3)对特殊功能寄存器SFR的操作必须使用直接寻址,也就是说,直接寻址是访问SFR的唯一方式。
4)对于8052单片机内部RAM的80H~FFH单元只能使用@Ri间接寻址方式访问。
5)将累加器A压入堆栈或弹出堆栈时,应使用PUSH ACC和POP ACC指令,不能使用PUSH A和POP A指令。
否则,程序编译会出错。因为累加器写成A或ACC在汇编语言指令中是有区别的,
使用A时,表示使用的是寄存器寻址方式:而使用ACC时,表示使用的是直接寻址方式。
OV A,#7
    LCALL GETSQ
LOOP:
    LJMP LOOP   
GETSQ:
    PUSH DPH
    PUSH DPL
    MOV DPTR,#TABLE
    MOVC A,@A+DPTR
    MOV R0,A
    POP DPL
    POP DPH
    RET
TABLE: DB 00H,32H,55H,78H,54H,54H,55H,76H,90H,21H
    END


这些指令都可以在KEIL验证,手册里面也有详细解释

wlo 发表于 2024-1-23 10:01:17

第十二节 单片机程序设计 和单片机FLASH作为EEPROM使用
这节课讲了软件延时框架
主要讲了单片机FLASH作为EEPROM使用
#include <STC8H.H>
#include "intrins.h"

void Delay1000ms(void);
static void IapIdle();
char IapReadByte(int addr);   //读取
void IapProgramByte(int addr, char dat);//写入
void IapEraseSector(int addr); //擦除
void main(void)
{       
        unsigned char leddata =0X01,leddata2 =0 ,i;
        unsigned char rear = 0,write = 0;
        P6M1 =0x00;
    P6M0 =0x00;
        P4M1 =0x00;
    P4M0 =0x00;
        P40 =0;
        leddata = IapReadByte(0);//先读取EEPROM地址数据
        if(leddata == 0xff)//判断第一次是不是0xff,stcEEPROM初始化的值是0xff
        {
                IapEraseSector(0);
                IapProgramByte(0,0X01);        //擦除,写入一个初始值                       
        }
        _nop_(); _nop_(); _nop_(); _nop_(); //给一点延时
        leddata = IapReadByte(0);//读取EEPROM地址数据                       
        while(1)
        {
                for (i= 0;i<8;i++)
                {               
                        P6 = ~leddata;   //流水灯
                        leddata <<= 1;               
                        if(0 == leddata)
                        {
                                leddata = 0X01;
                        }
                        IapEraseSector(0);
                        IapProgramByte(0,leddata>>1);//右移保存当前值,如果不右移就会保存下一个值
                        Delay1000ms();               
                }       
        }
}
/*----------------------------
关闭IAP
----------------------------*/
static void IapIdle()
{
    IAP_CONTR = 0;                  //关闭IAP功能
       
    IAP_CMD = 0;                  //清除命令寄存器
    IAP_TRIG = 0;                   //清除触发寄存器
    IAP_ADDRH = 0x80;               //将地址设置到非IAP区域
    IAP_ADDRL = 0;
}
char IapReadByte(int addr)
{
    char dat;                     //数据缓冲区

    IAP_CONTR = 0X80;         //使能IAP
        IAP_TPS =12;
    IAP_CMD = 1;             //设置IAP命令
    IAP_ADDRL = addr;               //设置IAP低地址
    IAP_ADDRH = addr >> 8;          //设置IAP高地址
    IAP_TRIG = 0x5a;                //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                //写触发命令(0xa5)
    _nop_();                        //等待ISP/IAP/EEPROM操作完成
    dat = IAP_DATA;               //读ISP/IAP/EEPROM数据
    IapIdle();                      //关闭IAP功能

    return dat;                     //返回
}
/*----------------------------
写一字节数据到ISP/IAP/EEPROM区域
----------------------------*/
void IapProgramByte(int addr, char dat)
{
    IAP_CONTR = 0X80;         //使能IAP
        IAP_TPS =12;
    IAP_CMD = 2;          //设置IAP命令
    IAP_ADDRL = addr;               //设置IAP低地址
    IAP_ADDRH = addr >> 8;          //设置IAP高地址
    IAP_DATA = dat;               //写ISP/IAP/EEPROM数据
    IAP_TRIG = 0x5a;                //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                //写触发命令(0xa5)
    _nop_();                        //等待ISP/IAP/EEPROM操作完成
    IapIdle();
}

/*----------------------------
扇区擦除
----------------------------*/
void IapEraseSector(int addr)
{
    IAP_CONTR = 0X80;         //使能IAP
        IAP_TPS =12;
    IAP_CMD = 3;            //设置IAP命令
    IAP_ADDRL = addr;               //设置IAP低地址
    IAP_ADDRH = addr >> 8;          //设置IAP高地址
    IAP_TRIG = 0x5a;                //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                //写触发命令(0xa5)
    _nop_();                        //等待ISP/IAP/EEPROM操作完成
    IapIdle();
}

void Delay1000ms(void)        //@11.0592MHz
{
        unsigned char data i, j, k;

        _nop_();
        _nop_();
        i = 43;
        j = 6;
        k = 203;
        do
        {
                do
                {
                        while (--k);
                } while (--j);
        } while (--i);
}


这是我用STC8H测试的,零零散散折腾了一天

wlo 发表于 2024-1-23 10:07:25

补第十一节课

eeprom扇区

wlo 发表于 2024-1-26 13:44:49

第十三节
中断原理及工作机制
中断是指计算机在执行其他程序的过程中,当出现了某些异常事件或某种请求时,CPU暂时中止正在执行的程序,而转去执行对异常事件或某种请求的服务程序。当服务完毕后,CPU 再回到被暂时中止的程序继续执行。
请示CPU中断的请求源称为中断源
中断源向CPU发出中断申请,CPU暂停当前工作转去处理中断源事件称为中断响应
对整个事件的处理过程称为   中断服务
事件处理完毕CPU返回到被中断的地方称为       中断返回
中断的优先级

计算机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断
要求为其服务的时候,就存在CPU优先响应哪一个中断源请求的问题。通常根据中
断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有
一个优先级别。CPU总是先响应优先级别最高的中断请求
当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序) ,发生了
另外一个优先级比它更高的中断源请求,CPU暂停原来中断源的服务程序,转而去
处理优先级更高的中断请求源,处理完以后,再回到原低优先级中断的服务程序,
这样的过程称为中断嵌套。
中断的优势
计算机采用中断技术,大大提高了工作效率和处理问题的灵活性,主要表现在3个方面:
1解决了快速CPU和慢速外设之间的矛盾,可使CPU和外设并行工作。
2可及时处理控制系统中许多随机参数和信息。
3具备了处理故障的能力,提高了机器的可靠性。
中断类似于程序设计中的调用子程序,但它们又有区别,一般保护断点是硬件完成的,
保护现场须在中断处理程序中用相应的指令完成。

使能中断和失能中断,可以通过指令设置相关特殊功能寄存器的内容来实现,
这是CPU能否接受中断请求的关键。只有在开中断的情况下,才有可能接受中断源的请求。
堆栈
堆栈区是将内部存储器的一部分区域划作专门用于堆栈的区域。堆栈操作有其特定的规则,
叫做后进先出 (Last In First Out,LIFO) 规则,即最后存入的数据将被最先取出。
堆栈区当前的栈顶地址用堆栈指针寄存器 (SP) 中的值表示,即SP 始终指向栈顶

中断的撤除
在响应中断请求后,返回主程序之前,该中断请求标志应该撤除,否则,
单片机执行完中断服务程序会误判为又发生了中断请求而错误地再次进入中断服务程序。
单片机中有些中断请求标志会自动撤除,有些不能自动撤除,必须用户使用相应的指令撤除

除外部中断2、外部中断3、定时器2、定时器3、定时器4固定是最低优先级中断外,其他
的中断都具有4个优先级,可实现四级中断嵌套。

21cnsound 发表于 2024-1-27 14:18:40

STC8H的试验箱看起来高大上,不是学生也能免费送吗?

wlo 发表于 2024-1-27 15:52:48

第十四课
外部中断控制LED
#include <STC8H.H>
#include "intrins.h"
unsigned char INT1_CNT;
void Delay10ms(void)        //@11.0592MHz
{
        unsigned char data i, j;
        _nop_();
        _nop_();
        i = 144;
        j = 157;
        do
        {
                while (--j);
        } while (--i);
}
void main(void)
{
        P6M1 = 0x00;
        P6M0 = 0x00;
        P4M1 = 0x00;
        P4M0 = 0x00;
        P3M1 = 0x50;
        P3M0 = 0x50;
        P40 = 0;
        IE1 = 0;
        EX1 = 1;
        IT1 = 1;
        INT1_CNT =0;
        EA = 1;
        P60 =1;
        while (1)
        {
                switch (INT1_CNT)
                        {
                                case 1:
                                        INT1_CNT =0;
                                        Delay10ms();
                                        P60 =~P60;
                                        while(P60 !=P60);
                                        break;
//                                case 2:
//                                        Delay5ms();
//                                        P60 =1;INT1_CNT =0;
//                                        break;
                                default:                                       
                                        INT1_CNT =0;
                                        break;                       
                        }       
        }               
}
/********************* INT0中断函数 *************************/
void INT1_int (void) interrupt INT1_VECTOR      //进中断时已经清除标志
{
   INT1_CNT ++;
}
页: 1 [2]
查看完整版本: STC8H8K64U学习打卡及心得 | 建议立即赠送 STC8H实验箱,提高学习效率