工作中的研发错题本:
1、宏定义
提示:这里描述项目中遇到的问题:
1.1 enum
合理运用枚举结构体,在最下面加入NUM,即枚举的个数
1.2 union
同一联合体,不同字段使用相同地址
union内成员可以共用地址,frambuffer八个字节等于struct内部八个字节,也可以访问某些具体位(注意默认的是大端还是小端)很好用!!
1.3 struct
①第一种
typedef struct{
}abcType;
abcType包括了struct
②第二种
如果结构体里面需要引用本结构体
typedef struct bb
{
Struct bb *qwr;
}abcType
因为在引用的时候,abcType还未定义但是struct bb已经定义了,所以里面需要用struct bb;
③第三种
若没有typedef的话,在定义的时候
struct bb
{
uint8 aa;
};
—**
2、CMOS与TTL电平
2.1 特点:
TTL电平的高电平大于2V,低电平<=0.8V
CMOS高电平接近电源电压,低电平接近0v
2.2 关系区别
CMOS是电压控制器件,输入总抗大,对于干扰信号捕捉能力强,所以不能悬空,需要接一个上拉或下拉电阻,给一个恒定的电平。
对于与门,与非门的多余输入端要接高电平,或门或或非门的多余管脚接低电平。
TTL悬空的时候相当于输入端接入高电平
3、左移右移
例子:对于8位的IO寄存器0001 1000,若想第三个IO口的电平置高,可以另外取0x01<<3,左移三位后变成0000 0100,然后或上原来的值,变成了0001 1100.达到了第三位拉高的目的
#define REG_Write(reg,mask,value) (reg) = (((reg) & ~(mask)) | (value))) /!< 向寄存器某些位写入值/
#define REG_Read(reg,mask,shift) (((reg)&(mask))>>(shift)) /!< 读寄存器某些位值/
#MASK寄存器中的掩码,shift是第几位
4、MOS管
G端代表栅极,gate,控制S端source源极与D端drain漏极导通与否,当mos管为N沟道增强型场效应管时G端通电压导通,去掉电压不导通,当mos管为P沟道增强型场效应管时反之
上面朝内是N mos管,朝外为P mos管
三极管
向外NPN,向内PNP
PNP,低电平导通;NPN高电平导通
看箭头怎么流,就给相应的电平,顺应着箭头方向给电平
看箭头方向,朝外为NPN,B到E箭头方向有小电流时,则C到E会有大电流;
看箭头方向,朝内为PNP,E到B箭头方向有小电流时,则E到C会有大电流;
.
4、STATIC变量声明
.
①用static声明局部变量
局部变量指在代码块{}内部定义的变量,指令执行到变量定义处时才给变量分配存储单元,跳出代码块时释放内存单元(生命期)
用static声明局部变量时,则改变变量的存储方式(生命期),使变量成为静态的局部变量,即编译时就为变量分配内存,直到程序退出才释放存储单元。
②用static声明外部变量
外部变量,编译时分配内存,程序结束时释放内存单元。作用域很广,整个文件都有效甚至别的文件也能引用它。为了限制某些外部变量的作用域,使其只在本文件中有效,而不能被其他文件引用,可以用static关键字对其作出声明。
Static函数
使该函数只能在本文件内生效,在其他文件来看是不可见的,又称静态函数。好处:无需担心其他文件中函数同名的影响。如想在其他文件中引用该函数,通过关键词extern来定义。
5、i++与++i:
提示:这里填写问题的分析:
uint8 num[5]={0,1,2,34,5};
uint8 i = 3;
uint8 test1 = num[i++];
printf("test1 = %d,i = %d\n",test1,i);
//输出 test1 =34,i = 4
//i++ 看成只有i,代入完了后再++
uint8 num[5]={0,1,2,34,5};
uint8 i = 3;
uint8 test1 = num[++i];
printf("test1 = %d,i = %d\n",test1,i);
//输出 test1 =5,i = 4
//++i看成一个整体直接+完后一起计算
6、字节对齐:
结构体字节对齐
struct STUDENT
{
char a;
int b;
char c;
}data;
//占用12个字节
在计算机内部的内存分配
7、堆栈
.
栈是单片机的RAM中一段连续的内存空间,内存空间大小由系统自动分配。栈的存放由上及下,先定义的变量地址高,后定义的变量地址 小。相邻的变量之间没有其他参数的内存占用。
.
堆是由开发人员分配与释放,类似于链表。堆的内存地址与栈相反,由高到低,但是相邻的变量之间不存在关系,当上一个变量分配内存然后释放完了之后,下一个变量的内存会首先使用上一个内存空间。
两者区别
①管理的方式不同
②内存空间生长方式不同
③空间分配大小不同
④空间分配效率不同
⑤存放的内容不同
8、嵌入式中的malloc与free
1、小型嵌入式设备中的 RAM 不足
2、不确定的,调用函数每次时间都不一样,因此是不安全的
3、有可能会产生内存碎片
μC/OS 提供的内存分配算法是只允许用户分配固定大小的内存块,当使用完成就将其放回内存池中,这样子分配效率极高,时间复杂度是 O(1),也就是一个固定的时间常数,并不会因为系统内存的多少而增加遍历内存块列表的时间,并且还不会导致内存碎片的出现
但是这样的内存分配机制会导致内存利用率的下降以及申请内存大小的限制
由于内存块的大小是固定的,因此无法解决弹性很大的这种需求,只能按照最大内存块的分配
9、中断发生的顺序
–
① 保存现场,保存处理机当前的信息
② CPU处理中断,处理中断函数(载入异常或中断处理函数到PC寄存器)
③ CPU处理完中断函数后将执行权交还回处理机(将控制权交给中断处理函数并执行)
④ 继续执行保存的信息(当处理函数执行完毕,恢复处理器状态信息)
⑤ 从异常或者中断中返回上一个处理点
10、case与break
如果case后面没有break,每个case都会打印
Continue会跳过本次循环,进入到下一个循环;Break会直接中断,跳出本次循环
11、地址偏移
地址偏移
数组起始地址data
data + 2 = &data[2]
*(data + 2) = data[2]
12、指针const
//1
uint8 *pt
*pt = 5
//2
uint8 data;
uint8 *pData = &data;
*pData = 5;
方式1这样写是错误的,只申请了指针的内存,并未申请存储数据的内存,可能会导致程序崩溃。
const数据或非const数据赋值给const指针是合法的
非const数据赋值给普通指针是合法的,const数据赋值给普通指针是不合法的
因为通过指针就可以修改const中的数据,这与const含义悖论了
Int* p1 与 int p1的区别
加在变量类型前面表示变量都是地址类型,int p1,p2,p3 = int *p1,*p2,*p3;
13、数据拼帧
方式一通过定义union结构体的变量,然后进行赋值拼帧
方式二通过循环+case
14、四字节向下对齐
**
15、宏的复用
例如需要配置相应IO管脚的FTM
可以定义IO的宏
前面的16位代表使用了哪个FTMn,后面的值是置位,例如PC0,掩码是0x3,是低两位
以此来对相应的IO管脚进行操作
16、链表使用
objType
typedef struct DLPC_Obj
{
struct DLPC_Obj *next; /*!< 链接到下个OBJ */
DLPC_FramType framType; /*!< 该obj的读写类型*/
uint16 dutyReg; /*!< 占空比寄存器值*/
boolean isUsed; /*!< 判断是否使用*/
}DLPC_ObjType;
1、插入结点
static void DLPC_ListInsert(DLPC_ObjType *pObj)
{
DLPC_ObjType *list = s_info.s_nowObj;
if(list == NULL) /* Firt */
{
pObj->next = NULL;
s_info.s_nowObj = pObj;
}
else
{
pObj->next = NULL;
list->next = pObj;
}
s_info.cmdNum++;
}
2、删除结点
static void DLPC_ListDelete(void)
{
DLPC_ObjType *list = s_info.s_nowObj;
DLPC_ObjType *nextObj;
if(list != NULL)
{
if(list->next != NULL) /* 下一个不为空 */
{
nextObj = list->next;
list->next = NULL;
s_info.s_nowObj = nextObj;
}
else /* 下一个为空 */
{
s_info.s_nowObj = NULL;
}
MemSet(list, 0, sizeof(DLPC_ObjType)); //清除数组缓存
if(s_info.cmdNum != 0)
{
(s_info.cmdNum)--;
}
}
s_info.diagData = 0;
s_info.nextStep = DLPC_CHECKCMD;
}