STM32 软件模拟IIC/I2C 完成寻址、读、写、按页写等操作

STM32 软件模拟IIC/I2C 完成寻址、读、写、按页写等操作

使用STM32 模拟IIC进行通信,相较于自带硬件接口稳定。
寻址:想要知道总线上挂载的从机数量及其地址,需要在总线写地址,如果相应位有应答,器件地址
(地址位为7位)左移一位低位补0进行显示,无应答位使用“–”替代。

实际运行效果: (显示效果类似于树莓派,树莓派显示原7位地址,高位补0)效果
按页写完读(主函数示例):从0x00地址开始,依次写入17个字节(数组已定义赋值,非赋值数组最好放在函数外做为全局变量,这样默认初始值就为0,而在函数内则为随机值),16个字节为1页,写完1页数据保存进非易失区后,继续写入剩余字节。从0x00地址开始,依次读出17个字节。

iic.c

IIC为半双工通信,数据线SDA既用作于发又用作于收但是不能同时进行,而STM32不同于51类单片机的是,IO脚需要配置输入输出模式,所以在用IO模拟IIC时STM32比51多两个函数:
IIC_SDA_IN();IIC_SDA_OUT();

因为有些例程中通过寄存器配置的方式转换IO输入输出,不是太理解,所以改用库函数进行配置。
#include "firmware.h"//里面包含iic.h

void IIC_Init(void)
{ 
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE );//时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;//SDA SCL WP
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ; //
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOF, &GPIO_InitStructure); //
	GPIO_SetBits(GPIOF,GPIO_Pin_0 | GPIO_Pin_1); //
}

void IIC_SDA_IN(void)//SDA用作输入:从机返回数据,从机应答等
{
	GPIO_InitTypeDef GPIO_InitStructure;

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN ; //
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOF, &GPIO_InitStructure); 	
}

void IIC_SDA_OUT(void)//SDA用作输出:写地址,写数据等
{
	GPIO_InitTypeDef GPIO_InitStructure;

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ; //
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOF, &GPIO_InitStructure); 	
}


void IIC_Start(void)//开始信号
{
	IIC_SDA_OUT(); //SDA定义为输出
	IIC_SDA = 1; 
	IIC_SCL = 1;
	delay_us(4);
	IIC_SDA = 0;  
	delay_us(4);
	IIC_SCL = 0; 
} 

void IIC_Stop(void)//停止信号
{
	IIC_SDA_OUT(); //SDA定义为输出
	IIC_SCL = 0;
	IIC_SDA = 0; 
	delay_us(4);
	IIC_SCL = 1; 
	delay_us(4);
	IIC_SDA = 1; //发送 I2C 总线结束信号
	delay_us(4); 
}

u8 IIC_Wait_Ack(void)//等待应答信号:0-应答;1-非应答
{
	u8 ack = 0;
	IIC_SDA = 1;
	IIC_SDA_IN(); //SDA定义为输入 
	delay_us(4); 
	IIC_SCL = 1;
	delay_us(1);
	ack = READ_SDA;
	delay_us(4);
	IIC_SCL = 0;
	return (ack); 
} 


void IIC_Ack(void)//产生 ACK 应答
{ 
	IIC_SCL = 0;
	IIC_SDA_OUT();
	IIC_SDA = 0;
	delay_us(2);
	IIC_SCL = 1;
	delay_us(2);
	IIC_SCL = 0;
}
 
void IIC_NAck(void)//产生 NACK 非应答
{ 
	IIC_SCL = 0;
	IIC_SDA_OUT();
	IIC_SDA = 1;
	delay_us(2);
	IIC_SCL = 1;
	delay_us(2);
	IIC_SCL = 0;
} 

void IIC_Write_Byte(u8 txd)//IIC 发送一个字节
{ 
	u8 t; 
	IIC_SDA_OUT(); 
	IIC_SCL = 0;//拉低时钟开始数据传输
	for(t = 0;t < 8;t++)
	{ 
		IIC_SDA = (txd & 0x80)>>7; //数据高位在前,低位在后,所以先发送高位
		txd <<= 1; 
		delay_us(4); 
		IIC_SCL = 1;
		delay_us(4); 
		IIC_SCL = 0;
		//delay_us(2);
	}
} 

u8 IIC_Read_Byte(void)//读一个字节
{ 
	u8 i,receive = 0;
	
	IIC_SDA_IN(); //SDA定义为输入
	for(i = 0;i < 8;i++ )
	{  
		delay_us(4);
		IIC_SCL = 1;
		receive <<= 1;
		if(READ_SDA == 1)
		{
			receive ++;
		}
		delay_us(4); 
		IIC_SCL = 0;
	}
	return receive;
} 

