STM32控制PCF8591数字模拟转换器程序

一、硬件连接和头文件定义

1.1 硬件连接

PCF8591引脚 → STM32引脚
1 (AIN0)  → 外部模拟输入0(可选)
2 (AIN1)  → 外部模拟输入1(可选)
3 (AIN2)  → 外部模拟输入2(可选)
4 (AIN3)  → 外部模拟输入3(可选)
5 (A0)    → GND(地址引脚0)
6 (A1)    → GND(地址引脚1)
7 (A2)    → GND(地址引脚2)
8 (VSS)   → GND
9 (SDA)   → PB7(I2C1_SDA)
10 (SCL)  → PB6(I2C1_SCL)
11 (OSC)  → NC(悬空)
12 (EXT)  → NC(悬空)
13 (AGND) → GND
14 (VREF) → 3.3V(参考电压)
15 (AOUT) → 模拟输出
16 (VDD)  → 3.3V

1.2 头文件定义 (pcf8591.h)

#ifndef __PCF8591_H
#define __PCF8591_H

#include "stm32f1xx_hal.h"
#include <stdint.h>
#include <string.h>

// PCF8591默认地址(A0=A1=A2=GND)
#define PCF8591_ADDR_WRITE   0x90  // 写地址
#define PCF8591_ADDR_READ    0x91  // 读地址

// 控制寄存器位定义
#define PCF8591_CTRL_ANALOG_OUT_ENABLE 0x40  // 模拟输出使能
#define PCF8591_CTRL_AUTO_INCREMENT    0x04  // 自动增量
#define PCF8591_CTRL_CHANNEL_MASK      0x03  // 通道选择掩码

// 通道定义
typedef enum {
    PCF8591_CHANNEL_0 = 0x00,  // 通道0
    PCF8591_CHANNEL_1 = 0x01,  // 通道1
    PCF8591_CHANNEL_2 = 0x02,  // 通道2
    PCF8591_CHANNEL_3 = 0x03,  // 通道3
} PCF8591_Channel;

// 输入模式定义
typedef enum {
    PCF8591_MODE_SINGLE_ENDED = 0x00,       // 单端输入
    PCF8591_MODE_DIFF_0_1     = 0x10,       // 差分输入0-1
    PCF8591_MODE_DIFF_2_3     = 0x20,       // 差分输入2-3
    PCF8591_MODE_SINGLE_MIXED = 0x30,       // 单端和差分混合
} PCF8591_InputMode;

// 工作模式定义
typedef enum {
    PCF8591_MODE_NORMAL = 0x00,      // 正常模式
    PCF8591_MODE_THREE_DIFF = 0x10,  // 三个差分输入
    PCF8591_MODE_SINGLE_DIFF = 0x20, // 一个单端+一个差分
    PCF8591_MODE_TWO_DIFF    = 0x30, // 两个差分输入
} PCF8591_WorkMode;

// PCF8591结构体
typedef struct {
    I2C_HandleTypeDef* hi2c;      // I2C句柄
    uint8_t dev_addr;             // 设备地址
    float vref;                   // 参考电压(V)
    uint8_t auto_increment;       // 自动增量标志
    uint8_t channel_count;        // 通道数量
    PCF8591_Channel current_channel;  // 当前通道
} PCF8591_HandleTypeDef;

// 函数声明
// 初始化函数
void PCF8591_Init(PCF8591_HandleTypeDef* hpcf, I2C_HandleTypeDef* hi2c, float vref);
uint8_t PCF8591_IsReady(PCF8591_HandleTypeDef* hpcf);

// 模拟输出函数
void PCF8591_SetAnalogOutput(PCF8591_HandleTypeDef* hpcf, uint8_t value);
void PCF8591_SetVoltage(PCF8591_HandleTypeDef* hpcf, float voltage);
void PCF8591_SetAnalogOutputPercent(PCF8591_HandleTypeDef* hpcf, float percent);

// 模拟输入函数
uint8_t PCF8591_ReadADC(PCF8591_HandleTypeDef* hpcf, PCF8591_Channel channel);
float PCF8591_ReadVoltage(PCF8591_HandleTypeDef* hpcf, PCF8591_Channel channel);
float PCF8591_ReadADCPercent(PCF8591_HandleTypeDef* hpcf, PCF8591_Channel channel);

