本文采用GD32F103C8T6开发板的标准库GD32F10x_Firmware_Library_V2.6.0,利用标准库的硬件I2C通信,实现了mcp4728的DAC转换,每个通道实现了0~VDD的电压输出。

本程序实现了GD32的硬件I2C通信,比软件模拟快速的多;

处理了通信失败的异常;

添加了超时通信,增强了程序的稳定性。

源码下载地址:https://gitee.com/gd_15/gd32f103c8t6-i2c-mcp4728.git

链接: https://pan.baidu.com/s/1v5W78oMLifZo0g9V24T-jw?pwd=t3q8 提取码: t3q8 

程序使用keil打开路径GD32F10x_Firmware_Library_V2.6.0\ Template\ Keil5_project  \Project.uvprojx

在官网下载了GD32F10x_Firmware_Library_V2.6.0标准库。

然后直接在标准库里面添加文件夹mcp4728_driver,这个文件夹里面包含了开发板PB6和PB7针脚的初始化,程序代码如下:

//mcp4728_i2c_driver.h

#ifndef __I2C_DRIVER_H
#define __I2C_DRIVER_H

#include "gd32f10x.h"

#define I2C_TIMEOUT     0xFFFF

typedef enum {
    I2C_SUCCESS,
    I2C_ERROR,
    I2C_TIMEOUT_ERROR
} i2c_status;

i2c_status i2c_master_init(void);
i2c_status i2c_write_byte(uint8_t device_address, uint8_t reg_address, uint8_t data);
i2c_status i2c_write_buffer(uint8_t device_address, uint8_t *buffer, uint16_t length);

#endif
//mcp4728_i2c_driver.c


#include "mcp4728_i2c_driver.h"
#include "gd32f10x.h"

i2c_status i2c_master_init(void)
{
    /* 使能外设时钟 */
    rcu_periph_clock_enable(RCU_I2C0);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_AF);
    
    /* 配置GPIO引脚 */
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6);  // SCL
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_7);  // SDA
    
    /* 配置I2C参数 */
    i2c_clock_config(I2C0, 100000, I2C_DTCY_2);         // 设置波特率100kHz
    i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);
    i2c_enable(I2C0);
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
    
    return I2C_SUCCESS;
}

i2c_status i2c_write_byte(uint8_t device_address, uint8_t reg_address, uint8_t data)
{
    uint32_t timeout = I2C_TIMEOUT;
    
    /* 发送起始条件 */
    i2c_start_on_bus(I2C0);
    while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)){
        if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
    }
    
    /* 发送设备地址+写标志 */
    i2c_master_addressing(I2C0, device_address<<1, I2C_TRANSMITTER);
    while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)){
        if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
    }
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
    
    /* 发送寄存器地址 */
    i2c_data_transmit(I2C0, reg_address);
    while(!i2c_flag_get(I2C0, I2C_FLAG_BTC)){
        if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
    }
    
    /* 发送数据 */
    i2c_data_transmit(I2C0, data);
    while(!i2c_flag_get(I2C0, I2C_FLAG_BTC)){
        if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
    }
    
    /* 发送停止条件 */
    i2c_stop_on_bus(I2C0);
    while(I2C_CTL0(I2C0)&0x0200){
        if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
    };
    
    return I2C_SUCCESS;
}

i2c_status i2c_write_buffer(uint8_t device_address, uint8_t *buffer, uint16_t length)
{
    uint32_t timeout = I2C_TIMEOUT;
    uint16_t i;
    
    /* 发送起始条件 */
    i2c_start_on_bus(I2C0);
    while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)){
        if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
    }
    
    /* 发送设备地址+写标志 */
    i2c_master_addressing(I2C0, device_address<<1, I2C_TRANSMITTER);
    while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)){
        if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
    }
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
    
    /* 连续发送数据 */
    for(i = 0; i < length; i++){
        i2c_data_transmit(I2C0, buffer[i]);
        while(!i2c_flag_get(I2C0, I2C_FLAG_BTC)){
            if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
        }
    }
    
    /* 发送停止条件 */
    i2c_stop_on_bus(I2C0);
    while(I2C_CTL0(I2C0)&0x0200){
        if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
    };
    
    return I2C_SUCCESS;
}
//mcp4728_driver.h


#ifndef __MCP4728_DRIVER_H
#define __MCP4728_DRIVER_H

#include "gd32f10x.h"

/* MCP4728状态码定义 */
typedef enum {
    MCP4728_OK,
    MCP4728_ERROR,
    MCP4728_INVALID_PARAM,
    MCP4728_COMM_ERROR
} mcp4728_status;

/* 功耗管理类型 */
#define MCP4728_PD_NORMAL       0x00
#define MCP4728_PD_GND_1K       0x01
#define MCP4728_PD_GND_100K     0x02
#define MCP4728_PD_GND_500K     0x03

/**
 * @brief 设置单个DAC通道输出值
 * @param device_address 设备I2C地址
 * @param channel 通道编号(0-3)
 * @param value 输出数值(0-4095)
 * @return 执行结果状态
 */
