当前位置:首页 > 工业控制 > 电子设计自动化
[导读]摘要: 以例举的方式,阐述单片机C语言编程中有关标志位的自定义以及对于其当前状态的保存问题。从标志位的理念着手,探讨在单片机C语言编程中自定义标志位的必要性、自定义标志位保存的必要性,以及自定义标志位保存

摘要: 以例举的方式,阐述单片机C语言编程中有关标志位的自定义以及对于其当前状态的保存问题。从标志位的理念着手,探讨在单片机C语言编程中自定义标志位的必要性、自定义标志位保存的必要性,以及自定义标志位保存的几种具体的操作方法。

引言

在现有的教课书及相关文章中,都难得提到在单片机C语言编程中对于自定义“位”的状态进行保存的理念。

当单片机C语言编程中提及“位”的概念时,人们自然会想到状态字PSW中PSW.5的F0与PSW.1的F1两个用户通用标志位。这两个标志位均可参与布尔运算、“位”控操作,也可随状态字PSW一起保存。但是,往往会忽视这一点:在一些特定的情况下,如在C语言编程的中断服务程序中,对状态字PSW中PSW.5的F0与PSW.1的F1这两个用户标志位的操作可能是无效的。如:

void EX1_ISR() interrupt 2 {//外部中断1

static unsigned int tempaddr;//定义接收地址缓存

static unsigned int tempkey;//定义接收数据缓存

unsigned int timecnt;

timecnt=TH1*256+TL1;

TH1=0;

TL1=0;

TR1=1;//定时器1启动

F0=~F0;//取反F0

if(F0) {

tempaddr=tempaddr<<1;

}

else {

tempkey=tempkey<<1;

}

}

以上是一段单片机外部中断1的中断服务程序,乍看似乎没什么问题,仿真调试时也能通过“编辑”。但实际上这是一段错误的程序——其中对“F0”用户标志位的“取反”操作是达不到其预期效果的。因为对“F0”用户标志位的“取反”操作是在中断服务程序中进行的。在进入中断时,C语言自动会保护“中断现场”——将程序指针PC、累加器ACC、状态字PSW等压入堆栈保护起来……直到中断返回时弹出堆栈并覆盖了中断服务时的变值,恢复到压入堆栈之前的原样。因此,状态字PSW中的F0也不例外,如果压入堆栈之前F0是处于逻辑“0”状态,中断返回后还是复原成逻辑“0”状态——不管中断服务程序中怎么取反改变——也就是说,在中断服务程序中试图改变F0之值的操作是有失偏颇的。对于上文例举的那段中断服务程序来说,若F0的初始状态为逻辑“0”,即进入中断服务之前和中断返回之后总是逻辑“0”,那么进入执行“F0=~F0”指令后F0总是逻辑“1”,因而接下运行的是“if(F0)”下花括号中“tempaddr=tempaddr<<1;”指令,而“else”下花括号中“tempkey=tempkey<<1”指令永远也运行不到。所以,若要中断服务程序达到预期的效果——“if(F0)”下花括号中的指令与“else”下花括号中的指令轮番运行,必须设立一个不受中断现场保护等影响的自定义标志位。

1自定义标志“位”的保存

在C语言编程中,自定义标志位的运用概率很大,有的一个程序中就会有好多的自定义标志位,且其中几个可能是必须要保存的。譬如有些控制器件中对一些控制状态进行保持,即使是停电之后再来电了这种控制状态依然能保持不变——这就牵涉到保存问题。

例举2:我们曾搞过一个镭射投影屏幕升降的无线遥控装置。这个以单片机为核心的控制装置与屏幕升降的卷动电机等都安装固定在一个直径不足50 mm的狭长铁桶里面,因此装入或拆卸都非常麻烦。为了一次性成功——避免再次拆卸装入的麻烦,在用C语言编程时我特意多用了一个自定义的标志位——翻转标志位“switch_sign”。因为无线遥控手柄上的向上“▲”、暂停“■”、向下“▼”的键标志都已是做定的,因此,如果由于接线等失误导致按向下“▼”键时投影屏幕向上卷、按向上“▲”键时投影屏幕却向下伸……有了“switch_sign”自定义的标志位就不用怕这些了。相关的C语言程序段如下:

