历史上的今天

今天是:2024年11月13日(星期三)

2019年11月13日 | 单片机C语言的位操作

发布者:不羁少年 来源: 51hei关键字:单片机  C语言  位操作 手机看文章 扫描二维码
随时随地手机看文章

由于PIC处理器对位操作是最高效的,所以把一些BOOL变量放在一个内存的位中,既可以达到运算速度快,又可以达到最大限度节省空间的目的。
在C中的位操作有多种选择。

*********************************************

如:char x;x=x|0B00001000;         /*对X的4位置1。*/

char x;x=x&0B11011111;               /*对X的5位清0。*/
把上面的变成公式则是:
#define bitset(var,bitno)(var |=1<#define bitclr(var,bitno)(var &=~(1<则上面的操作就是:char x;bitset(x,4)
char x;bitclr(x,5)
*************************************************
但上述的方法有缺点,就是对每一位的含义不直观,最好是能在代码中能直观看出每一位代表的意思,这样就能提高编程效率,避免出错。
如果我们想用X的0-2位分别表示温度、电压、电流的BOOL值可以如下:
unsigned char x @ 0x20;                           /*象汇编那样把X变量定义到一个固定内存中。*/
bit temperature@ (unsigned)&x*8+0;      /*温度*/
bit voltage@ (unsigned)&x*8+1;            /*电压*/
bit current@ (unsigned)&x*8+2;             /*电流 */
这样定义后X的位就有一个形象化的名字,不再是枯燥的1、2、3、4等数字了。
可以对X全局修改,也可以对每一位进行操作:
char=255;
temperature=0;
if(voltage)......
*****************************************************************
还有一个方法是用C的struct结构来定义:
如:
struct cypok{
                     temperature:1;               /*温度*/
                     voltage:1;                     /*电压*/
                     current:1;                      /*电流*/
none:4;
           }x @ 0x20;
这样就可以用
x.temperature=0;
if(x.current)....
等操作了。
**********************************************************
上面的方法在一些简单的设计中很有效,但对于复杂的设计中就比较吃力。如象在多路工业控制上。前端需要分别收集多路的多路信号,然后再设定控制多路的多路输出。如:有2路控制,每一路的前端信号有温度、电压、电流。后端控制有电机、喇叭、继电器、LED。如果用汇编来实现的话,是很头疼的事情,用C来实现是很轻松的事情,这里也涉及到一点C的内存管理(其实C的最大优点就是内存管理)。采用如下结构:
union cypok{
                 struct out{
                                 motor:1;               /*电机*/
                                 relay:1;                /*继电器*/
                                 speaker:1;           /*喇叭*/
                                 led1:1;                /*指示灯*/
                                 led2:1;                /*指示灯*/
                              }out;
struct in{
                                none:5;
                                temperature:1;                  /*温度*/
                                voltage:1;                         /*电压*/
                                current:1;                          /*电流*/
                               }in;
                 char x;
};
union cypok an1;
union cypok an2;
上面的结构有什么好处呢?听小弟道来:
细分了信号的路an1和an2;
细分了每一路的信号的类型(是前端信号in还是后端信号out):
an1.in ;
an1.out; 
an2.in;
an2.out;
然后又细分了每一路信号的具体含义,如:
an1.in.temperature;
an1.out.motor;
an2.in.voltage;
an2.out.led2;等
这样的结构很直观的在2个内存中就表示了2路信号。并且可以极其方便的扩充。
如添加更多路的信号,只需要添加:
union cypok an3;
union cypok an4;
。。。。。。。。。。。。。。。
从上面就可以看出用C的巨大好处。
PICC每日一贴。(初谈如何从汇编转向PICC)  
  
小弟不才,特抛砖引玉,与大家共勉。
聊聊如何从汇编转向PICC。
因为HIDE-TECH PICC破解版很多,所以HIDE PICC有比其它PICC有更多的用户,虽然它的编译效率不是最好。最好的是CCS,但没破戒版。。。,不过用HIDE PICC精心安排函数一样可以获得很高的编译效率,还是人脑是第一的。


当然要求你要有C语言的基础。PICC不支持C++,这对于习惯了C++的朋友还得翻翻C语言的书。


C代码的头文件一定要有
#include
它是很多头文件的集合,C编译器在pic.h中根据你的芯片自动栽入相应的其它头文件。


这点比汇编好用。