// 多通道读取
void PCF8591_ReadAllChannels(PCF8591_HandleTypeDef* hpcf, uint8_t* data);
void PCF8591_ReadAllVoltages(PCF8591_HandleTypeDef* hpcf, float* voltages);

// 高级功能
void PCF8591_SetInputMode(PCF8591_HandleTypeDef* hpcf, PCF8591_InputMode mode);
void PCF8591_SetWorkMode(PCF8591_HandleTypeDef* hpcf, PCF8591_WorkMode mode);
void PCF8591_EnableAutoIncrement(PCF8591_HandleTypeDef* hpcf, uint8_t enable);

// 波形生成函数
void PCF8591_GenerateSineWave(PCF8591_HandleTypeDef* hpcf, float freq, float amplitude, uint16_t samples);
void PCF8591_GenerateSquareWave(PCF8591_HandleTypeDef* hpcf, float freq, float amplitude, uint16_t samples);
void PCF8591_GenerateTriangleWave(PCF8591_HandleTypeDef* hpcf, float freq, float amplitude, uint16_t samples);
void PCF8591_GenerateSawtoothWave(PCF8591_HandleTypeDef* hpcf, float freq, float amplitude, uint16_t samples);

// DAC校准函数
void PCF8591_CalibrateDAC(PCF8591_HandleTypeDef* hpcf);
void PCF8591_SetCalibration(PCF8591_HandleTypeDef* hpcf, float gain, float offset);

// 调试和测试
void PCF8591_SelfTest(PCF8591_HandleTypeDef* hpcf);
void PCF8591_PrintStatus(PCF8591_HandleTypeDef* hpcf);

#endif /* __PCF8591_H */

二、核心驱动实现

2.1 初始化函数 (pcf8591.c)

#include "pcf8591.h"
#include <math.h>

// 内部变量
typedef struct {
    float gain_correction;   // 增益校正
    float offset_correction; // 偏置校正
} PCF8591_Calibration;

static PCF8591_Calibration calib = {1.0f, 0.0f};

// 初始化PCF8591
void PCF8591_Init(PCF8591_HandleTypeDef* hpcf, I2C_HandleTypeDef* hi2c, float vref) {
    if (hpcf == NULL || hi2c == NULL) return;
    
    hpcf->hi2c = hi2c;
    hpcf->dev_addr = PCF8591_ADDR_WRITE;
    hpcf->vref = vref;
    hpcf->auto_increment = 0;
    hpcf->channel_count = 4;
    hpcf->current_channel = PCF8591_CHANNEL_0;
    
    // 设置默认模式:单端输入,通道0
    uint8_t ctrl_byte = PCF8591_CTRL_ANALOG_OUT_ENABLE;  // 使能模拟输出
    
    HAL_I2C_Master_Transmit(hpcf->hi2c, hpcf->dev_addr, &ctrl_byte, 1, 100);
    
    // 初始化输出为0
    PCF8591_SetAnalogOutput(hpcf, 0);
}

// 检查设备是否就绪
uint8_t PCF8591_IsReady(PCF8591_HandleTypeDef* hpcf) {
    if (hpcf == NULL || hpcf->hi2c == NULL) return 0;
    
    HAL_StatusTypeDef status = HAL_I2C_IsDeviceReady(hpcf->hi2c, hpcf->dev_addr, 1, 10);
    return (status == HAL_OK);
}

2.2 模拟输出函数

// 设置模拟输出(原始值 0-255)
void PCF8591_SetAnalogOutput(PCF8591_HandleTypeDef* hpcf, uint8_t value) {
    if (hpcf == NULL || hpcf->hi2c == NULL) return;
    
    // 应用校准
    int16_t calibrated_value = (int16_t)(value * calib.gain_correction + calib.offset_correction);
    
    // 限制在0-255范围内
    if (calibrated_value < 0) calibrated_value = 0;
    if (calibrated_value > 255) calibrated_value = 255;
    
    uint8_t buffer[2];
    
    // 控制字节:使能模拟输出
    buffer[0] = PCF8591_CTRL_ANALOG_OUT_ENABLE;
    
    // 数据字节
    buffer[1] = (uint8_t)calibrated_value;
    
    // 发送数据
    HAL_I2C_Master_Transmit(hpcf->hi2c, hpcf->dev_addr, buffer, 2, 100);
}

