DAC8760 菊花链驱动

环境:STM32F103RC,主频 72MHz(外部晶振)或64MHz(HSI)

          两块DAC8760菊花链链接,采用SPI1驱动

note:发生电流回路开路等时,ALARM脚持续低,当电流回路等正常,DAC芯片能自动恢复正常,故程序无需特殊处理。

代码实现:

//DAC8760.c

#define DAC8760_GLOBALS
#include "DAC8760.h"

static const uint8_t ORange[6] = 
{
    0, // 0~5V(OFF)
    0, // 1~5V
    1, // 0~10V
    3, // -10~10V
    6, // 0~20mA
    5, // 4~20mA
}

/******************************************************
函数名称:WriteSingle_DAC8760
函数描述:单块DAC的写函数
Calls:HAL_SPI_TransmitReceive
Called by: Init_DAC8760
输入参数:	ch_Addr:写操作地址
			ch_Data:数据
返回值:HAL status
******************************************************/					 
static HAL_StatusTypeDef WriteSingle_DAC8760(uint8_t ch_Addr, uint16_t ch_Data)
{
	HAL_StatusTypeDef status;
    uint8_t liv_Data[3]; // 0: ch_Addr  1:ch_Datadata高8位 2:ch_Datadata低8位
						 
	
	uint8_t liv_RXData[3] = {0};
	
	liv_Data[0] = ch_Addr;
	liv_Data[1] = (ch_Data & 0xff00) >> 8;
	liv_Data[2] = ch_Data & 0x00ff;
	
	DAC8760_LATCH_0;
	status = HAL_SPI_TransmitReceive(&hSPI1, liv_Data, liv_RXData, 3, TIMEOUT); 
	DAC8760_LATCH_1;	
	
	delay_us(1);
	return status;
}

/******************************************************
函数名称:Write_DAC8760
函数描述:2块DAC菊花链的写函数
Calls:HAL_SPI_TransmitReceive
Called by: EnableOutputControl、ChangeOutputRange等
输入参数:	ch1_Addr:通道1写操作地址
			ch1_Data:通道1的数据
			ch2_Addr:通道2写操作地址
			ch2_Data:通道2的数据
返回值:HAL status
******************************************************/
static HAL_StatusTypeDef Write_DAC8760(uint8_t ch1_Addr, uint16_t ch1_Data, uint8_t ch2_Addr, uint16_t ch2_Data)
{
	HAL_StatusTypeDef status;
    uint8_t liv_Data[6]; // 0: ch2_Addr  1:ch2_Datadata高8位 2:ch2_Datadata低8位
						 // 0: ch1_Addr  1:ch1_Datadata高8位 2:ch1_Datadata低8位	
	
	uint8_t liv_RXData[6] = {0};
	
	liv_Data[0] = ch2_Addr;
	liv_Data[1] = (ch2_Data & 0xff00) >> 8;
	liv_Data[2] = ch2_Data & 0x00ff;
	
	liv_Data[3] = ch1_Addr;
	liv_Data[4] = (ch1_Data & 0xff00) >> 8;
	liv_Data[5] = ch1_Data & 0x00ff;
	
	DAC8760_LATCH_0;
	status = HAL_SPI_TransmitReceive(&hSPI1, liv_Data, liv_RXData, 6, TIMEOUT); 
	DAC8760_LATCH_1;	
	
	delay_us(1);
	return status;
}

