STM32的I2C作为主机的情况相信很多同学都用过,网上也有很多教程,但是作为从设备使用的例子应该不多,本文通过硬件和软件的层面,介绍如何把STM32设置为一个I2C从机。
1 硬件连接
测试方法:用一个USB转I2C的工具接到STM32的I2C引脚上,通过上位机工具进行读写操作。如果没有这个工具,也可以用另外一个stm32或者其他设备测试通讯,同时也可以借助示波器或者逻辑分析仪来辅助调试。
硬件连接:
STM32这边使用硬件I2C1(PB6、PB7),并外接上拉电阻。
2 软件编程
根据STM32数据参考手册,I2C作为从设备时发送和接收的流程如下:
测试例程:定义一个256字节的buffer用来存放I2C从机的数据,默认赋初值0-255,然后通过中断的方式实现I2C数据读写。
示例代码如下:
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_i2c.h"
#include "misc.h"
#define I2CSLAVE_ADDR 0x40 << 1 // address is 0x40
#define I2C1_CLOCK_FRQ 100000 // I2C-Frq in Hz (100 kHz)
#define I2C1_RAM_SIZE 256 // RAM Size in Byte
#define I2C1_MODE_WAITING 0 // Waiting for commands
#define I2C1_MODE_SLAVE_ADR_WR 1 // Received slave address (writing)
#define I2C1_MODE_ADR_BYTE 2 // Received ADR byte
#define I2C1_MODE_DATA_BYTE_WR 3 // Data byte (writing)
#define I2C1_MODE_SLAVE_ADR_RD 4 // Received slave address (to read)
#define I2C1_MODE_DATA_BYTE_RD 5 // Data byte (to read)
uint8_t i2c1_mode = I2C1_MODE_WAITING;
uint8_t i2c1_ram_adr = 0;
uint8_t i2c1_ram[I2C1_RAM_SIZE];
uint8_t Get_I2C1_Ram(uint8_t adr)
{
return i2c1_ram[adr];
}
void Set_I2C1_Ram(uint8_t adr, uint8_t val)
{
i2c1_ram[adr] = val;
return;
}
void I2C1_Ram_Init(void)
{
uint16_t i;
for (i = 0; i < 256; i++)
{
Set_I2C1_Ram(i, i);
}
}
void I2C1_Slave_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Configure I2C_EE pins: SCL and SDA */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure the I2C event priority */
NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Configure I2C error interrupt to have the higher priority */
NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
NVIC_Init(&NVIC_InitStructure);
/* I2C configuration */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = I2CSLAVE_ADDR;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = I2C1_CLOCK_FRQ;
/* I2C Peripheral Enable */
I2C_Cmd(I2C1, ENABLE);
/* Apply I2C configuration after enabling it */
I2C_Init(I2C1, &I2C_InitStructure);
I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE); //Part of the STM32 I2C driver
I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);
I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE); //Part of the STM32 I2C driver
I2C1_Ram_Init();
}
void I2C1_ClearFlag(void)
{
/* ADDR Flag clear */
while((I2C1->SR1 & I2C_SR1_ADDR) == I2C_SR1_ADDR)
{
I2C1->SR1;
I2C1->SR2;
}
/* STOPF Flag clear */
while((I2C1->SR1&I2C_SR1_STOPF) == I2C_SR1_STOPF)
{
I2C1->SR1;
I2C1->CR1 |= 0x1;
}
}
void I2C1_EV_IRQHandler(void)
{
uint8_t wert;
uint32_t event;
/* Reading last event */
event = I2C_GetLastEvent(I2C1);
/* Event handle */
if(event == I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED)
{
// Master has sent the slave address to send data to the slave
i2c1_mode = I2C1_MODE_SLAVE_ADR_WR;
}
else if(event == I2C_EVENT_SLAVE_BYTE_RECEIVED)
{
// Master has sent a byte to the slave
wert = I2C_ReceiveData(I2C1);
// Check address
if(i2c1_mode == I2C1_MODE_SLAVE_ADR_WR)
{
i2c1_mode = I2C1_MODE_ADR_BYTE;
// Set current ram address
i2c1_ram_adr = wert;
}
else
{
i2c1_mode = I2C1_MODE_DATA_BYTE_WR;
// Store data in RAM
Set_I2C1_Ram(i2c1_ram_adr, wert);
// Next ram adress
i2c1_ram_adr++;
}
}
else if(event == I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED)
{
// Master has sent the slave address to read data from the slave
i2c1_mode = I2C1_MODE_SLAVE_ADR_RD;
// Read data from RAM
wert = Get_I2C1_Ram(i2c1_ram_adr);
// Send data to the master
I2C_SendData(I2C1, wert);
// Next ram adress
i2c1_ram_adr++;
}
else if(event == I2C_EVENT_SLAVE_BYTE_TRANSMITTED)
{
// Master wants to read another byte of data from the slave
i2c1_mode = I2C1_MODE_DATA_BYTE_RD;
// Read data from RAM
wert = Get_I2C1_Ram(i2c1_ram_adr);
// Send data to the master
I2C_SendData(I2C1, wert);
// Next ram adress
i2c1_ram_adr++;
}
else if(event == I2C_EVENT_SLAVE_STOP_DETECTED)
{
// Master has STOP sent
I2C1_ClearFlag();
i2c1_mode = I2C1_MODE_WAITING;
}
}
void I2C1_ER_IRQHandler(void)
{
if (I2C_GetITStatus(I2C1, I2C_IT_AF))
{
I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
}
}
3 运行测试
3.1 I2C连续写入
通过上位机工具写入:
通过逻辑分析仪抓取波形:
3.2 I2C连续读取
通过上位机工具连续读取256字节:
通过逻辑分析仪抓取波形:
3.3 I2C单次读写测试
通过上位机工具读取原值,再写入新值,最后再读取新值:
通过逻辑分析仪抓取波形:
4 总结
通过上位机工具的测试以及逻辑分析仪的解析,STM32的硬件I2C从机通信正常且稳定,读写速度测试了100k和400k,没有发现问题,至此测试完成。
好了,关于STM32如何设置从机模式就介绍到这里,如果你们有什么问题,欢迎评论区留言。
需要完整源码工程的同学可以自行下载:源码下载地址 https://download.csdn.net/download/ShenZhen_zixian/87950363
另外还有一篇HAL库版本的博客:STM32设置为I2C从机模式(HAL库版本)
如果这篇文章能够帮到你,就…懂的。