// 设置输出电压(单位:伏特)
void PCF8591_SetVoltage(PCF8591_HandleTypeDef* hpcf, float voltage) {
    if (hpcf == NULL) return;
    
    // 计算DAC值
    // DAC值 = (电压 / 参考电压) * 255
    float dac_value_f = (voltage / hpcf->vref) * 255.0f;
    
    // 限制范围
    if (dac_value_f < 0.0f) dac_value_f = 0.0f;
    if (dac_value_f > 255.0f) dac_value_f = 255.0f;
    
    uint8_t dac_value = (uint8_t)dac_value_f;
    
    PCF8591_SetAnalogOutput(hpcf, dac_value);
}

// 设置输出电压百分比(0.0 - 1.0)
void PCF8591_SetAnalogOutputPercent(PCF8591_HandleTypeDef* hpcf, float percent) {
    if (hpcf == NULL) return;
    
    // 限制百分比范围
    if (percent < 0.0f) percent = 0.0f;
    if (percent > 1.0f) percent = 1.0f;
    
    uint8_t dac_value = (uint8_t)(percent * 255.0f);
    PCF8591_SetAnalogOutput(hpcf, dac_value);
}

2.3 模拟输入函数

// 读取ADC原始值(0-255)
uint8_t PCF8591_ReadADC(PCF8591_HandleTypeDef* hpcf, PCF8591_Channel channel) {
    if (hpcf == NULL || hpcf->hi2c == NULL) return 0;
    
    uint8_t buffer[2] = {0};
    uint8_t ctrl_byte = channel;  // 选择通道
    
    if (hpcf->auto_increment) {
        ctrl_byte |= PCF8591_CTRL_AUTO_INCREMENT;
    }
    
    // 发送控制字节,选择通道
    if (HAL_I2C_Master_Transmit(hpcf->hi2c, hpcf->dev_addr, &ctrl_byte, 1, 100) != HAL_OK) {
        return 0;
    }
    
    // 读取转换结果
    // 注意:PCF8591在发送控制字节后会进行一次转换
    // 需要读取两次,第一次是上一次的转换结果
    HAL_I2C_Master_Receive(hpcf->hi2c, PCF8591_ADDR_READ, buffer, 2, 100);
    
    // 返回第二次转换的结果
    return buffer[1];
}

// 读取输入电压(单位:伏特)
float PCF8591_ReadVoltage(PCF8591_HandleTypeDef* hpcf, PCF8591_Channel channel) {
    if (hpcf == NULL) return 0.0f;
    
    uint8_t adc_value = PCF8591_ReadADC(hpcf, channel);
    
    // 电压 = (ADC值 / 255) * 参考电压
    float voltage = ((float)adc_value / 255.0f) * hpcf->vref;
    
    return voltage;
}

// 读取输入电压百分比(0.0 - 1.0)
float PCF8591_ReadADCPercent(PCF8591_HandleTypeDef* hpcf, PCF8591_Channel channel) {
    if (hpcf == NULL) return 0.0f;
    
    uint8_t adc_value = PCF8591_ReadADC(hpcf, channel);
    
    return (float)adc_value / 255.0f;
}

2.4 多通道读取函数

// 读取所有通道的原始值
void PCF8591_ReadAllChannels(PCF8591_HandleTypeDef* hpcf, uint8_t* data) {
    if (hpcf == NULL || hpcf->hi2c == NULL || data == NULL) return;
    
    uint8_t buffer[5] = {0};  // 控制字节 + 4个通道数据
    uint8_t ctrl_byte = PCF8591_CTRL_AUTO_INCREMENT;  // 使能自动增量
    
    // 发送控制字节
    if (HAL_I2C_Master_Transmit(hpcf->hi2c, hpcf->dev_addr, &ctrl_byte, 1, 100) != HAL_OK) {
        return;
    }
    
    // 读取5个字节(包含一个无效字节)
    HAL_I2C_Master_Receive(hpcf->hi2c, PCF8591_ADDR_READ, buffer, 5, 100);
    
    // 保存4个通道的数据(跳过第一个无效字节)
    for (int i = 0; i < 4; i++) {
        data[i] = buffer[i + 1];
    }
}