/******************************************************
函数名称:Read_DAC8760
函数描述:2块DAC菊花链的读函数
Calls:HAL_SPI_TransmitReceive
Called by: EnableOutputControl、ChangeOutputRange等
输入参数:	ch1_Addr:通道1读的寄存器地址
			ch2_Addr:通道2读的寄存器地址
			data: data[0]--Ch1 data;data[1]--Ch2 data
返回值:HAL status
******************************************************/
static HAL_StatusTypeDef Read_DAC8760(uint8_t ch1_Addr, uint8_t ch2_Addr, uint16_t *data)
{
	HAL_StatusTypeDef status;
	uint8_t liv_TXData[6]; // 0: ch2_Addr  1:ch2_Datadata高8位 2:ch2_Datadata低8位
						 // 0: ch1_Addr  1:ch1_Datadata高8位 2:ch1_Datadata低8位	
	uint8_t liv_RXData[6];
	
	liv_TXData[0] = W_ADDR_READ;
	liv_TXData[1] = (ch2_Addr & 0xff00) >> 8;
	liv_TXData[2] = ch2_Addr & 0x00ff;
	
	liv_TXData[3] = W_ADDR_READ;
	liv_TXData[4] = (ch1_Addr & 0xff00) >> 8;
	liv_TXData[5] = ch1_Addr & 0x00ff;
		
	DAC8760_LATCH_0;
	status = HAL_SPI_TransmitReceive(&hSPI1, liv_TXData, liv_RXData, 6, TIMEOUT); 
	DAC8760_LATCH_1;

	delay_us(1);	
//	if (status == HAL_OK)
//	{
//		*(data + 1) = (liv_RXData[4] << 8) | liv_RXData[5];
//	}
		
	liv_TXData[0] = W_ADDR_NOP;
	liv_TXData[1] = 0;
	liv_TXData[2] = 0;
	
	liv_TXData[3] = W_ADDR_NOP;
	liv_TXData[4] = 0;
	liv_TXData[5] = 0;
	
	DAC8760_LATCH_0;
	status = HAL_SPI_TransmitReceive(&hSPI1, liv_TXData, liv_RXData, 6, TIMEOUT); 
	DAC8760_LATCH_1;
	delay_us(1);	
	
	if (status == HAL_OK)
	{
		*data = (liv_RXData[1] << 8) | liv_RXData[2];
		*(data + 1) = (liv_RXData[4] << 8) | liv_RXData[5];  // 2018.9.18修改
	}
	
	return status;	
}

/******************************************************
函数名称:EnableOutputControl
函数描述:输出使能控制
Calls:Read_DAC8760、Write_DAC8760
Called by: Clear2RoadsOutput、Init_DAC8760
输入参数:ch1Enable:1--使能输出;0--禁能
		  ch2Enable:1--使能输出;0--禁能
返回值:无
******************************************************/
static void EnableOutputControl(uint8_t ch1Enable, uint8_t ch2Enable)
{
	#ifdef READ_CTRL
	uint16_t oldStatus[2], newStatus[2];
	#endif
	
	assert_param((ch1Enable <= 1) && (ch2Enable <= 1));

	#ifdef READ_CTRL
	if (Read_DAC8760(R_ADDR_CTRL, R_ADDR_CTRL, oldStatus) == HAL_OK)
	{
		oldStatus[0] &= ~(CTRL_OUTEN);
		oldStatus[1] &= ~(CTRL_OUTEN);
		
		newStatus[0] = oldStatus[0] | SET_OUTEN(ch1Enable);
		newStatus[1] = oldStatus[1] | SET_OUTEN(ch2Enable);
		
		Write_DAC8760(W_ADDR_CTRL, newStatus[0], W_ADDR_CTRL, newStatus[1]);
	}
	#else
	if (ch1Enable == 1)
	{
		gUCTRL_Value.usCTRL1Value |= SET_OUTEN;
	}
	else
	{
		gUCTRL_Value.usCTRL1Value &= ~SET_OUTEN;
	}
	
	if (ch2Enable == 1)
	{
		gUCTRL_Value.usCTRL2Value |= SET_OUTEN;
	}
	else
	{
		gUCTRL_Value.usCTRL2Value &= ~SET_OUTEN;
	}
	
	Write_DAC8760(W_ADDR_CTRL, gUCTRL_Value.usCTRL1Value, W_ADDR_CTRL, gUCTRL_Value.usCTRL2Value);
	#endif	
	
}	

/******************************************************
函数名称:Clear2RoadsOutput
函数描述:使两通道的输出为0
Calls:EnableOutputControl
Called by: Init_DAC8760
输入参数:无
返回值:无
******************************************************/
static void Clear2RoadsOutput(void)
{
	EnableOutputControl(OUTPUT_DISENABLE, OUTPUT_DISENABLE);
	DAC8760_CLR_1;
	delay_us(6);
	DAC8760_CLR_0;	
}

