雅特力AT32 I2C实战:从零构建EEPROM存储系统

张开发
2026/4/12 11:26:47 15 分钟阅读

分享文章

雅特力AT32 I2C实战:从零构建EEPROM存储系统
1. 硬件连接与基础配置第一次玩AT32的I2C外设时我对着开发板上的SCL和SDA引脚发呆了半天。后来发现硬件连接其实就三个要点上拉电阻、开漏输出、引脚复用。以AT32F403A开发板为例I2C1的SCL(PB6)和SDA(PB7)需要配置为复用开漏模式记得接上4.7K上拉电阻——这个值我实测过在标准模式(100kHz)下最稳定。硬件初始化代码要特别注意时钟使能顺序// 使能GPIOB和I2C1时钟 crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_I2C1_PERIPH_CLOCK, TRUE); // 配置PB6(SCL)和PB7(SDA) gpio_init_type gpio_init_struct; gpio_default_para_init(gpio_init_struct); gpio_init_struct.gpio_mode GPIO_MODE_MUX; gpio_init_struct.gpio_out_type GPIO_OUTPUT_OPEN_DRAIN; gpio_init_struct.gpio_pull GPIO_PULL_UP; gpio_init_struct.gpio_pins GPIO_PINS_6 | GPIO_PINS_7; gpio_init(GPIOB, gpio_init_struct);2. I2C外设初始化详解AT32的I2C初始化比STM32复杂些关键是要配置时钟控制寄存器(I2C_CLKCTRL)。这里有个坑官方库里的i2c_init()函数需要传入数字滤波值和时钟配置值。我推荐使用Artery提供的配置工具生成时钟参数比如100kHz标准模式对应0x60E02E2E。实测过的初始化代码i2c_init_type i2c_init_struct; i2c_default_para_init(i2c_init_struct); i2c_init_struct.clock_speed I2C_CLOCK_SPEED_STANDARD; i2c_init_struct.digital_filter 0x0F; // 数字滤波值 i2c_init_struct.clock_ctrl 0x60E02E2E; // 100kHz配置 i2c_init(I2C1, i2c_init_struct);3. EEPROM读写实战我用AT24C02做测试时发现必须遵循EEPROM的页写入时序。每次写入后要加5ms延时AT24C02的典型写入时间。读操作则需要注意先发送设备地址写标志再发送存储地址最后重启通信改为读模式。完整的页写入函数示例void eeprom_page_write(uint16_t addr, uint8_t *data, uint8_t len) { while(i2c_flag_get(I2C1, I2C_BUSYF_FLAG)); // 等待总线空闲 i2c_start_generate(I2C1); // 发送起始条件 while(!i2c_flag_get(I2C1, I2C_TDIS_FLAG)); i2c_data_send(I2C1, 0xA0); // 设备地址 写 while(!i2c_flag_get(I2C1, I2C_TDIS_FLAG)); i2c_data_send(I2C1, addr); // 存储地址 for(uint8_t i0; ilen; i){ while(!i2c_flag_get(I2C1, I2C_TDIS_FLAG)); i2c_data_send(I2C1, data[i]); } i2c_stop_generate(I2C1); // 发送停止条件 delay_ms(5); // 必须的写入等待 }4. 三种传输模式对比轮询模式最简单但效率低适合低速场景。中断模式能释放CPU资源但要注意中断优先级设置——我遇到过因为优先级配置不当导致通信失败的情况。DMA模式最省心适合大数据量传输配置时要注意DMA通道与I2C事件的配合。DMA模式配置关键点// 使能DMA时钟 crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE); // 配置DMA通道 dma_init_type dma_init_struct; dma_default_para_init(dma_init_struct); dma_init_struct.direction DMA_DIR_MEMORY_TO_PERIPHERAL; dma_init_struct.memory_inc_enable TRUE; dma_init_struct.peripheral_data_width DMA_PERIPHERAL_DATA_WIDTH_BYTE; dma_init_struct.memory_data_width DMA_MEMORY_DATA_WIDTH_BYTE; dma_init(DMA1_CHANNEL6, dma_init_struct); // 以I2C1_TX为例 // 使能I2C的DMA请求 i2c_dma_enable(I2C1, I2C_DMA_REQUEST_TX, TRUE);5. 常见问题排查调试I2C时我的必备工具是逻辑分析仪。这几个错误我踩过坑无ACK响应检查设备地址是否正确7位地址要左移1位总线锁死有时需要重新初始化I2C外设数据错位检查时钟配置是否符合从设备要求一个实用的总线恢复函数void i2c_bus_recover(void) { GPIOB-pins GPIO_PINS_6 | GPIO_PINS_7; // 强制拉高 gpio_init_struct.gpio_mode GPIO_MODE_OUTPUT; gpio_init(GPIOB, gpio_init_struct); // 模拟I2C总线恢复时序 for(uint8_t i0; i9; i){ GPIOB-pins GPIO_PINS_6; delay_us(5); GPIOB-pins GPIO_PINS_6 | GPIO_PINS_7; delay_us(5); } // 恢复GPIO配置 gpio_init_struct.gpio_mode GPIO_MODE_MUX; gpio_init(GPIOB, gpio_init_struct); }6. 性能优化技巧通过实测发现开启I2C的**快速模式(400kHz)**需要优化PCB布局。如果走线较长建议缩短SCL/SDA走线长度减小上拉电阻值我用2.2K在400kHz下稳定启用I2C的数字滤波功能DFLT值设为0x0F时钟配置工具的参数解读参数说明标准模式值快速模式值SCLHSCL高电平周期0x600x1ESCLLSCL低电平周期0xE00x3CSDAD数据保持时间0x020x01SCLD数据建立时间0x0E0x077. 完整项目集成在实际项目中我习惯将EEPROM操作封装成独立模块。这个头文件接口很好用// eeprom.h #define EEPROM_ADDR 0xA0 typedef enum { EEPROM_OK, EEPROM_BUSY, EEPROM_ERROR } eeprom_status_t; eeprom_status_t eeprom_write(uint16_t addr, uint8_t *data, uint16_t len); eeprom_status_t eeprom_read(uint16_t addr, uint8_t *data, uint16_t len);配合FreeRTOS使用时记得添加互斥锁保护I2C总线SemaphoreHandle_t i2c_mutex xSemaphoreCreateMutex(); void task_eeprom(void *param) { if(xSemaphoreTake(i2c_mutex, pdMS_TO_TICKS(100)) pdTRUE){ eeprom_read(0x00, buffer, 32); xSemaphoreGive(i2c_mutex); } }

更多文章