环境: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