ESP32S3学习——i2c,点亮0.96寸oled

本文介绍了ESP32-S3芯片上的I2C驱动程序,包括概述、功能、配置流程、主机和从机模式通信、中断处理、自定义配置和错误处理。示例代码展示了如何初始化I2C、读写寄存器以及删除驱动程序。此外,还提及了OLED显示屏的驱动知识,如初始化、地址设置和清屏操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

芯片:esp32s3

开发环境:espidfv4.4

一、官网相关资料(哎嘿嘿,中文的I2C 驱动程序 - ESP32-S3 - — ESP-IDF 编程指南 v4.4.2 文档)

1)概述

ESP32-S3 有两个 I2C 控制器(也称为端口),负责处理在 I2C 两根总线上的通信。每个控制器都可以设置为主机或从机。例如,可以同时让一个控制器用作主机,另一个用作从机。

2)功能

I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备以下功能:

  • 在主机模式下读写字节

  • 支持从机模式

  • 读取并写入寄存器,然后由主机读取/写入

3)如何使用

  1. 配置驱动程序 - 设置初始化参数(如主机模式或从机模式,SDA 和 SCL 使用的 GPIO 管脚,时钟速度等)

  2. 安装驱动程序- 激活一个 I2C 控制器的驱动,该控制器可为主机也可为从机

  3. 根据是为主机还是从机配置驱动程序,选择合适的项目

    1. 主机模式下通信 - 发起通信(主机模式)

    2. 从机模式下通信 - 响应主机消息(从机模式)

  4. 中断处理 - 配置 I2C 中断服务

  5. 用户自定义配置 - 调整默认的 I2C 通信参数(如时序、位序等)

  6. 错误处理 - 如何识别和处理驱动程序配置和通信错误

  7. 删除驱动程序- 在通信结束时释放 I2C 驱动程序所使用的资源

4)配置流程

设置 i2c_config_t 结构中的几个参数
  • 设置 I2C 工作模式 - 从 i2c_mode_t 中选择主机模式或从机模式

  • 设置 通信管脚

    • 指定 SDA 和 SCL 信号使用的 GPIO 管脚

    • 是否启用 ESP32-S3 的内部上拉电阻

  • (仅限主机模式)设置 I2C 时钟速度

  • (仅限从机模式)设置以下内容:

    • 是否应启用 10 位寻址模式

    • 定义 从机地址

然后,初始化给定 I2C 端口的配置,请使用端口号和 i2c_config_t 作为函数调用参数来调用 i2c_param_config() 函数。在此阶段,i2c_param_config() 还将其他 I2C 配置参数设置为 I2C 总线协议规范中定义的默认值。有关默认值及修改默认值的详细信息

5) 时钟选择

增加了 时钟源分配器,用于支持不同的时钟源。时钟分配器将选择一个满足所有频率和能力要求的时钟源(如 i2c_config_t::clk_flags 中的要求)。

i2c_config_t::clk_flags0 时,时钟分配器将仅根据所需频率进行选择。如果不需要诸如 APB 之类的特殊功能,则可以将时钟分配器配置为仅根据所需频率选择源时钟。为此,请将 i2c_config_t::clk_flags 设置为 0。有关时钟特性,请参见下表

时钟名称时钟频率SCL 的最大频率时钟功能
XTAL 时钟40 MHz2 MHz/
RTC 时钟20 MHz1 MHzI2C_SCLK_SRC_FLAG_AWARE_DFS, I2C_SCLK_SRC_FLAG_LIGHT_SLEEP

时钟功能是嘛

i2c_config_t::clk_flags 的解释如下:

  1. I2C_SCLK_SRC_FLAG_AWARE_DFS:当 APB 时钟改变时,时钟的波特率不会改变。

  2. I2C_SCLK_SRC_FLAG_LIGHT_SLEEP:支持轻度睡眠模式,APB 时钟则不支持。

  3. ESP32-S3 可能不支持某些标志,请在使用前阅读技术参考手册

sugar,暂时用不到这些功能

6)安装驱动程序

