蓝桥杯嵌入式G4实战构建可编程电阻数据采集系统在嵌入式系统开发中I²C总线因其简洁的两线设计和多设备支持特性成为连接各类传感器的首选方案。本文将带领读者基于STM32G4系列微控制器开发一个完整的可编程电阻数据采集系统通过按键控制MCP4017数字电位器实时采集分压电压并将历史数据存储到AT24C02 EEPROM中。这个项目不仅涵盖了I²C通信的核心技术点还融合了ADC采集、按键中断和LCD显示等嵌入式开发常见功能模块。1. 系统架构设计与硬件连接1.1 整体系统框图本项目的硬件架构围绕STM32G4微控制器构建主要包含以下关键组件[STM32G4] ├── I²C总线 │ ├── MCP4017数字电位器 │ └── AT24C02 EEPROM ├── ADC通道(PB14) │ └── 电压分压电路 ├── 4个独立按键 └── LCD显示屏1.2 I²C设备地址配置在硬件连接前需要明确两个I²C从设备的地址配置设备写地址读地址地址引脚连接AT24C020xA00xA1A2A1A0GNDMCP40170x5E0x5F固定地址1.3 关键电路设计电压采集部分采用简单的分压电路设计3.3V ────┬───[R110kΩ]───┬─── GND │ │ [MCP4017] [PB14 ADC输入]电压计算公式为Vout 3.3V × (R_MCP4017) / (R_MCP4017 10kΩ)2. 开发环境准备与基础配置2.1 CubeMX工程配置使用STM32CubeMX进行基础外设配置I²C配置使用PB6(SCL)和PB7(SDA)模式选择I2C时钟速度配置为100kHzADC配置选择PB14作为ADC输入通道分辨率设置为12位(0-4095)采样时间设置为2.5个时钟周期GPIO配置按键引脚设置为输入模式启用上拉LCD接口根据实际连接配置2.2 关键代码结构工程应包含以下核心文件/Project ├── Core/ │ ├── Src/ │ │ ├── main.c │ │ ├── i2c_hal.c # I²C底层驱动 │ │ └── adc.c # ADC采集处理 ├── Drivers/ └── Inc/ ├── i2c_hal.h └── mcp4017_at24c02.h # 设备专用驱动3. I²C设备驱动实现3.1 AT24C02 EEPROM驱动3.1.1 字节写入操作void EEPROM_WriteByte(uint8_t addr, uint8_t data) { I2C_Start(); I2C_SendByte(0xA0); // 器件写地址 I2C_WaitAck(); I2C_SendByte(addr); // 内存地址 I2C_WaitAck(); I2C_SendByte(data); // 写入数据 I2C_WaitAck(); I2C_Stop(); HAL_Delay(5); // 写入周期等待 }注意AT24C02每次写入后需要约5ms的编程时间在此期间不会响应新的指令。3.1.2 随机读取操作uint8_t EEPROM_ReadByte(uint8_t addr) { uint8_t data; I2C_Start(); I2C_SendByte(0xA0); // 器件写地址 I2C_WaitAck(); I2C_SendByte(addr); // 要读取的地址 I2C_WaitAck(); I2C_Start(); I2C_SendByte(0xA1); // 器件读地址 I2C_WaitAck(); data I2C_ReceiveByte(); I2C_SendNotAck(); I2C_Stop(); return data; }3.2 MCP4017数字电位器驱动3.2.1 电阻值设置void MCP4017_SetResistance(uint8_t value) { // 确保值在0-127范围内 value value 0x7F; I2C_Start(); I2C_SendByte(0x5E); // 器件写地址 I2C_WaitAck(); I2C_SendByte(value); // 设置电阻值 I2C_WaitAck(); I2C_Stop(); }3.2.2 当前电阻值读取uint8_t MCP4017_GetResistance(void) { uint8_t value; I2C_Start(); I2C_SendByte(0x5F); // 器件读地址 I2C_WaitAck(); value I2C_ReceiveByte(); I2C_SendNotAck(); I2C_Stop(); return value; }4. 系统功能集成与实现4.1 按键控制逻辑设计系统使用4个按键实现不同功能按键功能描述B1设置电阻为最小值(0Ω)B2设置中间电阻值(约10.24kΩ)B3设置较大电阻值(约50.39kΩ)B4设置最大电阻值(100kΩ)并保存数据按键处理代码示例void Key_Process(void) { if(KEY0_PRESSED()) { // B1按键 MCP4017_SetResistance(0x00); LCD_ShowString(Line8, Resistance: MIN); } else if(KEY1_PRESSED()) { // B2按键 MCP4017_SetResistance(0x0D); LCD_ShowString(Line8, Resistance: MID); } else if(KEY2_PRESSED()) { // B3按键 MCP4017_SetResistance(0x40); LCD_ShowString(Line8, Resistance: HIGH); } else if(KEY3_PRESSED()) { // B4按键 MCP4017_SetResistance(0x7F); Save_Voltage_To_EEPROM(); LCD_ShowString(Line8, Data Saved!); } }4.2 电压采集与计算ADC采集和电压计算实现float Get_Voltage(void) { uint16_t adc_value; float voltage; uint8_t res_value MCP4017_GetResistance(); float resistance res_value * 787.4f; // 转换为实际电阻值(Ω) // 读取ADC值 HAL_ADC_Start(hadc1); adc_value HAL_ADC_GetValue(hadc1); // 两种计算方式验证 voltage adc_value * 3.3f / 4095.0f; // 直接ADC转换 float calc_voltage 3.3f * resistance / (resistance 10000.0f); // 理论计算 // 可在LCD上显示两种结果进行对比 return voltage; }4.3 数据存储与读取EEPROM数据存储方案设计#define VOLTAGE_ADDR 0x00 #define RESISTANCE_ADDR 0x02 void Save_Data_To_EEPROM(float voltage, uint8_t resistance) { // 将电压值转换为16位整数(放大100倍) uint16_t volt_int (uint16_t)(voltage * 100); // 分高低字节存储 EEPROM_WriteByte(VOLTAGE_ADDR, (volt_int 8) 0xFF); EEPROM_WriteByte(VOLTAGE_ADDR1, volt_int 0xFF); // 存储电阻值 EEPROM_WriteByte(RESISTANCE_ADDR, resistance); } float Read_Voltage_From_EEPROM(void) { uint8_t high_byte EEPROM_ReadByte(VOLTAGE_ADDR); uint8_t low_byte EEPROM_ReadByte(VOLTAGE_ADDR1); uint16_t volt_int (high_byte 8) | low_byte; return volt_int / 100.0f; }5. 系统调试与优化技巧5.1 I²C通信常见问题排查调试I²C设备时可能遇到的问题及解决方法设备无响应检查设备地址是否正确确认上拉电阻已连接(通常4.7kΩ)用逻辑分析仪捕捉I²C波形数据错误检查时钟速度是否过快确认应答信号处理正确验证数据传输的MSB/LSB顺序AT24C02写入失败确保写入周期等待时间足够(5ms)检查页写入边界(不超过页大小)5.2 精度提升方法提高系统测量精度的几种策略ADC校准HAL_ADCEx_Calibration_Start(hadc1, ADC_SINGLE_ENDED);多次采样平均#define SAMPLE_TIMES 16 uint32_t adc_sum 0; for(int i0; iSAMPLE_TIMES; i) { HAL_ADC_Start(hadc1); adc_sum HAL_ADC_GetValue(hadc1); } uint16_t adc_avg adc_sum / SAMPLE_TIMES;温度补偿 根据环境温度调整参考电压值5.3 功耗优化建议针对电池供电应用的优化措施降低工作频率设置系统时钟为最低适用频率降低I²C总线速度间歇工作模式void Enter_LowPower_Mode(void) { // 配置外设进入低功耗状态 HAL_ADC_Stop(hadc1); HAL_I2C_DeInit(hi2c1); // 进入STOP模式等待按键中断唤醒 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 唤醒后重新初始化 SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); MX_ADC1_Init(); }动态电压调节HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE2);6. 功能扩展与进阶应用6.1 多组数据存储方案扩展EEPROM存储功能支持多组数据存储#define MAX_RECORDS 10 #define RECORD_SIZE 3 // 2字节电压 1字节电阻 void Save_Record(uint8_t index, float voltage, uint8_t resistance) { if(index MAX_RECORDS) return; uint16_t addr index * RECORD_SIZE; uint16_t volt_int (uint16_t)(voltage * 100); EEPROM_WriteByte(addr, (volt_int 8) 0xFF); EEPROM_WriteByte(addr1, volt_int 0xFF); EEPROM_WriteByte(addr2, resistance); } void Read_Record(uint8_t index, float *voltage, uint8_t *resistance) { if(index MAX_RECORDS) return; uint16_t addr index * RECORD_SIZE; uint8_t high_byte EEPROM_ReadByte(addr); uint8_t low_byte EEPROM_ReadByte(addr1); *voltage ((high_byte 8) | low_byte) / 100.0f; *resistance EEPROM_ReadByte(addr2); }6.2 上位机通信接口通过UART添加与PC的通信功能void Send_Data_To_PC(float voltage, uint8_t resistance) { char buffer[64]; int len snprintf(buffer, sizeof(buffer), Voltage: %.2fV, Resistance: %d (%.2fkΩ)\r\n, voltage, resistance, resistance*0.7874); HAL_UART_Transmit(huart1, (uint8_t*)buffer, len, HAL_MAX_DELAY); } void USART1_IRQHandler(void) { if(__HAL_UART_GET_FLAG(huart1, UART_FLAG_RXNE)) { uint8_t cmd huart1.Instance-RDR; switch(cmd) { case R: // 读取当前数据 Send_Current_Data(); break; case S: // 存储当前数据 Save_Current_Data(); break; // 其他命令处理... } } }6.3 电阻-温度特性应用利用MCP4017的线性特性实现温度传感器模拟// 假设使用NTC热敏电阻特性 float Simulate_NTC_Temperature(uint8_t digi_res) { const float B 3950.0; // B值 const float R25 10000.0; // 25℃时的电阻值 float R digi_res * 787.4f; float steinhart; steinhart R / R25; // (R/Ro) steinhart log(steinhart); // ln(R/Ro) steinhart / B; // 1/B * ln(R/Ro) steinhart 1.0 / (25.0 273.15); // (1/To) steinhart 1.0 / steinhart; // 倒数 steinhart - 273.15; // 转换为℃ return steinhart; }在实际项目中这个系统框架可以扩展为完整的物联网传感节点通过增加无线通信模块将采集数据上传到云平台。我曾在一个环境监测项目中采用类似架构通过Wi-Fi模块定期上传温度和湿度数据系统稳定运行了两年多期间仅更换过一次电池。