用STM32F4探索者做软件模拟IIC实验

1. 实验目的

通过软件模拟I2C,实现和24C02(EEPROM)之间的双向通信。

2. 实验流程

初始化IIC;
编写起始、停止、应答信号函数;
编写向24C02指定地址写入数据函数;
编写在24C02指定地址读取数据函数;
编写main函数

2.1 初始化IIC

这里是使用GPIOB8,GPIOB9模拟IIC,GPIOB8对应的是IIC的SCL,GPIOB9对应的是IIC的SDA。
在这里插入图片描述
如图所示,PB9对应的就是18位和第19位,00是输入模式,01是输出模式;两个位&(与运算),相同为本身,不同为0;两个位|(或运算),有1为1,两者0为0;

&= ~(1<<X);就是把X位置0,其他位置不变。
|=1<<X; 就是把X位置1,其他位置不变。

GPIOB->MODER&=~(3<<(9*2));(3<<(9 * 2))就是把第18位和第19位置为1,其他位置为0; ~(3<<(9 * 2))就是把第18位和第19位置为0,其他位置为1;&= ~(3<<(9 * 2));就是把第18位和第19位置为0,其他位变为原来的值。GPIOB->MODER|=0<<9 * 2;就是第18位置为0,其他位不变。(这里可以不加这个,不影响),所以就是输入模式。

GPIOB->MODER&=~(3<<(9 * 2));就是把第18位和第19位置为0,其他位变为原来的值;GPIOB->MODER|=1<<9 * 2; 就是第18位置为1,其他位不变。所以就是输出模式。

#define SDA_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}	 //PB9输入模式  
#define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;}  //PB9输出模式
#define IIC_SCL    PBout(8)   //SCL,可以输出0和1
#define IIC_SDA    PBout(9)   //SDA,可以输出0和1
#define READ_SDA   PBin(9)    //输入SDA 
//初始化IIC
void IIC_Init(void)
{			
	  GPIO_InitTypeDef  GPIO_InitStructure;
	  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
	  //GPIOB8,B9初始化设置
	  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式   
	  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
	  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉          
	  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
	  IIC_SCL=1;  //默认拉高的,因为空闲状态就是电平拉高的
	  IIC_SDA=1;  //默认拉高的,因为空闲状态就是电平拉高的
}

2.2 编写起始、停止、应答信号函数

2.2.1 产生IIC起始信号

在这里插入图片描述产生起始信号:SDA线开始输出高电平,SCL开始输出高电平,过一段时间,SDA线输出低电平,SCL还是输出高电平。这里的IIC_SCL=0;是下一时刻准备发送或接收数据。

//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //SDA线输出, PB9输出模式,PB8初始化就是输出模式
	IIC_SDA=1;	   //拉高,代表空闲状态	   
	IIC_SCL=1;     //拉高,代表空闲状态
	delay_us(4);
 	IIC_SDA=0;     //START:when CLK is high,DATA change form high to low  //变低了
	delay_us(4);   //延长一段时间是根据时序图来的
	IIC_SCL=0;     //钳住I2C总线,准备发送或接收数据 
}	  
2.2.2 产生IIC停止信号

产生停止信号:SDA线开始输出低电平,SCL线开始输出低电平,过一小段时间,SDA线输出高电平,SCL线输出高电平。

//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();     //SDA线输出
	IIC_SCL=0;     //开始的时候就是低,然后再变为高的
	IIC_SDA=0;     //STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;     //发送I2C总线结束信号
	delay_us(4);							   	
}
2.2.3 等待应答信号

在这里插入图片描述
READ_SDA 是读取PB9输入状态的值是0还是1;如果是0则结束循环,代表数据有效,返回0;是1则循环,如果一段时间还是1,代表数据无效,返回1。

#define READ_SDA   PBin(9)    //输入SDA 
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();            //SDA设置为输入  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)      //是1(高电平)就无效
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
2.2.4 产生ACK应答

在这里插入图片描述
产生应答信号就是在SCL线由低电平——高电平——低电平,这期间SDA线都处于低电平。

//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
2.2.5 不产生ACK应答

不产生应答信号就是在SCL线由低电平——高电平——低电平,这期间SDA线都处于高电平。

//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
2.2.6 IIC发送一个字节

发送一个字节,一位一位发送出去;0x80是1000 0000,txd&0x80是取其最高位, txd<<=1; 把数据左移1位,下一次就发送次高位,以此类推,把8位数据发完。注意在发送数据的时候,SCL线由低电平——高电平——低电平。
在这里插入图片描述

