单片机C语言基础入门第五章:语句的组成(分支、循环结构)
前面介绍了单片机C语言编程中表达式和运算符的规范以及规则,但是仅仅有表达式和运算符是远不能满足编程的要求。通常程序在单片机中执行是逐步按照地址的顺序进行的,这样会产生一个问题就是当用户希望程序是按照某种条件来运行的时候就会变得复杂,接下来要向大家介绍一下单片机C语言中的语句的表达,在编程中经常用的语句表达式主要是条件语句以及循环语句。有了这几种控制语句使得在编程过程中用户可以按照自己的逻辑或者条件或者时序条件来设计程序执行的顺序。
单片机C语言的条件语句。
主要包括if语句和switch语句。If语句在单片机c语言中是极为常用的语句,用来判断某些变量(信号)达到预期的值后,再执行满足条件的指令,或者执行不满足条件的指令。If 语句的基本形式有三种:
直接的if语句;
if……else……语句;
if……else if……语句;
下面对这三种语句逐一进行介绍
1.直接if语句,其表达式为if(表达式)语句
直接的if语句的执行过程如下图所示:
从图中可以看到,如果表达式为真的时候才执行if的语句,否则直接跳过执行if以外的语句。在单片机的中断延时程序中可以看到:
void INT0_ISR() interrupt 0 //外部中断服务程序
{
Delayms(100);//延时程序100ms一般用于去除抖动,自己编写的延时程序
If(INT0)//判断INT0的电平信号,如果是1运行线下面的程序,如果为0则跳出服务程序,也就是判断是不是误动作导致的INT0发生变化。
{
……//用户的程序
}
}
2.if……else……语句,一般表示为:if(表达式)语句1 else 语句2
if……else……语句是当表达式为真或者假的时候,都会执行相应的程序语句,这样的好处是当判断表达式(或者信号)的真假的时候都会有相应的动作与之对应,其执行的流程图如下:
在单片机程序的设计中这样的例子也很多,比如下面的一段程序来自STC15系列单片机库函数的timer 初始化程序
u8 Timer_Inilize(u8TIM, TIM_InitTypeDef *TIMx)
{
……
if(TIM == Timer0)
{
TR0 = 0; //停止计数
if(TIMx->TIM_Interrupt== ENABLE) ET0 = 1; //允许中断
else ET0= 0; //禁止中断
if(TIMx->TIM_Polity== PolityHigh) PT0 = 1; //高优先级中断
else PT0= 0; //低优先级中断
if(TIMx->TIM_Mode> TIM_16BitAutoReloadNoMask) return 2; //错误
TMOD =(TMOD & ~0x03) | TIMx->TIM_Mode; //工作模式,0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
if(TIMx->TIM_ClkSource== TIM_CLOCK_12T) AUXR &= ~0x80; //12T
if(TIMx->TIM_ClkSource== TIM_CLOCK_1T) AUXR |= 0x80; //1T
if(TIMx->TIM_ClkSource== TIM_CLOCK_Ext) TMOD |= 0x04; //对外计数或分频
else TMOD&= ~0x04; //定时
if(TIMx->TIM_ClkOut== ENABLE) INT_CLKO |= 0x01; //输出时钟
else INT_CLKO&= ~0x01; //不输出时钟
TH0 =(u8)(TIMx->TIM_Value >> 8);
TL0 =(u8)TIMx->TIM_Value;
if(TIMx->TIM_Run== ENABLE) TR0 = 1; //开始运行
return 0; //成功
}
……//省略下面的部分
}
可以看到if……else……在单片机中的应用。
3.if……else if ……。这样的判断语句的基本格式为:if(表达式1)语句1 else if(表达式2) 语句2 else if(表达式3) 语句3……,其执行的流程如下:
这种结构的好处在于,多个判断表达式和多个执行语句,在数学中这可以表示分段函数的取值,在但片中也通常用于判断电压信号灯具有连续变化的数值信号分段取值的对应不同执行程序的场合。并且这种结构的一个好处是当满足其中的某一个条件的时候,就会执行相应的程序,而不再执行后面的程序,这样可以提高程序运行的效率。
4.有了前面三种if语句的基本组成然后就是if语句的嵌套组成的if复合语句,也就是在if语句中的执行语句中继续嵌入if语句来实现多重if语句的嵌套结构。在这里就不详细介绍了,其用法和基本的if语句相同。
switch语句实现多分支的选择语句,其原理和if……else if……相似,利用多个条件实现多个分支语句的控制,其实也可以从名字看出来switch就是多路开关的意思,即根据变量的取值那然后选择相对应的语句来执行。其表达式如下:
switch(表达式)
{
case 常量表达式1: 语句1
case 常量表达式2: 语句2
case 常量表达式3: 语句3
……
case 常量表达式n: 语句n
default:语句n+1
}
这样会根据表达式的不同常量结果来选择相对应的语句来执行,注意:在这里没有break的时候,执行的顺序会从上往下一直进行,会得到我们并不希望得到的结果,因此在switch语句的case分支语句中增加break来实现真正的分支选择形式,如下:
switch(表达式)
{
case 常量表达式1: {语句1;break;}
case 常量表达式2: {语句2;break;}
case 常量表达式3: {语句3;break;}
……
case 常量表达式n: {语句n;break;}
default:语句n+1;break;
}
这样增加break以后会得到相应的分支语句,执行完后就会跳出switch语句,其break的用法将在后面介绍。
循环语句
1.goto语句,其表达式为goto lable;lable为程序的标号。
goto语句和汇编的Jmp语句类似就是根据程序的标号来进行不会的跳动,结合if语句就构成了循环结构,但是这种语句在单片机C语言中的出现率并不是很高,除非不得已的时候才使用这用语句。
2.while语句,其表达式为while(表达式) 语句,执行的流程如下:
在单片机中百分之百用到的语句,因为在单片机中不管是在带系统的小的任务还是不带系统的应用中,单片机的每个小程序都是在一个死循环中进行的,也就是在固定的程序空间进行执行,因为单片机的程序是在一个while的死循环中进行,一般常用的是while(1),因此经常看到单片机的程序如下所示:
初始化程序;
while(1)//死循环
{
用户程序;
}
While语句中还有do……while语句语句也在单片机编程中经常用到,其表达式分别为:
do
循环体语句
while(表达式)
执行的流程为:
do……while循环常用到模拟一些总线的输出过程中比如SPI,I2C等。
3.for 循环语句, for(表达式1;表达式2;表达式3)语句
一般在常用的是for(循环变量赋初值;循环条件;循环变量增值)语句,在单片机中的死循环中有的时候while(1)会用for(;;)循环来代替,这样也是一个死循环,主要在语句中填写用户的程序即可。其执行的流程如下:
For循环也经常用于模拟SPI、I2C、UART等串口通信的程序中,比如:
void Write_Byte(unsigned char value)
{
unsigned char i;
for (i=0;i<8;i++)
{
if (((value>>(7-i))&0x01)==0x01) ;
SPI_DI=1;
else SPI_DI=0;
SPI_SCL=0;
delay(5); //必须要加延时,否则会因为操作太快而不响应。
SPI_SCL=1;
delay(5);//必须要加延时,否则会因为操作太快而不响应。
}
}
4.break、continue和return语句
break、continue和return都可以使得程序在跳出当前的流程。其中break前面已经在switch语句中涉及,他可以跳出switch语句,进入并执行switch后面的语句。值得注意的是break语句不能用于循环语句和switch之外的任何其他语句,也就是break跳出程序是限制在循环语句和switch语句之中的。然而continue是结束本次循环,也就是跳出循环体下面尚未执行的语句,然后接着进行下一次是否执行循环的判定,不跳出循环体,两者的执行流程如下,以while作为例子。而return是表示从被调函数返回到主调函数继续执行,返回时可附带一个返回值,由return后面的参数指定。return后函数就结束了,后面的语句不再执行(这将在以后的函数中介绍)。
两个while循环中的执行流线个流程如下:
while(表达式1) while(表达式1)
{ {
…… ……
If(表达式2)break; if(表达式2)continue;
…… ……
} }
分支结构的语句和循环语句在单片机的开发过程过程中是及常用到C语言控制语句,也是构成C语言基本函数的重要一部分,因此能够对这两种语句进行熟练的运用会对单片机的开发有着重要的作用。
今天这章的主要内容就是,介绍了分支语句if和switch语句,和循环goto、while、do while、for语句,并介绍了break、continue的区别,以及初步认识了return的功能,谢谢大家,希望大家积极讨论,有什么错误还请各位指正。
单片机简单快速入门篇 第五章 LED的骚操作(一)
前文再续,书接上一回,LED的点亮,把P2.0管脚清0就可以,那么把LED熄灭,也就是把P2.0管脚置1,LED1两边电压都是5V,自然没有电流流过,LED1不亮
软件程序把P2.0置1,即P20=1。当然,作为一个有追求的程序员,程序应该是这样的
那么问题来了,LED亮和灭都学会了,要让LED一亮一灭,程序怎么编呢?So easy,看下面看下面(肚子突然有点饿)
先点亮再熄灭,while(1)负责无限循环,确保一闪一闪亮晶晶,没毛病!下载程序运行。然而。。。十分钟过去了,一小时过去了。。。为什么LED灯一直亮,没有闪烁呢?难道程序错了吗?错是不可能错的,这么简单是不可能错的。
LED灯确实是在闪烁,只不过是以微秒级的时间单位在闪烁,1秒=1000000微秒,理所当然的,肉眼看不出来灯在闪烁,但是细心观察就会发现LED灯比P20=0时暗了一半,因为亮和暗的时间各占一半,也就是说有一半时间是熄灭的,亮度当然少了一半。
看到这个,大家有没有什么想法呢?假如我们把时间分成16,一份亮,十五份灭,亮度是不是就是1/16了呢!如图所示
如果LED灯是白光的,这不就是有了16个等级的亮度,也就是16级灰度,如果分成256级,那就有了256个灰度,如果把800X600个LED灯组成一个显示屏,这不就是一个256灰度的黑白显示屏了吗!再把白色的LED灯替换成三色(RGB颜色)的LED灯,每个颜色分成256亮度等级,又变成了256色的彩色LED显示屏,再继续细分变成24位真彩色,哇塞,发达了,原来彩色显示屏这么简单,赶紧开工厂,发大财,迎娶白富美,从此人生走向巅峰!啪。。。快醒醒,把口水擦一下!
骚年,属于你的白富美(高富帅?)还在上幼儿园,咱先别着急,先把单片机学好。你看,16个等级的亮度就用了16行程序,那要是256级是不是就要256行啊!这程序还能读吗?有没有简单点的方法!这就涉及到延时程序了。比如我要求LED半秒钟亮,半秒钟灭,那么程序就应该是这样的:
LED1=LED_Liang;
延时500毫秒;
LED1=LED_Mie;
延时500毫秒;
假如我不想延时500毫秒了,我要延时100毫秒,或者50毫秒。。。(程序猿有时也是很善变的),怎么办呢?
LED1=LED_Liang;
延时1毫秒(x); //x表示需要延时的豪
//秒数
LED1=LED_Mie;
延时1毫秒(x);
这样就能够快速的延时你所需要的时间了。这个“延时1毫秒(x)”程序怎么写呢?这时候STC的STC-ISP软件闪亮登场!是时候解锁STC-ISP的第一个隐藏姿势了[泪奔]
STC-ISP下载请看第三章,打开软件后,选择软件延时计算器,选择系统频率,用12M晶振的选择12,6M晶振的选择6,定时时长输入1,单位选择毫秒,8051指令选择STC-Y1,STC89系列的选这个,好了,看下面编辑框是不是生成了延时1毫秒的程序呢!如果选择1微秒,程序变成这样
那么,下面我们就可以编写属于自己的延时程序了。首先先新建一个文件,把这两个函数复制进去
然后保存到“源文件”文件夹下,文件名称输入“delay.c”
这是延时1毫秒和1微秒的,如何实现输入时间呢?这就要用到带参数传递的函数了,把Delay1ms()改成Delay_ms(unsigned int ms),函数体里面加一个对ms参数的自减操作,微秒的也同样操作
ms参数每减1,延时1毫秒,直到ms为0时,函数返回,ms用无符号整型,可以输入从0到65535的数字,也就是延时从0~65535毫秒,为什么用无符号数,因为延时输入负数没有意义,虽然定义int型也不会报错,但是延时负100毫秒有意义吗?延时微秒的函数也是同样操作。这里要注意的是“--”符号的位置,在参数前表示先减1,再判断条件,在参数后面,表示先判断条件,再减1,所以只需要记住在前先减1,再后后减1就够用了,“++”也是一样的,这个规则解决不了的语句通通不用,我们不需要写那些高深莫测的语句。调用时填入所需要的整数即可,比如想要延时10毫秒Delay_ms(10)。
好了,delay.c文件写好后添加到工程里,然后就可以编译了。[恐惧],卖糕的!竟然有错误!记住这个错误的句式,表示的是函数没有定义或者没有声明。记不住也没关系,初学者经常都会犯这个的错误,多错几次就能长点心了。
这里是nop()函数没有定义,这个其实是一个空操作,延时最常用的一个操作,它在“intrings.h”头文件,在delay.c文件中包含该头文件即可
编译,通过,好了可以在main函数中延时了
刚刚吃过一次亏,不能这么快就忘了,main函数可跟你delay_ms函数不熟,想要捞过界不要拜码头的吗?先回去写张拜贴过来吧!
OK!那就写吧!先新建一个文件,保存成delay.h文件名,同样保存在源文件文件夹下
这里要注意的有三点,第一点,“#idndef。。。#define。。。#endif”这个是头文件的标准格式,以后每建一个头文件照抄这个格式就行了,简单点来说就是为了避免重复声明。第二,endif后面记得多打几个回车,否则会收到一张黄牌警告。第三,声明的函数记得加分号,通常这些声明函数都是从C原函数直接复制过来的,C文件里的原函数是没有分号的,复制过来容易忘记输分号,编译的时候,你会收到一堆错误。最后,插入“intrings.h”头文件是为了方便使用nop空操作函数,delay.c和delay.h作为常用文件可以复制到其他项目中使用,用到延时的地方通常也会用nop函数,使用前只需插入delay.h头文件就可以了。
好了,main.c文件插入delay.h头文件,编译通过,下载执行,可以看到LED1没秒钟亮灭闪烁一下。大功告成!
不过编译的时候还是产生了一个警告,强迫症患者可能受不了,非要把它去掉,这个其实是delay.c文件里定义了两个函数,delay_ms和delay_us,delay_us没用到,所以编译器警告你不要占着茅坑不拉屎,强迫症患者可以注释掉没用到的函数。也可以不管它。
好了,延时函数有了,LED可以随便你喜欢怎么玩了,喜欢九浅一深还是九深一浅,随你喜欢了
或者
大家可以自设置看看实际运行效果。今天就先讲到这里了。
未完待续。。。
相关问答
单片机 前三 章 哪章比较重要?硬件结构这个章节重要硬件结构这个章节重要
单片机 程序状态字psw是什么意思-ZOL问答单片机程序状态字(ProgramStatusWord,PSW)是指单片机在执行过程中,用于存储当前程序执行状态的字节。它包含了当前指令地址、指令类型、操作数地址、取数寄...
电气自动化基础知识有哪些 - 懂得电:由三个频率相同、电势振幅相等、相位差互差120°角的交流电路组成的电力系统,叫三相交流电。...电气自动化首先要学好电工基础,这是为接下来电...
飞思卡尔9S12XS128 单片机 的中断向量和中断号,在芯片手册里...第一章,1.6ResetsandInterrupts一般都是exception的章节里面,或者看BSP代码相关的.h里面看那些宏即可。工程下面有个文件夹“Includes”面包含文...
飞思卡尔9S12XS128 单片机 的中断向量和中断号,在芯片手册里...这往下定义“/****************interruptvectornumbers****************/”我把它复...把这往下定义“/****************inter...
51 单片机 初学者要挑选什么开发板?-ZOL问答就比如郭天祥要教你做第14章的时钟那你有可能也想来个掉电保护,那么你须要一块...51单片机有点过时,楼主可以申请复杂点的单片机。这个主要看你的基础了,基础...
扶梯价格?二、牵引链条张紧装置和断裂监控装置三、梳齿板保护装置四、围裙板保护装置五、扶手带入口安全保护装置六、梯级间隙照明装置序言前言第一章...
帮个忙在座的朋友,在线等!急急急! 单片机 开发板哪个好? 单片 ...[回答]一、单片机与嵌入式系统概述:包括嵌入式系统概念、吴鉴鹰单片机开发板的使用、应用领域和发展趋势、市场主流单片机性能比较与选型、常用51核单片机...
怎么自制个数据采集系统?分两步:一、自己做一个基于单片机的数据采集系统。二、把你制作的东西,分功能概述、硬件系统、软件系统、展望等几章来讲解。把你做的东西写清楚,就可以了...
电气自动化基础知识有哪些 - Nov梦梦3 的回答 - 懂得原发布者:861866670电气控制基础知识第二章学看电气控制电路图复习电气元件及其图符介绍•电气控制设备图符•电气控制设备图符•电气控制设备图符2...