今天的重点是利用STM32的软件方案和MPU60506轴姿态传感器建立通讯,今天只完成了简单的发送地址和接收应答的部分,特此记录一下过程,以后忘记可以随时翻出来看看。
先介绍最基本的I2C通讯的最基本的6个时序:
一:起始条件:SCL高电平期间,SDA从高电平切换到低电平
时序图从左到右的顺序为1、2、3、4.
二:终止条件:SCL高电平期间,SDA从低电平切换到高电平
三:发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
四:接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
五:发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
六:接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
好了到此结束,整个I2C通讯的所有最基本的时序都理清了,和固定的芯片通讯都要建立在这6个基本时序的上层了。下面就是我写的六个时序的代码部分,可以结合着我画的图来理解,能很快的应用起来,刚开始的小伙伴估计不太会看这个时序图,我也是刚刚才有所感觉的,画的不好,理解的不够透彻,还望多多指正啊!
下面是MyIC.c文件
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#include "Delay.h"
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIO_POART, MyI2C_SCL, (BitAction)BitValue);
Delay_us(10);
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIO_POART, MyI2C_SDA, (BitAction)BitValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIO_POART, MyI2C_SDA);
Delay_us(10);
return BitValue;
}
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(GPIO_CLOCK, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Pin = MyI2C_SCL | MyI2C_SDA;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_POART, &GPIO_InitStruct);
// MyI2C_W_SCL(1);
// MyI2C_W_SDA(1);
GPIO_SetBits(GPIO_POART, MyI2C_SCL | MyI2C_SDA);
}
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
void MyI2C_Send_Byte(uint8_t Byte)
{
uint8_t i;
for( i = 0; i < 8; i ++)
{
MyI2C_W_SDA(Byte & (0x80 >> i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
uint8_t MyI2C_Receive_Byte(void)
{
uint8_t i, Byte = 0x00;
MyI2C_W_SDA(1);
for( i = 0; i < 8; i ++)
{
MyI2C_W_SCL(1);
if(MyI2C_R_SDA() == 1) { Byte |= (0x80 >> i);}
MyI2C_W_SCL(0);
}
return Byte;
}
void MyI2C_Send_Ack(uint8_t BitAck)
{
MyI2C_W_SDA(BitAck);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
uint8_t MyI2C_Receive_Ack(void)
{
uint8_t BitAck;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
BitAck = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return BitAck;
}
下面是MyIC.h文件
#ifndef __MYI2C_H
#define __MYI2C_H
// 需要更改端口的话只需要在这4个宏定义中更改就行了,其他的地方不用改
#define GPIO_CLOCK RCC_APB2Periph_GPIOB
#define GPIO_POART GPIOB
#define MyI2C_SCL GPIO_Pin_10
#define MyI2C_SDA GPIO_Pin_11
void MyI2C_Init(void); //我的I2C初始化
void MyI2C_Start(void); //我的I2C开始时序
void MyI2C_Stop(void); //我的I2C结束时序
void MyI2C_Send_Byte(uint8_t Byte); //我的I2C发送一个字节
uint8_t MyI2C_Receive_Byte(void); //我的I2C接收一个字节
void MyI2C_Send_Ack(uint8_t BitAck); // 我的I2C发送应答位
uint8_t MyI2C_Receive_Ack(void); //我的I2C接收应答位
#endif
下面是main.c文件,测试基础的I2C代码的时序都有没有问题
回复显示0代表有这个地址,回复显示1代表没有这个地址
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "MyI2C.h"
int main(void)
{
OLED_Init(); //oled 屏幕初始化
MyI2C_Init(); //我的I2C初始化
MyI2C_Start(); //我的I2C开始时序
MyI2C_Send_Byte(0xD0); //我的I2C发送地址 这里要是有个遍历就可以把所有的地址都发送一遍测试都有哪个地址
uint8_t Ack = MyI2C_Receive_Ack(); //我的I2C接收应答位赋值给Ack
MyI2C_Stop(); //我的I2C停止时序
OLED_ShowNum(1, 1, Ack, 3); //OLED显示应答结果
while(1)
{
}
}