STM32——IIC篇

技术笔记!

一、IIC总线协议介绍(掌握)

1.1  IIC总线结构图

1.2  IIC协议时序

1.3  硬件和软件IIC对比

二、AT24C02介绍(了解)

2.1  AT24C02通讯地址

三、AT24C02读写时序(掌握)

3.1  写时序

3.2  读时序

四、AT24C02驱动步骤(掌握)

为什么IIC总线SDA建议用开漏模式?

五、编程实战(掌握)

iic.c

#include "./BSP/IIC/myiic.h"
#include "./SYSTEM/delay/delay.h"

void iic_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    IIC_SCL_GPIO_CLK_ENABLE();  /* SCL引脚时钟使能 */
    IIC_SDA_GPIO_CLK_ENABLE();  /* SDA引脚时钟使能 */

    gpio_init_struct.Pin = IIC_SCL_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
    HAL_GPIO_Init(IIC_SCL_GPIO_PORT, &gpio_init_struct);    /* SCL */

    gpio_init_struct.Pin = IIC_SDA_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;            /* 开漏输出 */
    HAL_GPIO_Init(IIC_SDA_GPIO_PORT, &gpio_init_struct);    /* SDA */
    /* SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */
}

static void iic_delay(void)
{
    delay_us(2);
}

/* 起始信号 */
void iic_start(void)
{
    /* SCL为高电平期间, SDA从高电平往低电平跳变*/
    IIC_SDA ( 1 );
    IIC_SCL ( 1 );
    iic_delay( );
    IIC_SDA ( 0 );
    iic_delay( );
    IIC_SCL ( 0 );
    iic_delay( );  /* 钳住总线, 准备发送/接收数据 */
}

/* 停止信号 */
void iic_stop(void)
{
    /* SCL为高电平期间, SDA从低电平往高电平跳变*/
    IIC_SDA ( 0 );
    iic_delay( );
    IIC_SCL ( 1 );
    iic_delay( );
    IIC_SDA ( 1 );  /* 发送总线停止信号*/
    iic_delay( );
}

/* 等待应答信号 */
uint8_t iic_wait_ack (void) /* return 1:fail 0:succeed*/
{
    IIC_SDA (1);    /* 主机释放SDA线 */
    iic_delay( );
    IIC_SCL (1);    /* 从机返回ACK*/
    iic_delay( );
    if ( IIC_READ_SDA ) /* SCL高电平读取SDA状态*/ 
    {
        iic_stop();     /* SDA高电平表示从机nack */ 
        return 1;
    }
    IIC_SCL(0);         /* SCL低电平表示结束ACK检查 */ 
    iic_delay( );
    return 0;
}

/* 应答信号 */
void iic_ack(void)
{ 
    IIC_SCL (0);
    iic_delay( );
    IIC_SDA (0);  /* 数据线为低电平,表示应答 */
    iic_delay( );
    IIC_SCL (1);
    iic_delay( );
}

/* 非应答信号 */
void iic_nack(void)
{ 
    IIC_SCL (0);
    iic_delay( );
    IIC_SDA (1);  /* 数据线为低电平,表示应答 */
    iic_delay( );
    IIC_SCL (1);
    iic_delay( );
}

/* 发送一个字节数据 */
void iic_send_byte(uint8_t data)
{
    for (uint8_t t = 0; t < 8; t++)
    {
        /* 高位先发 */
        IIC_SDA((data & 0x80) >> 7);
        iic_delay( );
        IIC_SCL ( 1 );
        iic_delay( );
        IIC_SCL ( 0 );
        data <<= 1;     /* 左移1位, 用于下一次发送 */
    }
    IIC_SDA ( 1 );      /* 发送完成,主机释放SDA线 */ 
}

/* 读取1字节数据 */
uint8_t iic_read_byte (uint8_t ack)
{ 
    uint8_t receive = 0 ;
    for (uint8_t t = 0; t < 8; t++)
    {
        /* 高位先输出,先收到的数据位要左移 */ 
        receive <<= 1;
        IIC_SCL ( 1 );
        iic_delay( );
        if ( IIC_READ_SDA ) receive++;
        IIC_SCL ( 0 );
        iic_delay( );
    }
    if ( !ack ) iic_nack();
    else iic_nack();
    return receive;
}

24cxx.c

