1 I2C从机设备
用过的触摸屏或者传感器的驱动接口大部分是I2C,它们属于从机设备,STM32的从机例程不够详细,这里总结一下STM32的从机设备的接收中断处理
2 实测效果

3 测试方法
3.1 使用esp32-c2来作为主机
-获取版本信息
发送0x20后,从机返回0xE001两个字节
-获取实时数据
发送0x21后,从机返回帧头+数据+校验
c3 3c 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 98
其中
c3 3c 是帧头
00到0e是数据
98是校验和
4 测试应用
4.1 esp32c2
void i2c_master_init_stm32(void){ i2c_config_t conf = { .mode = I2C_MODE_MASTER, .sda_io_num = I2C_MASTER_SDA_IO, .scl_io_num = I2C_MASTER_SCL_IO, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master = { .clk_speed = I2C_MASTER_FREQ_HZ, } }; i2c_param_config(I2C_MASTER_NUM, &conf); i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);} esp_err_t i2c_master_read_slave(uint8_t reg_addr, uint8_t *data, size_t len){ i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); // 发送从机地址和写标志 i2c_master_write_byte(cmd, STM32_SLAVE_ADDR | I2C_MASTER_WRITE, true); // 发送寄存器地址 i2c_master_write_byte(cmd, reg_addr, true); i2c_master_start(cmd); // 重启I2C总线 // 发送从机地址和读标志 i2c_master_write_byte(cmd, STM32_SLAVE_ADDR | I2C_MASTER_READ, true); // 读取数据 if (len > 1) { i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK); } i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS)); i2c_cmd_link_delete(cmd); return ret;} 获取版本信息uint8_t out_version[2] = {0}; if (ESP_OK == i2c_master_read_slave(reg, out_version, sizeof(out_version))){ uint16_t fw_version = (out_version[0]<<8) | out_version[1]; rt_kprintf("ver: 0x%04X\n", fw_version); }else{ rt_kprintf("CMD_READ_VERSION error\n");} 获取实时数据uint8_t _data[18] = {0}; if (ESP_OK == i2c_master_read_slave(reg, _data, sizeof(_data))){ rt_kprintf("|"); for (int i=0;i<sizeof(_data);i++ ) {rt_kprintf("%02x ",_data[i]); } rt_kprintf("|\r\n"); }else{rt_kprintf("CMD_READ_DATA error\n");}
4.2 stm32f103
I2C_HandleTypeDef hi2c1;uint8_t i2c_rx_buffer[RX_BUFFER_SIZE]; /* External buffers and index */volatile uint8_t tx_buffer[18];uint8_t checksum_8(uint8_t *buffer, uint16_t buffer_length){ uint16_t sum = 0; /* pass through message buffer */ while (buffer_length--) { sum += *buffer++; } return sum*0xff;}4.2.1 中断处理- 寄存器版本volatile uint8_t tx_idx;void SlaveTxCpltCallback(uint8_t reg, I2C_TypeDef *I2Cx){ uint8_t byte = 0xFF; uint16_t length = 1; switch (reg) { case REG_VERSION: if (tx_idx == 0) { tx_buffer[0] = FIRMWARE_VERSION; tx_buffer[1] = 0x01; } byte = tx_buffer[tx_idx++]; length = 2; break; case REG_DATA: if (tx_idx == 0) { tx_buffer[0] = 0xC3; tx_buffer[1] = 0x3C; for (int i = 0; i < 16; i++) { tx_buffer[2 + i] = i; } tx_buffer[17] = checksum_8(tx_buffer, sizeof(tx_buffer)-1); } byte = tx_buffer[tx_idx++]; length = sizeof(tx_buffer); break; case REG_RESET: NVIC_SystemReset(); return; default: byte = 0xFF; length = 1; break; } /* Send and wrap index */ I2Cx->DR = byte; if (tx_idx >= length) { tx_idx = 0; }}void I2C1_EV_IRQHandler(void) { static uint8_t current_register = 0; uint32_t sr1 = I2C1->SR1; uint32_t sr2; //uint8_t addr7 = (uint8_t)((sr1 & I2C_SR1_ADDR) >> 1); /* Address match event */ if (sr1 & I2C_SR1_ADDR) { /* Reset index and register for each new transaction */ sr2 = I2C1->SR2; (void) sr2; tx_idx = 0; return; } /* Receive register byte */ if ((sr1 & I2C_SR1_RXNE) != 0) { current_register = (uint8_t)I2C1->DR; tx_idx = 0; return; } /* Transmit data when TXE and not BTF */ if ((sr1 & I2C_SR1_TXE) != 0 && !(sr1 & I2C_SR1_BTF)) { SlaveTxCpltCallback(current_register, I2C1); return; } /* Stop detection: clear by reading SR1 then writing CR1 */ if (sr1 & I2C_SR1_STOPF) { (void)I2C1->SR1; I2C1->CR1 |= I2C_CR1_PE; /* Reset for next transaction */ tx_idx = 0; } if (sr1 & I2C_SR1_AF) { I2C1->SR1 &= ~(I2C_SR1_AF); I2C_Reset(I2C1); } } - HAL版本void I2C1_ER_IRQHandler(void) { HAL_I2C_ER_IRQHandler(&hi2c1); HAL_I2C_ErrorCallback(&hi2c1);}void I2C1_EV_IRQHandler(void){ HAL_I2C_EV_IRQHandler(&hi2c1);}static uint8_t reg_addr; static uint16_t tx_len; /* HAL Callbacks ------------------------------------------------------*/void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode){ if (AddrMatchCode != (hi2c->Init.OwnAddress1)) { return; } // Prepare to receive register address if master writes if (TransferDirection == I2C_DIRECTION_TRANSMIT) { HAL_I2C_Slave_Seq_Receive_IT(hi2c, ®_addr, 1, I2C_FIRST_FRAME); } else { HAL_I2C_Slave_Seq_Transmit_IT(hi2c, tx_buffer, tx_len, I2C_NEXT_FRAME); }}void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c){ switch (reg_addr) { case REG_VERSION: tx_buffer[0] = FIRMWARE_VERSION; // version tx_buffer[1] = 0x01; tx_len = 2; break; case REG_DATA: tx_buffer[0] = 0xC3; tx_buffer[1] = 0x3C; for (int i = 0; i < 16; i++) tx_buffer[2 + i] = i; tx_buffer[17] = checksum_8(tx_buffer, sizeof(tx_buffer)-1); tx_len = 18; break; case REG_RESET: NVIC_SystemReset(); return; default: tx_buffer[0] = 0xFF; tx_len = 1; } }void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c){ HAL_I2C_EnableListen_IT(hi2c); }void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c){ HAL_I2C_EnableListen_IT(hi2c);}void enable_i2c_int(void){ SET_BIT(hi2c1.Instance->CR1, I2C_CR1_ACK); __HAL_I2C_ENABLE(&hi2c1); __HAL_I2C_ENABLE_IT(&hi2c1, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR); // Start listening for address match in interrupt mode HAL_I2C_EnableListen_IT(&hi2c1);}void I2C_Reset(I2C_TypeDef *I2C) { I2C->CR1 &= ~I2C_CR1_PE; // 关闭 I2C I2C->CR1 |= I2C_CR1_PE; // 重新启用 I2C } void I2C1_ForceReset(void) { __HAL_RCC_I2C1_FORCE_RESET(); // 强制复位 I2C1 __HAL_RCC_I2C1_RELEASE_RESET(); // 释放复位}void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) { uint32_t error = HAL_I2C_GetError(hi2c); if (error & HAL_I2C_ERROR_BERR) { //__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_BERR); I2C1->CR1 &= ~I2C_CR1_PE; // 关闭 I2C HAL_I2C_DeInit(&hi2c1); I2C1_ForceReset(); HAL_I2C_Init(&hi2c1); enable_i2c_int(); return; } if (error & HAL_I2C_ERROR_AF) { __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF); } if (error & HAL_I2C_ERROR_ARLO) { __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ARLO); } }
5 总结
处理I2C从机回复多字节数据时,每次只能向数据寄存器填充1个字节,根据长度和偏移位置来填充需要发送的数据