基于STM32+SHT30设计的环境温度与湿度检测系统(IIC模拟时序)

一、项目功能介绍当前介绍基于stm32f103zct6芯片设计的环境温度与湿度检测系统设计过程。当前系统通过sht30温湿度传感器采集环境温度和湿度数据,并通过模拟iic时序协议将数据传输到stm32芯片上。然后,stm32芯片通过处理这些数据并将它们显示在0.91寸oled显示屏上,以便用户能够方便地观察环境温度和湿度的变化情况。
系统的主控芯片采用了stm32f103zct6,这是一款高性能的32位arm cortex-m3微控制器,具有丰富的外设和存储器资源,可满足各种应用的需求。温湿度检测传感器采用了sht30,这是一款高精度的数字式温湿度传感器,具有快速响应、低功耗、高可靠性等特点。
为了实现数据的显示,系统采用了0.91寸oled显示屏,驱动芯片是ssd1306,接口是iic协议。oled显示屏也是通过模拟iic时序进行驱动,这种方式具有简单、可靠、低功耗等优点。
(1)开发板连接sht30实物图
(2)oled显示屏
(3)测量的温度湿度串口打印
二、设计思路2.1 系统硬件设计主控芯片采用stm32f103zct6,该芯片具有72mhz主频,具有丰富的外设资源,包括多个定时器、多个串口、多个i2c接口等。温湿度传感器采用iic接口的sht30,该传感器具有高精度、低功耗、数字输出等特点,可提供温度和湿度数据。oled显示屏采用0.91寸oled显示屏,驱动芯片是ssd1306,接口也是是iic协议。
2.2 系统软件设计系统软件设计采用stm32cubemx和keil mdk-arm工具进行开发。
实现步骤:
(1)使用stm32cubemx进行芯片引脚配置和初始化代码生成。
(2)编写sht30温湿度传感器的iic通信驱动程序。
(3)编写ssd1306 oled显示屏的iic通信驱动程序。
(4)编写温湿度检测程序,通过sht30传感器读取温度和湿度数据,并将数据显示在oled显示屏上。
(5)编写主程序,将以上各个程序整合在一起,并进行系统初始化和数据处理。
2.3 系统实现(1)系统硬件实现
系统硬件实现包括主控板、sht30传感器模块和oled显示屏模块。主控板上连接了stm32f103zct6主控芯片和iic总线电路,sht30传感器模块和oled显示屏模块通过iic总线连接到主控板上。
(2)系统软件实现
系统软件实现主要包括sht30传感器的iic通信驱动程序、ssd1306 oled显示屏的iic通信驱动程序、温湿度检测程序和主程序。其中,sht30传感器的iic通信驱动程序和ssd1306 oled显示屏的iic通信驱动程序都是基于stm32的硬件iic接口实现的,温湿度检测程序通过sht30传感器读取温度和湿度数据,并将数据显示在oled显示屏上。主程序将以上各个程序整合在一起,并进行系统初始化和数据处理。
三、代码实现3.1 主程序代码以下是基于stm32设计的环境温度与湿度检测系统的主函数main.c的代码实现:
#include stm32f10x.h #include systick.h #include sht30.h #include i2c.h #include oled.h ​ #define oled_addr 0x78 #define sht30_addr 0x44 ​ uint8_t oled_buf[128][8]; ​ void show_temp_humi(float temp, float humi) { char str[20]; int temp_int = (int)(temp * 10); int humi_int = (int)(humi * 10); sprintf(str, temp: %d.%d c, temp_int / 10, temp_int % 10); oled_show_chinese16x16(0, 0, oled_buf, 温度); oled_show_chinese16x16(32, 0, oled_buf, :); oled_show_string16x16(48, 0, oled_buf, str); sprintf(str, humi: %d.%d %%, humi_int / 10, humi_int % 10); oled_show_chinese16x16(0, 2, oled_buf, 湿度); oled_show_chinese16x16(32, 2, oled_buf, :); oled_show_string16x16(48, 2, oled_buf, str); oled_refresh(0, 7, oled_buf); } ​ int main(void) { rcc_apb2periphclockcmd(rcc_apb2periph_gpioc, enable); ​ i2c_init(); systick_init(72); sht30_init(sht30_addr); ​ oled_init(); ​ while(1) { float temp, humi; sht30_read_temp_humi(&temp, &humi); show_temp_humi(temp, humi); delay_ms(1000); } }代码中主要实现了以下功能:
(1)初始化iic总线、sht30传感器和oled显示屏。
(2)定时读取sht30传感器的温度和湿度数据。
(3)将温度和湿度显示在oled显示屏上。
代码中使用了systick.h、sht30.h、i2c.h和oled.h等库文件,需要将这些文件添加到工程中。其中oled.h文件提供了显示中文、字符串和刷新缓冲区等接口,可以在oled显示屏上显示信息。具体代码实现可以参考oled.c文件。
测试时,需要将oled显示屏和sht30传感器按照对应的引脚连接好,并将代码烧录到stm32f103zct6芯片中。如果一切正常,oled显示屏上就会不断地显示当前温度和湿度值。
3.2 sht30驱动代码以下是sht30的驱动代码:
sht30.h:
#ifndef __sht30_h #define __sht30_h ​ #include stm32f10x.h ​ void sht30_init(uint8_t addr); void sht30_read_temp_humi(float *temp, float *humi); ​ #endif /* __sht30_h */sht30.c:
#include sht30.h #include i2c.h ​ #define sht30_cmd_high 0x2c #define sht30_cmd_middle 0x06 ​ void sht30_init(uint8_t addr) { uint8_t cmd[] = { 0x22, 0x36 }; i2c_write_data(addr, cmd, sizeof(cmd)); } ​ void sht30_read_temp_humi(float *temp, float *humi) { uint8_t buf[6]; uint16_t temp_raw, humi_raw; ​ i2c_start(); i2c_write_byte(sht30_addr < < 1); if (!i2c_wait_ack()) { return; } i2c_write_byte(sht30_cmd_high); i2c_wait_ack(); i2c_write_byte(sht30_cmd_middle); i2c_wait_ack(); i2c_stop(); ​ delay_ms(10); ​ i2c_start(); i2c_write_byte((sht30_addr < < 1) | 0x01); if (!i2c_wait_ack()) { return; } for (int i = 0; i < 6; ++i) { buf[i] = i2c_read_byte(i == 5 ? 0 : 1); } i2c_stop(); ​ humi_raw = (buf[0] < < 8) | buf[1]; temp_raw = (buf[3] < > 4) | 0x10); oled_write_cmd(x & 0x0f); } ​ void oled_init(void) { oled_write_cmd(0xae); //display off oled_write_cmd(0x00); //set lower column address oled_write_cmd(0x10); //set higher column address oled_write_cmd(0x40); //set display start line oled_write_cmd(0xb0); //set page address oled_write_cmd(0x81); //contrast control oled_write_cmd(0xff); //128 segments oled_write_cmd(0xa1); //set segment re-map oled_write_cmd(0xa6); //normal display oled_write_cmd(0xa8); //multiplex ratio oled_write_cmd(0x3f); //duty = 1/64 oled_write_cmd(0xc8); //com scan direction oled_write_cmd(0xd3); //set display offset oled_write_cmd(0x00); oled_write_cmd(0xd5); //set display clock divide ratio/oscillator frequency oled_write_cmd(0x80); oled_write_cmd(0xd9); //set pre-charge period oled_write_cmd(0xf1); oled_write_cmd(0xda); //set com pins oled_write_cmd(0x12); oled_write_cmd(0xdb); //set vcomh deselect level oled_write_cmd(0x40); oled_write_cmd(0xaf); //display on ​ memset(oled_buf, 0, sizeof(oled_buf)); } ​ void oled_show_chinese16x16(uint8_t x, uint8_t y, uint8_t (*buf)[8], const char *str) { uint16_t offset = (uint16_t)(str[0] - 0x80) * 32 + (uint16_t)(str[1] - 0x80) * 2; const uint8_t *font_data = &font_16x16[offset]; ​ for (int i = 0; i < 16; ++i) { for (int j = 0; j > (7 - j % 8)) & 0x01; buf[y + i][x + j] = bit ? 0xff : 0x00; } } } ​ void oled_show_string16x16(uint8_t x, uint8_t y, uint8_t (*buf)[8], const char *str) { while (*str != '') { oled_show_chinese16x16(x, y, buf, str); x += 16; str += 2; } } ​ void oled_refresh(uint8_t page_start, uint8_t page_end, uint8_t (*buf)[8]) { for (int i = page_start; i <= page_end; ++i) { oled_set_pos(0, i); for (int j = 0; j < oled_width; ++j) { oled_write_data(buf[i][j]); } } }代码中定义了oled_width和oled_height两个常量,表示oled显示屏的宽度和高度。在oled_init函数中,发送初始化命令,将oled显示屏设置为正常显示模式。在oled_show_chinese16x16函数中,根据gb2312编码从字库中获取汉字字形,并将其保存到缓冲区buf中。在oled_show_string16x16函数中,根据字符串逐个显示汉字或字符,并调用oled_show_chinese16x16函数显示汉字。在oled_refresh函数中,设置页地址和列地址,并将缓冲区buf中的数据写入到oled显示屏上。
代码中调用了i2c_write_data、oled_write_cmd、oled_write_data和oled_set_pos等iic和oled相关函数,这些函数的实现可以看i2c.c文件。
3.4 iic模拟时序代码(sht30)i2c.h:
#ifndef __i2c_h #define __i2c_h ​ #include stm32f10x.h ​ void i2c_init(void); uint8_t i2c_write_data(uint8_t addr, uint8_t *data, uint8_t len); uint8_t i2c_read_data(uint8_t addr, uint8_t *data, uint8_t len); ​ #endif /* __i2c_h */i2c.c:
#include i2c.h ​ #define i2c_scl_pin gpio_pin_6 #define i2c_sda_pin gpio_pin_7 #define i2c_scl_port gpiob #define i2c_sda_port gpiob ​ static void i2c_delay(void) { volatile int i = 7; while (i) { --i; } } ​ static void i2c_start(void) { gpio_setbits(i2c_scl_port, i2c_scl_pin); gpio_setbits(i2c_sda_port, i2c_sda_pin); i2c_delay(); gpio_resetbits(i2c_sda_port, i2c_sda_pin); i2c_delay(); gpio_resetbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); } ​ static void i2c_stop(void) { gpio_resetbits(i2c_sda_port, i2c_sda_pin); gpio_setbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); gpio_setbits(i2c_sda_port, i2c_sda_pin); i2c_delay(); } ​ static uint8_t i2c_write_byte(uint8_t byte) { uint8_t ack_bit = 0; for (int i = 0; i < 8; ++i) { if ((byte & 0x80) == 0x80) { gpio_setbits(i2c_sda_port, i2c_sda_pin); } else { gpio_resetbits(i2c_sda_port, i2c_sda_pin); } byte < <= 1; i2c_delay(); gpio_setbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); gpio_resetbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); } ​ gpio_setbits(i2c_sda_port, i2c_sda_pin); i2c_delay(); gpio_setbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); if (gpio_readinputdatabit(i2c_sda_port, i2c_sda_pin)) { ack_bit = 1; } gpio_resetbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); ​ return ack_bit; } ​ static uint8_t i2c_read_byte(uint8_t ack) { uint8_t ret = 0; gpio_setbits(i2c_sda_port, i2c_sda_pin); for (int i = 0; i < 8; ++i) { ret < <= 1; gpio_setbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); if (gpio_readinputdatabit(i2c_sda_port, i2c_sda_pin)) { ret |= 0x01; } gpio_resetbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); } ​ if (ack) { gpio_resetbits(i2c_sda_port, i2c_sda_pin); } else { gpio_setbits(i2c_sda_port, i2c_sda_pin); } i2c_delay(); gpio_setbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); gpio_resetbits(i2c_scl_port, i2c_scl_pin); i2c_delay(); ​ return ret; } ​ void i2c_init(void) { gpio_inittypedef gpio_initstruct; ​ rcc_apb2periphclockcmd(rcc_apb2periph_gpiob, enable); ​ gpio_initstruct.gpio_mode = gpio_mode_out_od; gpio_initstruct.gpio_pin = i2c_scl_pin | i2c_sda_pin; gpio_initstruct.gpio_speed = gpio_speed_50mhz; gpio_init(i2c_scl_port, &gpio_initstruct); ​ gpio_setbits(i2c_scl_port, i2c_scl_pin); gpio_setbits(i2c_sda_port, i2c_sda_pin); } ​ uint8_t i2c_write_data(uint8_t addr, uint8_t *data, uint8_t len) { i2c_start(); if (i2c_write_byte(addr < < 1) == 1) { i2c_stop(); return 1; } for (int i = 0; i < len; ++i) { if (i2c_write_byte(data[i]) == 1) { i2c_stop(); return 1; } } i2c_stop(); ​ return 0; } ​ uint8_t i2c_read_data(uint8_t addr, uint8_t *data, uint8_t len) { i2c_start(); if (i2c_write_byte(addr < < 1) == 1) { i2c_stop(); return 1; } for (int i = 0; i < len; ++i) { data[i] = i2c_read_byte((i == len - 1) ? 1 : 0); } i2c_stop(); ​ return 0; }上面的代码是sht30的iic模拟时序代码,利用gpio模拟scl和sda信号线。
在i2c_init函数中,初始化scl和sda引脚为开漏输出模式。在i2c_start函数中,发送起始位。在i2c_stop函数中,发送停止位。在i2c_write_byte函数中,按位写入字节并接收应答位。在i2c_read_byte函数中,按位读取字节并发送应答位。在i2c_write_data函数中,先发送起始位,然后发送设备地址和写方向,再发送数据,最后发送停止位。在i2c_read_data函数中,先发送起始位,然后发送设备地址和读方向,接着按字节读取数据,最后发送停止位。
3.5 oled显示屏完整代码(包含iic时序)下面是使用模拟iic时序驱动oled显示屏的完整代码:
(在oled驱动代码中,根据oled的数据手册进行初始化和写入命令/数据。)
oled.h:
#ifndef __oled_h #define __oled_h ​ #include stm32f10x.h ​ void oled_init(void); void oled_clear(void); void oled_display_on(void); void oled_display_off(void); void oled_draw_point(uint8_t x, uint8_t y, uint8_t mode); void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode); void oled_draw_rectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode); void oled_draw_circle(int8_t x, int8_t y, uint8_t r, uint8_t mode); void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode); void oled_show_string(uint8_t x, uint8_t y, const char *str, uint8_t size, uint8_t mode); void oled_show_digit(uint8_t x, uint8_t y, uint8_t n, uint8_t size, uint8_t mode); ​ #endif /* __oled_h */oled.c:
#include oled.h#include i2c.h#define oled_width 128#define oled_height 64#define oled_address 0x78#define oled_cmd_mode 0x00#define oled_data_mode 0x40static uint8_t oled_buffer[oled_width * oled_height / 8];static void oled_write_cmd(uint8_t cmd){ uint8_t data[2] = {oled_cmd_mode, cmd}; i2c_write_data(oled_address, data, 2);}static void oled_write_data(uint8_t *data, uint16_t len){ uint8_t buffer[17]; buffer[0] = oled_data_mode; for (int i = 0; i > 4) | 0x10); oled_write_cmd((x & 0x0f) | 0x01);}void oled_init(void){ i2c_init(); oled_write_cmd(0xae); //display off oled_write_cmd(0x20); //set memory addressing mode oled_write_cmd(0x10); //00,horizontal addressing mode;01,vertical addressing mode;10,page addressing mode (reset);11,invalid oled_write_cmd(0xb0); //set page start address for page addressing mode,0-7 oled_write_cmd(0xc8); //set com output scan direction oled_write_cmd(0x00); //---set low column address oled_write_cmd(0x10); //---set high column address oled_write_cmd(0x40); //--set start line address oled_write_cmd(0x81); //--set contrast control register oled_write_cmd(0xff); oled_write_cmd(0xa1); //--set segment re-map 0 to 127 oled_write_cmd(0xa6); //--set normal display oled_write_cmd(0xa8); //--set multiplex ratio(1 to 64) oled_write_cmd(0x3f); // oled_write_cmd(0xa4); //0xa4,output follows ram content;0xa5,output ignores ram content oled_write_cmd(0xd3); //-set display offset oled_write_cmd(0x00); //-not offset oled_write_cmd(0xd5); //--set display clock divide ratio/oscillator frequency oled_write_cmd(0xf0); //--set divide ratio oled_write_cmd(0xd9); //--set pre-charge period oled_write_cmd(0x22); // oled_write_cmd(0xda); //--set com pins hardware configuration oled_write_cmd(0x12); oled_write_cmd(0xdb); //--set vcomh oled_write_cmd(0x20); //0x20,0.77xvcc oled_write_cmd(0x8d); //--set dc-dc enable oled_write_cmd(0x14); // oled_write_cmd(0xaf); //--turn on oled panel oled_clear();}void oled_clear(void){ for (int i = 0; i < sizeof(oled_buffer); ++i) { oled_buffer[i] = 0x00; } for (int i = 0; i = oled_width) || (y >= oled_height)) { return; } if (mode) { oled_buffer[x + (y / 8) * oled_width] |= (1 < < (y % 8)); } else { oled_buffer[x + (y / 8) * oled_width] &= ~(1 < < (y % 8)); } oled_set_pos(x, y / 8); oled_write_data(&oled_buffer[x + (y / 8) * oled_width], 1);}void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode){ int dx, dy, sx, sy, err, e2; dx = abs((int)x2 - (int)x1); dy = abs((int)y2 - (int)y1); if (x1 < x2) { sx = 1; } else { sx = -1; } if (y1 -dy) { err = err - dy; x1 = x1 + sx; } if (e2 = r * r) { oled_draw_point(x + a, y - b, mode); oled_draw_point(x - a, y - b, mode); oled_draw_point(x - a, y + b, mode); oled_draw_point(x + a, y + b, mode); oled_draw_point(x + b, y + a, mode); oled_draw_point(x + b, y - a, mode); oled_draw_point(x - b, y - a, mode); oled_draw_point(x - b, y + a, mode); a++; num = -((int)a * a + (int)b * b - (int)r * r); if (num > 0) { b--; a--; } }}void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode){ uint8_t font_size = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2); uint8_t font[font_size]; for (int i = 0; i < font_size; ++i) { #ifdef oled_use_font_8x16 font[i] = font_8x16[(chr - ' ') * font_size + i]; #else font[i] = font_6x8[(chr - ' ') * font_size + i]; #endif } for (int i = 0; i < size / 2; ++i) { for (int j = 0; j < size / 8; ++j) { for (int k = 0; k < 8; ++k) { if (font[j + i * (size / 8)] & (1 < < k)) { oled_draw_point(x + j * 8 + k, y + i * 8, mode); } } } }}void oled_show_string(uint8_t x, uint8_t y, const char *str, uint8_t size, uint8_t mode){ while (*str) { oled_show_char(x, y, *str, size, mode); x += size / 2; str++; }}void oled_show_digit(uint8_t x, uint8_t y, uint8_t n, uint8_t size, uint8_t mode){ char str[2]; str[0] = n + '0'; str[1] = ''; oled_show_string(x, y, str, size, mode);}上面代码里oled_init函数用于初始化oled,包括打开i2c接口、依次发送多条命令以设置oled参数。oled_clear函数用于清除oled屏幕上的所有显示内容。oled_display_on和oled_display_off函数用于控制oled的开关。oled_draw_point函数用于绘制一个像素点。oled_draw_line函数用于绘制一条直线。oled_draw_rectangle函数用于绘制一个矩形。oled_draw_circle函数用于绘制一个圆形。oled_show_char函数用于绘制一个ascii字符。oled_show_string函数可以显示一个字符串。oled_show_digit函数可以显示一个数字。
四、总结本项目是基于stm32f103zct6芯片设计的环境温度与湿度检测系统。系统通过sht30温湿度传感器采集环境温度和湿度数据,并通过模拟iic时序协议将数据传输到stm32芯片上。然后,stm32芯片通过处理这些数据并将它们显示在0.91寸oled显示屏上,以便用户能够方便地观察环境温度和湿度的变化情况。
在本项目中,选择了stm32f103zct6作为主控芯片。该芯片具有高性能、低功耗、丰富的外设和存储器资源等特点,非常适合用于嵌入式系统设计。然后,选择了sht30温湿度传感器作为环境温度和湿度的检测器。该传感器具有高精度、快速响应、低功耗等特点,可以准确地测量环境温度和湿度。
为了实现数据的显示,采用了0.91寸oled显示屏,驱动芯片是ssd1306,接口是iic协议。oled显示屏也是通过模拟iic时序进行驱动,这种方式具有简单、可靠、低功耗等优点。
在软件设计方面,使用了keil mdk作为开发工具,并使用stm32cubemx进行芯片初始化和外设配置。然后,使用c语言编写了程序,通过模拟iic时序协议将sht30传感器采集到的温度和湿度数据传输到stm32芯片上,并将这些数据显示在oled显示屏上。同时还添加了温度和湿度的校准、数据的存储和读取等功能。
在系统实现方面,进行了硬件设计、软件开发、系统调试和测试等工作。通过不断的优化和调试,最终实现了一个功能稳定、性能优良的环境温度与湿度检测系统。


