Arduino程序main函数结构分析

Arduino程序main函数结构分析

在做arduino开发的时候,很多人直接使用的是Arduino IED,打开IDE的时候,我们看到的是这样的界面:

Arduino程序main函数结构分析

我们会看到setup和loop函数,还有几句注释,提示我们把运行一遍的程序放在setup中,把重复运行的部分放在loop中。但是,好多人都疑问main函数呢?程序的入口函数呢?

这就需要我们来好好看看arduino的framework了,先看一下framework的部分程序的位置:

Arduino程序main函数结构分析

其实main函数存在于Arduino核心库中,且仍然是程序的入口。在Arduino核心库中可见main.cpp文件:

Arduino程序main函数结构分析

这个main()就是整个arduino程序的入口。

进入main函数之后,进行了两项初始化工作。init和initVariant。所有单片机共同的初始化部分放在init中,将不同型号单片机的初始化部分放在initVariant中,目前initVariant函数是空函数,是为以后可能的升级预留的。

init()

这个函数的实现是在wiring.c文件中。具体可看该文件:

Arduino程序main函数结构分析

先普及个知识点:

这个里面用到了很多sbi()和cbi()函数?这函数啥意思呢?

其实就是AVR里面的几个简单的汇编指令。

sbi():sbi p,b 置位i/o 位,例如:sbi(DDRB,7);等价于DDRB |= 0x80;

cbi():cbi p,b 清零i/o 位,例如:cbi(DDRB,7);等价于DDRB &= ~0x80;

init函数初始化了所有定时计数器,还初始化了AD转换模块,最后将管脚0和1于bootloader断开,这样0,1号引脚能作为普通数字引脚使用了。

init函数中还包含了大量的预编译命令来判断设备中是否有某个硬件,从而决定是否初始化。

在init函数中,将所有定时计数器的频率都设为cpu频率的64分频。将0号定时计数器用来记录系统运行时间,并对其他依赖时间的函数提供支持,比如millis,micros,delay等函数。所以在开发中timer0其实已经被系统占用了,就不要在用了。也将其他定时计数器全都设定为相位修正(phase correct)PWM模式。

Arduino是如何通过timer0来精确计时的呢?

arduino用两个变量来记录时间,用timer0_millis来记录毫秒数,用timer0_fract来记录微秒,这里比较巧妙后面解释。假设每过a毫秒b微秒0号计数器溢出中断,则将a毫秒累加到timer0_millis上,b微妙累加到timer0_fract上,当timer0_fract累加到1毫秒时,则将timer0_millis加1。

timer0_millis是unsigned long类型的,4个字节,最多能记录4294967295毫秒,49天。而timer0_fract是unsigned char类型,只有1个字节,并不能记录下1000个微秒,怎么办呢?实际上,timer0_fract记录的是微秒数除以8。因为如果晶振为16MHz的话,那么每1毫秒24微妙中断一次,晶振为8MHz的话,每2毫秒48微妙中断一次,将微秒数除以8并不会损失精度。

setup()

控制器通电或者复位后,就会开始执行setup()函数中的程序,并且程序只会执行一次。通常会在setup()函数中完成Arduino的初始化设置,如配置I/O口状态和初始化串口。

loop()

setup()函数执行完毕后,就会执行loop()函数中的程序,并且loop()函数是一个死循环,里面的程序会不断的执行。通常loop()函数中完成主函数功能。

serialEventRun

Arduino程序main函数结构分析

这是串口事件。主要实现位置在这里

Arduino程序main函数结构分析

每个if中,先判断串口缓冲区是否有数据,再判断用户是否定义了SerialEvent函数,如果都为真的话,则调用用户定义的SerialEvent函数。

SerialEvent函数的属性为:

void serialEvent() __attribute__((weak));

也就是可以定义也可以不定义他,如果不定义这个函数,编译器将会给这个函数指针赋0。

以Serial0为例。假如Serial0_available()函数定义了的话,那么在第三个判断条件中调用这个函数不会有问题,如果未定义的话,那么不会进行第三个判断,不会调用这个函数,也不会有问题。对于SerialEvent函数,如果用户未定义它,那么判断条件为假,不会调用。只有用户定义了它才会调用。

从main函数中可以看到,SerialEvent不是中断实现的,仅仅是在loop后面调用的一个函数,因此要使用它的话,就不能在loop中再写一个死循环了!SerialEvent要等到每次loop执行完了才会执行,因此速度会比较慢,尤其是在loop中用delay函数延迟了几秒的,这么长时间的延迟可能会使得串口的接收缓冲区填满。

总结一下:通过以上程序可见,Arduino程序中编写的setup和loop函数,都在main函数中调用了。loop的循环执行,是通过for循环实现的,且每次loop结束后,都会进行串口事件判断,也正是因为这种设计,串口事件不能实时响应。



分享到:


相關文章: