温湿度采集与OLED显示
一、I2C总线协议
I2C(Inter-Integrated Circuit)是一种用于连接低速设备到处理器或微控制器的串行通信协议。I2C协议允许多个设备共享同一总线,通过地址来区分不同的设备。根据实现方式的不同,I2C可以分为软件I2C和硬件I2C。
1. 硬件I2C:
- 硬件I2C是由微控制器或处理器的专用硬件模块实现的。这个模块负责生成I2C时序和处理数据传输。
- 硬件I2C的优点包括:
- 高可靠性:由于时序由硬件控制,因此更加精确和可靠。
- 低CPU负载:硬件模块可以独立处理I2C通信,减轻CPU的负担。
- 易于使用:通常有现成的库和API供开发者使用,简化了编程工作。
- 硬件I2C的缺点是灵活性较低,因为它依赖于特定的硬件模块。
2. 软件I2C:
- 软件I2C是通过软件代码来模拟I2C总线的时序和通信协议。它不依赖于专用的硬件模块,而是使用通用的I/O引脚来模拟I2C信号。
- 软件I2C的优点包括:
- 灵活性高:可以在任何具有可用I/O引脚的设备上实现。
- 可定制性:开发者可以根据需要调整时序和参数。
- 软件I2C的缺点是:
- CPU负载较高:需要CPU实时处理I2C通信,可能导致较高的CPU占用率。
- 时序精度较低:由于时序由软件控制,可能不如硬件I2C精确。
- 复杂性较高:实现软件I2C需要编写更多的代码,并且调试可能更加复杂。
总结来说,硬件I2C依赖于专用硬件模块,具有更高的可靠性和较低的CPU负载,而软件I2C通过软件模拟实现,具有更高的灵活性和可定制性,但可能导致较高的CPU负载和时序精度较低。
二、实现温湿度采集
1.配置芯片
在STM32CubeMX中新建工程并选择我们所需的芯片

配置RCC

配置SYS

配置USART1

配置I2C2

配置TIM1

配置时钟

设置项目


之后生成代码
添加AHT20文件
1.新建文件夹

在keil中新建组

将新建的组名字改为与之前的文件夹同样的名字

在组里添加新文件

分别添加一个.c文件和.h文件


2.实验代码
在文件中添加代码
aht20.c
#include "aht20.h"
#define AHT20_ADDRESS 0x70 // 从机地址
//AHT20 的驱动程序
void AHT20_Init () //AHT20初始化函数 记住要在"aht20.h"中声明
{
uint8_t readBuffer;//用于接收状态信息
HAL_Delay(40);
HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, &readBuffer, 1, HAL_MAX_DELAY);//I2C读取函数,读数据函数 readBuffer此时获得了一个字节的状态字。
if((readBuffer & 0x08) == 0x00) //判断第三位是否为0 发送0xBE命令初始化
{
uint8_t sendBuffer [3] = {0xBE , 0x08 , 0x00};
HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);//I2C发送函数
}
}
void AHT20_Read(float *Temperature , float *Humidity) //AHT20读取温度湿度函数 记住要在"aht20.h"中声明
{
uint8_t sendBuffer [3] = {0xAC , 0x33 , 0x00};
uint8_t readBuffer [6];
HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
HAL_Delay(75);
HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, readBuffer, 6, HAL_MAX_DELAY);
if((readBuffer[0] & 0x80) == 0x00)
{
uint32_t date = 0;//接收温湿度需要2个半字节 所以要32
date = ((uint32_t )readBuffer[3] >> 4) + ((uint32_t )readBuffer[2] << 4) + ((uint32_t )readBuffer[1] << 12);//对数据进行移位拼接.
*Humidity = date * 100.0f / (1 << 20);//(1 << 20) 意为2的20次方. 乘100.0可以表示为百分数
date = (((uint32_t )readBuffer[3] & 0x0F)<< 16) + ((uint32_t )readBuffer[4] << 8) + (uint32_t )readBuffer[5];//& 0x0F: 将这个无符号整数与十六进制数0x0F进行按位与操作。0x0F的二进制表示为00001111,这个操作会保留readBuffer[3]的低四位,即将高四位清零。
*Temperature = date * 200.0f / (1 << 20) - 50;
}
}
aht20.h
/*
* aht20.h
*
* Created on: Apr 25, 2024
* Author: lenovo
*/
#ifndef INC_AHT20_H_
#define INC_AHT20_H_
#include "i2c.h"
void AHT20_Init (void);
void AHT20_Read(float *Temperature , float *Humidity);
#endif /* INC_AHT20_H_ */
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "i2c.h"
#include <stdio.h>
#include "string.h"
#include "aht20.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
AHT20_Init ();//初始化AHT20
float temperature , humidity ; //定义温湿度变量
char message [50]; //后续串口打印数据
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
AHT20_Read( &temperature , &humidity); //读取AHT20
sprintf(message ,"温度: %f℃ , 湿度: %f.\r\n",temperature , humidity);//拼接
HAL_UART_Transmit(&huart1, (uint8_t*)message,strlen(message) , HAL_MAX_DELAY);//串口发送函数
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
之后连接实物,再将串口连接电脑,得到传感器结果
3.实验结果

