STM32Cube MX 下IIC的配置与使用--GPIO模拟

本文介绍了在STM32下的IIC的基本使用方法,通过对板载具备IIC接口EEPROM的读写,完成对IIC驱动程序的测试。

硬件平台:STM32F107VCT6开发板

软件平台:STM32Cube MX + MDK5.22


1. 进行STM32Cube MX的配置

STM32Cube MX 下IIC的配置与使用--GPIO模拟_第1张图片

配置PB6和PB7为输出模式,同时配置了USART1进行串口调试使用。然后生成工程。


2. 打开工程,可以看到GPIO的初始化状态

STM32Cube MX 下IIC的配置与使用--GPIO模拟_第2张图片


3. 模拟IIC驱动程序源文件代码

/**
  * @file  iic_dup.c
  * @brief IIC上层程序
  * @par   date        version    author    remarks
  *        2016-03-21  v1.0       zbt       初次创建
  *
  */

/** 头文件包含区 ------------------------------------------------ */
#include "iic_dup.h"

/** 私有宏(类型定义) -------------------------------------------- */ 
#define IIC1_SCL(pin_status)        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, pin_status);
#define IIC1_SDA(pin_status)        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, pin_status);
#define IIC1_SCL_IS_HIGH()          (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) != GPIO_PIN_RESET)
#define IIC1_SDA_IS_HIGH()          (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) != GPIO_PIN_RESET)

/** 私有变量 --------------------------------------------------- */


/** 外部变量 --------------------------------------------------- */

/** 私有函数原型 ----------------------------------------------- */
static void iic_delay(void);

/** 公有函数 --------------------------------------------------- */
/**
  * @brief  IIC启动
  * @param  None
  * @retval None
  * @note   当SCL处于高电平状态时,SDA出现一个下降沿
            即产生IIC启动信号
  */
void iic_start(void)
{
    IIC1_SCL(GPIO_PIN_SET);
    /** SDA产生一个下降沿 */
    IIC1_SDA(GPIO_PIN_SET);
    iic_delay(); 
    
    IIC1_SDA(GPIO_PIN_RESET);
    iic_delay(); 
    IIC1_SCL(GPIO_PIN_RESET);   /**< 拉低准备发送数据 */
    iic_delay();   
}

/**
  * @brief  IIC停止
  * @param  None
  * @retval None
  * @note   当SCL处于高电平状态时,SDA出现一个上升沿
            即产生IIC停止信号
  */
void iic_stop(void)
{
    IIC1_SCL(GPIO_PIN_RESET);
    iic_delay();
    /** SDA产生一个上升沿 */
    IIC1_SDA(GPIO_PIN_RESET);
    iic_delay();
    
    IIC1_SCL(GPIO_PIN_SET);
    iic_delay();
    IIC1_SDA(GPIO_PIN_SET);
    iic_delay();
}

/**
  * @brief  IIC发送1byte数据
  * @param  None
  * @retval None
  * @note   
  */
void iic_sendbyte(uint8_t byte)
{
    uint8_t i;
    
    /** 发送一个字节的高7位 */
    for (i = 0; i < 8; i++)
    {
        if (byte & 0x80)
        {
            IIC1_SDA(GPIO_PIN_SET);
        }
        else
        {
            IIC1_SDA(GPIO_PIN_RESET);
        }
        
        iic_delay();
        IIC1_SCL(GPIO_PIN_SET);
        iic_delay();
        IIC1_SCL(GPIO_PIN_RESET);
        
        if (i == 7)
        {
            IIC1_SDA(GPIO_PIN_SET);
        }
        
        byte <<= 1;
        iic_delay();
    }      
}

/**
  * @brief  IIC读取1byte数据
  * @param  None
  * @retval None
  * @note             
  */
uint8_t iic_readbyte(void)
{
    uint8_t i;
    uint8_t recv_value = 0;
    
    IIC1_SDA(GPIO_PIN_SET);
    iic_delay();
    
    for (i = 0; i < 8; i++)
    {
        IIC1_SCL(GPIO_PIN_SET);
        iic_delay();
        recv_value <<= 1;
        if (IIC1_SDA_IS_HIGH())
        {
            recv_value |= 0x01;
        }
        else
        {
            recv_value &= ~0x01;
        }
        
        iic_delay();
        IIC1_SCL(GPIO_PIN_RESET);
    }
    
    return recv_value;
}

/**
  * @brief  IIC等待应答信号
  * @param  None
  * @retval ack_status: 应答状态,0表示应答,1表示设备无响应
  */
uint8_t iic_wait_ack(void)
{
    uint8_t ack_status = 0;
    
    /** 在等待应答信号之前,要释放总线,即将SDA置位 */
    IIC1_SDA(GPIO_PIN_SET);
    iic_delay();
    IIC1_SCL(GPIO_PIN_SET);
    iic_delay();
    
    if (IIC1_SDA_IS_HIGH())
    {    
        ack_status = 1;
        iic_stop();
    }
    else
    {
        ack_status = 0;
    }
    
    IIC1_SCL(GPIO_PIN_RESET);
    iic_delay();
    
    return ack_status;
}

/**
  * @brief  主机(主控制器)产生应答信号
  * @param  None
  * @retval None
  */
void iic_ack(void)
{
    IIC1_SDA(GPIO_PIN_RESET);
    iic_delay();
    
    IIC1_SCL(GPIO_PIN_SET);
    iic_delay();
    IIC1_SCL(GPIO_PIN_RESET);
    iic_delay();
    
    IIC1_SDA(GPIO_PIN_SET);
}

/**
  * @brief  主机(主控制器)产生不应答信号
  * @param  None
  * @retval None
  */
void iic_nack(void)
{
    IIC1_SDA(GPIO_PIN_SET);
    iic_delay();
    
    IIC1_SCL(GPIO_PIN_SET);
    iic_delay();
    IIC1_SCL(GPIO_PIN_RESET);
    iic_delay();
}

/**
  * @brief  检测IIC总线上的设备状态
  * @param  device_addr: 从机设备地址 
  * @retval ack_status: 0 (正常)or 1(异常)
  * @note   主机发送设备地址等待从机应答,若有从机正确的应答信号
            则表明IIC总线上挂接了设备,否则表示IIC总线上未检测到
            设备
  */
uint8_t iic_check_device_status(uint8_t device_addr)
{
    uint8_t ack_status;
    
    if (IIC1_SCL_IS_HIGH() && IIC1_SDA_IS_HIGH())
    {
        iic_start();
        
        iic_sendbyte(device_addr | IIC_WRITE);
        ack_status = iic_wait_ack();

        iic_stop();
        
        return ack_status;
    }
    
    return 1;
}

/** 私有函数 --------------------------------------------------- */
/**
  * @brief  用于模拟IIC时的简单延时
  * @param  None
  * @retval None
  */
static void iic_delay(void)
{
    uint8_t i = 0;
    uint8_t delay = 5;
    
    while (delay--)
    {
        i = 10;
        while (i--);
    }
}

4. AT24C02部分驱动代码

/**
  * @file  at24c02.c
  * @brief at24c02驱动程序
  * @par   date        version    author    remarks
  *        2016-03-21  v1.0       zbt       初次创建
  *
  */

/** 头文件包含区 ------------------------------------------------ */
#include "at24c02.h"
#include "iic_dup.h"

/** 私有宏(类型定义) -------------------------------------------- */ 
#define AT24C02_DEVICE_ADDR         0xA0
#define AT24C02_PAGE_SIZE           8
#define AT24C02_MEM_SIZE            256
#define AT24C02_ADDR_BYTE           1

/** 私有变量 --------------------------------------------------- */
uint8_t test_buffer[AT24C02_MEM_SIZE];

/** 外部变量 --------------------------------------------------- */

/** 私有函数原型 ----------------------------------------------- */
//static void AT24C02_ack(void);
static void AT24C02_error_handle(void);
static void AT24C02_read_test(void);
static void AT24C02_write_test(void);
static void AT24C02_erase_test(void);

/** 公有函数 --------------------------------------------------- */
/**
  * @brief  AT24C02与主控制器的IIC通讯测试代码
  * @param  None
  * @retval None
  */
void AT24C02_iic_test(void)
{
    iic_stop();     /**< 必须先复位IIC总线上的设备到待机模式 */
    HAL_Delay(10);
    
    /** 检测总线上是否挂接了IIC设备(此处为AT24C02) */
    if (iic_check_device_status(AT24C02_DEVICE_ADDR) == 0)
    {
        printf("iic device exists\n");
    }
    else
    {
        printf("no iic device exists\n");
    }
    
    AT24C02_write_test();
    HAL_Delay(5);
    AT24C02_read_test();
    HAL_Delay(5);
    AT24C02_erase_test();
}

/**
  * @brief  从AT24C02中读取数据
  * @param  read_data: 读取到的数据
  * @param  start_addr: 读取数据的起始地址
  * @param  data_length: 数据的长度
  * @retval None
  */
void AT24C02_read_data(uint8_t *read_data, uint16_t start_addr, uint16_t data_length)
{
    uint16_t i;
    
    iic_start();
    iic_sendbyte(AT24C02_DEVICE_ADDR | IIC_WRITE);
//    AT24C02_ack();
    if (iic_wait_ack() != 0)
    {
        AT24C02_error_handle();
        printf("first read error\r\n");
    }
    
    if (AT24C02_ADDR_BYTE == 1)
    {
        iic_sendbyte((uint8_t)start_addr & 0xff);
//        AT24C02_ack();
        if (iic_wait_ack() != 0)
        {
            AT24C02_error_handle();
            printf("addr byte error\r\n");
        }
    }
    
    iic_start();
    iic_sendbyte(AT24C02_DEVICE_ADDR | IIC_READ);
//    AT24C02_ack();
    if (iic_wait_ack() != 0)
    {
        AT24C02_error_handle();
        printf("read data error\r\n");
    }
    
    for (i = 0; i < data_length; i++)
    {
        read_data[i] = iic_readbyte();
        
        if (i != data_length - 1)
        {
            iic_ack();      /**< 读完非最后一个字节后, 产生应答信号 */
        }
        else
        {
            iic_nack();     /**< 最后一个字节读完后 产生非应答信号 */
        }
//        printf("read data is %d\n", read_data[i]);  /**< 调试代码 */
    }
    
    iic_stop();
}

