×
单片机 > 单片机程序设计 > 详情

STM32Cube MX 下IIC的配置与使用

发布时间:2020-06-06 发布时间:
|

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


硬件平台:STM32F107VCT6开发板


软件平台:STM32Cube MX + MDK5.22


1. 进行STM32Cube MX的配置



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


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



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(); 进行读写测试成功。




『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
浅谈AVR中定时器几种工作模式