// 读取所有通道的电压值
void PCF8591_ReadAllVoltages(PCF8591_HandleTypeDef* hpcf, float* voltages) {
    if (hpcf == NULL || voltages == NULL) return;
    
    uint8_t adc_values[4];
    PCF8591_ReadAllChannels(hpcf, adc_values);
    
    for (int i = 0; i < 4; i++) {
        voltages[i] = ((float)adc_values[i] / 255.0f) * hpcf->vref;
    }
}

三、波形生成功能

3.1 波形生成函数 (pcf8591_waveform.c)

#include "pcf8591.h"
#include <math.h>

// 生成正弦波
void PCF8591_GenerateSineWave(PCF8591_HandleTypeDef* hpcf, float freq, float amplitude, uint16_t samples) {
    if (hpcf == NULL || freq <= 0 || samples == 0) return;
    
    // 计算周期
    float period_us = 1000000.0f / freq;  // 周期(微秒)
    float sample_interval_us = period_us / samples;
    
    // 计算幅度系数
    float amplitude_factor = amplitude / hpcf->vref;
    if (amplitude_factor > 0.5f) amplitude_factor = 0.5f;  // 限制幅度
    
    for (uint16_t i = 0; i < samples; i++) {
        // 计算正弦波值
        float angle = 2.0f * 3.1415926f * (float)i / (float)samples;
        float sine_value = 0.5f + amplitude_factor * sinf(angle);  // 偏移到0-1范围
        
        // 转换为DAC值
        uint8_t dac_value = (uint8_t)(sine_value * 255.0f);
        
        // 设置输出
        PCF8591_SetAnalogOutput(hpcf, dac_value);
        
        // 延时
        Delay_us((uint32_t)sample_interval_us);
    }
}

// 生成方波
void PCF8591_GenerateSquareWave(PCF8591_HandleTypeDef* hpcf, float freq, float amplitude, uint16_t samples) {
    if (hpcf == NULL || freq <= 0 || samples == 0) return;
    
    float period_us = 1000000.0f / freq;
    float half_period_us = period_us / 2.0f;
    
    // 计算DAC值
    float amplitude_factor = amplitude / hpcf->vref;
    if (amplitude_factor > 0.5f) amplitude_factor = 0.5f;
    
    uint8_t high_value = (uint8_t)((0.5f + amplitude_factor) * 255.0f);
    uint8_t low_value = (uint8_t)((0.5f - amplitude_factor) * 255.0f);
    
    while (1) {  // 持续生成
        // 高电平
        PCF8591_SetAnalogOutput(hpcf, high_value);
        Delay_us((uint32_t)half_period_us);
        
        // 低电平
        PCF8591_SetAnalogOutput(hpcf, low_value);
        Delay_us((uint32_t)half_period_us);
    }
}

// 生成三角波
void PCF8591_GenerateTriangleWave(PCF8591_HandleTypeDef* hpcf, float freq, float amplitude, uint16_t samples) {
    if (hpcf == NULL || freq <= 0 || samples == 0) return;
    
    float period_us = 1000000.0f / freq;
    float sample_interval_us = period_us / samples;
    float amplitude_factor = amplitude / hpcf->vref;
    
    if (amplitude_factor > 0.5f) amplitude_factor = 0.5f;
    
    while (1) {  // 持续生成
        // 上升沿
        for (uint16_t i = 0; i < samples / 2; i++) {
            float value = 0.5f - amplitude_factor + (2.0f * amplitude_factor * i) / (samples / 2.0f);
            uint8_t dac_value = (uint8_t)(value * 255.0f);
            PCF8591_SetAnalogOutput(hpcf, dac_value);
            Delay_us((uint32_t)sample_interval_us);
        }
        
        // 下降沿
        for (uint16_t i = 0; i < samples / 2; i++) {
            float value = 0.5f + amplitude_factor - (2.0f * amplitude_factor * i) / (samples / 2.0f);
            uint8_t dac_value = (uint8_t)(value * 255.0f);
            PCF8591_SetAnalogOutput(hpcf, dac_value);
            Delay_us((uint32_t)sample_interval_us);
        }
    }
}