/******************************************************
函数名称:ChangeOutputRange
函数描述:改变输出范围,并且当需要将量程改成(1~5V)或(0~5V)时进行一些处理
Calls:Read_DAC8760、Write_DAC8760、SetCh1DACData、SetCh2DACData
Called by: sDoIntSomething
输入参数:pUHoldingReg: 传入gUHoldingReg共用体指针         
		  roadFlag:需要修改量程的通道
		  bkpFlag:BKP备份标志
返回值:无
******************************************************/
void ChangeOutputRange(UHoldingReg *pUHoldingReg, uint32_t roadFlag, SFlag *pSFlag, uint32_t bkpFlag)
{
	if (bkpFlag == TRUE)
	{
		// 备份
		gUBKPData.CH1_AOUT_Range = pUHoldingReg->CH1_AOUT_Range;
		gUBKPData.CH2_AOUT_Range = pUHoldingReg->CH2_AOUT_Range;
		gSFlag.Write_BKP_Flag = TRUE;
	}
	
	#ifdef READ_CTRL
	uint8_t ch1Range = ORange[pUHoldingReg->CH1_AOUT_Range];
	uint8_t ch2Range = ORange[pUHoldingReg->CH2_AOUT_Range];	

	uint16_t oldRange[2], newRange[2];
		
	Read_DAC8760(R_ADDR_CTRL, R_ADDR_CTRL, oldRange);
	
	oldRange[0] &= ~(CTRL_RANGE);
	oldRange[1] &= ~(CTRL_RANGE);
	
	newRange[0] = oldRange[0] | SET_RANGE(ch1Range);
	newRange[1] = oldRange[1] | SET_RANGE(ch2Range);
	
	Write_DAC8760(W_ADDR_CTRL, newRange[0], W_ADDR_CTRL, newRange[1]);
	
	#else
	gUCTRL_Value.usCTRL1Value &= (ORange[pUHoldingReg->CH1_AOUT_Range] | 0xFFF8);
	gUCTRL_Value.usCTRL1Value |= ORange[pUHoldingReg->CH1_AOUT_Range];
	
	gUCTRL_Value.usCTRL2Value &= (ORange[pUHoldingReg->CH2_AOUT_Range] | 0xFFF8);
	gUCTRL_Value.usCTRL2Value |= ORange[pUHoldingReg->CH2_AOUT_Range];	
	
	Write_DAC8760(W_ADDR_CTRL, gUCTRL_Value.usCTRL1Value, W_ADDR_CTRL, gUCTRL_Value.usCTRL2Value);
	#endif
	
	// 当用户选择量程为1~5V,需要软件将输出设为1V
	switch (roadFlag)
	{
		case CH1AOUT_RANGE_ADD:
		{
			pUHoldingReg->Ch1_AOUT_Value = 0;
			if (pUHoldingReg->CH1_AOUT_Range == VORANGE_1_5)
			{
				SetCh1DACData(0x3333);
				pUHoldingReg->Ch1_AOUT_Value = 1;
			}
			else if(pUHoldingReg->CH1_AOUT_Range == VORANGE_0_5)
			{
				SetCh1DACData(0);
			}
			else if (pUHoldingReg->CH1_AOUT_Range ==IORANGE_4_20)
			{
				pUHoldingReg->Ch1_AOUT_Value = 4;
			}
			
			pSFlag->Aout1Rang_ChangeFlag = TRUE;
			break;
		}
		case CH2AOUT_RANGE_ADD:
		{
			pUHoldingReg->Ch2_AOUT_Value = 0;
			if (pUHoldingReg->CH2_AOUT_Range == VORANGE_1_5)
			{
				SetCh2DACData(0x3333);
				pUHoldingReg->Ch2_AOUT_Value = 1;
			}
			else if(pUHoldingReg->CH2_AOUT_Range == VORANGE_0_5)
			{
				SetCh2DACData(0);
			}
			else if (pUHoldingReg->CH2_AOUT_Range ==IORANGE_4_20)
			{
				pUHoldingReg->Ch2_AOUT_Value = 4;
			}
			
			pSFlag->Aout2Rang_ChangeFlag = TRUE;			
			break;
		}
		default:
			break;
	}
	
}

/******************************************************
函数名称:SetCh1DACData
函数描述:设置通道1DAC输出,并立即更新。
Calls:Write_DAC8760
Called by: ChangeOutputRange、sDoFloatSomething
输入参数:dacValue: 0~65535
返回值:无
******************************************************/
void SetCh1DACData(uint16_t dacValue)
{
	Write_DAC8760(W_ADDR_DATA, dacValue, W_ADDR_NOP, 0);
}

/******************************************************
函数名称:SetCh2DACData
函数描述:设置通道2DAC输出,并立即更新。
Calls:Write_DAC8760
Called by: ChangeOutputRange、sDoFloatSomething
输入参数:dacValue: 0~65535
返回值:无
******************************************************/
void SetCh2DACData(uint16_t dacValue)
{
	Write_DAC8760(W_ADDR_NOP, 0, W_ADDR_DATA, dacValue);
}