u8 Write_Device_Addr(u8 addr)//只写地址
{
	u8 Read_ACK = 0;
	IIC_Start();
	IIC_Write_Byte(addr);
	Read_ACK = IIC_Wait_Ack();
	IIC_Stop();
	
	return(Read_ACK);
}

void Search_Device_Addr(void)//在总线上搜寻挂载的器件地址
{
	u8 result = 0;
	u8 j = 0;
	for(j = 0;j < 128; j++)
	{
		if((j % 16) == 0)
		{
			printf("\r\n");
		}
		result = Write_Device_Addr(j << 1);
		if(result == 0)
		{
			printf(" %X ",j << 1);//%X 十六进制输出,大写;%x 小写
		}
		else
		{
			printf(" -- ");
		}
	}
}

u8 Device_Read_One_Byte(u8 IC_Addr,u8 Addr)//读指定器件的指定位置中的一个字节
{
	u8 dat;

	IIC_Start();
	IIC_Write_Byte(IC_Addr);//写地址,7位地址左移,低位补0
	IIC_Wait_Ack();//等待应答

	IIC_Write_Byte(Addr);//写位置
	IIC_Wait_Ack();

	IIC_Start();
	IIC_Write_Byte((IC_Addr) | 0x01);//读写位改为读
	IIC_Wait_Ack();

	dat = IIC_Read_Byte();
	IIC_NAck();//读一个字节结束
	IIC_Stop();
	
	return dat;
}

void Device_Write_One_Byte(u8 IC_Addr,u8 Addr,u8 data)//写指定器件的指定位置中的一个字节
{
	IIC_Start();
	IIC_Write_Byte(IC_Addr);//写地址,7位地址左移,低位补0
	IIC_Wait_Ack();//等待应答

	IIC_Write_Byte(Addr);//写位置
	IIC_Wait_Ack();

	IIC_Write_Byte(data);//写数据
	IIC_Wait_Ack();

	IIC_Stop();//发送完结束信号后,器件才会把数据进行擦写操作,搬运到非易失区,这段时间器件不再响应主机操作
}

void Device_Read_Bytes(u8 IC_Addr,u8 Addr,u8 *buf,u8 len)//连续读指定器件的指定位置中的多个字节
{
	IIC_Start();
	IIC_Write_Byte(IC_Addr);//写地址,7位地址左移,低位补0
	IIC_Wait_Ack();//等待应答

	IIC_Write_Byte(Addr);//写位置
	IIC_Wait_Ack();

	IIC_Start();
	IIC_Write_Byte((IC_Addr) | 0x01);//读写位改为读
	IIC_Wait_Ack();

	while(len > 1)
	{
		*buf++ = IIC_Read_Byte();
		IIC_Ack();
		len--;
	}
	*buf = IIC_Read_Byte();//循环体结束指针已经指向最后一个字节存放位置
	IIC_NAck();//读一个字节结束
	IIC_Stop();	
}

void Device_Write_Bytes(u8 IC_Addr,u8 Addr,u8 *buf,u8 len)//连续写指定器件的指定位置中的多个字节
{
	while(len > 0)
	{
		IIC_Start();
		IIC_Write_Byte(IC_Addr);//写地址,7位地址左移,低位补0
		IIC_Wait_Ack();//等待应答

		IIC_Write_Byte(Addr++);//写位置
		IIC_Wait_Ack();

		IIC_Write_Byte(*buf++);//写数据
		IIC_Wait_Ack();

		IIC_Stop();//发送完结束信号后,器件才会把数据进行擦写操作,搬运到非易失区,这段时间器件不再响应主机操作
		delay_ms(10);//eeprom连续写时必须加,否则下个字节写入失败
		len--;
	}
}

void Device_Page_Write(u8 IC_Addr,u8 Addr,u8 *buf,u8 len)//按页写指定器件的指定位置
{
	u8 i = 0;
	while(len > 0)
	{
		IIC_Start();
		IIC_Write_Byte(IC_Addr);//写地址,7位地址左移,低位补0
		IIC_Wait_Ack();//等待应答

		IIC_Write_Byte(Addr);//写位置
		IIC_Wait_Ack();
		while(len > 0)
		{
			IIC_Write_Byte(*buf++);//写数据
			IIC_Wait_Ack();
			len--;
			Addr++;//防止写入字节数比一页多,自动累加至下一页起始地址
			i++;//判断写入字节数
			if(i == Page_Size)
			{
				break;
			}
		}
		IIC_Stop();//发送完结束信号后,器件才会把数据进行擦写操作,搬运到非易失区,这段时间器件不再响应主机操作
		delay_ms(10);//eeprom连续写时必须加,否则下个字节写入失败
		i = 0;
	}
}

iic.h

#ifndef __IIC_H
#define __IIC_H

#include "firmware.h"