// 生成锯齿波
void PCF8591_GenerateSawtoothWave(PCF8591_HandleTypeDef* hpcf, float freq, float amplitude, uint16_t samples) {
    if (hpcf == NULL || freq <= 0 || samples == 0) return;
    
    float period_us = 1000000.0f / freq;
    float sample_interval_us = period_us / samples;
    float amplitude_factor = amplitude / hpcf->vref;
    
    if (amplitude_factor > 0.5f) amplitude_factor = 0.5f;
    
    while (1) {  // 持续生成
        for (uint16_t i = 0; i < samples; i++) {
            float value = 0.5f - amplitude_factor + (2.0f * amplitude_factor * i) / samples;
            uint8_t dac_value = (uint8_t)(value * 255.0f);
            PCF8591_SetAnalogOutput(hpcf, dac_value);
            Delay_us((uint32_t)sample_interval_us);
        }
    }
}

四、校准和高级功能

4.1 校准函数 (pcf8591_calibration.c)

#include "pcf8591.h"

// DAC校准
void PCF8591_CalibrateDAC(PCF8591_HandleTypeDef* hpcf) {
    if (hpcf == NULL) return;
    
    // 测量实际输出电压
    // 需要外部高精度电压表连接到AOUT
    
    // 步骤1:输出0V
    PCF8591_SetAnalogOutput(hpcf, 0);
    // 这里应该读取实际输出电压(需要外部测量)
    // float measured_0v = 0.0f;  // 从外部设备读取
    
    // 步骤2:输出满量程
    PCF8591_SetAnalogOutput(hpcf, 255);
    // float measured_255 = hpcf->vref;  // 从外部设备读取
    
    // 计算增益和偏置误差
    // float ideal_slope = hpcf->vref / 255.0f;
    // float actual_slope = (measured_255 - measured_0v) / 255.0f;
    // calib.gain_correction = ideal_slope / actual_slope;
    // calib.offset_correction = -measured_0v / actual_slope;
}

// 设置校准参数
void PCF8591_SetCalibration(PCF8591_HandleTypeDef* hpcf, float gain, float offset) {
    calib.gain_correction = gain;
    calib.offset_correction = offset;
}

// 设置输入模式
void PCF8591_SetInputMode(PCF8591_HandleTypeDef* hpcf, PCF8591_InputMode mode) {
    if (hpcf == NULL) return;
    
    // 读取当前控制寄存器
    uint8_t ctrl_byte = 0;
    // 注意:PCF8591没有直接的寄存器读取功能
    // 通常我们会保存当前的控制字节状态
    
    // 设置输入模式
    ctrl_byte = mode | PCF8591_CTRL_ANALOG_OUT_ENABLE;
    
    HAL_I2C_Master_Transmit(hpcf->hi2c, hpcf->dev_addr, &ctrl_byte, 1, 100);
}

// 设置工作模式
void PCF8591_SetWorkMode(PCF8591_HandleTypeDef* hpcf, PCF8591_WorkMode mode) {
    if (hpcf == NULL) return;
    
    uint8_t ctrl_byte = mode | PCF8591_CTRL_ANALOG_OUT_ENABLE;
    HAL_I2C_Master_Transmit(hpcf->hi2c, hpcf->dev_addr, &ctrl_byte, 1, 100);
}

// 使能/禁用自动增量
void PCF8591_EnableAutoIncrement(PCF8591_HandleTypeDef* hpcf, uint8_t enable) {
    if (hpcf == NULL) return;
    
    hpcf->auto_increment = enable;
}

五、主程序示例

5.1 基本输出示例 (main_basic.c)

#include "main.h"
#include "pcf8591.h"

// 全局变量
I2C_HandleTypeDef hi2c1;
PCF8591_HandleTypeDef hpcf;

// 系统时钟配置
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
    // 配置HSE
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);
    
    // 配置系统时钟
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                  |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

// GPIO初始化
static void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 使能GPIO时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    // 配置I2C引脚
    GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

// I2C初始化
static void MX_I2C1_Init(void) {
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 100000;  // 100kHz
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c1.Init.OwnAddress1 = 0;
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c1.Init.OwnAddress2 = 0;
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    HAL_I2C_Init(&hi2c1);
}

// 串口初始化(调试用)
void MX_USART1_UART_Init(void) {
    // 串口1初始化代码...
}

// 延时函数
void Delay_ms(uint32_t ms) {
    HAL_Delay(ms);
}

void Delay_us(uint32_t us) {
    uint32_t delay = us * (SystemCoreClock / 1000000) / 5;
    while(delay--) {
        __NOP();
    }
}

// 示例1:基本输出电压控制
void Example_BasicOutput(void) {
    printf("PCF8591 Basic Output Test\n");
    printf("==========================\n");
    
    // 初始化PCF8591
    PCF8591_Init(&hpcf, &hi2c1, 3.3f);  // 参考电压3.3V
    
    if (PCF8591_IsReady(&hpcf)) {
        printf("PCF8591 Initialized Successfully!\n");
    } else {
        printf("PCF8591 Initialization Failed!\n");
        return;
    }
    
    // 测试输出电压
    printf("\nTesting Output Voltages:\n");
    
    // 输出0V
    PCF8591_SetVoltage(&hpcf, 0.0f);
    printf("Output: 0.0V\n");
    Delay_ms(1000);
    
    // 输出1.65V
    PCF8591_SetVoltage(&hpcf, 1.65f);
    printf("Output: 1.65V\n");
    Delay_ms(1000);
    
    // 输出3.3V
    PCF8591_SetVoltage(&hpcf, 3.3f);
    printf("Output: 3.3V\n");
    Delay_ms(1000);
    
    // 输出斜坡电压
    printf("\nGenerating Ramp Voltage...\n");
    for (uint8_t i = 0; i <= 255; i++) {
        PCF8591_SetAnalogOutput(&hpcf, i);
        Delay_ms(10);
    }
}

// 示例2:读取模拟输入
void Example_ReadInputs(void) {
    printf("PCF8591 Analog Input Test\n");
    printf("==========================\n");
    
    // 读取所有通道
    uint8_t adc_values[4];
    float voltages[4];
    
    while (1) {
        PCF8591_ReadAllChannels(&hpcf, adc_values);
        PCF8591_ReadAllVoltages(&hpcf, voltages);
        
        printf("ADC Values: ");
        for (int i = 0; i < 4; i++) {
            printf("CH%d: %3d ", i, adc_values[i]);
        }
        printf("\n");
        
        printf("Voltages:   ");
        for (int i = 0; i < 4; i++) {
            printf("CH%d: %.3fV ", i, voltages[i]);
        }
        printf("\n\n");
        
        Delay_ms(500);
    }
}

// 示例3:生成波形
void Example_GenerateWaveform(void) {
    printf("PCF8591 Waveform Generation\n");
    printf("===========================\n");
    
    // 生成正弦波
    printf("Generating Sine Wave (1kHz, 1.5Vpp)\n");
    PCF8591_GenerateSineWave(&hpcf, 1000.0f, 1.5f, 100);
    
    // 生成方波
    printf("Generating Square Wave (500Hz, 2Vpp)\n");
    PCF8591_GenerateSquareWave(&hpcf, 500.0f, 2.0f, 100);
}

int main(void) {
    // HAL库初始化
    HAL_Init();
    
    // 系统时钟配置
    SystemClock_Config();
    
    // 外设初始化
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_USART1_UART_Init();
    
    printf("STM32 PCF8591 Control Program\n");
    printf("==============================\n\n");
    
    // 运行示例
    Example_BasicOutput();
    // Example_ReadInputs();
    // Example_GenerateWaveform();
    
    return 0;
}

六、高级应用示例

6.1 电压监控和报警系统

// 电压监控结构体
typedef struct {
    float lower_threshold;   // 下限阈值
    float upper_threshold;   // 上限阈值
    uint8_t alarm_enabled;   // 报警使能
    void (*alarm_callback)(uint8_t channel, float voltage);  // 报警回调
} VoltageMonitor;

VoltageMonitor monitors[4];

// 初始化电压监控
void VoltageMonitor_Init(void) {
    for (int i = 0; i < 4; i++) {
        monitors[i].lower_threshold = 0.5f;
        monitors[i].upper_threshold = 2.5f;
        monitors[i].alarm_enabled = 1;
        monitors[i].alarm_callback = NULL;
    }
}

