STM32的I2C从机接收
小陈学不停 2025-05-27

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

 #define STM32_SLAVE_ADDR                    0x50  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];  #define REG_VERSION            0x20#define REG_DATA               0x21#define REG_RESET              0xEE#define FIRMWARE_VERSION       0xE0/* 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个字节,根据长度和偏移位置来填充需要发送的数据


声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 相关技术文库
  • 工业
  • 安防
  • 航空
  • CAN
下载排行榜
更多
评测报告
更多
广告