载入的头文件中其实是声明芯片的寄存器和一些函数。
顺便摘抄一个片段:
static volatile unsigned char TMR0 @ 0x01;
static volatile unsigned char PCL @ 0x02;
static volatile unsigned char STATUS @ 0x03;
可以看出和汇编的头文件中定义寄存器是差不多的。如下:
TMR0 EQU 0X01;
PCL  EQU 0X02;
STATUS EQU 0X03;
都是把无聊的地址定义为大家公认的名字。


一:怎么附值?
如对TMR0附值:
汇编中:MOVLW 200;
MOVWF TMR0;当然得保证当前页面在0,不然会出错。
C语言:TMR0=200;//无论在任何页面都不会出错。
可以看出来C是很直接了当的。并且最大好处是操作一个寄存器时候,不用考虑页面的问题。一切由C自动完成。


二:怎么位操作?
汇编中的位操作是很容易的。在C中更简单。
C的头文件中已经对所有可能需要位操作的寄存器的每一位都有定义名称:
如:PORTA的每一个I/O口定义为:RA0、RA1、RA2。。。RA7。
OPTION的每一位定义为:PS0、PS1、PS2 、PSA 、T0SE、T0CS、INTEDG 、RBPU。
可以对其直接进行运算和附值。
如:
RA0=0;
RA2=1;
在汇编中是:
BCF PORTA,0;
BSF PORTA,2;
可以看出2者是大同小异的,只是C中不需要考虑页面的问题。


三:内存分配问题:
在汇编中定义一个内存是一件很小心的问题,要考虑太多的问题,稍微不注意就会出错。比如16位的运算等。用C就不需要考虑太多。
下面给个例子:
16位的除法(C代码):
INT X=5000;
INT Y=1000;
INT Z=X/Y;
而在汇编中则需要花太多精力。
给一个小的C代码,用RA0控制一个LED闪烁:
#include
void main(){
int x;  
CMCON=0B111;   file://关掉A口比较器,要是有比较器功能的话。
ADCON1=0B110;          file://关掉A/D功能,要是有A/D功能的话。
TRISA=0;              file://A口全为输出。
loop:RA0=!RA0;           
for(x=60000;--x;){;}           file://延时
goto loop;
}
说说RA0=!RA0的意思:PIC对PORT寄存器操作都是先读取----修改----写入。

上句的含义是程序先读RA0,然后取反,最后把运算后的值重新写入RA0,这就实现了闪烁的功能。

(一点经验)如何有效的实时控制LED闪烁。  
  
在很多设计中需要有精彩而实用的LED闪烁来表示设备工作正常与否和工作状态。


在一些实时性要求不高的设计中可以用插入延时来控制LED闪烁。


它的缺点现而易见:1:LED闪烁方式反映慢。2:在延时过程不能干其它工作(中断除外),浪费了资源。3:代码雍长,真正控制LED就几个个指令,其它的延时代码占了99%的空间。


如果用TMR1或TMR2来做一个时钟,上面的种种缺点就可以避免,使得你可以腾出大量的时间做更有效的工作。