#define IIC_SCL PFout(1)  //模拟输出时钟脉冲
#define IIC_SDA PFout(0)  //数据线定义输出
#define IIC_WP  PFout(2)  //数据写保护引脚

#define READ_SDA PFin(0)  //数据线定义输入

#define Page_Size 16     //一页存储的字节数,具体看相应器件的datasheet

void IIC_SDA_IN(void);   //IO定义输入输出配置
void IIC_SDA_OUT(void);
	
void IIC_Init(void);
void IIC_Start(void);
void IIC_Stop(void);
u8 IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
void IIC_Write_Byte(u8 txd);
u8 IIC_Read_Byte(void);
u8 Write_Device_Addr(u8 addr);
void Search_Device_Addr(void);
u8 Device_Read_One_Byte(u8 IC_Addr,u8 Addr);
void Device_Write_One_Byte(u8 IC_Addr,u8 Addr,u8 data);
void Device_Read_Bytes(u8 IC_Addr,u8 Addr,u8 *buf,u8 len);
void Device_Write_Bytes(u8 IC_Addr,u8 Addr,u8 *buf,u8 len);
void Device_Page_Write(u8 IC_Addr,u8 Addr,u8 *buf,u8 len);

#endif

main.c

#include "firmware.h"
#include "string.h"

//u8 buf[17];
u8 buf[17] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
			  0x10,0x11,0x12,0x13,0x14,0x15,0x16};

int main(void)
{
	u8 i = 0;
	
	ALL_Init();//包含IIC初始化
	
	printf("123456789\r\n");
	
	IIC_WP = 0;//写保护引脚拉低
	Search_Device_Addr();
	printf("\r\n");
	delay_ms(10);
	
	for(i = 0;i < 17;i++)//处理定义的数组
	{
		buf[i] = buf[i] + 1;
	}
	Device_Page_Write(0xA4,0x00,buf,17);//页写入处理过后的数组
	delay_ms(10);
	Device_Read_Bytes(0xA4,0x00,buf,17);//读出写过后地址的数据
	for(i = 0;i < 17;i++)
	{
		printf(" %2X ",buf[i]);//十六进制显示
	}
	while(1)
	{
		
	}
}

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的C语言程序,用于在STM32模拟IIC从机: ```c #include "stm32f4xx.h" #define I2C_SLAVE_ADDRESS 0x50 void I2C_Configuration(void); void I2C_Read_Callback(uint8_t* p_data, uint16_t size); int main(void) { uint8_t data[10] = {0}; I2C_Configuration(); while(1) { // Wait for I2C transaction to complete } } void I2C_Configuration(void) { GPIO_InitTypeDef GPIO_InitStruct; I2C_InitTypeDef I2C_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; // Enable I2C and GPIO clocks RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // Configure GPIO pins for I2C GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStruct); // Configure I2C I2C_InitStruct.I2C_Mode = I2C_Mode_SMBusSlave; I2C_InitStruct.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS << 1; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_ClockSpeed = 100000; // 100 kHz I2C_Init(I2C1, &I2C_InitStruct); // Enable interrupts for I2C event and buffer interrupts NVIC_InitStruct.NVIC_IRQChannel = I2C1_EV_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = I2C1_ER_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); // Enable I2C interrupts I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE); // Enable I2C I2C_Cmd(I2C1, ENABLE); } void I2C_Read_Callback(uint8_t* p_data, uint16_t size) { // Handle I2C read request here } void I2C1_EV_IRQHandler(void) { uint8_t data[10] = {0}; uint16_t data_len = 0; if(I2C_GetITStatus(I2C1, I2C_IT_ADDR)) { // Address matched, read request if(I2C_GetTransferDirection(I2C1) == I2C_DirectionReceiver) { // Call callback function to handle read request I2C_Read_Callback(data, data_len); } } else if(I2C_GetITStatus(I2C1, I2C_IT_RXNE)) { // Data received } else if(I2C_GetITStatus(I2C1, I2C_IT_TXE)) { // Data transmitted } else if(I2C_GetITStatus(I2C1, I2C_IT_STOPF)) { // Stop condition detected I2C_ClearITPendingBit(I2C1, I2C_IT_STOPF); } } void I2C1_ER_IRQHandler(void) { // Handle I2C errors here } ``` 该程序首先配置了GPIO和I2C模块,然后在主循环中等待I2C事务完成。当I2C事件发生时,`I2C1_EV_IRQHandler()`中断处理程序将被调用。在该函数中,程序检查I2C事件类型,并针对不同的事件类型执行不同的操作。例如,如果地址匹配,则表示收到了取请求,因此程序将调用回调函数来处理该请求。如果数据接收或数据传输,则程序将取或入收到的数据。如果检测到停止条件,则程序将清除中断标志位。最后,如果出现I2C错误,则将在`I2C1_ER_IRQHandler()`中断处理程序中处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值