mcp4728_status mcp4728_set_single_channel(uint8_t device_address, uint8_t channel, uint16_t value);

/**
 * @brief 同步设置全部四个DAC通道输出值
 * @param device_address 设备I2C地址
 * @param values 四个通道的输出数组指针
 * @return 执行结果状态
 */
mcp4728_status mcp4728_set_all_channels(uint8_t device_address, uint16_t values[4]);

/**
 * @brief 将所有通道置于指定功耗管理模式
 * @param device_address 设备I2C地址
 * @param pd_type 功耗管理模式
 * @return 执行结果状态
 */
mcp4728_status mcp4728_power_down(uint8_t device_address, uint8_t pd_type);

#endif
//mcp4728_driver.c 

#include "mcp4728_driver.h"
#include "mcp4728_i2c_driver.h"

mcp4728_status mcp4728_set_single_channel(uint8_t device_address, uint8_t channel, uint16_t value)
{
    uint8_t cmd_byte;
    uint8_t data_bytes[2];
    
    if(channel > 3 || value > 4095){
        return MCP4728_INVALID_PARAM;
    }
    
    // 构造命令字节:快速写模式(0x00)+通道选择
    cmd_byte = 0x58 | (channel << 1); 
    
    // 数据高4位和低8位分别放在两个字节中
    data_bytes[0] = (value >> 8) & 0x0F;
    data_bytes[1] = value & 0xFF;
    
    if(i2c_write_buffer(device_address, &cmd_byte, 1) != I2C_SUCCESS){
        return MCP4728_COMM_ERROR;
    }
    
    if(i2c_write_buffer(device_address, data_bytes, 2) != I2C_SUCCESS){
        return MCP4728_COMM_ERROR;
    }
    
    return MCP4728_OK;
}

mcp4728_status mcp4728_set_all_channels(uint8_t device_address, uint16_t values[4])
{
    uint8_t buffer[9]; // 命令+4通道各2字节数据
    int i = 0, j = 0;
    
    /* 第一字节为序列号写入命令
     C2=0,C1=1,C0=0,W1=1,W2=0,DAC1=0,DAC0=0,UDAC=0
     SEQ WRITE command
    */
    buffer[j++] = 0x50; 
    
    // 四个通道的数据依次放入缓冲区
    for(i = 0; i < 4; i++) {
        if(values[i] > 4095){
            return MCP4728_INVALID_PARAM;
        }
        buffer[j++] = (values[i] >> 8) & 0x0F;  // 高4位
        buffer[j++] = values[i] & 0xFF;         // 低8位
    }
    
    if(i2c_write_buffer(device_address, buffer, 9) != I2C_SUCCESS){
        return MCP4728_COMM_ERROR;
    }
    
    return MCP4728_OK;
}

mcp4728_status mcp4728_power_down(uint8_t device_address, uint8_t pd_type)
{
    uint8_t buffer[9];
    int i;
    
    if(pd_type > 3){
        return MCP4728_INVALID_PARAM;
    }
    
    buffer[0] = 0x50; // SEQ WRITE command
    
    for(i = 1; i < 9; i += 2){
        buffer[i] = ((pd_type & 0x03) << 4);  // PD bits in upper nibble of first byte
        buffer[i+1] = 0x00;                   // Second byte is zero when powering down
    }
    
    if(i2c_write_buffer(device_address, buffer, 9) != I2C_SUCCESS){
        return MCP4728_COMM_ERROR;
    }
    
    return MCP4728_OK;
}

然后在文件夹GD32F10x_Firmware_Library_V2.6.0\Template目录下的main.c文件中修改如下

//main.c

#include "mcp4728_driver.h"
#include "mcp4728_i2c_driver.h"
#define MCP4728_ADDRESS 0x60

void mcp4728_process(void)
{
    uint16_t dac_values[4] = {1023, 2047, 3071, 4095}; // example: 0~4095
    
   
    if(i2c_master_init() != I2C_SUCCESS){
        //error_handler();
    }
    
    while(1){
        if(mcp4728_set_all_channels(MCP4728_ADDRESS, dac_values) == MCP4728_OK){
            printf("DAC successfully\n");
        }else{
            printf("Failed  DAC values\n");
        }
        
        dac_values[0] = (dac_values[0]+10)%4096;
        dac_values[1] = (dac_values[1]+10)%4096;
        dac_values[2] = (dac_values[2]+10)%4096;
        dac_values[3] = (dac_values[3]+10)%4096;
        
        delay_1ms(500);
    }
}

int main(void)
{
    systick_config();

    gd_eval_com_init(EVAL_COM0);
    mcp4728_process();
}

弄好后直接选择目录GD32F10x_Firmware_Library_V2.6.0\  Template\ Keil5_project 下的Project.uvprojx,编译即可。

介绍以下mcp4728的功能

12bit的精度,通信频率支持 I2C的标准频率100KHZ,同时支持400KHZ,还支持1.7MHZ和3.4MHZ,输出的模拟电压可达5.5伏。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