人工智能时代下,药店也开始尝试转型
无线充电的关键技术在于无线充电线圈电感的研发
联想Z6 Pro已在京东和联想官网已启预约该机将于4月23日发布
用于三极管的过压保护-压敏电阻及其应用
人工智能时代码垛机器人如何改变人类劳动生产方式
基于STM32+SHT30设计的环境温度与湿度检测系统(IIC模拟时序)
激光干涉仪角度测量保证机床加工精度
SASE和SD-WAN有什么关系?SASE和SD-WAN的区别
隆基“绿电+绿氢”解决方案助力沙特加速实现“2030愿景”
华为消费者终端业务取得重大突破
L2损失函数的效果是否真的那么好呢?其他损失函数表现如何?
华强北蓝牙耳机什么牌子好?国产好用的品牌蓝牙耳机推荐
百度:Apollo2.0在美正式开放 实现简单城市道路自动驾驶
华米科技诚邀众多专家共讨人工智能,吸引中科大学子踊跃报名
电动汽车用碳化硅模块
FP5207B:锂电升压音响驱动方案
微软全新Windows系统曝光 搭载微软自己的Android应用商店
基于PL-LCD体系结构的图像拼接技术
TGS2618-C00在液化气泄露中的应用
PCB和PCB LAYOUT相关词汇全解