配置好 I2C 驱动程序后,使用以下参数调用函数 i2c_driver_install() 安装驱动程序:

  • 端口号,从 i2c_port_t 中二选一

  • 主机或从机模式,从 i2c_mode_t 中选择

  • (仅限从机模式)分配用于在从机模式下发送和接收数据的缓存区大小。I2C 是一个以主机为中心的总线,数据只能根据主机的请求从从机传输到主机。因此,从机通常有一个发送缓存区,供从应用程序写入数据使用。数据保留在发送缓存区中,由主机自行读取。

  • 用于分配中断的标志(请参考 esp_hw_support/include/esp_intr_alloc.h 中 ESP_INTR_FLAG_* 值)

7)主机模式通讯

为优化通信流程,驱动程序提供一个名为 “命令链接” 的容器,该容器应填充一系列命令,然后传递给 I2C 控制器执行。

8)向从机发送数据

使用命令链接的流程

  1. 使用 i2c_cmd_link_create() 创建一个命令链接。

    然后,将一系列待发送给从机的数据填充命令链接:

    1. 启动位 - i2c_master_start()

    2. 从机地址 - i2c_master_write_byte()。提供单字节地址作为调用此函数的实参。

    3. 数据 - 一个或多个字节的数据作为 i2c_master_write() 的实参。

    4. 停止位 - i2c_master_stop()

    函数 i2c_master_write_byte()i2c_master_write() 都有额外的实参,规定主机是否应确认其有无接受到 ACK 位。

  2. 通过调用 i2c_master_cmd_begin() 来触发 I2C 控制器执行命令链接。一旦开始执行,就不能再修改命令链接。

  3. 命令发送后,通过调用 i2c_cmd_link_delete() 释放命令链接使用的资源

9)主机读取数据

write改为i2c_master_read_byte() 和/或 i2c_master_read() 填充命令链接,在步骤 5 中配置最后一次的读取,以便主机不提供 ACK 位。

10)指示写入或读取数据

主机实际执行的操作信息存储在从机地址的最低有效位中。

因此,为了将数据写入从机,主机发送的命令链接应包含地址 (ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE

11)从机模式,还从来没用过

API 为从机提供以下功能:

  • i2c_slave_read_buffer()

    当主机将数据写入从机时,从机将自动将其存储在接收缓存区中。从机应用程序可自行调用函数 i2c_slave_read_buffer()。如果接收缓存区中没有数据,此函数还具有一个参数用于指定阻塞时间。这将允许从机应用程序在指定的超时设定内等待数据到达缓存区。

  • i2c_slave_write_buffer()

    发送缓存区是用于存储从机要以 FIFO 顺序发送给主机的所有数据。在主机请求接收前,这些数据一直存储在发送缓存区。函数 i2c_slave_write_buffer() 有一个参数,用于指定发送缓存区已满时的块时间。这将允许从机应用程序在指定的超时设定内等待发送缓存区中足够的可用空间。

peripherals/i2c 中可找到介绍如何使用这些功能的代码示例。

12)中断处理

安装驱动程序时,默认情况下会安装中断处理程序。但是,您可以通过调用函数 i2c_isr_register() 来注册自己的而不是默认的中断处理程序。在运行自己的中断处理程序时,可以参考 ESP32-S3 技术参考手册 > I2C 控制器 (I2C) > 中断 [PDF],以获取有关 I2C 控制器触发的中断描述。

调用函数 i2c_isr_free() 删除中断处理程序

13)用户自定义配置

如本节末尾所述 配置驱动程序,函数 i2c_param_config() 在初始化 I2C 端口的驱动程序配置时,也会将几个 I2C 通信参数设置为 I2C 总线协议规范 规定的默认值。 其他一些相关参数已在 I2C 控制器的寄存器中预先配置。

通过调用下表中提供的专用函数,可以将所有这些参数更改为用户自定义值。请注意,时序值是在 APB 时钟周期中定义。APB 的频率在 I2C_APB_CLK_FREQ 中指定

要更改的参数函数
SCL 脉冲周期的高电平和低电平i2c_set_period()
在产生 启动 信号期间使用的 SCL 和 SDA 信号时序i2c_set_start_timing()
在产生 停止 信号期间使用的 SCL 和 SDA 信号时序i2c_set_stop_timing()
从机采样以及主机切换时,SCL 和 SDA 信号之间的时序关系i2c_set_data_timing()
I2C 超时i2c_set_timeout()
优先发送/接收最高有效位 (LSB) 或最低有效位 (MSB),可在 i2c_trans_mode_t 定义的模式中选择i2c_set_data_mode()

上述每个函数都有一个 get 对应项来检查当前设置的值。例如,调用 i2c_get_timeout() 来检查 I2C 超时值。

要检查在驱动程序配置过程中设置的参数默认值,请参考文件 driver/i2c.c 并查找带有后缀 _DEFAULT 的定义。

通过函数 i2c_set_pin() 可以为 SDA 和 SCL 信号选择不同的管脚并改变上拉配置。如果要修改已经输入的值,请使用函数 i2c_param_config()

ESP32-S3 的内部上拉电阻范围为几万欧姆,因此在大多数情况下,它们本身不足以用作 I2C 上拉电阻。建议用户使用阻值在 I2C 总线协议规范 规定范围内的上拉电阻。

14)错误处理

大多数 I2C 驱动程序的函数在成功完成时会返回 ESP_OK ,或在失败时会返回特定的错误代码。实时检查返回的值并进行错误处理是一种好习惯。驱动程序也会打印日志消息,其中包含错误说明,例如检查输入配置的正确性。有关详细信息,请参考文件 driver/i2c.c 并用后缀 _ERR_STR 查找定义。

使用专用中断来捕获通信故障。例如,如果从机将数据发送回主机耗费太长时间,会触发 I2C_TIME_OUT_INT 中断。详细信息请参考 中断处理

如果出现通信失败,可以分别为发送和接收缓存区调用 i2c_reset_tx_fifo()i2c_reset_rx_fifo() 来重置内部硬件缓存区。

15)删除驱动程序

当使用 i2c_driver_install() 建立 I2C 通信,一段时间后不再需要 I2C 通信时,可以通过调用 i2c_driver_delete() 来移除驱动程序以释放分配的资源。

由于函数 i2c_driver_delete() 无法保证线程安全性,请在调用该函数移除驱动程序前务必确保所有的线程都已停止使用驱动程序。

16)应用示例

I2C 主机和从机示例:peripherals/i2c。已经没有啦

我将修改示例程序来驱动oled,会添加详尽的注释

二、配置流程

1)配置i2c_config_t,调用i2c_param_config,再安装驱动程序i2c_driver_install

2)读寄存器:i2c_master_write_read_device

3)写寄存器:i2c_master_write_to_device

4)删去驱动:i2c_driver_delete

就这些,不考虑一些默认参数,还是很简单呐

三、oled屏驱动简要知识

SSD1306 的每页包含了 128 个字节(1288个点,每页8行128列,一列8bit,自自上到下刷新),总共 8 页,这样刚好是 12864 的点阵大小

1)初始化:写一堆寄存器,看厂家给的就ok,就能用

2)页地址:0xb0-0xb7[2:0],低三位

3)列地址(低四位):00-0f 低四位

4)列地址(高四位):10-1f

5)对比度:0x81写8位数据,值越大越亮

6)关闭:0xae,开启oxaf

8)电荷泵:0x8d写8位数据:a2=0/1

1、清屏

for(i=0;i<8;i++)

{

OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)

//起始列地址0x00,写128个数据,写满一页

OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址

OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址

for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);

} //更新显示

 

需要注意一点,设备地址!!!!!

我使用的商家的代码

 

将写函数直接替换后屏幕并不能点亮,是设备地址的问题,图片中将地址右移动才是正确的地址,原因是:写函数再一次将地址左移,类似这样 addr<<1|WRITE

 

明白了吧

三、代码加注释

main

/* i2c - Simple example

   Simple I2C example that shows how to initialize I2C
   as well as reading and writing from and to registers for a sensor connected over I2C.

   The sensor used in this example is a MPU9250 inertial measurement unit.

   For other examples please check:
   https://github.com/espressif/esp-idf/tree/master/examples

   See README.md file to get detailed usage of this example.

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"
#include "oled.h"
#include "bmp.h"
static const char *TAG = "i2c-simple-example";

#define I2C_MASTER_SCL_IO           GPIO_NUM_22      /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO           GPIO_NUM_21      /*!< GPIO number used for I2C master data  */
#define I2C_MASTER_NUM              0                          /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ          400000                     /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE   0                          /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE   0                          /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS       1000



//     return i2c_master_write_read_device(I2C_MASTER_NUM, MPU9250_SENSOR_ADDR, &reg_addr, 1, data, len, I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);

