中断是单片机系统重点中的重点,因为有了中断,单片机就具备了快速协调多模块的工作能力,可以完成复杂的任务。
C语言数组
数组的基本概念
数组是具有相同数据类型的有序数据的组合,一般来讲,数组定义后满足以下三个条件:
- 具有相同的数据类型
- 具有相同的名字
- 在存储器中是被连续存放的
- unsigned char LedChar[] =
- {
- 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
- 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
- };
复制代码 在这个数组中的每一个值都称之为数组的一个元素,这些元素都具备相同的数据类型就是unsigned cahr型,它们有一个共同的名字LedChar,不管是放在RAM里面还是放在Flash里面,它们都是存放在一块连续的存储空间里面的。
数组的下标由0开始
数组的声明
一维数组的声明格式如下:
数据类型 数组名[数组长度];
数组的数据类型声明的是该数组的每个元素的类型,即一个数组中的元素具有相同的数据类型
数组名的声明要符合C语言固定的标识符的声明要求,只能由字母、数字、下划线这三种符号组成,且第一个字符只能是字母或者下划线
方括号中的数组长度是一个常量或者常量表达式,并且必须是正整数
数组的初始化
数组在进行声明的同时可以进行初始化操作,格式如下:
数据类型 数组名[数组长度] = {初值列表};
例如:- unsigned char LedChar[] =
- {
- 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
- 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
- };
复制代码 初值列表里的数据之间要用逗号隔开
初值列表的初值的数量必须等于小于数组长度,当小于数组长度时,数组的后边没有赋初值的元素由系统自动赋值为0
若给数组的所有元素都赋初值,可以省略数组的长度
系统为数组分配连续的存储单元的时候,数组元素的相对次序由下标来决定,就是说LedChar[0],LedChar[2],...,LedChar[15]是按照顺序依次排下来的
数组的使用和赋值
在C语言程序中,是不能一次使用整个数组的,只能使用数组的单个元素。
一个数组元素相当于一个变量,使用数组元素的时候与使用相同数据类型的变量方法是一样的。
由以下几点要注意:
- 引用数组的时候,那个方括号里的数字代表的是数组元素的下标,而数组初始化的时候方括号里的数字代表的是这个数组中元素的总数
- 数组元素的方括号里的下标可以是整形常数、整形变量或者表达式,而数组初始化的时候方括号里的数字必须是常数而不能是变量
- 数组整体赋值只能在初始化的时候进行,在程序执行代码中只能对单个元素赋值
if语句
if语句有两个关键字:if和else,把这两个关键字翻译一下就是:“如果”和“或者”。if语句一共有三种格式
if语句的默认形式
执行过程:条件表达式为真,则执行语句1,否则不执行语句1
if...else语句
- if (条件表达式)
- {
- 语句1;
- }else{ 语句2;}
复制代码 执行过程:条件表达式为真,则执行语句1,否则执行语句2
if...else if语句
- if (条件表达式1)
- {
- 语句1;
- }
- else if (条件表达式2)
- {
- 语句2;
- }
- else if (条件表达式3)
- {
- 语句3;
- }
- else
- {
- 语句4;
- }
复制代码 执行过程:条件表达式1为真,则执行语句1,否则判断条件表达式2,若条件表达式2为真,则执行语句2,否则判断条件表达式3,若条件表达式3为真,则执行语句3,否则执行语句4
switch语句
用if...else语句处理多分支的时候,分支太多就会显得不方便,且容易出现if和else配对出现错误的情况,在C语言中提供了另外一种多分支选择的语句--switch语句,基本语法如下:- switch (表达式)
- {
- case 常量表达式1:语句1;
- case 常量表达式2:语句2;
- case 常量表达式3:语句3;
- default:语句4;
- }
复制代码 执行过程:首先计算“表达式”的值,然后从第一个case开始,与“常量表达式x”进行比较,如果与当前常量表达式的值不相等,那么就不执行冒号后边的语句x,一旦发现和某个常量表达式的值相等了,那么它会执行之后所有的语句,如果直到最后一个常量都没有找到相等的值,那么就会执行default后面的语句。
当找到一个相等的case分支后,会执行该分支以及之后所有的分支的语句,在C语言里面,有一条break语句,作用的跳出当前的循环语句,包括for循环和while循环,同时,它还能用来结束switch语句块。- switch (表达式)
- {
- case 常量表达式1:语句1;break;
- case 常量表达式2:语句2;break;
- case 常量表达式3:语句3;break;
- default:语句4;break;
- }
复制代码 这样子在执行对应case后面分支之后会因为break语句而直接跳出了switch语句,继续执行switch语句后面的语句了
数码管的动态显示
动态显示的基本原理
多个数码管显示数字的时候,实际上是轮流点亮数码管(一个时刻内只有一个数码管是亮的),利用人眼的视觉暂留现象(也叫余晖效应),就可以做到看起来所有数码管都同时亮了,这就是动态显示,也叫动态扫描。
例如,有两个数码管,要显示数字“12”,先让高位的位选三极管导通,然后控制段选让其显示“1”,延时一定时间后,再让低位的位选三极管导通,然后控制段选让其显示“2”。把这个流程以一定速度循环运行就可以让数码管显示出“12”,由于交替速度非常快,人眼识别到的就是“12”这两位数字同时亮了。
将时间控制在10s内就可以实现动态显示了。
[[数码管代码部分#^6-segmentDigitalTubeDynamicDisplay|6段数码管的动态显示]]
数码管显示消隐
”鬼影“的出现,主要是在数码管位选和段选产生的瞬态造成的。
举个例子:在数码管动态显示的那部分程序中,实际上,每一个数码管点亮的持续时间是1ms的时间,1ms后进行下一个数码管的切换。在进行数码管切换的时候,比如从case 5要切换到case 0的时候,case 5的位选用的是“ADDR0 = 1; ADDR1 = 0; ADDR2 = 1;”假如此刻case 5也就是最高位数码管对应的值是0,要切换成的case 0的数码管位选是“ADDR0 = 0; ADDR1 = 0; ADDR2 = 0;”而对应的数码管的值假如是1。又因为C语言程序是一句一句顺序往下执行的,每一条语句的执行都会占用一定的时间,即使这个时间很短。但是当把“ADDR0 = 1”改变成“ADDR0 = 0”时,这个瞬间存在了一个中间状态“ADDR0 = 0; ADDR1 = 0; ADDR2 = 1;”在这个瞬间上,就给case 4对应的数码管DS5瞬间赋值了0。当全部写完“ADDR0 = 0; ADDR1 = 0; ADDR2 = 0;”后,P0还没有正式赋值,而P0却保持了前一次的值,也就是在这个瞬间,又给case 0对应的数码管DS1赋值了一个0。直到把case 0后边的语句全部完成后,刷新才正式完成。而在这个刷新过程中,有两个瞬间给错误的数码管赋了值,虽然很弱(因为亮的时间很短),但是还是能够发现。
搞明白原理后,解决起来就简单了,避免这两次瞬间错误就可以了。不产生瞬间错误的方法是,在进行位选切换期间,避免一切数码管的赋值即可。
方法有两个:
- 刷新之前关闭所有段,改变好位选之后,再打开段
- 关闭数码管的位,赋值过程做好之后,再打开
实施:
- 关闭段:在switch语句之前,加上 P0 = 0xFF;
- 关闭位:在switch语句之前,加上 ENLED = 1;
单片机中断系统
中断的产生背景
在单片机的程序处理中会遇到以下情景:
当单片机正在专心致志的做一件事情的时候,总会有一件或者多件紧迫或者不紧迫的事情发生,需要去关注,有一些需要停下手头的工作马上去处理了,只有处理完了,才能回头继续完成刚才的工作。这种情况下单片机的中断系统就该发挥它强大作用了,合理巧妙的利用中断,不仅可以获得处理突发状况的能力,而且可以使单片机能够“同时”完成多项任务。
定时器中断的应用
定时器和中断不是一回事,定时器是单片机模块的一个资源,确确实实存在的一个模块,而中断,是单片机的一种运行机制。
标准51单片机中控制中断的寄存器有两个:
中断使能寄存器
上表为中断使能寄存器的位分配,下表为中断使能寄存器的位描述
中断使能寄存器IE的0~5位控制了6个中断使能,而第6位没有用到,第7位是总开关。
总开关相当于总闸,也就是说,只要用到了中断,就要写 EA = 1这一句,打开中断总开关,然后用到哪个分中断,再打开相对应的控制位就可以了。
[[数码管代码部分#^DynamicDisplayWithInterruptions|# 采用中断的动态显示]]
在该程序中,有两个函数:
- void InterruptTimer0() interrupt 1
- {
- ;
- }
复制代码 函数前面 void表示函数返回值为空,即中断服务函数无返回值,函数名是 InterruptTimer0,这个函数名在符合函数命名规则的前提下可以随便取,取这个名字是为了方便区分和记忆,而后的 interrupt是关键字,一定不能错,这是中断特有的关键字,另外后边还有一个数字1,是中断查询序列。
表中第二行的T0中断,要使能这个中断那么就要把它的中断使能位ET0置为1,当它的中断标志TF0变为1时,就会触发T0中断了,这时就应该来执行中断函数了,单片机靠的就是中断向量地址来找到这个中断函数的,所以interrupt后面中断函数编号的数字x就是中断向量得出的,它的计算方法是x * 8 + 3 = 向量地址。该值x也就是表内的第一栏内容了,也就是中断函数编号,要用的时候查表直接用就可。
中断函数完成后,每当满足中断条件而触发中断后,系统就会自动来调用中断函数。
中断优先级寄存器
单片机程序有一般紧急的中断,有特别紧急的中断,这取决于具体的系统设计,这就涉及中断优先级和中断嵌套的概念。
中断优先级有两种:
抢占优先级
上表为中断优先寄存器的位分配,下表为中断优先寄存器的位描述
IP这个寄存器的每一位,表示对应中断的抢占优先级,每一位的复位值都是0,当把某一位设置为1的时候,这一位的优先级就比其他位的优先级高了。
比如,设置PT0位为1后,当单片机在主循环或者任何其他中断程序中执行时,一旦定时器T0发生中断,作为更高的优先级,程序马上就会跑到T0的中断程序来执行。
反过来,当单片机正在T0中断程序中执行,如果有其他中断发生了,还是会继续执行T0中断程序,直到把T0中的中断程序执行完毕后,才会去执行其他中断程序。
当进入低优先级中断时,如又发生了高优先级中断,则立刻进入高优先级中断执行,处理完高优先级中断后,再返回处理低优先级中断,这个过程就称为中断嵌套,也称为抢占。
抢占优先级:优先级高的中断可以打断优先级低的中断的执行,,从而形成嵌套。当然反过来,优先级低的中断是不能打断优先级高的中断的。
固有优先级
该表的最后一栏就是固有优先级。
在中断优先级的编号中,一般都是数字越小,优先级越高。
从表中可以看出,一共有1~6共6级优先级,这里的优先级与抢占优先级的一个不同点就是,它不具有抢占性,也就是说,即使在低优先级中断执行过程中又发生高优先级的中断,那么高优先级的中断也只能等到低优先级的中断执行完成后才能得到响应。
抢占优先级和固有优先级的协同,可以使单片机中断系统有条不紊的工作,既不会无休止的嵌套又可以保证必要时紧急任务得到优先处理。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |