当前位置: 代码迷 >> 综合 >> 【ODYSSEY-STM32MP157C】驱动 UART 读取传感器数据
  详细解决方案

【ODYSSEY-STM32MP157C】驱动 UART 读取传感器数据

热度:48   发布时间:2024-02-24 17:03:54.0

我们在上一节《【ODYSSEY-STM32MP157C】驱动 GPIO 实现呼吸灯》 已经驱动 GPIO 实现呼吸灯功能,本节我们将在 Linux 上操作 STM32MP157C 的 UART2 串口与传感器进行通信,并将传感器数据打印出来。

在这里插入图片描述

准备材料

  • Seeed:ODYSSEY-STM32MP157C 开发板
  • 攀藤:PMS5003ST 传感器

PMS5003ST 简介

PMS5003ST 是攀藤科技的一款空气质量传感器,可以同时监测空气中颗粒物浓度(PM1.0、PM2.5、PM10)、甲醛浓度和环境温湿度。由于采用激光散射原理,因此可以连续采集,并且有较高的精度和稳定性。

在这里插入图片描述

同时,传感器模块以通用数字接口形式输出,简化了控制端的开发。

在这里插入图片描述

PMS5003ST 的数字接口定义如下:

管脚 功能 说明 STM32MP157C
PIN1 VCC 电源正(+5V) ④ 5V
PIN2 GND 电源负 ⑥ GND
PIN3 SET 设置管脚(3.3V)
PIN4 RXD 串口接收管脚(3.3V) ⑧ PF5/USART2_TX
PIN5 TXD 串口发送管脚(3.3V) ⑩ PD6/USART2_RX
PIN6 RESET 模块复位信号(3.3V,低复位)
PIN7 NC -
PIN8 PWM/NC PWM输出

在本案例,我们只需要将传感器的 PIN1、PIN2、PIN4、PIN5 与 ODYSSEY-STM32MP157C 扩展接口的 pin 4、6、8、10 连接即可。

在这里插入图片描述

安装 usart2 驱动

  1. 在编译之前,需要下载对应版本的内核头文件。

    sudo apt update
    sudo apt install linux-headers-$(uname -r) -y
    

    注意:这一步可能需要开启代理才能完成!

  2. 下载 seeed-linux-dtoverlays 仓库,编译并安装 stm32p1 驱动。

    git clone https://github.com/Seeed-Studio/seeed-linux-dtoverlays
    

    编译、安装

    cd seeed-linux-dtoverlays
    make all_stm32mp1 CUSTOM_MOD_FILTER_OUT="jtsn-wm8960"
    sudo make install_stm32mp1 CUSTOM_MOD_FILTER_OUT="jtsn-wm8960"
    

    对应的 ko 文件将安装到 /lib/modules/4.19.9-stm32-r1/extra/seeed/ 目录,dtbo 文件将安装到 /lib/firmware/ 目录。

  3. 修改 /boot/uEnv.txt 文件,在该文件末尾添加一行,加载 usart2 驱动。

    uboot_overlay_addr2=/lib/firmware/stm32mp1-seeed-usart2-overlay.dtbo
    
  4. reboot 重启系统,可以看到系统增加了 /dev/ttySTM2 设备节点。

    执行 dmesg | grep ttySTM* 查看启动信息:

    [    0.000000] Kernel command line: console=ttySTM0,115200 root=/dev/mmcblk0p6 ro rootfstype=ext4 rootwait coherent_poot
    [    1.060447] 4000e000.serial: ttySTM2 at MMIO 0x4000e000 (irq = 25, base_baud = 4000000) is a stm32-usart
    [    1.062277] 40010000.serial: ttySTM0 at MMIO 0x40010000 (irq = 27, base_baud = 4000000) is a stm32-usart
    [    1.062366] console [ttySTM0] enabled
    [    1.064147] 5c000000.serial: ttySTM1 at MMIO 0x5c000000 (irq = 70, base_baud = 4000000) is a stm32-usart
    [    1.064420] serial serial0: tty port ttySTM1 registered
    

    执行 cat /proc/tty/driver/stm32-usart 查看串口驱动:

    serinfo:1.0 driver revision:
    0: uart:stm32-usart mmio:0x40010000 irq:27 tx:1141 rx:63 RTS|CTS|DTR|DSR|CD
    1: uart:stm32-usart mmio:0x5C000000 irq:70 tx:44691 rx:2274 RTS|CTS|DTR|DSR|CD
    2: uart:stm32-usart mmio:0x4000E000 irq:25 tx:0 rx:0 CTS|DSR|CD
    

读取传感器数据

实际上,只要我们在串口2连接上传感器,就可以通过 cat /dev/ttySTM2 获取传感器的数据了。但是,这些是二进制格式的数据,我们看不懂,因此,我们需要对其进行解析。

测试串口

我选择用 Python 来编程,首先安装 serial 和 pyserial 库。

pip3 install serial
pip3 install pyserial