//     uint8_t write_buf[2] = {reg_addr, data};

//     ret = i2c_master_write_to_device(I2C_MASTER_NUM, MPU9250_SENSOR_ADDR, write_buf, sizeof(write_buf), I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);



/**
 * @brief i2c master initialization
 */
static esp_err_t i2c_master_init(void)
{
    int i2c_master_port = I2C_MASTER_NUM;

    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };

    i2c_param_config(i2c_master_port, &conf);

    return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}


void app_main(void)
{
    uint8_t data[2];
    ESP_ERROR_CHECK(i2c_master_init());
    ESP_LOGI(TAG, "I2C initialized successfully");

    /* Read the MPU9250 WHO_AM_I register, on power up the register should have the value 0x71 */

    OLED_Init();			//初始化OLED  
	OLED_Clear()  	; 
	OLED_DrawBMP(0,0,128,8,BMP1);  //图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)

    //ESP_ERROR_CHECK(i2c_driver_delete(I2C_MASTER_NUM));
    ESP_LOGI(TAG, "I2C unitialized successfully");
}

oled.c

//////////////////////////////////////////////////////////////////////////////////
// 本程序只供学习使用,未经作者许可,不得用于其它任何用途
// 中景园电子
// 店铺地址:http://shop73023976.taobao.com/?spm=2013.1.0.0.M4PqC2
//
//   文 件 名   : main.c
//   版 本 号   : v2.0
//   作    者   : HuangKai
//   生成日期   : 2014-0101
//   最近修改   :
//   功能描述   : OLED 4接口演示例程(51系列)
//               说明:
//               ----------------------------------------------------------------
//               GND    电源地
//               VCC  接5V或3.3v电源
//               D0   P1^0(SCL)
//               D1   P1^1(SDA)
//               RES  接P12
//               DC   接P13
//               CS   接P14
//               ----------------------------------------------------------------
//  修改历史   :
//  日    期   :
//  作    者   : HuangKai
//  修改内容   : 创建文件
// 版权所有,盗版必究。
// Copyright(C) 中景园电子2014/3/16
// All rights reserved
//******************************************************************************/。
#include "oled.h"

#include "oledfont.h"

// OLED的显存
// 存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127

void OLED_WR_Byte(unsigned dat, unsigned cmd)
{
	int ret;
	uint8_t write_buf[2] = {cmd?0x40:0x00, dat};

	ret = i2c_master_write_to_device(0, OLED_ADDR>>1, write_buf, sizeof(write_buf), 1000 / portTICK_RATE_MS);

	if (ret != ESP_OK)
	{
		printf("err\r\n");
	}
}

/********************************************
// fill_Picture
********************************************/
void fill_picture(unsigned char fill_Data)
{
	unsigned char m, n;
	for (m = 0; m < 8; m++)
	{
		OLED_WR_Byte(0xb0 + m, 0); // page0-page1
		OLED_WR_Byte(0x00, 0);	   // low column start address
		OLED_WR_Byte(0x10, 0);	   // high column start address
		for (n = 0; n < 128; n++)
		{
			OLED_WR_Byte(fill_Data, 1);
		}
	}
}

// 坐标设置

