GD32的IIC读写

引言

在嵌入式开发中,实时时钟(RTC)模块是记录时间的关键组件。本文基于GD32F4xx微控制器和PCF8563 RTC芯片,详细讲解如何实现高精度时间记录与显示。通过SysTick定时器实现延时功能,并结合I2C通信协议与PCF8563交互,最终实现每秒打印当前时间的功能。


硬件与开发环境
  • 主控芯片:GD32F450(Cortex-M4内核,主频120MHz)
  • RTC模块:PCF8563(I2C接口,支持年月日、时分秒及闹钟功能)
  • 开发环境:Keil MDK或GCC + GD32 Firmware Library

系统关键模块解析
1. SysTick定时器配置

SysTick被配置为1MHz的中断频率,提供微秒级定时服务,支撑延时函数实现。

c

Copy

// systick.c
void systick_config(void) {
    if (SysTick_Config(SystemCoreClock / 1000000U)) { // 1MHz中断
        while(1); // 错误处理
    }
    NVIC_SetPriority(SysTick_IRQn, 0x00U); // 最高优先级
}

// 中断中更新延时计数器
void SysTick_Handler(void) {
    delay_decrement();
}
2. 延时函数实现
  • 微秒级延时:直接操作计数器实现精准延时。
  • 毫秒级延时:基于微秒延时的封装。

c

Copy

void delay_1us(uint32_t count) {
    delay = count;
    while (delay != 0);
}

void delay_1ms(uint32_t count) {
    delay = count * 1000; // 转换为微秒
    while (delay != 0);
}
3. PCF8563 RTC驱动
  • 时间结构体转换:将年月日转换为PCF8563的BCD格式,处理世纪位(支持2000-2199年)。

c

Copy

void PCF8563_set_clock(Clock_t c) {
    uint8_t p[7];
    p[0] = ((c.second / 10) << 4) | (c.second % 10); // 秒
    p[5] = (c.year >= 2100 ? 1 << 7 : 0) | ((c.month / 10) << 4) | (c.month % 10); // 世纪位
    I2C_WRITE(PCF8563_ADDR, PCF8563_REG_TD, p, 7);
}

void PCF8563_get_clock(Clock_t *c) {
    uint8_t p[7];
    I2C_READ(PCF8563_ADDR, PCF8563_REG_TD, p, 7);
    c->year = ((p[6] >> 4) * 10 + (p[6] & 0x0F)) + (p[5] >> 7 ? 2100 : 2000); // 解析年份
}
4. 主程序流程
  • 外设初始化:配置中断优先级、SysTick、USART(调试输出)、I2C及RTC。
  • 时间设置与读取:初始设置时间后,每秒读取并打印时间信息。

c

Copy

int main(void) {
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    systick_config();
    Usart0_init(); // 初始化调试串口
    I2C_init();
    PCF8563_init();

    Clock_t c = {2025, 2, 28, 4, 23, 59, 52};
    PCF8563_set_clock(c);

    while (1) {
        PCF8563_get_clock(&c);
        printf("%d-%02d-%02d %02d:%02d:%02d Week:%d\n", 
               c.year, c.month, c.day, c.hour, c.minute, c.second, c.week);
        delay_1ms(1000); // 精确延时1秒
    }
}

关键问题与优化建议
  1. SysTick中断频率
    • 配置为1MHz时需确保主频稳定。若主频为120MHz,分频后每微秒触发一次中断,适用于高精度场景,但频繁中断可能增加CPU负载。可评估是否需要调整分频系数。
  2. RTC时间转换验证
    • 需验证世纪位处理逻辑:当year >= 2100时,Cent位应置1,确保年份正确解析。
  3. I2C通信稳定性
    • 增加超时机制和错误重传,提升抗干扰能力。例如在I2C_READ/WRITE中添加重试逻辑。

扩展功能
  • 闹钟中断:通过配置PCF8563的闹钟寄存器,结合GD32的外部中断引脚,实现定时唤醒功能。
  • 低功耗优化:在等待延时或空闲时进入低功耗模式,通过RTC闹钟唤醒系统。

结语

本文基于GD32F4xx和PCF8563实现了一个完整的RTC应用,涵盖硬件初始化、SysTick延时、I2C通信及时间管理。开发者可根据需求扩展闹钟、低功耗等功能,为物联网设备、数据记录仪等场景提供可靠时间基准。完整代码已通过测试,可在GitHub获取,欢迎交流优化建议。

### 配置GD32硬件IIC接口 #### 初始化库函数准备 为了配置GD32的硬件IIC接口,首先需要准备好初始化所需的库文件。通常情况下,在开发环境中会使用官方固件库来简化外设驱动程序的编写工作[^3]。 ```c #include "gd32f30x.h" ``` 这段代码展示了如何引入必要的头文件以便后续调用相应的API函数。 #### GPIO端口设置 在启动任何通信之前,必须先设定好GPIO引脚的功能模式。对于IIC来说,SCL(串行时钟线)和SDA(串行数据线)应该被指定为复用推挽输出方式,并启用内部上拉电阻: ```c void gpio_config(void){ rcu_periph_clock_enable(RCU_GPIOB); //使能GPIOB时钟 /* SDA and SCK configuration */ gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); } ``` 上述C语言片段说明了针对特定管脚执行的操作,即把PB6和PB7分别作为IIC的数据线(SDA)与时钟信号(SCL)[^1]。 #### IIC模块参数调整 完成基本IO之后,下一步就是对实际使用的IIC外设进行具体参数定义,比如波特率、地址匹配模式等。这里以标准速率为例展示部分关键步骤: ```c i2c_parameter_struct i2c_initpara; rcu_periph_clock_enable(RCU_I2C1); /* configure the I2C init parameter struct with default values */ i2c_struct_para_init(&i2c_initpara); // 设置IIC的工作频率 i2c_initpara.i2c_timen= 4; i2c_initpara.i2c_dutycycle = I2C_DUTYCYCLE_2; i2c_initpara.i2c_ownaddress = 0xA0; i2c_initpara.i2c_ack = I2C_ACK_ENABLE; i2c_initpara.i2c_acknowledgement_address = I2C_ADDRESSINGMODE_7BIT; i2c_initpara.i2c_generalcall = I2C_GENERALCALL_DISABLE; /* initialize I2C1 with the given parameters */ i2c_init(I2C1,&i2c_initpara); /* enable I2C1 */ i2c_enable(I2C1); /* enable acknowledge */ i2c_acknowledge_config(I2C1,I2C_ACK_ENABLE); ``` 以上代码段描述了一个完整的IIC初始化过程,包括但不限于时间常数的选择以及应答机制的开启状态等细节处理[^2]。 #### 数据收发功能实现 最后一步则是构建发送接收子程序,确保能够按照既定协议与外部器件交换信息。下面给出了一组简单的例子用来演示这一逻辑框架: ```c uint8_t send_data(uint8_t *pdata,uint16_t length){ uint16_t count = 0; while(count < length){ /* wait until TBE flag is set */ while(i2c_flag_get(I2C1, I2C_FLAG_TBE)== RESET); /* transmit data byte by byte*/ i2c_data_transmit(I2C1,*pdata++); count++; } } uint8_t receive_data(uint8_t *pdata,uint16_t length){ uint16_t count = 0; while(count < length){ /* wait until RBNE flag is set */ while(!i2c_flag_get(I2C1, I2C_FLAG_RBNE)); /* read received data from DR register */ *pdata++ = i2c_data_receive(I2C1); count++; } } ``` 这些辅助性的传输例程可以帮助开发者更方便地操控底层硬件资源,进而达成预期目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值