问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

STM32 HAL库F103系列之IIC实验

创作时间:
作者:
@小白创作中心

STM32 HAL库F103系列之IIC实验

引用
CSDN
1.
https://blog.csdn.net/2302_79878697/article/details/138253223

本文详细介绍了如何使用STM32 HAL库实现IIC通信,并通过AT24C02 EEPROM存储器的读写实验进行验证。文章从IIC总线协议的基础知识开始,逐步讲解了AT24C02的使用方法和具体的代码实现,适合有一定基础的开发者学习参考。

IIC总线协议

IIC总线协议介绍

IIC(Inter Integrated Circuit)是一种同步串行半双工通信总线。其主要特点如下:

  • 总线结构:由时钟线SCL和数据线SDA组成,都接有上拉电阻,确保总线空闲状态为高电平。
  • 多设备支持:支持多设备连接,允许多主机存在,每个设备都有一个唯一的地址。
  • 电容限制:连接到总线上的设备数量受总线的最大电容400pf限制。
  • 传输速率:支持三种传输速率:标准模式100k bit/s、快速模式400k bit/s、高速模式3.4Mbit/s。

IIC基本信号实现

起始信号

void iic_start(void)
{
    IIC_SDA(1);
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(0);
    iic_delay();
    IIC_SCL(0);
    iic_delay();
}

停止信号

void iic_stop(void)
{
    IIC_SDA(0);
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(1);
    iic_delay();
}

检测应答信号

uint8_t iic_wait_ack(void)
{
    IIC_SDA(1);
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    if (IIC_READ_SDA)
    {
        iic_stop();
        return 1;
    }
    IIC_SCL(0);
    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();
}

发送1字节数据

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;
    }
    IIC_SDA(1);
}

读取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_ack();
    return receive;
}

IIC配置步骤

  1. 使能SCL和SDA对应时钟:__HAL_RCC_GPIOB_CLK_ENABLE()
  2. 设置GPIO工作模式:SDA开漏/SCL推挽输出模式,使用HAL_GPIO_Init初始化
  3. 编写基本信号:起始信号、停止信号、应答信号
  4. 编写读和写函数:iic_read_byteiic_send_byte

开漏输出模式的必要性

开漏输出模式的特点是不能输出高电平,必须有外部(或内部)上拉才能输出高电平。这种模式非常适合IIC总线的SDA线,因为:

  • 输出时:主机(MCU)输出0可以拉低信号,实现低电平发送;输出1(实际不起作用)由外部上拉电阻上拉,实现高电平发送。
  • 输入时:主机(MCU)设置输出1状态,相当于释放SDA脚,外部器件可以主动控制SDA脚的高低电平。
  • MCU可以通过读取IDR状态寄存器来检测SDA脚的高低电平状态。

AT24C02 EEPROM存储器

原理图

EEPROM是一种掉电后数据不丢失的存储器,常用于存储配置信息。AT24C02是一个2K bit的EEPROM存储器,使用IIC通信方式。其写操作地址为0xA0,读操作地址为0xA1。

AT24C02读写时序

AT24C02配置步骤

  1. 初始化IIC接口
  2. 编写写入/读取一个字节数据函数,遵循读写时序流程编写
  3. 编写连续读和连续写函数,在2的基础上进行实现

实验:驱动AT24C02实现读和写1字节数据

myiic.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();
    IIC_SDA_GPIO_CLK_ENABLE();
    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);
    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);
}

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

void iic_start(void)
{
    IIC_SDA(1);
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(0);
    iic_delay();
    IIC_SCL(0);
    iic_delay();
}

void iic_stop(void)
{
    IIC_SDA(0);
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(1);
    iic_delay();
}

uint8_t iic_wait_ack(void)
{
    IIC_SDA(1);
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    if (IIC_READ_SDA)
    {
        iic_stop();
        return 1;
    }
    IIC_SCL(0);
    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;
    }
    IIC_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_ack();
    return receive;
}

myiic.h

#ifndef __MYIIC_H
#define __MYIIC_H
#include "./SYSTEM/sys/sys.h"

#define IIC_SCL_GPIO_PORT GPIOB
#define IIC_SCL_GPIO_PIN GPIO_PIN_6
#define IIC_SCL_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)
#define IIC_SDA_GPIO_PORT GPIOB
#define IIC_SDA_GPIO_PIN GPIO_PIN_7
#define IIC_SDA_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)

#define IIC_SCL(x) do{ x ? HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_SET) : HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_RESET); }while(0)
#define IIC_SDA(x) do{ x ? HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_SET) : HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_RESET); }while(0)
#define IIC_READ_SDA HAL_GPIO_ReadPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN)

void iic_init(void);
void iic_start(void);
void iic_stop(void);
uint8_t iic_wait_ack(void);
void iic_ack(void);
void iic_nack(void);
void iic_send_byte(uint8_t data);
uint8_t iic_read_byte(uint8_t ack);
#endif

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)
{
    iic_start();
    iic_send_byte(0xA0);
    iic_wait_ack();
    iic_send_byte(addr);
    iic_wait_ack();
    iic_send_byte(data);
    iic_wait_ack();
    iic_stop();
    delay_ms(10);
}

uint8_t at24c02_read_one_byte(uint8_t addr)
{
    uint8_t rec = 0;
    iic_start();
    iic_send_byte(0xA0);
    iic_wait_ack();
    iic_send_byte(addr);
    iic_wait_ack();
    iic_start();
    iic_send_byte(0xA1);
    iic_wait_ack();
    rec = iic_read_byte(0);
    iic_stop();
    return rec;
}

24cxx.h

#ifndef __24CXX_H
#define __24CXX_H
#include "./SYSTEM/sys/sys.h"

void at24c02_init(void);
void at24c02_write_one_byte(uint8_t addr, uint8_t data);
uint8_t at24c02_read_one_byte(uint8_t addr);
#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/24CXX/24cxx.h"

int main(void)
{
    uint8_t key;
    uint8_t i = 0;
    uint8_t data = 0;

    HAL_Init();
    sys_stm32_clock_init(RCC_PLL_MUL9);
    delay_init(72);
    usart_init(115200);
    led_init();
    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);
    }
}
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号