历史上的今天
返回首页

历史上的今天

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

正在发生

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

2019-11-13 来源:51hei

由于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一定不能和汇编相比的认识提供了说法。

推荐阅读

史海拾趣

芯旺微电子(ChipON)公司的发展小趣事

芯旺微电子自2012年成立以来,始终致力于高可靠性MCU器件的研发设计。公司凭借其自主研发的KungFu内核架构处理器,迅速在汽车级和工业级混合信号MCU领域崭露头角。随着技术的不断积累和市场的深入拓展,芯旺微电子逐渐从一家初创公司发展成为国内车规级MCU市场的领军企业。

ANADIGICS公司的发展小趣事

随着市场需求的不断变化和技术的不断进步,芯旺微电子不断拓展产品线,覆盖了从8位到32位不同性能的MCU产品。同时,公司还积极研发DSP、数模混合SOC等多元化产品,以满足不同领域的需求。这些产品的推出不仅丰富了芯旺微电子的产品线,也进一步提升了其在电子行业中的竞争力。

American Technical Ceramics (ATC)公司的发展小趣事

芯旺微电子一直将技术创新作为公司发展的核心驱动力。公司拥有一支高素质的研发团队,不断在CPU系统结构、编译器、IDE软件、数模混合设计等领域进行技术创新和突破。这些技术创新的成果不仅提升了芯旺微电子的产品性能和质量,也为其在激烈的市场竞争中赢得了更多的机会和优势。

Holtek(合泰)公司的发展小趣事

近年来,芯旺微电子在资本市场也取得了显著的成果。公司先后完成了多轮融资,吸引了众多知名投资机构的关注和投资。同时,芯旺微电子也在积极推进上市进程,计划通过资本市场进一步拓展业务规模和提升品牌影响力。这些战略布局的实施不仅为芯旺微电子提供了更多的资金支持和发展动力,也为其未来的发展奠定了坚实的基础。

Boyd Corporation公司的发展小趣事

随着公司规模的扩大和市场需求的增长,Boyd Corporation积极拓展全球业务版图。公司在多个国家和地区设立了生产基地和研发中心,以便更好地服务全球客户。通过全球化布局,Boyd Corporation不仅提升了生产效率,还加强了与全球客户的合作关系,进一步巩固了其在电子行业的领导地位。

Daykin Electric Corp公司的发展小趣事

1969年,大金成功开发了一台室外机连接多台室内机的家用多联系统空调。这一创新产品不仅提高了空调的能效比,还满足了大型住宅和办公场所的多样化需求。多联系统空调的诞生,使得大金在家用中央空调领域取得了领先地位。

问答坊 | AI 解惑

PLD设计技巧——消除组合逻辑产生的毛刺

PLD设计技巧——消除组合逻辑产生的毛刺…

查看全部问答>

Linux面试题,看你能得多少分?

一.填空题 1. 在Linux系统中,以 方式访问设备 。 2. Linux内核引导时,从文件 中读取要加载的文件系统。 3. Linux文件系统中每个文件用 来标识。 4. 全部磁盘块由四个部分组成,分别为 。 5. 链接分为: 和 。 6. 超级块包含了 和 等重要的 ...…

查看全部问答>

stepldr引导eboot不成功

     根据datasheet (nand flash controller)The S3C2416 is equipped with an internal SRAM buffer called ‘Steppingstone’. This supports NAND flash boot loader. When you use IROM boot and select nand flash as boo ...…

查看全部问答>

求ADS8364在FPGA数据采集系统中的AD转换控制的verilog或VHDL程序代码

求ADS8364在FPGA数据采集系统中的AD转换控制的verilog或VHDL程序代码,急需希望大家帮助!…

查看全部问答>

ARM仿真(裸机实现多任务)

用ARM裸机实现多任务 采用定时器0来产生一个时间片timer,1timer=1us 接着达到某个时间(timer的n倍)时某个任务执行 然后每个任务都这样等待某个时间后执行 定时器0产生中断,代码如下 /****************************************************** ...…

查看全部问答>

为什么我在evc中输出只有emulator可选,没有设备可选?

为什么我在evc中输出只有emulator可选,没有设备可选? 这样我就没法把程序download到wince设备上了?为什么? 是不是少装了什么?请各位帮帮忙.…

查看全部问答>

[100分] Wince下修复SqlCE文件报错. 在线等.

private void btnRepair_Click(object sender, EventArgs e)         {             SqlCeEngine engine = new SqlCeEngine(string.Format(\"Data Source = {0};password ...…

查看全部问答>

求一完整程序~关于EVC4.0下的UDP编程

求一完整程序~关于EVC4.0下的UDP编程!有的大大请加我QQ 41368886 还有哪个大大有Pocket PC 2002一Win32 (WINCE X 86) Debug 的开发环境也发给我!  求~~跪求这两个 …

查看全部问答>

寻迹小车

各位大哥,跪求:寻迹小车原理图和程序,要详细的谢谢了!!…

查看全部问答>