历史上的今天
今天是: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<
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语言的书。
上句的含义是程序先读RA0,然后取反,最后把运算后的值重新写入RA0,这就实现了闪烁的功能。 在一些实时性要求不高的设计中可以用插入延时来控制LED闪烁。
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寄存器操作都是先读取----修改----写入。
(一点经验)如何有效的实时控制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. 在Linux系统中,以 方式访问设备 。 2. Linux内核引导时,从文件 中读取要加载的文件系统。 3. Linux文件系统中每个文件用 来标识。 4. 全部磁盘块由四个部分组成,分别为 。 5. 链接分为: 和 。 6. 超级块包含了 和 等重要的 ...… 查看全部问答> |
|
根据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裸机实现多任务 采用定时器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编程!有的大大请加我QQ 41368886 还有哪个大大有Pocket PC 2002一Win32 (WINCE X 86) Debug 的开发环境也发给我! 求~~跪求这两个 … 查看全部问答> |




