关键字
嵌入式开发
硬件资源
互斥锁
freeRTOS操作系统
串口通信
基本介绍
互斥锁(英文:Mutual exclusion,常缩写为Mutex,是一种常用在多线程编程中,防止多个线程对一个公共资源做读写操作的机制,以保证共享操作的数据的完整性。互斥锁是最基本的进程或者线程间同步的方法,用来保护临界区,以保证任何时候只有一个线程或者进程在访问共享资源(如共享的代码段)。
笔者使用的操作系统为freeRTOS,下文提到的“进程”实则为freeRTOS中的“任务”,”Task“。
互斥锁在串口通信中的应用
在串口通信中,互斥锁经常被用到。用于保证串口发送的一帧数据的完整性。不会被插入其他数据。
串口资源被抢占
uart_send_hello()
{
char send_data[6] = "hello";
tx_send_data(data,length);//底层串口发送函数
}
uart_send_world()
{
char send_data[6] = "world";
tx_send_data(data,length);//底层串口发送函数
}
如果不进行加锁,当uart_send_hello() 执行到一半时,可能被其他更高优先级的调用uart_send_world() 的进程打断,导致发送的数据为“heworldllo”、或“hellworldo”等其他情况。
使用互斥锁保证一帧串口数据的完整性
为了避免这种情况,可以在串口发送的开始和结束阶段“加锁”,进行简单的封装。这样在不同的进程调用封装函数tx_send_data_with_lock()
的时候,即使进程的优先级不同,也不会出现一帧数据还没有发送完就被打断。
tx_send_data_with_lock(char *data, int lenth)
{
MutexLock(uart_lock);//上锁
tx_send_data(data,lenth);
MutexUnLock(uart_lock);//解锁
}
uart_send_hello()
{
char send_data[6] = "hello";
tx_send_data_with_lock(data,length);//带互斥所封装的串口发送函数
}
uart_send_world()
{
char send_data[6] = "world";
tx_send_data_with_lock(data,length);//带互斥所封装的串口发送函数
}
使用互斥锁保证两帧数据的时间间隔
如果有些应用场景,对串口发送的时间间隔有要求:保证两帧数据的时间间隔不少于20ms,那么在适当的位置加入互斥锁可以简单实现这个功能。例如,要保证两个“hello”之间的间隔不小于20mS。
写法一:不可行
uart_send_hello()
{
char send_data[6] = "hello";
tx_send_data_with_lock(data,length);//带互斥所封装的串口发送函数
delayms(20);//任务休眠(延时)20mS
}
写法二:可行
uart_send_hello()
{
MutexLock(hello_lock);//上锁
char send_data[6] = "hello";
tx_send_data_with_lock(data,length);//带互斥所封装的串口发送函数
delayms(20);//任务休眠(延时)20mS
MutexUnLock(hello_lock);//解锁
}
写法一中,虽然uart_send_hello() 在结束发送”hello“后进行了20mS的延时,但实际当两个线程同时调用uart_send_hello() 时,并不是等待一个完整的uart_send_hello() 执行完再执行另一个uart_send_hello() :前一个进程中的”uart_lock“被释放之后,下一个调用uart_send_hello() 的进程可以立即取”uart_lock“执行并执行串口发送任务,这样仍然不能保证两个”hello“之间的发送间隔为20mS以上。
写法二能有效实现这个功能,由于”hello_lock“互斥锁的存在,不管以任何方式调用uart_send_hello() ,在发送”hello“之后始终会进行20mS的延时。
扩展
同理,在不同的位置上锁,还可以实现不同的功能,如:
- 保证所有相临的串口发送帧(不仅仅是“hello”)间隔不少于20mS
- 保证串口发送一帧数据和接收一帧数据的间隔不少于50mS
- 使用互斥锁保证其他硬件资源或全局变量不被抢占操作。