阅读引言: 本文我想给大家分享一下我在学习过程中遇到的以及了解到的一些导致单片机运行卡死(死锁)的一些常见原因和解决办法, 请注意, 只是列举,并不是全部, 因为导致单片机运行卡死的原因无穷无尽。
目录
[1.死循环, 条件一直成立](#1.死循环, 条件一直成立)
一、裸机层面
1.死循环, 条件一直成立
cpp
/* 伪代码 */
while(一直为真)
{
}
比如按键的检测等待按键释放的操作
2.中断问题
cpp
/* 在执行某一个中断服务函数的时候, 没有清除中断标志位 */
void EXTI_IRQHandler(void)
{
//没清除中断标志位, 导致cpu一直进入中断, 主逻辑没有执行, 导致程序出现卡死的现象
}
在标准库中, 执行完中断服务函数之后, 需要手动清除中断标志位, 不然会一直进入中断, hal库的大部分的中断服务函数执行完之后, 会自动的清除中断标志位。
3.外设的初始化问题
cpp
void main()
{
//initialize code
//某一个外设初始化失败, 导致程序卡在错误处理的那个函数
}
就会导致单片机看起来卡死了一样。
另一个列子:
4.堆栈溢出
4.1递归导致栈溢出
cpp
void recursion()
{
recursion();
}
void main()
{
recursion();
}
递归函数没有结束条件, 导致栈空间空间溢出。
4.2访问非法的内存地址
cpp
int main()
{
int arr[10] = {0};
arr[10] = 100; /* 写非法的内存空间 */
}
4.3内存泄漏
cpp
void Getmemery()
{
char *p = (char *)malloc(10);
}
void main()
{
for(;;)
Getmemery();
}
这里我使用for循环来申请空间, 来模拟我们到处申请堆空间而没有释放导致的内存泄漏, 最后导致程序崩溃。
5.时钟的配置问题
cpp
void main()
{
//外设的时钟配置不对, 导致程序卡死
//无法执行到这里
while(1) {
//code
}
}
6.程序逻辑问题
假设你设计的程序需要等待每一个外设接收到数据, 或者发送数据才往下执行, 这个时候就需要你自己找到你自己的代码的每一句是在干嘛。
二、RTOS层面
当你的单片机上了实时操作相同, 或者mpu装linux操作系统, 面临死锁的常见原因如下:
在单片机系统中,死锁是指多个任务或线程由于彼此之间的资源竞争而陷入永久阻塞的状态。死锁可能发生在多任务或多线程系统中,其中每个任务或线程都在等待其他任务或线程释放它们占用的资源。 导致单片机死锁的情况通常与资源竞争和资源分配有关。以下是一些可能导致死锁的情况:
互斥资源:多个任务或线程同时竞争同一个互斥资源,例如共享内存区域或硬件寄存器。如果没有适当地管理和同步资源的访问,就可能导致死锁。
循环等待:多个任务或线程形成了一个循环等待资源的环路。每个任务都在等待下一个任务释放资源,但所有任务都被其他任务所占用,导致死锁。
延迟分配:某个任务占用资源后,未能及时释放资源。其他任务需要该资源来继续运行,但由于该资源一直被占用,导致死锁。
为了避免单片机死锁的发生,可以采取以下策略:
使用合适的同步机制:使用互斥锁、信号量或其他适当的同步机制来管理共享资源的访问,确保资源在同一时间只能被一个任务或线程使用。
避免循环等待:设计任务或线程的执行顺序,避免出现循环等待资源的情况。
及时释放资源:确保任务或线程在使用完资源后,及时释放资源,以便其他任务或线程可以继续执行。
调整系统资源分配:合理规划系统资源的分配和调度,避免资源的过度分配或容量不足。 5. 使用死锁检测与恢复机制:实施死锁检测算法,当检测到死锁的发生,通过中断或其他手段恢复系统正常运行。
- mutex
什么是死锁:线程1和线程2 都获得了自己的锁, 线程1的锁为锁1, 线程2的锁为锁2, 这是如果线程1或者线程2 想去获得对方的锁或资源就无法获得, 造成死锁.导致二者都拿不到锁, 导致任务阻塞。
任务或者线程在访问完临界资源后记得释放锁。
三、常见的调试办法
1.printf函数打印
将单片机的uart配置好之后, 重写fputc函数, 勾选上微小C库, 随时随地的打印。
printf函数的用途:
查看函数有没有运行到, 定位出问题的地方
查看变量的值
打印调试的信息
生成单片机运行的日志信息
2.使用keil的调试功能
不太好用, 除非是真的真的特变需要知道每一个寄存器, 或者内存的值才使用这个模式, 个人觉得用这个调试程序怪怪的, 可能是不太熟练吧。
四、学习资料
提取码:1234