/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 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 "gpio.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 */ #define SCL_H HAL_GPIO_WritePin(SCL_GPIO_Port,SCL_Pin,GPIO_PIN_SET); #define SCL_L HAL_GPIO_WritePin(SCL_GPIO_Port,SCL_Pin,GPIO_PIN_RESET); #define SDA_H HAL_GPIO_WritePin(SDA_GPIO_Port,SDA_Pin,GPIO_PIN_SET); #define SDA_L HAL_GPIO_WritePin(SDA_GPIO_Port,SDA_Pin,GPIO_PIN_RESET); #define SDA_R HAL_GPIO_ReadPin(SDA_GPIO_Port,SDA_Pin) #define IIC_DELAY HAL_Delay(1); #define OLED_CMD 0 #define OLED_DATA 1 #define LCD_ADDR 0x78 #define CMD_WRITE 0x00 #define DATA_WRITE 0x40 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ void iicack() { SDA_L SCL_H IIC_DELAY SCL_L SDA_H } void iicwaitack(void) { SDA_H IIC_DELAY SCL_H IIC_DELAY SCL_L IIC_DELAY } //start void iicstart() { SDA_H SCL_H IIC_DELAY SDA_L IIC_DELAY SDA_L IIC_DELAY } //stop void iicstop() { SDA_L SCL_H IIC_DELAY SDA_H IIC_DELAY } //fashuju void iicfashuju(uint8_t byte) { for(int i=0;i<8;i++) { if(byte & 0x80) { SDA_H } else { SDA_L } SCL_H IIC_DELAY; SCL_L byte <<=1; } iicwaitack(); } //dushuju uint8_t iccdushuju() { uint8_t i,byte = 0; for(i=0;i<8;i++) { byte<<=1; SCL_H if(SDA_R) { byte|=0x01; } IIC_DELAY SCL_L } return byte; } void oled_wr_byte(uint8_t dat,uint8_t mode) { iicstart(); iicfashuju(0x78); iicwaitack(); if(mode) { iicfashuju(0x40); }else{ iicfashuju(0x00); } iicwaitack(); iicfashuju(dat); iicwaitack(); iicstop(); } void OLDE_lint(void) { oled_wr_byte(0XAE,OLED_CMD); oled_wr_byte(0X00,OLED_CMD); oled_wr_byte(0X10,OLED_CMD); oled_wr_byte(0X40,OLED_CMD); oled_wr_byte(0X81,OLED_CMD); oled_wr_byte(0XCF,OLED_CMD); oled_wr_byte(0XA1,OLED_CMD); oled_wr_byte(0XC8,OLED_CMD); oled_wr_byte(0XA6,OLED_CMD); oled_wr_byte(0XA8,OLED_CMD); oled_wr_byte(0X3F,OLED_CMD); oled_wr_byte(0XD3,OLED_CMD); oled_wr_byte(0X00,OLED_CMD); oled_wr_byte(0XD5,OLED_CMD); oled_wr_byte(0X80,OLED_CMD); oled_wr_byte(0XD9,OLED_CMD); oled_wr_byte(0XF1,OLED_CMD); oled_wr_byte(0XDA,OLED_CMD); oled_wr_byte(0X12,OLED_CMD); oled_wr_byte(0XDB,OLED_CMD); oled_wr_byte(0X30,OLED_CMD); oled_wr_byte(0X20,OLED_CMD); oled_wr_byte(0X02,OLED_CMD); oled_wr_byte(0X8D,OLED_CMD); oled_wr_byte(0X14,OLED_CMD); oled_wr_byte(0XAF,OLED_CMD); } void LCD_Write_Cmd(uint8_t cmd) { iicstart(); iicfashuju(LCD_ADDR); iicfashuju(cmd); iicstop(); } // ???? void LCD_Write_Data(uint8_t data) { iicstart(); iicfashuju(LCD_ADDR); iicfashuju(DATA_WRITE); iicfashuju(data); iicstop(); } void LCD_Init() { HAL_Delay(50); LCD_Write_Cmd(0xE2); HAL_Delay(10); LCD_Write_Cmd(0xA2); LCD_Write_Cmd(0x89); LCD_Write_Cmd(0xC0); LCD_Write_Cmd(0x2F); LCD_Write_Cmd(0xF8); LCD_Write_Cmd(0x00); LCD_Write_Cmd(0x27); LCD_Write_Cmd(0x5C); LCD_Write_Cmd(0xAC); LCD_Write_Cmd(0x01); LCD_Write_Cmd(0xAF); HAL_Delay(100); } /* 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(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ // HAL_Init(); // SystemClock_Config(); // MX_GPIO_Init(); // LCD_Init(); OLDE_lint(); while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ // iicstart(); // iicfashuju(0X78); // iicfashuju(0X40); // iicfashuju(0XAF); // //iicfashuju(0XA5); // iicstop(); // // oled_wr_byte(0XAF,OLED_CMD); HAL_Delay(100); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** 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.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 4; RCC_OscInitStruct.PLL.PLLN = 72; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; 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 */
时间: 2025-07-04 10:18:55 浏览: 2
代码概述
你正在使用 **STM32 HAL 库** 实现一个 **GPIO模拟I2C通信模块**,并通过它控制一个 **OLED 或 LCD12864 显示屏**。代码中实现了完整的 I2C 起始信号、停止信号、发送字节、读取字节、ACK/NACK 判断等功能,并调用了 OLED 初始化函数 `OLDE_lint()` 来配置屏幕。
但在以下方面存在 **错误或改进空间**:
- `iicwaitack()` 没有真正等待 ACK。
- 宏定义中的分号问题可能导致编译错误。
- `oled_wr_byte()` 中地址和命令格式可能不正确。
- 初始化顺序和主循环调用方式需修正。
---
代码解析
### ✅ 1. 宏定义分号问题(建议修正)
```c
#define SCL_H HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET)
#define SCL_L HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_RESET)
#define SDA_H HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_Pin, GPIO_PIN_SET)
#define SDA_L HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_Pin, GPIO_PIN_RESET)
```
注意:**不要在宏定义末尾加 `;`**,否则在条件语句中使用会报错。
---
### ❌ 2. `iicwaitack()` 函数逻辑错误
当前版本只拉高SCL,没有实际检测SDA是否为低(即没有判断ACK)。
#### ✅ 改进后的版本:
```c
uint8_t iicwaitack(void)
{
uint8_t timeout = 0;
SDA_H;
IIC_DELAY;
SCL_H;
IIC_DELAY;
while (SDA_R) { // 等待SDA变低,表示收到ACK
timeout++;
if (timeout > 100) {
SCL_L;
return 1; // 超时无ACK
}
IIC_DELAY;
}
SCL_L;
return 0; // 成功收到ACK
}
```
并在调用位置加入错误处理判断。
---
### ✅ 3. `oled_wr_byte()` 函数调用顺序合理化
确保在每次发送数据前都检查ACK状态:
```c
void oled_wr_byte(uint8_t dat, uint8_t mode)
{
iicstart();
iicfashuju(0x78); // 假设为设备地址+写标志
if (iicwaitack() != 0) goto error; // 无应答处理
iicfashuju(mode ? 0x40 : 0x00); // 命令/数据标识
if (iicwaitack() != 0) goto error;
iicfashuju(dat);
if (iicwaitack() != 0) goto error;
error:
iicstop();
}
```
---
### ❌ 4. `OLDE_lint()` 函数未被调用导致无效
你现在在 main 函数里写了:
```c
OLDE_lint();
while (1)
```
但并没有放在初始化之后的正确位置,而且缺少延时和持续刷新机制。
#### ✅ 正确调用方式如下:
```c
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
OLDE_lint(); // 初始化OLED
while (1)
{
// 可添加其他显示操作
HAL_Delay(100);
}
}
```
---
### ⚠️ 5. `LCD_ADDR` 和 `DATA_WRITE` 的使用方式混乱
你在 `LCD_Write_Cmd()` 和 `LCD_Write_Data()` 中使用了与 `oled_wr_byte()` 不同的接口风格。请统一采用一种通信协议标准,例如全部使用 `oled_wr_byte()` 来发送命令/数据。
建议将 `LCD_Write_Cmd()` 和 `LCD_Write_Data()` 删除或替换为统一接口:
```c
void OLED_Set_Cmd(uint8_t cmd)
{
oled_wr_byte(cmd, OLED_CMD);
}
void OLED_Set_Data(uint8_t data)
{
oled_wr_byte(data, OLED_DATA);
}
```
---
知识点(列出该代码中遇到的知识点)
1. **I2C应答机制检测**:理解如何通过检测SDA电平来判断从机是否返回ACK,从而实现可靠的通信。
2. **宏定义语法规范**:掌握宏定义不应以分号结尾,避免在复杂结构中出现语法错误。
3. **嵌入式显示屏初始化流程**:熟悉OLED或LCD12864等显示屏的标准初始化命令序列及其作用。
阅读全文
相关推荐






