c语言uint8的数组怎么转换为uint32_MBD开发嵌入式软件的C语言基础

基于模型的设计让软件开发变得简单了,大量没有计算机软件背景的工程师也可以进行软件开发了。

这个现象在汽车行业尤其突出,像我,大学就是发动机专业,工作中也可以通过使用Simulink和代码生成工具开发嵌入式C代码。

但是,我也经常听到有人抱怨:现在的工程师太过依赖Simulink,根本就不知道C语言是怎么回事了,这在我们软件开发中很不方便!

果有此事?果有此事!

不得不说,毕竟你做的是软件开发工作,即便是有了Simulink,我们可以不再写C代码了,基本的C语言的概念还是有必要的。

写到这里,又想起另外一件事……

若干年前,在德尔福中国研发中心B楼的二楼,那位把写代码当成写诗的同事跟我说:三楼来了一位博士,做软件开发,居然连.h文件是做什么用的都不知道!言语中流露出一种鄙夷。

我听后一惊,确实,我们大学毕业,虽说大多都学过C语言,并且不少学生会参加C语言的等级考试,但考试中,相比于.h文件的作用,老师们更喜欢考查i++和++i之间的区别。

在后来的日子里,不管是在德尔福还是在迈斯沃克,面对那些自称有着丰富软件项目经验的应聘者,我都会问这样一个问题:来,说一下,C语言中.h文件是做什么用的?

回答各式各样,通常都答不到点子上。

今天既然要写基于模型设计的C语言基础,不妨就从.h文件的作用开始写起。


1. .h文件的作用

当前技术背景下,工程化的项目已经没有小到一个文件就可以搞定的了,但凡有点规模的项目,基本上都是模块化开发的。模块化开发的时候,对应每个模块,通常会写两个文件,一个.c,另一个.h,比如module1.c,module1.h。.c文件通常用于定义变量和实现函数,而.h文件,除了定义一些公用的宏和类型之外,还会有下面这样的代码:

      extern uint8 var1;

      extern uint8 var2;

      extern void myfunction(void);

这里 var1、var2是全局变量,而myfunction()是全局函数。

.h文件的主要作用是接口,不同模块之间的接口,比如,在module2模块中如果要用到module1里面定义的全局变量或者全局函数,只要在module2.h文件中加这样一样代码:#include “module1.h”。负责module1模块的工程师,如果想调用module2模块提供的全局函数,也只要看一下module2.h即可。

所以我经常跟用户强调不要去读自动生成的代码,如果一定要读,也只要读.h文件即可。

我们在.h文件中经常会看到这样一段代码:

      #ifndef _MODULE1_H_

      #define _MODULE1_H_

然后在.h文件的最末尾有:

      #endif

这样的代码在.c文件中没有,请问,这又是什么意思?

2. 条件编译的作用

如果一个项目有10个模块,module1为其中的一个模块,其他模块在用到module1中定义的全局变量或者全局函数的时候,通常会在自己模块的.h文件中加这样一行代码#include “module1.h”,如果10个模块中的9个模块都有这样一行代码,在预编译的时候,就会重复加载很多次module1.h文件。怎么解决这个问题,可以通过宏_MODULE1_H_来确认是否已经加载过module1.h里的代码,第一次加载的时候,发现没有定义过宏_MODULE1_H_,也就是#ifndef _MODULE1_H_为真,于是定义这个宏,并加载后面的代码,在第二次再遇到#include “module1.h”的时候,已经有_MODULE1_H_这个宏了,#ifndef _MODULE1_H_后面的内容就不再被考虑,直接到#endif。

条件编译,顾名思义,也就是条件成立的时候给后面的内容做编译,条件不成立,则直接跳过。

3. 从C到hex或者exe,都经历了什么?

编译,通常我们会说把C编译成hex文件。其实,这种说法是不准确的,当然,你也可以认为这里的编译是广义上的编译,也就是我们经常在各种编译工具中的IDE界面下看到的Build,没错,Build更准确一些(Build是否应该翻译成“构建”?)。

从C到hex的整个Build过程会经历编译和链接两个阶段。编译,就是把C代码转换成目标代码,通常扩展名为.obj或者.o。目标代码不是最终的二进制码,目标代码中没有代码和变量的地址信息,编译过程中不会提示你某个变量没有定义或者某个变量被多次定义,只要你合理的使用extern关键字。

地址信息是在链接(Link)过程中加上去的,链接器会把编译好的目标代码根据链接文件,把代码和数据分配到合适的存储区域。

4. extern关键字

