初识串口
Tag 串口, on by view 40

一直很想了解设备驱动编程。所以就从最基础,最简单的设备开始了解。串口,应该是所有设备中最简单最常见的设备。在linux中,有一种最简单的串口设备在/dev下表现的是字符设备(c),字符设备可以被打开并且读写。

我认识的第一个最简单的串口设备是一个usb接口的gps模块,如下图 fmvi8se8 它是我目前为止,用过的最简单的串口设备,它只需要读,无需写,单向通讯,我只需要打开字符设备,然后不停的从中读取数据就可以了,里面的数据是GPS定位信息相关的字符串,根据它的文档,进行字段解析,就能获取到经纬度、时间、角度、速度等信息。我曾经用golang写过一个一个程序获取这些信息 sbtao9zf 这个程序,实际上可以算是一个用户态的设备驱动程序。

我认识的第二个串口设备是一套蓝牙模块,用了usb转串口线连接蓝牙模块 ddlsq0na 按照设备的文档,我将它通过usb转串口线连接,然后将usb插上电脑,成功识别到串口。然后手机通过客服提供的调试app连接蓝牙信号,电脑上用串口读写软件将串口打开,手机上发送信息,电脑串口上能收到信息,电脑串口上还能发送特定的AT指令对设备进行查询与设置。 3wwg8uas 值得注意的是,我在连接模块的过程中,将usb转串口上的tx连接了模块tx,rx连接了模块的rx,导致无法收发消息,客服帮我找出问题并告诉我应该将tx连接rx,rx连接tx。想到这一点我恍然大悟,这跟rust里的channel,go里的chan,以及linux上的通用管道一样,都属于管道模型,对端的输出接入到设备的输入,对端才能将信息传给设备。后续我想我会进一步对这个蓝牙模块编写用户态驱动程序,进而尝试编写内核态驱动程序。

其实早在我认识GPS串口设备之前,我也试过温湿度传感器DHT22,但是,这玩意儿根本不是串口,它只有一个数据接口(DATA),也就是不像串口那样分rx,tx,它需要通过gpio口连接DATA针,然后通过高电位低电位来实现双向通讯,从而获取到温湿度数据。它既然不是串口设备那么也不能作为通用设备直插一般的电脑了。

在我看来,程序能驱动物理设备,也是一件很有趣的事情。


初学rust,如何实现在运行时对全局变量设置和读取
Tag 运行时, 全局变量, 读写, on by view 230

在rust中,如何实现在运行时对全局变量设置和读取,这个问题困扰了我一段时间。因为在rust中,全局变量是不能在运行时修改的。rust的全局变量是属于全局静态变量,使用关键词static来定义,如下:

static mut count: usize = 0;

pub fn init_config(file_path: &str) {
    count = count + 1;
}

编译器会告诉你修改count不安全(unsafe)。

➜  las git:(feature/self-update-with-watcher-fork) ✗ cargo build
   Compiling las v0.1.1 (/root/code/las)
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
  --> src/config/cfg.rs:35:13
   |
35 |     count = count + 1;
   |             ^^^^^ use of mutable static
   |
   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

error[E0133]: use of mutable static is unsafe and requires unsafe function or block
  --> src/config/cfg.rs:35:5
   |
35 |     count = count + 1;
   |     ^^^^^^^^^^^^^^^^^ use of mutable static
   |
   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

For more information about this error, try `rustc --explain E0133`.
error: could not compile `las` (bin "las") due to 2 previous errors

一定要修改的话,需要加unsafe块包裹住:

static mut count: usize = 0;

pub fn init_config(file_path: &str) {
    unsafe { count = count + 1 };
}

但是,在rust中使用unsafe是一件不好的事情,我们应该尽量避免使用unsafe,这里有另一个方案可以实现在运行时设置全局变量,就是使用once_cell这个包,虽然它的底层也是基于unsafe来实现的,但是它对“不安全”代码进行了一定的包装。具体用法如下:

static CFG: OnceCell<Conf> = OnceCell::new();

// 初始化和设置
pub fn init_config(file_path: &str) {
    CFG.get_or_init(|| load_config(file_path));
}

// 读取
pub fn get_config() -> &'static Conf {
    CFG.get().unwrap()
}

可以看到,我们可以在运行时使用once_cellget_or_init方法对全局变量进行初始化设置,它还有set方法也可以在运行时对全局变量进行设置,get方法在运行时对全局变量进行读取。从而实现不直接使用unsafe块,来操作全局变量。而且once_cell使用的全局变量完全不必申明为可变(mut)变量。

我们可以通过追踪once_cell::get_or_init方法,可以看到在initialize方法中unsafe块中,将value设置给了*slot

qby9tqm9

可以看到,once_cell对全局变量的操作进行了安全封装,因此,建议使用once_cell进行全局变量的操作,而不是到处使用unsafe块进行不安全的操作。

^注意:实践证明,OnceCell只能调用set()对全局变量设置一次,后续再调用set()就会报错。要反复读写甚至多线程中读写全局变量,应该使用RwLock,详情参考新的文章《初学rust,多线程中读写全局变量》