EFM32 模拟I2C案例代码(二)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_36075612/article/details/90208596

这篇跟第一篇一个样,我这里只是方便做下记录而已,各位看官随意选择阅读。需创建两个文件my_i2c.h与my_i2c.c,该代码是参照正点原子实例代码修改。(EFM32与MPU9250模拟I2C通讯)

my_i2c.h:

#ifndef __MY_I2C_H
#define	__MY_I2C_H

#include <stdint.h>
#include "em_chip.h"
#include "em_cmu.h"
#include "em_gpio.h"


/* Using PA0 (SDA) and PA1 (SCL) */

#define SDA_IN()   GPIO_PinModeSet(gpioPortA, 0, gpioModeInput, 1);          //PA0 输入模式
#define SDA_OUT()  GPIO_PinModeSet(gpioPortA, 0, gpioModePushPull, 1);       //PA0 输出模式

//IO操作函数     
//SCL   PA1
#define I2C_SCL_1()  	 {GPIO_PinOutSet(gpioPortA,1);}						 //高
#define I2C_SCL_0()  	 {GPIO_PinOutClear(gpioPortA,1);} 					 //低
//SDA   PA0
#define I2C_SDA_1()  	 {GPIO_PinOutSet(gpioPortA,0);}						 //高
#define I2C_SDA_0()  	 {GPIO_PinOutClear(gpioPortA,0);} 					 //低

#define I2C_SDA_READ()   (GPIO_PinInGet(gpioPortA, 0))   					 //输入SDA 


//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);				//发送IIC开始信号
void IIC_Stop(void);	  			//发送IIC停止信号
void IIC_Send_Byte(uint8_t txd);			//IIC发送一个字节
uint8_t IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
uint8_t IIC_Wait_Ack(void); 				//IIC等待ACK信号
void IIC_Ack(void);					//IIC发送ACK信号
void IIC_NAck(void);				//IIC不发送ACK信号

void IIC_Write_One_Byte(uint8_t daddr,uint8_t addr,uint8_t data);
uint8_t IIC_Read_One_Byte(uint8_t daddr,uint8_t addr);	 

#endif

 my_i2c.c:

#include "em_i2c.h"
#include "em_emu.h"
#include "my_i2c.h"
#include "em_cmu.h"
#include "em_gpio.h"

/**
 * 长延时函数	 us=1,延时1us
 * 
 * @author jun (2019/5/11)
 * 
 * @param us 
 * 
 * @return void 
 */
static void delay_us(uint32_t us)
{
  uint32_t i;
  for(; us!=0; us--)
       for (i=0; i<10; i++);        //如果I2C时钟线交叉时,增大i的值即可
}

/**
 * IIC初始化函数	
 * 
 * @author jun (2019/5/11)
 * 
 * @param void 
 * 
 * @return void 
 */
void IIC_Init(void)
{	
	CMU_ClockEnable(cmuClock_GPIO, true);
	
	/* Starting LFXO and waiting until it is stable */
	CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
	/* Routing the LFXO clock to the RTC */
	CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);		//32KHz
	/* Configure interrupt pin*/
	GPIO_PinModeSet(gpioPortC, 4, gpioModeInput, 0);
	/* Using PA0 (SDA) and PA1 (SCL) */
	GPIO_PinModeSet(gpioPortA, 0, gpioModeWiredAndPullUpFilter, 1);		//SDA	PA0
	GPIO_PinModeSet(gpioPortA, 1, gpioModeWiredAndPullUpFilter, 1);		//SCL	PA1
	
	I2C_SDA_1();
	I2C_SCL_1();  
}

/**
 * IIC起始信号
 * 
 * @author jun (2019/5/11)
 * 
 * @param void 
 * 
 * @return void 
 */
void IIC_Start(void)
{
	//SDA_OUT();     //sda线输出
	I2C_SDA_1();	  	  
	I2C_SCL_1();
	delay_us(4);
 	I2C_SDA_0();//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	I2C_SCL_0();//钳住I2C总线,准备发送或接收数据 
}	  

/**
 * IIC停止信号
 * 
 * @author jun (2019/5/11)
 * 
 * @param void 
 * 
 * @return void 
 */
void IIC_Stop(void)
{
	//SDA_OUT();//sda线输出
	I2C_SCL_0();
	I2C_SDA_0();//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	I2C_SCL_1(); 
	delay_us(4);			
	I2C_SDA_1();//发送I2C总线结束信号		
}