void OLED_Set_Pos(unsigned char x, unsigned char y)
{
	OLED_WR_Byte(0xb0 + y, OLED_CMD);
	OLED_WR_Byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD);
	OLED_WR_Byte((x & 0x0f), OLED_CMD);
}
// 开启OLED显示
void OLED_Display_On(void)
{
	OLED_WR_Byte(0X8D, OLED_CMD); // SET DCDC命令
	OLED_WR_Byte(0X14, OLED_CMD); // DCDC ON
	OLED_WR_Byte(0XAF, OLED_CMD); // DISPLAY ON
}
// 关闭OLED显示
void OLED_Display_Off(void)
{
	OLED_WR_Byte(0X8D, OLED_CMD); // SET DCDC命令
	OLED_WR_Byte(0X10, OLED_CMD); // DCDC OFF
	OLED_WR_Byte(0XAE, OLED_CMD); // DISPLAY OFF
}
// 清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{
	u8 i, n;
	for (i = 0; i < 8; i++)
	{
		OLED_WR_Byte(0xb0 + i, OLED_CMD); // 设置页地址(0~7)
		OLED_WR_Byte(0x00, OLED_CMD);	  // 设置显示位置—列低地址
		OLED_WR_Byte(0x10, OLED_CMD);	  // 设置显示位置—列高地址
		for (n = 0; n < 128; n++)
			OLED_WR_Byte(0, OLED_DATA);
	} // 更新显示
}
void OLED_On(void)
{
	u8 i, n;
	for (i = 0; i < 8; i++)
	{
		OLED_WR_Byte(0xb0 + i, OLED_CMD); // 设置页地址(0~7)
		OLED_WR_Byte(0x00, OLED_CMD);	  // 设置显示位置—列低地址
		OLED_WR_Byte(0x10, OLED_CMD);	  // 设置显示位置—列高地址
		for (n = 0; n < 128; n++)
			OLED_WR_Byte(1, OLED_DATA);
	} // 更新显示
}
// 在指定位置显示一个字符,包括部分字符
// x:0~127
// y:0~63
// mode:0,反白显示;1,正常显示
// size:选择字体 16/12
void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 Char_Size)
{
	unsigned char c = 0, i = 0;
	c = chr - ' '; // 得到偏移后的值
	if (x > Max_Column - 1)
	{
		x = 0;
		y = y + 2;
	}
	if (Char_Size == 16)
	{
		OLED_Set_Pos(x, y);
		for (i = 0; i < 8; i++)
			OLED_WR_Byte(F8X16[c * 16 + i], OLED_DATA);
		OLED_Set_Pos(x, y + 1);
		for (i = 0; i < 8; i++)
			OLED_WR_Byte(F8X16[c * 16 + i + 8], OLED_DATA);
	}
	else
	{
		OLED_Set_Pos(x, y);
		// for(i=0;i<6;i++)
		// OLED_WR_Byte(F6x8[c][i],OLED_DATA);
	}
}
// m^n函数
u32 oled_pow(u8 m, u8 n)
{
	u32 result = 1;
	while (n--)
		result *= m;
	return result;
}
// 显示2个数字
// x,y :起点坐标
// len :数字的位数
// size:字体大小
// mode:模式	0,填充模式;1,叠加模式
// num:数值(0~4294967295);
void OLED_ShowNum(u8 x, u8 y, u32 num, u8 len, u8 size2)
{
	u8 t, temp;
	u8 enshow = 0;
	for (t = 0; t < len; t++)
	{
		temp = (num / oled_pow(10, len - t - 1)) % 10;
		if (enshow == 0 && t < (len - 1))
		{
			if (temp == 0)
			{
				OLED_ShowChar(x + (size2 / 2) * t, y, ' ', size2);
				continue;
			}
			else
				enshow = 1;
		}
		OLED_ShowChar(x + (size2 / 2) * t, y, temp + '0', size2);
	}
}
// 显示一个字符号串
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 Char_Size)
{
	unsigned char j = 0;
	while (chr[j] != '\0')
	{
		OLED_ShowChar(x, y, chr[j], Char_Size);
		x += 8;
		if (x > 120)
		{
			x = 0;
			y += 2;
		}
		j++;
	}
}
// 显示汉字
void OLED_ShowCHinese(u8 x, u8 y, u8 no)
{
	u8 t, adder = 0;
	OLED_Set_Pos(x, y);
	for (t = 0; t < 16; t++)
	{
		OLED_WR_Byte(Hzk[2 * no][t], OLED_DATA);
		adder += 1;
	}
	OLED_Set_Pos(x, y + 1);
	for (t = 0; t < 16; t++)
	{
		OLED_WR_Byte(Hzk[2 * no + 1][t], OLED_DATA);
		adder += 1;
	}
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[])
{
	unsigned int j = 0;
	unsigned char x, y;

	if (y1 % 8 == 0)
		y = y1 / 8;
	else
		y = y1 / 8 + 1;
	for (y = y0; y < y1; y++)
	{
		OLED_Set_Pos(x0, y);
		for (x = x0; x < x1; x++)
		{
			OLED_WR_Byte(BMP[j++], OLED_DATA);
		}
	}
}