#defineuint unsigned int

#defineuchar unsigned char

uint Decode_addr,Decode_key,addr;

sbit JD1_out=P0^4;//定义继电器1控制输出端

sbit JD2_out=P0^5;//定义继电器2控制输出端

sbit BEEP=P0^3;//定义蜂鸣声响输出

bit bdata switch_sign;//自定义的翻转标志位(应作全局变量定义)

voidTelecontrol_Data() {

……

if(Decode_addr==0x5535) {//地址码核对

if(Decode_key==0x00C0) {//“▲”键码核对

BEEP=1;//蜂鸣声响输出

if(switch_sign) {//翻转标志位

JD1_out=0;//继电器1控制输出端

JD2_out=1; //继电器2控制输出端

}

else {

JD1_out=1;//继电器1控制输出端

JD2_out=0;//继电器2控制输出端

}

}

if(Decode_key==0x0030) {//“■”键码核对

BEEP=1;//蜂鸣声响输出

JD1_out=0;//继电器1控制输出端

JD2_out=0;//继电器2控制输出端

}

if(Decode_key==0x000C) {//“▼”键码核对

BEEP=1;//蜂鸣声响输出

if(switch_sign) {//翻转标志位

JD1_out=1;//继电器1控制输出端

JD2_out=0;//继电器2控制输出端

}

else {

JD1_out=0;//继电器1控制输出端

JD2_out=1;//继电器2控制输出端

}

}

}

if(Decode_addr==0x5D35) {//取反操作纠正时地址码核对

if(Decode_key==0x00C0) {//“▲”键码核对

BEEP=1;//蜂鸣声响输出

switch_sign=~switch_sign; //取反一次翻转标志位

save_data();//调用保存数据子程序

}

}

}

从上面这段遥控数据处理子程序可以看到:在任何时候两个继电器的控制输出端JD1_out与JD2_out至多只能开一个。当遥控“▲”键按下有效时,翻转标志位“switch_sign”的逻辑“0”或逻辑“1”状态将决定两个继电器的控制输出端JD1_out与JD2_out的一开一关,或一关一开;同理,当遥控“▼”键按下有效时也会得到与“▲”键按下相反的类同效果……也就是说,只要改变翻转标志位“switch_sign”的逻辑状态,就能改变两个继电器控制输出端谁“开”谁“关”的控制输出状态。亦即,在同一个遥控按键按下时(如“▲”键按下),标志位“switch_sign”的逻辑状态不同,两个继电器控制输出端谁“开”谁“关”的控制输出状态也将不同。其中的蜂鸣声响提示按键操作是否有效。

投影屏幕升降的动力电机是一个AC 220V的交流电机,图1是电机控制电路的简图。由此可见,当继电器JD1闭合,JD2断开时,电机M中的L1为主绕组,L2为启动副绕组,电机将一个方向运转;当继电器JD1断开,JD2闭合时,电机M中的L1为启动副绕组,而L2为主绕组了,电机将以原来的反方向运转。结合上文,改变翻转标志位“switch_sign”的逻辑状态→改变两个继电器控制输出端谁“开”谁“关”的控制输出状态→改变电机的运转方向→投影屏幕的升降状态。也就是说,改变翻转标志位“switch_sign”的逻辑状态,就可纠正遥控电机运转方向及其投影屏幕的升降状态。将遥控按键与投影屏幕升降的对应关系协调后,必须保存自定义的标志位“switch_sign”当前的逻辑状态,否则,断电后下一次上电复位初始化,又可能要出洋相了。