/******************************************************
函数名称:Init_DAC8760
函数描述:SPI1和DAC8760的初始化代码
Calls:WriteSingle_DAC8760、Write_DAC8760、Clear2RoadsOutput等
Called by: main
输入参数:hspi:传入SPI_HandleTypeDef结构体指针
		 pUHoldingReg:传入gUHoldingReg共用体指针 
		 pUCTRL_Value:用来存放2块DAC芯片的控制寄存器值
返回值:无
******************************************************/
void Init_DAC8760(SPI_HandleTypeDef *hspi, UHoldingReg *pUHoldingReg, UCTRL_Value *pUCTRL_Value)
{
	
	/**********************SPI1模块的初始化代码,配置成主机模式******************/
	__HAL_RCC_SPI1_CLK_ENABLE();
	
	hspi->Instance = SPI1;
	hspi->Init.Mode = SPI_MODE_MASTER; // 主模式
	hspi->Init.Direction = SPI_DIRECTION_2LINES; // 双线模式
	hspi->Init.DataSize = SPI_DATASIZE_8BIT; // 发送接收8位帧结构
	hspi->Init.CLKPolarity = SPI_POLARITY_LOW; // 串行同步时钟空闲状态为低电平
	hspi->Init.CLKPhase = SPI_PHASE_1EDGE; // 串行同步时钟的第1个跳变沿(上升沿)数据被采样
	hspi->Init.NSS =  SPI_NSS_SOFT; // SS信号由硬件(NSS管脚)还是软件控制
	hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // SPI波特率预分频值 18M/8
	hspi->Init.FirstBit = SPI_FIRSTBIT_MSB;
	hspi->Init.TIMode = SPI_TIMODE_DISABLE;
	hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // 关闭硬件CRC
	hspi->Init.CRCPolynomial = 7; // CRC值计算的多项式
	HAL_SPI_Init(hspi);
	
	__HAL_SPI_ENABLE(hspi);
	/****************************************************************************/	
	
	/**********************DAC8760初始化部分*************************************/
	WriteSingle_DAC8760(W_ADDR_RESET, SOFTWARE_RESET);	

	pUCTRL_Value->usCTRL1Value |= CTRL_DCEN;
	WriteSingle_DAC8760(W_ADDR_CTRL, CTRL_DCEN);	
		
	Write_DAC8760(W_ADDR_NOP, 0, W_ADDR_RESET, SOFTWARE_RESET);
	
	pUCTRL_Value->usCTRL2Value |= CTRL_DCEN; 	
	Write_DAC8760(W_ADDR_NOP, 0, W_ADDR_CTRL, CTRL_DCEN ); // 菊花链使能			

	pUCTRL_Value->usCTRL1Value &= (ORange[pUHoldingReg->CH1_AOUT_Range] | 0xFFF8);
	pUCTRL_Value->usCTRL1Value |= ORange[pUHoldingReg->CH1_AOUT_Range];
	
	pUCTRL_Value->usCTRL2Value &= (ORange[pUHoldingReg->CH2_AOUT_Range] | 0xFFF8);
	pUCTRL_Value->usCTRL2Value |= ORange[pUHoldingReg->CH2_AOUT_Range];
	
	Write_DAC8760(W_ADDR_CTRL, pUCTRL_Value->usCTRL1Value, W_ADDR_CTRL, pUCTRL_Value->usCTRL2Value); // 菊花链使能和输出范围设置				
	
	Clear2RoadsOutput();	
	EnableOutputControl(TRUE, TRUE);	
	                                                                                                                                                                                                                                                	
	/****************************************************************************/	
}
// DAC8760.h
ifndef _DAC8760_H
#define _DAC8760_H

#ifdef DAC8760_GLOBALS
#define DAC8760_EXT
#else
#define DAC8760_EXT extern 
#endif

#include "global.h"

#define DAC8760_CLR_0  			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET)
#define DAC8760_CLR_1  			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET)
#define DAC8760_LATCH_0			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define DAC8760_LATCH_1			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)
#define ALARM_PORT_STATUS()     HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)

/* 通道定义 */
#define AOUT_CH1 	1
#define AOUT_CH2 	2

/* DAC8760 8位地址 */
#define W_ADDR_NOP           0x00
#define W_ADDR_DATA          0x01 // 写数据寄存器
#define W_ADDR_READ          0x02 // 读
#define W_ADDR_CTRL          0x55 // 写控制寄存器
#define W_ADDR_RESET         0x56 // 写复位寄存器
#define W_ADDR_CONFIG        0x57 // 写配置寄存器
#define W_ADDR_GAIN          0x58 // 写增益校准寄存器
#define W_ADDR_ZERO          0x59 // 写零校准寄存器
#define W_ADDR_WDT           0x95 // 写看门狗寄存器

