第52节:程序后续升级修改的利器,return语句鲜为人知的用法

发布者:正在搬砖的河马71最新更新时间:2016-03-15 来源: eefocus关键字:升级修改  return语句 手机看文章 扫描二维码
随时随地手机看文章
开场白:

return语句经常用在带参数返回的函数中,字面上理解就是返回的意思,因此很多单片机初学者很容易忽略了return语句还有中断强行退出的功能。利用这个强行退出的功能,在项目后续程序的升级修改上很方便,还可以有效减少if语句的嵌套层数,使程序阅读起来很简洁。这一节要教大家return语句三个鲜为人知的用法:

第一个鲜为人知的用法:在空函数里,可以插入很多个return语句,不仅仅是一个。

第二个鲜为人知的用法:return语句可以有效较少程序里条件判断语句的嵌套层数。

第三个鲜为人知的用法:return语句本身已经包含了类似break语句的功能,不管当前处于几层的内部循环嵌套,只要遇到return语句都可以强行退出全部循环,并且直接退出当前子程序,不执行当前子程序后面的任何语句,这个功能实在是太强大,太铁腕了。

具体内容,请看源代码讲解。

(1)硬件平台:

基于朱兆祺51单片机学习板。

(2)实现功能:

本程序实现的功能跟第三十九节是一摸一样的,唯一的差别就是在第三十九节的基础上,插入了几个return语句,用新的return语句替代原来的条件和循环判断语句。

波特率是:9600 。

通讯协议:EB 00 55 XX YY

加无效填充字节后,上位机实际上应该发送:00 EB 00 55 XX YY

其中第1位00是无效填充字节,防止由于硬件原因丢失第一个字节。

其中第2,3,4位EB 00 55就是数据头

后2位XX YY就是有效数据

任意时刻,单片机从电脑“串口调试助手”上位机收到的一串数据中,只要此数据中包含关键字EB 00 55 ,并且此关键字后面两个字节的数据XX YY 分别为01 02,那么蜂鸣器鸣叫一声表示接收的数据头和有效数据都是正确的。

也就是说,当在 串口助手往单片机发送十六进制数据串: eb 00 55 01 02 时,会听到蜂鸣器”滴”的一声。

(3)源代码讲解如下:

#include "REG52.H"

#define const_voice_short 40 //蜂鸣器短叫的持续时间

#define const_rc_size 10 //接收串口中断数据的缓冲区数组大小

#define const_receive_time 5 //如果超过这个时间没有串口数据过来,就认为一串数据已经全部接收完,这个时间根据实际情况来调整大小

void initial_myself(void);

void initial_peripheral(void);

void delay_long(unsigned int uiDelaylong);

void T0_time(void); //定时中断函数

void usart_receive(void); //串口接收中断函数

void usart_service(void); //串口服务程序,在main函数里

sbit beep_dr=P2^7; //蜂鸣器的驱动IO口

unsigned int uiSendCnt=0; //用来识别串口是否接收完一串数据的计时器

unsigned char ucSendLock=1; //串口服务程序的自锁变量,每次接收完一串数据只处理一次

unsigned int uiRcregTotal=0; //代表当前缓冲区已经接收了多少个数据

unsigned char ucRcregBuf[const_rc_size]; //接收串口中断数据的缓冲区数组

unsigned int uiRcMoveIndex=0; //用来解析数据协议的中间变量

unsigned int uiVoiceCnt=0; //蜂鸣器鸣叫的持续时间计数器

void main()

{

initial_myself();

delay_long(100);

initial_peripheral();

while(1)

{

usart_service(); //串口服务程序

}

}

/* 注释一:

* 以下函数说明了,在空函数里,可以插入很多个return语句。

* 用return语句非常便于后续程序的升级修改。

*/

void usart_service(void) //串口服务程序,在main函数里

