单片机学习小组
直播中

楼斌

8年用户 869经验值
私信 关注

怎样去编写HT32F5232利用I2C读写AT24C02的程序呢

怎样去编写HT32F5232利用I2C读写AT24C02的程序呢?

回帖(1)

曹玥

2022-1-27 14:53:20
花了好多的时间算是把软件I2C协议的代码写出来了。然后进阶,把HT32F5232利用I2C读写AT24C02的程序写出来了。这个过程中很坑的地方就是数据线SDA要一直在输入和输出这里切换,因为这个HT32输入时需要输入使能,就很麻烦。鉴于I2C之前有学过,这里就不贴出来关于I2C的详细解释了。
主要的代码:
(1)I2C

#include "iic.h"
#include "systick.h"
#include "usart.h"

static void I2C_CKCU_Config()
{
        CKCU_PeripClockConfig_TypeDef CCLOCK;
       
        CCLOCK.Bit.PA     = 1;
        CCLOCK.Bit.AFIO  = 1;
       
        CKCU_PeripClockConfig(CCLOCK, ENABLE);
}

static void I2C_GPIO_Config()
{
        AFIO_GPxConfig(I2C_SCL_GPIO_ID, I2C_SCL_PIN, I2C_SCL_AFIO_MODE);
        AFIO_GPxConfig(I2C_SDA_GPIO_ID, I2C_SDA_PIN, I2C_SDA_AFIO_MODE);
       
        /* Configure the GPIO pin                                                                                 */
        GPIO_PullResistorConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_PR_DISABLE);
        GPIO_DriveConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_DV_8MA);
        GPIO_DirectionConfig(I2C_SCL_PORT, I2C_SCL_PIN, GPIO_DIR_OUT);
       
        GPIO_PullResistorConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_PR_DISABLE);
        GPIO_DriveConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DV_8MA);
        GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_OUT);
}

void I2C_Initia()
{
        I2C_CKCU_Config();
        I2C_GPIO_Config();
       
        /* 一定要先发一遍停止信号 */
        I2C_Stop();
}

void I2C_Start()
{
        I2C_SCL_1();
        I2C_SDA_1();
        Systick_Delay_us(DELAYTIME);
       
        I2C_SDA_0();
        Systick_Delay_us(DELAYTIME);
       
        I2C_SCL_0();
        Systick_Delay_us(DELAYTIME);
}

void I2C_Stop()
{
        I2C_SDA_0();
        I2C_SCL_1();
        Systick_Delay_us(DELAYTIME);
        I2C_SDA_1();
}

/* 返回0表示正确应答,1表示无器件响应 */
u8 I2C_WaitAck()
{
        u8 ack;
       
        I2C_SCL_1();
        Systick_Delay_us(DELAYTIME);
       
        I2C_SDA_1();
        Systick_Delay_us(DELAYTIME);
        I2C_SDA_Direction_Input();
        I2C_SDA_InputConfig();
       
        if( I2C_SDA_READ() ) {
                ack = 1;
        }
        else {
                ack = 0;
        }
        I2C_SCL_0();
        Systick_Delay_us(DELAYTIME);
        return ack;
}

void I2C_SendByte(u8 _ucByte)
{
        u8 i;

        /* 先发送字节的高位bit7 */
        for (i = 0; i < 8; i++)
        {               
                if (_ucByte & 0x80)
                {
                        I2C_SDA_1();
                }
                else
                {
                        I2C_SDA_0();
                }
               
                Systick_Delay_us(DELAYTIME);
                I2C_SCL_1();
                Systick_Delay_us(DELAYTIME);
                I2C_SCL_0();
               
                if (i == 7)
                {
                         I2C_SDA_1(); // 释放总线
                }
                _ucByte <<= 1;        /* 左移一个bit */
                Systick_Delay_us(DELAYTIME);
        }
}

u8 I2C_ReadByte()
{
        u8 i;
        u8 data;
       
        data = 0;
       
        for(i = 0;i < 8;i++)
        {
                data <<= 1;
                I2C_SCL_1();
                Systick_Delay_us(DELAYTIME);
               
                I2C_SDA_Direction_Input();
                I2C_SDA_InputConfig();
       
                if( I2C_SDA_READ() ) {
                        data++;
                }
                I2C_SCL_0();
                Systick_Delay_us(DELAYTIME);
        }
        return data;
}

// 返回值为0表示正确,返回1表示未探测到
u8 I2C_CheckDevice(u8 _Address)
{
//        u8 time = 0;
        u8 ucAck = 0;
       
        I2C_Start();
        I2C_SendByte(_Address | EEPROM_I2C_WR);
        ucAck =I2C_WaitAck();
        I2C_SDA_Direction_Output();
        I2C_Stop();

        return ucAck;
}

void I2C_Ack()
{
        I2C_SDA_0();        /* CPU驱动SDA = 0 */
        Systick_Delay_us(DELAYTIME);
       
        I2C_SCL_1();        /* CPU产生1个时钟 */
        Systick_Delay_us(DELAYTIME);
       
        I2C_SCL_0();
        Systick_Delay_us(DELAYTIME);
        I2C_SDA_1();        /* CPU释放SDA总线 */
}

void I2C_NAck()
{
        I2C_SDA_1();        /* CPU驱动SDA = 1 */
        Systick_Delay_us(DELAYTIME);
       
        I2C_SCL_1();        /* CPU产生1个时钟 */
        Systick_Delay_us(DELAYTIME);
       
        I2C_SCL_0();
        Systick_Delay_us(DELAYTIME);
}
(2)AT24C02

//1表示正常,0表示不正常
u8 EE_CheckOK()
{
        if( I2C_CheckDevice(EEPROM_DEV_ADDR) == 0 ){
                printf("rn发送地址完成,AT24C02在线n");
                return 1;
        }
        else{
                printf("rn发送地址完成,AT24C02离线n");
                I2C_Stop();
                return 0;
        }
}

/**********************************************
*        _usAddress : 起始地址                                          *
*        _usSize : 数据长度,单位为字节                          *
*        _pWriteBuf : 存放读到的数据的缓冲区指针          *
***********************************************/

/* 为了提高连续写的效率: 本函数采用page wirte操作。*/
/* _usSize大于8会在第四步卡住 */
u8 EE_WriteBytes(u8 *_pWriteBuf, u16 _usAddress, u16 _usSize)
{
        u16 i,m;
        u16 usAddr;

        usAddr = _usAddress;       
        for (i = 0; i < _usSize; i++)
        {
                /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
                if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0)
                {
                        /* 启动内部写操作 */
                        I2C_Stop();
                       
                        for (m = 0; m < 1000; m++)
                        {                               
                                I2C_Start();
                               
                                /* 第2步:高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
                                I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);       
                               
                                if (I2C_WaitAck() == 0)
                                {
                                        break;
                                }
                        }
                        I2C_SDA_Direction_Output();
                        /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
                        I2C_SendByte((uint8_t)usAddr);
                       
                        if(I2C_WaitAck() != 0)
                        {
                                time++;
                               
                                if( time ==WAITMAX ){
                                        printf("写入数据第四步无应答");
                                        return 0;
                                }
                        }
                        time = 0;
                }
                I2C_SDA_Direction_Output();
                /* 第6步:开始写入数据 */
                I2C_SendByte(_pWriteBuf);
       
                /* 第7步:发送ACK */
                if(I2C_WaitAck() != 0)
                {
                        time++;
                               
                        if( time ==WAITMAX ){
                                printf("写入数据第七步无应答");
                                return 0;
                        }
                }
                time = 0;
                I2C_SDA_Direction_Output();
                usAddr++;        /* 地址增1 */               
        }
        I2C_SDA_Direction_Output();
        /* 命令执行成功,发送I2C总线停止信号 */
        I2C_Stop();
        return 1;
}

u8 EE_ReadBytes(u8 *_pReadBuf, u16 _usAddress, u16 _usSize)
{
        u16 i;
        u32 time = 0;
       
        I2C_Initia();
        I2C_Start();
       
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);
       
        if ( I2C_WaitAck() != 0 )
        {
                time++;
               
                if(time == WAITMAX){
                        printf("rn读取数据第二步无应答rn");
                        return 0;
                }
        }
        I2C_SDA_Direction_Output();
        time = 0;

        /* 发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
        I2C_SendByte((u8)_usAddress);
       
        if ( I2C_WaitAck() != 0 )
        {
                time++;
               
                if(time == WAITMAX){
                        printf("rn读取数据第四步EEPROM无应答rn");        /* EEPROM器件无应答 */
                        return 0;
                }
        }
        I2C_SDA_Direction_Output();
        time = 0;
       
        /* 重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
        I2C_Start();
       
        /* 发起控制字节,高7bit是地址,bit0是读写控制位 */
        I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD);
       
        if ( I2C_WaitAck() != 0 )
        {
                time++;
               
                if(time == WAITMAX){
                        printf("rn读取数据第七步EEPROM无应答rn");        /* EEPROM器件无应答 */
                        return 0;
                }
        }
       
        time = 0;       
        I2C_SDA_Direction_Input();
        I2C_SDA_InputConfig();
        /* 第9步:循环读取数据 */
        for (i = 0; i < _usSize; i++)
        {
                _pReadBuf = I2C_ReadByte();        /* 读1个字节 */
               
                /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
                if (i != _usSize - 1)
                {
                        I2C_SDA_Direction_Output();
                        I2C_Ack();        /* CPU产生ACK信号(驱动SDA = 0) */
                        I2C_SDA_Direction_Input();
                        I2C_SDA_InputConfig();
                }
                else
                {
                        I2C_SDA_Direction_Output();
                        I2C_NAck();        /* CPU产生NACK信号(驱动SDA = 1) */
                }
        }
       
        I2C_Stop();
        printf("rn读取数据成功rn");
        return 1;        /* 执行成功 */
}
对硬件方面(如引脚、模式选择等的封装):