这个关键字是给编译器看的,编译器遇到这个关键字,就知道这个关键字修饰的那个全局变量或者全局函数不在本文件中定义,而是在其他文件中有定义,所以编译器在编译本文件的时候不会因为没有定义这个全局变量或者全局函数而提示“未定义”之类的错误,当然,如果到了链接阶段,链接器找遍了所有的目标文件,都找不到你所谓的extern变量的话,那个时候,链接器就会提示undefined reference 了。

5. static变量和全局变量的区别

C语言中static变量和全局变量一样,编译、链接之后会分配固定的RAM地址,不同的是,static变量的作用域仅限于文件级别,你没法让module1中定义的staitc变量被module2模块使用,自然,加了static关键字的变量,也不会在module1.h文件中做extern声明。

另外,static除了可以修饰变量之外,还可以修饰函数。 

6. #pragma的作用

翻开C标准,你会发现,#pragma在标准里并没有明确的定义,而是预留给编译器扩展使用,通常,大多数编译器使用如下格式使用:

#pragma mysection begin

Uint8 var1;

Uint16 var2;

#pragma mysection end

需要强调的是这种格式并非标准化的格式,不同的编译器之间可能有所不同,使用前需要查看编译器手册,使用正确的格式。

上面的4行代码是什么作用呢?中间两行显然是定义了两个变量var1和var2,前后的两行#pragma是把这两个变量放到了mysection区域,而这个mysection的地址范围是在link文件中定义的。

当然,也可以使用@将变量分配到某一地址下,不过这种做法在Simulink模型中实现起来非常麻烦,不建议使用。

7. const和volatile关键字的作用

const,常数,不变的,变量前面加这个关键字,我们会称这个变量为常数型变量。编译器在遇到前面加了限定词const的变量之后,如果后续代码中有对这个变量进行写操作的语句,编译器就会报错,因为这个变量是不变的。

volatile,字面意思是易变的、易逝的,通常用来对付那些存储在某些端口的数据,或者某个RAM/ROM区间的变量,这个数据是容易变化的。

而这两个关键字也经常一起使用,比如标定量,标定量显然在程序运行过程中不会变化,而标定量经常被标定工具修改,如果不加上volatile限定词,修改后的数值就可能不会被用到后续的计算中。

8. typedef的作用

曾经有人质问我为什么生成的代码里变量的数据类型不能直接使用C语言中的char、unsigned short、int等基本数据类型,而偏偏要弄一些uint8_T, uint16_T等等?

好吧,我猜测更多友善的用户不去质问,但心里也许有同样的疙瘩,很多编码规范里都规定不准使用C语言的基础数据类型,做这条规定的主要考虑是代码的可移植性,比如,原有的算法运行在16位单片机上,现在性能要求提升,硬件平台升级到32位机,如果代码里全都是基础数据类型,代码的移植将非常麻烦,而如果使用了typedef定义之后的类型,只需要改变typedef即可。

9. 宏定义#define

关于#define,我们在代码中可能会看到这样一些使用方式

    #define MY_MACRO

    #define  K      30

    #define MAX(a,b)  (a>b)?a:b

先说第一种,就是定义了MY_MACRO,MY_MACRO就是一个宏,这个宏就像一个标记,后续代码可以判断是否有这个宏,有的话怎么样,没有的话又怎么样,.h的文件最开始的那段,就是这种用法。

第二种,就是把K定义为30,后续代码中凡是遇到K的地方,编译器编译的时候,就直接替换成30。

第三种,相当于用宏的方式定义了一个函数,跟普通的函数定义不同,在宏定义函数的时候无需定义形参的数据类型,而普通函数的形参是需要定义数据类型的,为什么呢?有兴趣的可以仔细想想,我就不去展开了。

10. 定点数

定点数就是用整数来表达小数,计算机里并没有定点数这种东西,所以我们在定义数据类型的时候,Simulink模型中可能会使用fixdt,而到C代码中,只能看到uint16,uint32等等。定点数中的小数点,是我们假设的,我们会在整数的某个位置假设有一个小数点,那么小数点左边的是整数部分,小数点右边的是小数部分,当然这些计算机并不知道,它依然当成整数运算处理,所以对于编程者来说,需要在运算前后做一些移位对齐小数点的工作,如果你用Simulink模型开发软件,移位对齐工作就不需要你去折腾了,代码生成的时候会被考虑进去。

想到哪写到哪,有些乱,没办法,我也想写的条理一些,但没做到。先写这些吧,如果网友觉得还有其他需要补充的,可以给我留言或者投稿。

推荐阅读

Automotive SPICE和Automotive SPICE评估的那些事

自动代码生成那点事儿

雕虫小技 | Simulink模型开发中的一些自动化方法

结构体变量建模之终极解决方案(续)

为什么一定要做SIL测试

2a7938af4d5b956a2aec6e6b8013a175.png

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值