#include "./BSP/IIC/myiic.h"
#include "./BSP/24CXX/24cxx.h"
#include "./SYSTEM/delay/delay.h"

void at24c02_init(void)
{
    iic_init();
}

void at24c02_write_one_byte(uint8_t addr, uint8_t data)
{
    /* 1、发送起始信号 */
    iic_start();
    
    /* 2、发送通讯地址(写操作地址) */
    iic_send_byte(0xA0);
    
    /* 3、等待应答信号 */
    iic_wait_ack();
    
    /* 4、发送内存地址 */
    iic_send_byte(addr);
    
    /* 5、等待应答信号 */
    iic_wait_ack();
    
    /* 6、发送写入数据 */
    iic_send_byte(data);
    
    /* 7、等待应答信号 */
    iic_wait_ack();
    
    /* 8、发送停止信号 */
    iic_stop();
    
    /* 等待EEPROM写入完成 */
    delay_ms(10);
}

uint8_t at24c02_read_one_byte(uint8_t addr)
{
    uint8_t rec = 0;
    
    /* 1、发送起始信号 */
    iic_start();
    
    /* 2、发送通讯地址(写操作地址) */
    iic_send_byte(0xA0);
    
    /* 3、等待应答信号 */
    iic_wait_ack();
    
    /* 4、发送内存地址 */
    iic_send_byte(addr);
    
    /* 5、等待应答信号 */
    iic_wait_ack();
    
    /* 6、发送起始信号 */
    iic_start();
    
    /* 7、发送通讯地址(读操作地址) */
    iic_send_byte(0xA1);
    
    /* 8、等待应答信号 */
    iic_wait_ack();
    
    /* 9、等待接收数据 */
    rec = iic_read_byte(0);
    /* 10、发送非应答(获取该地址即可) */
    
    /* 11、发送停止信号 */
    iic_stop();
    
    return rec;
}

main.c

int main(void)
{
    uint8_t key;
    uint8_t i = 0;
    uint8_t data = 0;
    
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    key_init();                                 /* 初始化按键 */
    at24c02_init();
    
    while (1)
    {
        key = key_scan(0);

        if (key == KEY1_PRES)
        {
            at24c02_write_one_byte(100, 66);
            printf("write data \r\n");
        }

        if (key == KEY0_PRES)
        {
            data = at24c02_read_one_byte(100);
            printf("read data:%d \r\n", data);
        }

        i++;

        if (i % 20 == 0)
        {
            LED0_TOGGLE();  /* 红灯闪烁 */
            i = 0;
        }

        delay_ms(10);
    }
}

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于STM32模拟IIC,下面是一份示例代码,你可以参考一下。 首先,需要在STM32的CubeMX中配置IIC引脚,选择PB6和PB7作为SCL和SDA引脚。 接下来,打开CubeMX的“Pinout”选项卡,在左侧的“Categories”中选择“I2C1”,将PB6和PB7分别设置为SCL和SDA引脚。然后点击“Generated Code”生成代码。 在生成的代码中,你需要找到以下部分: ``` /* I2C1 GPIO Configuration PB6 ------> I2C1_SCL PB7 ------> I2C1_SDA */ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); ``` 这是IIC引脚的配置代码,确保你的引脚与这里的配置相同。 接下来,是IIC初始化和读写数据的代码: ``` I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } } void I2C_WriteData(uint8_t addr, uint8_t reg, uint8_t data) { HAL_I2C_Mem_Write(&hi2c1, addr << 1, reg, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000); } void I2C_ReadData(uint8_t addr, uint8_t reg, uint8_t* data, uint8_t len) { HAL_I2C_Mem_Read(&hi2c1, addr << 1, reg, I2C_MEMADD_SIZE_8BIT, data, len, 1000); } ``` 其中,MX_I2C1_Init()函数用于初始化IIC,I2C_WriteData()函数用于向设备写入数据,I2C_ReadData()函数用于从设备读取数据。 使用示例: ``` uint8_t data = 0x12; I2C_WriteData(0x50, 0x20, data); //向地址为0x50的设备的0x20寄存器写入0x12 uint8_t readData[2] = {0}; I2C_ReadData(0x50, 0x20, readData, 2); //从地址为0x50的设备的0x20寄存器读取2个字节的数据 ``` 你可以根据你的具体需求修改代码中的地址、寄存器和数据等参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值