3 .IIC协议的通信过程( 此处重点 )
既然IIC是双向通信的,那主机肯定也是需要从外设读取数据的,那这个读取的过程又是怎么实现的呢?毕竟外设对于我们而言是不能直接操作的,我们能操作的只有stm32。我们知道,一个IIC总线上,可以挂载多个设备,那么stm32如何确定是哪个外设正在跟我进行通信呢。对于此,那些生产外设模块的厂商们就约定,要是这个设备使用IIC协议进行通信,那么就要给这个设备指定一个器件地址,以供芯片访问。这个器件地址会在你购买其模块的时候在使用手册上注明。所以,要跟哪个模块通信,就一定要通过查阅其使用手册,找到它的器件地址。
所以,在上文所述的最开始的一个字节的数据传输过程中,这一个数据往往是器件地址。这样,对应的外设才知道,是要跟我进行通信。读取数据,也是同理,要想从外设中读取到数据,主机要明确三点:从哪个外设中的哪个地方读取数据,读取到的数据要存到哪里。
所以主机,在开始读数据之前,主机必须要先给外设发器件地址,数据所在的地址,外设才会知道你要从该地址读取数据,从而把数据通过SDA线传出来。至于具体的每个字节的传输过程,和上面所讲的从主机到外设的过程差不多,只不过反了一个反向而已,并且主机的等待应答变成了主动应答。
以下是IIC协议代码
/* 设置SDA总线为输出模式 参数值:NULL 返回值:NULL */ void IIC_setSDAMode_Out() {
GPIO_InitTypeDef GPIO_IIC; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); GPIO_IIC.GPIO_Mode = GPIO_Mode_OUT; //输出 GPIO_IIC.GPIO_OType = GPIO_OType_PP; //推挽 GPIO_IIC.GPIO_Pin = GPIO_Pin_15; //引脚 GPIO_IIC.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_IIC.GPIO_Speed = GPIO_Speed_25MHz; //输出 GPIO_Init(GPIOE, &GPIO_IIC); } /* 设置SDA总线为输入模式 参数值:NULL 返回值:NULL */ void IIC_setSDAMode_In() {
GPIO_InitTypeDef GPIO_IIC; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); GPIO_IIC.GPIO_Mode = GPIO_Mode_IN; //输出 GPIO_IIC.GPIO_Pin = GPIO_Pin_15; //引脚 GPIO_IIC.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOE, &GPIO_IIC); } /* IIC开始信号 参数值:NULL 返回值:NULL */ void IIC_Start() {
IIC_setSDAMode_Out(); IIC_SDA_OUT(1); //总线释放状态 IIC_SCL_OUT(1); delay_us(5); IIC_SDA_OUT(0); //SDA跳变为低电平 delay_us(5); IIC_SCL_OUT(0); delay_us(5); } /* IIC停止信号 参数值:NULL 返回值:NULL */ void IIC_Stop() {
IIC_setSDAMode_Out(); IIC_SDA_OUT(0); IIC_SCL_OUT(0); delay_us(5); IIC_SCL_OUT(1); //SDA跳变为高电平 delay_us(5); IIC_SDA_OUT(1); delay_us(5); } /* 主机写入数据到外设中 参数值: data 要写入的一个字节 返回值:NULL */ void IIC_writeByte(u8 data) {
IIC_setSDAMode_Out(); IIC_SCL_OUT(0); //只有时钟线拉低,SDA上的数据才允许写入 delay_us(5); //将数据一位一位的发出去 for(int i =0;i<8;i++) {
if(data&(0x1<<(7-i))) //高位先入 {
IIC_SDA_OUT(1); } else {
IIC_SDA_OUT(0); } IIC_SCL_OUT(1); //让外设读取数据 delay_us(5); IIC_SCL_OUT(0); //重新拉低,准备写入下一位数据 delay_us(5); } } /* 主机从外设中读取一个字节的数据 参数值:NULL 返回值:NULL */ u8 IIC_readByte() {
u8 data = 0; IIC_setSDAMode_In(); IIC_SCL_OUT(0); //先拉低,为读取数据做准备 delay_us(5); for(int i=0;i<8;i++) {
IIC_SCL_OUT(1); // SCL为高期间才可以读取数据 delay_us(5); if(IIC_SDA_IN) {
data|=(0x01<<(7-i)); }else{
data &= ~(0x1<<(7-i)); } IIC_SCL_OUT(0); delay_us(5); } return data; } /* 主机等待应答 参数值:NULL 返回值:ack 0 应答 1 不应答 */ u8 IIC_waitAck() {
u8 ack =0; IIC_setSDAMode_In(); IIC_SCL_OUT(0); //准备时序 delay_us(5); IIC_SCL_OUT(1); delay_us(5); if(IIC_SDA_IN) {
ack =1; } else {
ack =0; } IIC_SCL_OUT(0); //拉低,表示应答完成 delay_us(5); return ack; } /* 主机主动应答 参数值: ack 0 应答 1 不应答 返回值:NULL */ void IIC_Ack(u8 ack) {
IIC_setSDAMode_Out(); IIC_SCL_OUT(0); delay_us(5); if(ack) {
IIC_SDA_OUT(1); } else {
IIC_SCL_OUT(0); } IIC_SCL_OUT(1); delay_us(5); IIC_SCL_OUT(0); delay_us(5); }
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/201298.html原文链接:https://javaforall.net
