嵌入式杂谈整理

时间:2023-01-23 20:16:35

在一个嵌入式论坛上看到一篇好文章,是一个做嵌入式的牛人写得一些嵌入式相关的杂七杂八的知识点,觉得有些内容很好,这里整理其中部分内容做技术积累, 也供大家共同学习。


1一个完整的程序必然至少有三个段, TEXT (正文,也就是最后用程序编译后的机器指令)段, BSS(未初始变量)段,  DATA(初始化变量)段。如果最后的整个程序放在RAM中,那么三个段可以连续放置,但是,如果程序是放置在ROM或者FLASH这种只读存储器中,那么你还需要指定你的其他段的地址,因为代码在运行中是不改变的,而后两者却不同。这些工作都是在链接的时候完成,编译器必然为你提供了一些手段让你完成这些工作。

还是那句话,有操作系统支持的编程屏蔽了这些细节,让你完全不用考虑这些头痛的问题。但是嵌入式开发者没有那么幸运,他们总是在一个冷冰冰的芯片上从头做起.

 

2 利用宏也可以提高代码的运行效率,子程序的调用需要压栈出栈,这一过程如果过于频繁会耗费掉大量的CPU运算资源。比如我们常常用到的对外部IO赋值的操作,你可以写一个类似下边的函数来实现:

void outb(unsigned char val, unsigned int *addr)
{
*addr = val;
}


仅仅是一句语句的函数,却要调用一个函数,如果不用函数呢,重复写上面的语句又显得罗嗦。不如用下面的宏实现。

#define outb(b, addr) (*(volatile unsigned char *)(addr) = (b))


由于不需要调用子函数,宏提高了运行效率,但是浪费了程序空间,这是由于凡是用到此宏的地方,都要替换为一句其代替的语句。开发者需要根据系统需求取舍时间与空间

 

4 在程序调试中,我们往往需要临时注释掉一大段代码,如果这段代码中已经有很多注释,那么用/* */这种方式注释会遇到匹配问题,很难一下将整段代码注释掉,// 这种注释很多编译器不支持,即使支持,把几十行代码用这种方式注释也要花掉你很多时间,有好的编辑器倒是支持用这种方式注释掉大段代码,但并非每个人都有用这样的编辑器。如果用预编译很容易处理这个问题,在你需要注释的代码前和代码后分别加:

#if 0

#endif

一段代码立马从编译器视线中消失,如果再想临时加入,只需要将0改为1即可



5

嵌入式杂谈整理

 

图1 中,RAM内存从地址0x00000000到0x000ffffff 1M范围内的内容全部被调入cache中,现在假定外部设备有新的数据到来并发生了中断,CPU在设定完DMA控制器以后继续其工作,DMA根据设定将1M的新数据装入RAM中并通知CPU新的数据到了。


注意问题来了,现在假定CPU要对新的数据操作了,因为此外部设备的数据被存放在从0x00000000的RAM中,而此段数据又恰好被cache命中,那么CPU将直接访问cache,中的数据,可是cache中的数据并非刚刚得到的新数据。我的一个师弟在调试一个网卡驱动时就遇到这个问题,看上去一切都没问题,代码没有问题,用sniffer从网络上看,数据也确实发到他的网卡,网卡中断级的调试也显示其收到了正确地内容,但是应用层却总是得到是似是而非的东西。真凶就在于cache,CPU并不知道此时cache中的数据已经过时,解决的办法就是在CPU访问异步事件控制的数据前一定要强行刷新cache中的内容,反之,从内存到外部设备搬移数据前一定要回写内存。一般CPU都提供了cache的刷新和回写机制,甚至有的CPU还有cache保护,即强制其不要对某一范围内的内存使用cache机制。