`
bliuqing
  • 浏览: 66480 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
最近访客 更多访客>>
社区版块
存档分类
最新评论

嵌入式程序员应该知道的16个问题

阅读更多
【转】http://blog.csdn.net/seraphsky/archive/2008/04/02/2244920.aspx

9、位操作(Bit manipulation) 
嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。 对这个问题有三种基本的反应

1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。

2) 用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。

3) 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:

#define BIT3 (0x1 << 3) (采用宏将数字定义为有意义的BIT3,明确,不易出错,改起来方便)

static int a;

void set_bit3(void) 

{

a |= BIT3;

}

void clear_bit3(void) 

{

a &= ~BIT3;

}



一些人喜欢为设置和清除值而定义一个掩码(待操作位全1,其余位全0的数,对于某个意义靠多位同时表示的最好带上掩码,隔离其他位的影响)同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作,先取反再&是对某位清0的最好操作。



考点:

在嵌入式系统中,时刻要关注移植性,具体的程序中不要出现具体的数字,这些数字都应该define成某个有意义的符号,可读性可移植性都很强,比如

#define BIT(x) (0x1 << (x))

X作为参数可以很方便的对任意位进行操作,意义明确,更改替换方便



10、访问固定的内存位置(Accessing fixed memory locations) 
嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。

在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:

int *ptr;

ptr = (int *)0x67a9;

*ptr = 0xaa55;

A more obscure approach is: ( 一个较晦涩的方法是):

*(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。



在嵌入式系统中,对于大量此类型数据如硬件寄存器应该采用如下方式

typedef volatile unsigned int HARD_REG;

#define REG_NAME  (*(HARD_REG *)ADDR)

即将ADDR强制转换为一个指向HARD_REG类型数据的指针,*HARD_REG为volatile的无符号整型数



11、中断(Interrupts) 
中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展-让标准C支持中断。其代表事实是,产生了一个新的关键字 __interrupt(51即如此)。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

__interrupt double compute_area (double radius) 

{

double area = PI * radius * radius;

printf("\nArea = %f", area);

return area;

}

这个函数有太多的错误了,以至让人不知从何说起了(前提是非操作系统下的中断服务函数):

1)ISR 不能返回一个值(都应该为void类型)。如果你不懂这个,那么你不会被雇用的。

2)ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。

3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额外的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。

///////////////////////////////

另外中断服务程序是运行在内核态的(linux),内核通常是不支持浮点运算的。

http://access911.net/n/doc1.asp?mode=a&aid=4750647

内核中的printk和标准库的printf不一样,前者因为由内核直接实现,不能支持浮点。

在<linux内核设计与实现>的第一章中内核开发的特点一小节里就有比较了内核开发与应用开发的差异。其中一点就是内核编程时浮点数的问题,书中有一句话是:内核编程时浮点数很难使用

因为没有浮点单元,内核要支持浮点必须把内核以soft-float 方式重新编译,其连接所有的库也都要用soft-float 方式编译.

否则另外一种方式使用整数定义浮点类型加浮点预算库完成你的工作,



http://topic.csdn.net/u/20070417/16/a4b56569-228c-4b70-b5ab-30ee61c99a3d.html

如果你的内核里编译进了浮点支持,那么是可以的。要不内核或是模块不能用float或是double内型的变量或函数



在配置内核的时候把浮点模拟器选上,应该是可以支持的,但是速度非常慢。
我曾经遇到过,硬件明明支持浮点运算的FPU,但是编译内核的时候选上了浮点模拟器,结果所有的应用程序的浮点运算速度都非常慢。所以我怀疑要支持浮点只要编译内核的时候选上,对于应用程序不需要怎么关心。

///////////////////////////////



4) 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。



12、符号扩展的代码例子(Code examples)
下面的代码输出是什么,为什么?

void foo(void)

{

unsigned int a = 6;

int b = -20;

(a+b > 6) ? puts("> 6") : puts("<= 6");

}

Vc6.0测试情况

void main(void)

{

       unsigned int a = 6;

       int b = -20;

       printf("unsigned int a + int b = %x\n", (a + b));



}

/*unsigned int a + int b = fffffff2*/

这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ">6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于频繁用到无符号数据类型的嵌入式系统(硬件寄存器的值全部是无符号的)来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics