采用GD32F103C8T6开发板的硬件I2C通信实现mcp4728的DAC转换(附源码下载)
本文采用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伏。




更多推荐


所有评论(0)