嵌入式中常见的问题锦集


该博客仅用来记录学习和工作中遇到的问题和解决方案,另外还包括一些工具便捷使用的方案,仅供参考和学习

Crash case

1. 栈满了(stack over)

a. 增加Task的栈大小
b. 当一个函数被注册在B Task中时(该Task的栈不能修改),可以在该函数中通过消息队列,将数据发送到A Task中(可自定义的Task)处理

2. 同一块buf释放两次

a. 内存分配注意事项:初始化后判空;使用后释放;释放后置空
	char *pm = (char *) malloc(size);
	if (pm == NULL) 内存分配失败 return;
	使用pm
	free(pm);
	pm = NULL;

3. 当Crash 时,但是因为log 会丢失,不能确切定位到Crash地方

a.这种情况很特殊,有时候会有完整的Crash报告,可以根据相关的工具解析出来大概Crash的原因;
b.如果不好定位地方,可以先==拿掉一些可疑函数==,从而粗略确定Crash位置,然后可以在可疑的
地方==提前return==(即不让他跑一段),来确定精细位置;总的来说就是精确定位Crash位
置,==将Crash慢慢的夹出来==

Problem solutions

1. 一个独立的模块想要调其他模块的函数接口

在原方案中,模块B需要调用模块A中的read and write 函数,因此需要包含模块A的头文件
解决方案:为了让模块B更加独立,可以在模块B中建立read 和 write 函数指针,并创建注册函数;
在模块A中调用模块B的注册函数,将模块A中的函数可以在模块B中使用,而保证编译的时候不依赖与模块A。
实现如下。
优点:减少模块之间的耦合
模块B:
typedef void (*read_f)(char* buf, int len);
typedef void (*write_f)(char* buf, int len);
read_f pread = NULL;
write_f pwrite = NULL;
void register_read_fun(void* pfun)
{
	pread = (read_f)pfun;
}
void register_write_fun(void* pfun)
{
	pwrite = (write_f)pfun;
}

void xxxB_task()
{
	if (xxx){
		pread(buf, len);
	}else (xxx){
		pwrite(buf, len);
	}
}
模块A:可以使用include模块B的头文件来调用注册函数,或者使用extern
extern void register_read_fun(void* pfun)
extern void register_write_fun(void* pfun)
void read(char* buf, int len)
{
	//读操作
}
void write(char* buf, int len)
{
	//写操作
}

void xxxA_task()
{
	register_read_fun(read);
	register_write_fun(write);
	while(1){
		//do some thing
	}
}

2. porting layer:

Q:一个烧录程序的功能,可以通过IIC\SPI\UART三种通信方式,未来还可能增加新的通信方式,
为了降低开发成本怎么设计。
A:烧录程序这一段的flow是相同的,仅通信方式不一样,通信中涉及到read,write,
setting等函数,我们可以使用一个函数指针结构体来实现上述功能。如果是C++就更好做了,
先建一个base类,其中有read,write,setting的虚函数,然后通过继承分别去实现不同的通信方式。
优点:扩展性好
通过结构体实现上述方案:
typedef void (*read_f)(char* buf, int len);
typedef void (*write_f)(char* buf, int len);
typedef void (*setting_f)(void* set);
typedef struct {
	read_f pread;
	write_f pwrite;
	setting_f psetting;
} Com;
Com c;
void main_task_init()
{
#ifdef IIC
c = iic函数接口
#enif
#ifdef SPI
c = spi函数接口
#enif
#ifdef UART
c = uart函数接口
#enif	
}
void main()
{
	main_task_init();
	c.psetting(buf, len);
	.....
	c.pread(buf, len);
	....
	c.pwrite(buf, len);
}

如果需要新加通信方式CAN
只需要新建一个文件,实现结构体中的函数,然后在main_task_init加上函数的注册即可

3. Task没有处理消息

1.可能是消息队列爆掉了
2.可能卡在处理某一条消息里面了,退不出来,所以下一条消息也没办法处理

4. 定时器

1.使用POSIX函数timer_create创建定时器(可移植性好)
2.使用epoll,及事件驱动方式,通过timerfd_create创建(适合配合文件系统使用)

5. 一般嵌入式软件架构(仅参考)

一般来说,分为四个层,分别是app层,service 层, kernel 层和drive驱动,
如下是一个host(PC或者手机),app层,service层设计的例子:
1. 从上到下host->app->service:host 发送送数据->app, app 通过中断接
收解析数据并发送->main_task(或者其他task)中,其中根据不同的
消息ID,处理不同的事情,如果需要将数据传输到service层的可以直接
调用service层的handle函数,service层可以保存数据,或者加载数据提供给kernel。
2.从下层到上层service->app->host: service层有一个注册函数,
可以将app的一个函数当作一个用户来注册(因此service还可以支持多用户,
需要一个source id来区分用户),在service处理传递kernel上来的数据,
调用用户的回调函数,回调函数中,根据不同的消息ID,处理不同功能的数据,
然后将结果通过uart的方式传输给host。
3.一般的消息格式typedef {int msg_id; int msg_len; char* msg_data} msg;

6. Android NDK Crash分析(一般是C++写的守护进程,用于对接硬件和HIDL)

一般是分析Crash的Callstack 或者是Tombstone;
分析流程和使用方法可参考:https://zhuanlan.zhihu.com/p/52270464

7. 嵌入式中常见的异常情况

1.assert : 断言, assert(0)
2.NMI Fault : 非屏蔽中断,可以先关闭中断,然后调用while(1); 进入死循环,等3-4分钟会触发该Crash
3.HARD Fault : 硬件故障,assert(0)也属于这一类
4.MEM Fault : 内存访问错误, char* p = NULL; *p=0; 可触发
5.USAGE Fault : 使用错误,比如除零;当时在这卡了好久,没有实现这种Crash; 
如果未出现这种Crash可以考虑以下两种情况:
	a.被系统优化了,我们可以在变量前面加volatile 修饰
	b.系统没有打开除0的异常处理,需要在系统初始化的时候去设置

Good tool solutions

1. 在win10上学习Linux

具体的Ubuntu 24.04 LTS安装步骤可参考这里

需求来源:如果你使用的是win10系统,但是想进行Liunx开发或者学习。我们可以选择安装虚拟机,
或者安装双系统(win+Linux),相比与后者,前者肯定是较优方案;但是在win10,
可以在Microsoft stroe 下载 Ubuntu 24.04 LTS,目前我认为是最优方案。
如下是安装步骤以便使用:
1.去Microsoft stroe 下载 Ubuntu 24.04 LTS, 并根据他的安装提示(需要设置一些东西),
然后重启,之后就可以使用linux的黑窗命令行了,是不是很简单;
2.安装Vscode, 安装后下载WLS插件(点击左下角的><符号可选择连接), 这样我们就可以以Vscode为编辑器,
进行后续工作了,非常方便,既可以编辑又可以输入命令,还包含了许多插件。
重要的是就不用面对黑框Terminal了,真好。
3.在开发中遇到什么工具没有,就下载,比如make, gdb, g++, gcc等,下载命令,敲一下他会有提示和建议
4.怎么优雅便捷的使用就需要自己慢慢探索了。
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值