iic学习笔记

发布时间 2023-12-24 16:35:04作者: 流光溢彩_lxy

一、简介

由飞利浦主导开发的片间互联协议。iic通信使用三线(sda scl以及gnd,不包括电源线),极大程度上减少了对ic的io口的占用。同时iic支持多主机以及多从机,方便了程序的设计。

二、协议层简介

在iic总线上scl的电平决定了整条iic总线的有效性。
当scl出于高电平时,主机与从机可以侦测sda数据的变化,sda上的数据有效。当scl出于低电平时sda线无效,目标准备读取下一位数据。
在iic总线上,一共有四种状态:起始,0/应答,1/非应答,结束 在scl线处于高电平的状态下,这四种状态对应的sda的状态分别是: 起始: sda产生下降沿 0/ack: sda处于低电平 1/nack:sda处于高电平 结束: sda产生上升沿 同时,在iic总线启动后以及一轮通信结束后,需要将scl置1,便于其他设备快速发起以及响应iic请求。而在通信中,scl要在读取前与读取后及时置0,便于目标设备读取下一位数据。
image image
iic帧格式
首先是起始信号,通知各设备开始传输。随后是1byte的目标设备的地址(准确的说,是7位地址加上一个读写控制位)。然后由目标设备给出ack。在主机收到ack后,可进行对应数据的读取。在数据传输部分,根据iic协议规定,每次只允许连续传输1byte,也即随后需要有一方及时给出ack,否则将超时终止传输(包括nack)。所有数据传输完毕后,主机设置结束信号。
在一次通信中,如果需要转变读写状态,需要主机重新发送起始信号、目标地址与读写状态。
image

三、参考实现:

基于stm32f103c6t6 HAL编写
iic.c

#include "iic.h"

//private functions 
void sda(uint8_t sta) {
    HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_Pin, sta ? GPIO_PIN_SET : GPIO_PIN_RESET);
    for (int i = 0;i < 100;i++);//delay

}

void scl(uint8_t sta) {
    HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, sta ? GPIO_PIN_SET : GPIO_PIN_RESET);
    for (int i = 0;i < 100;i++);
}

void signal(uint8_t sig) {
    sda(sig);
    scl(0);
    scl(1);
    scl(0);
}


//public functions
HAL_StatusTypeDef iic_st(void) {
    sda(1);//startup
    scl(1);
    sda(0);
    scl(0);
    return HAL_OK;
}

HAL_StatusTypeDef iic_ed(void) {
    scl(0);//end
    sda(0);
    scl(1);
    sda(1);
    return HAL_OK;
}

HAL_StatusTypeDef iic_tx(uint8_t data) {


    for (int i = 0;i < 8;i++) {
        signal(data >> 7);
        data <<= 1;
    }


    return HAL_OK;
}

HAL_StatusTypeDef iic_rx(GPIO_PinState* pdata) {
    uint8_t pin_sta = 0, data = 0;
    
    data = 0;
    scl(0);
    sda(1);
    
    for (int i = 7;i >= 0;i--) {
        scl(1);
        pin_sta = HAL_GPIO_ReadPin(SDA_GPIO_Port, SDA_Pin);
        data += pin_sta<<i;
        scl(0);
    }
    *pdata = data;

    return HAL_OK;
}

HAL_StatusTypeDef iic_wait_ack(void) {
    uint8_t t = 0xff;
    
    scl(0);
    sda(1);
    scl(1);
    while (t--) {
        if (HAL_GPIO_ReadPin(SDA_GPIO_Port, SDA_Pin) == GPIO_PIN_RESET) {
            scl(0);
            return HAL_OK;
        }
    }
    iic_ed();
    return HAL_TIMEOUT;
}

HAL_StatusTypeDef iic_send_ack(uint8_t ack) {
    signal(ack);
    return HAL_OK;
}

 

iic.h

#ifndef __IIC_H__
#define __IIC_H__

#include "stm32f1xx_hal.h"

#define SDA_Pin GPIO_PIN_8
#define SDA_GPIO_Port GPIOB
#define SCL_Pin GPIO_PIN_9
#define SCL_GPIO_Port GPIOB

HAL_StatusTypeDef iic_st(void);
HAL_StatusTypeDef iic_ed(void);
HAL_StatusTypeDef iic_tx(uint8_t data);
HAL_StatusTypeDef iic_rx(uint8_t* data);
HAL_StatusTypeDef iic_wait_ack(void);
HAL_StatusTypeDef iic_send_ack(uint8_t ack) ;

#endif