GD32开发实战指南(基础篇) 第21章 I2C

发布者:Huanle666最新更新时间:2024-11-08 来源: elecfans关键字:GD32  开发实战  I2C 手机看文章 扫描二维码
随时随地手机看文章


    /* clear the ADDSEND bit */

    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);


    if(1 == number_of_byte)

    {

        /* send a stop condition to I2C bus */

        i2c_stop_on_bus(I2C_BUS[i2c_id]);

    }


    /* while there is data to be read */

    while(number_of_byte)

    {

        if(3 == number_of_byte)

        {

            /* wait until BTC bit is set */

            while(!i2c_flag_get(I2C_BUS[i2c_id], I2C_FLAG_BTC));


            /* disable acknowledge */

            i2c_ack_config(I2C_BUS[i2c_id], I2C_ACK_DISABLE);

        }

        if(2 == number_of_byte)

        {

            /* wait until BTC bit is set */

            while(!i2c_flag_get(I2C_BUS[i2c_id], I2C_FLAG_BTC));


            /* send a stop condition to I2C bus */

            i2c_stop_on_bus(I2C_BUS[i2c_id]);

        }


        /* wait until the RBNE bit is set and clear it */

        if(i2c_flag_get(I2C_BUS[i2c_id], I2C_FLAG_RBNE))

        {

            /* read a byte from the EEPROM */

            *p_buffer = i2c_data_receive(I2C_BUS[i2c_id]);


            /* point to the next location where the byte read will be saved */

            p_buffer++;


            /* decrement the read bytes counter */

            number_of_byte--;

        }

    }


    /* wait until the stop condition is finished */

    while(I2C_CTL0(I2C_BUS[i2c_id]) & 0x0200);


    /* enable acknowledge */

    i2c_ack_config(I2C_BUS[i2c_id], I2C_ACK_ENABLE);

    i2c_ackpos_config(I2C_BUS[i2c_id], I2C_ACKPOS_CURRENT);

}

最后看下主函数吧。


/*

    brief      main function

    param[in]  none

    param[out] none

    retval     none

*/

int main(void)

{

    //systick init

    sysTick_init();


    // led init

    led_init(LED1);


    //usart init 115200 8-N-1

    com_init(COM1, 115200, 0, 1);


    /* initialize EEPROM  */

    I2C_EE_Init(IIC0);

    i2c_24c02_test(IIC0);


    while(1)

    {

        led_toggle(LED1);

        delay_ms(1000);

    }

}

很简单,往AT24C02中写入数据,然后再读取数据,读写测试的函数如下:


/*

    brief      I2C read and write functions

    param[in]  i2c_typedef_enum i2c_id

    param[out] none

    retval     I2C_OK or I2C_FAIL

*/

uint8_t i2c_24c02_test(i2c_typedef_enum i2c_id)

