大家平时进行MCU软件开发时,基本上都会有一个初始化过程(如果没有的话,就不知道该说什么好了)。我们的硬件寄存器、模块的配置、软件数据结构、系统初始状态等,都是需要进行初始化设置的,以便于把控系统的初始运行状态。那么,初始化过程中一般会进行哪些工作呢?
1、初始化延时一段时间
1//伪代码
2#define SYS_INITIAL_DELY (200)
3void SystemInitial(void)
4{
5 Delay_ms(SYS_INITIAL_DELY); //系统延时
6 sLowInitial(); //底层初始化
7 sFucInitial(); //功能初始化
8 //......
9}
10void main(void) {
11
12 SystemInitial(); //系统初始化
13 //......
14}
Delay一下的原因:
首先聊聊对于"等待MCU电源稳定防止跑飞"这个原因,多少有点牵强,因为Delay函数也是一种正常程序运行,如果MCU跑飞Delay函数也是无法缓解的。
如果是为了防止外设初始化失败可能还说得过去,毕竟对于MCU内部外设功能的初始化有时候需要一个稳定的电源,否则容易产生初始化不成功等等异常。
不过根据之前开发MCU的经验来看,主要是MCU与其他模块对接时候的上电运行门限存在差异,都知道大部分芯片都会有一个最低上电启动电压,电压太低芯片无法启动,电压如果在最低电压附近波动容易造成系统不稳定,如果外部芯片与MCU共用同一个电源,MCU上电运行电压门限低,而外部芯片门限高,当MCU已经开始进行配置外部芯片操作,而外部芯片才刚刚开始进入运行初始化,这样会导致MCU对外部芯片配置不成功等问题。
2、比较传统的初始化结构图
解释一下:
上面的结构图采用自底向上的初始化层次,先进行最基础的系统时钟和总线时钟的配置,以及一些CPU相关的底层初始化;对于MCU次底层的是各个外设的初始化比如UART、USB、SDIO等等;然后对于数据结构和模型初始化可以根据具体功能模块进行划分,最后是对EEPROM、FLASH等存储信息进行读取以恢复系统之前的默认参数和状态。
可以根据自身项目具体情况进行设计,比如含有RTOS、GUI等等组件初始化也是需要进行划分与归类的,这里仅仅提供一些思路。
1、直接初始化输出基础类型大小
1void PrintTypeSize(void)
2{
3 printf("Type Size:\n");
4 printf("char : %u\n", sizeof(char));
5 printf("short : %u\n", sizeof(short));
6 printf("int : %u\n", sizeof(int));
7 printf("long : %u\n", sizeof(long));
8 printf("long long : %u\n", sizeof(long long));
9 printf("void* : %u\n", sizeof(void *));
10 printf("float : %u\n", sizeof(float));
11 printf("double : %u\n", sizeof(double));
12 //......Add your printf of your think
13}
解释一下:
这一段代码很简单吧,估计有很多朋友都没有真正在自己所用的平台上打印过,在学习C语言的过程中应该都知道这中间有些数据类型是与平台相关的,如果没有对这些基础类型的长度把控好,估计bug就来找你了.
同时大家在平时的编程过程中也一定要注意数据类型的范围,引起的后果可大可小哦,同时对于数据类型的深入学习可以参考往期文章。
2、字节对齐与顺序
1#include
2#include
3#include
4#include
5/***************************************************
6 * Fuction: 10次运行Operation
7 * Author :(公众号:最后一个bug)
8 ***************************************************/
9#define TEN_OPERATE_TIMES(x) do { x; x; x; x; x; x; x; x; x; x; } while (0)
10/***************************************************
11 * Fuction: 50次运行Operation
12 * Author :(公众号:最后一个bug)
13 ***************************************************/
14#define FIFTY_OPERATE_TIMES(x) do {\
15 TEN_OPERATE_TIMES(x); \
16 TEN_OPERATE_TIMES(x); \
17 TEN_OPERATE_TIMES(x); \
18 TEN_OPERATE_TIMES(x); \
19 TEN_OPERATE_TIMES(x); \
20 } while (0)
21/***************************************************
22 * Fuction: 答应对应运算op所需时间
23 * Author :(公众号:最后一个bug)
24 ***************************************************/
25#define PRINT_TIME(name, operate, count) do { \
26 unsigned char i = 0;\
27 LARGE_INTEGER StartTime;\
28 LARGE_INTEGER EndTime;\
29 LARGE_INTEGER Freq;\
30 QueryPerformanceFrequency (&Freq);\
31 QueryPerformanceCounter(&StartTime);\
32 for (i = 0; i < count; i++) { \
33 FIFTY_OPERATE_TIMES(operate); \
34 } \
35 QueryPerformanceCounter(&EndTime);\
36 printf("%-8s %7.8f ms\n", name, (EndTime.QuadPart -StartTime.QuadPart )*1000.0f*1000.0f/Freq.QuadPart/ (double)(count * 50.0));\
37 } while (0)
38
39
40/***************************************************
41 * Fuction: 变量定义区域
42 * Author :(公众号:最后一个bug)
43 ***************************************************/
44volatile float fv = 1.0;
45volatile float fv_out = 0.8;
46/***************************************************
47 * Fuction: 测试main函数
48 * Author :(公众号:最后一个bug)
49 ***************************************************/
50int main(int argc, char *argv[]) {
51
52 PRINT_TIME("Mul :", fv_out *= fv, 200);
53 PRINT_TIME("Div :", fv_out /= fv, 200);
54 PRINT_TIME("sin(x):", fv_out = sinf(fv), 100);
55 PRINT_TIME("cos(x):", fv_out = cosf(fv), 100);
56 printf("\n欢迎关注公众号:最后一个bug\n");
57 return 0;
58}
运行结果看一看:
解析一下:
首先这里主要是宏定义和do{}while(0);的结合使用案例,大家可以学习一下这种定义方式。
设计亮点 : 前面两个宏定义的设计大家应该会有疑问为什么不在for循环里面直接cnt累计时间,最后累计好足够的时间不就可以计算每次运算的时间了吗 ?
大家需要注意的是for循环也是由几条汇编语句构成的,如果所测试的运算操作时间非常短,测试的最小时间也不会小于for运算的时间,这样测量误差较大,所以这里构造了多次运算,从而与for语句运算时间不在一个运行时间等级上便可以提高精度。
如果很多小伙伴听得迷迷糊糊,上图!!!!
END
→点关注,不迷路←