/* 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 "spi.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//#include "i2c_sw.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "delay.h"
#include "spi_480_272_lcd.h"
#include "display.h"
#include "soft_i2c.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
//typedef uint32_t u32;
//typedef uint16_t u16;
//typedef uint8_t u8;
//typedef const uint8_t uc8;
//
//#ifndef __cplusplus
//typedef enum {FALSE = 0, TRUE = !FALSE} bool;
//#endif
// 错误代码扩展(与soft_i2c.h对应)
#define I2C_SUCCESS 0
#define I2C_ERROR_ACK 1
#define I2C_ERROR_TIMEOUT 2 // 新增:超时错误
// I2C设备参数(根据实际硬件修改)
#define SLAVE_DEVICE_ADDR 0x48 // 7位从机地址(不含读写位)
#define TEST_REG_ADDR 0x00 // 测试用寄存器地址
#define I2C_RETRY_COUNT 3 // 通信失败重试次数
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
// 函数声明
void I2C_CommunicationTest(void);
void PrintData(uint8_t* data, uint8_t len);
uint8_t I2C_WriteWithRetry(uint8_t devAddr, uint8_t* data, uint8_t len);
uint8_t I2C_ReadWithRetry(uint8_t devAddr, uint8_t* data, uint8_t len);
uint8_t I2C_WriteRegWithRetry(uint8_t devAddr, uint8_t regAddr, uint8_t* data, uint8_t len);
uint8_t I2C_ReadRegWithRetry(uint8_t devAddr, uint8_t regAddr, uint8_t* data, uint8_t len);
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t g_i2cStatus = I2C_SUCCESS; // 全局I2C状态,用于调试
/* 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_SPI1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
// 初始化硬件资源
I2C_Init(); // 初始化软件I2C
printf("系统初始化完成,I2C引脚配置为开漏输出+外部上拉\r\n");
printf("从机地址: 0x%02X,测试寄存器: 0x%02X\r\n", SLAVE_DEVICE_ADDR, TEST_REG_ADDR);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
I2C_CommunicationTest(); // 执行I2C通信测试
Delay_Ms(1000); // 1秒测试间隔
/* 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_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 */
// printf重定向到USART1
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
// 带重试机制的I2C写操作(提高可靠性)
uint8_t I2C_WriteWithRetry(uint8_t devAddr, uint8_t* data, uint8_t len)
{
uint8_t retry = 0;
while (retry < I2C_RETRY_COUNT)
{
g_i2cStatus = I2C_Write(devAddr, data, len);
if (g_i2cStatus == I2C_SUCCESS)
return I2C_SUCCESS;
retry++;
Delay_Ms(50); // 重试前延时
I2C_Stop(); // 确保总线释放
}
return g_i2cStatus;
}
// 带重试机制的I2C读操作
uint8_t I2C_ReadWithRetry(uint8_t devAddr, uint8_t* data, uint8_t len)
{
uint8_t retry = 0;
while (retry < I2C_RETRY_COUNT)
{
g_i2cStatus = I2C_Read(devAddr, data, len);
if (g_i2cStatus == I2C_SUCCESS)
return I2C_SUCCESS;
retry++;
Delay_Ms(50);
I2C_Stop();
}
return g_i2cStatus;
}
// 带重试机制的寄存器写操作
uint8_t I2C_WriteRegWithRetry(uint8_t devAddr, uint8_t regAddr, uint8_t* data, uint8_t len)
{
uint8_t retry = 0;
while (retry < I2C_RETRY_COUNT)
{
g_i2cStatus = I2C_WriteReg(devAddr, regAddr, data, len);
if (g_i2cStatus == I2C_SUCCESS)
return I2C_SUCCESS;
retry++;
Delay_Ms(50);
I2C_Stop();
}
return g_i2cStatus;
}
// 带重试机制的寄存器读操作
uint8_t I2C_ReadRegWithRetry(uint8_t devAddr, uint8_t regAddr, uint8_t* data, uint8_t len)
{
uint8_t retry = 0;
while (retry < I2C_RETRY_COUNT)
{
g_i2cStatus = I2C_ReadReg(devAddr, regAddr, data, len);
if (g_i2cStatus == I2C_SUCCESS)
return I2C_SUCCESS;
retry++;
Delay_Ms(50);
I2C_Stop();
}
return g_i2cStatus;
}
// I2C通信测试主函数
void I2C_CommunicationTest(void)
{
uint8_t writeData[5] = {0x11, 0x22, 0x33, 0x44, 0x55};
uint8_t readData[5] = {0};
uint8_t status;
printf("\r\n===== 开始I2C通信测试 =====");
// 测试1: 直接向从机写入数据(无寄存器地址)
status = I2C_WriteWithRetry(SLAVE_DEVICE_ADDR, writeData, 5);
if (status == I2C_SUCCESS)
{
printf("\r\n测试1: 直接写入成功: ");
PrintData(writeData, 5);
}
else
{
printf("\r\n测试1: 直接写入失败,错误代码: %d(重试%d次后)", status, I2C_RETRY_COUNT);
}
Delay_Ms(100); // 等待从机处理
// 测试2: 直接从从机读取数据(无寄存器地址)
status = I2C_ReadWithRetry(SLAVE_DEVICE_ADDR, readData, 5);
if (status == I2C_SUCCESS)
{
printf("测试2: 直接读取成功: ");
PrintData(readData, 5);
// 验证读取数据与写入数据是否一致(仅适用于支持回读的设备)
if (memcmp(writeData, readData, 5) == 0)
printf(" 数据校验一致");
else
printf(" 数据校验不一致");
}
else
{
printf("\r\n测试2: 直接读取失败,错误代码: %d(重试%d次后)", status, I2C_RETRY_COUNT);
}
Delay_Ms(100);
memset(readData, 0, 5); // 清空接收缓存
// 测试3: 向指定寄存器写入数据
status = I2C_WriteRegWithRetry(SLAVE_DEVICE_ADDR, TEST_REG_ADDR, writeData, 5);
if (status == I2C_SUCCESS)
{
printf("\r\n测试3: 寄存器0x%02X写入成功: ", TEST_REG_ADDR);
PrintData(writeData, 5);
}
else
{
printf("\r\n测试3: 寄存器0x%02X写入失败,错误代码: %d(重试%d次后)", TEST_REG_ADDR, status, I2C_RETRY_COUNT);
}
Delay_Ms(100);
// 测试4: 从指定寄存器读取数据
status = I2C_ReadRegWithRetry(SLAVE_DEVICE_ADDR, TEST_REG_ADDR, readData, 5);
if (status == I2C_SUCCESS)
{
printf("测试4: 寄存器0x%02X读取成功: ", TEST_REG_ADDR);
PrintData(readData, 5);
// 验证寄存器读写一致性
if (memcmp(writeData, readData, 5) == 0)
printf(" 数据校验一致");
else
printf(" 数据校验不一致");
}
else
{
printf("\r\n测试4: 寄存器0x%02X读取失败,错误代码: %d(重试%d次后)", TEST_REG_ADDR, status, I2C_RETRY_COUNT);
}
printf("\r\n===== 测试结束 =====");
}
// 打印数据(十六进制格式)
void PrintData(uint8_t* data, uint8_t len)
{
for (uint8_t i = 0; i < len; i++)
{
printf("0x%02X ", data[i]);
if ((i + 1) % 5 == 0) // 每5个数据换行
printf("\r\n ");
}
}
/* 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 */
#include "soft_i2c.h"
#include "delay.h" // 引入自定义延时函数头文件
// 设置SDA为输出模式
void I2C_SetSDAOutputMode(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(I2C_SDA_PORT, &GPIO_InitStruct);
}
// 设置SDA为输入模式
void I2C_SetSDAInputMode(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(I2C_SDA_PORT, &GPIO_InitStruct);
}
// 初始化I2C引脚 - 使用HAL库函数
void I2C_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOC_CLK_ENABLE();
// 配置SCL引脚
GPIO_InitStruct.Pin = I2C_SCL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(I2C_SCL_PORT, &GPIO_InitStruct);
// 配置SDA引脚为输出模式
I2C_SetSDAOutputMode();
// 初始化引脚为高电平
I2C_SCL_HIGH;
I2C_SDA_HIGH;
Delay_Ms(10); // 使用毫秒延时
}
// 产生I2C起始信号
// 起始条件:SCL高电平时,SDA从高到低跳变
void I2C_Start(void) {
I2C_SetSDAOutputMode();
I2C_SDA_HIGH;
I2C_SCL_HIGH;
I2C_delay(); // 使用专用I2C延时函数
I2C_SDA_LOW; // 拉低SDA,产生起始信号
I2C_delay();
I2C_SCL_LOW; // 拉低SCL,准备发送数据
I2C_delay();
}
// 产生I2C停止信号
// 停止条件:SCL高电平时,SDA从低到高跳变
void I2C_Stop(void) {
I2C_SetSDAOutputMode();
I2C_SDA_LOW;
I2C_delay();
I2C_SCL_HIGH;
I2C_delay();
I2C_SDA_HIGH; // 拉高SDA,产生停止信号
I2C_delay();
}
// 发送应答信号
// ack=0: 应答(ACK), ack=1: 非应答(NACK)
void I2C_SendAck(uint8_t ack) {
I2C_SetSDAOutputMode();
if (ack) {
I2C_SDA_HIGH; // 非应答(NACK)
} else {
I2C_SDA_LOW; // 应答(ACK)
}
I2C_delay();
I2C_SCL_HIGH; // 时钟高电平,从机读取应答
I2C_delay();
I2C_SCL_LOW; // 拉低时钟
I2C_delay();
I2C_SDA_HIGH; // 释放SDA
}
// 接收应答信号
// 返回0: 应答(ACK), 返回1: 非应答(NACK)
uint8_t I2C_ReceiveAck(void) {
uint8_t ack;
I2C_SetSDAInputMode();
I2C_delay();
I2C_SCL_HIGH; // 时钟高电平,读取应答
I2C_delay();
ack = I2C_SDA_READ; // 读取应答信号
I2C_SCL_LOW; // 拉低时钟
I2C_delay();
I2C_SetSDAOutputMode();
return ack;
}
// 发送一个字节数据
void I2C_SendByte(uint8_t data) {
uint8_t i;
I2C_SetSDAOutputMode();
for (i = 0; i < 8; i++) {
// 发送高位
if (data & 0x80) {
I2C_SDA_HIGH;
} else {
I2C_SDA_LOW;
}
data <<= 1;
I2C_delay();
I2C_SCL_HIGH; // 时钟高电平,从机读取数据
I2C_delay();
I2C_SCL_LOW; // 拉低时钟,准备下一位数据
I2C_delay();
}
}
// 接收一个字节数据
uint8_t I2C_ReceiveByte(void) {
uint8_t i, data = 0;
I2C_SetSDAInputMode();
for (i = 0; i < 8; i++) {
data <<= 1;
I2C_SCL_HIGH; // 时钟高电平,读取数据
I2C_delay();
if (I2C_SDA_READ) {
data |= 0x01;
}
I2C_SCL_LOW; // 拉低时钟,准备下一位
I2C_delay();
}
I2C_SetSDAOutputMode();
return data;
}
// 向I2C设备发送数据
uint8_t I2C_Write(uint8_t devAddr, uint8_t* data, uint8_t len) {
uint8_t i;
I2C_Start(); // 发送起始信号
I2C_SendByte((devAddr << 1) & 0xFE); // 发送设备地址+写标志
if (I2C_ReceiveAck() != 0) { // 等待应答
I2C_Stop();
return I2C_ERROR_ACK; // 无应答,失败
}
for (i = 0; i < len; i++) {
I2C_SendByte(data[i]); // 发送数据
if (I2C_ReceiveAck() != 0) { // 等待应答
I2C_Stop();
return I2C_ERROR_ACK; // 无应答,失败
}
}
I2C_Stop(); // 发送停止信号
return I2C_SUCCESS; // 成功
}
// 向I2C设备指定寄存器发送数据
uint8_t I2C_WriteReg(uint8_t devAddr, uint8_t regAddr, uint8_t* data, uint8_t len) {
uint8_t i;
I2C_Start(); // 发送起始信号
I2C_SendByte((devAddr << 1) & 0xFE); // 发送设备地址+写标志
if (I2C_ReceiveAck() != 0) { // 等待应答
I2C_Stop();
return I2C_ERROR_ACK; // 失败
}
I2C_SendByte(regAddr); // 发送寄存器地址
if (I2C_ReceiveAck() != 0) { // 等待应答
I2C_Stop();
return I2C_ERROR_ACK; // 失败
}
for (i = 0; i < len; i++) {
I2C_SendByte(data[i]); // 发送数据
if (I2C_ReceiveAck() != 0) { // 等待应答
I2C_Stop();
return I2C_ERROR_ACK; // 失败
}
}
I2C_Stop(); // 发送停止信号
return I2C_SUCCESS; // 成功
}
// 从I2C设备读取数据
uint8_t I2C_Read(uint8_t devAddr, uint8_t* data, uint8_t len) {
uint8_t i;
I2C_Start(); // 发送起始信号
I2C_SendByte((devAddr << 1) | 0x01); // 发送设备地址+读标志
if (I2C_ReceiveAck() != 0) { // 等待应答
I2C_Stop();
return I2C_ERROR_ACK; // 失败
}
for (i = 0; i < len; i++) {
data[i] = I2C_ReceiveByte(); // 接收数据
// 最后一个字节发送非应答,其他发送应答
if (i == len - 1) {
I2C_SendAck(1); // 非应答
} else {
I2C_SendAck(0); // 应答
}
}
I2C_Stop(); // 发送停止信号
return I2C_SUCCESS; // 成功
}
// 从I2C设备指定寄存器读取数据
uint8_t I2C_ReadReg(uint8_t devAddr, uint8_t regAddr, uint8_t* data, uint8_t len) {
// 先发送寄存器地址
I2C_Start();
I2C_SendByte((devAddr << 1) & 0xFE); // 写操作
if (I2C_ReceiveAck() != 0) {
I2C_Stop();
return I2C_ERROR_ACK; // 失败
}
I2C_SendByte(regAddr); // 发送寄存器地址
if (I2C_ReceiveAck() != 0) {
I2C_Stop();
return I2C_ERROR_ACK; // 失败
}
// 重新发送起始信号,开始读取数据
return I2C_Read(devAddr, data, len);
}
这是STM32F103VCT6的主函数以及GPIO模拟I2C的代码,与以下从机代码,I2C通讯不成功,请改善。/* 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 "adc.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "delay.h"
#include "soft_i2c.h"
#include "stm32g0xx_hal.h"
#include <string.h>
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
// 从机地址定义(需与主机中设置的SLAVE_DEVICE_ADDR一致)
#define I2C_SLAVE_ADDR 0x48
// 缓冲区大小(需在soft_i2c.h中定义)
#define I2C_RX_BUFFER_SIZE 32
#define I2C_TX_BUFFER_SIZE 32
/* 定义设备地址和串口配置 */
#define DEVICE_COUNT 8 // 设备总数
#define UART_PER_DEVICE 4 // 每个设备的串口数
#define I2C_SLAVE_ADDRESS 0x48 // I2C从机地址
/* 传感器数据结构 */
typedef struct {
uint16_t adValue; // AD值
uint8_t deviceId; // 设备ID
uint8_t uartId; // 串口ID
uint8_t crcValid; // CRC校验结果
uint8_t data5; // 第五位数据
uint8_t data6; // 第六位数据
} SensorData;
// I2C通信状态枚举
typedef enum {
I2C_CMD_NONE,
I2C_CMD_READ_SENSOR, // 读取传感器数据(主机指令)
I2C_CMD_WRITE_REG, // 写入寄存器(主机指令)
I2C_CMD_READ_REG // 读取寄存器(主机指令)
} I2C_CmdTypeDef;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
// 寄存器定义(与主机TEST_REG_ADDR对应)
#define TEST_REG_ADDR 0x00 // 测试用寄存器地址
#define REG_BUFFER_SIZE 16 // 寄存器缓冲区大小
// 函数声明
void ProcessI2CCommand(void);
void UpdateSensorData(void);
uint16_t CRC16_Calculate(uint8_t *data, uint16_t length);
// 函数声明
void I2C_Slave_HandleData(void);
void I2C_Slave_PrepareResponse(void);
/* 全局变量 */
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart3;
extern UART_HandleTypeDef huart4;
UART_HandleTypeDef *uartHandles[UART_PER_DEVICE] = {&huart1, &huart2, &huart3, &huart4};
SensorData sensorData[UART_PER_DEVICE];
uint8_t modbusTxBuf[8];
uint8_t modbusRxBuf[10];
uint16_t crcTemp;
uint8_t deviceId = 0; // 当前设备ID (0-7)
/* CRC校验函数 */
uint16_t CRC16_Calculate(uint8_t *data, uint16_t length) {
uint16_t crc = 0;
uint8_t crc1 = 0, crc2 = 0;
uint16_t i;
for (i = 0; i < length; i++) {
if ((i % 2) == 0)
crc1 += data[i];
crc2 += data[i];
}
crc = crc2;
crc *= crc1;
crc *= 7639;
return crc;
}
uint8_t CRC16_Verify(uint8_t *data, uint16_t length) {
uint16_t crc_calculated, crc_received;
crc_calculated = CRC16_Calculate(data, length - 2);
crc_received = (data[length - 2] << 8) | data[length - 1];
return (crc_calculated == crc_received);
}
/* 传感器通信函数 */
uint8_t ReadSensorData(uint8_t uartId, uint8_t reg) {
uint8_t i;
uint32_t timeout;
/* 清空接收缓冲区 */
for (i = 0; i < 10; i++)
modbusRxBuf[i] = 0;
/* 构建Modbus请求 */
modbusTxBuf[0] = 1;
modbusTxBuf[1] = 0xAA;
modbusTxBuf[2] = 0x03; // 读保持寄存器功能码
modbusTxBuf[3] = reg;
modbusTxBuf[4] = 0;
modbusTxBuf[5] = 1;
/* 计算CRC并添加到发送缓冲区 */
crcTemp = CRC16_Calculate(modbusTxBuf, 6);
modbusTxBuf[6] = crcTemp >> 8;
modbusTxBuf[7] = crcTemp & 0xFF;
/* 发送请求 */
HAL_UART_Transmit(uartHandles[uartId], modbusTxBuf, 8, 1000);
/* 等待接收响应 */
timeout = 100000;
while (timeout-- && modbusRxBuf[0] != 0x01); // 等待响应头
if (timeout == 0)
return 0; // 接收超时
/* 接收完整响应 */
timeout = 100000;
while (timeout-- && modbusRxBuf[1] != 0xBB); // 等待响应标志
if (timeout == 0)
return 0; // 接收超时
/* 验证CRC */
sensorData[uartId].crcValid = CRC16_Verify(modbusRxBuf, 8);
return 0; // CRC校验失败
}
//typedef uint32_t u32;
//typedef uint16_t u16;
//typedef uint8_t u8;
//typedef const uint8_t uc8;
//
//#ifndef __cplusplus
//typedef enum {FALSE = 0, TRUE = !FALSE} bool;
//#endif
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t regBuffer[REG_BUFFER_SIZE] = {0}; // 寄存器缓冲区(模拟从机寄存器)
I2C_CmdTypeDef i2cCmd = I2C_CMD_NONE; // 当前I2C命令
uint8_t currentRegAddr = 0; // 当前操作的寄存器地址
/* 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 */
void I2C_Slave_Process(void);
/* 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_DMA_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
MX_USART4_UART_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
uint8_t i;
// 初始化I2C从机(开漏输出+上拉,与主机硬件匹配)
I2C_Slave_Init();
// 初始化寄存器(默认值)
regBuffer[TEST_REG_ADDR] = 0x00;
printf("I2C从机初始化完成,地址:0x%02X\r\n", I2C_SLAVE_ADDR);
printf("等待主机通信...\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* 轮询所有串口读取传感器数据 */
for (i = 0; i < UART_PER_DEVICE; i++) {
ReadSensorData(i, 0x0B);
HAL_Delay(10); // 简单延时,避免总线竞争
}
// 2. 处理I2C通信(必须高频调用,确保不遗漏主机指令)
I2C_Slave_Process();
// 3. 检查是否收到主机数据,若有则处理并生成应答
if (I2C_Slave_GetRxDataLength() > 0) {
ProcessI2CCommand(); // 解析主机命令
I2C_Slave_ClearRxBuffer(); // 清空接收缓冲区
}
// 4. 短暂延时,降低CPU占用
Delay_Ms(1);
/* 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};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** 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.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 8;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
// 处理主机发送的I2C命令
void ProcessI2CCommand(void) {
uint8_t rxData[I2C_RX_BUFFER_SIZE];
uint8_t rxLen = I2C_Slave_GetRxDataLength();
uint8_t txData[I2C_TX_BUFFER_SIZE];
uint8_t txLen = 0;
// 读取主机发送的数据
I2C_Slave_ReadRxBuffer(rxData, rxLen);
printf("收到主机数据(长度:%d):", rxLen);
for (uint8_t i = 0; i < rxLen; i++) {
printf("0x%02X ", rxData[i]);
}
printf("\r\n");
// 解析主机命令(根据主机操作类型适配)
// 情况1:主机执行"直接写"(无寄存器地址,如测试1)
if (rxLen >= 1) {
// 保存数据到寄存器(模拟从机存储)
memcpy(regBuffer, rxData, (rxLen < REG_BUFFER_SIZE) ? rxLen : REG_BUFFER_SIZE);
i2cCmd = I2C_CMD_WRITE_REG;
}
// 情况2:主机执行"寄存器写"(含寄存器地址,如测试3)
// 主机格式:[REG_ADDR, DATA1, DATA2, ...]
if (rxLen >= 2) {
currentRegAddr = rxData[0]; // 寄存器地址
if (currentRegAddr < REG_BUFFER_SIZE) {
// 写入寄存器数据
uint8_t dataLen = rxLen - 1;
memcpy(®Buffer[currentRegAddr], &rxData[1],
(dataLen <= REG_BUFFER_SIZE - currentRegAddr) ? dataLen : (REG_BUFFER_SIZE - currentRegAddr));
i2cCmd = I2C_CMD_WRITE_REG;
}
}
// 情况3:主机执行"直接读"或"寄存器读"(需返回对应数据)
// 生成应答数据(与主机读取逻辑匹配)
if (i2cCmd == I2C_CMD_WRITE_REG || i2cCmd == I2C_CMD_NONE) {
// 若主机写后读,则返回写入的寄存器数据(确保主机memcmp校验通过)
if (currentRegAddr < REG_BUFFER_SIZE) {
// 读取寄存器数据作为应答(长度与主机写入一致)
txLen = (rxLen < I2C_TX_BUFFER_SIZE) ? rxLen : I2C_TX_BUFFER_SIZE;
memcpy(txData, ®Buffer[currentRegAddr], txLen);
} else {
// 寄存器地址无效,返回错误码0xFF
txData[txLen++] = 0xFF;
}
} else if (i2cCmd == I2C_CMD_READ_SENSOR) {
// 若主机读取传感器数据,返回传感器AD值(示例)
txData[txLen++] = (sensorData[0].adValue >> 8) & 0xFF; // 高8位
txData[txLen++] = sensorData[0].adValue & 0xFF; // 低8位
txData[txLen++] = sensorData[0].crcValid; // CRC结果
}
// 将应答数据写入发送缓冲区,供主机读取
I2C_Slave_WriteTxBuffer(txData, txLen);
printf("已准备应答数据(长度:%d):", txLen);
for (uint8_t i = 0; i < txLen; i++) {
printf("0x%02X ", txData[i]);
}
printf("\r\n");
// 重置命令状态
i2cCmd = I2C_CMD_NONE;
}
/* 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 */
#include "soft_i2c.h"
#include "delay.h" // 引入自定义延时函数头文件
#include <string.h>
// 全局变量
static I2C_Slave_StateTypeDef i2c_state = I2C_SLAVE_IDLE;
static uint8_t rx_buffer[I2C_RX_BUFFER_SIZE];
static uint8_t rx_index = 0;
static uint8_t tx_buffer[I2C_TX_BUFFER_SIZE];
static uint8_t tx_index = 0;
static uint8_t tx_length = 0;
static uint8_t received_addr = 0;
static uint8_t is_read_mode = 0;
// 配置SDA为输出模式
static void I2C_SetSDAOutputMode(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C_SLAVE_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(I2C_SLAVE_SDA_PORT, &GPIO_InitStruct);
}
// 配置SDA为输入模式
static void I2C_SetSDAInputMode(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C_SLAVE_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(I2C_SLAVE_SDA_PORT, &GPIO_InitStruct);
}
// 产生应答信号
static void I2C_SendAck(void) {
I2C_SetSDAOutputMode();
HAL_GPIO_WritePin(I2C_SLAVE_SDA_PORT, I2C_SLAVE_SDA_PIN, GPIO_PIN_RESET); // 拉低SDA表示应答
I2C_delay(); // 使用I2C专用延时
}
// 产生非应答信号
static void I2C_SendNack(void) {
I2C_SetSDAOutputMode();
HAL_GPIO_WritePin(I2C_SLAVE_SDA_PORT, I2C_SLAVE_SDA_PIN, GPIO_PIN_SET); // 拉高SDA表示非应答
I2C_delay(); // 使用I2C专用延时
}
// 释放SDA线
static void I2C_ReleaseSDA(void) {
I2C_SetSDAInputMode();
}
// 读取SDA线状态
static uint8_t I2C_ReadSDA(void) {
return HAL_GPIO_ReadPin(I2C_SLAVE_SDA_PORT, I2C_SLAVE_SDA_PIN);
}
// 初始化I2C从机
void I2C_Slave_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置SCL引脚为输入,上拉
GPIO_InitStruct.Pin = I2C_SLAVE_SCL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(I2C_SLAVE_SCL_PORT, &GPIO_InitStruct);
// 配置SDA引脚为输入,上拉
GPIO_InitStruct.Pin = I2C_SLAVE_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(I2C_SLAVE_SDA_PORT, &GPIO_InitStruct);
// 初始化缓冲区
I2C_Slave_ClearRxBuffer();
I2C_Slave_ClearTxBuffer();
// 初始化状态
i2c_state = I2C_SLAVE_IDLE;
}
// 等待SCL线变为高电平
static uint8_t I2C_WaitForSCLHigh(uint32_t timeout_ms) {
uint32_t start_time = HAL_GetTick();
while (HAL_GPIO_ReadPin(I2C_SLAVE_SCL_PORT, I2C_SLAVE_SCL_PIN) == GPIO_PIN_RESET) {
if ((HAL_GetTick() - start_time) >= timeout_ms) {
return 0; // 超时
}
Delay1us(1); // 1us短暂延时
}
return 1; // 成功
}
// 等待SCL线变为低电平
static uint8_t I2C_WaitForSCLLow(uint32_t timeout_ms) {
uint32_t start_time = HAL_GetTick();
while (HAL_GPIO_ReadPin(I2C_SLAVE_SCL_PORT, I2C_SLAVE_SCL_PIN) == GPIO_PIN_SET) {
if ((HAL_GetTick() - start_time) >= timeout_ms) {
return 0; // 超时
}
Delay1us(1); // 1us短暂延时
}
return 1; // 成功
}
// 检测起始信号
static uint8_t I2C_DetectStart(void) {
// 起始条件:SCL高电平时,SDA从高到低跳变
if (HAL_GPIO_ReadPin(I2C_SLAVE_SCL_PORT, I2C_SLAVE_SCL_PIN) == GPIO_PIN_SET &&
I2C_ReadSDA() == GPIO_PIN_RESET) {
Delay1us(2); // 2us延时
// 再次确认
if (HAL_GPIO_ReadPin(I2C_SLAVE_SCL_PORT, I2C_SLAVE_SCL_PIN) == GPIO_PIN_SET &&
I2C_ReadSDA() == GPIO_PIN_RESET) {
return 1; // 检测到起始信号
}
}
return 0;
}
// 检测停止信号
static uint8_t I2C_DetectStop(void) {
// 停止条件:SCL高电平时,SDA从低到高跳变
if (HAL_GPIO_ReadPin(I2C_SLAVE_SCL_PORT, I2C_SLAVE_SCL_PIN) == GPIO_PIN_SET &&
I2C_ReadSDA() == GPIO_PIN_SET) {
Delay1us(2); // 2us延时
// 再次确认
if (HAL_GPIO_ReadPin(I2C_SLAVE_SCL_PORT, I2C_SLAVE_SCL_PIN) == GPIO_PIN_SET &&
I2C_ReadSDA() == GPIO_PIN_SET) {
return 1; // 检测到停止信号
}
}
return 0;
}
// 接收一个字节
static uint8_t I2C_ReceiveByte(void) {
uint8_t data = 0;
uint8_t i;
I2C_SetSDAInputMode();
for (i = 0; i < 8; i++) {
// 等待SCL变为高电平
if (!I2C_WaitForSCLHigh(1)) {
return 0; // 超时
}
// 读取数据位(高位在前)
data <<= 1;
if (I2C_ReadSDA()) {
data |= 0x01;
}
Delay1us(2); // 2us延时
// 等待SCL变为低电平
if (!I2C_WaitForSCLLow(1)) {
return 0; // 超时
}
}
return data;
}
// 发送一个字节
static void I2C_SendByte(uint8_t data) {
uint8_t i;
I2C_SetSDAOutputMode();
for (i = 0; i < 8; i++) {
// 设置数据位(高位在前)
if (data & 0x80) {
HAL_GPIO_WritePin(I2C_SLAVE_SDA_PORT, I2C_SLAVE_SDA_PIN, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(I2C_SLAVE_SDA_PORT, I2C_SLAVE_SDA_PIN, GPIO_PIN_RESET);
}
data <<= 1;
Delay1us(2); // 2us延时
// 等待SCL变为高电平
I2C_WaitForSCLHigh(1);
Delay1us(2); // 2us延时
// 等待SCL变为低电平
I2C_WaitForSCLLow(1);
}
I2C_ReleaseSDA(); // 释放SDA线,准备接收应答
}
// I2C从机处理函数,需要在主循环中调用
void I2C_Slave_Process(void) {
uint8_t ack;
// 检测起始信号
if (I2C_DetectStart()) {
i2c_state = I2C_SLAVE_ADDR_MATCHED;
rx_index = 0;
tx_index = 0;
// 接收地址字节
received_addr = I2C_ReceiveByte();
// 检查地址是否匹配
if ((received_addr & 0xFE) == (I2C_SLAVE_ADDR << 1)) {
// 地址匹配,确定读写模式
is_read_mode = (received_addr & 0x01);
// 发送应答
I2C_SendAck();
// 等待SCL变为低电平
I2C_WaitForSCLLow(1);
if (is_read_mode) {
i2c_state = I2C_SLAVE_SENDING;
} else {
i2c_state = I2C_SLAVE_RECEIVING;
}
} else {
// 地址不匹配,不发送应答
I2C_SendNack();
i2c_state = I2C_SLAVE_IDLE;
}
return;
}
// 检测停止信号
if (I2C_DetectStop()) {
i2c_state = I2C_SLAVE_IDLE;
return;
}
// 根据当前状态处理
switch (i2c_state) {
case I2C_SLAVE_RECEIVING:
// 接收数据
if (rx_index < I2C_RX_BUFFER_SIZE) {
rx_buffer[rx_index++] = I2C_ReceiveByte();
I2C_SendAck(); // 发送应答
I2C_WaitForSCLLow(1);
} else {
// 缓冲区已满,发送非应答
I2C_ReceiveByte(); // 仍然读取数据但不存储
I2C_SendNack();
I2C_WaitForSCLLow(1);
}
break;
case I2C_SLAVE_SENDING:
// 发送数据
if (tx_index < tx_length) {
I2C_SendByte(tx_buffer[tx_index++]);
// 等待主机应答
I2C_WaitForSCLHigh(1);
ack = I2C_ReadSDA();
I2C_WaitForSCLLow(1);
if (ack) {
// 收到非应答,停止发送
i2c_state = I2C_SLAVE_IDLE;
}
} else {
// 没有更多数据发送,发送0xFF
I2C_SendByte(0xFF);
// 等待主机应答
I2C_WaitForSCLHigh(1);
I2C_WaitForSCLLow(1);
i2c_state = I2C_SLAVE_IDLE;
}
break;
default:
break;
}
}
// 获取接收缓冲区的数据长度
uint8_t I2C_Slave_GetRxDataLength(void) {
return rx_index;
}
// 读取接收缓冲区数据
uint8_t I2C_Slave_ReadRxBuffer(uint8_t *data, uint8_t len) {
uint8_t i;
if (data == NULL || len == 0) {
return 0;
}
uint8_t read_len = (len < rx_index) ? len : rx_index;
for (i = 0; i < read_len; i++) {
data[i] = rx_buffer[i];
}
return read_len;
}
// 清空接收缓冲区
void I2C_Slave_ClearRxBuffer(void) {
rx_index = 0;
memset(rx_buffer, 0, I2C_RX_BUFFER_SIZE);
}
// 写入发送缓冲区
uint8_t I2C_Slave_WriteTxBuffer(uint8_t *data, uint8_t len) {
if (data == NULL || len == 0 || len > I2C_TX_BUFFER_SIZE) {
return 0;
}
memcpy(tx_buffer, data, len);
tx_length = len;
tx_index = 0;
return len;
}
// 清空发送缓冲区
void I2C_Slave_ClearTxBuffer(void) {
tx_length = 0;
tx_index = 0;
memset(tx_buffer, 0, I2C_TX_BUFFER_SIZE);
}
最新发布