三、OLED屏显
1.显示自己的学号和姓名
(1)u8g2库的移植
新建工程
配置RCC

配置SYS

配置I2C2

配置TIM1

配置时钟

配置项目

下载通过网盘分享的文件:u8g2-master.zip
链接: https://pan.baidu.com/s/13E0xfqspVAFwrcQMkbjnkw 提取码: jp3k
在刚刚建立的项目中新建两个文件夹

将链接里面的文件下载解压,把文件里面csrc文件夹的所有文件复制到u8g2文件夹里面
进入keil,新建分组,命名为u8g2

把下载的文件里面csrc文件夹的所有文件添加到u8g2组里面

随后将新建文件夹(U8g2和test)的路径加进去
点击魔法棒,选择c/c++

精简u8g2_d_setup.c:在keil的u8g2组里面打开u8g2_d_setup.c

(保留上面这部分,其他全部删除或者注释掉)
精简u8g2_d_memory.c:在keil的u8g2组里面打开u8g2_d_memory.c

(保留上面这部分,其他全部删除或者注释掉)
编写移植函数(.c和.h)

stm32_u8g2.h
#ifndef __STM32_U8G2_H
#define __STM32_U8G2_H
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "u8g2.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
#define u8 unsigned char // ?unsigned char ????
#define MAX_LEN 128 //
#define OLED_ADDRESS 0x78 // oled
#define OLED_CMD 0x00 //
#define OLED_DATA 0x40 //
/* USER CODE BEGIN Prototypes */
uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
void u8g2Init(u8g2_t *u8g2);
void draw(u8g2_t *u8g2);
void testDrawPixelToFillScreen(u8g2_t *u8g2);
#endif
stm32_u8g2.c
#include "stm32_u8g2.h"
#include "tim.h"
#include "i2c.h"
uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
/* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
static uint8_t buffer[128];
static uint8_t buf_idx;
uint8_t *data;
switch (msg)
{
case U8X8_MSG_BYTE_INIT:
{
/* add your custom code to init i2c subsystem */
MX_I2C2_Init(); //I2C初始化
}
break;
case U8X8_MSG_BYTE_START_TRANSFER:
{
buf_idx = 0;
}
break;
case U8X8_MSG_BYTE_SEND:
{
data = (uint8_t *)arg_ptr;
while (arg_int > 0)
{
buffer[buf_idx++] = *data;
data++;
arg_int--;
}
}
break;
case U8X8_MSG_BYTE_END_TRANSFER:
{
if (HAL_I2C_Master_Transmit(&hi2c2, OLED_ADDRESS, buffer, buf_idx, 1000) != HAL_OK)
return 0;
}
break;
case U8X8_MSG_BYTE_SET_DC:
break;
default:
return 0;
}
return 1;
}
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
switch (msg)
{
case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
__NOP();
break;
case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
for (uint16_t n = 0; n < 320; n++)
{
__NOP();
}
break;
case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
HAL_Delay(1);
break;
case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
//Tims_delay_us(5);
break; // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
break; // arg_int=1: Input dir with pullup high for I2C clock pin
case U8X8_MSG_GPIO_I2C_DATA: // arg_int=0: Output low at I2C data pin
break; // arg_int=1: Input dir with pullup high for I2C data pin
case U8X8_MSG_GPIO_MENU_SELECT:
u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0);
break;
case U8X8_MSG_GPIO_MENU_NEXT:
u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0);
break;
case U8X8_MSG_GPIO_MENU_PREV:
u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0);
break;
case U8X8_MSG_GPIO_MENU_HOME:
u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0);
break;
default:
u8x8_SetGPIOResult(u8x8, 1); // default return value
break;
}
return 1;
}
//U8g2的初始化,需要调用下面这个u8g2_Setup_ssd1306_128x64_noname_f函数,该函数的4个参数含义:
//u8g2:传入的U8g2结构体
//U8G2_R0:默认使用U8G2_R0即可(用于配置屏幕是否要旋转)
//u8x8_byte_sw_i2c:使用软件IIC驱动,该函数由U8g2源码提供
//u8x8_gpio_and_delay:就是上面我们写的配置函数
void u8g2Init(u8g2_t *u8g2)
{
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay); // 初始化u8g2 结构体
u8g2_InitDisplay(u8g2); //
u8g2_SetPowerSave(u8g2, 0); //
u8g2_ClearBuffer(u8g2);
}
//void draw(u8g2_t *u8g2)
//{
// u8g2_ClearBuffer(u8g2);
//
// u8g2_SetFontMode(u8g2, 1); /*字体模式选择*/
// u8g2_SetFontDirection(u8g2, 0); /*字体方向选择*/
// u8g2_SetFont(u8g2, u8g2_font_inb24_mf); /*字库选择*/
// u8g2_DrawStr(u8g2, 0, 20, "U");
//
// u8g2_SetFontDirection(u8g2, 1);
// u8g2_SetFont(u8g2, u8g2_font_inb30_mn);
// u8g2_DrawStr(u8g2, 21,8,"8");
//
// u8g2_SetFontDirection(u8g2, 0);
// u8g2_SetFont(u8g2, u8g2_font_inb24_mf);
// u8g2_DrawStr(u8g2, 51,30,"g");
// u8g2_DrawStr(u8g2, 67,30,"\xb2");
//
// u8g2_DrawHLine(u8g2, 2, 35, 47);
// u8g2_DrawHLine(u8g2, 3, 36, 47);
// u8g2_DrawVLine(u8g2, 45, 32, 12);
// u8g2_DrawVLine(u8g2, 46, 33, 12);
//
// u8g2_SetFont(u8g2, u8g2_font_4x6_tr);
// u8g2_DrawStr(u8g2, 1,54,"github.com/olikraus/u8g2");
//
// u8g2_SendBuffer(u8g2);
// HAL_Delay(1000);
//}
//
//画点填充
void testDrawPixelToFillScreen(u8g2_t *u8g2)
{
int t = 1000;
u8g2_ClearBuffer(u8g2);
for (int j = 0; j < 64; j++)
{
for (int i = 0; i < 128; i++)
{
u8g2_DrawPixel(u8g2,i, j);
}
}
HAL_Delay(t);
}
之后添加位置

(2)显示姓名学号
1.打开PCtoLCD2002(可自行在网络下载)
2.选择模式 - 字符模式
3.点击选项,进行设置

在输入栏输入需要取模的汉字(只能是汉字),然后点击生成字模

其中我们的完整主函数为
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"
#include "u8g2.h"
#include "stm32_u8g2.h"
#include "OLED_1.h"
#include <stdio.h>
#include "string.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_TIM1_Init();
MX_I2C1_Init();
u8g2_t u8g2;
u8g2_Init(&u8g2);
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
u8g2_SetFont(&u8g2,u8g2_font_ncenB12_tf);
u8g2_DrawXBMP(&u8g2,32,0,16,16,xiang);
u8g2_DrawXBMP(&u8g2,48,0,16,16,yue);
u8g2_DrawXBMP(&u8g2,64,0,16,16,liang);
u8g2_SetFont(&u8g2,u8g2_font_ncenB10_tf);
u8g2_DrawStr(&u8g2,16,50,"632207030523");
u8g2_SendBuffer(&u8g2);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
其中,我们的字模数据保存在我的字库OLED_1.h函数中,大家也可以自己定义一个字库,或者直接在主函数中声明就可以了。我这里展示我自己定义工程的字库。
OLED_1.h
static const unsigned char xiang[] =
{0x10,0x00,0x10,0x1F,0x10,0x11,0x7E,0x11,0x10,0x1F,0x18,0x11,0x38,0x1F,0x54,0x11,0x12,0x11,0x10,0x1F,0x80,0x00,0x10,0x21,0x12,0x49,0x12,0x48,0xE1,0x0F,0x00,0x00};
static const unsigned char yue[] =
{0x00,0x00,0xF8,0x1F,0x08,0x10,0x08,0x10,0x08,0x10,0xF8,0x1F,0x08,0x10,0x08,0x10,0x08,0x10,0xF8,0x1F,0x08,0x10,0x08,0x10,0x04,0x10,0x04,0x10,0x02,0x14,0x01,0x08};
static const unsigned char liang[] =
{0x40,0x00,0x80,0x00,0xFE,0x3F,0x00,0x00,0xF8,0x0F,0x08,0x08,0xF8,0x0F,0x00,0x00,0xFE,0x7F,0x02,0x40,0xF1,0x27,0x10,0x04,0x10,0x04,0x08,0x44,0x04,0x44,0x03,0x78};
实验结果

2.滑动显示
用字库工具生成字模

在先前的OLED_1.h将之前要显示的文字的字模修改为现在所要展示的
static const unsigned char chong[] =
{0x00,0x08,0x00,0x1F,0xFC,0x00,0x80,0x00,0xFF,0x7F,0x80,0x00,0xF8,0x0F,0x88,0x08,0xF8,0x0F,0x88,0x08,0xF8,0x0F,0x80,0x00,0xFC,0x1F,0x80,0x00,0xFF,0x7F,0x00,0x00};
static const unsigned char qing[] =
{0x80,0x00,0x00,0x01,0xFC,0x7F,0x04,0x00,0x04,0x01,0x04,0x01,0x04,0x01,0xF4,0x3F,0x04,0x01,0x84,0x02,0x84,0x02,0x44,0x04,0x42,0x04,0x22,0x08,0x11,0x10,0x08,0x60};
static const unsigned char jiao[] =
{0x40,0x00,0x80,0x00,0x80,0x00,0xFF,0x7F,0x00,0x00,0x08,0x08,0x08,0x10,0x04,0x24,0x12,0x24,0x20,0x02,0x40,0x01,0x80,0x00,0x40,0x01,0x30,0x02,0x0C,0x0C,0x03,0x70};
static const unsigned char tong[] =
{0x00,0x00,0xE2,0x1F,0x04,0x08,0x84,0x05,0x00,0x02,0xE0,0x3F,0x27,0x22,0x24,0x22,0xE4,0x3F,0x24,0x22,0x24,0x22,0xE4,0x3F,0x24,0x22,0x24,0x2A,0x2A,0x10,0xF1,0x7F};
static const unsigned char da[] =
{0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0xFF,0x7F,0x80,0x00,0x80,0x00,0x40,0x01,0x40,0x01,0x20,0x02,0x20,0x02,0x10,0x04,0x08,0x08,0x04,0x10,0x03,0x60};
static const unsigned char xue[] =
{0x44,0x10,0x88,0x10,0x88,0x08,0x00,0x04,0xFE,0x7F,0x02,0x40,0x01,0x20,0xF8,0x07,0x00,0x02,0x80,0x01,0xFF,0x7F,0x80,0x00,0x80,0x00,0x80,0x00,0xA0,0x00,0x40,0x00};
main.c进行修改后完整代码为
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"
#include "u8g2.h"
#include "stm32_u8g2.h"
#include "OLED_1.h"
#include <stdio.h>
#include "string.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_TIM1_Init();
u8g2_t u8g2;
u8g2Init(&u8g2);
float temperature=1, humindity;
uint16_t x=0;
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
// u8g2_SetFont(&u8g2,u8g2_font_ncenB12_tf);
//u8g2_DrawXBMP(&u8g2,32,0,16,16,xiang);
// u8g2_DrawXBMP(&u8g2,48,0,16,16,yue);
// u8g2_DrawXBMP(&u8g2,64,0,16,16,liang);
//u8g2_SetFont(&u8g2,u8g2_font_ncenB10_tf);
// u8g2_DrawStr(&u8g2,16,50,"632207030523");
// u8g2_SendBuffer(&u8g2);
if(x<=128)
{
x++;//????,??????x???????????
}
else if(x>128)//??
{
x=0;
}
u8g2_SetFont(&u8g2,u8g2_font_ncenB12_tf);//??????
//(???????,????x?y???????????????????)
u8g2_DrawXBMP(&u8g2,x+16,0,16,16,chong);
u8g2_DrawXBMP(&u8g2,x+32,0,16,16,qing);
u8g2_DrawXBMP(&u8g2,x+48,0,16,16,jiao);
u8g2_DrawXBMP(&u8g2,x+64,0,16,16,tong);
u8g2_DrawXBMP(&u8g2,x+80,0,16,16,da);
u8g2_DrawXBMP(&u8g2,x+96,0,16,16,xue);
u8g2_SetFont(&u8g2,u8g2_font_ncenB10_tf);
u8g2_SendBuffer(&u8g2);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
之后运行并下载代码
实验结果如下
OLED移动显示
3.显示温湿度
首先我们需要将前文提到的AHT20的采集程序添加到我们的工程中,添加完之后,和之前的步骤相同,只是将串口打印改为了由OLED屏输出。需要值得注意的是,u8g2库并不能直接展示变量,所以我们还是需要使用sprintf将变量转义为字符。同时通过u8g2_DrawUTF8输出我们的UTF8编码格式的数据。main.c代码如下
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"
#include "u8g2.h"
#include "stm32_u8g2.h"
#include "OLED_1.h"
#include "aht20.h"
#include <stdio.h>
#include "string.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
u8g2_t u8g2;
u8g2_Init(&u8g2); //初始化
/* USER CODE END 2 */
float temperature=1 , humidity ;
char t[5];
char h[5];
// uint16_t x=0;
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
// //显示汉字和数字
// u8g2_SetFont(&u8g2,u8g2_font_ncenB12_tf);//设置字体格式
// //(参数顺序依次是,结构体、x、y、字宽、字高、储存要显示的字点阵的数组)
// u8g2_DrawXBMP(&u8g2,32,0,16,16,xing);
// u8g2_DrawXBMP(&u8g2,48,0,16,16,yue);
// u8g2_DrawXBMP(&u8g2,64,0,16,16,liang);
// u8g2_SetFont(&u8g2,u8g2_font_ncenB10_tf);
// u8g2_DrawStr(&u8g2,16,50,"632207030523");
// u8g2_SendBuffer(&u8g2);
//滚动显示汉字
// if(x<=128)
// {
// x++;//向右滑动,可以提高每次x的增加量来控制滑动速度
// }
// else if(x>128)//置零
// {
// x=0;
// }
// u8g2_SetFont(&u8g2,u8g2_font_ncenB12_tf);//设置字体格式
// //(参数顺序依次是,结构体、x、y、字宽、字高、储存要显示的字点阵的数组)
// u8g2_DrawXBMP(&u8g2,x+16,0,16,16,chong);
// u8g2_DrawXBMP(&u8g2,x+32,0,16,16,qing);
// u8g2_DrawXBMP(&u8g2,x+48,0,16,16,jiao);
// u8g2_DrawXBMP(&u8g2,x+64,0,16,16,tong);
// u8g2_DrawXBMP(&u8g2,x+80,0,16,16,da);
// u8g2_DrawXBMP(&u8g2,x+96,0,16,16,xue);
// u8g2_SetFont(&u8g2,u8g2_font_ncenB10_tf);
// u8g2_SendBuffer(&u8g2);
//显示采集到的温度和湿度
AHT20_Read( &temperature , &humidity); //读取AHT20
sprintf(t ," %f",temperature);//拼接
sprintf(h ," %f",humidity);//拼接
u8g2_SetFont(&u8g2,u8g2_font_ncenB12_tf);//设置字体格式
//(参数顺序依次是,结构体、x、y、字宽、字高、储存要显示的字点阵的数组)
u8g2_DrawXBMP(&u8g2,32,0,16,16,wen);
u8g2_DrawXBMP(&u8g2,48,0,16,16,du);
u8g2_DrawStr(&u8g2,64,16,":");
u8g2_SetFont(&u8g2,u8g2_font_ncenB10_tf);
u8g2_DrawXBMP(&u8g2,32,32,16,16,shi);
u8g2_DrawXBMP(&u8g2,48,32,16,16,du);
u8g2_DrawStr(&u8g2,64,48,":");
u8g2_SetFont(&u8g2,u8g2_font_ncenB10_tf);
u8g2_DrawUTF8(&u8g2,80,16,t);
u8g2_DrawUTF8(&u8g2,80,48,h);
u8g2_SendBuffer(&u8g2);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
实验结果如下:

四、实验总结
1. I2C总线通信协议的理解与应用
理解I2C协议:
通过本次实验,我对I2C总线通信协议有了更深入的理解。I2C协议是一种同步串行通信协议,通常用于连接低速外围设备(如传感器、EEPROM等)到微控制器。
I2C总线的特点是只需要两条线:数据线(SDA)和时钟线(SCL)。这种简洁的连接方式使得多个设备可以共享同一总线,减少了引脚的使用。
硬件与软件I2C的选择:
在实验中,我使用了STM32F103的硬件I2C模块来实现与AHT20温湿度传感器的通信。硬件I2C模块提供了较高的传输速率和较低的CPU负载,这在处理多个传感器数据时尤为重要。
通过对比软件I2C和硬件I2C,我认识到硬件I2C在处理复杂通信时的优势,尤其是在需要高可靠性和高效率的应用中。
2. AHT20温湿度传感器数据采集
数据采集的实现:
实验中,我成功实现了每隔2秒采集一次AHT20传感器的温湿度数据,并通过串口发送到上位机。这一过程涉及到对AHT20数据手册的深入阅读和理解,以及对STM32串口通信的配置。
在数据采集过程中,我遇到了一些挑战,如确保数据的准确性和稳定性。通过调整采样频率和优化数据处理算法,我最终实现了稳定的数据采集。
3. OLED显示屏的应用
OLED显示原理:
通过实验,我理解了OLED显示屏的工作原理和汉字点阵编码方法。OLED显示屏通过控制每个像素点的亮度来显示图像,这使得它能够提供高对比度和低功耗的显示效果。
在实验中,我学会了如何使用STM32F103的SPI或I2C接口来控制OLED显示屏,实现了复杂的显示效果,如滑动显示长字符。
滑动显示的实现:
实现长字符的滑动显示是一个技术挑战,需要对OLED显示屏的刷新机制有深入的理解。通过使用硬件刷屏模式,我成功实现了平滑的滑动效果,这在显示长文本或动态信息时非常有用。
4.心得体会
本次实验让我深刻体会到硬件与软件结合的重要性。通过对STM32F103的编程和外围设备的连接,我掌握了如何将理论知识应用于实际问题的解决中。
实验中遇到的各种技术挑战,如I2C通信的调试和OLED显示的优化,通过查阅资料和反复测试,我最终实现了预期功能。这一过程不仅提升了我的技术能力,也增强了我的问题解决能力。
在实验过程中,我对I2C和SPI通信协议有了更深入的理解。这些协议的细节对于确保设备间的可靠通信至关重要。
通过对协议的深入研究,我学会了如何优化通信过程,减少错误和干扰,这对于提高系统的稳定性和性能非常重要。
实验不仅提升了我的技术能力,也加深了我对嵌入式系统的理解。通过对STM32F103的编程和外围设备的连接,我掌握了如何设计和实现一个完整的嵌入式系统。
这次实验经验为我今后的学习和工作打下了坚实的基础,让我对嵌入式系统的设计和开发有了更全面的认识。
通过这次实验,我不仅掌握了I2C和SPI通信协议的应用,还提升了我的编程能力和硬件调试技巧。这些经验将对我今后的学习和工作产生深远的影响。
897

被折叠的 条评论
为什么被折叠?



