GPIO软件模拟IIC时序--转载

2019-04-15 15:25发布

转载自http://blog.chinaunix.net/uid-29512885-id-5763629.html,仅用作备份,自己平时移植I2C驱动用

一、MPU6050中的IIC时序

1.1 START和STOP



SDA和SCL在高电平时,SDA拉低表示START。SCL拉低,表示可以传输数据。
SDA和SCL在低电平时,SDA拉高表示STOP。 SCL拉高,表示传输数据结束。
  1. /******************************************
  2. *函数原型: void IIC_Start(void)
  3. *功能: 产生IIC起始信号
  4. ******************************************/
  5. void IIC_Start(void)
  6. {
  7.     SDA_OUT();
  8.     IIC_SDA=1;
  9.     IIC_SCL=1;
  10.     delay_us(4);
  11.     IIC_SDA=0; //START:when CLK is high,SDA change from hig to low
  12.     delay_us(4);
  13.     IIC_SCL=0; //Ready Transmit or Receive
  14. }

  15. /******************************************
  16. *函数原型: void IIC_Start(void)
  17. *功能: 产生IIC结束信号
  18. ******************************************/
  19. void IIC_Stop(void)
  20. {
  21.     SDA_OUT();
  22.     IIC_SDA=0;
  23.     IIC_SCL=0; 
  24.     delay_us(4);
  25.     IIC_SDA=1; //STOP:when CLK is low,SDA change from low to high
  26.     IIC_SCL=1; //发送I2C总线结束信号
  27.     delay_us(4);
  28. }
一些的宏定义:
  1. #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
  2. #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) 
  3. #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) 

  4. #define GPIOB_ODR_Addr (GPIOB_BASE+12)
  5. #define GPIOB_IDR_Addr (GPIOB_BASE+8)


  6. #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
  7. #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入

  8. //驱动接口,GPIO模拟IIC
  9. //PB7->I2C_SDA
  10. //PB6->I2C_SCL
  11. #define SDA_IN() {GPIOB->CRL&=0x0FFFFFFF;GPIOB->CRL|=0x80000000;} //上拉输入
  12. #define SDA_OUT() {GPIOB->CRL&=0x0FFFFFFF;GPIOB->CRL|=0x30000000;} //通用推挽输出,不用硬件IIC


  13. //IO操作函数
  14. #define IIC_SCL PBout(6)
  15. #define IIC_SDA PBout(7)
  16. #define READ_SDA PBin(7)

1.2 ACK和NACK时序图


在START信号后,读取8位的数据,STM32需要对MPU6050发出响应以同步。
第九个SCL时,SCL从低电平变成高电平后,SDA如果是低电平则是ACK,如果是高电平则是NACK。
  1. /******************************************
  2. *函数原型: void IIC_Wait_Ack(void)
  3. *功能: 等待应答信号到来
  4. *输出; 1,接收应答失败
  5.        0,接收应答成功
  6. ******************************************/
  7. u8 IIC_Wait_Ack(void)
  8. {
  9.     u8 ucErrTime = 0;
  10.     SDA_IN();
  11.     IIC_SDA=1;
  12.     delay_us(1);
  13.     IIC_SCL=1;
  14.     delay_us(1);
  15.     while(IIC_SDA) //最多等待50us
  16.     {
  17.         ucErrTime++;
  18.         if(ucErrTime>50)
  19.         {
  20.             IIC_Stop();
  21.             return 1;
  22.         }
  23.         delay_us(1);
  24.     }
  25.     IIC_SCL=0; //时钟输出0
  26.     return 0;
  27. }

  28. /******************************************
  29. *函数原型: void IIC_Ack(void)
  30. *功能: 产生ACK应答信号SDA=0
  31. ******************************************/
  32. void IIC_Ack(void)
  33. {
  34.     IIC_SCL=0;
  35.     SDA_OUT();
  36.     IIC_SDA=0;
  37.     delay_us(1);
  38.     IIC_SCL=1;
  39.     delay_us(1);
  40.     IIC_SCL=0;
  41. }

  42. /******************************************
  43. *函数原型: void IIC_Ack(void)
  44. *功能: 产生ACK应答信号SDA=0
  45. ******************************************/
  46. void IIC_NAck(void)
  47. {
  48.     IIC_SCL=0;
  49.     SDA_OUT();
  50.     IIC_SDA=1;
  51.     delay_us(1);
  52.     IIC_SCL=1;
  53.     delay_us(1);
  54.     IIC_SCL=0;
  55. }

1.3 MPU6050写入时序



写时序的步骤:START+(MPU6050地址+W)+等待ACK+寄存器地址+等待ACK+写入的数据+等待ACK+STOP。
读时序的步骤:START+(MPU6050地址+W)+等待ACK+寄存器地址+START+读取数据+ACK响应+STOP。

  1. /****************************************************
  2. *函数原型: u8 IICwriteBytes(u8 dev, u8 reg, u8 length, u8 *data)
  3. *功能: 将多个字节写入指定设备 指定寄存器
  4. *输入: dev 目标设备地址
  5. * reg 寄存器地址
  6. * length 要写的字节数
  7. * *data 将要写的数据的首地址
  8. *返回: 返回是否成功,1成功
  9. ****************************************************/
  10. u8 IICwriteBytes(u8 dev, u8 reg, u8 length, u8 *data)
  11. {
  12.     u8 count = 0;
  13.     IIC_Start();
  14.     IIC_Send_Byte(dev); //发送写命令
  15.     IIC_Wait_Ack();
  16.     IIC_Send_Byte(reg); //发送写入的地址
  17.     IIC_Wait_Ack();
  18.     for(count=0;count<length;count++)
  19.     {
  20.         IIC_Send_Byte(data[count]);
  21.         IIC_Wait_Ack();
  22.     }
  23.     IIC_Stop(); //发送停止信号
  24.     
  25.     return 1;
  26. }

  27. /******************************************
  28. *函数原型: u8 IIC_Read_Byte(unsigned char ack)
  29. *功能: 读取一个Byte的字节
  30. *输入: 读取一个字节,ack=1,发送ACK,ack=0,发送nACK
  31. *返回: 读取到的Byte
  32. ******************************************/
  33. u8 IIC_Read_Byte(unsigned char ack)
  34. {
  35.     unsigned char i, receive = 0;
  36.     SDA_IN(); //设置为输入
  37.     for(i=0;i<8;i++)
  38.     {
  39.         IIC_SCL=0;
  40.         delay_us(1);
  41.         IIC_SCL=1;
  42.         receive<<=1;
  43.         if(READ_SDA) receive++;
  44.         delay_us(1);
  45.     }
  46.     if(ack)
  47.         IIC_Ack(); //发送ACK
  48.     else
  49.         IIC_NAck(); //发送NACK
  50.     
  51.     return receive;    
  52. }

留下了一个不太清楚的内容,按位,位长度写数据:

  1. /****************************************************
  2. *函数原型: u8 IICwriteBits(u8 dev, u8 reg, u8 bitStart, u8 length, u8 data)
  3. *功能: 读、修改、写指定设备、指定寄存器一个字节中的多个位
  4. *输入: dev 目标设备地址
  5. * reg 寄存器地址
  6. * bitStart 目标字节的起始位,自左向右?
  7. * length 位长度
  8. * data 存放改变目标字节位的值
  9. *返回: 返回是否成功,1成功
  10. ***************************