从例举2的程序中还可以看到,翻转标志位“switch_sign”的取反操作也是用同一个遥控器上的“▲”键来执行的,只是在遥控器的地址编码上动了点手脚——改变了一下地址编码(0x5D35),待操作对应协调后再改回到原来的地址编码(0x5535)。

2保存1个字节来复原自定义标志“位”

自定义标志“位”的保存及其复原有很多种方法,我曾尝试过几种方法。例3是一种保存1个字节来复原1个自定义标志位的方法,具体操作如下:

static unsigned char current_dat;//定义一个通用的辅助字节变量

bit bdata switch_sign;//自定义的翻转标志位(应作全局变量定义)

……

switch_sign=~switch_sign;//取反1次翻转标志位

if(switch_sign) {//判断switch_sign是逻辑“1”还是逻辑“0”

current_dat=0xA5;//对通用的辅助字节变量赋值

}

else {

current_dat=0x00;//通用的辅助字节变量

}

save_data();//调用保存数据子程序

BEEP=1;//蜂鸣声响输出

以上程序是:取反翻转标志位“switch_sign”后,若“switch_sign”为逻辑“1”状态,就给通用的辅助字节变量“current_dat”赋值“0xA5”(当然,这数据由你自己随意定);若“switch_sign”为逻辑“0”状态,则给通用的辅助字节变量“current_dat”赋值“0x00”(这数据也是自己随意定的,只要与前面那个不一样就是了),然后调用保存数据程序将通用的辅助字节变量“current_dat”保存起来。这样,即使断电了翻转标志位“switch_sign”的当前状态已间接地被通用的辅助字节变量“current_dat”保存起来了……当下一次上电复位初始化时,应先将保存的辅助变量“current_dat”的数据读出来,并还原成翻转标志位“switch_sign”相应的逻辑状态。上电初始化时若从存储处读出的数据是“0xA5”,则将翻转标志位“switch_sign”置“1”;若读出的数据是“0x00”,则将翻转标志位“switch_sign”置“0”——这就与原来保存时的状态对应起来了。其操作过程如例4:

static unsigned char current_dat;//定义一个通用的辅助字节变量

static unsigned char addr;//自定义地址变量缓冲单元

static unsigned char Rdat;//自定义读数据缓冲单元

bit bdata switch_sign;//自定义的翻转标志位(应作全局变量定义)

……

addr=0x7F6;//给一个原来的存储地址

REEPROM();//调用读取E2PROM的子程序

current_dat=Rdat;//将读出的数据还给通用的辅助字节变量

if(current_dat==0xA5) {//判断读出的数据是否等于“0xA5”

switch_sign=1;//将翻转标志位“switch_sign”置“1”

}

else {

switch_sign=0;//将翻转标志位“switch_sign”置“0”

}

31个字节保存8个自定义“位”

用保存一个自定义的字节变量来复原一个自定义标志位的过程上文已叙述了,接下来阐述1个字节变量保存8个自定义“位”的方案。1个字节变量保存8个自定义“位”的方案很多,例5是其中比较理想的一种:

#defineuint unsigned int

#defineuchar unsigned char

uintaddr;

ucharWdat,Rdat;

uchar bdatacurrent_dat;//在可位寻址区定义unsigned char类型的字节变量current_dat

sbitsign_bit1= current_dat^0;//用关键字sbit 定义位变量来独立访问可寻址位对象中的1位

sbitsign_bit2= current_dat^1;//自定义标志位2

sbitsign_bit3= current_dat^2;//自定义标志位3

……

sbitsign_bit8= current_dat^7;//自定义标志位8

……

void Bit_save() {//自定义标志位保存子程序

addr=0x7F6;//给予存储地址

Wdat= current_dat;//将current_dat赋值给写E2PROM的缓冲单元Wdat

save_data();//调用保存子程序存储current_dat数据

}