// 电压监控任务
void VoltageMonitor_Task(PCF8591_HandleTypeDef* hpcf) {
    float voltages[4];
    
    // 读取所有通道电压
    PCF8591_ReadAllVoltages(hpcf, voltages);
    
    // 检查每个通道
    for (int i = 0; i < 4; i++) {
        if (monitors[i].alarm_enabled) {
            if (voltages[i] < monitors[i].lower_threshold) {
                // 低于下限阈值
                printf("ALARM: CH%d voltage LOW: %.3fV\n", i, voltages[i]);
                if (monitors[i].alarm_callback) {
                    monitors[i].alarm_callback(i, voltages[i]);
                }
            } else if (voltages[i] > monitors[i].upper_threshold) {
                // 高于上限阈值
                printf("ALARM: CH%d voltage HIGH: %.3fV\n", i, voltages[i]);
                if (monitors[i].alarm_callback) {
                    monitors[i].alarm_callback(i, voltages[i]);
                }
            }
        }
    }
}

// 报警回调示例
void Alarm_Callback(uint8_t channel, float voltage) {
    // 可以控制LED闪烁、蜂鸣器报警等
    printf("Channel %d alarm: %.2fV\n", channel, voltage);
}

6.2 PID电压控制

// PID控制器结构体
typedef struct {
    float kp;           // 比例系数
    float ki;           // 积分系数
    float kd;           // 微分系数
    float setpoint;     // 设定点
    float integral;     // 积分项
    float prev_error;   // 上次误差
    float output_min;   // 输出最小值
    float output_max;   // 输出最大值
} PIDController;

// 初始化PID控制器
void PID_Init(PIDController* pid, float kp, float ki, float kd, float setpoint) {
    pid->kp = kp;
    pid->ki = ki;
    pid->kd = kd;
    pid->setpoint = setpoint;
    pid->integral = 0.0f;
    pid->prev_error = 0.0f;
    pid->output_min = 0.0f;
    pid->output_max = 3.3f;
}

// PID计算
float PID_Calculate(PIDController* pid, float measured, float dt) {
    float error = pid->setpoint - measured;
    
    // 比例项
    float proportional = pid->kp * error;
    
    // 积分项
    pid->integral += error * dt;
    float integral = pid->ki * pid->integral;
    
    // 微分项
    float derivative = pid->kd * (error - pid->prev_error) / dt;
    pid->prev_error = error;
    
    // 计算输出
    float output = proportional + integral + derivative;
    
    // 输出限幅
    if (output < pid->output_min) output = pid->output_min;
    if (output > pid->output_max) output = pid->output_max;
    
    return output;
}

// PID电压控制示例
void Example_PIDControl(void) {
    PIDController pid;
    PID_Init(&pid, 1.0f, 0.1f, 0.01f, 2.0f);  // 目标电压2.0V
    
    float measured_voltage = 0.0f;
    float control_voltage = 0.0f;
    
    while (1) {
        // 读取当前电压(通道0)
        measured_voltage = PCF8591_ReadVoltage(&hpcf, PCF8591_CHANNEL_0);
        
        // 计算PID输出
        control_voltage = PID_Calculate(&pid, measured_voltage, 0.1f);  // dt=0.1s
        
        // 设置输出电压
        PCF8591_SetVoltage(&hpcf, control_voltage);
        
        printf("Measured: %.3fV, Output: %.3fV, Error: %.3fV\n", 
               measured_voltage, control_voltage, pid.setpoint - measured_voltage);
        
        Delay_ms(100);
    }
}

七、调试和测试功能

7.1 自检和状态显示