{

// if(uiSendCnt>=const_receive_time&&ucSendLock==1) //原来的语句,现在被两个return语句替代了

// {

if(uiSendCnt

{

return; //强行退出本子程序,不执行以下任何语句

}

if(ucSendLock==0) //不是最新一次接受到串口数据,直接退出本程序,不执行return后的任何语句。

{

return; //强行退出本子程序,不执行以下任何语句

}

/* 注释二:

* 以上两条return语句就相当于原来的一条if(uiSendCnt>=const_receive_time&&ucSendLock==1)语句。

* 用了return语句后,就明显减少了一个if嵌套。

*/

ucSendLock=0; //处理一次就锁起来,不用每次都进来,除非有新接收的数据

//下面的代码进入数据协议解析和数据处理的阶段

uiRcMoveIndex=0; //由于是判断数据头,所以下标移动变量从数组的0开始向最尾端移动

// while(uiRcregTotal>=5&&uiRcMoveIndex<=(uiRcregTotal-5)) //原来的语句,现在被两个return语句替代了

while(1) //死循环可以被以下return或者break语句中断,return本身已经包含了break语句功能。

{

if(uiRcregTotal<5) //串口接受到的数据太少

{

uiRcregTotal=0; //清空缓冲的下标,方便下次重新从0下标开始接受新数据

return; //强行退出while(1)循环嵌套,直接退出本程序,不执行以下任何语句

}

if(uiRcMoveIndex>(uiRcregTotal-5)) //数组缓冲区的数据已经处理完

{

uiRcregTotal=0; //清空缓冲的下标,方便下次重新从0下标开始接受新数据

return; //强行退出while(1)循环嵌套,直接退出本程序,不执行以下任何语句

}

/* 注释三:

* 以上两条return语句就相当于原来的一条while(uiRcregTotal>=5&&uiRcMoveIndex<=(uiRcregTotal-5))语句。

* 以上两个return语句的用法,同时说明了return本身已经包含了break语句功能,不管当前处于几层的内部循环嵌套,

* 都可以强行退出循环,并且直接退出本程序。

*/

if(ucRcregBuf[uiRcMoveIndex+0]==0xeb&&ucRcregBuf[uiRcMoveIndex+1]==0x00&&ucRcregBuf[uiRcMoveIndex+2]==0x55) //数据头eb 00 55的判断

{

if(ucRcregBuf[uiRcMoveIndex+3]==0x01&&ucRcregBuf[uiRcMoveIndex+4]==0x02) //有效数据01 02的判断

{

uiVoiceCnt=const_voice_short; //蜂鸣器发出声音,说明数据头和有效数据都接收正确

}

break; //退出while(1)循环

}

uiRcMoveIndex++; //因为是判断数据头,游标向着数组最尾端的方向移动

}

uiRcregTotal=0; //清空缓冲的下标,方便下次重新从0下标开始接受新数据

// }

}

void T0_time(void) interrupt 1 //定时中断

{

TF0=0; //清除中断标志

TR0=0; //关中断

if(uiSendCnt

{

uiSendCnt++; //表面上这个数据不断累加,但是在串口中断里,每接收一个字节它都会被清零,除非这个中间没有串口数据过来

ucSendLock=1; //开自锁标志

}

if(uiVoiceCnt!=0)

{

uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫

beep_dr=0; //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。

}

else

{

; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。

beep_dr=1; //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。

}

TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b

TL0=0x0b;

TR0=1; //开中断

}

void usart_receive(void) interrupt 4 //串口接收数据中断

{

if(RI==1)

{

RI = 0;

++uiRcregTotal;

if(uiRcregTotal>const_rc_size) //超过缓冲区

{

uiRcregTotal=const_rc_size;

}

ucRcregBuf[uiRcregTotal-1]=SBUF; //将串口接收到的数据缓存到接收缓冲区里

uiSendCnt=0; //及时喂狗,虽然main函数那边不断在累加,但是只要串口的数据还没发送完毕,那么它永远也长不大,因为每个中断都被清零。

}

else //我在其它单片机上都不用else这段代码的,可能在51单片机上多增加" TI = 0;"稳定性会更好吧。

{

TI = 0;

}

}

void delay_long(unsigned int uiDelayLong)

{

unsigned int i;

unsigned int j;

for(i=0;i

{

for(j=0;j<500;j++) //内嵌循环的空指令数量

{

; //一个分号相当于执行一条空语句

}

}

}

void initial_myself(void) //第一区 初始化单片机

{

beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。

//配置定时器

TMOD=0x01; //设置定时器0为工作方式1

TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b

TL0=0x0b;

//配置串口

SCON=0x50;

TMOD=0X21;

TH1=TL1=-(11059200L/12/32/9600); //这段配置代码具体是什么意思,我也不太清楚,反正是跟串口波特率有关。

TR1=1;

}

void initial_peripheral(void) //第二区 初始化外围

{

EA=1; //开总中断

ES=1; //允许串口中断

ET0=1; //允许定时中断

TR0=1; //启动定时中断

}

总结陈词:

 

我在第一节就告诉读者了,搞单片机开发如果不会C语言的指针也没关系,不会影响做项目。我本人平时做项目时,也很少用指针,只有在三种场合下我才会用指针,因为在这三种场合下,用了指针感觉程序阅读起来更加清爽了。所以,指针还是有它独到的好处,有哪三种好处?欲知详情,请听下回分解-----指针的第一大好处,让一个函数可以封装多个相当于return语句返回的参数。

关键字:升级修改  return语句 引用地址:第52节:程序后续升级修改的利器,return语句鲜为人知的用法

上一篇:第51节:利用ADC0832采集电压信号进行滤波处理
下一篇:第53节:指针让一个函数可以封装多个参数

小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
502 Bad Gateway

502 Bad Gateway


openresty
设计资源 培训 开发板 精华推荐

502 Bad Gateway

502 Bad Gateway


openresty
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

502 Bad Gateway

502 Bad Gateway


openresty
502 Bad Gateway

502 Bad Gateway


openresty
502 Bad Gateway

502 Bad Gateway


openresty
随便看看
    502 Bad Gateway

    502 Bad Gateway


    openresty
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
502 Bad Gateway

502 Bad Gateway


openresty