void Bit_comeback() {//自定义标志位复原子程序

addr=0x7F6;//给一个原来的存储地址

REEPROM();//调用读取E2PROM的子程序

current_dat=Rdat;

//将读出的数据还给通用的辅助字节变量

}

以上这段程序所阐述的,也许是有关自定义位操作及其保存的一种最简捷的方案了。首先是在可位寻址区定义ucsigned char类型的通用字节变量current_dat,再用关键字“sbit”定义位变量来独立访问可寻址位对象的其中一位。这样将自定义标志位提高到类同于特殊功能寄存器(SFR)中可位访问的方式来操作了——字节变量current_dat中的8个位各自可以独立操作,且其保存或读出复原都只要直接将字节变量current_dat进行保存或读取即可,无须像其他方案那样需要进行逻辑与、逻辑或等的辅助操作。

结语

单片机的C语言编程中不一定都要有自定义的标志

位,但是在某些场合运用了自定义的标志位,会使整个程序显得简洁而明快。当然,对于自定义标志位的保存也是视其具体情况而定——应该说是不得已而为之的。(单片机与嵌入式系统应用 作者: 张春峰 )



来源:HWan13140次

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

随着各大手机品牌推出带无线充电功能的手机,大家对无线充电技术已不再陌生。充电时,只需将手机往无线充电器上一放即可,不需要在凌乱的包里翻出充电器。

关键字: 手机 无线充电器 方案

摘要:分布式光伏项目因投资收益率较高,目前正处于快速发展的阶段。现首先对建设分布式光伏项目的意义及工程流程进行了说明,然后对施工中遇到的主要技术问题进行了分析,最后给出了解决方案,可供设计和工程技术人员参考。

关键字: 分布式光伏 方案 技术

摘要:新建厂站的测控支持直接接入智能远动机的要求,而某些采用非IEC61850通信规约和非IEC103通信规约厂站的测控不支持直接接入智能远动机的要求。过渡阶段,可以通过规约转换器接入智能远动机,最终将测控改造成支持IE...

关键字: 测控 方案 DNP测控

摘要:在智能制造的时代背景下,智能仓储越来越受到制药行业的重视,当传统的"高架库+人工叉车"已无法满足企业进一步的要求时,繁多的智能化方案扑面而来,现根据制药行业的特殊性以及智能仓储的发展现状,对目前适用于制药行业的智能...

关键字: 智能制造 智能仓储 方案

编身边的有不少朋友老家在乡镇,相比北上广深,家里那简直都是土豪式的独栋住宅,有些也是复式的商品房。 不过他们都有一个问题,就是wifi信号不好,只有wifi路由放置的那一层楼有信号,其他楼层wifi信号很弱,网速慢,网...

关键字: Wi-Fi 方案

摘 要:传统仓储管理存在存储不集中,作业流程繁琐,无法进行批次管理和库位管理,导致出现仓库作业效率低下、员工工作量不均衡、库存量大等问题。文中通过建立现代化仓储WMS管理体系,实现了仓储资源共享,降低了企业运营成本, 提...

关键字: 仓储 管理 WMS 规划 方案

在工作中,凡是涉及到产品开发几乎都会实现参数存储功能,一般参数存储会采用如下的存储介质进行。

关键字: MCU 参数存储 方案

随着科学技术的发展,LED技术也在不断发展,为我们的生活带来各种便利,为我们提供各种各样生活信息,造福着我们人类。信息可视化作为当今交通监控指挥中心的主旨,为提高其交通管理水平、提升处突指挥、日常管理效能,强化动态管控,...

关键字: LED 显示 方案

北斗卫星定位系统是由中国建立的区域导航定位系统,该系统由四颗(两颗工作卫星、2颗备用卫星)北斗定位卫星(北斗一号)、地面控制中心为主的地面部分、北斗用户终端三部分组成。从2000年开始,中国陆续发射了四颗“

关键字: 方案 3000 北斗卫星 定位系统
关闭
关闭