目录
- [一、STM32 嵌入式开发基础](#一、STM32 嵌入式开发基础)
-
- [1.1 STM32 简介](#1.1 STM32 简介)
- [1.2 开发环境搭建](#1.2 开发环境搭建)
- [1.3 外设驱动基础](#1.3 外设驱动基础)
- [二、传感器数据采集实战(I2C 总线)](#二、传感器数据采集实战(I2C 总线))
-
- [2.1 I2C 总线基础](#2.1 I2C 总线基础)
- [2.2 驱动实现](#2.2 驱动实现)
-
- [2.2.1 使用 STM32CubeMX 配置 I2C 外设](#2.2.1 使用 STM32CubeMX 配置 I2C 外设)
- [2.2.2 编写传感器读写函数](#2.2.2 编写传感器读写函数)
- [2.2.3 数据校验](#2.2.3 数据校验)
- [2.3 实战](#2.3 实战)
- [三、传感器数据采集实战(ADC 外设)](#三、传感器数据采集实战(ADC 外设))
-
- [3.1 ADC 基础](#3.1 ADC 基础)
- [3.2 驱动实现](#3.2 驱动实现)
-
- [3.2.1 使用 STM32CubeMX 配置 ADC](#3.2.1 使用 STM32CubeMX 配置 ADC)
- [3.2.2 编写 ADC 采集函数](#3.2.2 编写 ADC 采集函数)
- [3.3 实战](#3.3 实战)
- [3.4 数据存储](#3.4 数据存储)
一、STM32 嵌入式开发基础
1.1 STM32 简介
STM32 是意法半导体推出的一系列 32 位微控制器,基于 ARM Cortex-M 内核 ,该内核赋予了 STM32 出色的性能,以 STM32F103 系列为例,其最高运行频率可达 72MHz,能够快速处理各种复杂任务,像在一些工业控制场景中,需要对大量传感器数据进行实时分析处理,STM32 就能凭借其高性能的内核迅速完成任务,保障系统的高效运行。
丰富的外设资源是 STM32 的一大亮点。它集成了 I2C、SPI、UART 等多种通信接口,方便与各类设备进行数据交互。例如在智能家居系统中,STM32 可以通过 I2C 接口连接温湿度传感器,获取环境温湿度数据;通过 SPI 接口与 Flash 存储器通信,实现数据的存储;利用 UART 接口与蓝牙模块通信,将采集到的数据传输到手机 APP 上进行展示 。同时,STM32 还内置了 ADC,能够将模拟信号转换为数字信号,这在传感器数据采集中非常关键,比如连接光敏电阻、压力传感器等模拟传感器时,ADC 就能将它们输出的模拟信号转化为数字量,供 STM32 进一步处理。正是因为 STM32 具备这些丰富的外设和强大的性能,使其成为传感器数据采集应用中的理想选择。
1.2 开发环境搭建
在 STM32 开发中,STM32CubeMX 和 Keil MDK-ARM 是两个重要的工具。
STM32CubeMX 是一款图形化工具,它为开发者提供了便捷的配置方式。打开 STM32CubeMX 后,第一步是选择目标芯片型号,比如我们要使用 STM32F103C8T6,就在芯片选择界面找到对应的型号。接着,可以通过直观的图形界面配置各种外设。在配置 I2C 外设时,只需在相应的配置区域,设置 I2C 的工作模式、时钟频率等参数,如将时钟频率设置为 100kHz 。它还能对时钟树进行可视化配置,调整系统时钟频率以满足不同应用需求,比如将系统时钟设置为 72MHz。完成所有配置后,点击 "Generate Code" 按钮,就能自动生成初始化代码,这些代码包含了对各种外设的初始化设置,大大减少了手动编写驱动程序的工作量。
Keil MDK-ARM 则是一款集成开发环境(IDE),它拥有丰富的功能。在 Keil MDK-ARM 中,可以将 STM32CubeMX 生成的代码导入项目。导入后,就能利用其编辑器进行代码的编写和修改。点击编译按钮,Keil MDK-ARM 会对代码进行编译,检查代码中的语法错误等问题。编译通过后,通过下载工具将程序下载到 STM32 开发板中,实现程序的烧录运行。在程序运行过程中,如果出现问题,还能使用 Keil MDK-ARM 的调试功能,如设置断点、单步执行、查看变量值等,方便排查程序中的错误,确保程序的正确性。
1.3 外设驱动基础
在操作 STM32 的外设时,有寄存器和 HAL 库两种方式。
直接操作寄存器是一种较为底层的方式,需要开发者对硬件原理有深入的了解。以操作 GPIO 为例,要设置某个 GPIO 引脚为输出模式,需要对相应的寄存器进行位操作,如对端口配置低寄存器(GPIOx_CRL)的对应位进行设置,这种方式虽然能够直接控制硬件,实现高效的操作,但代码编写较为复杂,容易出错,对开发者的要求较高。
而 HAL 库(Hardware Abstraction Layer)则将底层的寄存器操作进行了封装,以更高级的函数形式提供给开发者。同样是设置 GPIO 引脚为输出模式,使用 HAL 库时,只需定义一个 GPIO_InitTypeDef 类型的结构体,设置好结构体中的参数,如 GPIO_MODE_OUTPUT_PP 表示推挽输出模式,然后调用 HAL_GPIO_Init 函数即可完成配置。在进行串口通信时,使用 HAL 库的 HAL_UART_Init 函数初始化串口,HAL_UART_Transmit 函数发送数据,HAL_UART_Receive 函数接收数据,大大简化了开发过程,即使是对硬件底层知识了解较少的开发者也能快速上手,并且 HAL 库具有较好的跨平台性,在不同型号的 STM32 上使用相同的 API,提高了代码的可移植性和复用性 。
二、传感器数据采集实战(I2C 总线)
2.1 I2C 总线基础
I2C(Inter-Integrated Circuit)总线采用两线制,由 SDA(Serial Data Line)数据线和 SCL(Serial Clock Line)时钟线组成。在数据传输过程中,SCL 线负责提供时钟信号,它就像一个指挥家,按照一定的频率发出脉冲信号,控制着整个数据传输的节奏。SDA 线则用于传输实际的数据,在 SCL 时钟信号的上升沿或下降沿,SDA 线上的数据会被发送或接收。
I2C 总线具有多主多从的特性,这意味着在一个 I2C 总线上可以连接多个主设备和多个从设备。主设备负责发起数据传输,控制总线的使用权,比如 STM32 微控制器在与其他设备通信时,常常作为主设备。从设备则只能被动响应主设备的请求,像各种传感器通常作为从设备。在一个智能环境监测系统中,可能有多个温湿度传感器、光照传感器等作为从设备,通过 I2C 总线连接到作为主设备的 STM32 上,STM32 可以根据需要依次与各个从设备进行通信,获取数据。
以温湿度传感器 AHT20 与 STM32 通信为例,首先 STM32 作为主设备,会在 SCL 线输出时钟信号,同时在 SDA 线上发送起始信号,通知 AHT20 准备接收数据。接着,STM32 会发送 AHT20 的设备地址,AHT20 接收到正确的地址后,会在 SDA 线上返回一个应答信号,表示自己已准备好接收后续指令。然后 STM32 发送读或写命令,若要读取温湿度数据,AHT20 在接收到读命令后,会将内部存储的温湿度数据通过 SDA 线逐位发送给 STM32,STM32 在 SCL 时钟信号的配合下,接收并解析这些数据,从而完成一次通信过程。
2.2 驱动实现
2.2.1 使用 STM32CubeMX 配置 I2C 外设
打开 STM32CubeMX 并选择好目标芯片型号后,在 "Pinout & Configuration" 选项卡中找到 "I2C" 外设选项。点击进入 I2C 配置界面,在 "Mode" 选项中选择 "I2C" 模式。然后在 "Configuration" 选项中,设置 "Clock Speed" 为 100kHz ,这个时钟频率决定了 I2C 通信的速度,100kHz 是一个常用的标准速率,适用于大多数传感器通信场景。设置完成后,点击右上角的 "Generate Code" 按钮,STM32CubeMX 会根据配置生成包含 I2C 初始化代码的工程文件,这些代码中包含了对 I2C 外设寄存器的初始化设置,确保 I2C 外设能按照我们配置的参数正常工作。
2.2.2 编写传感器读写函数
c
uint8_t aht20_read_temperature(float* temp)
这个函数的作用是从 AHT20 温湿度传感器读取温度数据。函数接收一个指向 float 类型变量的指针temp,用于存储读取到的温度值。在函数内部,首先通过 I2C 通信向 AHT20 发送读取温度的命令,然后等待 AHT20 响应并返回数据。接收到的数据是原始的二进制数据,需要按照 AHT20 的数据手册进行解析和转换,将其转换为实际的温度值后,存储到temp指向的变量中。如果读取和转换过程都正常,函数返回 0 表示成功;若出现错误,如通信超时、数据校验失败等,返回非 0 值表示错误。
c
uint8_t aht20_read_humidity(float* humi)
该函数用于读取 AHT20 温湿度传感器的湿度数据。与读取温度函数类似,它接收一个指向 float 类型变量的指针humi,用于存储湿度值。函数通过 I2C 与 AHT20 通信,发送读取湿度的命令,接收 AHT20 返回的原始数据,再根据数据手册将原始数据解析转换为实际的湿度值,存储到humi指向的变量中。同样,函数返回 0 表示读取成功,非 0 表示读取失败。
2.2.3 数据校验
AHT20 传感器返回的温度数据是 20 位的原始数据。以温度数据为例,具体解析过程如下:首先将接收到的 20 位数据进行移位操作,将有效数据位提取出来。假设接收到的 20 位数据存储在一个 32 位的变量raw_data中,通过raw_data = (raw_data >> 4) & 0xFFFFF;操作,将数据右移 4 位,并与 0xFFFFF 进行与运算,得到真正表示温度的 20 位有效数据。然后根据 AHT20 数据手册中的公式进行转换,假设转换公式为temperature = (float)raw_data * 200.0 / 1048576.0 - 50.0;,将提取出的原始数据代入公式,即可得到实际的温度值。
在数据校验方面,AHT20 可能会采用 CRC 校验等方式。我们在代码中需要根据数据手册实现相应的 CRC 校验算法。例如,在接收到数据后,计算接收到数据的 CRC 校验值,与 AHT20 发送的 CRC 校验值进行比较,如果两者相等,则说明数据在传输过程中没有出错;若不相等,则说明数据可能出现错误,需要重新读取数据。
2.3 实战
首先在 Keil MDK-ARM 中打开 STM32CubeMX 生成的工程文件。在主函数中,初始化 I2C 外设和 UART 外设。初始化 I2C 外设可以调用 STM32CubeMX 生成的初始化函数,确保 I2C 能正常与 AHT20 通信。初始化 UART 外设时,设置好波特率、数据位、停止位等参数,比如将波特率设置为 115200,以便与电脑串口助手进行通信。
在一个无限循环中,调用前面编写的aht20_read_temperature和aht20_read_humidity函数,分别读取 AHT20 的温度和湿度数据。读取到数据后,将温度和湿度数据按照一定的格式进行打包,例如将温度和湿度数据转换为字符串格式,使用sprintf函数将数据格式化输出到一个字符数组中,如sprintf(buffer, "Temperature: %.2f C, Humidity: %.2f %%RH\r\n", temperature, humidity);,其中buffer是字符数组,temperature和humidity分别是读取到的温度和湿度值。
然后调用 UART 的发送函数,将打包好的数据发送到电脑串口助手。为了实现每秒刷新一次数据,可以使用定时器中断或者简单的延时函数,如HAL_Delay(1000);,该函数会使程序暂停 1000 毫秒,即 1 秒,这样就能实现每秒读取一次 AHT20 温湿度数据并发送到电脑串口助手的功能,在串口助手上就能实时看到环境的温湿度变化情况。
三、传感器数据采集实战(ADC 外设)
3.1 ADC 基础
ADC(Analog-to-Digital Converter)即模拟数字转换器,它的主要作用是将连续变化的模拟信号转换为离散的数字信号 。以常见的音频信号为例,我们日常生活中听到的声音是模拟信号,通过麦克风采集后,输出的是随声音变化而连续变化的电压信号,而 ADC 可以对这个模拟电压信号进行采样和量化,将其转换为数字信号,这样数字设备就能对这些信号进行处理、存储和传输。
STM32 内置了 12 位的 ADC,这意味着它可以将模拟信号转换为 2 的 12 次方,即 4096 个不同的数字值。其输入电压范围通常为 0 - 3.3V,可连接多种模拟传感器信号。比如电位器,当旋转电位器的旋钮时,其输出的电压会在 0 - 3.3V 之间变化,将这个电压信号连接到 STM32 的 ADC 输入引脚,ADC 就能将其转换为对应的数字值,通过读取这个数字值,就能知道电位器的旋转位置。又如光敏电阻,其电阻值会随着光照强度的变化而改变,在一定的电路中,会输出与光照强度相关的模拟电压信号,连接到 STM32 的 ADC 后,就能将光照强度转换为数字信号,方便进行后续的处理和分析。
3.2 驱动实现
3.2.1 使用 STM32CubeMX 配置 ADC
在 STM32CubeMX 的 "Pinout & Configuration" 选项卡中找到 "ADC" 外设选项并点击进入配置界面。在 "Mode" 选项中,选择 "Continuous Conversion Mode",即连续转换模式,这样 ADC 会不断地对输入信号进行转换,无需每次都手动触发转换。在 "Channel Configuration" 区域,选择要使用的 ADC 通道,如将 PA0 引脚配置为 ADC 的输入通道,因为 PA0 引脚连接了我们需要采集信号的模拟传感器(如电位器、光敏电阻等) 。如果只需要采集单个通道的信号,只需在 "Regular Channel Sequence" 列表中添加一个条目,并将其通道设置为 PA0 对应的通道号。设置完成后,点击 "Generate Code" 按钮,生成包含 ADC 初始化代码的工程文件,这些代码会初始化 ADC 的时钟、转换模式、通道等参数,为后续的数据采集做好准备。
3.2.2 编写 ADC 采集函数
c
uint16_t adc_read_value(void)
这个函数用于读取 ADC 的原始值。在函数内部,首先调用 HAL 库提供的函数HAL_ADC_Start启动 ADC 转换,然后调用HAL_ADC_PollForConversion函数等待转换完成,该函数会阻塞程序执行,直到 ADC 完成一次转换。转换完成后,通过HAL_ADC_GetValue函数获取 ADC 转换后的原始值,并返回这个值。这个原始值是一个介于 0 - 4095 之间的整数,代表了输入模拟信号对应的数字量。
假设要将 ADC 采集到的原始值转换为实际的电位器电压,可使用公式voltage = adc_value * 3.3f / 4095,其中adc_value是adc_read_value函数返回的原始值,3.3f 表示 STM32 的 ADC 参考电压为 3.3V,4095 是 12 位 ADC 能表示的最大数字值。通过这个公式,就能将原始的 ADC 值转换为实际的电压值,单位为伏特(V),这样我们就能直观地了解电位器输出的电压大小。
3.3 实战
在实际应用中,我们希望采集光敏电阻的 ADC 值,并根据这个值控制 LED 的亮度。实现思路如下:在主函数中,首先初始化 ADC 和 LED 相关的 GPIO 引脚。初始化 ADC 时,可调用 STM32CubeMX 生成的初始化函数,确保 ADC 能正常工作。初始化 LED 引脚时,将其设置为输出模式,以便控制 LED 的亮灭和亮度。
在一个无限循环中,调用adc_read_value函数读取光敏电阻的 ADC 值。根据读取到的 ADC 值来调整 LED 的亮度,ADC 值越小,说明光照强度越强,此时需要让 LED 更亮;ADC 值越大,光照强度越弱,LED 亮度应降低。通过 PWM(Pulse Width Modulation,脉冲宽度调制)来实现 LED 亮度的调节。PWM 是一种通过改变脉冲信号的占空比(即高电平在一个周期内所占的比例)来控制输出信号平均功率的技术。在 STM32 中,可以利用定时器产生 PWM 信号,比如使用 TIM3 定时器,配置其为 PWM 输出模式,根据 ADC 值计算出对应的占空比,然后通过设置 TIM3 的比较寄存器的值来改变 PWM 信号的占空比,从而实现对 LED 亮度的控制。例如,当 ADC 值为 0 时,将 PWM 占空比设置为 100%,LED 全亮;当 ADC 值为 4095 时,将 PWM 占空比设置为 0%,LED 熄灭,在两者之间时,根据 ADC 值按比例调整占空比,实现 LED 亮度的平滑变化。
3.4 数据存储
将采集到的温湿度、光敏数据存储到 STM32 内置 Flash 具有重要意义。一方面,在一些需要长期记录数据的应用场景中,如环境监测系统,即使设备断电,存储在 Flash 中的历史数据也不会丢失,方便后续查询和分析环境数据的变化趋势。另一方面,当设备重新上电后,可以读取这些历史数据,快速恢复到之前的工作状态或进行数据的连续性分析。
在 STM32 中实现数据存储到内置 Flash,可使用 HAL 库提供的函数。在存储数据前,首先需要解锁 Flash,调用HAL_FLASH_Unlock函数实现。然后,确定要存储数据的 Flash 地址,根据数据手册可知 STM32 内部 Flash 的地址范围,选择合适的地址区域,要注意避开存储程序代码的区域,以免覆盖程序导致设备无法正常运行。假设我们选择从地址0x08010000开始存储数据。在存储温湿度数据时,将温度和湿度数据按照一定的格式进行打包,如将它们组合成一个结构体,然后将结构体的数据写入 Flash。写入前,需要先擦除目标 Flash 区域,因为 Flash 的写入操作必须在已擦除的区域进行,调用HAL_FLASHEx_Erase函数进行擦除操作,指定擦除的类型(如页擦除)、起始地址和擦除的页数。擦除完成后,使用HAL_FLASH_Program函数将打包好的数据写入 Flash。
当设备重新上电后,读取历史数据时,首先要确定数据存储的地址,然后使用指针操作直接读取该地址的数据。例如,定义一个指向存储数据结构体的指针,将其指向存储数据的 Flash 地址,通过解引用指针即可获取存储的数据,再将数据解析为温度、湿度和光敏值,供后续程序使用,实现数据的恢复和继续处理 。