// 初始化SSD1306
void OLED_Init(void)
{

	OLED_WR_Byte(0xAE, OLED_CMD); //--display off
	OLED_WR_Byte(0x00, OLED_CMD); //---set low column address
	OLED_WR_Byte(0x10, OLED_CMD); //---set high column address
	OLED_WR_Byte(0x40, OLED_CMD); //--set start line address
	OLED_WR_Byte(0xB0, OLED_CMD); //--set page address
	OLED_WR_Byte(0x81, OLED_CMD); // contract control
	OLED_WR_Byte(0xFF, OLED_CMD); //--128
	OLED_WR_Byte(0xA1, OLED_CMD); // set segment remap
	OLED_WR_Byte(0xA6, OLED_CMD); //--normal / reverse
	OLED_WR_Byte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3F, OLED_CMD); //--1/32 duty
	OLED_WR_Byte(0xC8, OLED_CMD); // Com scan direction
	OLED_WR_Byte(0xD3, OLED_CMD); //-set display offset
	OLED_WR_Byte(0x00, OLED_CMD); //

	OLED_WR_Byte(0xD5, OLED_CMD); // set osc division
	OLED_WR_Byte(0x80, OLED_CMD); //

	OLED_WR_Byte(0xD8, OLED_CMD); // set area color mode off
	OLED_WR_Byte(0x05, OLED_CMD); //

	OLED_WR_Byte(0xD9, OLED_CMD); // Set Pre-Charge Period
	OLED_WR_Byte(0xF1, OLED_CMD); //

	OLED_WR_Byte(0xDA, OLED_CMD); // set com pin configuartion
	OLED_WR_Byte(0x12, OLED_CMD); //

	OLED_WR_Byte(0xDB, OLED_CMD); // set Vcomh
	OLED_WR_Byte(0x30, OLED_CMD); //

	OLED_WR_Byte(0x8D, OLED_CMD); // set charge pump enable
	OLED_WR_Byte(0x14, OLED_CMD); //

	OLED_WR_Byte(0xAF, OLED_CMD); //--turn on oled panel
}

