XiLinx SDK FreeRTOS学习记录(六)——一些总结

写在前面

好久没写blog了,最近真的好忙,这篇blog就总结一下最近在FreeRTOS里摸爬滚打写bug然后debug的经历吧。

定时器

首先是关于定时器的一个妙用,定时器在初始化之后是可以暂停的,用xTimerStop()函数,可以让这个定时器暂停计时,但是注意要用在vTaskStartScheduler()函数之后,因为这个函数会激活所有的定时器和任务,激活之后再暂停。下面是xTimerStop()函数的用法,第一个参数是我们要暂停的定时器的句柄,第二个参数是等待的时间,单位是ticks,就是说等待这段时间之后定时器暂停。

BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );

对于这样的一个场景,比如说我们需要在某一时刻或者代码运行到某一行的时候,开始计时在一段时间之后执行一个函数,此时我们就可以使用xTimerChangePeriod()函数来更改我们已经初始化好的一个定时器,并将其重新激活,非常方便。下面是xTimerChangePeriod()函数的用法,第一个参数是要修改的定时器的句柄,第二个参数是新的定时时间,第三个参数是等待多久将其激活。

BaseType_t xTimerChangePeriod( 	TimerHandle_t xTimer,
 								TickType_t xNewPeriod,
 								TickType_t xTicksToWait );

任务中的全局变量以及任务外的全局变量

这个问题是在实际调试代码的过程中发现的,首先是发现了一个全局变量,在一个.c文件中定义并赋值,但是在另一个.c文件中extern了以后它却被置为0了,很是抽象,当时觉得是FreeRTOS的全局变量操作起来可能会有一些优化什么的,可能被优化掉,但是想到初始化一个队列肯定不会被优化掉,所以就在第一个.c文件中初始化了一个队列,然后第二个.c文件中extern它,并且将其中的数receive过来,但是神奇的事情发生了,当我的代码运行到队列receive时它卡死了,FreeRTOS报错队列定义错误,队列不存在,很是离谱,于是我意识到这个优化不仅仅针对我们自己定义的全局变量,连FreeRTOS的队列都会被优化,此时我想到一个问题,我先后调用这两个.c文件,虽然是同一个任务,但是是在两次不同的执行当中,第一次执行这个任务时调用了第一个.c文件,然后任务结束了以后FreeRTOS可能就将内存释放了,然后第二次再调用它的时候就出现了变量未被定义这样的问题。

解决方案:将全局变量定义在创建任务的.c文件当中,然后在两次执行调用的不同的.c文件中都extern这个变量,问题解决。

结构体中的字节对齐方式

这个跟FreeRTOS没有什么关系了,纯粹的就是C语言结构体的知识,这个具体的对齐规则我也不太懂,大概来说就是编译器有一套规则来对齐你的结构体,使得它实际占用的空间要比你实际定义的空间要大一些,但是编译速度会快一些(个人理解)。

但是这就会造成一个问题,当我们定义一个结构体,它当中的成员的地址是编译器帮我们编译好的,举个例子,定义如下结构体:

typedef struct my
{
    char a;
    int aa;
} my;

当我们获取aa的值或者我们给aa赋值的时候我们会使用my.aa的操作,这个其实就是编译器帮我们编译好的地址,但是按照我们通常的理解,它的首地址就应该是my[1],但是事实并非如此,或者说不一定如此,编译器会根据它自己的规则来对齐字节,我们认为这个结构体的大小是5字节,它实际有可能占了8字节,我们认为aa的首地址应该是my[1],其实有可能是my[4],这就是编译器对齐字节的结果。

于是,当我们尝试用一个完整的帧结构,使用memcpy对结构体赋值的时候,就有可能出现结构体中的成员的地址跟我们帧结构中成员的地址不匹配的结果,造成我们读取到的数据是错误的,因此,如果我们使用这种方式给结构体赋值的时候,最好加上这样一串代码__attribute__((packed)),它的作用就是告诉编译器按照正常方式紧凑对齐字节,这时4字节就是4字节,1字节就是1字节,就不会出现问题了。

typedef struct my
{
    char a;
    int aa;
} __attribute__((packed)) my;