// 自检函数
void PCF8591_SelfTest(PCF8591_HandleTypeDef* hpcf) {
    printf("PCF8591 Self Test\n");
    printf("=================\n");
    
    // 检查设备是否就绪
    if (PCF8591_IsReady(hpcf)) {
        printf("✓ I2C Communication: OK\n");
    } else {
        printf("✗ I2C Communication: FAILED\n");
        return;
    }
    
    // 测试DAC输出
    printf("\nTesting DAC Output...\n");
    
    // 测试几个关键点
    float test_voltages[] = {0.0f, 0.825f, 1.65f, 2.475f, 3.3f};
    
    for (int i = 0; i < 5; i++) {
        PCF8591_SetVoltage(hpcf, test_voltages[i]);
        printf("Set Voltage: %.3fV\n", test_voltages[i]);
        Delay_ms(500);
    }
    
    // 返回0V
    PCF8591_SetVoltage(hpcf, 0.0f);
    
    // 测试ADC输入(需要外部连接)
    printf("\nTesting ADC Input...\n");
    printf("Connect test voltages to AIN0-AIN3\n");
    printf("Press any key to continue...\n");
    
    // 等待按键
    while (!serial_available());
    serial_read();
    
    float voltages[4];
    PCF8591_ReadAllVoltages(hpcf, voltages);
    
    for (int i = 0; i < 4; i++) {
        printf("Channel %d: %.3fV\n", i, voltages[i]);
    }
    
    printf("\nSelf Test Complete!\n");
}

// 打印状态信息
void PCF8591_PrintStatus(PCF8591_HandleTypeDef* hpcf) {
    printf("PCF8591 Status\n");
    printf("==============\n");
    printf("I2C Address: 0x%02X\n", hpcf->dev_addr);
    printf("Reference Voltage: %.2fV\n", hpcf->vref);
    printf("Auto Increment: %s\n", hpcf->auto_increment ? "Enabled" : "Disabled");
    printf("Channel Count: %d\n", hpcf->channel_count);
    printf("Current Channel: %d\n", hpcf->current_channel);
}

八、实用工具函数

8.1 电压表功能

// 简易数字电压表
void DigitalVoltmeter(PCF8591_HandleTypeDef* hpcf, PCF8591_Channel channel) {
    printf("Digital Voltmeter - Channel %d\n", channel);
    printf("Press 'q' to quit\n");
    
    char key = 0;
    while (key != 'q') {
        float voltage = PCF8591_ReadVoltage(hpcf, channel);
        
        // 显示电压条
        printf("Voltage: %6.3fV  [", voltage);
        
        int bars = (int)(voltage * 20 / hpcf->vref);  // 20个字符宽度
        for (int i = 0; i < 20; i++) {
            if (i < bars) {
                printf("█");
            } else {
                printf(" ");
            }
        }
        printf("]\r");
        
        Delay_ms(100);
        
        // 检查按键
        if (serial_available()) {
            key = serial_read();
        }
    }
    printf("\n");
}

参考代码 stm32控制PCF8591输出电压程序 www.youwenfan.com/contentcnv/71693.html

九、项目文件结构

PCF8591_Project/
├── Core/
│   ├── Src/
│   │   ├── main.c              # 主程序
│   │   ├── stm32f1xx_it.c      # 中断服务程序
│   │   └── stm32f1xx_hal_msp.c # HAL MSP文件
│   └── Inc/
│       ├── main.h
│       ├── stm32f1xx_it.h
│       └── stm32f1xx_hal_conf.h
├── Drivers/
│   ├── STM32F1xx_HAL_Driver/
│   └── CMSIS/
├── PCF8591/
│   ├── pcf8591.c              # 主驱动文件
│   ├── pcf8591.h              # 头文件
│   ├── pcf8591_waveform.c     # 波形生成
│   └── pcf8591_calibration.c  # 校准功能
├── Middlewares/
└── README.md

十、使用注意事项

  1. 电源要求

    • VDD: 2.5V-6V
    • VREF: ≤ VDD
    • AGND和VSS必须连接在一起
  2. I2C时序

    • 最大时钟频率:100kHz(标准模式)
    • 需要上拉电阻(4.7kΩ-10kΩ)
  3. 模拟输出

    • 输出阻抗:约1kΩ
    • 建立时间:约100µs
    • 输出电流:最大3mA
  4. 模拟输入

    • 输入阻抗:极高
    • 转换时间:约100µs
    • 输入电压范围:0-VREF
  5. PCB布局

    • 模拟和数字地分开
    • VREF引脚需要去耦电容(0.1µF)
    • 信号线远离高频噪声源
posted @ 2026-05-20 11:49  yes_go  阅读(19)  评论(0)    收藏  举报