STM8S自学笔记-008 模拟I2C

I²C

I²C(Inter-Integrated Circuit)字面上的意思是集成电路之间,它其实是I²C Bus简称,所以中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。——维基百科

设计概要

【背景】STM8运行在16MHz频率上。
本设计使用GPIO模拟方式来设计I2C通信的底层,包括:
①GPIO初始化
②GPIO输出设置
③GPIO输入读取
④生成起始信号
⑤生成结束信号
⑥发送字节
⑦读取字节
注1:④-⑦中的延时设计采用软件延时方式
注2:从⑥和⑦估算,峰值通信速度是50kbps

底层代码

GPIO初始化

开发板上,PB4和PB5端口具有真开漏功能(因为这两个端口刚好是I2C外设的默认端口)。
将PB4和PB5初始化为开漏模式、高阻抗、快速模式。
开漏模式:开漏模式下,端口输出有线与特性;只有在开漏模式下,端口既可以当输入端口、也可以当输出端口。
高阻抗:相当于端口输出H
快速模式:-

#define I2C_SCL_GPIO_PORT       GPIOB
#define I2C_SCL_GPIO_PIN        GPIO_PIN_4
#define I2C_SDA_GPIO_PORT       GPIOB
#define I2C_SDA_GPIO_PIN        GPIO_PIN_5
#define I2C_SCL_INIT_STATE      GPIO_MODE_OUT_OD_HIZ_FAST   //GPIO_MODE_OUT_OD_HIZ_FAST or GPIO_MODE_OUT_OD_HIZ_SLOW
#define I2C_SDA_INIT_STATE      GPIO_MODE_OUT_OD_HIZ_FAST

/**
  * @brief  I2C GPIO Init API
  * @param  None
  * @retval None
  */
void I2C_HAL_Init(void)
{
    GPIO_Init( I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PIN, I2C_SCL_INIT_STATE);
    GPIO_Init( I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN, I2C_SDA_INIT_STATE);
}

GPIO输出设置,GPIO输入读取

/**
  * @brief  I2C GPIO Output API
  * @param  pin_id : the specific pin to write.
  * @param  bit_status : the output value to apply. Bit_SET or Bit_RESET
  * @retval None
  */
void I2C_HAL_PinWrite(uint8_t pin_id, uint8_t bit_status)
{
    switch(pin_id)
    {
    case I2C_PIN_SCL:
        if( bit_status != Bit_RESET){
            GPIO_WriteHigh( I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PIN);
        }
        else{
            GPIO_WriteLow(  I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PIN);
        }
        break;
        
    case I2C_PIN_SDA:
        if( bit_status != Bit_RESET){
            GPIO_WriteHigh( I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN);
        }
        else{
            GPIO_WriteLow( I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN);
        }
        break;
        
    default:
        break;
    }
}

/**
  * @brief  I2C GPIO Read API
  * @param  pin_id : the specific pin to read.
  * @retval the input value. Bit_SET or Bit_RESET.
  */
uint8_t I2C_HAL_PinRead(uint8_t pin_id)
{
    uint8_t bit_status = 0;
    
    switch( pin_id){
    case I2C_PIN_SCL:
        bit_status = !!GPIO_ReadInputPin( I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PIN);
        break;
        
    case I2C_PIN_SDA:
        bit_status = !!GPIO_ReadInputPin( I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN);
        break;
        
    default:
        break;
    }
    
    return bit_status;
}

生成起始信号,生成结束信号

/**
  * @brief  Generates I2C communication START condition.
  * @param  None
  * @retval None
  */
void I2C_HAL_GenerateStart(void)
{
    I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_SET);
    I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_SET);
    DELAY_APL_Delayus(5);
    I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_RESET);
    DELAY_APL_Delayus(5);
    I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_RESET);
    DELAY_APL_Delayus(5);
}

/**
  * @brief  Generates I2C communication STOP condition.
  * @param  None
  * @retval None
  */
void I2C_HAL_GenerateStop(void)
{
    I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_RESET);
//    I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_RESET);
    DELAY_APL_Delayus(5);
    I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_SET);
    DELAY_APL_Delayus(5);
    I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_SET);
    DELAY_APL_Delayus(5);
}

发送字节

主节点发送每一个字节后,需要限时检查从节点是否接受到了数据。如果从节点接受到了数据,函数才返回ACK;如果返回了NACK,则说明通信失败,继续通信下去已经没有意义。
在/* Check slave’s ACK */步骤,使用了开漏模式的线与特性。

/**
  * @brief  Send byte to slave.
  * @param  data: the byte to send
  * @retval Slave's ACK or NACK
  */
uint8_t I2C_HAL_SendByte(uint8_t data)
{
    uint8_t bit_mask;
    uint32_t time_out = 200;
    uint8_t return_val = I2C_NACK;      /* Init to Slave receive failure */
    
    /* Send byte */
    for( bit_mask = 0x80; bit_mask != 0; bit_mask >>= 1){
        if( !!(bit_mask&data)){
            I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_SET);
        }
        else{
            I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_RESET);
        }
        DELAY_APL_Delayus(5);
        I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_SET);
        DELAY_APL_Delayus(10);
        I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_RESET);
        DELAY_APL_Delayus(5);
    }
    /* Release SDA Line */
    I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_SET);     
    /* Check slave's ACK */
    DELAY_APL_Delayus(5);
    I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_SET);
    while( time_out --){
        if( I2C_HAL_PinRead(I2C_PIN_SDA) == Bit_RESET){
            return_val = I2C_ACK;       /* Get Slave receive Success */
            break;
        }
    }
    I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_RESET);
    DELAY_APL_Delayus(5);
    
    return return_val;
}

读取字节

从从节点接收每一个字节后,主节点必须告诉从节点是否继续接受下一个字节,即ack_status。
当ack_status是ACK时,表示“继续下一个字节的接收”;当ack_status是NACK时,表示“接收结束”。

/**
  * @brief  Receive byte from slave API.
  * @param  data: the pointor to the received data
  * @param  ack_status: Master's ACN or NACK
  * @retval None
  */
void I2C_HAL_ReadByte(uint8_t* data, uint8_t ack_status)
{
    uint8_t bit_mask;
    uint8_t data_temp = 0;
    
    I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_SET);//Release SDA Line
    /* Byte Receive */
    for( bit_mask = 0x80; bit_mask != 0; bit_mask >>= 1)
    {
        DELAY_APL_Delayus(5);
        I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_SET);
        DELAY_APL_Delayus(10);
        if( I2C_HAL_PinRead(I2C_PIN_SDA) == Bit_SET)
        {
             data_temp |= bit_mask;
        }
        I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_RESET);
        DELAY_APL_Delayus(5);
    }
    *data = data_temp;
    /* ACK/NACK Send */
    if( ack_status == I2C_ACK)
    {
        I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_RESET);
    }
    else //if( ack_status == I2C_NACK)
    {
        I2C_HAL_PinWrite(I2C_PIN_SDA, Bit_SET);
    }
    DELAY_APL_Delayus(5);
    I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_SET);
    DELAY_APL_Delayus(10);
    I2C_HAL_PinWrite(I2C_PIN_SCL, Bit_RESET);
    DELAY_APL_Delayus(5);
}
  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值