{

    uint16_t i;

    uint8_t i2c_buffer_write[BUFFER_SIZE];

    uint8_t i2c_buffer_read[BUFFER_SIZE];


    printf('\r\nAT24C02 writing...\r\n');


    /* initialize i2c_buffer_write */

    for(i = 0; i < BUFFER_SIZE; i++)

    {

        i2c_buffer_write[i] = i;

        printf('0x%02X ', i2c_buffer_write[i]);

        if(15 == i % 16)

        {

            printf('\r\n');

        }

    }

    /* EEPROM data write */

    eeprom_buffer_write(i2c_id, i2c_buffer_write, EEP_FIRST_PAGE, BUFFER_SIZE);

    printf('AT24C02 reading...\r\n');

    /* EEPROM data read */

    eeprom_buffer_read(i2c_id, i2c_buffer_read, EEP_FIRST_PAGE, BUFFER_SIZE);

    /* compare the read buffer and write buffer */

    for(i = 0; i < BUFFER_SIZE; i++)

    {

        if(i2c_buffer_read[i] != i2c_buffer_write[i])

        {

            printf('0x%02X ', i2c_buffer_read[i]);

            printf('Err:data read and write aren't matching.\n\r');

            return I2C_FAIL;

        }

        printf('0x%02X ', i2c_buffer_read[i]);

        if(15 == i % 16)

        {

            printf('\r\n');

        }

    }

    printf('I2C-AT24C02 test passed!\n\r');

    return I2C_OK;

}

当然在读写测试之前应该对AT24C02进行初始化操作。


/**

  * @brief  I2C 外设(EEPROM)初始化

  * @param  i2c_typedef_enum i2c_id

  * @retval 无

  */

void I2C_EE_Init(i2c_typedef_enum i2c_id)

{

    /* 选择EEPROM要写入的地址 */

#ifdef EEPROM_BLOCK0_ADDRESS

    /* 选择 EEPROM Block0 来写入 */

    eeprom_address = EEPROM_BLOCK0_ADDRESS;

#endif


#ifdef EEPROM_BLOCK1_ADDRESS  

    /* 选择 EEPROM Block1 来写入 */

    eeprom_address = EEPROM_BLOCK2_ADDRESS;

#endif


#ifdef EEPROM_BLOCK2_ADDRESS  

    /* 选择 EEPROM Block2 来写入 */

    eeprom_address = EEPROM_BLOCK2_ADDRESS;

#endif


#ifdef EEPROM_BLOCK3_ADDRESS  

    /* 选择 EEPROM Block3 来写入 */

    eeprom_address = EEPROM_BLOCK3_ADDRESS;

#endif

    i2c_gpio_config(i2c_id); 

 

    i2c_mode_config(i2c_id);

}

5.2 实验现象

下载好程序后,打开串口助手,可以看到如下信息。

1684633679809tka9v1xdzp

最后,我们使用逻辑分析来查看数据。

168463368049216b3sl9j91

使用的400kHz的速率,可以看到数据的写操作和前面分析的时序是一样的,完全吻合。

6 软件I2C

6.1 具体代码实现

首先实现I2C的协议。


/**

  * @brief  延时nus

    @param  nus为要延时的us数.    nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)    

  * @retval None

  */

void I2C_Delay_us(uint32_t nus)

{        

    uint32_t temp;

    SysTick->LOAD=nus*fac_us;      //时间加载

    SysTick->VAL=0x00;          //清空计数器

    SysTick->CTRL=0x01 ;        //开始倒数

    do

    {

        temp=SysTick->CTRL;

    }while((temp&0x01)&&!(temp&(1<<16)));    //等待时间到达

    SysTick->CTRL=0x00;      //关闭计数器

    SysTick->VAL =0X00;    //清空计数器 

}


/**

  * @brief  延时nms

    @param  nms:要延时的ms数

  * @retval None

  */

void I2C_Delay_ms(uint16_t nms)

{

    uint32_t i;

    for(i=0;i

注释很清楚,对照I2C的协议看就行。


接着就是实现AT2C02的读写操作。


/**

  * @brief  EEPROM_CheckOk, 判断串行EERPOM是否正常

  * @param  None

  * @retval 1 表示正常, 0 表示不正常

  */

uint8_t EEPROM_CheckOk(void)

{

    if (I2C_CheckDevice(EEPROM_DEV_ADDR) == 0)

    {

        return 1;

    }

    else

    {

        /* 失败后,切记发送I2C总线停止信号 */

        I2C_Stop();        

        return 0;

    }

}


/**

  * @brief  EEPROM_ReadBytes, 从串行EEPROM指定地址处开始读取若干数据

  * @param  _usAddress : 起始地址

  *            _usSize : 数据长度,单位为字节

  *         _pReadBuf : 存放读到的数据的缓冲区指针

  * @retval 0 表示失败,1表示成功

  */

uint8_t EEPROM_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)

{

    uint16_t i;

    

    /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */

    

    /* 第1步:发起I2C总线启动信号 */

    I2C_Start();

    

    /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */

    I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);    /* 此处是写指令 */

    

    /* 第3步:发送ACK */

    if (I2C_WaitAck() != 0)

    {

        goto cmd_fail;    /* EEPROM器件无应答 */

    }


    /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */

    I2C_SendByte((uint8_t)_usAddress);

    

    /* 第5步:发送ACK */

    if (I2C_WaitAck() != 0)

    {

        goto cmd_fail;    /* EEPROM器件无应答 */

    }

    

    /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */

    I2C_Start();

    

    /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */

    I2C_SendByte(EEPROM_DEV_ADDR | I2C_RD);    /* 此处是读指令 */

    

    /* 第8步:发送ACK */

    if (I2C_WaitAck() != 0)

    {

        goto cmd_fail;    /* EEPROM器件无应答 */

    }    

    

    /* 第9步:循环读取数据 */

    for (i = 0; i < _usSize; i++)

    {

        _pReadBuf[i] = I2C_ReadByte();    /* 读1个字节 */

        

        /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */

        if (i != _usSize - 1)

        {

            I2C_Ack();    /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */

        }

        else

        {

            I2C_NAck();    /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */

        }

    }

    /* 发送I2C总线停止信号 */

    I2C_Stop();

    return 1;    /* 执行成功 */


cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */

    /* 发送I2C总线停止信号 */

    I2C_Stop();

    return 0;

}


/**

  * @brief  EEPROM_WriteBytes, 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率

  * @param  _usAddress : 起始地址

[1] [2] [3] [4]
关键字:GD32  开发实战  I2C 引用地址:GD32开发实战指南(基础篇) 第21章 I2C

上一篇:GD32开发实战指南(基础篇) 第18章 CRC校验
下一篇:GD32 MCU超频后无法再次下载程序的解决办法

推荐阅读最新更新时间:2026-03-25 01:01

GD32开发实战指南(基础篇) 第8章 定时器
开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 PWM输出的工作原理 脉冲宽度调制(PWM) ,是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。 GD32 的定时器除了 TIMER5 和 6(基本定时器)。其他的定时器都可以用来产生 PWM 输出。 每个定时器有四个通道,每一个通道都有一个捕获比较寄存器,,将寄存器值和计数器值比较,通过比较结果输出高低电平,便可以实现脉冲宽度调制模式(PWM信号)。 在上一节,讲解了定时器
[单片机]
GD32开发实战指南(基础篇) 第14章 内部温度传感器
开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 内部温度传感器工作原理 GD32 有一个内部的温度传感器,可以用来测量 CPU 及周围的温度(TA)。该温度传感器在内部和 ADCx_IN16 输入通道相连接,此通道把传感器输出的电压转换成数字值。温度传感器模拟输入推荐采样时间是 17.1μs。GD32 的内部温度传感器支持的温度范围为: -40~125度。精度比较差,为±1.5℃左右。 GD32 内部温度传感器的使用很简单,只要设置一下内部 ADC,并激活其内部通道就差不多了。关于 ADC 的设置,我们在前面的章节已经进行了详细的介绍,这里就不再多说。接下来我们介
[单片机]
MYD-LR3576 AMP非对称多核开发实战:从配置到深度优化
一、什么是AMP?为什么重要? AMP(Asymmetric Multi-Processing)非对称多处理架构,允许单个芯片的不同核心运行不同的或裸机程序。相比传统的SMP(对称多处理),AMP具有独特优势。 核心特性: 异构运算:不同核心运行最适合的操作系统,如处理复杂应用,保障实时任务; 资源隔离:各核心拥有独立内存空间,避免资源冲突; 灵活:通过共享内存、RPMSG等方式实现高效核间通信; 商业价值: 成本优化:单芯片替代多芯片方案,减少面积和数量; 开发灵活:支持Linux、RT-Thread、裸机程序的自由组合; 系统可靠:故障隔离,关键任务永不掉线;
[嵌入式]
MYD-LR3576 AMP非对称多核<font color='red'>开发</font><font color='red'>实战</font>:从配置到深度优化
【深度实战】米尔RK3576开发板AMP非对称多核开发指南:从配置到实战
一、什么是AMP?为什么重要? AMP(Asymmetric Multi-Processing)非对称多处理架构,允许单个芯片的不同核心运行不同的操作系统或裸机程序。相比传统的SMP(对称多处理),AMP具有独特优势。 核心特性: • 异构运算:不同核心运行最适合的操作系统,如Linux处理复杂应用,RT-Thread保障实时任务 • 资源隔离:各核心拥有独立内存空间,避免资源冲突 • 灵活通信:通过共享内存、RPMSG等方式实现高效核间通信 商业价值: • 成本优化:单芯片替代多芯片方案,减少PCB面积和元器件数量 • 开发灵活:支持Linux、RT-Thread、裸机程序的自由组合 • 系统可靠:故障隔离,关键任务
[嵌入式]
【深度<font color='red'>实战</font>】米尔RK3576<font color='red'>开发</font>板AMP非对称多核<font color='red'>开发</font>指南:从配置到<font color='red'>实战</font>
MYD-LD25X Cortex-M33实时核开发实战解析
在嵌入式系统设计中,如何平衡高性能计算与实时控制一直是工程师面临的挑战。STM32MP257的异构架构为这一难题提供了优雅的解决方案,而其中的Cortex-M33实时核更是实现硬实时性能的关键所在。 一、异构架构:分工明确,效能卓越 STM32MP257采用创新的双核子系统设计: Cortex-A35应用核(双核1.5GHz):运行Linux系统,负责复杂UI、网络通信、文件管理等非实时任务。 Cortex-M33实时核(400MHz):专攻实时控制,具备纳秒级中断响应,集成FPU和DSP指令集。 二、架构优势凸显: 硬件级资源隔离:通过RIF单元确保M33核独占关键外设,避免核间冲突 能效精细控制:支持独
[嵌入式]
MYD-LD25X Cortex-M33实时核<font color='red'>开发</font><font color='red'>实战</font>解析
STM32实战指南:智能温度控制系统开发从入门到精通全攻略
一、项目背景与意义 在工业自动化、智能家居、农业大棚等场景中,精准的温度控制是实现高效生产的关键。传统温控系统存在成本高、扩展性差等问题,基于STM32的解决方案具有以下优势: 1. 高性能:Cortex-M3内核提供72MHz主频 2. 低成本:核心板价格低于20元 3. 易扩展:丰富的外设接口支持多种传感器 4. 低功耗:多种省电模式可选 本系统实现功能: – 实时温度采集(精度±0.5℃) – 自适应PID算法控制 – 双模式人机交互(本地+远程) – 多重安全保护机制 — 二、硬件系统深度解析 2.1 元件选型指南 2.2 硬件电路设计要点 温度采集电路 ! (https://i3.
[单片机]
STM32<font color='red'>实战</font>指南:智能温度控制系统<font color='red'>开发</font>从入门到精通全攻略
《STM32库开发实战指南 》USART
补充几个当时还不会的知识点。 发送寄存器 寄存器 功能 TE 发送使能 TXE 发送单个字节的时候使用,检查发送寄存器为空? TC 发送字符串的时候使用,实质上时多次调用发送单字符的函数,但是最后一次退出前要检查TC,发送完成寄存器 TXIE 发送完成中断使能 printf的多态 我们可以在Keil5中的工程选项中,勾选Use MicroLIB,这样我们就可以在工程中使用stdio.h头文件了。 当然了,单片机又没有屏幕和键盘,所以原来printf、scanf的实现方法当然不能用了,不过我们可以通过串口的方式来实现其。按照串口的方法,将要显示的内容输出到串口,将要获得的字符从串口中读取,那么无论是printf
[单片机]
S3C2440 开发实战(2):start.S初认识 + SDRAM配置 + 重定位
1、看门狗部分 废话不多说,直接开始配置,首先就是要关闭暂时不使用的看门狗,找到看门狗的寄存器:WTCON,将其第0位置0,即禁用看门狗,即: # define pWTCON 0x53000000 //WTCON地址 ldr r0, =pWTCON mov r1, #0x0 str r1, //关闭看门狗 2、时钟部分 在S3C2440中有三种时钟频率,分别为FCLK, HCLK, PCLK,分别控制不同种类的外设,在时钟树中可以进行查找,这里不做过多赘述,在以后的博客中有应用。 从芯片手册中查找FCLK, HCLK, PCLK 的最高频率,在范围内我们选择设置FCLK = 400MHZ, H
[单片机]
S3C2440 <font color='red'>开发</font>板<font color='red'>实战</font>(2):start.S初认识 + SDRAM配置 + 重定位
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved