精华内容
下载资源
问答
  • (一)项目任务ü 任务1:用定时方式控制LEDn 子任务1.1 两种定期器...swtich语句认识n 子任务2.2 中断系统的认识n 子任务2.3 采用定时和中断方式驱动按键(二)相关知识点2.1、定时器的基本概念标准的51单片机有T0...

    (一)项目任务

    ü 任务1:用定时方式控制LED

    n 子任务1.1 两种定期器的认识

    n 子任务1.2 掌握定期器的定时原理和计算方法

    n 子任务1.3 采用定时器方法实现点亮LED功能

    ü 任务2:采用按键扫描控制LED

    n 子任务2.1 C语言---swtich语句认识

    n 子任务2.2 中断系统的认识

    n 子任务2.3 采用定时和中断方式驱动按键

    (二)相关知识点

    2.1、定时器的基本概念

    标准的51单片机有T0和T1两个定时器,52单片机多一个T2定时器,其余跟51单片机一样,有细心的读者会发现项目中用的是52单片机,名称却为51单片机,通常情况下把51和52单片机统称为51单片机。

    举个例子说明定时器工作的基本原理,假设一个水瓶的容量为65536ml,现在以1ml/s的速度往水瓶里滴水,那么需要65536s才能将水瓶滴满,再滴一滴水就会溢出。从中得到的启发是只要速度一定,可以根据滴的次数可以知道时间。假设现在需要定时30000s,只需往空瓶子里滴30000滴水即可,但是这样的话需要一直盯着水滴数。其实还有更好的办法,可以预先往水瓶里滴35536ml的水,再往水瓶里滴水,只要发现水瓶里的水溢出表示30000s的时间到了,定时器的工作方式亦是如此,使用定时器前首先了解相关寄存器。

    33b2dc77945c3f433c86e6ff53a52998.png

    章项目都以配置定时器T0为例,首先看下TCON寄存器各个位的含义。

    5a1712f4d056116a28879da34709209c.png

    TF1:定时器/计数器T1溢出标志。T1被允许计数以后,从初值开始加1计数。当最高位产生溢出时由硬件置“1”,向CPU请求中断,一直保持到CPU响应中断时,才由硬件清“0”(TF1也可由程序查询清“0”)。

    TR1:定时器T1的运行控制位。该位由软件置位和清零。TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。

    TF0:同理与TF1,TF1是针对定时器/计数器T1,TF0是针对定时器/计数器T0。

    TR0:同理与TR1,TR1是针对定时器T1,TF0是针对定时器T0。

    5ae9cc90e620ddaa3f9c7bc0f86071cd.png
    表3 寄存器TMOD各位功能描述(不可位寻址)

    0d13d263b676ad8db14f149dd610e20a.png

    常用的模式有模式1和模式2,模式2常用在串口通信中,本项目中以模式1为主。注意寄存器TCON可以寻址,寄存器TMOD不可位寻址。寻址的意思是可以单独操作寄存器中的某一位,例如寄存器TCON中的第四位TR0可以取出来单独赋值,进行TR0 = 1或者TR0 = 0操作,但寄存器TMOD不可单独操作某个位,只能进行整体赋值,例如TMOD = 0x01。

    定时器内容较为丰富,建议大家阅读官方数据手册。本节部分内容摘自该手册定时器部分,本节只列出了定时器需要用到的寄存器,仅供参考。

    采用查询法配置定时器的步骤:

    1、通过配置定时器TMOD确定定时模式;

    2、将计算得到的初值装载到TH0和TL0;

    3、通过配置TR0启动定时器0;

    4、判断标志位TF0,如TF0为1表示溢出,可以通过软件方式清零再重新进行监测。

    2.2、定时器的定时计算

    时钟周期定义为时钟脉冲的倒数(时钟周期就是单片机外接晶振的倒数,例如12M的晶振,它的时间周期就是1/12000000),是计算机中最基本的、最小的时间单位。

    机器周期是指完成一个基本操作所需要的时间。机器周期主要针对汇编语言而言,在汇编语言下程序的每一条语句执行所使用的时间都是机器周期的整数倍,而且语句占用的时间是可以计算出来,而C语言一条语句的时间是确定的。51单片机系列在其标准架构下一个机器周期是12个时钟周期,也就是12/11059200秒。现在有不少增强型的51单片机,其速度都比较快,有的1个机器周期等于4个时钟周期,有的1个机器周期就等于1个时钟周期,也就是说大体上其速度可以达到标准51架构的3倍或12倍。因为项目中使用的是标准的51单片机,机器周期是12个时钟周期。

    再讲解下定时器和计数器这两个概念。定时器和计数器是单片机内部的同一个模块,通过配置SFR(特殊功能寄存器)可以实现两种不同的功能,大多数情况下是使用定时器功能,因此也主要来讲定时器功能,计数器功能大家自己了解下即可

    配套的单片机学习版采用的12M的无源晶振,那么时钟周期为1/12000000,机器周期为12/12000000,假设定时20ms,那么需要的机器周期为0.02/(12/12000000)=20000个,根据2.1小结讲解的需要在定时器预装65536-20000=45536个值,因为采用的定时模式1,将45536化为十六进制装载到寄存器TH0和TL0中,45536 / 256=177,45536 % 256=244,将177转化为十六进制0XB1装载到TH0,将244转化为十六进制0XF4装载到TL0。

    同理可以计算得16位寄存器最大能定时65536*(12/12000000)≈65ms,一般来说定时的时间尽量取在0~65ms中间值,如本例中定时器配置的定时20ms,如需定时1s,只要定时循环50次即可。读者可以计算下定时50ms需要多大的装载初值。

    注意:当采用11.0592M晶振时,采用类似的计算方式!

    2.3、C语言-----“|”,“&”

    对不可寻址的寄存器需要同时处理8个位,例如采用定时器0的模式1(M1=0,M0=1),配置寄存器TMOD=0x01,改配置虽然正确配置了第0、1位,同时也改变了第2到第7位,因此希望通过某种操作方式只改变第0、1位。在实际操作过程中采用TMOD=TMOD&0XFC,TMOD= TMOD|0X01,看似更为复杂,其实逻辑更为严谨。

    运算符“|”表示“按位或”,例如“1|1”为1,“1|0”为0,“0|1”为1,“0|0”为0,可见1“或”任何数都为1,0“或”0才为0。

    运算符“&”表示“按位与”,例如“1&1”为1,“1&0”为0,“0&1”为0,“0&0”为0,可见0“与”任何数都为0,1“与”1才为1。

    2.4、C语言-----swtich语句

    在项目三中采用if else语句用于条件的判断,如果条件较多的情况一般采用swtich语句,如下所示。在switch语句中有四个关键字,分别为switch、case、default和break。当switch后的表达式满足某个case后的常量表达式时,运行该case以后的语句块。注意每个case后面都需要break,表示跳出swtich语句。default表示当表达式没有匹配的case时,运行default后的语句块。

    switch(表达式)
    {
    case常量表达式1: 执行语句;break;
    case常量表达式2: 执行语句;break;
    case常量表达式3: 执行语句;break;
    case常量表达式4: 执行语句;break;
    default: 执行语句;break;
    }

    2.5、中断系统

    CPU正在运行时,外界发生了紧急事件请求,CPU暂停当前工作,处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。向CPU请求中断的源称为中断源。中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,有个优先响应的问题,在本项目中只涉及到定时器0和定时器1的中断,详细请参看芯片手册中P156。

    通过一个例子帮助读者理解中断含义,比如小明在家看电视,突然听到有敲门声,按下电视遥控暂停键去开门,开完门后继续看电视,这就是中断也是最常用的中断形式。还是小明在家看电视,突然听到有敲门声,按下电视遥控的暂停键再去开门,刚走到门口突然听到厨房里水开的声音,急忙跑到厨房把电源关了,再去开门,开完门后继续看电视,这是嵌套中断,在 “开门”这个中断源里又有“关电源”这个中断源,小明会根据事重缓急优先处理各个事件,单片机也是如此。

    bd37651de0e9276005ee417e6bc819f9.png
    图1 中断优先查询次序

    b1ad206b04e50b1251eea196b45c444e.png
    图2 C语言优先中断查询函数

    采用中断系统需编写中断函数,如3.2小节采用定时器0,中断函数为“void InterruptTimer0() interrupt 1”,它的格式为“void 变量名() interrupt 1”,变量名后的括号里 “void”可写可不写,其余部分要按照规范写,如图2所示一般将中断函数放在程序的末尾,函数名不需要进行声明!

    表5 中断允许寄存器(可位寻址)

    c543f3e33fb3752fd1f5da914d55b76e.png

    如表5所示,介绍常用的控制位。

    EA:CPU的总中断允许控制位,EA=1,使能总中断,EA=0,关闭总中断。

    ET0:T0的溢出中断允许位。ET0=1,允许T0中断;ET0=0禁止T0中断。

    ES:串行口1中断允许位。ES=1,允许串行口1中断;ES=0,禁止串行口1中断。

    IE中断允许寄存器在本项目中只要用到了第一、七位,定时器常配合中断使用,因此要使能EA和ET0位,同时也介绍了第四位ES,因为在串口中断中也会用到。

    2.6、全局变量和局部变量

    全局变量:在所有函数外部定义的变量称为全局变量,它的作用域贯穿整个程序。全局变量定义必须在所有函数之外。在项目3第3.2小结程序中定义的table1[15],变量定义在主函数和子函数之外。在同一源文件中,允许全局变量和局部变量同名,在局部变量作用域内,同名的全局变量不起作用。

    局部变量:函数内部的变量称为局部变量,它的作用域仅限于函数内部,该函数结束后里面的变量也无效了。不同函数里面局部变量可以同名,分配不同的存放单元,互不相干,对于初学者编者建议尽量使用不同变量名。

    由于定义全局变量会永久占用单片机内存单元,局部变量只有在具有该变量的函数使用时才会占用内存,使用完该函数后会自动释放内存,因此编者建议尽量使用局部变量。

    在本项目3.2小节Key_Scan()函数中有“static unsigned char cnt1=0xFF”语句,把“cnt1”叫做静态局部变量,若没有“static”这个关键字,“unsigned char cnt1=0xFF”语句中的“cnt1”叫做局部变量。举例说明,变量cnt1在Key_Scan()函数作用后变成0xAA,那么Key_Scan()被调用完成后释放内存空间,再下次调用Key_Scan()时,变量cnt1又恢复到初值0xFF,但是我们不希望变成初值,而是希望执行上一次函数执行结束后的值0xAA,那么在不使用全局变量的情况下可以通过添加“static”关键字实现。

    (三)设计实施

    3.1、查询法定时

    #include <reg52.h>
    sbit LED=P0^4;
    void main()
    {	
    	unsigned char cnt=0;
    	TMOD &= 0XFC; // TMOD= TMOD&0XFC 可以简写成TMOD &= 0XFC
    	TMOD |= 0X01;	// TMOD= TMOD|0X01 可以简写成TMOD |= 0X01
            TH0 = 0XB1 ; //定时20ms
    	TL0 = 0XF4;
    	TR0 = 1;  //启动定时器,TR为Time Run
    	 while(1)
    	 {
    		  if(1 == TF0)  //检测标志位是否为1,TF为Time Flag
    			{
    				TF0 = 0;//标志位要及时复位,方便下次计时
    				TH0 = 0XB1;//重新加载初值
    			        TL0 = 0xF4;
    				cnt++;
    				if(cnt >= 50)
    				{
    					cnt=0;
    					LED=~LED;
    				}		
    			} 
    	 }
    }
    

    ① 在项目二中采用delay函数延时的方式来闪烁LED,虽然跟本小节的实验现象一致,但是有根本性区别。delay函数是在延时过程中占用了大量CPU资源,因此不能进行其他函数运行,如果模块较多会造成信号堵塞,影响其他功能运行。定时器是由独立的模块运行,不占有CPU资源,可以让单片机做更多的事,因此在以后项目中尽量采用定时器定时。

    ② 定时器模式1在发生溢出后会自动清空初值,因此在每次计数时要重新加载初值;

    ③ “1==TF0”也可以写成“TF0==1”,因为该语句的含义是判断TF0是否为1,如果写成

    “TF0==1”方式,有可能写成“TF0=1”,这样的话程序不会报错,但是语句是有问题的;如果写成“1 ==TF0”,那么如果漏了一个等号写成“1 =TF0”,程序会直接报错,这种方式方便读者查错。

    ④ 如果采用变量进行计数,如小节中的cnt,一定要及时清零;

    ⑤ TMOD、TH0、TR0等一些定时器相关的变量都在头文件中进行过定义,因此不需要再重新定义;

    3.2、按键中断法

    #include <reg52.h>
    sbit LED1 = P0^4;
    sbit LED2 = P0^5;
    sbit KEY1 = P3^4;
    sbit KEY2 = P3^5;
    unsigned char KeyNum=0;   
    unsigned char KeyLock1=0; //按键1的标志位
    unsigned char KeyLock2=0; //按键2的标志位
    void KEY_Scan();
    void KEY_Action();
    void main()
    {	
    	EA = 1;//打开总中断
    	TMOD &= 0XFC;
    	TMOD |= 0X01;
    	TH0 = (65536-2000)/256;//定时2ms
    	TL0 = (65536-2000)%256;
    	ET0 = 1;
    	TR0 = 1;
    	 while(1)
    	 {
    		  KEY_Action();
    	 }
    }
    void KEY_Scan()
    {
    	static unsigned char cnt1=0xFF;
    	static unsigned char cnt2=0xFF;
      	cnt1 =(cnt1<<1)|KEY1;//a语句
    	cnt2 =(cnt2<<1)|KEY2;
    	if(cnt1 != 0x00) // b语句
    	{
    		KeyLock1=0;//c语句
    	}
    	else if(KeyLock1==0)// 检测到按键按下状态且第一次按下
    	{
    	  	KeyNum = 1;
    		KeyLock1=1;// 防止重复触发
    	}
    	if(cnt2 != 0x00)
    	{
    		KeyLock2=0;
    	}
    	else if(KeyLock2==0)
    	{
    	  	KeyNum = 2;
    		KeyLock2=1;
    	}
    }
    void KEY_Action()
    {
    	 switch(KeyNum)
    	 {
    	    case 1:LED1 =!LED1; KeyNum = 0;break;
    	    case 2:LED2 =!LED2; KeyNum = 0;break;
    	   	default:break;          
    	  }
    }
    void InterruptTimer0() interrupt 1
    {
        TH0 = (65536-2000)/256;
    	TL0 = (65536-2000)%256;
    	KEY_Scan();
    }
    

    ① 按键操作采用定时中断的方式,有效避免了delay函数占用CPU资源,在以后的程序中推荐使用这种方式;

    ② 按键定时中断的原理是将按键部分分为两部分,一部分是按键扫描KEY_Scan()在中断函数中每隔2ms进行扫描,另一部分是按键函数KEY_Action()在while函数中不停运行等待指令执行,当KEY_Scan()函数检测到按键按下,那么就执行KEY_Action()函数内容;

    ③ KEY_Scan()函数每隔2ms检测变量cnt1是否为0x00,0x00表示按键按下,若检测到非按下状态,标志位KeyLock1为0。若检测到按键按下状态且第一次按下,则KeyNum = 1,同时将KeyLock1赋值为1,防止重复触发。注意else if语句一定要结合if理解。

    ④ KEY_Scan()中的“cnt1 =(cnt1<<1)|KEY1”语句用来检测按键是否按下,cnt1初值为0b1111 1111,假设按键已稳定按下,则KEY1为零,经过第一个2ms后cnt1为(11111111<<1)|0,即1111 1110,经过第2个2ms后cnt1为(11111110<<1)|0,即1111 1100…..那么经过8个2ms后此时cnt1为0x00,此状态法有效解决按键抖动问题;

    ⑤ cnt2同理于cnt1;

    ⑥ KEY_Action()函数接收到KeyNum的值进行相应的动作。

    (四)小结

    1、熟练掌握定时器、中断的使用方法;

    2、掌握按键中断扫描的方法;

    3、掌握swtich使用方法。

    可以在B站观看相关视频!!

    哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili

    展开全文
  • 串口中断方式的发送过程是怎么回事? 有人问过这个问题: “用串口中断接收比较好理解,只要接收缓存接收到数据,标志位就会置位,然后进入中断服务程序读取接收缓存中的数据,但是在发送时,应该是在数据...

    串口中断方式的发送过程是怎么回事?

     

    有人问过这个问题

        “用串口中断接收比较好理解,只要接收缓存接收到数据,标志位就会置位,然后进入中断服务程序读取接收缓存中的数据,但是在发送时,应该是在数据发送出去之后发送标志位才会置位(我是msp430单片机),那么此时等数据发送出去之后进入中断又有什么意义呢?到底如何通过串口中断来发送数据呢?”

         通过阅读 STM32 官方库的代码,我找到了答案。

         中断发送方式的基本过程是:每发送一个字节,该字节发送完毕引发中断,中断处理程序再发送下一个字节……直到发送完成。

         这里有两个细节问题需要注意:

    1. 第一个字节是怎样发送的?因为发第一个字节之前,并没有发送字节完成所触发的中断,那第一个字节怎么被发送出去?这关系到中断在硬件层面的触发方式:如果是电平触发的中断,在中断式发送函数中会使能相应的中断,因为发送缓冲区为空,其所处的电平状态就在中断功能被使能后会直接触发中断,导致第一个字节在中断处理函数中被发送出去;如果是电平跳变触发的中断,为了引发中断,只好把第一个字节在中断式发送函数中“手动”发送出去,由此引发一个中断的“多米诺”效应,把后面的字节依次发送出去。参考:https://www.lpcware.com/content/forum/uart-tx-interrupt-should-i-put-least-one-byte-tx-fifo
    2. 怎么才算“完成”?完成是用参数指定的欲发送字节数决定的,但发送达到指定数目,中断处理函数中就失能相应中断,于是中断的“多米诺”链条停止。 

    转载于:https://www.cnblogs.com/zhangbing12304/p/10881768.html

    展开全文
  • 既然和外界需要“交互”,就必须使用“中断”功能,所以一般串口中断是配合使用的。 在总结串口使用方法之前需要对一些基本概念进行理解 第一. 关于波特率(baud rate)的解释,网上有很多文章,以我个人的肤浅...

    51单片机的串口功能就是和外界进行通讯,所谓的“外界”也就是与单片机进行交互的媒介,最常用的就是我们经常使用的计算机、平板或者其他设备(比如另一个单片机配合显示模块使用)。

    既然和外界需要“交互”,就必须使用“中断”功能,所以一般串口和中断是配合使用的。

    在总结串口使用方法之前需要对一些基本概念进行理解

    第一. 关于波特率(baud rate)的解释,网上有很多文章,以我个人的肤浅理解觉得说得是这样一回事儿:

    西方某大国正在经历总统换届选举,选来选去没有选出个像样的,公民们有意见,非要到国会进行“抗议(0元购)”,一下子来了7680口,有的人还拿着枪,有长的有短的,还有扛炮的!眼看着要乱,警察和抗议者达成协议,进来可以但是为了保证秩序,不能一下子都进来,需要分批进入,根据法律,拿枪的也不犯法,为了保证安全,需要对进场的人进行监视,尤其是带枪的(只限短款,扛炮的就先等等吧,几百年了这地方就被烧了一回,这次得谨慎点)。规定:每组只限2个人,每批包含4组,每组相距等同的距离,一次只能进入1批。根据带枪与否划分“风险级别”,两个人都带枪入场的为最高风险,两个人都不带枪的为最低风险,两个人其中之一带枪且带枪的人走在前面的为次级风险,两个人其中之一带枪且带枪的人走在后面的为三级风险。同时,不能光进不出,前门进后面出,进多少就得出多少,后门根据前门进入的人数统计需要出去的人数,每批之间的间隔时间并不固定,谁还没想在议长的椅子上坐会儿呢?

    上面的例子中那2个人中的每个人就可以看成是计算机系统中的“位”,而2个人为一组就可以看成是通信领域中的“码元”,因为带枪与否是两种状态,所以共组成了四种状态,就是例子中的“风险级别”。

    好了,现在有了这些概念就可以研究下面的概念了:

    位:计算机系统中一个二进制数,0或者1,上面的例子中那2个人中的每个人就可以看成是计算机系统中的“位”

    码元:一个二进制数是由若干个“位”构成的,而码元就是这个指定的二进制数,也就是说码元包含若干个二进制位,可以是1位,2位,或者3位,至于到底是几位是根据功能要求人为指定的,叫做“调制”,码元是传输信息的基本单位。上面的例子中每个小组就是一个“码元”

    字节(字符):计算机系统存储单元的一个基本单位就是字节,一个字节包含8个位(bit),上面的例子中每1批就是一个字节,每批包含4组总共8个人

    帧:通用异步接收发送方式(UART)中把特定的一组二进制数称为“帧”,这组特定的二进制数是由“开始位”,“数据位”,“奇偶校验位”,“停止位”依次连接而成的,等同于上面例子中的每批,在进出“国会”的时候,需要前后被两个警员夹在中间,前面一个警员就是“开始位”,末尾一个警员就是“停止位”,这里省去“奇偶校验位”,这“串”起来的2个警员再加上这1批中的8个人就是1“帧”。

    比特率:单位时间内传输的二进制位的数量,单位 bit/s,等同于上面例子中统计每小时总共进去多少人

    调制速率:单位时间传输码元的数量,等同于上面例子中统计每小时总共进去多少组

    波特率:应该被称作调制速率,简称调制率,单位是波特(baud),可见,所谓波特率就是调制速率,对应的也是上面例子中的组数

    公式:D=调制速率(波特率),R=比特率,L=每个码元中的比特(位)数,M=码元的状态数(上面的例子,带枪和不带枪2种情况(位)就构成4种风险级别(状态))

    D=\frac{R}{L}=\frac{R}{\log_2M}

    调制速率(波特率)与比特率的关系

    比特率 = 调制速率(波特率)x 码元(单个调制状态)对应的二进制位数

    不同的码元可以包含不同的位数,位数越多能分辨的不同状态的数量就越多,例如如果码元包含1个位,则只能分辨2种状态,如果码元包含2个位,则共能分辨4种状态,如果是包含3位呢,就共能分辨8种状态,可见,包含的位数越多,状态的分辨率越高,其实状态的总数就是2的N次方,N就是码元中包含的“位”数。至于一个码元要包含多少个二进制位,根据功能由设计者指定(你说了算!技术学到这儿是不是有点当家做主的感觉?)。

    如果码元就包含1个二进制位,则比特率就等于调制速率(波特率),被称为“两相调制”;如果码元就包含2个二进制位,共四种状态,则比特率就等于调制速率(波特率)X2,被称为“四相调制”;如果码元就包含3个二进制位,共8种状态,则比特率就等于调制速率(波特率)X3,被称为“八相调制”。

    最后问个问题,总共7680名抗议者要“有序抗议”国会,要求一天8小时“抗议完毕”,那么每小时要进出多少人?又是多少组呢?总共7680名抗议者,每组2人,每批4组,每批配备2名警员,每批一共10人,要让7680名抗议者“有序抗议”国会,配上警员的人数总共进出的人数达到9600(这个数字熟悉吗?),8小时抗议完毕,每小时要进出1200人,每小时600组,每小时60批(加上警员),结果中的1200人就等同于“位”的概念,600组就等同于“调制速率(波特率)”的概念,60批就等同与“字节(字符)”的概念

    第二. 有了前面的“国会抗议”事件,相信大家对比特率,波特率,调制速率都有了个了解,接下来的问题是:在异步串口通讯中为什么要保证发送方与接收方的波特率相同,答案包括两个方面:

    (1)单片机的“调制速率(波特率)”是如何确定的
             51 单片机中的“调制速率(波特率)”就是比特率,即每秒传送的位数,单位bit/s(位/秒)

    (2)“调制速率(波特率)”设置的意义
             单片机工作时必须根据内部或者外部设置的时钟基准对多种任务进行协调,对于串口通讯的操作同样少不了使用时钟基准作为时间参照,这个问题涉及到“采样”的概念,请参考博文串口波特率问题的处理,里面讲得很清楚,其实就是为了同步采样频率。另外,从文中也可以推算出“波特率”与“采样频率”、“机器频率”、“振荡频率”的关系,首先,“采样频率”=16X“波特率”,“采样频率”=“机器频率”,如果以12分频(12T)为准,“振荡频率”=12X“机器频率”,所以“振荡频率”=12X16X“波特率”;从周期的角度来看,“波特周期”=16X“采样周期”,“采样周期”=“机器周期”,“机器周期”(12T分频)=12X“震荡周期”,所以“波特周期”=16X12X“振荡周期”,明白了各个频率之间的关系就自然能理解后面介绍的初值问题

    第三. 51单片机异步串行通讯(UART)功能的使用

    与51单片机其他功能的实现类似,要使用串口中断功能就必须包含三个步骤:1. 设置+赋值  2. 中断设计

    设置和赋值操作因单片机型号的不同而不完全相同,但是原理是相似的,举一反三即可。这里用到的芯片是STC12C2052AD

    (1)设置+赋值

    这一步就是设置与串口和中断功能相关的寄存器以指定单片机的工作状态

    设置“辅助寄存器(Auxiliary Register)” AUXR
    AUXR可以设置定时器、串口传输的分频,这一设置决定了“机器频率”与“振动频率”的关系

    其中通过设置“T0x12”和“T1x12”分别设置定时器T0和T1的分频,默认为0即12分频
    其中通过设置“UART_M0x6”设置串口的分频,默认为0即12分频

     

    设置“串口控制寄存器(Serial Control Register)”SCON
    通过设置SCON可以指定波特率发生器的时钟基准


    SM0/SM1/SM2三个位的设置可以指定波特率发生器的时钟基准

    SM0 SM1 模式 描述 波特率时钟基准
    0 0 0 半双工状态,发送接收8位数据,使用移位寄存器 f_osc/12
    0 1 1 全双工状态,发送10位数据 定时器1的溢出率/PCON寄存器SMOD位
    1 0 2 全双工状态,发送11位数据 f_osc/16或者f_osc/32
    1 1 3 全双工状态,发送11位数据 定时器1的溢出率/PCON寄存器SMOD位








    SM0(serial mode bit0):串口模式0移位寄存器
    SM1(serial mode bit1):串口模式1,8位通用异步接收可变时钟基准寄存器
    SM2(serial mode bit0):多处理器协同通讯设置位,
                                               在模式0下SM2无效,配置模式0时SM2应该设置为0
                                               在模式1下,SM2用来检测接收过程中的有效“结束位”,当SM2=1时,仅当检测到有效“结束位”后,RI才被激活
                                               在模式2和3下,SM2用来激活“多处理器协同通讯”
    REN(reception enable):串口接收功能的使能位,REN=1:允许串口接收;REN=0:禁止串口接收
    TB8:在模式2和3中,TB8用来存储发送数据的第9位
    RB8:在模式2和3中,RB8用来存储接收数据的第9位
    TI(transimission interrupt):发送中断标志位,在模式0中,当第8位发送后由硬件置1;在模式1~3中,当“结束位”发送后由硬件置1,TI必须由软件置0
    RI(reception interrupt):接收中断标志位,在模式0中,当第8位接收后由硬件置1;在模式1~3中,当“结束位”接收后由硬件置1,RI必须由软件置0
    模式0:以固定的波特率工作在半双工状态
                 通过RxD管脚发送和接收8位数据,在数据发送与接收过程中通过TxD管脚输出移位时钟,波特率设置为12分之1的振荡频率
    模式1:以可变的波特率工作在全双工状态
                 传输过程中数据为10位,1个起始位+8个数据位+1个结束位,在数据接收过程中,结束位被存储在RB8位中
                 波特率的值取决于定时器1的溢出率和“电源控制寄存器”中SMOD位的设置值(这部分在后面介绍初值问题时有讲解)
    模式2:以固定的波特率工作在全双工状态
                 传输过程中数据为11位,1个起始位+8个数据位+1个可编程位+1个结束位,在数据接收过程中,可编程位被存储在RB8位中,
                 在数据发送过程中,可编程位被存储在TB8位中
                 波特率的值为32分之1的振荡频率或者16分之1的振荡频率,取决于“电源控制寄存器”中SMOD位的设置值
    模式3    以可变的波特率工作在全双工状态(波特率)
                 此模式与模式2相同,只是波特率是可变的,波特率的值取决于定时器1的溢出率和“电源控制寄存器”中SMOD位的设置值

     

    设置“定时器模式控制寄存器(Time Mode Control Register)” TMOD
    当在SCON寄存器设置了模式1或者3时则指定定时器1作为波特率发生器的时间基准,因此需要通过TMOD对定时器1进行设定

    TMOD各个位的设置意义不在此处详谈,可以参考数据手册上对定时器设置的内容
    一般使用定时器1工作在模式2作为波特率发生器(高四位的设置:GATE=0, C/T=0, M1=1, M0=0, 第四位设置为0)模式2为“自动填装定时器”,自动填装的意思就是用定时器的低8位TL1计时,当溢出时将高8位TH1的数值自动填装到低8位TL1
    (TH1:定时器1的高8位;TL0:定时器1的低8位)

     

    问题:“填装”的是什么值?
    这就要引出定时器的初值设置问题,至于什么是初值和实现怎样的功能请参考之前我写的一篇文章,51单片机硬件定时器设定的初值问题,如果知道了初值的概念和怎样设置初值,就应该考虑定时器、初值和波特率的关系以便正确使用串口功能,之前讲过,51 单片机中的“调制速率(波特率)”就是比特率,即每秒传送的位数,单位bit/s(位/秒),为了保证传输的正确性,每个位又被采样分频设置成若干个检测点,也就是说1个位被检测若干次,取其中指定的几次进行比较以确定数据传输是正确的,这样的采样分频设置可以是16采样(每位检测16次)、32采样(每位检测32次)或者64采样(每位检测64次),而每一个检测点即采样点就意味着定时器需要计数溢出一次,这里注意每个检测点不是定时器计数一次,而是计数溢出一次,而定时器计多少数后溢出就需要我们为定时器指定“初值”,这就是指定“初值”的原因!如果采用32次采样设置,波特率和定时器计数溢出频率的关系就可以用如下公式表示

    Baud=\frac{f_{overflow}}{32}

    Baud:波特率,这个值是预先知道的,我们可以在各种波特率值中选择一个(问题又来了,为什么波特率是选择出来的,而不是自己指定的?大家可以自行寻找答案)

    f_overflow:定时器的计数溢出频率

    当设置相应寄存器用定时器1的模式2作为波特率发生器时,使用的是低8位作为定时器,高8位和低8位在最初都被设置为初值,但高8位不参与计时,只是存储初值(当低8位计时溢出时,高8位自动再将初值传送给低8位,这样每次就不用重复设置初值了)。低8位作为定时器从初值开始计数直到超过最大值255时就发生了1次溢出,溢出后,存储在高8位的初值自动加载到低8位中重新开始计数,这样就可以知道在单位时间内(1秒)定时器一共“溢出”多少次,就是“溢出率”即f_overflow。既然提到“率”就必须和时间联系在一起,既然溢出率就是溢出的次数,而溢出的次数又是定时器计数造成的,因此就必须知道定时器每1次计数用了多少时间,在51单片机中,定时器每计数一次所经过的时间就是一个机器周期(看到这儿也应该理解“机器周期”的概念了),所以从计时到溢出所用的时间就等于T=NxM,N=从计时开始到溢出总共计数的次数,就是255-初值,其中255是8位定时器最大计数值;M=每次计数所用的时间就是1个机器周期,所以单位时间的溢出次数即溢出率 f_overflow = 1/NxM,到这里就可以建立波特率和初值、机器周期的关系

    Baud = \frac{1}{32\cdot N\cdot M}

    定时器的计数频率即机器频率又与时钟基准频率(振荡频率)有关,这样又把波特率和时钟基准频率建立了联系,公式如下

    Baud=\frac{1}{32\cdot N }\cdot \frac{1}{M}=\frac{1}{32\cdot N }\cdot \frac{f_{osc}}{12}=\frac{f_{osc}}{32\cdot 12\cdot N }

    Baud:波特率

    f_osc:时钟基准频率,也是振荡频率,例如使用外部晶振12MHz,那么f_osc就是12MHz

    M=每次计数所用的时间就是1个机器周期,倒数就是机器频率,而定时器机器频率(f_timer)=振荡频率(f_osc)/12,原因是系统采用了12分频,意思是外部晶振每振动12次,定时器计数1次,为什么采用12分频而不是其他值呢?其实这个分频数可以选择但不能随意指定,可以选择例如2分频,4分频,8分频,或者其他值,选择分频可以通过指定相应的寄存器值来实现,这里不展开,可以参考相应的技术手册。选择不同的分频意味着使用不同的速度,分频数越高,速度越慢,想要提高速度可以选择1分频,还记得之前介绍的那个AUXR寄存器吗?就是用来干这个事情的

    在实际计算波特率的初值时,还需要考虑另外一个参数,就是SMOD,这个参数是一个叫“电源控制寄存器(PCON)”中的一个位,通过设置SMOD,可以实现不同波特率的取值

     

    设置“电源控制寄存器(Power Control Register)” PCON

    SMOD=0,波特率以32分频(即每一位采样32次)(以32分频为例,具体参考技术手册)
    SMOD=1,波特率采样频率提高1倍

    最终波特率公式如下

    Baud=\frac{2^{smod}\cdot f_{osc}}{32\cdot 12\cdot N }=\frac{2^{smod}\cdot f_{osc}}{32\cdot 12\cdot \left (255-TH1 \right ) }

    Baud:波特率

    f_osc:时钟频率即振荡频率

    TH1:存储在定时器1高8位的初值

    SMOD:PCON寄存器的SMOD位(0:32分频;1:16分频)

    在实际计算中波特率是事先选择的定值,例如4800,9600等,通过上面公式视为了计算初值,例如波特率选择为 4800 bit/s,使用12分频的外部12MHz晶振,SMOD=1,则初值 TH1约等于243

    到这里就把选择指定波特率并且利用定时器1作为波特率发生器的设置和初值计算问题说完了

    为了正确使用串口还不得不用到中断处理,当系统接收到或者发送出需要的数据时,通过中断处理就可以实现系统和外界的交互,因此还必须设置和中断相关的寄存器

     

    设置“中断使能寄存器(Interrupt Enable Register)” IE

    中断使能寄存器(IE)可以按位赋值,所谓“按位”的意思就是可以单独指定EA的值,比如形如 EA=xxx,所谓“使能”就是开关的意思
    EA:总中断使能,想要使用中断功能,就必须将EA设置为1,否则任何中断都不能用
    ES:串口中断使能,想要使用串口中断功能,就必须将ES设置为1
    EPCA_LVD:PCA模块、低压检测中断
    EADC_SPI:ADC、SPI中断
    ET1:定时器1的中断使能
    EX1:外部中断 INT1 使能
    ET0:定时器0的中断使能
    EX0:外部中断 INT0 使能

    需要使用串口功能时,需要将 EA设置为1,将 ES设置为1

     

    (2)中断

    除了几个必须要进行设置的特殊功能寄存器外,还有一个寄存器也比较重要,就是SBUF,全程“串口数据缓冲器(Serial Data Buffer)”在通讯中无论是外界向单片机通过串口写入数据,还是外界通过串口读取单片机中的数据,都必须使用SBUF作为中转站,在使用时,把SBUF放置在中断处理函数中

    另外,在中断处理函数中需要指定中断类型为“串口中断”,通过指定“中断代号码”来实现,因为系统中断可以不同的处理程序触发的,比如定时器,外部中断,所以需要指定是哪个处理程序触发的,采用中断号来区分,实现串口中断处理程序的中断号为 4。

    串口中断处理函数的格式是  void 函数名() interrupt 4 {中断处理程序}

    中断处理程序的编写包括
    1. SBUF赋值
    2. 发送标志TI、接收标志RI在程序中清0(单片机发送数据后TI自动置1,单片机接收数据后RI自动置1,在程序中清0后等待下一次发送、接收数据)

    具体的程序示例我就不再写了,网上有很多,这里主要是把关于串口中断功能的相关设置总结一下,同时也就几个概念作出解释以便理解,本人也是初学者,如果有误还请见谅,指出,一同进步。

     

     

    展开全文
  • 之前讲过中断的概念,没看过的小伙伴可以 “中断”-嵌入式的灵魂 看一下,今天给大家讲一下,三种中断中最不起眼的串口中断,说实话,串口中断不适合单独讲,适合放在通信里去讲。大家都知道,我们做单片机想提高,...

    今天粉丝催更,求“串口中断”的教程,第一次被催啊,我稍微认真点写。

    之前讲过中断的概念,没看过的小伙伴可以 “中断”-嵌入式的灵魂 看一下,今天给大家讲一下,三种中断中最不起眼的串口中断,说实话,串口中断不适合单独讲,适合放在通信里去讲。

    efb497fc314acb16b93a3a72ea04b251.png

    大家都知道,我们做单片机想提高,肯定往通信连接的方向走,往这个方向就难免碰到串口通信的问题,今天我们就从最简单的UART(通用异步收发器)来讲,毕竟通信方式太多。

    2e89fd6008c27e213bf3e740d3d6a0a2.png

    具体的通信什么的我就不讲了,单独讲。。。。

    这里要明确一点,串口中断就是服务于串口的,别的地方用不着,而且以我的认知只有51保存了,而且发扬光大,别的都直接 上库 ,所以以下我用51单片机来讲。

    开始:

    单片机会通过一系列的操作将多位数据传出去或者传回来。

    以上就是串口通信的核心,单片机在传出去或者回来的时候都会经过一个“容器”,这个“容器”专业名词是SBUF,记着 去和回来都是这个;

    8408594f1f97fa99e727dcec2ea5bb65.png

    那问题来了,都是一个我们如何去用它,这时候我们就可以用串口中断了;

    比如你现在正在往外发送数据,经过SBUF停下来了,只有串口中断开了你才能出去,像一个保安,回来也是一样,必须经过在串口中断才行;

    具体怎么配置我用51程序给大家看一下:

    #include #define uchar unsigned char#definr uint unsigned int unsigned char flag,a,i;uchar code table[] = "I get";void init(){TMOD = 0x20;TH1 = 0xfd;TL1 = 0xfd;TR1 = 1;REN = 1;SM0 = 0;SM1 = 1;EA = 1;ES = 1;}void main(){init();while(1){if(flag == 1){ES = 0;for(i = 0;i < 6; i ++){SBUF = table[i];while(!TI);TI = 0;}SBUF = a;while(!TI);TI = 0;ES = 1;flag = 0;}}}void ser()interrupt 4{RI = 0;a = SBUF;flag= 1;}

    之间一直不停的开启或者关闭串口中断,就是为了防止进去的出来的懵逼。

    展开全文
  • 这是北航士谔书院的第 949篇推送本文介绍中断(interrupt)的基本概念,并演示定时器中断、外部中断的使用方法。如果同学们对文章内容有疑问,或发现文中有任何不妥之处,请点击“阅读原文”在页面末尾评论区留言,我...
  • 今天给大家讲一下“中断”,一个让我刚开始接触单片机头疼的概念,我相信也是很多初学者头疼的地方,之所以头疼只是因为他太过于抽象,本章我用形象的举例来给大家讲出来。注意:本章节我用Arduino来讲,选用它的...
  • 文章目录显示屏LCD介绍LCD1602的使用1602的接口说明1602的主要技术参数1602内部系统结构图1602基本操作时序写操作时序图1602的命令指令码忙检测函数相关代码串口中断配置计算机串行通信基础并行通信串行通信串行通信...
  • cc2530入门 与串口中断处理

    万次阅读 2016-05-05 17:26:19
    做毕业设计的时候用到了cc2530开发板,用协议栈编程实现智能家居的功能。现在总结一下:  先从Z_Stack的main()函数开始。 main函数在ZMain.c中,总体来说,...先引入一下概念性的东西: 端点(EndPoint):是协议栈应
  • 1.0.2通信的几个专业性概念 (1)同步和异步 (2)双工 半双工,单工 (3)并行通信和串行通信 (4)电平信号和差分信号 1.1串行通信 (1)串口通信 (2)一种特定的通信协议,一般叫串口通信,串行通信,UART USART...
  • STM32F7系列芯片集成了L1高速缓存,即L1 CACHE,包括D-CACHE和I-CACHE。...在聊这个话题之前,先说几个基本概念或术语。1、芯片复位后,I-CACHE,D-CACHE默认是关闭的。可以分别打开或关闭以及配置各自大小。2...
  • 串口中断的标志位必须通过软件置0,其他好像是自动的,不嫌麻烦就都用软件置0 外部中断代码 低电平触发 #include <reg52.h> #define uint unsigned int #define uchar unsigned char sbit key_s2 = P3^0;
  • 定时器中断 外部中断 串口中断 基本概念 对于单片机来讲, 中断是指 CPU 在处理某一事件 A 时, 发生了另一事件 B, 请求 CPU 迅速去处理(中断发生); CPU 暂时停止当前的工作(中断响应), 转去 处理事件 B(中断...
  • 在本系列的第四篇文章中,讲述了串口发送数据基本配置...串口接收数据主要通过中断来实现,中断概念在本系列的第三篇文章中提到过,这里再重复一下:中断有很多的触发方式,你在代码中先要设定好它的触发方式,待...
  • 本实验首先大家自身要有stm32cubemx基础配置,比如GPIO、中断串口、SPI等,FreeRTOS有相关的调用函数基础及调度任务的概念都需要提前理解。单独stm32cubemx或FreeRTOS网上很多,这里就不细说了。 第一步首先打开...
  • (三)stm32之串口通信DMA传输完成中断 2017-02-04来源: eefocus关键字:stm32 串口通信 DMA传输 中断 收藏 评论0 分享到 微博 QQ 微信 LinkedIn 一、DMA功能简介  首先唠叨一下DMA的基本概念,DMA的出现大大减轻了...
  • 前言 cpu在执行程序时,如果有外部中断触发时,如定时器中断、串行总线中断等,cpu停止当前任务从 而转去响应中断处理。...例如,在单片机裸机编程中,需要通过串口(UART) 进行数据通信,对于接收数据情况下,在采用
  • 1)学习过单片机的用户,对串口不会太陌生,在单片机串口编程中,需要用户直接对寄存器以及中断进行控制。 2)在 linux 串口编程中,无论是从 linux 官方直接下载的原生态内核,还是任何厂家提供的linux 内核,...
  •  首先唠叨一下DMA的基本概念,DMA的出现大大减轻了CPU的工作量。在硬件系统中,主要由CPU(内核)、外设、内存(SRAM)、总线等结构组成,数据经常要在内存和外设之间,外设和外设之间转移。例如:CPU需要处理从外设...
  • 串口通信

    2021-03-05 16:19:42
    串口通信的基本原理4.S5PV210串行通信接口详解1:串口原理5.S5PV210串行通信接口详解2:串口周边6.S5PV210串行通信接口详解3:串口中断7.S5PV210串行通信编程实战8.RS232与RS485的区别 前言 S5PV210 1.通信涉及的...
  • UART串口通信 串行接口相关知识点 并行通信、串行通信的概念。 单工、半双工、全双工的概念。 异步串行通信:通信双方在没有同步时钟的前提下,将一个字符(包括特定的附加位)按位进行传输的通信方式。 波特率:每...
  • 3)、串口中断 1、中断的概念 中断触发后,CPU会从主函数调到中断函数中。 外部中断1: 低电平触发:P3.2是0 下降沿触发:p3.2电平:1变为0则触发。 外部中断2同1。 2、中断允许控制 EA是总...
  • STM32使用HAL库实现串口通讯

    千次阅读 2019-02-24 23:52:32
    小小的总结下串口中断接收怎么用:中断发送中断发送的触发流程如何使用发送中断HAL库串口测试程序 理论讲解 查询模式 中断模式 中断的概念 一、对中断的理解。 该文章引用自单片机技术–新浪博客 为了提高CPU的...
  • 基本概念: 通信方式: 串行:一位一位的发送或者接收数据 发送或接收一位数据只需要一个时钟周期 并行:一次发送多位或接收多位数据 一个时钟周期可以发送多位数据 串行:优点:节约硬件资源 缺点:速度慢 并行:...
  • 串口通讯

    2021-01-06 08:11:47
    文章目录一、概念二、寄存器1).串行口控制寄存器SCON和PCONa>.**串行控制寄存器** **SCON**b>.**串行控制寄存器** **PCON**2).串行口数据缓冲寄存器SBUF3).从机地址控制寄存器SADEN/SADDR4).串行口中断相关寄存器...
  • 在接下来的教程中,将会介绍定时器、串口通信等外设的基本使用,而这些外设的使用都要涉及中断,而且中断是单片机或嵌入式开发中一个相当重要的概念。不能掌握中断系统,就无法灵活应用这些外设。一、中断定义CPU在...
  • 最近学习中对寄存器的概念理解很迷惑,I/O口操作/中断/定时器/串口通信四大模块的寄存器应用不太明白,这篇文章,解释的不错,希望帮到各位!  1、21个寄存器介绍 2、位寻址解释  3、具体编程中对寄存器的注意...
  • stm32串口通信

    2020-12-04 10:30:31
    stm32串口通信stm32串口通信程序实例C语言程序里全局变量、局部变量、堆、栈等概念stm32的堆、栈、全局变量的分配地址 stm32串口通信程序实例 ...将stm32f10x_it.c文件的串口中断服务函数修改如下 int i=0; uint8_
  • 这里我们来看看串口通讯 一,计算机通信: 计算机通信是将计算机技术和通信技术相结合,完成计算机与外部设备或者计算机与计算机的信息交换。可以分成两个方式:并行通信和串行通信。 二,并行通信,串行通信 1...
  • 在普通51单片机中,一共只有5个中断,其中2个外部中断,2个定时/计数器中断和一个串口中断。但在STM32中,中断的数量大大增加,同时配置和使用也相对复杂。此篇文章主要讲解STM32外部中断的配置和使用。一、STM中断...
  • 在普通51单片机中,一共只有5个中断,其中2个外部中断,2个定时/计数器中断和一个串口中断。但在STM32中,中断的数量大大增加,同时配置和使用也相对复杂。此篇文章主要讲解STM32外部中断的配置和使用。一、STM中断...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 188
精华内容 75
关键字:

串口中断概念