//IIC发送一个字节
//返回从机有无应答
//1,有应答  
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	 //设置为输出    
    IIC_SCL=0;   //拉低时钟开始数据传输     //取最高位,右移7位
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7; 
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
}
2.2.7 IIC读取一个字节

读取一个字节,也是一个位一个位就行读,if(READ_SDA)receive++; 如果读到SDA线上是1就加1。

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

2.3 编写向24C02指定地址写入数据函数

在这里插入图片描述
写的时候,EEPROM的设备地址是0XA0;读的时候,EEPROM的设备地址是0XA1。
在这里插入图片描述
MSB:最高位;LSB:最低位;这里的24C02的总容量是256,所以发送存储地址的时候,不用分高8位和低8位来发送。

产生起始信号;
发送从设备地址;
等待从设备应答;
发送写入数据的目的地址;
等待从设备应答;
发送要写入的数据;
等待从设备应答;
产生停止信号;

//在AT24CXX指定地址写入一个数据 
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{	
  IIC_Start();                 //产生起始信号  	
  IIC_Send_Byte(0XA0);         //发送器件地址0XA0,写数据 
  IIC_Wait_Ack();	           //等待应答  
  IIC_Send_Byte(WriteAddr);    //发送写入数据的目的地址
  IIC_Wait_Ack(); 	 		   //等待应答 								  		   
  IIC_Send_Byte(DataToWrite);  //发送要写入的数据							   
  IIC_Wait_Ack();  		       //等待应答    
  IIC_Stop();                  //产生停止信号 
  delay_ms(10);	 
}
//在AT24C02里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为 0~255
//pBuffer   :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
	while(NumToWrite--)
	{
		AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
		WriteAddr++;
		pBuffer++;
	}
}

2.4 编写在24C02指定地址读取数据函数

在这里插入图片描述

产生起始信号;
发送写从设备地址(0XA0);
等待从设备应答;
发送读取数据的目的地址;
等待从设备应答;
产生起始信号;
发送读从设备地址(0XA1);
等待从设备应答;
读取数据;
产生停止信号;

//在24C02指定地址读出一个数据
//ReadAddr:开始读数的地址  
//返回值  :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=0;		  	    	//接收数据变量															 
    IIC_Start();                //产生起始信号
	IIC_Send_Byte(0XA0);        //发送器件地址0XA0,写数据 	   
	IIC_Wait_Ack();             //等待应答
    IIC_Send_Byte(ReadAddr);    //发送读取数据地址
	IIC_Wait_Ack();	            //等待应答
	IIC_Start();  	 	        //产生起始信号
	IIC_Send_Byte(0XA1);        //进入接收模式			   
	IIC_Wait_Ack();	            //等待应答
    temp=IIC_Read_Byte(0);      //接收数据		   
    IIC_Stop();                 //产生一个停止条件	    
	return temp;                //返回读取的结果
}
//在AT24C02里面的指定地址开始读出指定个数的数据
//ReadAddr :开始读出的地址 对24c02为0~255
//pBuffer  :数据数组首地址
//NumToRead:要读出数据的个数
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
	while(NumToRead)
	{
		*pBuffer++ = AT24CXX_ReadOneByte(ReadAddr++);	
		NumToRead--;
	}
}

2.5 main.c函数

const u8 TEXT_Buffer[]={"Explorer STM32F4 IIC TEST"};   //要写入到24c02的字符串数组
#define SIZE sizeof(TEXT_Buffer)	//写入的字符串数组大小
 int main(void)
{ 
	u8 key;
	u16 i;
	u8 datatemp[SIZE];	  /  /接收读取数据的数组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);    //初始化延时函数
	uart_init(115200);	//初始化串口波特率为115200
	LED_Init();					//初始化LED 
	KEY_Init(); 				//按键初始化  
	AT24CXX_Init();			   //IIC初始化 
	AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);   //写入一个数组数据
	AT24CXX_Read(0,datatemp,SIZE);            //读取刚刚写入的数据
	for(i = 0;i < SIZE;i++){	
		printf("%c",datatemp[i]);	          //打印接收到的数据
		if(i == SIZE-1){
		printf("\r\n");	
		}
	}
	while(1){} 
}

3. 实验结果

在这里插入图片描述
读出了刚刚写入24C02的数据。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值