<think>好的,用户需要基于STM32F103C8T6主控和多种传感器的家庭安全系统硬件设计方案,包括阈值报警、ESP32S3CAM监控与陌生人识别、OLED显示和APP集成。首先,我需要理清各个模块的连接方式和系统架构。 首先,主控是STM32F103C8T6,需要确认其引脚资源和接口是否足够。传感器包括MQ4、MQ7、DHT11、水位传感器、门窗传感器、光电传感器。这些传感器可能需要不同的接口,比如模拟输入或数字输入。MQ4和MQ7是气体传感器,通常输出模拟信号,需要连接到ADC引脚。DHT11是温湿度传感器,使用单总线协议,可能需要一个GPIO。水位传感器可能是模拟或数字输出,同样需要ADC或GPIO。门窗传感器和光电传感器可能为数字输入,比如开关信号,连接到GPIO。 然后是ESP32S3CAM用于监控和陌生人识别,这里需要考虑与STM32的通信方式,可能是串口或者SPI/I2C。需要确定数据传输协议和命令格式。OLED屏幕通常使用I2C或SPI,根据用户提供的引用,可以选用I2C连接,如PB6和PB7。报警模块如蜂鸣器和LED需要GPIO控制。 APP集成部分可能需要无线模块,比如ESP32S3CAM自带的WiFi,或者额外添加蓝牙模块。数据上传到云端或直接与手机通信,这里需要确定通信协议,比如MQTT或HTTP。 阈值报警的逻辑需要主控实时读取传感器数据,比较设定的阈值,触发相应的报警动作。例如,气体浓度超标触发蜂鸣器,光照不足开灯等,参考引用中的控制逻辑。 硬件设计中需要注意电源管理,各传感器的供电需求,是否需要电平转换,以及PCB布局中的抗干扰措施。另外,STM32的ADC资源是否足够,可能需要分配多个模拟通道。 软件部分需要编写各个传感器的驱动,处理数据采集、处理、通信以及报警逻辑。ESP32S3CAM的陌生人识别可能需要集成图像识别算法,如使用TensorFlow Lite,或者调用云端API。APP方面可能需要开发一个能够接收数据显示和发送阈值设定的应用,考虑使用Flutter或Android Studio。 可能的问题包括传感器数据的噪声处理、多任务调度、通信稳定性以及功耗优化。需要确保各模块协同工作,避免资源冲突,比如I2C地址冲突,或者中断过多影响系统实时性。 最后,验证各个模块的功能,进行集成测试,确保报警准确,监控流畅,数据显示正确,APP交互可靠。需要参考现有的设计案例,如引用中的系统结构,调整适合当前需求的方案。</think>### 基于STM32F103C8T6的家庭安全系统硬件设计方案 #### 一、系统架构 系统以STM32F103C8T6为核心,通过多传感器采集环境参数,结合ESP32S3CAM实现监控与陌生人识别,通过OLED显示数据,并通过无线通信与APP集成。整体架构分为**数据采集层、控制处理层、交互层**三部分[^2]。 --- #### 二、硬件模块设计 1. **主控模块** - **STM32F103C8T6**:72MHz主频,64KB Flash,20KB RAM,支持多路ADC/DAC、UART、SPI、I2C接口。 - **电源设计**:3.3V供电,需配置LDO稳压电路(如AMS1117-3.3),电流需满足传感器总功耗。 2. **传感器模块** | 传感器 | 功能 | 接口类型 | STM32引脚连接 | |---------------|------------------------|----------------|-----------------------| | MQ4(甲烷) | 可燃气体浓度检测 | ADC模拟输入 | PA0(ADC1_IN0) | | MQ7(CO) | 一氧化碳浓度检测 | ADC模拟输入 | PA1(ADC1_IN1) | | DHT11 | 温湿度检测 | 单总线协议 | PB0(GPIO推挽输出) | | 水位传感器 | 漏水/水位监测 | ADC模拟输入 | PA2(ADC1_IN2) | | 门窗传感器 | 门窗开关状态检测 | GPIO数字输入 | PC13(外部中断) | | 光电传感器 | 入侵检测(光照变化) | GPIO数字输入 | PC14(外部中断) | 3. **报警模块** - **蜂鸣器**:连接PB8(GPIO推挽输出,低电平触发) - **LED指示灯**:连接PB9(GPIO推挽输出,高电平点亮) 4. **显示模块** - **OLED屏幕(0.96)**:通过I2C接口连接,引脚分配: ```text SDA -> PB7 SCL -> PB6 ``` 5. **监控与识别模块** - **ESP32S3CAM**: - 与STM32通过UART通信(TX: PA9, RX: PA10) - 图像采集:OV2640摄像头(集成在ESP32S3CAM模块) - 陌生人识别:基于TensorFlow Lite的轻量级人脸识别算法 6. **无线通信模块** - **ESP32S3CAM内置WiFi**:用于数据传输至云端/APP - **协议**:MQTT协议上传传感器数据至阿里云IoT平台 --- #### 三、关键电路设计 1. **ADC信号调理电路**(MQ4/MQ7/水位传感器) - 传感器输出端串联10kΩ电阻分压,并联0.1μF电容滤波 - 公式:$$ V_{out} = \frac{R_{load}}{R_{sensor} + R_{load}} \cdot V_{cc} $$ 2. **ESP32S3CAM与STM32通信接口** - UART波特率:115200bps,需配置电平转换电路(如TXS0108E) --- #### 四、软件逻辑设计 1. **主程序流程** ```text 初始化外设 → 读取传感器数据 → 阈值比较 → 触发报警 → 更新OLED显示 → 发送数据至ESP32S3CAM → 等待中断 ``` 2. **陌生人识别算法** - ESP32S3CAM端运行人脸检测模型(YOLO Tiny) - 比对已知人脸库(存储于SPI Flash),未匹配则触发报警 3. **APP集成方案** - **功能**:实时显示数据、报警推送、阈值设置 - **开发框架**:Android端使用MQTT客户端(如Paho),界面采用Jetpack Compose --- #### 五、阈值报警逻辑 | 报警类型 | 触发条件 | 动作 | |------------------|---------------------------------------|------------------------| | 气体超标 | MQ4/MQ7 ADC值 > 设定阈值 | 蜂鸣器鸣响,APP推送 | | 非法入侵 | 光电传感器触发 + 门窗传感器状态变化 | LED闪烁,启动摄像头录像| | 水位异常 | 水位传感器ADC值 < 阈值 | 继电器关闭模拟阀门 | --- #### 六、PCB设计注意事项 1. 模拟信号与数字信号分区布局 2. ESP32S3CAM天线区域预留净空区 3. ADC走线使用屏蔽线或短路径,避免干扰 ---
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值