下面是用TMR1作时钟的C代码(RB1、RB2、RB3控制LED)示例:
void set_tmr1(){
TMR1L=0xdc;
TMR1H=0xb;       /*设定初值3036*/
T1CON=0B10001;             /*设定TMR1   0.125s溢出一次*/
}
void interrupt time(){
if(TMR1IF){
T1CON=0B10000;            /*关闭TMR1*/ 
TMR1L=0xdc;                  
TMR1H=0xb;                   /*TMR1设初值
T1CON=0B10001;            /*从新设分频比,打开TMR1*/ 
if(s++>8){        /*每S清0*/
s=0;
if(ss++>60)/*每分钟清0*/
ss=0;
}
TMR1IF=0;
return;
}
}unsigned char s;                    /*每0.125S累加1*/
unsigned char ss;                  /*每1秒累加1*/
void main(){
set_tmr1();
........;                             /*设定I/O口,开TMR1中断*/
while(1){
if(...)                              /*判断闪烁方式语句,下同*/
RB1=(bit)(s>4);            /*每1s闪烁一次,占空比50%(调节>后面值可以改变)*/
if(...)
RB2=(bit)(!ss);             /*每1分钟闪烁一次,亮1秒,熄59秒*/
if(...)
RB3=(bit)(s==0 || s==2 || s== 4 || s== 6);      /*每0.25S闪烁一次*/
.........;                            /*其它工作*/

}
这样的框架对于基于要求实时性高的软件查询的程序是很有效的。
在PICC中使用常数指针。  
常数指针使用非常灵活,可以给编程带来很多便利。
我测试过,PICC也支持常数指针,并且也会自动分页,实在是一大喜事。
定义一个指向8位RAM数据的常数指针(起始为0x00):
#define DBYTE ((unsigned char volatile *) 0)
定义一个指向16位RAM数据的常数指针(起始为0x00):
#define CWORD ((unsigned int volatile *) 0)
((unsigned char volatile *) 0)中的0表示指向RAM区域的起始地址,可以灵活修改它。

DBYTE[x]中的x表示偏移量。

下面是一段代码1:

char a1,a2,a3,a4;

#define DBYTE ((unsigned char volatile *) 0)

void main(void){
long cc=0x89abcdef;
a1=DBYTE[0x24];
a2=DBYTE[0x25];
a3=DBYTE[0x26];
a4=DBYTE[0x27];
while(1);
}

2:

char a1,a2,a3,a4;

#define DBYTE ((unsigned char volatile *) 0)

void pp(char y){
a1=DBYTE[y++];
a2=DBYTE[y++];
a3=DBYTE[y++];
a4=DBYTE[y];
}
void main(void){
long cc=0x89abcdef;
char x;
x=&cc;
pp(x);
while(1);
}
3:
char a1,a2,a3,a4;
#define DBYTE ((unsigned char volatile *) 0)
void pp(char y){
a1=DBYTE[y++];
a2=DBYTE[y++];
a3=DBYTE[y++];
a4=DBYTE[y];
}
void main(void){
bank1 static long cc=0x89abcdef;
char x;
x=&cc;
pp(x);
while(1);
}
关于BOOL量的一点应用。  
  
  /*bit型变量只能是全局的或静态的,
而有时我门在实际应用中既要改变某“位"变量的值;
又要保证这个函数的独立性;那不可避免的要把
这个函数做成有参函数,可是bit型变量是不能用做参数的;
那该咋办泥?还好!有位段。
看看:*/
/********************************************/ 
union FLAG
{
unsigned char BYTE;
struct 
{
  unsigned char b0:1;
  unsigned char b1:1;
  unsigned char b2:1;
  unsigned char b3:1;
  unsigned char b4:1;
  unsigned char b5:1;
  unsigned char b6:1;
  unsigned char b7:1;  
}bool;
};
/********************************************/
union      FLAG               mode;
#define    auto_bit         mode.bool.b0
#define    cool_bit         mode.bool.b1
#define    dar_bit         mode.bool.b2
#define    fan_bit         mode.bool.b3
#define    heat_bit         mode.bool.b4
#define    swing_bit     mode.bool.b5
#define    bed_bit        mode.bool.b6
#define    time_bit         mode.bool.b7
/********************************************/
void mode_task(in_mode)
union FLAG *in_mode;
{
in_mode -> bool.b0=1;
in_mode -> bool.b5=1;
/*也可这样写
in_mode -> BYTE|=0x21;*/
}
/********************************************/
void main(void)
{
  mode.BYTE=0X00;
  while(1)
  {
   mode_task(&mode);
  }
}
/********************************************/
这样写多爽!
这里涉及了结构,联合,位段,及指针;可得先把基础概念搞清楚!

   用PICC写高效的位移操作。  
  
  在许多模拟串行通信中需要用位移操作。
以1-W总线的读字节为例,原厂的代码是:
unsigned char read_byte(void)
{
unsigned char i;
unsigned char value = 0;
for (i = 0; i < 8; i++)
{
  if(read_bit()) value| = 0 x 01<  // reads byte in, one byte at a time and then
  // shifts it left
  delay(10); // wait for rest of timeslot
}
return(value);
}
虽然可以用,但编译后执行效率并不高效,这也是很多朋友认为C一定不能和汇编相比的认识提供了说法。

[1] [2]
关键字:单片机  C语言  位操作 引用地址:单片机C语言的位操作

上一篇:PIC单片机开发板之矩阵键盘扫描(if()else嵌套方式)
下一篇:pic单片机控制电机启动、转动、停止C语言程序

推荐阅读

对于今年五月份刚上任的意法半导体CEO Jean-Marc Chery来说,现在正在面临一个巨大考验:一方面,在过去十几年一直推动半导体产业高速发展的智能手机产业步入平缓;另一方面,物联网、AI和智能汽车等新兴应用蓄势待发;再者,中美之间的贸易纠纷让给整个电子产业带来了更多的不确定性。 意法半导体总裁兼首席执行官Jean-Marc Chery 在如此繁杂的外部环...
任意波形发生器是越来越普遍使用的一类信号源,如何计量任意波形发生器也是计量单位或部门所关心的事情。2006年发布的国家计量技术规范JJF 1152-2006,给出了详细规范和解释。结合这个规范和新的任意波形发生器(如M8190A)的指标,任意波形发生器主要应该计量的指标如下: 1)幅度特性(幅度范围,最大允许误差,偏置误差) 2)频率特性(频率...
“若中国盼我赢得大选,是对我最大的侮辱”,特朗普曾公开表示。但他万万没想到,他的断供手段有多认真,中国芯片企业站他胜选站得就有多坚定。 从过去几年来看,虽然芯片进口受到限制,但硬币的另一面也让国内的需求市场感受到了供应链体系危机,“国产替代”呼声的日益高涨下国内自主芯片厂商迎来了春天,巨大的发展空间与机遇也吸引着越来越多的企业、...
芯观点──聚焦国内外产业大事件,汇聚中外名人专家观点,剖析行业发展动态,带你读懂未来趋势!集微网报道,中国台湾地区工研院产科国际所预估今年全球半导体市场规模将达5509亿美元,其中台湾地区半导体产业产值将达4.1兆元新台币(约合1473亿美元),约占全球26.7%的份额。这一数字足以显示台湾地区半导体产业的举足轻重。那么台湾地区在全球半导体产业...

史海拾趣

问答坊 | AI 解惑

仪陇153单片机实现灯的渐亮控制,请帮我看下出了什么问题?

以下是我编的用em78p153单片机控制led灯渐亮程序,没有发现有什么错误的地方,软件仿真过了。可是烧录后用万用表接p5口发现表的指针在3-4v间摆个不停。实在找不出软件有什么问题了。 会不会是153的ic不适合用于这样的控制啊?头疼。 R17 EQ ...…

查看全部问答∨

ASIC Verification Engineer

ASIC Verification Engineer LSI上海研发中心高薪诚聘通讯存储领域人才,薪水待遇优厚,部分人员有出国培训机会。(部门内部推荐,成功机会更大) 有意者请将中英文简历发送至:asic_tapeout@hotmail.com ASIC Verification Engineer DESCRIPTION ...…

查看全部问答∨

有人用过EP9307-II的板子吗?

如题,刚开始学,想请教一下。…

查看全部问答∨

WinCE下取得硬件唯一标识

首先问一下,硬件唯一标识是不是就是指IMEI号。 如果硬件唯一标识是指IMEI号,那么在WinCE5.0下如何取得呢,我找了资料了,很多人说用“lineGetGeneralInfo”这个方法,但是我在WinCE5.0上试了,首先WinCE5.0的SDK中没有CellCore.lib,我下载了WM ...…

查看全部问答∨

MSP430USB仿真器制作全套资料

在网上看见的,赶紧转发过来,以前一直想自己做一个,后来一连上了好几个项目,没有时间来玩了,其实工作了的朋友可以找利尔达买,透露个小秘密他们是可以讲价的,喊价400,买一送一,实际上和淘宝上价格差不多了,但是他们有技术支持和服务的,比 ...…

查看全部问答∨

请教版主:STM32的USB启动时序

使用芯片:STM32F103 我看参考手册的驱动部分要求需要等待一定时间,但是库代码没加延时(见以下红色部分)。我算了下我的系统不满足这个延时,但USB模块启动无异常,是不是根据自己时钟加上一个满足规格的延时更稳定一点? 谢谢版主指点! ...…

查看全部问答∨

DSP的地址线和数据线有相应的寄存器吗?

DSP的地址线和数据线有相应的寄存器吗?如果没有,那么怎么写数据呢? 另外,我在一本书上见到用DSP的低8位数据线连接LCD的数据线,用DSp的8-12位做LCD的控制线,不明白该怎样控制,是何道理,各位高手帮我解决一下吧,我是初学者,这些问题困扰我 ...…

查看全部问答∨

谁有IIC通信成功的程序

谁有IIC通信成功的程序,借我看看…

查看全部问答∨

这是什么元件?

这是什么元件? [ 本帖最后由 王阿东 于 2012-5-3 20:10 编辑 ]…

查看全部问答∨

感觉 【stm32/stm8】这块越来越垃圾

不知道为什么,感觉 【stm32/stm8】这块越来越垃圾 唉。。。…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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