/**
 * 等待应答信号到来
 * 
 * @author jun (2019/5/11)
 * 
 * @param void 
 * 
 * @return 1,接收应答失败 
 *         0,接收应答成功
 */
uint8_t IIC_Wait_Ack(void)
{
	uint8_t ucErrTime=0;
	//SDA_IN();      //SDA设置为输入  
	I2C_SDA_1(); delay_us(1);	   
	I2C_SCL_1(); delay_us(1);	 
	while(I2C_SDA_READ())
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	I2C_SCL_0();//时钟输出0 	   
	return 0;  
} 

/**
 * 产生ACK应答
 * 
 * @author jun (2019/5/11)
 * 
 * @param void 
 * 
 * @return void
 */
void IIC_Ack(void)
{
	I2C_SCL_0();
	//SDA_OUT();
	I2C_SDA_0();
	delay_us(2);
	I2C_SCL_1();
	delay_us(2);
	I2C_SCL_0();
}
 
/**
 * 不产生ACK应答	
 * 
 * @author jun (2019/5/11)
 * 
 * @param void 
 * 
 * @return void
 */
void IIC_NAck(void)
{
	I2C_SCL_0();
	//SDA_OUT();
	I2C_SDA_1();
	delay_us(2);
	I2C_SCL_1();
	delay_us(2);
	I2C_SCL_0();
}					 				     

/**
 * IIC发送一个字节
 * 
 * @author jun (2019/5/11)
 * 
 * @param txd 
 * 
 * 返回从机有无应答
 * @return  1,有应答
 *          0,无应答		
 */
void IIC_Send_Byte(uint8_t txd)
{                        
    uint8_t t;   
	//SDA_OUT(); 	    
    I2C_SCL_0();//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {     
		if(txd&0x80) {
			I2C_SDA_1();
		} else { 
			I2C_SDA_0();
		}			
		delay_us(2);   	 //对TEA5767这三个延时都是必须的
		I2C_SCL_1();
		delay_us(2); 
		I2C_SCL_0();	
		if(t == 7) {
			I2C_SDA_1(); // 释放总线
		}
		txd<<=1;         /* 左移一个bit */
		delay_us(2);
    }	 
} 	    
  
/**
 * 读1个字节
 * 
 * @author jun (2019/5/11)
 * 
 * @param ack 
 * 			    ack=1时,发送ACK
 * 				ack=0时,发送nACK 
 * 
 * @return  
 *          	
 */
uint8_t IIC_Read_Byte(uint8_t ack)
{
	uint8_t i,receive=0;
    //SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++)
	{
        I2C_SCL_0(); 
        delay_us(2);
		I2C_SCL_1();
        receive<<=1;
        if(I2C_SDA_READ())receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

调用

#include "em_cmu.h"
#include "em_gpio.h"
#include "my_i2c.h"
#include "mpu9250.h"


static void Delayms(uint32_t ms)
{
  uint32_t i;
  
  for(; ms != 0; ms--)	
       for (i=0; i<500; i++);
}


int main(void)
{
    uint8_t res = 0;
    IIC_Init(); 
    Init_MPU9250();
    res=MPU_Read_Byte(MPU9250_ADDR,MPU_DEVICE_ID_REG);
    Delayms(100);
    MPU_Write_Byte(MPU9250_ADDR,MPU_INTBP_CFG_REG,0X82);
    Delayms(1);
	res=MPU_Read_Byte(MPU9250_ADDR,MPU_INTBP_CFG_REG);
    printf("res : %d \r\n", res);
}

往MPU9250中的0x37寄存器,写入0x82的数据,波形如下:

 读取MPU9250中的0x37寄存器的数据(0x82),波形如下:

读取MPU9250中的0x75寄存器的数据(0x71),也就是ChipID值,波形如下: 

//时间太短,延时不够长
 static void delay_us(uint32_t us)
{
  uint32_t i;
  for(; us!=0; us--)
       for (i=0; i<1; i++);
}

//如果I2C时钟线的停止位与起始位交叉时,修改my_i2c.c中的delay_us()函数中的 i 值,增大即可。


//时间修改长点
 static void delay_us(uint32_t us)
{
  uint32_t i;
  for(; us!=0; us--)
       for (i=0; i<10; i++);
}

停止位与起始位交叉问题,已解决。 

猜你喜欢

转载自blog.csdn.net/qq_36075612/article/details/90208596
今日推荐