#ifndef _IIC_H
#define _IIC_H

#include "ht32f5xxxx_01.h"

#define EEPROM_I2C_WR                                0                /* 写控制bit */
#define EEPROM_I2C_RD                                1                /* 读控制bit */
#define EEPROM_DEV_ADDR                                0xA0        /* 24xx02的设备地址 */
#define EEPROM_PAGE_SIZE                          8                /* 24xx02的页面大小 */
#define EEPROM_SIZE                                          256                /* 24xx02总容量 */

/* 定义I2C的SCL连接的GPIO端口 */
#define I2C_SCL_GPIO_ID                                        GPIO_PA                                        /* GPIO端口 */
#define I2C_SCL_PIN                                                GPIO_PIN_0                                /* 连接到SCL时钟线的GPIO */
#define I2C_SCL_AFIO_MODE                                AFIO_FUN_GPIO
#define I2C_SCL_PORT                                        HT_GPIOA

/* 定义I2C的SDA连接的GPIO端口 */
#define I2C_SDA_GPIO_ID                                        GPIO_PA                                        /* GPIO端口 */
#define I2C_SDA_PIN                                                GPIO_PIN_1                                /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_AFIO_MODE                                AFIO_FUN_GPIO
#define I2C_SDA_PORT                                        HT_GPIOA

#define I2C_SCL_1()                                                GPIO_SetOutBits(I2C_SCL_PORT, I2C_SCL_PIN)
#define I2C_SCL_0()                                                GPIO_ClearOutBits(I2C_SCL_PORT, I2C_SCL_PIN)
       
#define I2C_SDA_1()                                                GPIO_SetOutBits(I2C_SDA_PORT, I2C_SDA_PIN)
#define I2C_SDA_0()                                                GPIO_ClearOutBits(I2C_SDA_PORT, I2C_SDA_PIN)
#define I2C_SDA_Direction_Input()                GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_IN)
#define I2C_SDA_InputConfig()                        GPIO_InputConfig(I2C_SDA_PORT, I2C_SDA_PIN, ENABLE)
#define I2C_SDA_InputDisable()                        GPIO_InputConfig(I2C_SDA_PORT, I2C_SDA_PIN, DISABLE)
#define I2C_SDA_Direction_Output()                GPIO_DirectionConfig(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_DIR_OUT)
#define I2C_SDA_READ()                                        GPIO_ReadInBit(I2C_SDA_PORT, I2C_SDA_PIN)

#define WAITMAX                                                        2000       
#define DELAYTIME                                                 2

void I2C_Initia(void);
void I2C_Start(void);
void I2C_Stop(void);
u8 I2C_WaitAck(void);
void I2C_SendByte(u8 _ucByte);
u8 I2C_ReadByte(void);
void I2C_Ack(void);
void I2C_NAck(void);
//FlagStatus I2C_CheckOK(void);
u8 I2C_CheckDevice(u8 _Address);

u8 EE_CheckOK(void);
u8 EE_WriteBytes(u8 *_pWriteBuf, u16 _usAddress, u16 _usSize);
u8 EE_ReadBytes(u8 *_pReadBuf, u16 _usAddress, u16 _usSize);
举报

更多回帖

×
20
完善资料,
赚取积分