1.硬件选型
单片机: GD32L233KBQ6
EEPROM: FM24C256E
A0-A2高低电平组合决定从机(EEPROM)地址,WP高低电平决定EEPROM写入权限,具体可查询官方手册。
2.main.c
#include "gd32l23x.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "gd32l23x_eval.h"
#include "gd32l23x_i2c.h"
#define EEPROM_ADDR 0x50
void delay(volatile uint32_t t) {
while(t--);
}
void usart0_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9); // PA9 -> USART0_TX
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10); // PA10 -> USART0_RX
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9 | GPIO_PIN_10);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9 | GPIO_PIN_10);
}
void usart0_config(void)
{
rcu_periph_clock_enable(RCU_USART0);
usart_deinit(USART0); // 复位
usart_baudrate_set(USART0, 115200U);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_enable(USART0);
}
void i2c0_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_I2C0);
gpio_af_set(GPIOB, GPIO_AF_4, GPIO_PIN_6 | GPIO_PIN_7);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6 | GPIO_PIN_7);
gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
}
void i2c0_config(void)
{
i2c_deinit(I2C0);
// 配置时钟:设置SCL高低电平持续时间 (单位 tI2CCLK),默认配置为 100kHz
i2c_master_clock_config(I2C0, 0x13, 0x13); // 根据手册调节 SCLH/SCLL
i2c_timing_config(I2C0, 0x03, 0x02, 0x02); // PSC, SCL delay, SDA delay
// 启用模拟和数字滤波
i2c_analog_noise_filter_enable(I2C0);
i2c_digital_noise_filter_config(I2C0, FILTER_LENGTH_2);
// 启用 I2C 外设
i2c_enable(I2C0);
}
/*!
\brief 向 I2C 外部 EEPROM 写入一个字节数据
\param[in] mem_addr: EEPROM 内部地址
\param[in] data: 要写入的数据
\retval 无
*/
void eeprom_write(uint16_t mem_addr, uint8_t data)
{
while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
// 顺序修正
i2c_master_addressing(I2C0, EEPROM_ADDR << 1, I2C_MASTER_TRANSMIT);
i2c_transfer_byte_number_config(I2C0, 3); // 地址高、低 + 数据
i2c_automatic_end_enable(I2C0);
i2c_start_on_bus(I2C0);
// 等待发送缓冲区空
while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
i2c_data_transmit(I2C0, (mem_addr >> 8) & 0xFF); // 高位地址
while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
i2c_data_transmit(I2C0, mem_addr & 0xFF); // 低位地址
while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
i2c_data_transmit(I2C0, data);
while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
}
/*!
\brief 从 I2C 外部 EEPROM 指定地址读取一个字节数据
\param[in] mem_addr: EEPROM 内部地址
\retval val: 读取到的数据
*/
uint8_t eeprom_read(uint16_t mem_addr)
{
uint8_t data;
// 等待I2C总线空闲
while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
// 第一步:发送内存地址(写操作)
i2c_master_addressing(I2C0, EEPROM_ADDR << 1, I2C_MASTER_TRANSMIT);
i2c_transfer_byte_number_config(I2C0, 2); // 高地址 + 低地址
i2c_automatic_end_enable(I2C0);
i2c_start_on_bus(I2C0);
while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
i2c_data_transmit(I2C0, (mem_addr >> 8) & 0xFF); // 地址高字节
while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
i2c_data_transmit(I2C0, mem_addr & 0xFF); // 地址低字节
// 等待地址写完 + STOP 发送
while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
// 第二步:重新发送起始信号,切换为读操作
i2c_master_addressing(I2C0, EEPROM_ADDR << 1, I2C_MASTER_RECEIVE);
i2c_transfer_byte_number_config(I2C0, 1); // 只读取1字节
i2c_automatic_end_enable(I2C0);
i2c_start_on_bus(I2C0);
// 等待数据到达
while(!i2c_flag_get(I2C0, I2C_FLAG_RBNE));
data = i2c_data_receive(I2C0);
// 等待 STOP 完成
while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
return data;
}
void i2c_flash_test(){
uint16_t test_addr = 0x1234;
uint8_t write_val = 0x66;
eeprom_write(test_addr, write_val);
delay_1ms(10);
uint8_t read_val = 0;
read_val = eeprom_read(test_addr);
char buf[64];
sprintf(buf, "\r\nI2C Flash Test - Wrote: 0x%02X, Read: 0x%02X\r\n", write_val, read_val);
for (char *p = buf; *p; ++p) {
usart_data_transmit(USART0, *p);
while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET);
}
}
int main(void)
{
SystemInit();
systick_config();
usart0_gpio_config();
usart0_config();
i2c0_gpio_config();
i2c0_config();
delay_1ms(10);
i2c_flash_test();
while (1);
}
3.效果实现及注意事项
注意:Start之后必须立即发送从机地址,否则某些芯片(如GD32)不会自动开始传输,甚至不激活I2C控制器。因此最好在
i2c_start_on_bus(I2C0);
前完成配置:
i2c_master_addressing(I2C0, EEPROM_ADDR << 1, I2C_MASTER_TRANSMIT);
i2c_transfer_byte_number_config(I2C0, 3); // 地址高、低 + 数据
i2c_automatic_end_enable(I2C0);
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_60013390/article/details/149112685
|
|