复制代码
#include "soft_i2c.h"
/* ========== 简易微秒延时 ========== */
static void I2C_Delay_us(uint32_t us)
{
volatile uint32_t count = us * 2U;
while(count--) { __NOP(); }
}
/* ========== SCL/SDA 电平控制 ========== */
static inline void SCL_H(void)
{
HAL_GPIO_WritePin(SOFT_I2C_SCL_PORT, SOFT_I2C_SCL_PIN, GPIO_PIN_SET);
}
static inline void SCL_L(void)
{
HAL_GPIO_WritePin(SOFT_I2C_SCL_PORT, SOFT_I2C_SCL_PIN, GPIO_PIN_RESET);
}
static inline void SDA_H(void)
{
HAL_GPIO_WritePin(SOFT_I2C_SDA_PORT, SOFT_I2C_SDA_PIN, GPIO_PIN_SET);
}
static inline void SDA_L(void)
{
HAL_GPIO_WritePin(SOFT_I2C_SDA_PORT, SOFT_I2C_SDA_PIN, GPIO_PIN_RESET);
}
static inline uint8_t SDA_Read(void)
{
return HAL_GPIO_ReadPin(SOFT_I2C_SDA_PORT, SOFT_I2C_SDA_PIN);
}
/* ========== 初始化 ========== */
void SoftI2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能GPIO时钟 (SCL=PF1, SDA=PF0, 都在GPIOF上) */
__HAL_RCC_GPIOF_CLK_ENABLE();
/* SCL -> 推挽输出 */
GPIO_InitStruct.Pin = SOFT_I2C_SCL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SOFT_I2C_SCL_PORT, &GPIO_InitStruct);
/* SDA -> 开漏输出 (支持双向) */
GPIO_InitStruct.Pin = SOFT_I2C_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; /* 开漏,外接上拉 */
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(SOFT_I2C_SDA_PORT, &GPIO_InitStruct);
/* 初始状态: SCL=1, SDA=1 (空闲) */
SCL_H();
SDA_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
}
/* ========== 起始信号 S ========== */
void SoftI2C_Start(void)
{
/* 确保SDA为输出高 */
SDA_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
/* SDA下降沿,SCL高 */
SDA_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
}
/* ========== 停止信号 P ========== */
void SoftI2C_Stop(void)
{
SDA_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
/* SDA上升沿,SCL高 */
SDA_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
}
/* ========== 等待ACK ========== */
uint8_t SoftI2C_WaitAck(void)
{
uint8_t ack;
/* SDA设为输入 (释放SDA线) */
SDA_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
/* 读取ACK */
ack = SDA_Read();
SCL_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
return ack; /* 0=ACK, 1=NACK */
}
/* ========== 发送ACK ========== */
void SoftI2C_SendAck(void)
{
SDA_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
}
/* ========== 发送NACK ========== */
void SoftI2C_SendNack(void)
{
SDA_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
}
/* ========== 发送1字节 ========== */
void SoftI2C_SendByte(uint8_t byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
if (byte & 0x80)
SDA_H();
else
SDA_L();
byte <<= 1;
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
SCL_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
}
}
/* ========== 读取1字节 ========== */
uint8_t SoftI2C_ReadByte(void)
{
uint8_t i, byte = 0;
/* SDA设为输入 (释放SDA线) */
SDA_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
for (i = 0; i < 8; i++)
{
byte <<= 1;
SCL_H();
I2C_Delay_us(SOFT_I2C_DELAY_US);
if (SDA_Read())
byte |= 0x01;
SCL_L();
I2C_Delay_us(SOFT_I2C_DELAY_US);
}
return byte;
}
/* ========== 写数据 (设备地址 + 数据) ==========
* addr: 7位设备地址,已左移1位 (如 0x44 << 1 = 0x88)
* 返回: 0=成功, 1=无ACK
*/
uint8_t SoftI2C_Write(uint8_t addr, uint8_t *data, uint8_t len)
{
uint8_t i;
SoftI2C_Start();
SoftI2C_SendByte(addr & 0xFE); /* 写模式: bit0=0 */
if (SoftI2C_WaitAck() != 0) {
SoftI2C_Stop();
return 1;
}
for (i = 0; i < len; i++)
{
SoftI2C_SendByte(data[i]);
if (SoftI2C_WaitAck() != 0) {
SoftI2C_Stop();
return 1;
}
}
SoftI2C_Stop();
return 0;
}
/* ========== 读数据 (设备地址 + 读取) ==========
* addr: 7位设备地址,已左移1位
* 返回: 0=成功, 1=无ACK
*/
uint8_t SoftI2C_Read(uint8_t addr, uint8_t *data, uint8_t len)
{
uint8_t i;
SoftI2C_Start();
SoftI2C_SendByte(addr | 0x01); /* 读模式: bit0=1 */
if (SoftI2C_WaitAck() != 0) {
SoftI2C_Stop();
return 1;
}
for (i = 0; i < len; i++)
{
data[i] = SoftI2C_ReadByte();
if (i < len - 1)
SoftI2C_SendAck(); /* 中间字节发ACK */
else
SoftI2C_SendNack(); /* 最后一个字节发NACK */
}
SoftI2C_Stop();
return 0;
}
复制代码
#include "sht40.h"
#include "soft_i2c.h"
#include <string.h>
/* ========== SHT40 CRC8校验 ==========
* 多项式: x^8 + x^5 + x^4 + 1 (0x31)
* 初始值: 0xFF
*/
static uint8_t SHT40_CRC8(uint8_t *data, uint8_t len)
{
const uint8_t poly = 0x31U;
uint8_t crc = 0xFFU;
uint8_t i, j;
for (i = 0; i < len; i++)
{
crc ^= data[i];
for (j = 0; j < 8; j++)
{
crc = (crc & 0x80U) ? ((crc << 1U) ^ poly) : (crc << 1U);
}
}
return crc;
}
/* ========== 传感器初始化检测 ==========
* 发送软复位指令检测传感器是否在线
* 返回: 0=成功, 1=失败
*/
uint8_t SHT40_Init(void)
{
uint8_t reset_cmd = SHT40_CMD_SOFT_RESET;
/* 发送软复位指令 */
if (SoftI2C_Write(SHT40_I2C_ADDR, &reset_cmd, 1) != 0)
return 1;
/* 复位等待 1ms */
HAL_Delay(1);
return 0;
}
/* ========== 读取温湿度数据 ==========
* 使用高精度测量模式 (0xFD)
* 测量时间: 约8.2ms (典型值)
*
* 返回数据格式 (6字节):
* [0] T_MSB, [1] T_LSB, [2] T_CRC
* [3] H_MSB, [4] H_LSB, [5] H_CRC
*
* 返回: 0=成功, 1=I2C通信失败, 2=CRC校验失败
*/
uint8_t SHT40_ReadData(SHT40_Data_t *data)
{
uint8_t buf[6] = {0};
uint16_t temp_raw, humi_raw;
uint8_t meas_cmd = SHT40_MEAS_HIGH_PREC;
/* 发送测量指令 */
if (SoftI2C_Write(SHT40_I2C_ADDR, &meas_cmd, 1) != 0)
return 1;
/* 等待转换完成 (高精度模式典型8.2ms,留足余量) */
HAL_Delay(10);
/* 读取6字节数据 */
if (SoftI2C_Read(SHT40_I2C_ADDR, buf, 6) != 0)
return 1;
/* CRC校验 */
if ((SHT40_CRC8(&buf[0], 2) != buf[2]) || (SHT40_CRC8(&buf[3], 2) != buf[5]))
return 2;
/* 原始数据拼接 */
temp_raw = ((uint16_t)buf[0] << 8) | buf[1];
humi_raw = ((uint16_t)buf[3] << 8) | buf[4];
/* 官方公式换算 (datasheet公式):
* Temperature = -45 + 175 * (raw / 65535)
* Humidity = -6 + 125 * (raw / 65535)
*/
data->Temperature = -45.0f + 175.0f * ((float)temp_raw / 65535.0f);
data->Humidity = -6.0f + 125.0f * ((float)humi_raw / 65535.0f);
/* 湿度限幅 0~100% */
if (data->Humidity < 0.0f) data->Humidity = 0.0f;
if (data->Humidity > 100.0f) data->Humidity = 100.0f;
return 0;
}
/* ========== 读取序列号 (可选,用于验证传感器) ==========
* 返回: 0=成功, 1=失败
*/
uint8_t SHT40_ReadSerial(uint32_t *serial)
{
uint8_t buf[6] = {0};
uint8_t cmd = SHT40_CMD_READ_SERIAL;
if (SoftI2C_Write(SHT40_I2C_ADDR, &cmd, 1) != 0)
return 1;
HAL_Delay(1);
if (SoftI2C_Read(SHT40_I2C_ADDR, buf, 6) != 0)
return 1;
if ((SHT40_CRC8(&buf[0], 2) != buf[2]) || (SHT40_CRC8(&buf[3], 2) != buf[5]))
return 2;
*serial = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
((uint32_t)buf[3] << 8) | buf[4];
return 0;
}
复制代码
#include "st7735.h"
#include "main.h"
#include "fonts.h"
#include "soft_i2c.h"
#include "sht40.h"
#include <stdio.h>
#include <string.h>
#define AUTO_BAUD_MODE0 /*自动波特率检测模式选择,从start位开始测量;若屏蔽选择下降沿到下降沿测量*/
// 外设句柄
I2C_HandleTypeDef hi2c1;
SPI_HandleTypeDef hspi1;
UART_HandleTypeDef UartHandle; // 全局变量,名称必须和py32f0xx_it.c中一致
// UART1初始化函数
void MX_USART1_UART_Init(void)
{
/* USART1初始化 */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
UartHandle.Instance = USART1;
UartHandle.Init.BaudRate = 115200;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_AUTOBAUDRATE_INIT;
UartHandle.AdvancedInit.AutoBaudRateEnable = UART_ADVFEATURE_AUTOBAUDRATE_ENABLE; /* 自动波特率使能 */
#ifdef AUTO_BAUD_MODE0
UartHandle.AdvancedInit.AutoBaudRateMode = UART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT; /* 自动波特率检测模式从start位开始测量波特率,上位机发送0x7f即可 */
#else
UartHandle.AdvancedInit.AutoBaudRateMode = UART_ADVFEATURE_AUTOBAUDRATE_ONFALLINGEDGE; /* 自动波特率检测模式下降沿到下降沿测量,上位机发送0x55即可 */
#endif
if (HAL_UART_DeInit(&UartHandle) != HAL_OK)
{
Error_Handler();
}
if (HAL_UART_Init(&UartHandle) != HAL_OK)
{
Error_Handler();
}
/* 使能USART1中断 */
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
/*
* 纯软件SPI测试版本
* 不初始化SPI/UART/I2C,只用GPIO模拟SPI时序驱动ST7735
* 目的:排除硬件SPI外设问题,验证屏幕是否正常工作
*
* 接线:
* PA1 = SCK (软件模拟时钟)
* PA7 = MOSI (软件模拟数据)
* PA4 = DC (命令/数据选择)
* PB0 = CS (片选,低电平有效)
* PB1 = BLK (背光,高电平点亮)
*/
/* ========== 简易延时 ========== */
static void Delay_us(uint32_t us)
{
/* HSI 8MHz, 约125ns per loop */
volatile uint32_t count = us * 2U;
while(count--) { __NOP(); }
}
static void Delay_ms(uint32_t ms)
{
while(ms--) { Delay_us(1000); }
}
/* ========== 软件SPI底层 ========== */
/* 软件SPI发送1字节 (MSB first, SPI Mode 0: CPOL=0, CPHA=0) */
static void SoftSPI_WriteByte(uint8_t data)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
/* 设置数据位 (MSB first) */
if (data & 0x80)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
/* SCK上升沿,ST7735采样数据 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
/* SCK下降沿,准备下一位 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
data <<= 1;
}
}
/* 发送命令 (DC=0) */
static void LCD_WriteCmd(uint8_t cmd)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); /* DC = 0 (命令) */
SoftSPI_WriteByte(cmd);
}
/* 发送数据 (DC=1) */
static void LCD_WriteData8(uint8_t data)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); /* DC = 1 (数据) */
SoftSPI_WriteByte(data);
}
/* 发送16位数据 */
static void LCD_WriteData16(uint16_t data)
{
LCD_WriteData8((uint8_t)(data >> 8));
LCD_WriteData8((uint8_t)(data & 0xFF));
}
/* ========== ST7735初始化命令 ========== */
static void LCD_Init(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); /* CS = 0 (选中) */
/* Software Reset */
LCD_WriteCmd(0x01);
Delay_ms(150);
/* Out of Sleep */
LCD_WriteCmd(0x11);
Delay_ms(255);
/* Frame Rate Control (Normal Mode) */
LCD_WriteCmd(0xB1);
LCD_WriteData8(0x01);
LCD_WriteData8(0x2C);
LCD_WriteData8(0x2D);
/* Frame Rate Control (Idle Mode) */
LCD_WriteCmd(0xB2);
LCD_WriteData8(0x01);
LCD_WriteData8(0x2C);
LCD_WriteData8(0x2D);
/* Frame Rate Control (Partial Mode) */
LCD_WriteCmd(0xB3);
LCD_WriteData8(0x01);
LCD_WriteData8(0x2C);
LCD_WriteData8(0x2D);
LCD_WriteData8(0x01);
LCD_WriteData8(0x2C);
LCD_WriteData8(0x2D);
/* Display Inversion Control */
LCD_WriteCmd(0xB4);
LCD_WriteData8(0x07);
/* Power Control 1 */
LCD_WriteCmd(0xC0);
LCD_WriteData8(0xA2);
LCD_WriteData8(0x02);
LCD_WriteData8(0x84);
/* Power Control 2 */
LCD_WriteCmd(0xC1);
LCD_WriteData8(0xC5);
/* Power Control 3 */
LCD_WriteCmd(0xC2);
LCD_WriteData8(0x0A);
LCD_WriteData8(0x00);
/* Power Control 4 */
LCD_WriteCmd(0xC3);
LCD_WriteData8(0x8A);
LCD_WriteData8(0x2A);
/* Power Control 5 */
LCD_WriteCmd(0xC4);
LCD_WriteData8(0x8A);
LCD_WriteData8(0xEE);
/* VCOM Control */
LCD_WriteCmd(0xC5);
LCD_WriteData8(0x0E);
/* Inversion Off - 先不反色,测试基础颜色 */
LCD_WriteCmd(0x20); /* Inversion Off */
/* Memory Access Control - 横屏模式, 交换行列 */
LCD_WriteCmd(0x36);
LCD_WriteData8(0x68); /* MV=1(交换行列), MY=0, MX=1, ML=0, BGR */
/* 0x68 = 0110 1000: MY=0, MX=1, MV=1, ML=0, BGR(bit3=1) */
/* Color Mode: 16-bit */
LCD_WriteCmd(0x3A);
LCD_WriteData8(0x05);
/* 先不设CASET/RASET范围,让填充函数自己设 */
/* Gamma Correction (Positive) */
LCD_WriteCmd(0xE0);
LCD_WriteData8(0x02); LCD_WriteData8(0x1C);
LCD_WriteData8(0x07); LCD_WriteData8(0x12);
LCD_WriteData8(0x37); LCD_WriteData8(0x32);
LCD_WriteData8(0x29); LCD_WriteData8(0x2D);
LCD_WriteData8(0x29); LCD_WriteData8(0x25);
LCD_WriteData8(0x2B); LCD_WriteData8(0x39);
LCD_WriteData8(0x00); LCD_WriteData8(0x01);
LCD_WriteData8(0x03); LCD_WriteData8(0x10);
/* Gamma Correction (Negative) */
LCD_WriteCmd(0xE1);
LCD_WriteData8(0x03); LCD_WriteData8(0x1D);
LCD_WriteData8(0x07); LCD_WriteData8(0x06);
LCD_WriteData8(0x2E); LCD_WriteData8(0x2C);
LCD_WriteData8(0x29); LCD_WriteData8(0x2D);
LCD_WriteData8(0x2E); LCD_WriteData8(0x2E);
LCD_WriteData8(0x37); LCD_WriteData8(0x3F);
LCD_WriteData8(0x00); LCD_WriteData8(0x00);
LCD_WriteData8(0x02); LCD_WriteData8(0x10);
/* Normal Display On */
LCD_WriteCmd(0x13);
Delay_ms(10);
/* Display On */
LCD_WriteCmd(0x29);
Delay_ms(100);
/* 关闭睡眠模式,防止自动息屏 */
LCD_WriteCmd(0x38); /* Idle Mode Off */
Delay_ms(10);
}
/* ========== 全屏填充颜色 ========== */
static void LCD_FillColor(uint16_t color)
{
uint16_t i;
uint32_t total = 162U * 132U; /* 写满整个内部显存,避免偏移计算误差 */
/* 设置写RAM窗口 (内部显存全范围 132x162) */
/* 横屏模式下 MV=1, 行列已交换, 但这里写的是内部显存地址, 不变 */
LCD_WriteCmd(0x2A); /* Column Address Set */
LCD_WriteData8(0x00); LCD_WriteData8(0x00); /* XS = 0 */
LCD_WriteData8(0x00); LCD_WriteData8(0xA1); /* XE = 161 */
LCD_WriteCmd(0x2B); /* Row Address Set */
LCD_WriteData8(0x00); LCD_WriteData8(0x00); /* YS = 0 */
LCD_WriteData8(0x00); LCD_WriteData8(0x83); /* YE = 131 */
LCD_WriteCmd(0x2C); /* Memory Write */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); /* DC = 1 */
for (i = 0; i < total; i++)
{
SoftSPI_WriteByte((uint8_t)(color >> 8));
SoftSPI_WriteByte((uint8_t)(color & 0xFF));
}
}
/* ========== 显示单个字符 (窗口写入模式,高效) ========== */
static void LCD_DrawChar(uint16_t x, uint16_t y, char ch, FontDef *font, uint16_t color, uint16_t bgColor)
{
uint32_t i, j, b;
uint16_t tmp;
/* 设置写RAM窗口: 列从 x 到 x+width-1, 行从 y 到 y+height-1 */
LCD_WriteCmd(0x2A); /* Column Address Set */
LCD_WriteData8((uint8_t)(x >> 8));
LCD_WriteData8((uint8_t)(x & 0xFF));
LCD_WriteData8((uint8_t)((x + font->width - 1) >> 8));
LCD_WriteData8((uint8_t)((x + font->width - 1) & 0xFF));
LCD_WriteCmd(0x2B); /* Row Address Set */
LCD_WriteData8((uint8_t)(y >> 8));
LCD_WriteData8((uint8_t)(y & 0xFF));
LCD_WriteData8((uint8_t)((y + font->height - 1) >> 8));
LCD_WriteData8((uint8_t)((y + font->height - 1) & 0xFF));
LCD_WriteCmd(0x2C); /* Memory Write */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); /* DC = 1 */
/* 字体数据: 行优先存储, 每行一个uint16_t, font->height个数据/字符 */
/* 有效位在高位: bit(16-width) ~ bit15 */
for (j = 0; j < font->height; j++)
{
tmp = font->data[(ch - 32) * font->height + j];
for (i = 0; i < font->width; i++)
{
b = tmp & (1 << (15 - i));
if (b)
LCD_WriteData16(color);
else
LCD_WriteData16(bgColor);
}
}
}
/* ========== 显示字符串 ========== */
static void LCD_DrawString(uint16_t x, uint16_t y, const char *str, FontDef *font, uint16_t color, uint16_t bgColor)
{
uint16_t curX = x;
while (*str != '\0')
{
LCD_DrawChar(curX, y, *str, font, color, bgColor);
curX += font->width;
str++;
}
}
/* ========== 画测试像素块 (用于验证显示是否正常) ========== */
static void LCD_DrawBlock(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color)
{
uint32_t i, total = (uint32_t)w * h;
LCD_WriteCmd(0x2A);
LCD_WriteData8((uint8_t)(x >> 8));
LCD_WriteData8((uint8_t)(x & 0xFF));
LCD_WriteData8((uint8_t)((x + w - 1) >> 8));
LCD_WriteData8((uint8_t)((x + w - 1) & 0xFF));
LCD_WriteCmd(0x2B);
LCD_WriteData8((uint8_t)(y >> 8));
LCD_WriteData8((uint8_t)(y & 0xFF));
LCD_WriteData8((uint8_t)((y + h - 1) >> 8));
LCD_WriteData8((uint8_t)((y + h - 1) & 0xFF));
LCD_WriteCmd(0x2C);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); /* DC = 1 */
for (i = 0; i < total; i++)
{
LCD_WriteData16(color);
}
}
/* ========== 浮点数转字符串 (简单实现,无sprintf依赖) ========== */
static void FloatToString(float value, char *buf, uint8_t decimal_places)
{
int32_t int_part;
int32_t frac_part;
uint8_t is_negative = 0;
uint8_t i = 0;
uint8_t j;
char temp[16];
int32_t multiplier = 1;
uint8_t k;
uint8_t frac_digits;
if (value < 0) {
is_negative = 1;
value = -value;
}
/* 整数部分 */
int_part = (int32_t)value;
/* 小数部分 */
{
float frac = value - (float)int_part;
for (k = 0; k < decimal_places; k++) multiplier *= 10;
frac_part = (int32_t)(frac * multiplier + 0.5f);
}
/* 处理进位 (如 99.99 -> 100.00) */
if (frac_part >= multiplier) {
frac_part = 0;
int_part++;
}
/* 转换整数部分到字符串 (倒序) */
if (int_part == 0) {
temp[i++] = '0';
} else {
while (int_part > 0) {
temp[i++] = '0' + (int_part % 10);
int_part /= 10;
}
}
/* 添加负号 */
if (is_negative) {
buf[0] = '-';
j = 1;
} else {
j = 0;
}
/* 倒序写入整数部分 */
while (i > 0) {
buf[j++] = temp[--i];
}
/* 小数点 */
if (decimal_places > 0) {
buf[j++] = '.';
/* 转换小数部分 */
i = 0;
if (frac_part == 0) {
for (k = 0; k < decimal_places; k++) {
buf[j++] = '0';
}
} else {
/* 倒序转换小数部分 */
frac_digits = 0;
while (frac_part > 0) {
temp[i++] = '0' + (frac_part % 10);
frac_part /= 10;
frac_digits++;
}
/* 补前导零 */
for (k = frac_digits; k < decimal_places; k++) {
buf[j++] = '0';
}
/* 倒序写入 */
while (i > 0) {
buf[j++] = temp[--i];
}
}
}
buf[j] = '\0';
}
/* ========== 系统时钟 ========== */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = 0x01U;
RCC_OscInitStruct.HSIState = 0x01U;
RCC_OscInitStruct.HSIDiv = 0x00U;
RCC_OscInitStruct.HSICalibrationValue = 0x00U;
RCC_OscInitStruct.LSIState = 0x00U;
if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { while(1); }
RCC_ClkInitStruct.ClockType = 0x07U;
RCC_ClkInitStruct.SYSCLKSource = 0x00U;
RCC_ClkInitStruct.AHBCLKDivider = 0x00U;
RCC_ClkInitStruct.APB1CLKDivider = 0x00U;
if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, 0x00U) != HAL_OK) { while(1); }
}
/* ========== 主函数 ========== */
int main(void)
{
SHT40_Data_t sht40_data;
uint8_t sht40_status;
char temp_str[16];
char humi_str[16];
uint32_t loop_count = 0;
HAL_Init();
SystemClock_Config();
/* 使能GPIO时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE(); /* PF0(SDA), PF1(SCL) for SHT40 I2C */
MX_USART1_UART_Init();
LOG_INFO("System Init Done!!!");
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
/* PA1(SCK)、PA4(DC)、PA7(MOSI) → 推挽输出 */
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_7;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* PB0(CS)、PB1(BLK) → 推挽输出 */
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* 初始电平 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); /* BLK = 1 (点亮背光) */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); /* CS = 1 (未选中) */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); /* DC = 1 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); /* SCK = 0 (空闲低) */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET); /* MOSI = 0 */
LOG_INFO("GPIO Init Done!!!");
/* 初始化软件I2C (用于SHT40) */
SoftI2C_Init();
LOG_INFO("SoftI2C Init Done!!!");
/* 初始化SHT40传感器 */
sht40_status = SHT40_Init();
if (sht40_status == 0) {
LOG_INFO("SHT40 Init OK!!!");
} else {
LOG_INFO("SHT40 Init FAILED!!!");
}
/* 初始化屏幕 */
LCD_Init();
/* 全屏白色背景 */
__disable_irq();
LCD_FillColor(0xFFFF); /* 全白 */
__enable_irq();
/* 显示标题 */
__disable_irq();
LCD_DrawString(5, 24, "Temp:", &Font_11x18, 0xF800, 0xFFFF);
LCD_DrawString(5, 48, "Humi:", &Font_11x18, 0x0000, 0xFFFF);
__enable_irq();
/* 主循环: 定时读取并刷新显示 */
while (1)
{
/* 读取温湿度 */
sht40_status = SHT40_ReadData(&sht40_data);
if (sht40_status == 0)
{
/* 转换浮点数为字符串 */
FloatToString(sht40_data.Temperature, temp_str, 1);
FloatToString(sht40_data.Humidity, humi_str, 1);
/* 拼接单位 */
strcat(temp_str, " C");
strcat(humi_str, " %");
LOG_INFO("Temp:");
LOG_INFO(temp_str);
LOG_INFO("Humi:");
LOG_INFO(humi_str);
}
else if (sht40_status == 2)
{
strcpy(temp_str, "CRC Err");
strcpy(humi_str, "CRC Err");
LOG_INFO("SHT40 CRC Error!!!");
}
else
{
strcpy(temp_str, "I2C Err");
strcpy(humi_str, "I2C Err");
LOG_INFO("SHT40 I2C Error!!!");
}
/* 刷新显示数值 (覆盖旧值区域) */
__disable_irq();
/* 温度值区域: x=55~155, y=24~42, 用背景色覆盖 */
LCD_DrawBlock(55, 24, 100, 18, 0xFFFF);
LCD_DrawString(55, 24, temp_str, &Font_11x18, 0xF800, 0xFFFF);
/* 湿度值区域: x=55~155, y=48~66, 用背景色覆盖 */
LCD_DrawBlock(55, 48, 100, 18, 0xFFFF);
LCD_DrawString(55, 48, humi_str, &Font_11x18, 0x0000, 0xFFFF);
__enable_irq();
/* 每2秒刷新一次 */
HAL_Delay(2000);
loop_count++;
}
}
// 错误处理
void Error_Handler(void)
{
while(1)
{
HAL_Delay(100U);
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif