静态变量static两种使用方式
一.定义局部变量
// 求阶乘 n!
int fac(int)
{
static int f = 1 ;
f = f * n ;
return f ;
}
main()
{
int i ,n ;
float sum;
scanf("%d",&n) ;
for(i = 1 ;i <= n ;i++)
{
sum = fac(i) ;
}
printf("输出%d!= %f ",n,sum) ;
}
2.这样声明的变量,其存储在程序的bss段,而在bss段在程序中执行时会初始化为0.
// eg:
static int b[5] ;
//则b[0] = 0 ,b[1] = 0等等. 而如果不是这样定义则输出可能是乱码
二.定义全局变量
当定义全局变量只限于本文件使用,而不被其他文件使用,这时可以在定义外部变量的时候加一个static声明。注意定义加了static和不加的外部变量都是存放在静态存储区,而区别就在于上一句的“只限于本文件使用”。
外部变量extern
作用:声明外部变量,扩展它在程序文件中的作用域。即在文件1定义的全局变量int A,可以在文件2中定义 extern A ,便可调用这个文件1中的全局变量。比如在实际具体工程项目中,在定时器文件中定义的全局变量时间int32_t g_iRunTime ,最长可以表示 24.85天,也就是你的终端可持续运行的时间,在其他项目其他文件中,像主函数文件中也需调用用于判断运行时间则就用extern g_iRunTime调用。
一. 只需返回一个值得时候
二. 要求返回多个值得时候
// 函数fun返回了三个值,通过return1个,和数组s[2]两个,以达到要求
int fun(int str[2]);
main()
{
int s[2] ;//定义一个数组来存放fun函数的返回的a b 之间最大值,最小值
scantf("%d%d",&a,&b);
if(fun(a,b,s))
{
printf("大的%d,小的%d",a[0],a[1]);
}
else
printf("相等");
}
int fun(int a ,int b ,int str[2])
{
if(a > b)
{
str[0] = a ;
str[1] = b ;
return 1 ;
}
if(a < b)
{
str[0] = b ;
str[1] = a ;
return 1 ;
}
else
return 0 ;
}
需要注意的是,一般原则:在程序设计中,再划分模块时要求模块”内聚性”强,与其他模块的“耦合性”弱。简单理解就是,在封装的各个函数中各有自己独立功能,即功能单一,方便调用,而不能搞的太乱。对于全局变量需限制使用。
在项目工程当中,或者简单的C语言代码当中,循环使用的非常普遍,掌握循环的各个形式非常重要,这里简单的介绍下循环控制中continue 与 break的区别。
利用代码体现:
//简介循环控制continue与break的区别
main()
{
while(表达式一)
{
if(表达式二)
{
break ; //跳出循环,也即跳出while语句转执行下面的语句
}
}
while(表达式一)
{
if(表达式二)
{
continue ; //跳到条件限制,也即跳到while语句判断中
}
}
}
如上面清晰易懂,在写代码当中或者看懂下载得源代码中,如果不是在主函数main中出现的while(1),其他函数中出现while(1),都必然存在一个break语句,在首先学习看懂源代码的任务中,这点比较重要。(看来这点只适合初学者啊,只要有点经验都知道这些浅显的但关键的知识点)
核心思想:通过消息队列机制,在MAIN函数中,通过消息传递任务来执行。 当某一任务需要执行的时候,通知消息队列,主程序去执行。 主程序在函数中解析消息队列,看有无任务。
typedef struct
{
uint8_t *MsgRam; /*队列缓存*/
uint8_t MsgSize; /*队列大小*/
uint8_t MsgCount; /*收到的新消息个数*/
uint8_t MsgWritePoint; /*写指针*/
uint8_t MsgReadPoint; /*读指针*/
}Msg_Fifo;
#define TaskSize (100) /*消息队列尺寸*/
typedef enum
{
Idletask = 0 ;
.... //消息类型,根据项目中的各个需要Cpu处理的模块决定
}
void bsp_Init_Msg_Fifo(void)
{
_MsgTask.MsgSize = TaskSize;
_MsgTask.MsgRam = _MsgRam; /*队列缓存*/
_MsgTask.MsgCount = 0; /*新数据个数*/
_MsgTask.MsgReadPoint = 0; /*读指针*/
_MsgTask.MsgWritePoint = 0; /*写指针*/
}
**至此,**在主函数mian中的while(1)中调用5,6两个函数询检即可。
队列FIFO运用主要是对CPU分配管理:一般的计算机系统只有一个CPU,如果在系统中的多个进程都满足运行条件,这就可以用一个就绪队列来进行管理。当某个进程需要执行的时候,它的进程名就插入到就绪队列的尾端。如果此队列是空的,CPU就立即执行此进程;如果此队列非空,则该进程就需要排在队列的尾端进行等待。CPU总是首先执行排在队首的进程,一个进程分配的一段时间执行完了,又将它插入队尾进行等待,CPU转而为下一个出现在队首的进程服务。如此。按照“FIFO”的原则一直进行下去,直到执行完的进程从队列中删掉。
队列fifo还可用于许多场景,比如串口fifo,多串口运行时的分配。
这部分我主要是针对stm32的程序来使用的,首先配置systick定时器作为系统滴答定时器。缺省定时周期为1ms。实现了多个软件定时器供主程序使用(精度1ms), 可以通过修改 TMR_COUNT 增减软件定时器个数。
这部分还是主要为CPU处理队列信息的使用做服务的。为每个单一任务设置定时器,一旦到了设定的时间就像队列写入一个该任务消息,直到CPU处理为止,定时器主要实现任务调度的问题。当然,风格不同的工程师有着不同的用法!
配置systick定时器,周期为1ms
方法:在core_cm3.h有一个这样的函数uint32_t SysTick_Config(uint32_t ticks),该函数的形参表示内核时钟多少个周期后触发一次Systick定时中断
– SystemCoreClock / 1000 表示定时频率为 1000Hz, 也就是定时周期为 1ms
– SystemCoreClock / 500 表示定时频率为 500Hz, 也就是定时周期为 2ms
– SystemCoreClock / 2000 表示定时频率为 2000Hz, 也就是定时周期为 500us
所以我们一般在定时器初始化函数中调用SysTick_Config(SystemCoreClock / 1000);形成周期性中断函数每一ms执行中断一次,即需要中断就要写了个中断服务程序void SysTick_Handler(void);该函数主要为下面制定软件定时器的计数每隔1ms进行减一操作。
软件定时器的设置
1.定义定时器结构体以及定时器的个数和各个任务定时器的名称
typedef struct
{
volatile uint8_t Flag; /* 定时到达标志 */
volatile uint32_t Count; /* 计数器 */
volatile uint32_t PreLoad; /* 计数器预装值 */
}SOFT_TMR;
/* 软件定时器个数 */
#define TMR_COUNT 5 //自己设定需要多少软件定时器
/*定义定时器名称*/
#define C_TMR_ID (4)
#define C_TMR_ID (3)
#define E1_TMR_ID (2)
#define E_TMR_ID (1)
#define cTMR_ID (0)