之后,我们可以在 Python 交互环境中进行测试:

>>> import serial
>>> port = serial.Serial('/dev/ttySTM2', 9600)
>>> port.isOpen()
True
>>> port.close()

OK,没问题,我们就可以开始写个解析程序了!

解析数据

新建一个 show_pms5003st.py 文件,添加代码框架:

import sys
import glob
import serial
import timedev_name = '/dev/ttySTM2'
baudrate = 9600CMD_READ = bytearray([0x42, 0x4d, 0xe2, 0x00, 0x00, 0x01, 0x71])
CMD_PASS = bytearray([0x42, 0x4d, 0xe1, 0x00, 0x00, 0x01, 0x70])
CMD_ACTI = bytearray([0x42, 0x4d, 0xe1, 0x00, 0x01, 0x01, 0x71])
CMD_STAN = bytearray([0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73])
CMD_NORM = bytearray([0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74])def loop(serial):passdef main():print("Run ODYSSEY-uart demo")s = serial.Serial(dev_name, baudrate)if not s.isOpen():s.open()try:s.write(CMD_PASS)except Exception as err:print(err)finally:time.sleep(1)loop(s)s.close()if __name__ == '__main__':main()

PMS5003ST 默认进入主动模式,即传感器会周期地主动向外发送数据,因此我在打开串口之后做的第一件事就是将其设置为被动模式,由程序主动去查询数据。返回的数据格式如下:

在这里插入图片描述

由于一个有效数据由两个字节构成,因此我增加了一个函数来处理:

def pms_value(hByte, lByte):return (hByte << 8 | lByte)

然后解析的重点就在 loop 函数中啦,因为每一个有效帧都是 0x42 + 0x4d 开头,所以只要识别出来,并且获取帧的长度进行解析即可。为了方便(偷懒),我没有增加状态记录,只有数据长度为 36 字节的才解析,然后进行校验,抽取有效数据并打印出来。

loop 函数代码如下:

def loop(serial):while True:serial.write(CMD_READ)start1 = serial.read(1)if (start1[0] == 0x42):start2 = serial.read(1)if (start2[0] == 0x4d):print("Is a frame")else:continueelse:continuelen1 = serial.read(1)len2 = serial.read(1)size = pms_value(len1[0], len2[0])if (size == 36):print("Is a response")resp = serial.read(size)for i in resp:print("{:x}".format(i), end=' ')print("")checksum = pms_value(resp[size-2], resp[size-1])dsum = start1[0] + start2[0] + len1[0] + len2[0]for i in range(0, size - 2):dsum = dsum + resp[i]dsum = dsum & 0xffffif (checksum != dsum):print("Checksum invalid. {} != {}".format(checksum, dsum))continuePM1_0_CF1  = pms_value(resp[0], resp[1])PM2_5_CF1  = pms_value(resp[2], resp[3])PM10_0_CF1 = pms_value(resp[4], resp[5])PM1_0_atm  = pms_value(resp[6], resp[7])PM2_5_atm  = pms_value(resp[8], resp[9])PM10_0_atm = pms_value(resp[10], resp[11])air_0_3um  = pms_value(resp[12], resp[13])air_0_5um  = pms_value(resp[14], resp[15])air_1_0um  = pms_value(resp[16], resp[17])air_2_5um  = pms_value(resp[18], resp[19])air_5_0um  = pms_value(resp[20], resp[21])air_10_0um = pms_value(resp[22], resp[23])hcho       = pms_value(resp[24], resp[25])temp       = pms_value(resp[26], resp[27])/10humi       = pms_value(resp[28], resp[29])/10version    = resp[32]errorCode  = resp[33]print("\nResponse => len: {} bytes, version: {:0>2x}, Error: {:0>2x}".format(size+4, version, errorCode))print("+-----------------------------------------------------+")print("| CF=1 | PM1.0 = {:<4d} | PM2.5 = {:<4d} | PM10 = {:<4d} |".format(PM1_0_CF1, PM2_5_CF1, PM10_0_CF1))print("| atm. | PM1.0 = {:<4d} | PM2.5 = {:<4d} | PM10 = {:<4d} |".format(PM1_0_atm, PM2_5_atm, PM10_0_atm))print("| | 0.3um = {:<4d} | 0.5um = {:<4d} | 1.0um = {:<4d} |".format(air_0_3um, air_0_5um, air_1_0um))print("| | 2.5um = {:<4d} | 5.0um = {:<4d} | 10um = {:<4d} |".format(air_2_5um, air_5_0um, air_10_0um))print("| extra | hcho = {:<4d} | temp = {:<.1f} | humi = {:<.1f} |".format(hcho, temp, humi))print("+-----------------------------------------------------+\n")time.sleep(3)

运行效果

执行 python3 show_pms5003st.py,运行情况如下:

在这里插入图片描述

非常棒,我们已经得到传感器数据了,下一节我们将学习如何将这些数据上传到云端。


在这里插入图片描述