/**
  * @brief  通过IIC向AT24C02写数据
  * @param  write_data:  要写入AT24C02的数据指针
  * @param  start_addr:  要写入AT24C02的起始地址
  * @param  data_length: 要写入AT24C02的数据长度
  * @retval None
  */
void AT24C02_write_data(uint8_t *write_data, uint16_t start_addr, uint16_t data_length)
{
    uint16_t i, j;
    uint16_t start_addr_dup;
    
    start_addr_dup = start_addr;
    for (i = 0; i < data_length; i++)
    {
        
        if ((i == 0) || (start_addr_dup & (AT24C02_PAGE_SIZE - 1)) == 0)
        {
            iic_stop();
            
            for (j = 0; j < 2000; j++)
            {
                iic_start();
                iic_sendbyte(AT24C02_DEVICE_ADDR | IIC_WRITE);
                
                if (iic_wait_ack() == 0)
                {
                    break;
                }
            }
            if (j >= 2000)
            {
                printf("j = 1000\r\n");
                AT24C02_error_handle();
            }
            
            if (AT24C02_ADDR_BYTE == 1)
            {
                iic_sendbyte((uint8_t)start_addr_dup & 0xff);
//                AT24C02_ack();
                if (iic_wait_ack() != 0)
                {
                    AT24C02_error_handle();
                    printf("addr_byte wrong\r\n");
                }
            }
        }
        
        iic_sendbyte(write_data[i]);
//        AT24C02_ack();
        if (iic_wait_ack() != 0)
        {
            AT24C02_error_handle();
            printf("write failed\r\n");
        }
        
//        printf("write_data is %d \n", write_data[i]);    /**< 调试代码 */ 
        start_addr_dup++;
    }
    
    iic_stop();
}

/** 私有函数 --------------------------------------------------- */
/** 以下为测试用程序 ------------------------------------------- */
/**
  * @brief  AT24C02读取数据测试
  * @param  None
  * @retval None
  */
static void AT24C02_read_test(void)
{
    uint16_t i;
    
    AT24C02_read_data(test_buffer, 0, (AT24C02_MEM_SIZE % 10));
    
    printf("read data is:\n");
    for (i = 0; i < (AT24C02_MEM_SIZE % 10); i++)
    {
        printf("%d ", test_buffer[i]);
    }
    
    printf("\r\nread test ok\r\n");
}

/**
  * @brief  AT24C02写数据测试
  * @param  None
  * @retval None
  */
static void AT24C02_write_test(void)
{
    uint16_t i;
    
    for (i = 0; i < (AT24C02_MEM_SIZE % 10); i++)
    {
        test_buffer[i] = i;
    }
    
    AT24C02_write_data(test_buffer, 0, (AT24C02_MEM_SIZE % 10));
    
    printf("write data is:\n");
    for (i = 0; i < (AT24C02_MEM_SIZE % 10); i++)
    {
        printf("%d ", test_buffer[i]);
    }
    
    printf("\r\nwrite test ok\r\n");
}

/**
  * @brief  AT24C02擦除数据测试
  * @param  None
  * @retval None
  */
static void AT24C02_erase_test(void)
{
    uint16_t i;
    
    for (i = 0; i < (AT24C02_MEM_SIZE % 10); i++)
    {
        test_buffer[i] = 0xff;
    }
    
    AT24C02_write_data(test_buffer, 0, (AT24C02_MEM_SIZE % 10));
    
    printf("erase value is: \n");
    for (i = 0; i < (AT24C02_MEM_SIZE % 10); i++)
    {
        printf("%d ", test_buffer[i]);
    }
    
    printf("\r\nerase test ok\r\n");
}
/** 以上为测试用程序 ------------------------------------------- */

/**
  * @brief  AT24C02应答错误处理程序
  * @param  None
  * @retval None
  */
static void AT24C02_error_handle(void)
{
    iic_stop();
//    printf("At24C02 read failed\r\n");   /**< 调试用 */
}

//static void AT24C02_ack(void)
//{
//    if (iic_wait_ack() != 0)
//    {
//        AT24C02_error_handle();
//    }
//}



5. 在主函数中添加 AT24C02_iic_test(); 进行读写测试成功。

你可能感兴趣的:(STM32,STM32,IIC)