/* 读操作命令寻址的寄存器地址 */ 
#define R_ADDR_SATUS       0x00
#define R_ADDR_DATA        0x01
#define R_ADDR_CTRL        0x02
#define R_ADDR_CONFIG      0x0B
#define R_ADDR_GAIN        0x13
#define R_ADDR_ZERO        0x17

/* 寄存器值宏定义 */
#define SOFTWARE_RESET   	1  
#define CTRL_DCEN          	(1 << 3) // 菊花链使能
#define CTRL_OUTEN         	(1 << 12)
#define CTRL_RANGE          (7 << 0)
#define TIMEOUT             500

#define OUTPUT_ENABLE       1
#define OUTPUT_DISENABLE    0

//#define READ_CTRL
#ifdef READ_CTRL
#define SET_OUTEN(x)        (x << 12)
#else
#define SET_OUTEN        	(1 << 12)
#endif
#define SET_RANGE(x)        (x & 0x07)

#pragma anon_unions
typedef union
{
	uint16_t usCTRLValue[2];
	struct
	{
		uint16_t usCTRL1Value;
		uint16_t usCTRL2Value;
	};
}UCTRL_Value;

#pragma no_anon_unions

DAC8760_EXT SPI_HandleTypeDef hSPI1;
DAC8760_EXT UCTRL_Value gUCTRL_Value;

void ChangeOutputRange(UHoldingReg *pUHoldingReg, uint32_t roadFlag, SFlag *pSFlag, uint32_t bkpFlag);
void SetDACData(uint16_t dac1Value, uint16_t dac2Value);
void SetCh1DACData(uint16_t dacValue);
void SetCh2DACData(uint16_t dacValue);
void Init_DAC8760(SPI_HandleTypeDef *hspi, UHoldingReg *pUHoldingReg, UCTRL_Value *pUCTRL_Value);
#endif

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
234 • 输 出 电 流 :4mA 至 至 20mA ; DAC7760 和 DAC8760 是低成本,高精度,全面集 0mA 至 至 20mA ;0mA 至 至 24mA 成,12 位和 16 位数模转换器 (DAC),此转换器设计 • 电 压 输 出 : 用于满足工业过程控制应用的需要。 这些器件可被设 – 0V 至 至 5V ;0V 至 至 10V ;±5V ;±10V 定为一个范围介于 4mA 至 20mA,0mA 至 20mA,或 – 0V 至 至 5.5V ;0V 至 至 11V ;±5.5V ;±11V 0mA 至 24mA 的电流输出;或者作为一个范围介于 ( 超 出 量 程 范围 围 10% ) 0V 至 5V,0V 至 10V,±5V,或 ±10V 的电压输出, • ±0.1% 满 量 程 范围 围 (FSR) 总 不 可 调 整 误差 差 (TUE) 可超出量程范围 10%(0V 至 5.5V,0V 至 最 大 值 11V,±5.5V,或 ±11V)。 电流和电压输出在由一个 • 微 分 非 线性 性 (DNL) :±1 最 低 有 效位 位 (LSB) 最 大 值 单个数据寄存器进行控制的同时,可被同时启用。 • 同 步 电 压 和 电 流 输 出 这些器件包括一个加电复位功能,以确保在一个已知状 • 内部 部 5V 基 准 (10 ppm/°C , 最 大 值 ) 态中加电(IOUT 和 VOUT 被禁用,并且处于高阻抗 • 内部 部 4.6V 电 源 输 出 (Hi-Z) 状态)。 CLR 和 CLR-SEL 引脚将电压输出设 • 可 靠 特 性 : 定为零量程或中量程,并且在输出被启用时,电流输出 – 循 环 冗 余码 码 (CRC) 校 验 和 看 门 狗 定 时 器 被设定为量程范围的低端。 零和增益寄存器可被设定 – 过 热 警 报 为在终端系统中对器件进行数字校准。 输出转换率也 – 开 路 警 报 , 短 接 电 流 限 制 由寄存器设定。 这些器件可在电流输出上添加一个外 • 宽 温 度 范 围 :-40°C 至 至 +125°C 部 HART ® 信号,并且可由一个 +10V 至 +36V 单电 • 6mm × 6mm 四 方 扁 平 无 引线 线 (QFN)-40 ; 薄 型 小 源,或高达 ±18V 的双电源供电运行。全部器件版本采 外 形 尺寸 寸 (TSSOP)-24 封 装 用 6mm × 6mm QFN-40 和 TSSOP-24 封装。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值